Day 17
This commit is contained in:
parent
8103dc20a5
commit
ca60c2b164
1 changed files with 248 additions and 0 deletions
248
src/bin/day17.rs
Normal file
248
src/bin/day17.rs
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
use std::time::Instant;
|
||||
|
||||
use aoc_2024::{load, print_res};
|
||||
use bstr::BString;
|
||||
use color_eyre::eyre::{bail, Context, OptionExt};
|
||||
use itertools::Itertools;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Computer {
|
||||
a: u64,
|
||||
b: u64,
|
||||
c: u64,
|
||||
|
||||
pc: usize,
|
||||
}
|
||||
|
||||
type Parsed = (Computer, Vec<u8>);
|
||||
|
||||
#[inline(never)]
|
||||
pub fn parsing(input: &BString) -> color_eyre::Result<Parsed> {
|
||||
let Some((regs, program)) = std::str::from_utf8(input)?.split_once("\n\n") else {
|
||||
bail!("Malformed input")
|
||||
};
|
||||
|
||||
let mut regs = regs.lines();
|
||||
let mut extract_reg = |name: char| -> color_eyre::Result<u64> {
|
||||
regs.next()
|
||||
.ok_or_eyre("Missing register")?
|
||||
.strip_prefix("Register ")
|
||||
.ok_or_eyre("Malformed prefix")?
|
||||
.strip_prefix(name)
|
||||
.ok_or_eyre("Malformed name")?
|
||||
.strip_prefix(": ")
|
||||
.ok_or_eyre("Malformed suffix")?
|
||||
.parse()
|
||||
.map_err(Into::into)
|
||||
};
|
||||
|
||||
let program = program
|
||||
.strip_prefix("Program: ")
|
||||
.ok_or_eyre("Malformed program")?
|
||||
.trim();
|
||||
|
||||
Ok((
|
||||
Computer {
|
||||
a: extract_reg('A').with_context(|| "Parsing A")?,
|
||||
b: extract_reg('B').with_context(|| "Parsing B")?,
|
||||
c: extract_reg('C').with_context(|| "Parsing C")?,
|
||||
|
||||
pc: 0,
|
||||
},
|
||||
program
|
||||
.split(',')
|
||||
.map(|n| {
|
||||
color_eyre::eyre::ensure!(
|
||||
n.len() == 1 && (b'0'..=b'7').contains(&n.as_bytes()[0]),
|
||||
"Instruction: {n}"
|
||||
);
|
||||
Ok(n.as_bytes()[0] - b'0')
|
||||
})
|
||||
.collect::<Result<_, _>>()?,
|
||||
))
|
||||
}
|
||||
|
||||
impl Computer {
|
||||
fn combo(&self, v: u8) -> u64 {
|
||||
match v {
|
||||
0..=3 => v as u64,
|
||||
4 => self.a,
|
||||
5 => self.b,
|
||||
6 => self.c,
|
||||
invalid => unreachable!("Invalid combo: {invalid}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, code: &[u8]) -> Vec<u8> {
|
||||
let mut output = Vec::new();
|
||||
while self.pc < code.len() {
|
||||
let operand = code[self.pc + 1];
|
||||
match code[self.pc] {
|
||||
// adv
|
||||
0 => {
|
||||
self.a >>= self.combo(operand);
|
||||
}
|
||||
// bxl
|
||||
1 => {
|
||||
self.b ^= operand as u64;
|
||||
}
|
||||
// bst
|
||||
2 => {
|
||||
self.b = self.combo(operand) % 8;
|
||||
}
|
||||
// jnz
|
||||
3 => {
|
||||
if self.a != 0 {
|
||||
self.pc = operand as usize;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// bxc
|
||||
4 => {
|
||||
self.b ^= self.c;
|
||||
}
|
||||
// out
|
||||
5 => {
|
||||
output.push((self.combo(operand) % 8) as u8);
|
||||
}
|
||||
// bdv
|
||||
6 => {
|
||||
self.b = self.a >> self.combo(operand);
|
||||
}
|
||||
// cdv
|
||||
7 => {
|
||||
self.c = self.a >> self.combo(operand);
|
||||
}
|
||||
invalid => unreachable!("Invalid opcode: {invalid}"),
|
||||
}
|
||||
|
||||
self.pc += 2;
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn part1((mut computer, code): Parsed) {
|
||||
print_res!(
|
||||
"Result: {}",
|
||||
computer.run(&code).iter().map(|s| s.to_string()).join(",")
|
||||
);
|
||||
}
|
||||
|
||||
fn display_code(code: &[u8]) {
|
||||
fn combo_display(v: u8) {
|
||||
match v {
|
||||
0..=3 => print!("{v}"),
|
||||
4 => print!("A"),
|
||||
5 => print!("B"),
|
||||
6 => print!("C"),
|
||||
_ => print!("<INVALID>"),
|
||||
}
|
||||
}
|
||||
|
||||
println!("Program:");
|
||||
for (i, chunk) in code.chunks_exact(2).enumerate() {
|
||||
print!(" [{:2}] ", i * 2);
|
||||
match chunk[0] {
|
||||
0 => {
|
||||
print!("A >>= ");
|
||||
combo_display(chunk[1]);
|
||||
}
|
||||
1 => {
|
||||
print!("B ^= {}", chunk[1]);
|
||||
}
|
||||
2 => {
|
||||
print!("B = ");
|
||||
combo_display(chunk[1]);
|
||||
print!(" % 8");
|
||||
}
|
||||
3 => {
|
||||
print!("A != 0 goto {}", chunk[1]);
|
||||
}
|
||||
4 => {
|
||||
print!("B ^= C");
|
||||
}
|
||||
5 => {
|
||||
print!("out(");
|
||||
combo_display(chunk[1]);
|
||||
print!(")");
|
||||
}
|
||||
6 => {
|
||||
print!("B = A >> ");
|
||||
combo_display(chunk[1]);
|
||||
}
|
||||
7 => {
|
||||
print!("C = A >> ");
|
||||
combo_display(chunk[1]);
|
||||
}
|
||||
_ => {
|
||||
print!("<INVALID>")
|
||||
}
|
||||
}
|
||||
println!()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn part2((computer, code): Parsed) {
|
||||
display_code(&code);
|
||||
|
||||
let mut possibles = vec![0u64];
|
||||
|
||||
for i in 0..code.len() {
|
||||
let mut new = Vec::new();
|
||||
|
||||
for a in possibles {
|
||||
for x in 0..=7 {
|
||||
let tentative = a | x << (i * 3);
|
||||
let mut computer = computer;
|
||||
computer.a = tentative;
|
||||
|
||||
let result = computer.run(&code);
|
||||
if i > 3 && (result.len() < i - 3 || result[0..(i - 3)] != code[0..(i - 3)]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
new.push(tentative);
|
||||
}
|
||||
}
|
||||
|
||||
possibles = new;
|
||||
}
|
||||
|
||||
for tentative in possibles {
|
||||
let mut computer = computer;
|
||||
computer.a = tentative;
|
||||
|
||||
let result = computer.run(&code);
|
||||
if result == code {
|
||||
print_res!("Value: {tentative}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
panic!("No solution found");
|
||||
}
|
||||
|
||||
pub fn main() -> color_eyre::Result<()> {
|
||||
let context = load()?;
|
||||
|
||||
let start = Instant::now();
|
||||
let parsed = parsing(&context.input)?;
|
||||
let elapsed = humantime::format_duration(start.elapsed());
|
||||
|
||||
let start = Instant::now();
|
||||
if context.part == 1 {
|
||||
part1(parsed);
|
||||
} else {
|
||||
part2(parsed);
|
||||
}
|
||||
let elapsed_part = humantime::format_duration(start.elapsed());
|
||||
|
||||
println!(" Parsing: {elapsed}");
|
||||
println!(" Solving: {elapsed_part}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue