From abda4f84d88b7a7170b9e3cfa257a254cb6f5d88 Mon Sep 17 00:00:00 2001 From: George Thomas Date: Tue, 9 Dec 2025 19:17:25 +0000 Subject: [PATCH] Solve day 1 in Rust --- rust/main.rs | 48 +++++++++++++++++++++++++++++- rust/puzzle.rs | 5 ++++ rust/puzzles/day1.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++ rust/puzzles/mod.rs | 1 + 4 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 rust/puzzle.rs create mode 100644 rust/puzzles/day1.rs create mode 100644 rust/puzzles/mod.rs diff --git a/rust/main.rs b/rust/main.rs index e7a11a9..1b39588 100644 --- a/rust/main.rs +++ b/rust/main.rs @@ -1,3 +1,49 @@ +mod puzzle; +mod puzzles; +use crate::puzzle::Puzzle; +use puzzles::day1; +use std::fmt::Display; +use std::fs; + +const PUZZLES: [&dyn SomePuzzle; 1] = [&day1::PUZZLE]; + fn main() { - println!("Hello, world!"); + [false, true].iter().for_each(|is_real_data| { + let t = if *is_real_data { "real" } else { "examples" }; + println!("{}", t); + PUZZLES.into_iter().for_each(|puzzle| { + println!(" {}", puzzle.number()); + let input = fs::read_to_string(format!("../inputs/{}/{}", t, puzzle.number())) + .expect("no input file"); + puzzle.run(&input).iter().zip(1..).for_each(|(output, n)| { + let expected = + fs::read_to_string(format!("../outputs/{}/{}/{}", t, puzzle.number(), n)) + .expect("no golden file"); + print!(" {}: ", n); + if expected == *output { + println!("OK"); + } else { + println!( + "expected {}, got {}", + expected.trim_end(), + output.trim_end() + ); + }; + }); + }); + }) +} + +pub trait SomePuzzle { + fn number(&self) -> u32; + fn run(&self, input: &str) -> Vec; +} +impl SomePuzzle for Puzzle { + fn number(&self) -> u32 { + self.number + } + fn run(&self, s: &str) -> Vec { + let input = (self.parser)(s); + self.parts.map(|p| p(&input).to_string() + "\n").to_vec() + } } diff --git a/rust/puzzle.rs b/rust/puzzle.rs new file mode 100644 index 0000000..8f212de --- /dev/null +++ b/rust/puzzle.rs @@ -0,0 +1,5 @@ +pub struct Puzzle { + pub number: u32, + pub parser: fn(&str) -> Input, + pub parts: [fn(&Input) -> Output; N], +} diff --git a/rust/puzzles/day1.rs b/rust/puzzles/day1.rs new file mode 100644 index 0000000..dd04b67 --- /dev/null +++ b/rust/puzzles/day1.rs @@ -0,0 +1,70 @@ +use crate::puzzle::Puzzle; + +pub const PUZZLE: Puzzle, i32, 2> = Puzzle { + number: 1, + parser: |input| { + input + .lines() + .filter(|line| !line.is_empty()) + .map(|line| { + let d = match line.chars().next().unwrap() { + 'L' => Direction::L, + 'R' => Direction::R, + c => panic!("Unknown direction: {}", c), + }; + let i: i32 = line[1..].parse().unwrap(); + (d, i) + }) + .collect() + }, + parts: [ + |instructions| { + let mut p = 50; + let mut r = 0; + for (d, i) in instructions { + let (_, p1) = step(*i, *d, p); + if p1 == 0 { + r += 1; + } + p = p1; + } + r + }, + |instructions| { + let mut p = 50; + let mut r = 0; + for (d, i) in instructions { + let (c, p1) = step(*i, *d, p); + let c1 = match d { + Direction::R => c.abs(), + Direction::L => { + if p == 0 { + c.abs() - 1 + } else if p1 == 0 { + c.abs() + 1 + } else { + c.abs() + } + } + }; + r += c1; + p = p1; + } + r + }, + ], +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Direction { + L, + R, +} + +fn step(i: i32, d: Direction, p: i32) -> (i32, i32) { + let p1 = match d { + Direction::L => p - i, + Direction::R => p + i, + }; + (p1.div_euclid(100), p1.rem_euclid(100)) +} diff --git a/rust/puzzles/mod.rs b/rust/puzzles/mod.rs new file mode 100644 index 0000000..ca9500f --- /dev/null +++ b/rust/puzzles/mod.rs @@ -0,0 +1 @@ +pub(crate) mod day1;