Skip to content

Commit 187025c

Browse files
committed
Day 17
1 parent 1b8ef9c commit 187025c

File tree

4 files changed

+169
-2
lines changed

4 files changed

+169
-2
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
4747
| [Day 13](./src/bin/13.rs) | `248.3µs` | `204.6µs` |
4848
| [Day 14](./src/bin/14.rs) | `46.0µs` | `47.2ms` |
4949
| [Day 15](./src/bin/15.rs) | `516.6µs` | `862.0µs` |
50-
| [Day 16](./src/bin/16.rs) | `2.5ms` | `22.2ms` |
50+
| [Day 16](./src/bin/16.rs) | `2.5ms` | `22.3ms` |
51+
| [Day 17](./src/bin/17.rs) | `1.2µs` | `42.4µs` |
5152

52-
**Total: 267.15ms**
53+
**Total: 267.25ms**
5354
<!--- benchmarking table --->
5455

5556
---

data/examples/17-1.txt

Lines changed: 5 additions & 0 deletions
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

data/examples/17-2.txt

Lines changed: 5 additions & 0 deletions
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

src/bin/17.rs

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
use itertools::Itertools;
2+
3+
advent_of_code::solution!(17);
4+
5+
const REGISTER_LEN: usize = "Register _: ".len();
6+
const PROGRAM_LEN: usize = "Program: ".len();
7+
8+
#[derive(Debug)]
9+
enum Instruction {
10+
Adv,
11+
Bxl,
12+
Bst,
13+
Jnz,
14+
Bxc,
15+
Out,
16+
Bdv,
17+
Cdv,
18+
}
19+
20+
impl From<u8> for Instruction {
21+
fn from(value: u8) -> Self {
22+
use Instruction::*;
23+
match value {
24+
0 => Adv,
25+
1 => Bxl,
26+
2 => Bst,
27+
3 => Jnz,
28+
4 => Bxc,
29+
5 => Out,
30+
6 => Bdv,
31+
7 => Cdv,
32+
_ => panic!(),
33+
}
34+
}
35+
}
36+
37+
impl Instruction {
38+
fn process(&self, computer: &mut Computer, operand: u8) -> bool {
39+
use Instruction::*;
40+
let literal = operand as u64;
41+
let combo = || match operand {
42+
0..=3 => operand as u64,
43+
4 => computer.register_a,
44+
5 => computer.register_b,
45+
6 => computer.register_c,
46+
_ => panic!(),
47+
};
48+
match self {
49+
Adv => computer.register_a /= 2u64.pow(combo() as u32),
50+
Bxl => computer.register_b ^= literal,
51+
Bst => computer.register_b = combo() & 0b111,
52+
Jnz => {
53+
if computer.register_a == 0 {
54+
return true;
55+
}
56+
computer.instruction_pointer = literal as u8;
57+
return false;
58+
},
59+
Bxc => computer.register_b ^= computer.register_c,
60+
Out => computer.output.push((combo() & 0b111) as u8),
61+
Bdv => computer.register_b = computer.register_a / 2u64.pow(combo() as u32),
62+
Cdv => computer.register_c = computer.register_a / 2u64.pow(combo() as u32),
63+
}
64+
true
65+
}
66+
}
67+
68+
#[derive(Debug, Default)]
69+
struct Computer {
70+
program: Vec<u8>,
71+
register_a: u64,
72+
register_b: u64,
73+
register_c: u64,
74+
instruction_pointer: u8,
75+
output: Vec<u8>,
76+
}
77+
78+
impl Computer {
79+
fn parse(input: &str) -> Self {
80+
let mut lines = input.lines();
81+
let register_a = lines.next().and_then(|line| line[REGISTER_LEN..].parse::<u64>().ok()).unwrap_or_default();
82+
let register_b = lines.next().and_then(|line| line[REGISTER_LEN..].parse::<u64>().ok()).unwrap_or_default();
83+
let register_c = lines.next().and_then(|line| line[REGISTER_LEN..].parse::<u64>().ok()).unwrap_or_default();
84+
lines.next();
85+
let program = lines.next().iter()
86+
.flat_map(|line| line[PROGRAM_LEN..].split(','))
87+
.flat_map(|number| number.parse::<u8>().ok())
88+
.collect_vec();
89+
Computer {
90+
program,
91+
register_a,
92+
register_b,
93+
register_c,
94+
..Computer::default()
95+
}
96+
}
97+
98+
fn execute(&mut self) {
99+
while self.instruction_pointer < self.program.len() as u8 {
100+
let pointer = self.instruction_pointer as usize;
101+
let (opcode, operand) = (self.program[pointer], self.program[pointer + 1]);
102+
let instruction = Instruction::from(opcode);
103+
if instruction.process(self, operand) {
104+
self.instruction_pointer += 2;
105+
}
106+
}
107+
}
108+
}
109+
110+
pub fn part_one(input: &str) -> Option<String> {
111+
let mut computer = Computer::parse(input);
112+
computer.execute();
113+
Some(computer.output.into_iter().join(","))
114+
}
115+
116+
pub fn part_two(input: &str) -> Option<u64> {
117+
let mut computer = Computer::parse(input);
118+
let mut register_a_candidate = 1;
119+
loop {
120+
computer.register_a = register_a_candidate;
121+
computer.register_b = 0;
122+
computer.register_b = 0;
123+
computer.instruction_pointer = 0;
124+
computer.output.clear();
125+
computer.execute();
126+
if computer.program.ends_with(&computer.output) {
127+
if computer.output.len() == computer.program.len() {
128+
break;
129+
}
130+
register_a_candidate <<= 3;
131+
continue
132+
}
133+
while register_a_candidate & 0b111 == 0b111 {
134+
register_a_candidate >>= 3;
135+
}
136+
register_a_candidate += 1;
137+
};
138+
Some(register_a_candidate)
139+
}
140+
141+
#[cfg(test)]
142+
mod tests {
143+
use super::*;
144+
145+
#[test]
146+
fn test_part_one() {
147+
let result = part_one(&advent_of_code::template::read_file_part("examples", DAY, 1));
148+
assert_eq!(result, Some(String::from("4,6,3,5,6,3,5,2,1,0")));
149+
}
150+
151+
#[test]
152+
fn test_part_two() {
153+
let result = part_two(&advent_of_code::template::read_file_part("examples", DAY, 2));
154+
assert_eq!(result, Some(117440));
155+
}
156+
}

0 commit comments

Comments
 (0)