#![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")) } }