1use crate::util::hash::*;
15use crate::util::iter::*;
16use crate::util::parse::*;
17use std::ops::AddAssign;
18
19#[derive(Copy, Clone, PartialEq, Eq, Hash)]
20struct Vector {
21 x: i32,
22 y: i32,
23 z: i32,
24}
25
26impl Vector {
27 #[inline]
28 fn new([x, y, z]: [i32; 3]) -> Self {
29 Self { x, y, z }
30 }
31
32 #[inline]
33 fn manhattan(self) -> i32 {
34 self.x.abs() + self.y.abs() + self.z.abs()
35 }
36}
37
38impl AddAssign for Vector {
39 #[inline]
40 fn add_assign(&mut self, rhs: Self) {
41 self.x += rhs.x;
42 self.y += rhs.y;
43 self.z += rhs.z;
44 }
45}
46
47#[derive(Copy, Clone)]
48pub struct Particle {
49 id: usize,
50 position: Vector,
51 velocity: Vector,
52 acceleration: Vector,
53}
54
55impl Particle {
56 #[inline]
57 fn tick(&mut self) {
58 self.velocity += self.acceleration;
59 self.position += self.velocity;
60 }
61
62 #[inline]
64 fn align(&mut self) -> bool {
65 let oldp = self.position.manhattan();
66 let oldv = self.velocity.manhattan();
67 self.tick();
68 oldp <= self.position.manhattan() && oldv <= self.velocity.manhattan()
69 }
70}
71
72pub fn parse(input: &str) -> Vec<Particle> {
73 input
74 .iter_signed()
75 .chunk::<3>()
76 .chunk::<3>()
77 .enumerate()
78 .map(|(id, [p, v, a])| Particle {
79 id,
80 position: Vector::new(p),
81 velocity: Vector::new(v),
82 acceleration: Vector::new(a),
83 })
84 .collect()
85}
86
87pub fn part1(input: &[Particle]) -> usize {
88 let min = input.iter().map(|p| p.acceleration.manhattan()).min().unwrap();
90 let mut candidates: Vec<_> =
91 input.iter().copied().filter(|p| p.acceleration.manhattan() == min).collect();
92
93 while !candidates.iter_mut().fold(true, |acc, particle| acc & particle.align()) {}
96
97 candidates.iter().min_by_key(|p| (p.velocity.manhattan(), p.position.manhattan())).unwrap().id
99}
100
101pub fn part2(input: &[Particle]) -> usize {
102 let mut particles = input.to_vec();
103 let mut collisions = FastMap::with_capacity(input.len());
104 let mut alive = vec![true; input.len()];
105
106 for _ in 1..40 {
107 for (i, particle) in particles.iter_mut().enumerate() {
108 if alive[i] {
111 particle.tick();
112
113 if let Some(j) = collisions.insert(particle.position, i) {
114 alive[i] = false;
115 alive[j] = false;
116 }
117 }
118 }
119
120 collisions.clear();
121 }
122
123 alive.iter().filter(|&&a| a).count()
124}