From e77e9ba766a983bce9e5c1c44ed4b93a1191198a Mon Sep 17 00:00:00 2001 From: red Date: Fri, 22 Mar 2024 01:15:48 +0100 Subject: [PATCH] Year 2016: Day 23 --- CHANGELOG.md | 5 ++++ Cargo.toml | 2 +- NOTES_2016.md | 4 +++ README.md | 2 +- benches/year_2016.rs | 16 +++++++++++- inputs/year_2016/day_23_input | 26 +++++++++++++++++++ src/year_2016.rs | 10 ++++++++ src/year_2016/assembunny.rs | 47 +++++++++++++++++++++++++++++------ src/year_2016/day_12.rs | 6 ++--- src/year_2016/day_23.rs | 46 ++++++++++++++++++++++++++++++++++ 10 files changed, 150 insertions(+), 14 deletions(-) create mode 100644 inputs/year_2016/day_23_input create mode 100644 src/year_2016/day_23.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ae8d64..cf17e20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,11 @@ Of note: - The changelog 2015.5.2 has been rewritten from each commit content. - This file may be amended entirely in the future to adhere to the [GNU Changelog style](https://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html#Style-of-Change-Logs) +## [2016.23.1] +### Added +- Solved [exercice for 2016, day 23](src/year_2016/23.rs). + + ## [2016.22.1] ### Added - Solved [exercice for 2016, day 22](src/year_2016/22.rs). diff --git a/Cargo.toml b/Cargo.toml index 56aee5f..cc6578c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "advent-rs" -version = "2016.22.1" +version = "2016.23.1" edition = "2021" authors = ["Arnaud 'red' Rouyer"] readme = "README.md" diff --git a/NOTES_2016.md b/NOTES_2016.md index 8d09c72..6e9e39d 100644 --- a/NOTES_2016.md +++ b/NOTES_2016.md @@ -102,3 +102,7 @@ I love interpreters. ## Day 22: Grid Computing So far, my [BreadthFirstSearch library](src/bfs.rs) has saved me from rewriting a BFS from scratch twice. + +## Day 23: Safe Cracking + +I had forgotten [the original was taking ten minutes to run](https://github.com/joshleaves/advent-rb/NOTES_2016.md#day-23-safe-cracking). This one takes only seven seconds, and I'll be happy with it. diff --git a/README.md b/README.md index 235790e..1fcc281 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ As I said, [Consistency is hard](https://github.com/joshleaves/advent-rb), and I I'm also adding notes that may be useful if you're (like me) discovering Rust: - [2015, complete!](NOTES_2015.md) -- [2016, up to day 22](NOTES_2016.md) +- [2016, up to day 23](NOTES_2016.md) # Regarding style rules I'm gonna use a mix of what `cargo fmt` does, with some stuff that feels more natural to me. diff --git a/benches/year_2016.rs b/benches/year_2016.rs index ed10413..d355150 100644 --- a/benches/year_2016.rs +++ b/benches/year_2016.rs @@ -20,6 +20,7 @@ use advent_rs::year_2016::day_19; use advent_rs::year_2016::day_20; use advent_rs::year_2016::day_21; use advent_rs::year_2016::day_22; +use advent_rs::year_2016::day_23; use criterion::{black_box, criterion_group, criterion_main, Criterion}; fn year_2016_day_01(c: &mut Criterion) { @@ -286,6 +287,18 @@ fn year_2016_day_22(c: &mut Criterion) { g2016_day_22.finish(); } +fn year_2016_day_23(c: &mut Criterion) { + let mut g2016_day_23 = c.benchmark_group("year_2016::day_23"); + let input_year_2016_day_23 = include_str!("../inputs/year_2016/day_23_input"); + g2016_day_23.bench_function("year_2016::day_23_v1", |b| { + b.iter(|| day_23::day_23_v1(black_box(input_year_2016_day_23))) + }); + g2016_day_23.bench_function("year_2016::day_23_v2", |b| { + b.iter(|| day_23::day_23_v2(black_box(input_year_2016_day_23))) + }); + g2016_day_23.finish(); +} + criterion_group!( benches, year_2016_day_01, @@ -309,6 +322,7 @@ criterion_group!( year_2016_day_19, year_2016_day_20, year_2016_day_21, - year_2016_day_22 + year_2016_day_22, + year_2016_day_23 ); criterion_main!(benches); diff --git a/inputs/year_2016/day_23_input b/inputs/year_2016/day_23_input new file mode 100644 index 0000000..c6257db --- /dev/null +++ b/inputs/year_2016/day_23_input @@ -0,0 +1,26 @@ +cpy a b +dec b +cpy a d +cpy 0 a +cpy b c +inc a +dec c +jnz c -2 +dec d +jnz d -5 +dec b +cpy b c +cpy c d +dec d +inc c +jnz d -2 +tgl c +cpy -16 c +jnz 1 c +cpy 96 c +jnz 91 d +inc a +inc d +jnz d -2 +inc c +jnz c -5 \ No newline at end of file diff --git a/src/year_2016.rs b/src/year_2016.rs index cf0eb2b..a1254c5 100644 --- a/src/year_2016.rs +++ b/src/year_2016.rs @@ -25,6 +25,7 @@ pub mod day_19; pub mod day_20; pub mod day_21; pub mod day_22; +pub mod day_23; pub fn solve(day: u8, part: u8, input: impl Into) -> Option { if part != 1 && part != 2 { @@ -54,6 +55,7 @@ pub fn solve(day: u8, part: u8, input: impl Into) -> Option { 20 => Some(format!("{}", day_20::day_20(part, input))), 21 => Some(format!("{}", day_21::day_21(part, input))), 22 => Some(format!("{}", day_22::day_22(part, input))), + 23 => Some(format!("{}", day_23::day_23(part, input))), _ => None, } } @@ -224,4 +226,12 @@ mod tests { assert_eq!(day_22::day_22_v1(input), 955); assert_eq!(day_22::day_22_v2(input), 246); } + + #[test] + #[ignore = "Too slow for CI"] + fn day_23() { + let input = include_str!("../inputs/year_2016/day_23_input"); + assert_eq!(day_23::day_23_v1(input), 13_776); + assert_eq!(day_23::day_23_v2(input), 479_010_336); + } } diff --git a/src/year_2016/assembunny.rs b/src/year_2016/assembunny.rs index 7f9f10a..56d5d41 100644 --- a/src/year_2016/assembunny.rs +++ b/src/year_2016/assembunny.rs @@ -1,4 +1,4 @@ -type Value = u64; +type Value = i64; type Register = u8; #[derive(Debug, Copy, Clone)] @@ -25,24 +25,27 @@ enum Instruction { // dec x decreases the value of register x by one. Decrement(Register), // jnz x y jumps to an instruction y away (positive means forward; negative means backward), but only if x is not zero. - JumpNotZero(ValueOrRegister, i8), + JumpNotZero(ValueOrRegister, ValueOrRegister), + // tgl x toggles the instruction x away (pointing at instructions like jnz does: positive means forward; negative means backward): + Toggle(Register), + Nop(), } pub struct Assembunny { pc: usize, - pub registers: [u64; 4], + pub registers: [Value; 4], instructions: Vec, } impl Assembunny { - const fn value_of(&self, item: ValueOrRegister) -> u64 { + const fn value_of(&self, item: ValueOrRegister) -> Value { match item { ValueOrRegister::Value(value) => value, ValueOrRegister::Register(register_id) => self.registers[register_id as usize], } } - pub fn set_register(&mut self, register: &str, value: u64) { + pub fn set_register(&mut self, register: &str, value: Value) { let reg_id = Self::register(register); self.registers[reg_id as usize] = value; } @@ -64,12 +67,35 @@ impl Assembunny { } Instruction::JumpNotZero(vor, offset) => { let value = self.value_of(*vor); + let offset = self.value_of(*offset); if value != 0 { - self.pc = ((self.pc as i8) + offset) as usize; + self.pc = ((self.pc as i64) + offset) as usize; } else { self.pc += 1; } } + Instruction::Toggle(register) => { + let target = (self.pc as Value + self.registers[*register as usize]) as usize; + if target >= self.instructions.len() { + self.pc += 1; + continue; + } + self.instructions[target] = match self.instructions[target] { + Instruction::Copy(vor, register) => { + Instruction::JumpNotZero(vor, ValueOrRegister::Register(register)) + } + Instruction::Increment(register) => Instruction::Decrement(register), + Instruction::Decrement(register) => Instruction::Increment(register), + Instruction::JumpNotZero(vor, offset) => match offset { + ValueOrRegister::Register(register) => Instruction::Copy(vor, register), + _ => Instruction::Nop(), + }, + Instruction::Toggle(offset) => Instruction::Increment(offset as u8), + Instruction::Nop() => Instruction::Nop(), + }; + self.pc += 1; + } + Instruction::Nop() => {} } } } @@ -108,15 +134,20 @@ impl Assembunny { // jnz x y jumps to an instruction y away (positive means forward; negative means backward), but only if x is not zero. "jnz" => { let vor = ValueOrRegister::parse(parts[1]); - let offset = parts[2].parse::().unwrap(); + let offset = ValueOrRegister::parse(parts[2]); instructions.push(Instruction::JumpNotZero(vor, offset)); } + // tgl x toggles the instruction x away (pointing at instructions like jnz does: positive means forward; negative means backward): + "tgl" => { + let register = Self::register(parts[1]); + instructions.push(Instruction::Toggle(register)); + } _ => panic!("Invalid instruction: {}", line), } } Assembunny { pc: 0, - registers: [0u64; 4], + registers: [0; 4], instructions: instructions, } } diff --git a/src/year_2016/day_12.rs b/src/year_2016/day_12.rs index b81f25f..325fee0 100644 --- a/src/year_2016/day_12.rs +++ b/src/year_2016/day_12.rs @@ -1,12 +1,12 @@ use super::assembunny::Assembunny; -pub fn day_12_v1(input: impl Into) -> u64 { +pub fn day_12_v1(input: impl Into) -> i64 { let mut bunny: Assembunny = Assembunny::from_input(&input.into()); bunny.run(); bunny.registers[0] } -pub fn day_12_v2(input: impl Into) -> u64 { +pub fn day_12_v2(input: impl Into) -> i64 { let mut bunny: Assembunny = Assembunny::from_input(&input.into()); bunny.set_register("c", 1); bunny.run(); @@ -14,7 +14,7 @@ pub fn day_12_v2(input: impl Into) -> u64 { bunny.registers[0] } -solvable!(day_12, day_12_v1, day_12_v2, u64); +solvable!(day_12, day_12_v1, day_12_v2, i64); #[cfg(test)] mod tests { diff --git a/src/year_2016/day_23.rs b/src/year_2016/day_23.rs new file mode 100644 index 0000000..408fc39 --- /dev/null +++ b/src/year_2016/day_23.rs @@ -0,0 +1,46 @@ +use super::assembunny::Assembunny; + +// pub fn day_12_v1(input: impl Into) -> i64 { +// } +// pub fn day_12_v2(input: impl Into) -> i64 { +// let mut bunny: Assembunny = Assembunny::from_input(&input.into()); +// bunny.set_register("c", 1); +// bunny.run(); + +// bunny.registers[0] +// } + +pub fn day_23_v1(input: impl Into) -> i64 { + let mut bunny: Assembunny = Assembunny::from_input(&input.into()); + bunny.set_register("a", 7); + bunny.run(); + + bunny.registers[0] +} + +pub fn day_23_v2(input: impl Into) -> i64 { + let mut bunny: Assembunny = Assembunny::from_input(&input.into()); + bunny.set_register("a", 12); + bunny.run(); + + bunny.registers[0] +} + +solvable!(day_23, day_23_v1, day_23_v2, i64); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn works_with_samples_v1() { + let sample_one = "cpy 2 a\n\ + tgl a\n\ + tgl a\n\ + tgl a\n\ + cpy 1 a\n\ + dec a\n\ + dec a"; + assert_eq!(day_23_v1(sample_one), 3); + } +}