Skip to content

Commit

Permalink
Year 2016: Day 21
Browse files Browse the repository at this point in the history
  • Loading branch information
joshleaves committed Mar 21, 2024
1 parent e136c72 commit 71e6b61
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 3 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
4 changes: 4 additions & 0 deletions NOTES_2016.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
16 changes: 15 additions & 1 deletion benches/year_2016.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand All @@ -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);
100 changes: 100 additions & 0 deletions inputs/year_2016/day_21_input
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions src/year_2016.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>) -> Option<String> {
if part != 1 && part != 2 {
Expand Down Expand Up @@ -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");
}
}
176 changes: 176 additions & 0 deletions src/year_2016/day_21.rs
Original file line number Diff line number Diff line change
@@ -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::<usize>().unwrap();
let rhs = parts[5].parse::<usize>().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::<usize>().unwrap();
Instruction::RotateLeft(steps)
}
("rotate", "right") => {
let steps = parts[2].parse::<usize>().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::<usize>().unwrap();
let rhs = parts[4].parse::<usize>().unwrap();
Instruction::ReversePositions(lhs, rhs)
}
("move", "position") => {
let lhs = parts[2].parse::<usize>().unwrap();
let rhs = parts[5].parse::<usize>().unwrap();
Instruction::MovePosition(lhs, rhs)
}
_ => panic!("Unknown instruction: {}", input),
}
}

fn execute_rev(self, mut password: Vec<char>) -> Vec<char> {
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<char>) -> Vec<char> {
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>) -> String {
let mut password = "abcdefgh".chars().collect::<Vec<_>>();
let instructions = input
.into()
.lines()
.map(|line| Instruction::new(line))
.collect_vec();
for instruction in instructions {
password = instruction.execute(password);
}

password.iter().collect::<String>()
}

pub fn day_21_v2(input: impl Into<String>) -> String {
let mut password = "fbgdceah".chars().collect::<Vec<_>>();
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::<String>()
}

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::<Vec<_>>();
let instructions = SAMPLE
.lines()
.map(|line| Instruction::new(line))
.collect_vec();
for instruction in instructions {
password = instruction.execute(password);
}
assert_eq!(password.iter().collect::<String>(), "decab");
}
}

0 comments on commit 71e6b61

Please sign in to comment.