use anyhow::anyhow; use std::{ env, fs, ops::{Add, Sub}, str::FromStr, }; #[derive(Debug, PartialEq, Eq)] enum Rotation { Left(u32), Right(u32), } #[derive(Debug)] struct ParseRotationError; impl FromStr for Rotation { type Err = ParseRotationError; fn from_str(s: &str) -> Result { let mut chars = s.chars(); let lr = chars.next().ok_or(ParseRotationError)?; let n: u32 = chars .collect::() .parse() .map_err(|_| ParseRotationError)?; match lr { 'L' => Ok(Self::Left(n)), 'R' => Ok(Self::Right(n)), _ => Err(ParseRotationError), } } } #[derive(Clone, Copy, PartialEq, Eq, Debug)] struct Dial(u8); struct RotateResult(u32, Dial); impl Dial { fn new() -> Self { Self(50) } fn rotate(self, rot: &Rotation) -> RotateResult { match rot { Rotation::Left(n) => self.rotate_left(*n), Rotation::Right(n) => self.rotate_right(*n), } } fn rotate_left(self, n: u32) -> RotateResult { let pos = self.0 as u32; if pos != 0 && n >= pos { let quot = n.div_ceil(100); RotateResult(quot, self - n) } else { RotateResult(0, self - n) } } fn rotate_right(self, n: u32) -> RotateResult { let pos = self.0 as u32; if n + pos >= 100 { let quot = n.div_ceil(100); RotateResult(quot, self + n) } else { RotateResult(0, self + n) } } fn rotate_all(self, rotations: &[Rotation]) -> RotateResult { let mut dial = Dial::from(50); let mut zeros_total = 0; for rot in rotations { let RotateResult(zeros, new_dial) = dial.rotate(rot); zeros_total += zeros; dial = new_dial; println!("{:?}, dial: {}, zeros: {}", rot, dial.0, zeros); } RotateResult(zeros_total, dial) } } impl From for Dial { fn from(value: u8) -> Self { Self(value) } } impl Add for Dial { type Output = Self; fn add(self, rhs: u32) -> Self::Output { let n = self.0 as u32; let m = (n + rhs) % 100; Self(m as u8) } } impl Sub for Dial { type Output = Self; fn sub(self, rhs: u32) -> Self::Output { let n = self.0 as u32; let m = (n + 100 - rhs % 100) % 100; Self(m as u8) } } impl PartialEq for Dial { fn eq(&self, other: &u8) -> bool { self.0 == *other } } fn part1(rotations: &[Rotation]) -> RotateResult { let mut dial = Dial::new(); let mut zeros = 0; for rot in rotations { match rot { Rotation::Left(n) => dial = dial - *n, Rotation::Right(n) => dial = dial + *n, } if dial == 0 { zeros = zeros + 1; } } RotateResult(zeros, dial) } fn main() -> anyhow::Result<()> { let usage_err = || anyhow!("usage: day1 PART INPUT"); let mut args = env::args(); let part: u8 = args .nth(1) .ok_or_else(usage_err)? .parse() .map_err(|_| usage_err())?; let input_path = args.next().ok_or_else(usage_err)?; let input = fs::read_to_string(input_path)?; let rotations: Vec = input .split_whitespace() .map(|s| s.parse().map_err(|_| anyhow!("bad rotation: {}", s))) .collect::>()?; match part { 1 => { let RotateResult(zeros, _) = part1(&rotations); println!("Result: {}", zeros); Ok(()) } 2 => { let dial = Dial::from(50); let RotateResult(zeros, _) = dial.rotate_all(&rotations); println!("Result: {}", zeros); Ok(()) } _ => Err(usage_err()), } } #[cfg(test)] mod tests { use super::*; #[test] fn parse_rotation() { assert!("L2" .parse::() .is_ok_and(|rot| rot == Rotation::Left(2))); assert!("R929" .parse::() .is_ok_and(|rot| rot == Rotation::Right(929))); assert!("M1".parse::().is_err()); assert!("L".parse::().is_err()); assert!("2".parse::().is_err()); } #[test] fn dial_ops() { let dial = Dial::new(); assert_eq!(dial + 1, 51); assert_eq!(dial + 50, 0); assert_eq!(dial + 100, 50); assert_eq!(dial + 150, 0); assert_eq!(dial + 2 + 48, 0); assert_eq!(dial + 45 + 60, 55); assert_eq!(dial - 45, 5); assert_eq!(dial - 45 - 10, 95); assert_eq!(dial - 1, 49); assert_eq!(dial - 68, 82); assert_eq!(dial - 68 - 30, 52); assert_eq!(dial - 100, 50); assert_eq!(dial - 998, 52); assert_eq!(dial - 150, 0); assert_eq!(dial - 51, 99); assert_eq!(dial - 50, 0); } #[test] fn dial_rotate() { let mut dial = Dial::new(); let rotations = vec![ // Example data (Rotation::Left(68), 1, 82), (Rotation::Left(30), 0, 52), (Rotation::Right(48), 1, 0), (Rotation::Left(5), 0, 95), (Rotation::Right(60), 1, 55), (Rotation::Left(55), 1, 0), (Rotation::Left(1), 0, 99), (Rotation::Left(99), 1, 0), (Rotation::Right(14), 0, 14), (Rotation::Left(82), 1, 32), // Additional data (Rotation::Left(200), 2, 32), (Rotation::Right(200), 2, 32), (Rotation::Left(30), 0, 2), (Rotation::Left(10), 1, 92), (Rotation::Right(10), 1, 2), (Rotation::Left(110), 2, 92), (Rotation::Right(210), 3, 2), (Rotation::Right(48), 0, 50), (Rotation::Right(1000), 10, 50), (Rotation::Right(48), 0, 98), (Rotation::Left(651), 6, 47), ]; for (rot, expected_zeros, expected_dial) in &rotations { let RotateResult(zeros, new_dial) = dial.rotate(rot); assert_eq!(&zeros, expected_zeros, "rotating with {:?}", rot); assert_eq!(&new_dial, expected_dial, "rotating with {:?}", rot); dial = new_dial; } } }