day-11.rs (7731B)
1 use std::collections::HashMap;
2 type Op = i128;
3
4 fn main() {
5 let input = std::fs::read_to_string("input.txt")
6 .unwrap()
7 .trim()
8 .split(",")
9 .map(|op| op.parse::<Op>().unwrap())
10 .collect::<Vec<Op>>();
11 println!("Rust:");
12 println!("Part 1: {}", part_1(&input));
13 println!("Part 2: ");
14 part_2(&input);
15 }
16
17 fn turn_left(dir: (Op, Op)) -> (Op, Op) {
18 // Takes in dir in the form of (0, 1).
19 match dir {
20 (0, 1) => (-1, 0),
21 (1, 0) => (0, 1),
22 (0, -1) => (1, 0),
23 (-1, 0) => (0, -1),
24 _ => panic!(),
25 }
26 }
27
28 fn turn_right(dir: (Op, Op)) -> (Op, Op) {
29 // Takes in dir in the form of (0, 1).
30 match dir {
31 (0, 1) => (1, 0),
32 (1, 0) => (0, -1),
33 (0, -1) => (-1, 0),
34 (-1, 0) => (0, 1),
35 _ => panic!(),
36 }
37 }
38
39 fn robot(program: &Vec<Op>, panels: &mut HashMap<(Op, Op), usize>) {
40 let mut program = program
41 .iter()
42 .enumerate()
43 .map(|(idx, &op)| (idx, op))
44 .collect::<HashMap<usize, Op>>();
45 let mut pc = 0;
46 let mut relative_base = 0;
47 let mut coord = (0, 0);
48 // Initially facing north.
49 let mut dir = (0, 1);
50
51 loop {
52 // Get color for current panel. Panels start black (0).
53 // This records only panels the robot has stepped on.
54 let color = panels.entry(coord).or_insert(0);
55 let (finished, step_output) = computer_step(
56 &mut program,
57 &mut pc,
58 &mut relative_base,
59 Some(*color as Op),
60 );
61 // First instruction is color to paint the panel to.
62 *panels.get_mut(&coord).unwrap() = step_output[0] as usize;
63 // Second instruction is direction to turn the robot.
64 if step_output[1] == 0 {
65 dir = turn_left(dir);
66 } else {
67 dir = turn_right(dir);
68 }
69 // Move robot forward by one panel.
70 coord = (coord.0 + dir.0, coord.1 + dir.1);
71 if finished {
72 break;
73 }
74 }
75 }
76
77 fn computer_step(
78 program: &mut HashMap<usize, Op>,
79 pc: &mut usize,
80 relative_base: &mut Op,
81 mut input: Option<Op>,
82 ) -> (bool, Vec<Op>) {
83 let mut output = vec![];
84 let get_param = |loc: usize, mode, program: &HashMap<usize, Op>, relative_base: &Op| -> Op {
85 match mode {
86 // Position mode.
87 0 => *program.get(&(program[&loc] as usize)).unwrap_or(&0),
88 // Immediate mode.
89 1 => program[&loc],
90 // Relative mode.
91 2 => *program
92 .get(&((*relative_base + program[&loc]) as usize))
93 .unwrap_or(&0),
94 _ => panic!(),
95 }
96 };
97
98 let get_res_loc =
99 |loc: usize, mode, program: &HashMap<usize, Op>, relative_base: &Op| -> usize {
100 match mode {
101 // Position mode.
102 0 => program[&loc] as usize,
103 // Immediate mode is banned.
104 // Relative mode.
105 2 => (relative_base + program[&loc]) as usize,
106 _ => panic!(),
107 }
108 };
109
110 loop {
111 let op_code = program[pc] % 100;
112 let mode_1 = (program[pc] % 1000) / 100;
113 let mode_2 = (program[pc] % 10000) / 1000;
114 let mode_3 = (program[pc] % 100000) / 10000;
115 match op_code {
116 1 => {
117 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
118 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
119 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
120 *program.entry(res_loc).or_default() = param_1 + param_2;
121 *pc += 4;
122 }
123 2 => {
124 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
125 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
126 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
127 *program.entry(res_loc as usize).or_default() = param_1 * param_2;
128 *pc += 4;
129 }
130 3 => {
131 let res_loc = get_res_loc(*pc + 1, mode_1, &program, relative_base);
132 // Only use input once.
133 if input.is_some() {
134 *program.entry(res_loc as usize).or_default() = input.unwrap();
135 input = None;
136 } else {
137 // Need new input.
138 return (false, output);
139 };
140 *pc += 2;
141 }
142 4 => {
143 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
144 output.push(param_1);
145 *pc += 2;
146 }
147 5 => {
148 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
149 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
150 if param_1 != 0 {
151 *pc = param_2 as usize;
152 } else {
153 *pc += 3;
154 }
155 }
156 6 => {
157 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
158 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
159 if param_1 == 0 {
160 *pc = param_2 as usize;
161 } else {
162 *pc += 3;
163 }
164 }
165 7 => {
166 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
167 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
168 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
169 *program.entry(res_loc as usize).or_default() =
170 if param_1 < param_2 { 1 } else { 0 };
171 *pc += 4;
172 }
173 8 => {
174 let res_loc = get_res_loc(*pc + 3, mode_3, &program, relative_base);
175 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
176 let param_2 = get_param(*pc + 2, mode_2, &program, relative_base);
177 *program.entry(res_loc as usize).or_default() =
178 if param_1 == param_2 { 1 } else { 0 };
179 *pc += 4;
180 }
181 9 => {
182 let param_1 = get_param(*pc + 1, mode_1, &program, relative_base);
183 *relative_base += param_1;
184 *pc += 2;
185 }
186 99 => {
187 break;
188 }
189 _ => {
190 println!("Unknown op {}", op_code);
191 println!("{}", program[pc]);
192 panic!();
193 }
194 }
195 }
196
197 (true, output)
198 }
199
200 fn part_1(input: &Vec<Op>) -> usize {
201 let mut panels = HashMap::new();
202 robot(input, &mut panels);
203 panels.len()
204 }
205
206 fn part_2(input: &Vec<Op>) {
207 let mut panels = HashMap::new();
208 // Set initial panel white.
209 panels.insert((0, 0), 1);
210 robot(input, &mut panels);
211 // Display the panel results.
212 // Check max/min coordinates.
213 let mut min_x = Op::max_value();
214 let mut max_x = Op::min_value();
215 let mut min_y = Op::max_value();
216 let mut max_y = Op::min_value();
217 for panel in panels.keys() {
218 min_x = min_x.min(panel.0);
219 max_x = max_x.max(panel.0);
220 min_y = min_y.min(panel.1);
221 max_y = max_y.max(panel.1);
222 }
223 // Print the panels.
224 for y in (min_y..=max_y).rev() {
225 for x in min_x..=max_x {
226 match *panels.get(&(x, y)).unwrap_or(&0) {
227 1 => print!("██"),
228 _ => print!(" "),
229 }
230 }
231 println!();
232 }
233 }