Implement day 5

Nom is really nice and fast, why did I write parsers manually before.
This commit is contained in:
2021-12-05 11:31:14 +01:00
parent 5e52da6e6b
commit 1433b0cdbe
6 changed files with 619 additions and 4 deletions

View File

@@ -60,3 +60,12 @@ impl<'a, I: FromStr> Iterator for LineParser<'a, I> {
self.iter.next()?.parse().ok()
}
}
/// Return two arguments in their natural PartialOrd order
pub fn ordered<O: PartialOrd>(a: O, b: O) -> (O, O) {
if a < b {
(a, b)
} else {
(b, a)
}
}

View File

@@ -1,9 +1,99 @@
use std::collections::HashMap;
use std::io::Read;
use std::iter::repeat;
pub fn part1(_input: &mut dyn Read) -> String {
todo!()
use nom::bytes::complete::tag;
use nom::sequence::tuple;
use nom::Finish;
use nom::IResult;
use crate::common::ordered;
use crate::common::LineIter;
type Coord = (u16, u16);
fn coordinates(input: &str) -> IResult<&str, Coord> {
use nom::character::complete;
let (input, (x, _, y)) = tuple((complete::u16, complete::char(','), complete::u16))(input)?;
Ok((input, (x, y)))
}
pub fn part2(_input: &mut dyn Read) -> String {
todo!()
fn line_definition(input: &str) -> IResult<&str, (Coord, Coord)> {
let (input, (begin, _, end)) = tuple((coordinates, tag(" -> "), coordinates))(input)?;
// Sorting the coordinates saves trouble later
Ok((input, ordered(begin, end)))
}
fn stripe(
map: &mut HashMap<Coord, u16>,
xs: impl Iterator<Item = u16>,
ys: impl Iterator<Item = u16>,
) {
for (x, y) in xs.zip(ys) {
*map.entry((x, y)).or_default() += 1;
}
}
fn part_common(input: &mut dyn Read, diagonals: bool) -> String {
let mut reader = LineIter::new(input);
let mut map = HashMap::new();
while let Some(line) = reader.next() {
let (begin, end) = line_definition(line).finish().unwrap().1;
if begin.0 == end.0 {
let y_range = begin.1..=end.1;
stripe(&mut map, repeat(begin.0), y_range);
} else if begin.1 == end.1 {
let x_range = begin.0..=end.0;
stripe(&mut map, x_range, repeat(begin.1));
} else if diagonals {
let x_range = begin.0..=end.0;
let y_range = (begin.1.min(end.1))..=(begin.1.max(end.1));
if begin.1 > end.1 {
// For a downward slope we need to reverse Y
stripe(&mut map, x_range, y_range.rev());
} else {
stripe(&mut map, x_range, y_range);
}
}
}
map.values().filter(|&&v| v > 1).count().to_string()
}
pub fn part1(input: &mut dyn Read) -> String {
part_common(input, false)
}
pub fn part2(input: &mut dyn Read) -> String {
part_common(input, true)
}
#[cfg(test)]
mod tests {
use crate::test_implementation;
use super::*;
const SAMPLE: &[u8] = include_bytes!("samples/05.txt");
#[test]
fn test_parser() {
assert_eq!(line_definition("6,4 -> 2,0"), Ok(("", ((2, 0), (6, 4)))));
}
#[test]
fn sample_part1() {
test_implementation(part1, SAMPLE, 5)
}
#[test]
fn sample_part2() {
test_implementation(part2, SAMPLE, 12)
}
}

10
2021/src/samples/05.txt Normal file
View File

@@ -0,0 +1,10 @@
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