From 71e6b6179314a2646ed7569835556cacf764df6a Mon Sep 17 00:00:00 2001 From: red Date: Thu, 21 Mar 2024 23:41:43 +0100 Subject: [PATCH] Year 2016: Day 21 --- CHANGELOG.md | 4 + Cargo.toml | 2 +- NOTES_2016.md | 4 + README.md | 2 +- benches/year_2016.rs | 16 +++- inputs/year_2016/day_21_input | 100 +++++++++++++++++++ src/year_2016.rs | 8 ++ src/year_2016/day_21.rs | 176 ++++++++++++++++++++++++++++++++++ 8 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 inputs/year_2016/day_21_input create mode 100644 src/year_2016/day_21.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ddd47e..6f329fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ 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.21.1] +### Added +- Solved [exercice for 2016, day 21](src/year_2016/21.rs). + ## [2016.20.1] ### Added - Solved [exercice for 2016, day 20](src/year_2016/20.rs). diff --git a/Cargo.toml b/Cargo.toml index 95559b9..0f9c682 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "advent-rs" -version = "2016.20.1" +version = "2016.21.1" edition = "2021" authors = ["Arnaud 'red' Rouyer"] readme = "README.md" diff --git a/NOTES_2016.md b/NOTES_2016.md index 89a6068..f7a57f7 100644 --- a/NOTES_2016.md +++ b/NOTES_2016.md @@ -94,3 +94,7 @@ As easy in Rust as it was in Ruby. ## Day 20: Firewall Rules One of Rust's biggest pleasure is code that compiles on the first try. + +## Day 21: Scrambled Letters and Hash + +I love interpreters. diff --git a/README.md b/README.md index 2a2aabc..cf3dd7c 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 20](NOTES_2016.md) +- [2016, up to day 21](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 d7a68cd..00d02bc 100644 --- a/benches/year_2016.rs +++ b/benches/year_2016.rs @@ -18,6 +18,7 @@ use advent_rs::year_2016::day_17; use advent_rs::year_2016::day_18; use advent_rs::year_2016::day_19; use advent_rs::year_2016::day_20; +use advent_rs::year_2016::day_21; use criterion::{black_box, criterion_group, criterion_main, Criterion}; fn year_2016_day_01(c: &mut Criterion) { @@ -260,6 +261,18 @@ fn year_2016_day_20(c: &mut Criterion) { g2016_day_20.finish(); } +fn year_2016_day_21(c: &mut Criterion) { + let mut g2016_day_21 = c.benchmark_group("year_2016::day_21"); + let input_year_2016_day_21 = include_str!("../inputs/year_2016/day_21_input"); + g2016_day_21.bench_function("year_2016::day_21_v1", |b| { + b.iter(|| day_21::day_21_v1(black_box(input_year_2016_day_21))) + }); + g2016_day_21.bench_function("year_2016::day_21_v2", |b| { + b.iter(|| day_21::day_21_v2(black_box(input_year_2016_day_21))) + }); + g2016_day_21.finish(); +} + criterion_group!( benches, year_2016_day_01, @@ -281,6 +294,7 @@ criterion_group!( year_2016_day_17, year_2016_day_18, year_2016_day_19, - year_2016_day_20 + year_2016_day_20, + year_2016_day_21 ); criterion_main!(benches); diff --git a/inputs/year_2016/day_21_input b/inputs/year_2016/day_21_input new file mode 100644 index 0000000..d7101be --- /dev/null +++ b/inputs/year_2016/day_21_input @@ -0,0 +1,100 @@ +reverse positions 1 through 6 +rotate based on position of letter a +swap position 4 with position 1 +reverse positions 1 through 5 +move position 5 to position 7 +swap position 4 with position 0 +swap position 4 with position 6 +rotate based on position of letter a +swap position 0 with position 2 +move position 5 to position 2 +move position 7 to position 1 +swap letter d with letter c +swap position 5 with position 3 +reverse positions 3 through 7 +rotate based on position of letter d +swap position 7 with position 5 +rotate based on position of letter f +swap position 4 with position 1 +swap position 3 with position 6 +reverse positions 4 through 7 +rotate based on position of letter c +move position 0 to position 5 +swap position 7 with position 4 +rotate based on position of letter f +reverse positions 1 through 3 +move position 5 to position 3 +rotate based on position of letter g +reverse positions 2 through 5 +rotate right 0 steps +rotate left 0 steps +swap letter f with letter b +rotate based on position of letter h +move position 1 to position 3 +reverse positions 3 through 6 +rotate based on position of letter h +swap position 4 with position 3 +swap letter b with letter h +swap letter a with letter h +reverse positions 1 through 6 +swap position 3 with position 6 +swap letter e with letter d +swap letter e with letter h +swap position 1 with position 5 +rotate based on position of letter a +reverse positions 4 through 5 +swap position 0 with position 4 +reverse positions 0 through 3 +move position 7 to position 2 +swap letter e with letter c +swap position 3 with position 4 +rotate left 3 steps +rotate left 7 steps +rotate based on position of letter e +reverse positions 5 through 6 +move position 1 to position 5 +move position 1 to position 2 +rotate left 1 step +move position 7 to position 6 +rotate left 0 steps +reverse positions 5 through 6 +reverse positions 3 through 7 +swap letter d with letter e +rotate right 3 steps +swap position 2 with position 1 +swap position 5 with position 7 +swap letter h with letter d +swap letter c with letter d +rotate based on position of letter d +swap letter d with letter g +reverse positions 0 through 1 +rotate right 0 steps +swap position 2 with position 3 +rotate left 4 steps +rotate left 5 steps +move position 7 to position 0 +rotate right 1 step +swap letter g with letter f +rotate based on position of letter a +rotate based on position of letter b +swap letter g with letter e +rotate right 4 steps +rotate based on position of letter h +reverse positions 3 through 5 +swap letter h with letter e +swap letter g with letter a +rotate based on position of letter c +reverse positions 0 through 4 +rotate based on position of letter e +reverse positions 4 through 7 +rotate left 4 steps +swap position 0 with position 6 +reverse positions 1 through 6 +rotate left 2 steps +swap position 5 with position 3 +swap letter b with letter d +swap letter b with letter d +rotate based on position of letter d +rotate based on position of letter c +rotate based on position of letter h +move position 4 to position 7 \ No newline at end of file diff --git a/src/year_2016.rs b/src/year_2016.rs index ad705e8..1d4c1a4 100644 --- a/src/year_2016.rs +++ b/src/year_2016.rs @@ -23,6 +23,7 @@ pub mod day_17; pub mod day_18; pub mod day_19; pub mod day_20; +pub mod day_21; pub fn solve(day: u8, part: u8, input: impl Into) -> Option { if part != 1 && part != 2 { @@ -206,4 +207,11 @@ mod tests { assert_eq!(day_20::day_20_v1(input), 4_793_564); assert_eq!(day_20::day_20_v2(input), 146); } + + #[test] + fn day_21() { + let input = include_str!("../inputs/year_2016/day_21_input"); + assert_eq!(day_21::day_21_v1(input), "gfdhebac"); + assert_eq!(day_21::day_21_v2(input), "dhaegfbc"); + } } diff --git a/src/year_2016/day_21.rs b/src/year_2016/day_21.rs new file mode 100644 index 0000000..a08110b --- /dev/null +++ b/src/year_2016/day_21.rs @@ -0,0 +1,176 @@ +use itertools::Itertools; +use std::str; + +#[derive(Debug)] +enum Instruction { + SwapPosition(usize, usize), + SwapLetter(char, char), + RotateLeft(usize), + RotateRight(usize), + RotateBasedOnLetter(char), + ReversePositions(usize, usize), + MovePosition(usize, usize), +} + +impl Instruction { + fn new(input: &str) -> Self { + let parts = input.split_whitespace().collect_vec(); + match (parts[0], parts[1]) { + ("swap", "position") => { + let lhs = parts[2].parse::().unwrap(); + let rhs = parts[5].parse::().unwrap(); + Instruction::SwapPosition(lhs, rhs) + } + ("swap", "letter") => { + let lhs = parts[2].as_bytes()[0] as char; + let rhs = parts[5].as_bytes()[0] as char; + Instruction::SwapLetter(lhs, rhs) + } + ("rotate", "left") => { + let steps = parts[2].parse::().unwrap(); + Instruction::RotateLeft(steps) + } + ("rotate", "right") => { + let steps = parts[2].parse::().unwrap(); + Instruction::RotateRight(steps) + } + ("rotate", "based") => { + let chr = parts[6].as_bytes()[0] as char; + Instruction::RotateBasedOnLetter(chr) + } + ("reverse", "positions") => { + let lhs = parts[2].parse::().unwrap(); + let rhs = parts[4].parse::().unwrap(); + Instruction::ReversePositions(lhs, rhs) + } + ("move", "position") => { + let lhs = parts[2].parse::().unwrap(); + let rhs = parts[5].parse::().unwrap(); + Instruction::MovePosition(lhs, rhs) + } + _ => panic!("Unknown instruction: {}", input), + } + } + + fn execute_rev(self, mut password: Vec) -> Vec { + match self { + Instruction::RotateLeft(steps) => password.rotate_right(steps), + Instruction::RotateRight(steps) => password.rotate_left(steps), + Instruction::MovePosition(lhs, rhs) => { + let extract = password.remove(rhs); + password.insert(lhs, extract); + } + Instruction::RotateBasedOnLetter(chr) => { + for rotate in 0..password.len() { + let mut tester = password.clone(); + tester.rotate_right(rotate); + let mut index = tester.iter().position(|c| *c == chr).unwrap(); + index += if index >= 4 { 2 } else { 1 }; + index %= password.len(); + tester.rotate_right(index); + if tester == password { + password.rotate_left(index); + break; + } + } + } + _ => { + password = self.execute(password); + } + }; + password + } + + fn execute(self, mut password: Vec) -> Vec { + match self { + Instruction::SwapPosition(lhs, rhs) => password.swap(lhs, rhs), + Instruction::SwapLetter(lhs, rhs) => { + for idx in 0..password.len() { + if password[idx] == lhs { + password[idx] = rhs; + } else if password[idx] == rhs { + password[idx] = lhs; + } + } + } + Instruction::RotateLeft(steps) => password.rotate_left(steps), + Instruction::RotateRight(steps) => password.rotate_right(steps), + Instruction::RotateBasedOnLetter(chr) => { + let mut index = password.iter().position(|c| *c == chr).unwrap(); + index += if index >= 4 { 2 } else { 1 }; + index %= password.len(); + password.rotate_right(index); + } + Instruction::ReversePositions(mut lhs, mut rhs) => { + while lhs < rhs { + password.swap(lhs, rhs); + lhs += 1; + rhs -= 1; + } + } + Instruction::MovePosition(lhs, rhs) => { + let extract = password.remove(lhs); + password.insert(rhs, extract); + } + } + password + } +} + +pub fn day_21_v1(input: impl Into) -> String { + let mut password = "abcdefgh".chars().collect::>(); + let instructions = input + .into() + .lines() + .map(|line| Instruction::new(line)) + .collect_vec(); + for instruction in instructions { + password = instruction.execute(password); + } + + password.iter().collect::() +} + +pub fn day_21_v2(input: impl Into) -> String { + let mut password = "fbgdceah".chars().collect::>(); + let mut instructions = input + .into() + .lines() + .map(|line| Instruction::new(line)) + .collect_vec(); + instructions.reverse(); + for instruction in instructions { + password = instruction.execute_rev(password); + } + + password.iter().collect::() +} + +solvable!(day_21, day_21_v1, day_21_v2, String); + +#[cfg(test)] +mod tests { + use super::*; + + const SAMPLE: &str = "swap position 4 with position 0\n\ + swap letter d with letter b\n\ + reverse positions 0 through 4\n\ + rotate left 1 step\n\ + move position 1 to position 4\n\ + move position 3 to position 0\n\ + rotate based on position of letter b\n\ + rotate based on position of letter d"; + + #[test] + fn can_interpret_code() { + let mut password = "abcde".chars().collect::>(); + let instructions = SAMPLE + .lines() + .map(|line| Instruction::new(line)) + .collect_vec(); + for instruction in instructions { + password = instruction.execute(password); + } + assert_eq!(password.iter().collect::(), "decab"); + } +}