day(24): add solution
Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
parent
40721c4d56
commit
a61866c12e
2 changed files with 182 additions and 0 deletions
5
samples/day24.txt
Normal file
5
samples/day24.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
19, 13, 30 @ -2, 1, -2
|
||||||
|
18, 19, 22 @ -1, -1, -2
|
||||||
|
20, 25, 34 @ -2, -2, -4
|
||||||
|
12, 31, 28 @ -1, -2, -1
|
||||||
|
20, 19, 15 @ 1, -5, -3
|
177
src/bin/day24.rs
Normal file
177
src/bin/day24.rs
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
|
use aoc_2023::*;
|
||||||
|
|
||||||
|
type Output1 = u32;
|
||||||
|
type Output2 = i128;
|
||||||
|
|
||||||
|
type Vec3D = Vector3D<i128>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct FloatingHailstone {
|
||||||
|
position: Vector3D<f64>,
|
||||||
|
velocity: Vector3D<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Hailstone {
|
||||||
|
position: Vec3D,
|
||||||
|
velocity: Vec3D,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hailstone {
|
||||||
|
fn as_float(&self) -> FloatingHailstone {
|
||||||
|
FloatingHailstone {
|
||||||
|
position: Vector3D::<f64>::new(
|
||||||
|
self.position.x() as f64,
|
||||||
|
self.position.y() as f64,
|
||||||
|
self.position.z() as f64,
|
||||||
|
),
|
||||||
|
velocity: Vector3D::<f64>::new(
|
||||||
|
self.velocity.x() as f64,
|
||||||
|
self.velocity.y() as f64,
|
||||||
|
self.velocity.z() as f64,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Hailstone> for (Vec3D, Vec3D) {
|
||||||
|
fn from(value: Hailstone) -> Self {
|
||||||
|
(value.position, value.velocity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Day24 {
|
||||||
|
hailstones: Vec<Hailstone>,
|
||||||
|
range: RangeInclusive<i128>,
|
||||||
|
}
|
||||||
|
impl Solution<Output1, Output2> for Day24 {
|
||||||
|
fn new<P: AsRef<Path>>(pathname: P) -> Self {
|
||||||
|
let lines: Vec<String> = file_to_lines(pathname);
|
||||||
|
|
||||||
|
let hailstones = lines
|
||||||
|
.into_iter()
|
||||||
|
.map(|l| {
|
||||||
|
let coords = l
|
||||||
|
.split(&[',', '@'])
|
||||||
|
.map(|c| c.trim().parse().unwrap())
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
Hailstone {
|
||||||
|
position: Vec3D::new(coords[0], coords[1], coords[2]),
|
||||||
|
velocity: Vec3D::new(coords[3], coords[4], coords[5]),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
let count = hailstones.len();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
hailstones,
|
||||||
|
range: if count == 5 {
|
||||||
|
5..=30 // small input
|
||||||
|
} else {
|
||||||
|
200000000000000..=400000000000000
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part_1(&mut self) -> Output1 {
|
||||||
|
let mut result = 0;
|
||||||
|
|
||||||
|
for (index, fst_h) in self.hailstones[1..].iter().enumerate() {
|
||||||
|
let (a, b) = (fst_h.position.x(), fst_h.position.y());
|
||||||
|
let (c, d) = (fst_h.velocity.x(), fst_h.velocity.y());
|
||||||
|
|
||||||
|
for snd_h in &self.hailstones[..index + 1] {
|
||||||
|
let (e, f) = (snd_h.position.x(), snd_h.position.y());
|
||||||
|
let (g, h) = (snd_h.velocity.x(), snd_h.velocity.y());
|
||||||
|
|
||||||
|
// If the determinant is zero there is no solution possible
|
||||||
|
// which implies the trajectories are parallel.
|
||||||
|
let determinant = d * g - c * h;
|
||||||
|
if determinant == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invert the 2x2 matrix then multiply by the respective columns to find the times.
|
||||||
|
let t = (g * (f - b) - h * (e - a)) / determinant;
|
||||||
|
let u = (c * (f - b) - d * (e - a)) / determinant;
|
||||||
|
|
||||||
|
// We can pick either the first or second hailstone to find the intersection position.
|
||||||
|
let x = a + t * c;
|
||||||
|
let y = b + t * d;
|
||||||
|
|
||||||
|
// Both times must be in the future and the position within the specified area.
|
||||||
|
if t >= 0 && u >= 0 && self.range.contains(&x) && self.range.contains(&y) {
|
||||||
|
result += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part_2(&mut self) -> Output2 {
|
||||||
|
const BRUTE_RANGE: RangeInclusive<i128> = -1000..=1000;
|
||||||
|
|
||||||
|
let mut possible_x_vel = Vec::new();
|
||||||
|
let mut possible_y_vel = Vec::new();
|
||||||
|
let mut possible_z_vel = Vec::new();
|
||||||
|
|
||||||
|
let mut iter = self.hailstones.iter().tuple_combinations();
|
||||||
|
while possible_x_vel.len() != 1 || possible_y_vel.len() != 1 || possible_z_vel.len() != 1 {
|
||||||
|
let (a, b) = iter.next().expect("No solution found");
|
||||||
|
let process = |possible: &mut Vec<i128>, idx: usize| {
|
||||||
|
let pos = (a.position.as_slice()[idx], b.position.as_slice()[idx]);
|
||||||
|
let vel = (a.velocity.as_slice()[idx], b.velocity.as_slice()[idx]);
|
||||||
|
|
||||||
|
if vel.0 != vel.1 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let delta = (pos.0 - pos.1).abs();
|
||||||
|
let this = BRUTE_RANGE
|
||||||
|
.clone()
|
||||||
|
.filter(|i| i != &vel.0 && delta % (i - vel.0) == 0)
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
possible.retain(|v| this.contains(v));
|
||||||
|
if possible.is_empty() {
|
||||||
|
possible.extend(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
process(&mut possible_x_vel, 0);
|
||||||
|
process(&mut possible_y_vel, 1);
|
||||||
|
process(&mut possible_z_vel, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (a, b) = (self.hailstones[0].as_float(), self.hailstones[1].as_float());
|
||||||
|
let (xv, yv, zv) = (
|
||||||
|
possible_x_vel[0] as f64,
|
||||||
|
possible_y_vel[0] as f64,
|
||||||
|
possible_z_vel[0] as f64,
|
||||||
|
);
|
||||||
|
|
||||||
|
let ma = (a.velocity.y() - yv) / (a.velocity.x() - xv);
|
||||||
|
let mb = (b.velocity.y() - yv) / (b.velocity.x() - xv);
|
||||||
|
|
||||||
|
let ca = a.position.y() - ma * a.position.x();
|
||||||
|
let cb = b.position.y() - mb * b.position.x();
|
||||||
|
|
||||||
|
let x = (cb - ca) / (ma - mb);
|
||||||
|
let y = ma * x + ca;
|
||||||
|
let t = (x - a.position.x()) / (a.velocity.x() - xv);
|
||||||
|
let z = a.position.z() + (a.velocity.z() - zv) * t;
|
||||||
|
|
||||||
|
((x + y + z) as i64).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
Day24::main()
|
||||||
|
}
|
||||||
|
|
||||||
|
test_sample!(day_24, Day24, 2, 47);
|
Loading…
Reference in a new issue