Skip to content

Commit 67679b5

Browse files
committed
Day 17 done
1 parent 9b51a7d commit 67679b5

File tree

5 files changed

+319
-3
lines changed

5 files changed

+319
-3
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
1010

1111
| Year | Completed |
1212
| :---: | :---: |
13-
| [2024](aoc2024) | 30/50 |
13+
| [2024](aoc2024) | 34/50 |
1414
| [2023](aoc2023) | 50/50 |
1515

1616
---

aoc2024/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
2121
| [Day 13](https://adventofcode.com/2024/day/13) | [code](src/bin/13.rs) |||
2222
| [Day 14](https://adventofcode.com/2024/day/14) | [code](src/bin/14.rs) |||
2323
| [Day 15](https://adventofcode.com/2024/day/15) | [code](src/bin/15.rs) |||
24-
| [Day 16](https://adventofcode.com/2024/day/16) | [code](src/bin/16.rs) | _ | _ |
25-
| [Day 17](https://adventofcode.com/2024/day/17) | [code](src/bin/17.rs) | _ | _ |
24+
| [Day 16](https://adventofcode.com/2024/day/16) | [code](src/bin/16.rs) | | |
25+
| [Day 17](https://adventofcode.com/2024/day/17) | [code](src/bin/17.rs) | | |
2626
| [Day 18](https://adventofcode.com/2024/day/18) | [code](src/bin/18.rs) | _ | _ |
2727
| [Day 19](https://adventofcode.com/2024/day/19) | [code](src/bin/19.rs) | _ | _ |
2828
| [Day 20](https://adventofcode.com/2024/day/20) | [code](src/bin/20.rs) | _ | _ |

aoc2024/data/examples/17-2.txt

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Register A: 2024
2+
Register B: 0
3+
Register C: 0
4+
5+
Program: 0,3,5,4,3,0

aoc2024/data/examples/17.txt

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Register A: 729
2+
Register B: 0
3+
Register C: 0
4+
5+
Program: 0,1,5,4,3,0

aoc2024/src/bin/17.rs

+306
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
use std::ops::BitXor;
2+
3+
use aoc_utils::*;
4+
use itertools::Itertools;
5+
6+
advent_of_code::solution!(17);
7+
8+
struct Cpu {
9+
program: Vec<Op>,
10+
pointer: usize,
11+
output: Vec<u64>,
12+
registers: [u64; 3],
13+
}
14+
15+
impl Cpu {
16+
fn run_program(mut self) -> Vec<u64> {
17+
while self.pointer < self.program.len() {
18+
let op = self.program[self.pointer];
19+
self.pointer += op.run(&mut self);
20+
}
21+
self.output
22+
}
23+
}
24+
25+
#[derive(Debug, Clone, Copy)]
26+
enum Reg {
27+
A,
28+
B,
29+
C,
30+
}
31+
32+
impl Reg {
33+
fn index(&self) -> usize {
34+
match self {
35+
Reg::A => 0,
36+
Reg::B => 1,
37+
Reg::C => 2,
38+
}
39+
}
40+
}
41+
42+
#[derive(Debug, Clone, Copy)]
43+
enum Op {
44+
Adv(COp), // 0 - divide A by COp ^ 2 > A
45+
Bxl(LitOp), // 1 - bitwise XOR on B by LitOp > B
46+
Bst(COp), // 2 - COp % 8 > B
47+
Jnz(LitOp), // 3 - nothing if A == 0, else jumpt pointer to A
48+
Bxc, // 4 - bitwise XOR of B & C > B
49+
Out(COp), // 5 - COp % 8 > output
50+
Bdv(COp), // 6 - divide A by COp ^ 2 > B
51+
Cdv(COp), // 7 - divide A by COp ^ 2 > C
52+
}
53+
54+
impl Op {
55+
fn from_chars(opcode: char, operand: char) -> Self {
56+
match opcode {
57+
'0' => Op::Adv(COp::from_char(operand)),
58+
'1' => Op::Bxl(LitOp::from_char(operand)),
59+
'2' => Op::Bst(COp::from_char(operand)),
60+
'3' => Op::Jnz(LitOp::from_char(operand)),
61+
'4' => Op::Bxc,
62+
'5' => Op::Out(COp::from_char(operand)),
63+
'6' => Op::Bdv(COp::from_char(operand)),
64+
'7' => Op::Cdv(COp::from_char(operand)),
65+
_ => panic!("unknown combo op"),
66+
}
67+
}
68+
69+
fn run(&self, cpu: &mut Cpu) -> usize {
70+
match self {
71+
Op::Adv(cop) => {
72+
let a_ind = Reg::A.index();
73+
let a = cpu.registers[a_ind];
74+
let x = match cop {
75+
COp::Num(n) => 2_u64.pow(*n as u32),
76+
COp::Reg(reg) => 2_u64.pow(cpu.registers[reg.index()] as u32),
77+
};
78+
cpu.registers[a_ind] = a / x;
79+
1
80+
}
81+
Op::Bxl(lit_op) => {
82+
let b_ind = Reg::B.index();
83+
let b = cpu.registers[b_ind];
84+
let x = b.bitxor(lit_op.0 as u64);
85+
cpu.registers[b_ind] = x;
86+
1
87+
}
88+
Op::Bst(cop) => {
89+
let b_ind = Reg::B.index();
90+
let x = match cop {
91+
COp::Num(n) => (*n as u64) % 8,
92+
COp::Reg(reg) => cpu.registers[reg.index()] % 8,
93+
};
94+
cpu.registers[b_ind] = x;
95+
1
96+
}
97+
Op::Jnz(lit_op) => {
98+
let a_ind = Reg::A.index();
99+
let a = cpu.registers[a_ind];
100+
if a != 0 {
101+
cpu.pointer = (lit_op.0 / 2) as usize;
102+
0
103+
} else {
104+
1
105+
}
106+
}
107+
Op::Bxc => {
108+
let b_ind = Reg::B.index();
109+
let c_ind = Reg::C.index();
110+
let b = cpu.registers[b_ind];
111+
let c = cpu.registers[c_ind];
112+
let x = b.bitxor(c);
113+
cpu.registers[b_ind] = x;
114+
1
115+
}
116+
Op::Out(cop) => {
117+
let x = match cop {
118+
COp::Num(n) => (*n as u64) % 8,
119+
COp::Reg(reg) => cpu.registers[reg.index()] % 8,
120+
};
121+
cpu.output.push(x);
122+
1
123+
}
124+
Op::Bdv(cop) => {
125+
let b_ind = Reg::A.index();
126+
let a_ind = Reg::A.index();
127+
let a = cpu.registers[a_ind];
128+
let x = match cop {
129+
COp::Num(n) => 2_u64.pow(*n as u32),
130+
COp::Reg(reg) => 2_u64.pow(cpu.registers[reg.index()] as u32),
131+
};
132+
cpu.registers[b_ind] = a / x;
133+
1
134+
}
135+
Op::Cdv(cop) => {
136+
let c_ind = Reg::C.index();
137+
let a_ind = Reg::A.index();
138+
let a = cpu.registers[a_ind];
139+
let x = match cop {
140+
COp::Num(n) => 2_u64.pow(*n as u32),
141+
COp::Reg(reg) => 2_u64.pow(cpu.registers[reg.index()] as u32),
142+
};
143+
cpu.registers[c_ind] = a / x;
144+
1
145+
}
146+
}
147+
}
148+
}
149+
150+
#[derive(Debug, Clone, Copy)]
151+
struct LitOp(u8);
152+
153+
impl LitOp {
154+
fn from_char(c: char) -> Self {
155+
match c {
156+
'0' => LitOp(0),
157+
'1' => LitOp(1),
158+
'2' => LitOp(2),
159+
'3' => LitOp(3),
160+
'4' => LitOp(4),
161+
'5' => LitOp(5),
162+
'6' => LitOp(6),
163+
'7' => LitOp(7),
164+
_ => panic!("unknown combo op"),
165+
}
166+
}
167+
}
168+
169+
#[derive(Debug, Clone, Copy)]
170+
enum COp {
171+
Num(u8),
172+
Reg(Reg),
173+
}
174+
175+
impl COp {
176+
fn from_char(c: char) -> Self {
177+
match c {
178+
'0' => COp::Num(0),
179+
'1' => COp::Num(1),
180+
'2' => COp::Num(2),
181+
'3' => COp::Num(3),
182+
'4' => COp::Reg(Reg::A),
183+
'5' => COp::Reg(Reg::B),
184+
'6' => COp::Reg(Reg::C),
185+
_ => panic!("unknown combo op"),
186+
}
187+
}
188+
}
189+
190+
pub fn part_one(input: &str) -> Option<String> {
191+
let parts = input.blocks();
192+
let registers = parts[0]
193+
.lines()
194+
.map(|l| {
195+
l.split_once(": ")
196+
.expect("Should parse register")
197+
.1
198+
.parse::<u64>()
199+
.expect("Register should contain number")
200+
})
201+
.collect_vec();
202+
let program = parts[1]
203+
.split_once(": ")
204+
.expect("Program should parse")
205+
.1
206+
.split(",")
207+
.map(|s| s.chars().next().expect("Splist shoudl be chars"))
208+
.collect_vec();
209+
let program = program
210+
.chunks(2)
211+
.map(|cs| Op::from_chars(cs[0], cs[1]))
212+
.collect_vec();
213+
let cpu = Cpu {
214+
program,
215+
pointer: 0,
216+
output: Vec::new(),
217+
registers: [registers[0], registers[1], registers[2]],
218+
};
219+
let res = cpu.run_program();
220+
Some(res.into_iter().join(","))
221+
}
222+
223+
fn find_initial_a<F>(curr: u64, index: usize, run: F, target: &[u64]) -> Option<u64>
224+
where
225+
F: Fn(u64) -> Vec<u64> + Clone,
226+
{
227+
if index == 0 {
228+
return Some(curr);
229+
}
230+
let next_index = index - 1;
231+
// check for each 3 bit value
232+
for n in 0..8 {
233+
// the program itself divides A by 8 every loop
234+
let new_curr = curr * 8 + n;
235+
let res = run(new_curr);
236+
if res[0] == target[next_index] {
237+
// if we find match at the target position, try for next position with value
238+
let new_val = find_initial_a(new_curr, next_index, run.clone(), target);
239+
if let Some(n) = new_val {
240+
return Some(n);
241+
}
242+
}
243+
}
244+
// match not found
245+
None
246+
}
247+
248+
pub fn part_two(input: &str) -> Option<u64> {
249+
let parts = input.blocks();
250+
let registers = parts[0]
251+
.lines()
252+
.map(|l| {
253+
l.split_once(": ")
254+
.expect("Should parse register")
255+
.1
256+
.parse::<u64>()
257+
.expect("Register should contain number")
258+
})
259+
.collect_vec();
260+
let program_str = parts[1]
261+
.split_once(": ")
262+
.expect("Program should parse")
263+
.1
264+
.trim();
265+
let program_literals = program_str
266+
.split(",")
267+
.map(|s| s.parse::<u64>().expect("Should parse literals"))
268+
.collect_vec();
269+
let program = program_str
270+
.split(",")
271+
.map(|s| s.chars().next().expect("Splits should be chars"))
272+
.collect_vec();
273+
let program = program
274+
.chunks(2)
275+
.map(|cs| Op::from_chars(cs[0], cs[1]))
276+
.collect_vec();
277+
let run = move |new_a: u64| {
278+
let cpu = Cpu {
279+
program: program.clone(),
280+
pointer: 0,
281+
output: Vec::new(),
282+
registers: [new_a, registers[1], registers[2]],
283+
};
284+
cpu.run_program()
285+
};
286+
find_initial_a(0, program_literals.len(), run, &program_literals)
287+
}
288+
289+
#[cfg(test)]
290+
mod tests {
291+
use super::*;
292+
293+
#[test]
294+
fn test_part_one() {
295+
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
296+
assert_eq!(result, Some("4,6,3,5,6,3,5,2,1,0".to_string()));
297+
}
298+
299+
#[test]
300+
fn test_part_two() {
301+
let result = part_two(&advent_of_code::template::read_file_part(
302+
"examples", DAY, 2,
303+
));
304+
assert_eq!(result, Some(117440));
305+
}
306+
}

0 commit comments

Comments
 (0)