From 8d5a501f76ef83f3b6cddbd0f93f7ebd44871d95 Mon Sep 17 00:00:00 2001 From: Luciano Mammino Date: Thu, 7 Dec 2023 18:27:16 +0000 Subject: [PATCH] added solution for y2023/ex06 --- Cargo.toml | 4 +- scaffold.mjs | 2 +- y2023/ex06/Cargo.toml | 16 +++ y2023/ex06/README.md | 75 +++++++++++ y2023/ex06/benches/bench_y2023ex06.rs | 11 ++ y2023/ex06/input.txt | 2 + y2023/ex06/src/lib.rs | 185 ++++++++++++++++++++++++++ 7 files changed, 291 insertions(+), 4 deletions(-) create mode 100644 y2023/ex06/Cargo.toml create mode 100644 y2023/ex06/README.md create mode 100644 y2023/ex06/benches/bench_y2023ex06.rs create mode 100644 y2023/ex06/input.txt create mode 100644 y2023/ex06/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 447f39d..a0343f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,9 +72,7 @@ members = [ "y2023/ex03", "y2023/ex04", "y2023/ex05", - "y2023/ex07", - "y2023/ex07", - "y2023/ex07", + "y2023/ex06", "y2023/ex07", ] diff --git a/scaffold.mjs b/scaffold.mjs index 971656f..d1c674d 100755 --- a/scaffold.mjs +++ b/scaffold.mjs @@ -16,7 +16,7 @@ const dayDir = `ex${day.padStart(2, '0')}` await $`mkdir -p ./${yearDir}` // Create rust project -// await $`cd ./y${year} && cargo new --lib ${dayDir}` +await $`cd ./y${year} && cargo new --lib ${dayDir}` // update Cargo.toml const cargoToml = `[package] diff --git a/y2023/ex06/Cargo.toml b/y2023/ex06/Cargo.toml new file mode 100644 index 0000000..c3775a2 --- /dev/null +++ b/y2023/ex06/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "y2023ex06" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nom = "7.1.3" + +[[bench]] +name = "bench_y2023ex06" +harness = false + +[dev-dependencies] +criterion = "0.5.1" diff --git a/y2023/ex06/README.md b/y2023/ex06/README.md new file mode 100644 index 0000000..3b7ab4b --- /dev/null +++ b/y2023/ex06/README.md @@ -0,0 +1,75 @@ +# Day 6: Wait For It + +[Check it out on adventofcode.com](https://adventofcode.com/2023/day/6) + +## Part One + +The ferry quickly brings you across Island Island. After asking around, you discover that there is indeed normally a large pile of sand somewhere near here, but you don't see anything besides lots of water and the small island where the ferry has docked. + +As you try to figure out what to do next, you notice a poster on a wall near the ferry dock. "Boat races! Open to the public! Grand prize is an all-expenses-paid trip to _Desert Island_!" That must be where the sand comes from! Best of all, the boat races are starting in just a few minutes. + +You manage to sign up as a competitor in the boat races just in time. The organizer explains that it's not really a traditional race - instead, you will get a fixed amount of time during which your boat has to travel as far as it can, and you win if your boat goes the farthest. + +As part of signing up, you get a sheet of paper (your puzzle input) that lists the _time_ allowed for each race and also the best _distance_ ever recorded in that race. To guarantee you win the grand prize, you need to make sure you _go farther in each race_ than the current record holder. + +The organizer brings you over to the area where the boat races are held. The boats are much smaller than you expected - they're actually _toy boats_, each with a big button on top. Holding down the button _charges the boat_, and releasing the button _allows the boat to move_. Boats move faster if their button was held longer, but time spent holding the button counts against the total race time. You can only hold the button at the start of the race, and boats don't move until the button is released. + +For example: + + Time: 7 15 30 + Distance: 9 40 200 + + +This document describes three races: + +* The first race lasts 7 milliseconds. The record distance in this race is 9 millimeters. +* The second race lasts 15 milliseconds. The record distance in this race is 40 millimeters. +* The third race lasts 30 milliseconds. The record distance in this race is 200 millimeters. + +Your toy boat has a starting speed of _zero millimeters per millisecond_. For each whole millisecond you spend at the beginning of the race holding down the button, the boat's speed increases by _one millimeter per millisecond_. + +So, because the first race lasts 7 milliseconds, you only have a few options: + +* Don't hold the button at all (that is, hold it for _`0` milliseconds_) at the start of the race. The boat won't move; it will have traveled _`0` millimeters_ by the end of the race. +* Hold the button for _`1` millisecond_ at the start of the race. Then, the boat will travel at a speed of `1` millimeter per millisecond for 6 milliseconds, reaching a total distance traveled of _`6` millimeters_. +* Hold the button for _`2` milliseconds_, giving the boat a speed of `2` millimeters per millisecond. It will then get 5 milliseconds to move, reaching a total distance of _`10` millimeters_. +* Hold the button for _`3` milliseconds_. After its remaining 4 milliseconds of travel time, the boat will have gone _`12` millimeters_. +* Hold the button for _`4` milliseconds_. After its remaining 3 milliseconds of travel time, the boat will have gone _`12` millimeters_. +* Hold the button for _`5` milliseconds_, causing the boat to travel a total of _`10` millimeters_. +* Hold the button for _`6` milliseconds_, causing the boat to travel a total of _`6` millimeters_. +* Hold the button for _`7` milliseconds_. That's the entire duration of the race. You never let go of the button. The boat can't move until you let go of the button. Please make sure you let go of the button so the boat gets to move. _`0` millimeters_. + +Since the current record for this race is `9` millimeters, there are actually `_4_` different ways you could win: you could hold the button for `2`, `3`, `4`, or `5` milliseconds at the start of the race. + +In the second race, you could hold the button for at least `4` milliseconds and at most `11` milliseconds and beat the record, a total of `_8_` different ways to win. + +In the third race, you could hold the button for at least `11` milliseconds and no more than `19` milliseconds and still beat the record, a total of `_9_` ways you could win. + +To see how much margin of error you have, determine the _number of ways you can beat the record_ in each race; in this example, if you multiply these values together, you get `_288_` (`4` \* `8` \* `9`). + +Determine the number of ways you could beat the record in each race. _What do you get if you multiply these numbers together?_ + +Your puzzle answer was `1155175`. + + +## Part Two + +As the race is about to start, you realize the piece of paper with race times and record distances you got earlier actually just has very bad [kerning](https://en.wikipedia.org/wiki/Kerning). There's really _only one race_ - ignore the spaces between the numbers on each line. + +So, the example from before: + + Time: 7 15 30 + Distance: 9 40 200 + + +...now instead means this: + + Time: 71530 + Distance: 940200 + + +Now, you have to figure out how many ways there are to win this single race. In this example, the race lasts for _`71530` milliseconds_ and the record distance you need to beat is _`940200` millimeters_. You could hold the button anywhere from `14` to `71516` milliseconds and beat the record, a total of `_71503_` ways! + +_How many ways can you beat the record in this one much longer race?_ + +Your puzzle answer was `35961505`. diff --git a/y2023/ex06/benches/bench_y2023ex06.rs b/y2023/ex06/benches/bench_y2023ex06.rs new file mode 100644 index 0000000..b13e0e9 --- /dev/null +++ b/y2023/ex06/benches/bench_y2023ex06.rs @@ -0,0 +1,11 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use y2023ex06::{part1, part2}; + +fn criterion_benchmark(c: &mut Criterion) { + let input = include_str!("../input.txt"); + c.bench_function("y2023ex06::part1", |b| b.iter(|| part1(black_box(input)))); + c.bench_function("y2023ex06::part2", |b| b.iter(|| part2(black_box(input)))); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/y2023/ex06/input.txt b/y2023/ex06/input.txt new file mode 100644 index 0000000..a523b19 --- /dev/null +++ b/y2023/ex06/input.txt @@ -0,0 +1,2 @@ +Time: 60 80 86 76 +Distance: 601 1163 1559 1300 \ No newline at end of file diff --git a/y2023/ex06/src/lib.rs b/y2023/ex06/src/lib.rs new file mode 100644 index 0000000..bf969ed --- /dev/null +++ b/y2023/ex06/src/lib.rs @@ -0,0 +1,185 @@ +use nom::{ + bytes::complete::tag, + character::complete::{newline, space1, u64}, + combinator::complete, + multi::separated_list1, + IResult, +}; + +#[derive(Debug, PartialEq, Eq)] +struct Race { + time: u64, + distance: u64, +} + +#[derive(Debug, PartialEq, Eq)] +struct Boat { + speed: u64, + distance_travelled: u64, + time_passed: u64, +} + +impl Boat { + fn new(speed: u64) -> Self { + Self { + speed, + distance_travelled: 0, + time_passed: 0, + } + } + + fn tick(&mut self) { + self.distance_travelled += self.speed; + self.time_passed += 1; + } + + fn race(mut self, time_limit: u64) -> u64 { + self.time_passed = self.speed; // time spent pressing the button + while self.time_passed < time_limit { + // race + self.tick(); + } + self.distance_travelled + } +} + +fn parse_input(input: &str) -> IResult<&str, Vec> { + // Time: 7 15 30 + // Distance: 9 40 200 + let (input, _) = tag("Time:")(input)?; + let (input, _) = space1(input)?; + let (input, times) = separated_list1(space1, u64)(input)?; + let (input, _) = newline(input)?; + let (input, _) = tag("Distance:")(input)?; + let (input, _) = space1(input)?; + let (input, distances) = complete(separated_list1(space1, u64))(input)?; + let races: Vec = times + .iter() + .zip(distances.iter()) + .map(|(time, distance)| Race { + time: *time, + distance: *distance, + }) + .collect(); + + Ok((input, races)) +} + +pub fn part1(input: &str) -> u64 { + let (_, races) = parse_input(input).unwrap(); + races + .iter() + .map(|race| { + let mut winning_count = 0; + for i in 1..race.time { + let boat = Boat::new(i); + let distance_travelled = boat.race(race.time); + if distance_travelled > race.distance { + winning_count += 1; + } + } + winning_count + }) + .product() +} + +pub fn part2(input: &str) -> usize { + let (_, races) = parse_input(input).unwrap(); + let (time, distance) = races.iter().fold( + (String::new(), String::new()), + |(time, distance), curr_race| { + let mut time = time.clone(); + time.push_str(curr_race.time.to_string().as_str()); + let mut distance = distance.clone(); + distance.push_str(curr_race.distance.to_string().as_str()); + (time, distance) + }, + ); + let time: u64 = time.parse().unwrap(); + let distance: u64 = distance.parse().unwrap(); + let race = Race { time, distance }; + + let mut winning_count = 0; + for i in 1..race.time { + let boat = Boat::new(i); + let distance_travelled = boat.race(race.time); + if distance_travelled > race.distance { + winning_count += 1; + } + } + winning_count +} + +#[cfg(test)] +mod tests { + use super::*; + const INPUT: &str = include_str!("../input.txt"); + const EXAMPLE_INPUT: &str = "Time: 7 15 30 +Distance: 9 40 200"; + + #[test] + fn test_parse() { + let (_, races) = parse_input(EXAMPLE_INPUT).unwrap(); + assert_eq!( + races, + vec![ + Race { + time: 7, + distance: 9 + }, + Race { + time: 15, + distance: 40 + }, + Race { + time: 30, + distance: 200 + }, + ] + ) + } + + #[test] + fn test_boat() { + let boat1 = Boat::new(1); + assert_eq!(boat1.race(7), 6); + + let boat2 = Boat::new(2); + assert_eq!(boat2.race(7), 10); + + let boat3 = Boat::new(3); + assert_eq!(boat3.race(7), 12); + + let boat4 = Boat::new(4); + assert_eq!(boat4.race(7), 12); + + let boat5 = Boat::new(5); + assert_eq!(boat5.race(7), 10); + + let boat6 = Boat::new(6); + assert_eq!(boat6.race(7), 6); + + let boat7 = Boat::new(7); + assert_eq!(boat7.race(7), 0); + } + + #[test] + fn test_part1_example() { + assert_eq!(part1(EXAMPLE_INPUT), 288); + } + + #[test] + fn test_part1() { + assert_eq!(part1(INPUT), 1155175); + } + + #[test] + fn test_part2_example() { + assert_eq!(part2(EXAMPLE_INPUT), 71503); + } + + #[test] + fn test_part2() { + assert_eq!(part2(INPUT), 35961505); + } +}