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