From 84c8c5e04029ea671ab08a42e9375660cc9b0d34 Mon Sep 17 00:00:00 2001 From: red Date: Tue, 21 May 2024 21:04:26 +0200 Subject: [PATCH] Year 2018: Day 06 --- CHANGELOG.md | 4 + Cargo.toml | 2 +- NOTES_2018.md | 4 + README.md | 2 +- benches/year_2018.rs | 19 ++++- inputs/year_2018/day_06_input | 50 +++++++++++++ src/year_2018.rs | 9 +++ src/year_2018/day_06.rs | 136 ++++++++++++++++++++++++++++++++++ 8 files changed, 223 insertions(+), 3 deletions(-) create mode 100644 inputs/year_2018/day_06_input create mode 100644 src/year_2018/day_06.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 101436c..a0ea4e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ Of note: - The changelog 2015.5.2 has been rewritten from each commit content. - This file may be amended entirely in the future to adhere to the [GNU Changelog style](https://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html#Style-of-Change-Logs) +## [2018.6.1] +### Added +- Solved [exercice for 2018, day 06](src/year_2018/06.rs). + ## [2018.5.1] ### Added - Solved [exercice for 2018, day 05](src/year_2018/05.rs). diff --git a/Cargo.toml b/Cargo.toml index 29d89e6..589cd38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "advent-rs" -version = "2018.5.1" +version = "2018.6.1" edition = "2021" authors = ["Arnaud 'red' Rouyer"] readme = "README.md" diff --git a/NOTES_2018.md b/NOTES_2018.md index f0588b4..3090d0b 100644 --- a/NOTES_2018.md +++ b/NOTES_2018.md @@ -18,3 +18,7 @@ There's something about this "find the most used record" type of exercises that ## Day 05: Alchemical Reduction As a programmer, your MUST have used, at least once, the command [`man ascii`](https://man.archlinux.org/man/core/man-pages/ascii.7.en), if only to find the integer value of a specific number. But were you to look specifically at the hexadecimal table, something may have caught your attention: uppercase letters, and their equivalent lowercase values, are all separated by 32 characters. Now, isn't that a nice number that plays VERY WELL with bit masks? Here's a little [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d9a88346debfc3c2470c6b1527202f30) to help you! + +## Day 06: Chronal Coordinates + +At first, I felt like expanding each area one by one, until I realised that just calculating distances was just easier. diff --git a/README.md b/README.md index 972550d..f312061 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ I'm also adding notes that may be useful if you're (like me) discovering Rust: - [2015, complete!](NOTES_2015.md) - [2016, complete!](NOTES_2016.md) - [2017, complete!](NOTES_2017.md) -- [2018, up to day 05](NOTES_2018.md) +- [2018, up to day 06](NOTES_2018.md) # Regarding style rules I'm gonna use a mix of what `cargo fmt` does, with some stuff that feels more natural to me. diff --git a/benches/year_2018.rs b/benches/year_2018.rs index 47685bb..1e38209 100644 --- a/benches/year_2018.rs +++ b/benches/year_2018.rs @@ -3,6 +3,7 @@ use advent_rs::year_2018::day_02; use advent_rs::year_2018::day_03; use advent_rs::year_2018::day_04; use advent_rs::year_2018::day_05; +use advent_rs::year_2018::day_06; use criterion::{black_box, criterion_group, criterion_main, Criterion}; fn year_2018_day_01(c: &mut Criterion) { @@ -80,12 +81,28 @@ fn year_2018_day_05(c: &mut Criterion) { g2018_day_05.finish(); } +fn year_2018_day_06(c: &mut Criterion) { + let input = include_str!("../inputs/year_2018/day_06_input"); + assert_eq!(day_06::day_06_v1(input), 3_251); + assert_eq!(day_06::day_06_v2(input), 47_841); + + let mut g2018_day_06 = c.benchmark_group("year_2018::day_06"); + g2018_day_06.bench_function("year_2018::day_06_v1", |b| { + b.iter(|| day_06::day_06_v1(black_box(input))) + }); + g2018_day_06.bench_function("year_2018::day_06_v2", |b| { + b.iter(|| day_06::day_06_v2(black_box(input))) + }); + g2018_day_06.finish(); +} + criterion_group!( benches, year_2018_day_01, year_2018_day_02, year_2018_day_03, year_2018_day_04, - year_2018_day_05 + year_2018_day_05, + year_2018_day_06 ); criterion_main!(benches); diff --git a/inputs/year_2018/day_06_input b/inputs/year_2018/day_06_input new file mode 100644 index 0000000..250be86 --- /dev/null +++ b/inputs/year_2018/day_06_input @@ -0,0 +1,50 @@ +118, 274 +102, 101 +216, 203 +208, 251 +309, 68 +330, 93 +91, 179 +298, 278 +201, 99 +280, 272 +141, 312 +324, 290 +41, 65 +305, 311 +198, 68 +231, 237 +164, 224 +103, 189 +216, 207 +164, 290 +151, 91 +166, 250 +129, 149 +47, 231 +249, 100 +262, 175 +299, 237 +62, 288 +228, 219 +224, 76 +310, 173 +80, 46 +312, 65 +183, 158 +272, 249 +57, 141 +331, 191 +163, 359 +271, 210 +142, 137 +349, 123 +55, 268 +160, 82 +180, 70 +231, 243 +133, 353 +246, 315 +164, 206 +229, 97 +268, 94 \ No newline at end of file diff --git a/src/year_2018.rs b/src/year_2018.rs index 2b63cdd..bcad9c0 100644 --- a/src/year_2018.rs +++ b/src/year_2018.rs @@ -7,6 +7,7 @@ pub mod day_02; pub mod day_03; pub mod day_04; pub mod day_05; +pub mod day_06; pub fn solve(day: u8, part: u8, input: impl Into) -> Option { if part > 2 { @@ -19,6 +20,7 @@ pub fn solve(day: u8, part: u8, input: impl Into) -> Option { 3 => Some(day_03::day_03(part, input).to_string()), 4 => Some(day_04::day_04(part, input).to_string()), 5 => Some(day_05::day_05(part, input).to_string()), + 6 => Some(day_06::day_06(part, input).to_string()), _ => None, } } @@ -61,4 +63,11 @@ mod tests { assert_eq!(day_05::day_05_v1(input), 11_298); assert_eq!(day_05::day_05_v2(input), 5_148); } + + #[test] + fn day_06() { + let input = include_str!("../inputs/year_2018/day_06_input"); + assert_eq!(day_06::day_06_v1(input), 3_251); + assert_eq!(day_06::day_06_v2(input), 47_841); + } } diff --git a/src/year_2018/day_06.rs b/src/year_2018/day_06.rs new file mode 100644 index 0000000..01176d9 --- /dev/null +++ b/src/year_2018/day_06.rs @@ -0,0 +1,136 @@ +use std::cmp::Ordering; +use std::collections::HashMap; +use std::collections::HashSet; + +use itertools::Itertools; + +type Coordinate = (i32, i32); + +struct ChronalCoordinates { + points: HashMap, + left: i32, + top: i32, + right: i32, + bottom: i32, +} + +impl ChronalCoordinates { + fn new(input: &str) -> Self { + let mut left = std::i32::MAX; + let mut top = std::i32::MAX; + let mut right = std::i32::MIN; + let mut bottom = std::i32::MIN; + + let points = input + .lines() + .enumerate() + .map(|(line_id, line)| { + let line_id = line_id as u8 + 1u8; + let coord: Coordinate = line + .split(", ") + .map(|v| v.parse::().unwrap()) + .collect_tuple() + .unwrap(); + left = std::cmp::min(left, coord.0); + top = std::cmp::min(top, coord.1); + right = std::cmp::max(right, coord.0); + bottom = std::cmp::max(bottom, coord.1); + (line_id, coord) + }) + .collect::>(); + + ChronalCoordinates { + points, + left, + top, + right, + bottom, + } + } + + fn solve_part_one(&self) -> u32 { + let mut countable_ids: HashMap = HashMap::new(); + let mut infinite_ids: HashSet = HashSet::new(); + + for x in self.left..=self.right { + for y in self.top..=self.bottom { + let mut closest_distance = std::i32::MAX; + let mut closest_id = 0; + + for (id, point) in self.points.iter() { + let distance = (x - point.0).abs() + (y - point.1).abs(); + + match distance.cmp(&closest_distance) { + Ordering::Greater => (), + Ordering::Equal => closest_id = 0, + Ordering::Less => { + closest_distance = distance; + closest_id = *id; + } + } + + if closest_id == 0 { + continue; + } + } + + if x == self.left || x == self.right || y == self.top || y == self.bottom { + infinite_ids.insert(closest_id); + countable_ids.remove(&closest_id); + } else if !infinite_ids.contains(&closest_id) { + *countable_ids.entry(closest_id).or_insert(0) += 1; + } + } + } + + *countable_ids.iter().max_by_key(|(_, v)| *v).unwrap().1 + } + + fn solve_part_two(&self, max_size: i32) -> u32 { + let mut area = 0; + + for x in self.left..=self.right { + for y in self.top..=self.bottom { + let distance = self.points.iter().fold(0, |acc, (_, point)| { + acc + (x - point.0).abs() + (y - point.1).abs() + }); + if distance < max_size { + area += 1; + } + } + } + + area + } +} + +pub fn day_06_v1(input: impl Into) -> u32 { + ChronalCoordinates::new(&input.into()).solve_part_one() +} +pub fn day_06_v2(input: impl Into) -> u32 { + ChronalCoordinates::new(&input.into()).solve_part_two(10_000) +} + +solvable!(day_06, day_06_v1, day_06_v2, u32); + +#[cfg(test)] +mod tests { + use super::*; + + const SAMPLE: &str = "1, 1\n\ + 1, 6\n\ + 8, 3\n\ + 3, 4\n\ + 5, 5\n\ + 8, 9"; + + #[test] + fn works_with_samples_v1() { + assert_eq!(day_06_v1(SAMPLE), 17); + } + + #[test] + fn works_with_samples_v2() { + assert_eq!(ChronalCoordinates::new(SAMPLE).solve_part_two(32), 16); + } +}