From 4db225683698cf7ce754e77bf374e9bbdbaa23a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20K=C3=B6rber?= Date: Wed, 6 Dec 2023 08:51:31 +0100 Subject: [PATCH] Add day 6 --- 2023/day6/.gitignore | 1 + 2023/day6/Cargo.lock | 39 ++++++++++ 2023/day6/Cargo.toml | 10 +++ 2023/day6/src/main.rs | 160 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 210 insertions(+) create mode 100644 2023/day6/.gitignore create mode 100644 2023/day6/Cargo.lock create mode 100644 2023/day6/Cargo.toml create mode 100644 2023/day6/src/main.rs diff --git a/2023/day6/.gitignore b/2023/day6/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/2023/day6/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/2023/day6/Cargo.lock b/2023/day6/Cargo.lock new file mode 100644 index 0000000..fe38edd --- /dev/null +++ b/2023/day6/Cargo.lock @@ -0,0 +1,39 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "day6" +version = "0.1.0" +dependencies = [ + "indoc", + "nom", +] + +[[package]] +name = "indoc" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] diff --git a/2023/day6/Cargo.toml b/2023/day6/Cargo.toml new file mode 100644 index 0000000..bd2b6da --- /dev/null +++ b/2023/day6/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "day6" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +indoc = "2.0.4" +nom = "7.1.3" diff --git a/2023/day6/src/main.rs b/2023/day6/src/main.rs new file mode 100644 index 0000000..4ef05d3 --- /dev/null +++ b/2023/day6/src/main.rs @@ -0,0 +1,160 @@ +use nom::{ + bytes::complete::tag, + character::complete::{char, digit1, multispace0, newline}, + combinator::map, + multi::{many1, separated_list1}, + sequence::{delimited, preceded, separated_pair, tuple}, + IResult, +}; + +fn number(i: &str) -> IResult<&str, usize> { + map(digit1, |f: &str| f.parse::().unwrap())(i) +} + +fn spaces(i: &str) -> IResult<&str, Vec> { + many1(char(' '))(i) +} + +#[derive(Debug)] +struct Race { + time: usize, + distance: usize, +} + +#[derive(Debug)] +struct RaceSheet { + races: Vec, +} + +impl RaceSheet { + fn parse(s: &str) -> IResult<&str, Self> { + let (rest, (times, distances)) = delimited( + multispace0, + separated_pair( + preceded( + tuple((tag("Time:"), spaces)), + separated_list1(spaces, number), + ), + newline, + preceded( + tuple((tag("Distance:"), spaces)), + separated_list1(spaces, number), + ), + ), + multispace0, + )(s)?; + + let races = times + .into_iter() + .zip(distances) + .map(|(time, distance)| Race { time, distance }) + .collect::>(); + + Ok((rest, Self { races })) + } +} + +fn part1(input: &str) -> Result { + let (rest, racesheet) = RaceSheet::parse(input).map_err(|e| e.to_string())?; + if !rest.is_empty() { + eprintln!("parsing rest found: {rest}"); + panic!(); + } + let result = racesheet + .races + .into_iter() + .map(|race| { + (0..=race.time) + .filter(|hold_time| { + let time_travelled = race.time - hold_time; + let speed = hold_time; + let distance_travelled = time_travelled * speed; + + distance_travelled > race.distance + }) + .collect::>() + .len() + }) + .product(); + Ok(result) +} + +fn part2(input: &str) -> Result { + let (rest, racesheet) = RaceSheet::parse(input).map_err(|e| e.to_string())?; + if !rest.is_empty() { + eprintln!("parsing rest found: {rest}"); + panic!(); + } + + let time = racesheet + .races + .iter() + .map(|race| race.time.to_string()) + .collect::() + .parse::() + .unwrap(); + + let distance = racesheet + .races + .iter() + .map(|race| race.distance.to_string()) + .collect::() + .parse::() + .unwrap(); + + let result = (0..=time) + .filter(|hold_time| { + let time_travelled = time - hold_time; + let speed = hold_time; + let distance_travelled = time_travelled * speed; + + distance_travelled > distance + }) + .collect::>() + .len(); + + Ok(result) +} + +fn main() -> Result<(), String> { + let input = include_str!("../input"); + + let args = std::env::args().skip(1).collect::>(); + let part = args[0].parse::().unwrap(); + + if part == 1 { + println!("Part 1 : {}", part1(input)?); + } else if part == 2 { + println!("Part 2 : {}", part2(input)?); + } else { + panic!("unknown part") + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use indoc::indoc; + + #[test] + fn example_01() { + let input = indoc! {" + Time: 7 15 30 + Distance: 9 40 200 + "}; + + assert_eq!(part1(&input).unwrap(), 288); + } + + #[test] + fn example_02() { + let input = indoc! {" + Time: 7 15 30 + Distance: 9 40 200 + "}; + + assert_eq!(part2(&input).unwrap(), 71503); + } +}