implement day 5
This commit is contained in:
parent
0948888663
commit
c57507f579
67
src/day_05/mod.rs
Normal file
67
src/day_05/mod.rs
Normal 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
114
src/day_05/model.rs
Normal 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
60
src/day_05/parsing.rs
Normal 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())
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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 }
|
||||
|
|
Loading…
Reference in a new issue