From ea5a57578e63202761bd65f662692e9dd8202dfb Mon Sep 17 00:00:00 2001 From: pareronia <49491686+pareronia@users.noreply.github.com> Date: Sun, 3 Dec 2023 16:42:13 +0000 Subject: [PATCH] AoC 2023 Day 3 - rust --- README.md | 2 +- src/main/rust/AoC2023_03/Cargo.toml | 9 ++ src/main/rust/AoC2023_03/src/main.rs | 121 +++++++++++++++++++++++ src/main/rust/Cargo.lock | 9 ++ src/main/rust/aoc/src/grid.rs | 141 +++++++++++++++++++++++++-- 5 files changed, 271 insertions(+), 11 deletions(-) create mode 100644 src/main/rust/AoC2023_03/Cargo.toml create mode 100644 src/main/rust/AoC2023_03/src/main.rs diff --git a/README.md b/README.md index 8a2c7f9a..f5d5558a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ | bash | | | | | | | | | | | | | | | | | | | | | | | | | | | c++ | | | | | | | | | | | | | | | | | | | | | | | | | | | julia | | | | | | | | | | | | | | | | | | | | | | | | | | -| rust | [✓](src/main/rust/AoC2023_01/src/main.rs) | [✓](src/main/rust/AoC2023_02/src/main.rs) | | | | | | | | | | | | | | | | | | | | | | | | +| rust | [✓](src/main/rust/AoC2023_01/src/main.rs) | [✓](src/main/rust/AoC2023_02/src/main.rs) | [✓](src/main/rust/AoC2023_03/src/main.rs) | | | | | | | | | | | | | | | | | | | | | | | ## 2022 diff --git a/src/main/rust/AoC2023_03/Cargo.toml b/src/main/rust/AoC2023_03/Cargo.toml new file mode 100644 index 00000000..abcd6ad6 --- /dev/null +++ b/src/main/rust/AoC2023_03/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "AoC2023_03" +version = "0.1.0" +edition = "2021" + +[dependencies] +aoc = { path = "../aoc" } +regex = "1.9" +lazy_static = "1.4" diff --git a/src/main/rust/AoC2023_03/src/main.rs b/src/main/rust/AoC2023_03/src/main.rs new file mode 100644 index 00000000..3acf01d6 --- /dev/null +++ b/src/main/rust/AoC2023_03/src/main.rs @@ -0,0 +1,121 @@ +#![allow(non_snake_case)] + +use aoc::{ + grid::{Cell, CharGrid, Grid}, + Puzzle, +}; +use lazy_static::lazy_static; +use regex::Regex; +use std::collections::BTreeMap; + +lazy_static! { + static ref REGEX_N: Regex = Regex::new(r"[0-9]+").unwrap(); +} + +#[derive(Debug)] +struct EnginePart { + part: char, + number: u32, + pos: Cell, +} + +struct AoC2023_03; + +impl AoC2023_03 {} + +impl aoc::Puzzle for AoC2023_03 { + type Input = Vec; + type Output1 = u32; + type Output2 = u32; + + aoc::puzzle_year_day!(2023, 3); + + fn parse_input(&self, lines: Vec) -> Vec { + fn find_engine_part( + grid: &CharGrid, + row: usize, + colspan: (usize, usize), + ) -> Option { + (colspan.0..colspan.1) + .flat_map(|col| grid.all_neighbours(&Cell::at(row, col))) + .filter(|n| !grid.get(n).is_ascii_digit()) + .filter(|n| grid.get(n) != '.') + .map(|n| { + let number = grid.get_row_as_string(row) + [colspan.0..colspan.1] + .parse::() + .unwrap(); + EnginePart { + part: grid.get(&n), + number, + pos: n, + } + }) + .next() + } + + let grid = CharGrid::from(&lines.iter().map(|s| s.as_str()).collect()); + grid.get_rows_as_string() + .iter() + .enumerate() + .flat_map(|(r, row)| { + let mut eps = vec![]; + for m in REGEX_N.find_iter(row) { + eps.push(find_engine_part(&grid, r, (m.start(), m.end()))); + } + eps.into_iter() + }) + .flatten() + .collect::>() + } + + fn part_1(&self, engine_parts: &Vec) -> u32 { + engine_parts.iter().map(|ep| ep.number).sum() + } + + fn part_2(&self, engine_parts: &Vec) -> u32 { + let mut d = BTreeMap::>::new(); + engine_parts + .iter() + .filter(|ep| ep.part == '*') + .for_each(|ep| d.entry(ep.pos).or_default().push(ep.number)); + d.iter() + .filter(|(_, v)| v.len() == 2) + .map(|(_, v)| v[0] * v[1]) + .sum() + } + + fn samples(&self) { + aoc::puzzle_samples! { + self, part_1, TEST, 4361, + self, part_2, TEST, 467835 + }; + } +} + +fn main() { + AoC2023_03 {}.run(std::env::args()); +} + +const TEST: &str = "\ +467..114.. +...*...... +..35..633. +......#... +617*...... +.....+.58. +..592..... +......755. +...$.*.... +.664.598.. +"; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn samples() { + AoC2023_03 {}.samples(); + } +} diff --git a/src/main/rust/Cargo.lock b/src/main/rust/Cargo.lock index 58640e86..472a2319 100644 --- a/src/main/rust/Cargo.lock +++ b/src/main/rust/Cargo.lock @@ -272,6 +272,15 @@ dependencies = [ "aoc", ] +[[package]] +name = "AoC2023_03" +version = "0.1.0" +dependencies = [ + "aoc", + "lazy_static", + "regex", +] + [[package]] name = "aho-corasick" version = "1.0.2" diff --git a/src/main/rust/aoc/src/grid.rs b/src/main/rust/aoc/src/grid.rs index d9f9dab1..94c6999c 100644 --- a/src/main/rust/aoc/src/grid.rs +++ b/src/main/rust/aoc/src/grid.rs @@ -1,3 +1,4 @@ +use std::cmp::Ordering; use std::fmt::{Display, Error, Formatter}; #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] @@ -23,6 +24,41 @@ impl Cell { } ans } + + pub fn all_neighbours(&self) -> Vec { + let mut ans = vec![]; + if self.row > 0 { + ans.push(Cell::at(self.row - 1, self.col)); + ans.push(Cell::at(self.row - 1, self.col + 1)); + if self.col > 0 { + ans.push(Cell::at(self.row - 1, self.col - 1)); + } + } + ans.push(Cell::at(self.row, self.col + 1)); + ans.push(Cell::at(self.row + 1, self.col + 1)); + ans.push(Cell::at(self.row + 1, self.col)); + if self.col > 0 { + ans.push(Cell::at(self.row, self.col - 1)); + ans.push(Cell::at(self.row + 1, self.col - 1)); + } + ans + } +} + +impl PartialOrd for Cell { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Cell { + fn cmp(&self, other: &Self) -> Ordering { + if self.row == other.row { + self.col.cmp(&other.col) + } else { + self.row.cmp(&other.row) + } + } } pub struct CellRange { @@ -104,6 +140,15 @@ pub trait Grid { fn get_data_mut(&mut self) -> &mut Vec>; + fn get_row_as_string(&self, row: usize) -> String; + + // TODO: iterator + fn get_rows_as_string(&self) -> Vec { + (0..self.height()) + .map(|row| self.get_row_as_string(row)) + .collect() + } + fn as_string(&self) -> String { self.get_data() .iter() @@ -225,17 +270,20 @@ pub trait Grid { fn capital_neighbours(&self, cell: &Cell) -> Vec { let mut ans = vec![]; - if cell.row > 0 { - ans.push(Cell::at(cell.row - 1, cell.col)); - } - if self.valid_column_index(cell.col + 1) { - ans.push(Cell::at(cell.row, cell.col + 1)); - } - if self.valid_row_index(cell.row + 1) { - ans.push(Cell::at(cell.row + 1, cell.col)); + for c in cell.capital_neighbours() { + if self.in_bounds(&c) { + ans.push(c); + } } - if cell.col > 0 { - ans.push(Cell::at(cell.row, cell.col - 1)); + ans + } + + fn all_neighbours(&self, cell: &Cell) -> Vec { + let mut ans = vec![]; + for c in cell.all_neighbours() { + if self.in_bounds(&c) { + ans.push(c); + } } ans } @@ -321,6 +369,10 @@ impl Grid for IntGrid { fn get_data_mut(&mut self) -> &mut Vec> { &mut self.data } + + fn get_row_as_string(&self, _row: usize) -> String { + todo!(); + } } impl Display for IntGrid { @@ -368,6 +420,10 @@ impl Grid for CharGrid { fn get_data_mut(&mut self) -> &mut Vec> { &mut self.data } + + fn get_row_as_string(&self, row: usize) -> String { + self.get_data()[row].iter().collect() + } } impl Display for CharGrid { @@ -468,6 +524,12 @@ mod tests { assert_eq!(grid, CharGrid::from(&vec!["YOY", "OYO", "YOY",])); } + #[test] + pub fn char_get_row_as_string() { + let grid = CharGrid::from(&vec!["XXX", "YYY", "ZZZ"]); + assert_eq!(grid.get_row_as_string(0), "XXX"); + } + #[test] pub fn iterator() { let grid = IntGrid::from(&vec!["12", "34"]); @@ -539,6 +601,65 @@ mod tests { ); } + #[test] + pub fn all_neighbours() { + let grid = + IntGrid::from(&vec!["12345", "12345", "12345", "12345", "12345"]); + assert_eq!( + grid.all_neighbours(&Cell::at(2, 2)), + vec![ + Cell::at(1, 2), + Cell::at(1, 3), + Cell::at(1, 1), + Cell::at(2, 3), + Cell::at(3, 3), + Cell::at(3, 2), + Cell::at(2, 1), + Cell::at(3, 1), + ] + ); + assert_eq!( + grid.all_neighbours(&Cell::at(2, 0)), + vec![ + Cell::at(1, 0), + Cell::at(1, 1), + Cell::at(2, 1), + Cell::at(3, 1), + Cell::at(3, 0), + ] + ); + assert_eq!( + grid.all_neighbours(&Cell::at(2, 4)), + vec![ + Cell::at(1, 4), + Cell::at(1, 3), + Cell::at(3, 4), + Cell::at(2, 3), + Cell::at(3, 3), + ] + ); + assert_eq!( + grid.all_neighbours(&Cell::at(0, 2)), + vec![ + Cell::at(0, 3), + Cell::at(1, 3), + Cell::at(1, 2), + Cell::at(0, 1), + Cell::at(1, 1), + ] + ); + assert_eq!( + grid.all_neighbours(&Cell::at(4, 2)), + vec![ + Cell::at(3, 2), + Cell::at(3, 3), + Cell::at(3, 1), + Cell::at(4, 3), + Cell::at(4, 1), + ] + ); + } + #[test] pub fn find_first_matching() { let grid =