1use std::cmp::Ordering;
2use std::collections::BinaryHeap;
3use utils::geometry::Vec3;
4use utils::prelude::*;
5
6#[derive(Clone, Debug)]
8pub struct Day23 {
9 bots: Vec<Bot>,
10}
11
12#[derive(Clone, Debug)]
13struct Bot {
14 pos: Vec3<i32>,
15 r: u32,
16}
17
18const MAX_POW2: u32 = 29;
20const MIN: i32 = -(1 << MAX_POW2);
21const MAX: i32 = (1 << MAX_POW2) - 1;
22
23impl Day23 {
24 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
25 Ok(Self {
26 bots: parser::number_range(MIN..=MAX)
27 .repeat_n(b',')
28 .with_prefix("pos=<")
29 .with_suffix(">, r=")
30 .then(parser::u32())
31 .map(|(pos, r)| Bot { pos: pos.into(), r })
32 .parse_lines(input)?,
33 })
34 }
35
36 #[must_use]
37 pub fn part1(&self) -> usize {
38 let strongest = self.bots.iter().max_by_key(|b| b.r).unwrap();
39
40 self.bots
41 .iter()
42 .filter(|&b| b.pos.manhattan_distance_to(strongest.pos) <= strongest.r)
43 .count()
44 }
45
46 #[must_use]
47 pub fn part2(&self) -> u32 {
48 let initial = Subcube::new(Vec3::splat(MIN), Vec3::splat(MAX), &self.bots);
49
50 let mut heap = BinaryHeap::with_capacity(2048);
51 heap.push(initial);
52
53 while let Some(s) = heap.pop() {
54 if s.size == 1 {
55 return s.dist;
56 }
57 heap.extend(s.split(&self.bots));
58 }
59
60 unreachable!()
61 }
62}
63
64#[derive(Clone, Debug, Eq, PartialEq)]
65struct Subcube {
66 min: Vec3<i32>,
67 max: Vec3<i32>,
68 bot_count: usize,
69 dist: u32,
70 size: u32,
71}
72
73impl Subcube {
74 #[inline]
75 fn new(min: Vec3<i32>, max: Vec3<i32>, bots: &[Bot]) -> Self {
76 Self {
77 min,
78 max,
79 bot_count: bots
80 .iter()
81 .filter(|&bot| bot.pos.manhattan_distance_to_aabb(min, max) <= bot.r)
82 .count(),
83 dist: Vec3::ORIGIN.manhattan_distance_to_aabb(min, max),
84 size: max.x.abs_diff(min.x) + 1,
85 }
86 }
87
88 #[inline]
89 fn split(&self, bots: &[Bot]) -> [Self; 8] {
90 let half = (self.size / 2) as i32;
91 let xs = [
92 (self.min.x, self.min.x + half - 1),
93 (self.min.x + half, self.max.x),
94 ];
95 let ys = [
96 (self.min.y, self.min.y + half - 1),
97 (self.min.y + half, self.max.y),
98 ];
99 let zs = [
100 (self.min.z, self.min.z + half - 1),
101 (self.min.z + half, self.max.z),
102 ];
103
104 std::array::from_fn(|i| {
105 let (min_x, max_x) = xs[i >> 2];
106 let (min_y, max_y) = ys[(i >> 1) & 1];
107 let (min_z, max_z) = zs[i & 1];
108 Self::new(
109 Vec3::new(min_x, min_y, min_z),
110 Vec3::new(max_x, max_y, max_z),
111 bots,
112 )
113 })
114 }
115}
116
117impl Ord for Subcube {
118 #[inline]
119 fn cmp(&self, other: &Self) -> Ordering {
120 self.bot_count
124 .cmp(&other.bot_count)
125 .then(self.dist.cmp(&other.dist).reverse())
126 .then(self.size.cmp(&other.size).reverse())
127 }
128}
129
130impl PartialOrd for Subcube {
131 #[inline]
132 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
133 Some(self.cmp(other))
134 }
135}
136
137examples!(Day23 -> (usize, u32) [
138 {
139 input: "pos=<0,0,0>, r=4\n\
140 pos=<1,0,0>, r=1\n\
141 pos=<4,0,0>, r=3\n\
142 pos=<0,2,0>, r=1\n\
143 pos=<0,5,0>, r=3\n\
144 pos=<0,0,3>, r=1\n\
145 pos=<1,1,1>, r=1\n\
146 pos=<1,1,2>, r=1\n\
147 pos=<1,3,1>, r=1",
148 part1: 7,
149 },
150 {
151 input: "pos=<10,12,12>, r=2\n\
152 pos=<12,14,12>, r=2\n\
153 pos=<16,12,12>, r=4\n\
154 pos=<14,14,14>, r=6\n\
155 pos=<50,50,50>, r=200\n\
156 pos=<10,10,10>, r=5",
157 part2: 36,
158 },
159]);