From fe6568c121d2c19f1a624594ee2146f868a68852 Mon Sep 17 00:00:00 2001 From: Daniel Lin Date: Tue, 17 Dec 2024 22:12:58 -0500 Subject: [PATCH] Day 17: Chronospatial Computer --- README.md | 2 +- rs/benches/criterion.rs | 10 +++- rs/src/day17.rs | 113 ++++++++++++++++++++++++++++++++++++++++ rs/src/lib.rs | 1 + rs/src/main.rs | 12 ++++- 5 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 rs/src/day17.rs diff --git a/README.md b/README.md index eb6e561..17df9f8 100644 --- a/README.md +++ b/README.md @@ -21,4 +21,4 @@ Development occurs in language-specific directories: |[Day14.hs](hs/src/Day14.hs)|[Day14.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day14.kt)|[day14.py](py/aoc2024/day14.py)|[day14.rs](rs/src/day14.rs)| |[Day15.hs](hs/src/Day15.hs)|[Day15.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day15.kt)|[day15.py](py/aoc2024/day15.py)|[day15.rs](rs/src/day15.rs)| |[Day16.hs](hs/src/Day16.hs)|[Day16.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day16.kt)|[day16.py](py/aoc2024/day16.py)|[day16.rs](rs/src/day16.rs)| -|[Day17.hs](hs/src/Day17.hs)|[Day17.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day17.kt)|[day17.py](py/aoc2024/day17.py)|| +|[Day17.hs](hs/src/Day17.hs)|[Day17.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day17.kt)|[day17.py](py/aoc2024/day17.py)|[day17.rs](rs/src/day17.rs)| diff --git a/rs/benches/criterion.rs b/rs/benches/criterion.rs index 93c123f..7fb38ed 100644 --- a/rs/benches/criterion.rs +++ b/rs/benches/criterion.rs @@ -1,6 +1,6 @@ use aoc2024::{ - day1, day10, day11, day12, day13, day14, day15, day16, day2, day3, day4, day5, day6, day7, - day8, day9, + day1, day10, day11, day12, day13, day14, day15, day16, day17, day2, day3, day4, day5, day6, + day7, day8, day9, }; use criterion::{black_box, Criterion}; use std::env; @@ -113,6 +113,12 @@ fn aoc2024_bench(c: &mut Criterion) -> io::Result<()> { g.bench_function("part 2", |b| b.iter(|| day16::part2(black_box(&data)))); g.finish(); + let data = get_day_input(17)?; + let mut g = c.benchmark_group("day 17"); + g.bench_function("part 1", |b| b.iter(|| day17::part1(black_box(&data)))); + g.bench_function("part 2", |b| b.iter(|| day17::part2(black_box(&data)))); + g.finish(); + Ok(()) } diff --git a/rs/src/day17.rs b/rs/src/day17.rs new file mode 100644 index 0000000..bee2b8c --- /dev/null +++ b/rs/src/day17.rs @@ -0,0 +1,113 @@ +use itertools::Itertools; + +fn parse(data: &str) -> Option<(usize, usize, usize, Vec)> { + let mut iter = data.lines(); + let a = iter.next()?.strip_prefix("Register A: ")?.parse().ok()?; + let b = iter.next()?.strip_prefix("Register B: ")?.parse().ok()?; + let c = iter.next()?.strip_prefix("Register C: ")?.parse().ok()?; + let program = iter + .find(|line| !line.is_empty())? + .strip_prefix("Program: ")? + .split(',') + .map(|s| s.parse()) + .collect::>() + .ok()?; + Some((a, b, c, program)) +} + +fn run(mut a: usize, mut b: usize, mut c: usize, program: &[usize]) -> Option> { + let mut outputs = vec![]; + let mut ip = 0; + while let Some((instruction, operand)) = program.get(ip).zip(program.get(ip + 1)) { + let combo = match operand { + 0..=3 => *operand, + 4 => a, + 5 => b, + 6 => c, + _ => return None, + }; + match instruction { + 0 => a >>= combo, + 1 => b ^= operand, + 2 => b = combo & 7, + 3 => { + if a != 0 { + ip = *operand; + continue; + } + } + 4 => b ^= c, + 5 => outputs.push(combo & 7), + 6 => b = a >> combo, + 7 => c = a >> combo, + _ => return None, + } + ip += 2; + } + Some(outputs) +} + +pub fn part1(data: &str) -> Option { + let (a, b, c, program) = parse(data)?; + Some( + run(a, b, c, &program)? + .into_iter() + .map(|num| num.to_string()) + .join(","), + ) +} + +pub fn part2(data: &str) -> Option { + let (_, b, c, program) = parse(data)?; + let mut candidates = vec![0]; + while !candidates.is_empty() { + let mut next = vec![]; + for base in candidates { + for i in 0..=7 { + let a = 8 * base + i; + let output = run(a, b, c, &program)?; + if output == program { + return Some(a); + } + if program.ends_with(&output) { + next.push(a); + } + } + } + candidates = next; + } + None +} + +#[cfg(test)] +mod tests { + use super::*; + use indoc::indoc; + use pretty_assertions::assert_eq; + + static EXAMPLE_1: &str = indoc! {" + Register A: 729 + Register B: 0 + Register C: 0 + + Program: 0,1,5,4,3,0 + "}; + + static EXAMPLE_2: &str = indoc! {" + Register A: 2024 + Register B: 0 + Register C: 0 + + Program: 0,3,5,4,3,0 + "}; + + #[test] + fn part1_examples() { + assert_eq!(Some("4,6,3,5,6,3,5,2,1,0".to_string()), part1(EXAMPLE_1)); + } + + #[test] + fn part2_examples() { + assert_eq!(Some(117440), part2(EXAMPLE_2)); + } +} diff --git a/rs/src/lib.rs b/rs/src/lib.rs index e3e48ea..bbabdce 100644 --- a/rs/src/lib.rs +++ b/rs/src/lib.rs @@ -6,6 +6,7 @@ pub mod day13; pub mod day14; pub mod day15; pub mod day16; +pub mod day17; pub mod day2; pub mod day3; pub mod day4; diff --git a/rs/src/main.rs b/rs/src/main.rs index fe54205..e1bc29b 100644 --- a/rs/src/main.rs +++ b/rs/src/main.rs @@ -1,7 +1,7 @@ use anyhow::anyhow; use aoc2024::{ - day1, day10, day11, day12, day13, day14, day15, day16, day2, day3, day4, day5, day6, day7, - day8, day9, + day1, day10, day11, day12, day13, day14, day15, day16, day17, day2, day3, day4, day5, day6, + day7, day8, day9, }; use std::collections::HashSet; use std::env; @@ -148,5 +148,13 @@ fn main() -> anyhow::Result<()> { println!(); } + if args.is_empty() || args.contains("17") { + println!("Day 17"); + let data = get_day_input(17)?; + println!("{}", day17::part1(&data).ok_or(anyhow!("None"))?); + println!("{:?}", day17::part2(&data).ok_or(anyhow!("None"))?); + println!(); + } + Ok(()) }