1use crate::intcode::Interpreter;
2use crate::intcode::features::Day09Features;
3use core::assert_matches;
4use utils::geometry::Vec2;
5use utils::prelude::*;
6
7#[derive(Clone, Debug)]
9pub struct Day19 {
10 interpreter: Interpreter,
11}
12
13impl Day19 {
14 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
15 Ok(Self {
16 interpreter: Interpreter::parse(input, 1)?,
17 })
18 }
19
20 #[must_use]
21 pub fn part1(&self) -> u32 {
22 let mut interpreter = Interpreter::new(Vec::new());
23 let (mut min_x, mut max_x) = (49, 49);
24 let mut affected = 0;
25 for y in (0..50).rev() {
26 let mut new_max_x = max_x;
27 while new_max_x > 0 && !self.is_affected(&mut interpreter, new_max_x, y) {
28 new_max_x -= 1;
29 }
30 if new_max_x == 0 && !self.is_affected(&mut interpreter, 0, y) {
31 continue;
33 }
34 max_x = new_max_x;
35
36 min_x = min_x.min(max_x);
37 while min_x > 0 && self.is_affected(&mut interpreter, min_x - 1, y) {
38 min_x -= 1;
39 }
40
41 affected += max_x - min_x + 1;
42 }
43 affected
44 }
45
46 #[must_use]
47 pub fn part2(&self) -> u32 {
48 let mut interpreter = Interpreter::new(Vec::new());
49 let inside_x = (0..=50)
50 .rev()
51 .chain(51..)
52 .find(|&x| self.is_affected(&mut interpreter, x, 50))
53 .expect("no solution found");
54
55 let initial = Vec2::new(inside_x.max(1), 50);
56 let mut row_start = self.find_row_start(&mut interpreter, 0, 50, initial);
57 let mut column_start =
58 self.find_column_start(&mut interpreter, row_start.x + 99, 0, initial);
59
60 loop {
61 let next_row_start = self.find_row_start(
62 &mut interpreter,
63 row_start.x,
64 column_start.y + 99,
65 row_start,
66 );
67 let next_column_start = self.find_column_start(
68 &mut interpreter,
69 next_row_start.x + 99,
70 column_start.y,
71 column_start,
72 );
73 if next_column_start.y == column_start.y {
74 return next_row_start.x * 10_000 + column_start.y;
75 }
76 (row_start, column_start) = (next_row_start, next_column_start);
77 }
78 }
79
80 fn find_row_start(
81 &self,
82 interpreter: &mut Interpreter,
83 mut x: u32,
84 y: u32,
85 last: Vec2<u32>,
86 ) -> Vec2<u32> {
87 let guess = x.max((last.x * y).div_ceil(last.y));
88 if guess > x && self.is_affected(interpreter, guess, y) {
89 x = guess;
90 while x > 0 && self.is_affected(interpreter, x - 1, y) {
91 x -= 1;
92 }
93 } else {
94 while !self.is_affected(interpreter, x, y) {
95 x += 1;
96 }
97 }
98 Vec2::new(x, y)
99 }
100
101 fn find_column_start(
102 &self,
103 interpreter: &mut Interpreter,
104 x: u32,
105 mut y: u32,
106 last: Vec2<u32>,
107 ) -> Vec2<u32> {
108 let guess = y.max((last.y * x).div_ceil(last.x));
109 if guess > y && self.is_affected(interpreter, x, guess) {
110 y = guess;
111 while y > 0 && self.is_affected(interpreter, x, y - 1) {
112 y -= 1;
113 }
114 } else {
115 while !self.is_affected(interpreter, x, y) {
116 y += 1;
117 }
118 }
119 Vec2::new(x, y)
120 }
121
122 fn is_affected(&self, interpreter: &mut Interpreter, x: u32, y: u32) -> bool {
123 interpreter.mem.clone_from(&self.interpreter.mem);
124 interpreter.ip = 0;
125 interpreter.relative_base = 0;
126 interpreter.push_input(i64::from(x));
127 interpreter.push_input(i64::from(y));
128
129 let output = interpreter.expect_output::<Day09Features>();
130 assert_matches!(output, 0 | 1, "expected output zero or one");
131 output == 1
132 }
133}
134
135examples!(Day19 -> (u32, u32) []);