Compare commits
4 commits
7e377d3f92
...
d0216fda25
Author | SHA1 | Date | |
---|---|---|---|
d0216fda25 | |||
123c62f810 | |||
577e2a2c78 | |||
32d1b2b370 |
|
@ -1,33 +1,21 @@
|
|||
use super::model::{Line, Point};
|
||||
use crate::parsing::{newline, parse_number};
|
||||
use yap::{IntoTokens, Tokens};
|
||||
use crate::parsing::{newline, parse_n, parse_number};
|
||||
use yap::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
|
||||
}
|
||||
parse_n(tokens, |t| parse_number(t), |t| t.token(',')).map(|point: [usize; 2]| Point {
|
||||
x: point[0],
|
||||
y: point[1],
|
||||
})
|
||||
}
|
||||
|
||||
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 {
|
||||
parse_n(tokens, |t| parse_point(t), |t| t.tokens(" -> ".chars())).map(|points: [Point; 2]| {
|
||||
Line {
|
||||
start: points[0],
|
||||
end: points[1],
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_lines(tokens: &mut impl Tokens<Item = char>) -> Vec<Line> {
|
||||
|
|
|
@ -7,8 +7,8 @@ use super::OctopusGrid;
|
|||
pub fn parse_octopus_grid(tokens: &mut impl Tokens<Item = char>) -> Option<OctopusGrid> {
|
||||
parse_n(
|
||||
tokens,
|
||||
&|t| parse_n(t, &|t| parse_digit(t), &|_| true),
|
||||
&|t| newline(t),
|
||||
|t| parse_n(t, |t| parse_digit(t), |_| true),
|
||||
|t| newline(t),
|
||||
)
|
||||
.map(|grid| OctopusGrid { grid })
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ pub fn parse_cave_system(tokens: &mut impl Tokens<Item = char>) -> Option<CaveSy
|
|||
}
|
||||
|
||||
pub fn parse_edge(tokens: &mut impl Tokens<Item = char>) -> Option<[Cave; 2]> {
|
||||
parse_n(tokens, &|t| parse_cave(t), &|t| t.token('-'))
|
||||
parse_n(tokens, |t| parse_cave(t), |t| t.token('-'))
|
||||
}
|
||||
|
||||
pub fn parse_cave(tokens: &mut impl Tokens<Item = char>) -> Option<Cave> {
|
||||
|
|
85
src/day_13/mod.rs
Normal file
85
src/day_13/mod.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
mod model;
|
||||
mod parsing;
|
||||
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
pub use model::{Fold, Grid, Input, Point};
|
||||
use yap::IntoTokens;
|
||||
|
||||
#[aoc_generator(day13)]
|
||||
pub fn parse_input(input: &str) -> Input {
|
||||
parsing::parse_input(&mut input.into_tokens()).unwrap()
|
||||
}
|
||||
|
||||
#[aoc(day13, part1)]
|
||||
pub fn part1(input: &Input) -> usize {
|
||||
let Input {
|
||||
mut grid,
|
||||
instructions,
|
||||
} = input.clone();
|
||||
grid.fold(instructions[0]);
|
||||
grid.count()
|
||||
}
|
||||
|
||||
#[aoc(day13, part2)]
|
||||
pub fn part2(input: &Input) -> String {
|
||||
let Input {
|
||||
mut grid,
|
||||
instructions,
|
||||
} = input.clone();
|
||||
|
||||
for instruction in instructions {
|
||||
grid.fold(instruction);
|
||||
}
|
||||
grid.to_string()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Input;
|
||||
|
||||
const EXAMPLE_INPUT: &str = "6,10
|
||||
0,14
|
||||
9,10
|
||||
0,3
|
||||
10,4
|
||||
4,11
|
||||
6,0
|
||||
6,12
|
||||
4,1
|
||||
0,13
|
||||
10,12
|
||||
3,4
|
||||
3,0
|
||||
8,4
|
||||
1,10
|
||||
2,14
|
||||
8,10
|
||||
9,0
|
||||
|
||||
fold along y=7
|
||||
fold along x=5";
|
||||
const RESULT_PART_1: usize = 17;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
let result = super::part1(&super::parse_input(EXAMPLE_INPUT));
|
||||
assert_eq!(result, RESULT_PART_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fold_twice() {
|
||||
let Input {
|
||||
mut grid,
|
||||
instructions,
|
||||
} = super::parse_input(EXAMPLE_INPUT);
|
||||
grid.fold(instructions[0]);
|
||||
assert_eq!(grid.count(), 17);
|
||||
grid.fold(instructions[1]);
|
||||
assert_eq!(grid.count(), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_example() {
|
||||
super::parse_input(EXAMPLE_INPUT);
|
||||
}
|
||||
}
|
93
src/day_13/model.rs
Normal file
93
src/day_13/model.rs
Normal file
|
@ -0,0 +1,93 @@
|
|||
use itertools::Itertools;
|
||||
|
||||
#[derive(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(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Fold {
|
||||
X(usize),
|
||||
Y(usize),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Grid {
|
||||
pub grid: Vec<Vec<bool>>,
|
||||
}
|
||||
|
||||
impl Grid {
|
||||
pub fn new(points: &[Point]) -> Self {
|
||||
let bounds = Point::get_bounds(points);
|
||||
let mut line = Vec::new();
|
||||
for _ in 0..=bounds.x {
|
||||
line.push(false)
|
||||
}
|
||||
let mut grid = Vec::new();
|
||||
for _ in 0..=bounds.y {
|
||||
grid.push(line.clone())
|
||||
}
|
||||
|
||||
for Point { x, y } in points {
|
||||
grid[*y][*x] = true;
|
||||
}
|
||||
Self { grid }
|
||||
}
|
||||
|
||||
pub fn fold(&mut self, instruction: Fold) {
|
||||
match instruction {
|
||||
Fold::X(fold) => {
|
||||
for y in 0..self.grid.len() {
|
||||
for x in fold + 1..self.grid[y].len() {
|
||||
self.grid[y][fold - (x - fold)] |= self.grid[y][x];
|
||||
}
|
||||
self.grid[y].truncate(fold)
|
||||
}
|
||||
}
|
||||
Fold::Y(fold) => {
|
||||
for y in fold + 1..self.grid.len() {
|
||||
for x in 0..self.grid[0].len() {
|
||||
self.grid[fold - (y - fold)][x] |= self.grid[y][x];
|
||||
}
|
||||
}
|
||||
self.grid.truncate(fold)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn count(&self) -> usize {
|
||||
self.grid
|
||||
.iter()
|
||||
.flat_map(|line| line.iter())
|
||||
.filter(|val| **val)
|
||||
.count()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Grid {
|
||||
fn to_string(&self) -> String {
|
||||
self.grid
|
||||
.iter()
|
||||
.map(|line| {
|
||||
line.iter()
|
||||
.map(|val| if *val { "#" } else { "." })
|
||||
.collect::<String>()
|
||||
})
|
||||
.join("\n")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Input {
|
||||
pub grid: Grid,
|
||||
pub instructions: Vec<Fold>,
|
||||
}
|
52
src/day_13/parsing.rs
Normal file
52
src/day_13/parsing.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use yap::Tokens;
|
||||
|
||||
use crate::parsing::{newline, parse_n, parse_number};
|
||||
|
||||
use super::{Fold, Grid, Input, Point};
|
||||
|
||||
pub fn parse_fold(tokens: &mut impl Tokens<Item = char>) -> Option<Fold> {
|
||||
yap::one_of!(ts from tokens;
|
||||
ts.tokens("fold along y=".chars()).then(|| parse_number(ts).map(Fold::Y)).flatten(),
|
||||
ts.tokens("fold along x=".chars()).then(|| parse_number(ts).map(Fold::X)).flatten(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn parse_point(tokens: &mut impl Tokens<Item = char>) -> Option<Point> {
|
||||
parse_n(tokens, |t| parse_number(t), |t| t.token(',')).map(|point: [usize; 2]| Point {
|
||||
x: point[0],
|
||||
y: point[1],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_input(tokens: &mut impl Tokens<Item = char>) -> Option<Input> {
|
||||
let points: Vec<Point> = tokens.sep_by(|t| parse_point(t), |t| newline(t)).collect();
|
||||
if points.is_empty() {
|
||||
return None;
|
||||
}
|
||||
tokens.skip_many(|t| newline(t));
|
||||
let instructions: Vec<Fold> = tokens.sep_by(|t| parse_fold(t), |t| newline(t)).collect();
|
||||
if instructions.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let grid = Grid::new(&points);
|
||||
Some(Input { grid, instructions })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use yap::IntoTokens;
|
||||
|
||||
use crate::day_13::Fold;
|
||||
|
||||
#[test]
|
||||
fn parse_fold() {
|
||||
assert_eq!(
|
||||
super::parse_fold(&mut "fold along y=7".into_tokens()),
|
||||
Some(Fold::Y(7)),
|
||||
);
|
||||
assert_eq!(
|
||||
super::parse_fold(&mut "fold along x=5".into_tokens()),
|
||||
Some(Fold::X(5)),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -14,5 +14,6 @@ pub mod day_09;
|
|||
pub mod day_10;
|
||||
pub mod day_11;
|
||||
pub mod day_12;
|
||||
pub mod day_13;
|
||||
|
||||
aoc_lib! { year = 2021 }
|
||||
|
|
|
@ -30,21 +30,25 @@ pub fn parse_number_with_radix<T: Num>(
|
|||
c.is_digit(base) && remaining >= 0
|
||||
})
|
||||
.collect();
|
||||
if !digits.is_empty() {
|
||||
T::from_str_radix(&digits, base).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
(!digits.is_empty())
|
||||
.then(|| T::from_str_radix(&digits, base).ok())
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub fn parse_n<T: Debug, P: Tokens<Item = char>, const N: usize>(
|
||||
tokens: &mut P,
|
||||
parser: &dyn Fn(&mut P) -> Option<T>,
|
||||
separator: &dyn Fn(&mut P) -> bool,
|
||||
) -> Option<[T; N]> {
|
||||
pub fn parse_n<R, T, I, P, S, const N: usize>(
|
||||
tokens: &mut T,
|
||||
parser: P,
|
||||
separator: S,
|
||||
) -> Option<[R; N]>
|
||||
where
|
||||
R: Debug,
|
||||
T: Tokens<Item = I>,
|
||||
P: FnMut(&mut T) -> Option<R>,
|
||||
S: FnMut(&mut T) -> bool,
|
||||
{
|
||||
tokens
|
||||
.sep_by(parser, separator)
|
||||
.collect::<Vec<T>>()
|
||||
.collect::<Vec<R>>()
|
||||
.try_into()
|
||||
.ok()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue