Skip to content

Commit

Permalink
Year 2024, Day 17
Browse files Browse the repository at this point in the history
  • Loading branch information
kurisuke committed Dec 19, 2024
1 parent 13ff348 commit 5ab6f4b
Show file tree
Hide file tree
Showing 2 changed files with 237 additions and 0 deletions.
235 changes: 235 additions & 0 deletions year2024/src/day17.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
use std::{cmp::Reverse, collections::BinaryHeap};

use common::day::Day;
use itertools::Itertools;

pub struct Day17 {}

impl Day for Day17 {
fn star1(&self, input: &str) -> String {
let mut computer = Computer::parse(input);
computer.run_until_halt();
computer.output.iter().map(|s| s.to_string()).join(",")
}

fn star2(&self, input: &str) -> String {
let computer_orig = Computer::parse(input);
search(computer_orig).unwrap().to_string()
}
}

#[derive(Clone)]
struct Computer {
program: Vec<usize>,
pub reg: [usize; 3],
ip: usize,
pub output: Vec<usize>,
}

impl Computer {
#[cfg(test)]
pub fn new(program: Vec<usize>, reg: [usize; 3]) -> Self {
Self {
program,
reg,
ip: 0,
output: vec![],
}
}

#[allow(dead_code)]
pub fn disassembly(&self) {
let mut ip = 0;
while ip < self.program.len() {
let instr = match self.program[ip] {
0 => "ADV",
1 => "BXL",
2 => "BST",
3 => "JNZ",
4 => "BXC",
5 => "OUT",
6 => "BDV",
7 => "CDV",
_ => unreachable!(),
};

let operand = match self.program[ip] {
0 | 6 | 7 => combo_str(self.program[ip + 1]),
1 | 3 => self.program[ip + 1].to_string(),
2 | 5 => format!("{} mod 8", combo_str(self.program[ip + 1])),
4 => String::new(),
_ => unreachable!(),
};

println!("{:04}: {} {}", ip, instr, operand);
ip += 2;
}
}

pub fn parse(input: &str) -> Self {
let secs: Vec<_> = input.split("\n\n").collect();

let mut reg = [0; 3];
for (i, line) in secs[0].lines().enumerate() {
let reg_val = line.split(": ").nth(1).unwrap();
reg[i] = reg_val.parse().unwrap();
}

let program = secs[1].trim().split(": ").nth(1).unwrap();
let program = program.split(',').map(|n| n.parse().unwrap()).collect();

Self {
program,
reg,
ip: 0,
output: vec![],
}
}

pub fn run_until_halt(&mut self) {
loop {
if self.step() {
break;
}
}
}

pub fn step(&mut self) -> bool {
match self.program[self.ip] {
0 => {
// adv
self.reg[0] >>= self.combo();
self.ip += 2;
}
1 => {
// bxl
self.reg[1] ^= self.literal();
self.ip += 2;
}
2 => {
// bst
self.reg[1] = self.combo() % 8;
self.ip += 2;
}
3 => {
// jnz
if self.reg[0] == 0 {
self.ip += 2;
} else {
self.ip = self.literal();
}
}
4 => {
// bxc
self.reg[1] ^= self.reg[2];
self.ip += 2;
}
5 => {
// out
self.output.push(self.combo() % 8);
self.ip += 2;
}
6 => {
// bdv
self.reg[1] = self.reg[0] >> self.combo();
self.ip += 2;
}
7 => {
// cdv
self.reg[2] = self.reg[0] >> self.combo();
self.ip += 2;
}
_ => unreachable!(),
}

self.ip + 1 >= self.program.len()
}

fn literal(&self) -> usize {
self.program[self.ip + 1]
}

fn combo(&self) -> usize {
let v = self.program[self.ip + 1];
match v {
0..=3 => v,
4..=6 => self.reg[v - 4],
_ => unreachable!(),
}
}
}

fn combo_str(n: usize) -> String {
match n {
0..=3 => n.to_string(),
4 => "<regA>".to_owned(),
5 => "<regB>".to_owned(),
6 => "<regC>".to_owned(),
_ => unreachable!(),
}
}

fn search(computer_orig: Computer) -> Option<usize> {
let mut search_states = BinaryHeap::new();
search_states.push(Reverse(0));
while let Some(Reverse(a)) = search_states.pop() {
for a_add in 0..8 {
let a_new = a + a_add;
let mut computer = computer_orig.clone();
computer.reg[0] = a_new;
computer.run_until_halt();
// println!("{}: {:?}", a_new, computer.output);
if computer.program == computer.output {
return Some(a_new);
}

if computer.program[computer.program.len() - computer.output.len()..] == computer.output
{
search_states.push(Reverse(a_new << 3));
}
}
}

None
}

#[cfg(test)]
mod tests {
use super::*;

const INPUT1: &str = r#"Register A: 729
Register B: 0
Register C: 0
Program: 0,1,5,4,3,0"#;

#[test]
fn example_programs() {
let mut c = Computer::new(vec![2, 6], [0, 0, 9]);
c.run_until_halt();
assert_eq!(c.reg[1], 1);

let mut c = Computer::new(vec![5, 0, 5, 1, 5, 4], [10, 0, 0]);
c.run_until_halt();
assert_eq!(c.output, [0, 1, 2]);

let mut c = Computer::new(vec![0, 1, 5, 4, 3, 0], [2024, 0, 0]);
c.run_until_halt();
assert_eq!(c.output, [4, 2, 5, 6, 7, 7, 7, 7, 3, 1, 0]);
assert_eq!(c.reg[0], 0);

let mut c = Computer::new(vec![1, 7], [0, 29, 0]);
c.run_until_halt();
assert_eq!(c.reg[1], 26);

let mut c = Computer::new(vec![4, 0], [0, 2024, 43690]);
c.run_until_halt();
assert_eq!(c.reg[1], 44354);
}

#[test]
fn star1() {
let d = Day17 {};
assert_eq!(d.star1(INPUT1), "4,6,3,5,6,3,5,2,1,0");
}
}
2 changes: 2 additions & 0 deletions year2024/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod day13;
mod day14;
mod day15;
mod day16;
mod day17;

pub struct Year2024 {}

Expand All @@ -39,6 +40,7 @@ impl Year for Year2024 {
14 => Some(Box::new(day14::Day14 {})),
15 => Some(Box::new(day15::Day15 {})),
16 => Some(Box::new(day16::Day16 {})),
17 => Some(Box::new(day17::Day17 {})),
_ => None,
}
}
Expand Down

0 comments on commit 5ab6f4b

Please sign in to comment.