diff --git a/src/days.rs b/src/days.rs index f591d7d..f74b0af 100644 --- a/src/days.rs +++ b/src/days.rs @@ -18,7 +18,7 @@ pub mod d17; pub mod d18; pub mod d19; pub mod d20; -// pub mod d21; +pub mod d21; // pub mod d22; // pub mod d23; // pub mod d24; diff --git a/src/days/d21.rs b/src/days/d21.rs new file mode 100644 index 0000000..42da274 --- /dev/null +++ b/src/days/d21.rs @@ -0,0 +1,151 @@ +#![allow(non_upper_case_globals)] +#![allow(dead_code)] + +use crate::prelude::*; + +pub type I = Vec; +pub type O = String; + +#[derive(Debug, Clone)] +pub enum Inst { + SwapPos (usize, usize), + SwapLetter (char, char), + RotateLeft (usize), + RotateRight (usize), + RotatePos (char), + Reverse (usize, usize), + Move (usize, usize) +} + +// const _rotations: [usize; 8] = [1,2,3,4,6,7,0,1]; +// ↓ +const _rotations: [usize; 8] = [1,1,6,2,7,3,0,4]; + + +impl Inst { + fn from_str (i: &str) -> Self { + let i = i.trim(); + if let Some(sfx) = i.strip_prefix("swap position ") { + let (l, r) = sfx.split_once(" with position ").unwrap(); + Inst::SwapPos(p!(l), p!(r)) + } else if let Some(sfx) = i.strip_prefix("swap letter ") { + let (l, r) = sfx.split_once(" with letter ").unwrap(); + Inst::SwapLetter(l.trim().chars().next().unwrap(), r.trim().chars().next().unwrap()) + } else if let Some(sfx) = i.strip_prefix("rotate left ") { + let (l, _) = sfx.split_once(' ').unwrap(); + Inst::RotateLeft(p!(l)) + } else if let Some(sfx) = i.strip_prefix("rotate right ") { + let (l, _) = sfx.split_once(' ').unwrap(); + Inst::RotateRight(p!(l)) + } else if let Some(sfx) = i.strip_prefix("rotate based on position of letter ") { + Inst::RotatePos(sfx.chars().next().unwrap()) + } else if let Some(sfx) = i.strip_prefix("reverse positions ") { + let (l, r) = sfx.split_once(" through ").unwrap(); + Inst::Reverse(p!(l), p!(r)) + } else if let Some(sfx) = i.strip_prefix("move position ") { + let (l, r) = sfx.split_once(" to position ").unwrap(); + Inst::Move(p!(l), p!(r)) + } else { + panic!("unknown instruction") + } + } + + fn apply (self, target: &mut Vec) { + use Inst as I; + match self { + I::SwapPos(l, r) => target.swap(l, r), + I::SwapLetter(l, r) => { + let lpos = target.iter().position(|&c| c == l); + let rpos = target.iter().position(|&c| c == r); + if let Some(lpos) = lpos && let Some(rpos) = rpos { + target.swap(lpos, rpos); + } + }, + I::RotateLeft(times) => target.rotate_left(times), + I::RotateRight(times) => target.rotate_right(times), + I::RotatePos(needle) => { + let pos = target.iter().position(|&c| c == needle); + if let Some(pos) = pos { + let rotations = pos + 1 + if pos >= 4 { 1 } else { 0 }; + let len = target.len(); // % len is useless in my input but needed for the general correctness of the algorithm + target.rotate_right(rotations % len); + } + }, + I::Reverse(from, to) => (&mut target[from..=to]).reverse(), + I::Move(from, to) => { + let val = target.remove(from); + target.insert(to, val); + } + } + } + + fn undo (self, target: &mut Vec) { + use Inst as I; + match self { + I::SwapPos(l, r) => target.swap(l, r), + I::SwapLetter(l, r) => { + let lpos = target.iter().position(|&c| c == l); + let rpos = target.iter().position(|&c| c == r); + if let Some(lpos) = lpos && let Some(rpos) = rpos { + target.swap(lpos, rpos); + } + }, + I::RotateLeft(times) => target.rotate_right(times), + I::RotateRight(times) => target.rotate_left(times), + I::RotatePos(needle) => { + let pos = target.iter().position(|&c| c == needle); + if let Some(pos) = pos { + target.rotate_left(_rotations[pos]); + } + }, + I::Reverse(from, to) => (&mut target[from..=to]).reverse(), + I::Move(from, to) => { + let val = target.remove(to); + target.insert(from, val); + } + } + } +} + +fn _parse (data: &str) -> I { + data.trim().lines().map(Inst::from_str).collect() +} + +fn _silver (start: &'static str, list: I) -> O { + let mut state = start.chars().collect_vec(); + list.into_iter().for_each(|i| i.apply(&mut state)); + state.into_iter().collect() +} + +fn _gold (start: &'static str, mut list: I) -> O { + list.reverse(); + let mut state = start.chars().collect_vec(); + list.into_iter().for_each(|i| i.undo(&mut state)); + state.into_iter().collect() +} + +#[cfg(test)] +mod test { + use super::*; + + fn read () -> I { + let data = inc!(21); + _parse(data) + } + + #[test] + fn silver () { + let data = read(); + let ans = _silver("abcdefgh", data); + + assert_eq!(ans, s!("bfheacgd")) + } + + #[test] + fn gold () { + let data = read(); + let ans = _gold("fbgdceah", data); + + assert_eq!(ans, s!("gcehdbfa")) + } +}