1use crate::util::heap::*;
20use crate::util::iter::*;
21use crate::util::parse::*;
22
23pub struct Nanobot {
24 x: i32,
25 y: i32,
26 z: i32,
27 r: i32,
28}
29
30impl Nanobot {
31 fn from([x, y, z, r]: [i32; 4]) -> Nanobot {
32 Nanobot { x, y, z, r }
33 }
34
35 fn manhattan(&self, other: &Nanobot) -> i32 {
36 (self.x - other.x).abs() + (self.y - other.y).abs() + (self.z - other.z).abs()
37 }
38}
39
40struct Cube {
41 x1: i32,
42 x2: i32,
43 y1: i32,
44 y2: i32,
45 z1: i32,
46 z2: i32,
47}
48
49impl Cube {
50 fn new(x1: i32, x2: i32, y1: i32, y2: i32, z1: i32, z2: i32) -> Cube {
51 Cube { x1, x2, y1, y2, z1, z2 }
52 }
53
54 fn split(&self) -> [Cube; 8] {
57 let Cube { x1, x2, y1, y2, z1, z2 } = *self;
58
59 let lx = (self.x1 + self.x2) / 2;
61 let ly = (self.y1 + self.y2) / 2;
62 let lz = (self.z1 + self.z2) / 2;
63 let ux = lx + 1;
64 let uy = ly + 1;
65 let uz = lz + 1;
66
67 [
69 Cube::new(x1, lx, y1, ly, z1, lz),
70 Cube::new(ux, x2, y1, ly, z1, lz),
71 Cube::new(x1, lx, uy, y2, z1, lz),
72 Cube::new(ux, x2, uy, y2, z1, lz),
73 Cube::new(x1, lx, y1, ly, uz, z2),
74 Cube::new(ux, x2, y1, ly, uz, z2),
75 Cube::new(x1, lx, uy, y2, uz, z2),
76 Cube::new(ux, x2, uy, y2, uz, z2),
77 ]
78 }
79
80 fn in_range(&self, nb: &Nanobot) -> bool {
83 let x = (self.x1 - nb.x).max(0) + (nb.x - self.x2).max(0);
84 let y = (self.y1 - nb.y).max(0) + (nb.y - self.y2).max(0);
85 let z = (self.z1 - nb.z).max(0) + (nb.z - self.z2).max(0);
86 x + y + z <= nb.r
87 }
88
89 fn closest(&self) -> i32 {
91 let x = self.x1.abs().min(self.x2.abs());
92 let y = self.y1.abs().min(self.y2.abs());
93 let z = self.z1.abs().min(self.z2.abs());
94 x + y + z
95 }
96
97 fn size(&self) -> i32 {
99 self.x2 - self.x1 + 1
100 }
101}
102
103pub fn parse(input: &str) -> Vec<Nanobot> {
104 input.iter_signed().chunk::<4>().map(Nanobot::from).collect()
105}
106
107pub fn part1(input: &[Nanobot]) -> usize {
108 let strongest = input.iter().max_by_key(|nb| nb.r).unwrap();
109 input.iter().filter(|nb| strongest.manhattan(nb) <= strongest.r).count()
110}
111
112pub fn part2(input: &[Nanobot]) -> i32 {
113 const SIZE: i32 = 1 << 29;
116 let mut heap = MinHeap::with_capacity(1_000);
117 heap.push((0, 0, 0), Cube::new(-SIZE, SIZE - 1, -SIZE, SIZE - 1, -SIZE, SIZE - 1));
118
119 while let Some((_, cube)) = heap.pop() {
120 if cube.size() == 1 {
121 return cube.closest();
122 }
123
124 for next in cube.split() {
125 let in_range = input.iter().filter(|nb| next.in_range(nb)).count();
126 let key = (input.len() - in_range, next.closest(), next.size());
127 heap.push(key, next);
128 }
129 }
130
131 unreachable!()
132}