implement day 5

This commit is contained in:
Jan Christian Grünhage 2021-12-05 11:09:21 +01:00
parent 0948888663
commit c57507f579
4 changed files with 242 additions and 0 deletions

67
src/day_05/mod.rs Normal file
View file

@ -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<Line> {
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<Line> = 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);
}
}

114
src/day_05/model.rs Normal file
View file

@ -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<Point> {
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<Vec<u8>>,
}
impl From<&[Line]> for Grid {
fn from(lines: &[Line]) -> Self {
let bounds = Point::get_bounds(&lines.iter().map(Line::get_bounds).collect::<Vec<Point>>());
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
}
}

60
src/day_05/parsing.rs Normal file
View file

@ -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<Item = char>) -> Option<Point> {
let numbers: Vec<usize> = 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<Item = char>) -> Option<Line> {
let points: Vec<Point> = 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<Item = char>) -> Vec<Line> {
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())
);
}
}

View file

@ -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 }