From c57507f579cd648b4f153500d775e646a6f6e689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Sun, 5 Dec 2021 11:09:21 +0100 Subject: [PATCH] implement day 5 --- src/day_05/mod.rs | 67 +++++++++++++++++++++++++ src/day_05/model.rs | 114 ++++++++++++++++++++++++++++++++++++++++++ src/day_05/parsing.rs | 60 ++++++++++++++++++++++ src/lib.rs | 1 + 4 files changed, 242 insertions(+) create mode 100644 src/day_05/mod.rs create mode 100644 src/day_05/model.rs create mode 100644 src/day_05/parsing.rs diff --git a/src/day_05/mod.rs b/src/day_05/mod.rs new file mode 100644 index 0000000..7c36a31 --- /dev/null +++ b/src/day_05/mod.rs @@ -0,0 +1,67 @@ +use aoc_runner_derive::{aoc, aoc_generator}; +use yap::IntoTokens; + +mod model; +mod parsing; + +pub use model::{Grid, Line, Point}; +pub use parsing::parse_lines; + +#[aoc_generator(day5)] +pub fn parse_input(input: &str) -> Vec { + parse_lines(&mut input.into_tokens()) +} + +#[aoc(day5, part1)] +pub fn part1(input: &[Line]) -> usize { + solve(input, &|line| line.is_horizontal() || line.is_vertical()) +} + +#[aoc(day5, part2)] +pub fn part2(input: &[Line]) -> usize { + solve(input, &|line| line.is_horizontal() || line.is_vertical() || line.is_diagonal()) +} + +pub fn solve(input: &[Line], filter: &impl Fn(&&Line) -> bool) -> usize { + let lines: Vec = input + .iter() + .filter(filter) + .copied() + .collect(); + let grid = Grid::from(&lines[..]); + grid.count() +} + +#[cfg(test)] +mod test { + const EXAMPLE_INPUT: &str = "0,9 -> 5,9 +8,0 -> 0,8 +9,4 -> 3,4 +2,2 -> 2,1 +7,0 -> 7,4 +6,4 -> 2,0 +0,9 -> 2,9 +3,4 -> 1,4 +0,0 -> 8,8 +5,5 -> 8,2"; + const RESULT_PART_1: usize = 5; + const RESULT_PART_2: usize = 12; + + #[test] + fn part1_example() { + let result = super::part1(&super::parse_input(EXAMPLE_INPUT)); + assert_eq!(result, RESULT_PART_1); + } + + #[test] + fn part2_example() { + let result = super::part2(&super::parse_input(EXAMPLE_INPUT)); + assert_eq!(result, RESULT_PART_2); + } + + #[test] + fn parse_example() { + let example = super::parse_input(EXAMPLE_INPUT); + assert!(example.len() == 10); + } +} diff --git a/src/day_05/model.rs b/src/day_05/model.rs new file mode 100644 index 0000000..458d084 --- /dev/null +++ b/src/day_05/model.rs @@ -0,0 +1,114 @@ +#[derive(Debug, PartialEq, Copy, Clone)] +pub struct Point { + pub x: usize, + pub y: usize, +} + +impl Point { + fn get_bounds(points: &[Point]) -> Point { + let x = points.iter().map(|p| p.x).max().unwrap_or_default(); + let y = points.iter().map(|p| p.y).max().unwrap_or_default(); + Point { x, y } + } +} + +#[derive(Debug, PartialEq, Copy, Clone)] +pub struct Line { + pub start: Point, + pub end: Point, +} + +impl Line { + pub fn is_vertical(&self) -> bool { + self.start.x == self.end.x + } + pub fn is_horizontal(&self) -> bool { + self.start.y == self.end.y + } + pub fn is_diagonal(&self) -> bool { + self.get_x_vec().abs() == self.get_y_vec().abs() + } + pub fn get_x_vec(&self) -> isize { + self.end.x as isize - self.start.x as isize + } + pub fn get_y_vec(&self) -> isize { + self.end.y as isize - self.start.y as isize + } + pub fn get_bounds(&self) -> Point { + Point::get_bounds(&[self.start, self.end]) + } + pub fn get_points(&self) -> Vec { + let x_vector = self.get_x_vec(); + let y_vector = self.get_y_vec(); + let x_distance = x_vector.abs() as usize; + let y_distance = y_vector.abs() as usize; + let distance = if x_distance >= y_distance { + x_distance + } else { + y_distance + }; + let mut points = Vec::new(); + for i in 0..=distance { + let x = (self.start.x as isize + + (x_vector as f64 / distance as f64 * i as f64) as isize) + as usize; + let y = (self.start.y as isize + + (y_vector as f64 / distance as f64 * i as f64) as isize) + as usize; + points.push(Point { x, y }) + } + points + } +} + +pub struct Grid { + pub grid: Vec>, +} + +impl From<&[Line]> for Grid { + fn from(lines: &[Line]) -> Self { + let bounds = Point::get_bounds(&lines.iter().map(Line::get_bounds).collect::>()); + let mut grid_line = Vec::with_capacity(bounds.x); + for _ in 0..=bounds.x { + grid_line.push(0) + } + let mut grid = Vec::with_capacity(bounds.y); + for _ in 0..=bounds.y { + grid.push(grid_line.clone()) + } + + lines + .iter() + .flat_map(Line::get_points) + .for_each(|point| grid[point.y][point.x] += 1); + + Grid { grid } + } +} + +impl Grid { + pub fn count(&self) -> usize { + self.grid + .iter() + .flat_map(|line| line.iter()) + .filter(|val| **val >= 2) + .count() + } +} + +impl ToString for Grid { + fn to_string(&self) -> String { + let mut string = String::new(); + self.grid.iter().for_each(|line| { + line.iter().for_each(|point| { + if *point == 0 { + string += "."; + } else { + string += &format!("{}", point); + } + }); + string += "\n"; + }); + string + } +} diff --git a/src/day_05/parsing.rs b/src/day_05/parsing.rs new file mode 100644 index 0000000..60b3ddc --- /dev/null +++ b/src/day_05/parsing.rs @@ -0,0 +1,60 @@ +use super::model::{Line, Point}; +use crate::parsing::{newline, parse_number}; +use yap::{IntoTokens, Tokens}; + +pub fn parse_point(tokens: &mut impl Tokens) -> Option { + let numbers: Vec = tokens + .sep_by(|t| parse_number(t), |t| t.token(',')) + .collect(); + if numbers.len() == 2 { + Some(Point { + x: numbers[0], + y: numbers[1], + }) + } else { + None + } +} + +pub fn parse_line(tokens: &mut impl Tokens) -> Option { + let points: Vec = tokens + .sep_by(|t| parse_point(t), |t| t.tokens(" -> ".into_tokens())) + .collect(); + if points.len() == 2 { + Some(Line { + start: points[0], + end: points[1], + }) + } else { + None + } +} + +pub fn parse_lines(tokens: &mut impl Tokens) -> Vec { + tokens.sep_by(|t| parse_line(t), |t| newline(t)).collect() +} + +#[cfg(test)] +mod test { + use super::super::model::{Line, Point}; + use yap::IntoTokens; + + #[test] + fn parse_point() { + assert_eq!( + Some(Point { x: 1, y: 3 }), + super::parse_point(&mut "1,3".into_tokens()) + ); + } + + #[test] + fn parse_line() { + assert_eq!( + Some(Line { + start: Point { x: 1, y: 3 }, + end: Point { x: 5, y: 3 } + }), + super::parse_line(&mut "1,3 -> 5,3".into_tokens()) + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index ad13ede..eedabef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,5 +6,6 @@ pub mod day_01; pub mod day_02; pub mod day_03; pub mod day_04; +pub mod day_05; aoc_lib! { year = 2021 }