Skip to content

Commit

Permalink
AoC 2023 Day 3 - rust
Browse files Browse the repository at this point in the history
  • Loading branch information
pareronia committed Dec 3, 2023
1 parent 9c99b59 commit ea5a575
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 11 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) | | | | | | | | | | | | | | | | | | | | | | |
<!-- @END:ImplementationsTable:2023@ -->

## 2022
Expand Down
9 changes: 9 additions & 0 deletions src/main/rust/AoC2023_03/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
121 changes: 121 additions & 0 deletions src/main/rust/AoC2023_03/src/main.rs
Original file line number Diff line number Diff line change
@@ -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<EnginePart>;
type Output1 = u32;
type Output2 = u32;

aoc::puzzle_year_day!(2023, 3);

fn parse_input(&self, lines: Vec<String>) -> Vec<EnginePart> {
fn find_engine_part(
grid: &CharGrid,
row: usize,
colspan: (usize, usize),
) -> Option<EnginePart> {
(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::<u32>()
.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::<Vec<_>>()
}

fn part_1(&self, engine_parts: &Vec<EnginePart>) -> u32 {
engine_parts.iter().map(|ep| ep.number).sum()
}

fn part_2(&self, engine_parts: &Vec<EnginePart>) -> u32 {
let mut d = BTreeMap::<Cell, Vec<u32>>::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();
}
}
9 changes: 9 additions & 0 deletions src/main/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

141 changes: 131 additions & 10 deletions src/main/rust/aoc/src/grid.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::cmp::Ordering;
use std::fmt::{Display, Error, Formatter};

#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
Expand All @@ -23,6 +24,41 @@ impl Cell {
}
ans
}

pub fn all_neighbours(&self) -> Vec<Cell> {
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<Ordering> {
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 {
Expand Down Expand Up @@ -104,6 +140,15 @@ pub trait Grid {

fn get_data_mut(&mut self) -> &mut Vec<Vec<Self::Item>>;

fn get_row_as_string(&self, row: usize) -> String;

// TODO: iterator
fn get_rows_as_string(&self) -> Vec<String> {
(0..self.height())
.map(|row| self.get_row_as_string(row))
.collect()
}

fn as_string(&self) -> String {
self.get_data()
.iter()
Expand Down Expand Up @@ -225,17 +270,20 @@ pub trait Grid {

fn capital_neighbours(&self, cell: &Cell) -> Vec<Cell> {
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<Cell> {
let mut ans = vec![];
for c in cell.all_neighbours() {
if self.in_bounds(&c) {
ans.push(c);
}
}
ans
}
Expand Down Expand Up @@ -321,6 +369,10 @@ impl Grid for IntGrid {
fn get_data_mut(&mut self) -> &mut Vec<Vec<u32>> {
&mut self.data
}

fn get_row_as_string(&self, _row: usize) -> String {
todo!();
}
}

impl Display for IntGrid {
Expand Down Expand Up @@ -368,6 +420,10 @@ impl Grid for CharGrid {
fn get_data_mut(&mut self) -> &mut Vec<Vec<char>> {
&mut self.data
}

fn get_row_as_string(&self, row: usize) -> String {
self.get_data()[row].iter().collect()
}
}

impl Display for CharGrid {
Expand Down Expand Up @@ -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"]);
Expand Down Expand Up @@ -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 =
Expand Down

0 comments on commit ea5a575

Please sign in to comment.