From d04af38c55032749fec88287df4e1a37698ca7a2 Mon Sep 17 00:00:00 2001 From: Timothy Quilling Date: Tue, 02 Dec 2025 04:50:35 +0000 Subject: [PATCH] add 2025 day1 in rust --- Cargo.toml | 10 +++++++--- src/2025/day1/rust/common.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/2025/day1/rust/mod.rs | 36 ++++++++++++++++++++++++++++++++++++ src/2025/day1/rust/part_a.rs | 13 +++++++++++++ src/2025/day1/rust/part_b.rs | 13 +++++++++++++ 5 file(s) changed, 137 insertion(s)(+), 3 deletion(s)(-) diff --git a/Cargo.toml b/Cargo.toml --- a/Cargo.toml +++ b/Cargo.toml @@ -43,14 +43,18 @@ name = "2024_day8" path = "src/2024/day8/rust/mod.rs" +[[bin]] +name = "2025_day1" +path = "src/2025/day1/rust/mod.rs" + [lints.rust] unsafe_code = "forbid" [lints.clippy] -enum_glob_use = "deny" +# enum_glob_use = "warn" # pedantic = "deny" -nursery = "deny" -unwrap_used = "deny" +# nursery = "warn" +# unwrap_used = "warn" [profile.release] opt-level = 'z' diff --git a/src/2025/day1/rust/common.rs b/src/2025/day1/rust/common.rs new file mode 100644 --- /dev/null +++ b/src/2025/day1/rust/common.rs @@ -0,0 +1,68 @@ +/// The direction to rotate the dial. +pub enum Direction { + Left, // Move toward lower numbers. + Right, // Move toward higher numbers. +} + +/// An instruction to rotate the dial. +pub struct Instruction { + pub direction: Direction, // L or R. + pub distance: i16, // How many clicks to rotate. +} +impl Instruction { + /// Parse an instruction from a line of input (e.g. "L68" or "R48"). + pub fn parse(line: &str) -> Self { + let direction = match &line[0..1] { + "L" => Direction::Left, // Parse the direction character. + "R" => Direction::Right, + _ => unreachable!(), // All input has a L/R direction. + }; + let distance = line[1..].parse().expect("valid distance"); // Then parse the distance value. + Self { + direction, + distance, + } + } +} + +/// A safe dial with positions 0-99 that wraps around. +pub struct Dial { + pub position: i16, // Current position on the dial. +} +impl Dial { + /// Create a new dial starting at position 50. + pub const fn new() -> Self { + Self { position: 50 } + } + + /// Rotate the dial according to an instruction, wrapping around at 0/100. + pub const fn rotate(&mut self, instruction: &Instruction) { + self.position = match instruction.direction { + Direction::Left => (self.position - instruction.distance).rem_euclid(100), // Wrap around at 0. + Direction::Right => (self.position + instruction.distance).rem_euclid(100), // Wrap around at 100. + }; + } + + /// Check if dial points at 0. + pub const fn is_at_zero(&self) -> bool { + self.position == 0 + } + + /// Count how many times the dial passes through 0 during a rotation. + pub const fn count_zero_crossings(&self, instruction: &Instruction) -> i16 { + match instruction.direction { + Direction::Left => { + let mut first_crossing = self.position; + if first_crossing == 0 { + first_crossing = 100; // If starting at 0, first crossing Left is after full rotation. + } + if instruction.distance < first_crossing { + 0 // Did not reach 0 during this rotation. + } else { + (instruction.distance - first_crossing) / 100 + 1 // Returns number of Left crossings. + } + } + Direction::Right => (self.position + instruction.distance) / 100, // Returns number of Right crossings. + } + } +} diff --git a/src/2025/day1/rust/mod.rs b/src/2025/day1/rust/mod.rs new file mode 100644 --- /dev/null +++ b/src/2025/day1/rust/mod.rs @@ -0,0 +1,36 @@ +mod common; +mod part_a; +mod part_b; + +use part_a::part_a; +use part_b::part_b; + +pub fn main() { + let input = include_str!("../input.txt"); + let example_input = include_str!("../input_example.txt"); + println!("Example Part A: {}", part_a(example_input)); + println!("Part A: {}", part_a(input)); + println!("Example Part B: {}", part_b(example_input)); + println!("Part B: {}", part_b(input)); +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_part_a_example() { + assert_eq!(part_a(include_str!("../input_example.txt")), 3); + } + #[test] + fn test_part_a() { + assert_eq!(part_a(include_str!("../input.txt")), 1071); + } + #[test] + fn test_part_b_example() { + assert_eq!(part_b(include_str!("../input_example.txt")), 6); + } + #[test] + fn test_part_b() { + assert_eq!(part_b(include_str!("../input.txt")), 6700); + } +} diff --git a/src/2025/day1/rust/part_a.rs b/src/2025/day1/rust/part_a.rs new file mode 100644 --- /dev/null +++ b/src/2025/day1/rust/part_a.rs @@ -0,0 +1,13 @@ +use super::common::{Dial, Instruction}; + +/// Count how many times the dial is left pointing at 0 after each rotation. +pub fn part_a(input: &str) -> i16 { + let mut dial = Dial::new(); // Dial starts at position 50. + let mut count = 0; // Count times dial is left pointing at 0. + for line in input.lines().filter(|l| !l.is_empty()) { + let instruction = Instruction::parse(line); + dial.rotate(&instruction); // Rotate the dial, first. + count += i16::from(dial.is_at_zero()); // Then increment count if at 0. + } + count // Return the count of times the dial pointed at 0. +} diff --git a/src/2025/day1/rust/part_b.rs b/src/2025/day1/rust/part_b.rs new file mode 100644 --- /dev/null +++ b/src/2025/day1/rust/part_b.rs @@ -0,0 +1,13 @@ +use super::common::{Dial, Instruction}; + +/// Count how many times the dial passes through 0 during any rotation. +pub fn part_b(input: &str) -> i16 { + let mut dial = Dial::new(); + let mut count = 0; + for line in input.lines().filter(|l| !l.is_empty()) { + let instruction = Instruction::parse(line); + count += dial.count_zero_crossings(&instruction); // Count zeros crossed during this rotation, first. + dial.rotate(&instruction); // Then rotate the dial. + } + count // Return the count of times the dial passed through 0. +} -- tangled.sh