programming puzzle solutions
at main 2.7 kB view raw
1/// The direction to rotate the dial. 2pub enum Direction { 3 Left, // Move toward lower numbers. 4 Right, // Move toward higher numbers. 5} 6 7/// An instruction to rotate the dial. 8pub struct Instruction { 9 pub direction: Direction, // L or R. 10 pub distance: u16, // How many clicks to rotate. 11} 12impl Instruction { 13 /// Parse an instruction from a line of input (e.g. "L68" or "R48"). 14 pub fn parse(line: &str) -> Self { 15 let direction = match &line[0..1] { 16 "L" => Direction::Left, // Parse the direction character. 17 "R" => Direction::Right, 18 _ => unreachable!(), // All input has a L/R direction. 19 }; 20 let distance = line[1..].parse().expect("valid distance"); // Then parse the distance value. 21 Self { 22 direction, 23 distance, 24 } 25 } 26} 27 28/// A safe dial with positions 0-99 that wraps around. 29pub struct Dial { 30 pub position: u16, // Current position on the dial. 31} 32impl Dial { 33 /// Create a new dial starting at position 50. 34 pub const fn new() -> Self { 35 Self { position: 50 } 36 } 37 38 /// Rotate the dial according to an instruction, wrapping around at 0/99 using modulo arithmetic. 39 /// 40 /// The remainder of the modulo operation is effectively, "what remains after subtracting 100 as many times as possible?" 41 /// This allows us to account for full rotations around the dial and determining what position we should have landed on. 42 pub const fn rotate(&mut self, instruction: &Instruction) { 43 self.position = match instruction.direction { 44 Direction::Left => (self.position + 100 - instruction.distance % 100) % 100, // For Left, add 100 before subtracting to avoid negative values. 45 Direction::Right => (self.position + instruction.distance) % 100, // For Right, simply wrap around at 100. 46 }; 47 } 48 49 /// Check if dial points at 0. 50 pub const fn is_at_zero(&self) -> bool { 51 self.position == 0 52 } 53 54 /// Count how many times the dial passes through 0 during a rotation using division. 55 pub const fn count_zero_crossings(&self, instruction: &Instruction) -> u16 { 56 match instruction.direction { 57 Direction::Left => { 58 let first_crossing = if self.position == 0 { 100 } else { self.position }; 59 if instruction.distance < first_crossing { 0 } // Distance too short to reach first crossing. 60 else { (instruction.distance - first_crossing) / 100 + 1 } // First crossing + additional full rotations. 61 } 62 Direction::Right => (self.position + instruction.distance) / 100, // Count complete 100-step cycles. 63 } 64 } 65}