1use utils::geometry::Vec2;
2use utils::prelude::*;
3
4#[derive(Clone, Debug)]
6pub struct Day10 {
7 message: String,
8 seconds: u32,
9}
10
11impl Day10 {
12 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
13 let vec2 = parser::i32()
14 .with_prefix(parser::take_while(|&x| x == b' '))
15 .repeat_n(b',')
16 .map(Vec2::from);
17
18 let mut points = vec2
19 .with_prefix("position=<")
20 .with_suffix("> velocity=<")
21 .then(vec2)
22 .with_suffix(">")
23 .repeat(parser::eol(), 2)
24 .parse_complete(input)?;
25
26 let mut seconds = 0;
27 let (mut last_x_diff, mut last_y_diff) = (i32::MAX, i32::MAX);
28 let message = 'time: loop {
29 let (mut min_x, mut max_x) = (i32::MAX, i32::MIN);
30 let (mut min_y, mut min_y_vel, mut max_y, mut max_y_vel) = (i32::MAX, 0, i32::MIN, 0);
31 for (p, v) in &points {
32 min_x = min_x.min(p.x);
33 max_x = max_x.max(p.x);
34
35 if p.y < min_y {
36 min_y = p.y;
37 min_y_vel = v.y;
38 }
39 if p.y > max_y {
40 max_y = p.y;
41 max_y_vel = v.y;
42 }
43 }
44
45 let (x_diff, y_diff) = (max_x.saturating_sub(min_x), max_y.saturating_sub(min_y));
46 if x_diff > last_x_diff
47 || y_diff > last_y_diff
48 || (x_diff == last_x_diff && y_diff == last_y_diff)
49 {
50 return Err(InputError::new(input, 0, "points never converge"));
51 }
52 (last_x_diff, last_y_diff) = (x_diff, y_diff);
53
54 if y_diff != 9 || x_diff % 8 != 5 {
56 let advance_by = if y_diff >= 10 && min_y_vel.saturating_sub(max_y_vel) > 0 {
57 ((y_diff - 9) / min_y_vel.saturating_sub(max_y_vel)).max(1)
58 } else {
59 1
60 };
61
62 for (p, v) in points.iter_mut() {
63 *p += *v * advance_by;
64 }
65 seconds += advance_by as u32;
66
67 continue;
68 }
69
70 let len = ((max_x - min_x + 3) / 8) as usize;
71 let mut letters = vec![0u64; len];
72 for (p, _) in points.iter() {
73 if (p.x - min_x) % 8 >= 6 {
74 continue 'time;
76 }
77
78 letters[(p.x - min_x) as usize / 8] |=
79 1 << (59 - ((p.x - min_x) % 8 + 6 * (p.y - min_y)));
80 }
81 break letters.into_iter().map(Self::ocr).collect::<String>();
82 };
83
84 Ok(Self { message, seconds })
85 }
86
87 fn ocr(letter: u64) -> char {
88 match letter {
110 0b001100010010100001100001100001111111100001100001100001100001 => 'A',
112 0b111110100001100001100001111110100001100001100001100001111110 => 'B',
113 0b011110100001100000100000100000100000100000100000100001011110 => 'C',
114 0b111111100000100000100000111110100000100000100000100000111111 => 'E',
115 0b111111100000100000100000111110100000100000100000100000100000 => 'F',
116 0b011110100001100000100000100000100111100001100001100011011101 => 'G',
117 0b100001100001100001100001111111100001100001100001100001100001 => 'H',
118 0b000111000010000010000010000010000010000010100010100010011100 => 'J',
119 0b100001100010100100101000110000110000101000100100100010100001 => 'K',
120 0b100000100000100000100000100000100000100000100000100000111111 => 'L',
121 0b100001110001110001101001101001100101100101100011100011100001 => 'N',
122 0b111110100001100001100001111110100000100000100000100000100000 => 'P',
123 0b111110100001100001100001111110100100100010100010100001100001 => 'R',
124 0b100001100001010010010010001100001100010010010010100001100001 => 'X',
125 0b111111000001000001000010000100001000010000100000100000111111 => 'Z',
126 _ => Self::unknown_letter(letter),
127 }
128 }
129
130 #[cold]
131 fn unknown_letter(letter: u64) -> char {
132 let mut display = String::new();
133 for b in (0..60).rev() {
134 display.push(if letter & (1 << b) == 0 { ' ' } else { '#' });
135 if b % 6 == 0 {
136 display.push('\n');
137 }
138 }
139 panic!("unknown letter {letter:#062b}:\n{display}");
140 }
141
142 #[must_use]
143 pub fn part1(&self) -> &str {
144 &self.message
145 }
146
147 #[must_use]
148 pub fn part2(&self) -> u32 {
149 self.seconds
150 }
151}
152
153examples!(Day10 -> (&'static str, u32) []);