From ba60d349648965167e783b227db3a2acf987145a Mon Sep 17 00:00:00 2001 From: Matt Harden Date: Mon, 16 Jun 2025 14:27:10 -0700 Subject: [PATCH] 2023 day 6. --- 2023/06/Cargo.toml | 7 +++ 2023/06/data/example1 | 2 + 2023/06/data/input | 2 + 2023/06/src/data.rs | 5 ++ 2023/06/src/main.rs | 10 ++++ 2023/06/src/part1/mod.rs | 17 +++++++ 2023/06/src/part2/mod.rs | 21 ++++++++ 2023/06/src/puzzle.rs | 103 +++++++++++++++++++++++++++++++++++++++ Cargo.lock | 30 ++++++++++++ 9 files changed, 197 insertions(+) create mode 100644 2023/06/Cargo.toml create mode 100644 2023/06/data/example1 create mode 100644 2023/06/data/input create mode 100644 2023/06/src/data.rs create mode 100644 2023/06/src/main.rs create mode 100644 2023/06/src/part1/mod.rs create mode 100644 2023/06/src/part2/mod.rs create mode 100644 2023/06/src/puzzle.rs diff --git a/2023/06/Cargo.toml b/2023/06/Cargo.toml new file mode 100644 index 0000000..cd2904e --- /dev/null +++ b/2023/06/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "y2023d06" +version = "0.1.0" +edition = "2024" + +[dependencies] +lazy-regex = "3.4.1" diff --git a/2023/06/data/example1 b/2023/06/data/example1 new file mode 100644 index 0000000..b39f49d --- /dev/null +++ b/2023/06/data/example1 @@ -0,0 +1,2 @@ +Time: 7 15 30 +Distance: 9 40 200 \ No newline at end of file diff --git a/2023/06/data/input b/2023/06/data/input new file mode 100644 index 0000000..df0a001 --- /dev/null +++ b/2023/06/data/input @@ -0,0 +1,2 @@ +Time: 53 91 67 68 +Distance: 250 1330 1081 1025 \ No newline at end of file diff --git a/2023/06/src/data.rs b/2023/06/src/data.rs new file mode 100644 index 0000000..5b7bd47 --- /dev/null +++ b/2023/06/src/data.rs @@ -0,0 +1,5 @@ +#[cfg(test)] +pub const EXAMPLE1: &'static str = include_str!("../data/example1"); + +#[allow(unused)] +pub const INPUT: &'static str = include_str!("../data/input"); diff --git a/2023/06/src/main.rs b/2023/06/src/main.rs new file mode 100644 index 0000000..27c3237 --- /dev/null +++ b/2023/06/src/main.rs @@ -0,0 +1,10 @@ +mod data; +mod part1; +mod part2; +mod puzzle; + +fn main() { + use data::INPUT; + println!("Part 1: {}", part1::run(INPUT)); + println!("Part 2: {}", part2::run(INPUT)); +} diff --git a/2023/06/src/part1/mod.rs b/2023/06/src/part1/mod.rs new file mode 100644 index 0000000..a7ffad2 --- /dev/null +++ b/2023/06/src/part1/mod.rs @@ -0,0 +1,17 @@ +use crate::puzzle::Puzzle; + +pub fn run(input: &str) -> u64 { + let puzzle: Puzzle = input.parse().expect("parse failed"); + puzzle.races.into_iter().map(|race| race.ways()).product() +} + +#[cfg(test)] +mod test { + use super::*; + use crate::data::EXAMPLE1; + + #[test] + fn test1() { + assert_eq!(run(EXAMPLE1), 288); + } +} diff --git a/2023/06/src/part2/mod.rs b/2023/06/src/part2/mod.rs new file mode 100644 index 0000000..6d5c814 --- /dev/null +++ b/2023/06/src/part2/mod.rs @@ -0,0 +1,21 @@ +use crate::puzzle::Puzzle; + +pub fn run(input: &str) -> u64 { + let puzzle: Puzzle = input + .replace(' ', "") + .replace(':', ": ") + .parse() + .expect("parse failed"); + puzzle.races.into_iter().map(|race| race.ways()).product() +} + +#[cfg(test)] +mod test { + use super::*; + use crate::data::EXAMPLE1; + + #[test] + fn test1() { + assert_eq!(run(EXAMPLE1), 71503); + } +} diff --git a/2023/06/src/puzzle.rs b/2023/06/src/puzzle.rs new file mode 100644 index 0000000..512a512 --- /dev/null +++ b/2023/06/src/puzzle.rs @@ -0,0 +1,103 @@ +use std::{iter::zip, str::FromStr}; + +use lazy_regex::regex_captures; + +#[derive(Debug)] +pub struct Puzzle { + pub races: Vec, +} + +#[derive(Debug)] +pub struct Race { + pub time: u64, + pub distance: u64, +} + +impl Race { + pub fn ways(&self) -> u64 { + let q = ceil_sqrt(self.time * self.time - 4 * self.distance); + q - ((self.time ^ q ^ 1) & 1) + } +} + +#[test] +fn test_race_ways() { + assert_eq!( + Race { + time: 7, + distance: 9, + } + .ways(), + 4 + ); + assert_eq!( + Race { + time: 15, + distance: 40, + } + .ways(), + 8 + ); + assert_eq!( + Race { + time: 30, + distance: 200, + } + .ways(), + 9 + ); + assert_eq!( + Race { + time: 71530, + distance: 940200, + } + .ways(), + 71503 + ); +} + +fn ceil_sqrt(n: u64) -> u64 { + return (n - 1).isqrt() + 1; +} + +#[test] +fn test_ceil_sqrt() { + for i in 0u64..5 { + for n in i.pow(2) + 1..=(i + 1).pow(2) { + assert_eq!(ceil_sqrt(n), i + 1); + } + } +} + +#[derive(Debug)] +pub struct ParseError; + +impl FromStr for Puzzle { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + let (_, time, distance) = regex_captures!( + r#"Time:\s+(\d+(?:\s+\d+)*)\nDistance:\s+(\d+(?:\s+\d+)*)"#, + s + ) + .ok_or(ParseError)?; + let times: Vec = time + .split_ascii_whitespace() + .map(|s| s.parse()) + .collect::>() + .map_err(|_| ParseError)?; + let distances: Vec = distance + .split_ascii_whitespace() + .map(|s| s.parse()) + .collect::>() + .map_err(|_| ParseError)?; + if times.len() != distances.len() { + return Err(ParseError); + } + Ok(Puzzle { + races: zip(times, distances) + .map(|(time, distance)| Race { time, distance }) + .collect(), + }) + } +} diff --git a/Cargo.lock b/Cargo.lock index bd1c549..5701685 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -287,6 +287,29 @@ dependencies = [ "either", ] +[[package]] +name = "lazy-regex" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] + +[[package]] +name = "lazy-regex-proc_macros" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.101", +] + [[package]] name = "memchr" version = "2.7.4" @@ -706,6 +729,13 @@ dependencies = [ "parse-display-with", ] +[[package]] +name = "y2023d06" +version = "0.1.0" +dependencies = [ + "lazy-regex", +] + [[package]] name = "y2024d01" version = "0.1.0"