Compare commits
2 commits
0948888663
...
ef64f62b28
Author | SHA1 | Date | |
---|---|---|---|
ef64f62b28 | |||
c57507f579 |
|
@ -32,7 +32,7 @@ pub trait State {
|
|||
}
|
||||
fn movements(&mut self, moves: &[Movement]) -> isize {
|
||||
for movement in moves {
|
||||
self.movement(&movement);
|
||||
self.movement(movement);
|
||||
}
|
||||
self.result()
|
||||
}
|
||||
|
@ -96,15 +96,17 @@ pub enum Movement {
|
|||
Up(isize),
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_mut_passed)]
|
||||
impl FromStr for Movement {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut tokens = s.into_tokens();
|
||||
yap::one_of!(ts from &mut tokens;
|
||||
ts.tokens("forward ".chars()).then(|| parse_number(ts).map(|num| Movement::Forward(num))).flatten(),
|
||||
ts.tokens("down ".chars()).then(|| parse_number(ts).map(|num| Movement::Down(num))).flatten(),
|
||||
ts.tokens("up ".chars()).then(|| parse_number(ts).map(|num| Movement::Up(num))).flatten(),
|
||||
).ok_or(())
|
||||
ts.tokens("forward ".chars()).then(|| parse_number(ts).map(Movement::Forward)).flatten(),
|
||||
ts.tokens("down ".chars()).then(|| parse_number(ts).map(Movement::Down)).flatten(),
|
||||
ts.tokens("up ".chars()).then(|| parse_number(ts).map(Movement::Up)).flatten(),
|
||||
)
|
||||
.ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ pub struct Game {
|
|||
|
||||
impl Board {
|
||||
pub fn mark(&mut self, num: usize) -> Option<usize> {
|
||||
if self.finished == true {
|
||||
if self.finished {
|
||||
return None;
|
||||
}
|
||||
for lines in self.fields.iter_mut() {
|
||||
|
|
65
src/day_05/mod.rs
Normal file
65
src/day_05/mod.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
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 }
|
||||
|
|
|
@ -12,10 +12,13 @@ pub fn parse_number<T: Num>(tokens: &mut impl Tokens<Item = char>) -> Option<T>
|
|||
parse_number_with_radix(tokens, 10)
|
||||
}
|
||||
|
||||
pub fn parse_number_with_radix<T: Num>(tokens: &mut impl Tokens<Item = char>, base: u32) -> Option<T> {
|
||||
pub fn parse_number_with_radix<T: Num>(
|
||||
tokens: &mut impl Tokens<Item = char>,
|
||||
base: u32,
|
||||
) -> Option<T> {
|
||||
tokens.skip_tokens_while(|t| *t == ' ');
|
||||
let digits: String = tokens.tokens_while(|c| c.is_digit(base)).collect();
|
||||
if digits.len() > 0 {
|
||||
if !digits.is_empty() {
|
||||
T::from_str_radix(&digits, base).ok()
|
||||
} else {
|
||||
None
|
||||
|
|
Loading…
Reference in a new issue