1
0
Fork 0

day(24): add solution

Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
Matej Focko 2024-07-07 21:58:41 +02:00
parent 40721c4d56
commit a61866c12e
Signed by: mfocko
SSH key fingerprint: SHA256:5YXD7WbPuK60gxnG6DjAwJiS9+swoWj33/HFu8g8JVo
2 changed files with 182 additions and 0 deletions

5
samples/day24.txt Normal file
View 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
View 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);