4 Commits

Author SHA1 Message Date
b5866b2d8a Modify points in-place
Thanks to skius from libera##rust for the idea
2021-12-13 22:55:14 +01:00
554683bc64 Actually apply nom 2021-12-13 22:20:02 +01:00
1031a8cdbb Avoid allocating the final buffer twice 2021-12-13 21:40:00 +01:00
5c764f6627 Don't use silly hashsets 2021-12-13 21:34:48 +01:00
2 changed files with 52 additions and 92 deletions

View File

@@ -7,7 +7,7 @@ use criterion::criterion_main;
use criterion::BenchmarkId; use criterion::BenchmarkId;
use criterion::Criterion; use criterion::Criterion;
const DAYS_IMPLEMENTED: usize = 12; const DAYS_IMPLEMENTED: usize = 13;
fn read_input(day: usize) -> Vec<u8> { fn read_input(day: usize) -> Vec<u8> {
let input_path = format!("inputs/{:02}.txt", day); let input_path = format!("inputs/{:02}.txt", day);

View File

@@ -1,148 +1,108 @@
use std::collections::HashSet;
use std::io::Read; use std::io::Read;
use std::str::FromStr;
use itertools::Itertools;
use nom::branch::alt; use nom::branch::alt;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::error::Error; use nom::combinator::map;
use nom::sequence::tuple; use nom::multi::many0;
use nom::sequence::preceded;
use nom::sequence::separated_pair;
use nom::sequence::terminated;
use nom::Finish; use nom::Finish;
use nom::IResult; use nom::IResult;
use crate::common::LineIter;
use crate::common::LineParser;
type Coords = (u16, u16); type Coords = (u16, u16);
#[derive(Copy, Clone)]
enum Fold { enum Fold {
X(u16), X(u16),
Y(u16), Y(u16),
} }
impl FromStr for Fold { fn parse_input(input: &[u8]) -> IResult<&[u8], (Vec<Coords>, Vec<Fold>)> {
type Err = Error<String>; use nom::character::complete::char;
fn from_str(s: &str) -> Result<Self, Self::Err> { let parse_coordinates = many0(terminated(parse_coordinate, char('\n')));
let (_, fold) = parse_fold(s) let parse_folds = many0(terminated(parse_fold, char('\n')));
.finish()
.map_err(|Error { input, code }| Error {
input: input.to_string(),
code,
})?;
Ok(fold) separated_pair(parse_coordinates, char('\n'), parse_folds)(input)
}
} }
fn parse_coordinates(input: &str) -> IResult<&str, Coords> { fn parse_coordinate(input: &[u8]) -> IResult<&[u8], Coords> {
use nom::character::complete::char; use nom::character::complete::char;
use nom::character::complete::u16; use nom::character::complete::u16;
let (input, (x, _, y)) = tuple((u16, char(','), u16))(input)?; separated_pair(u16, char(','), u16)(input)
Ok((input, (x, y)))
} }
fn parse_fold(input: &str) -> IResult<&str, Fold> { fn parse_fold(input: &[u8]) -> IResult<&[u8], Fold> {
use nom::character::complete::char;
use nom::character::complete::u16; use nom::character::complete::u16;
let (input, (_, axis, _, coord)) = tuple(( preceded(
tag("fold along "), tag("fold along "),
alt((char('x'), char('y'))), alt((
char('='), preceded(tag("x="), map(u16, Fold::X)),
u16, preceded(tag("y="), map(u16, Fold::Y)),
))(input)?; )),
)(input)
let fold = match axis {
'x' => Fold::X(coord),
'y' => Fold::Y(coord),
_ => unreachable!("Should be filtered by nom"),
};
Ok((input, fold))
} }
fn read_dots(reader: &mut LineIter<'_>) -> HashSet<Coords> { fn read_input(input: &mut dyn Read) -> (Vec<Coords>, Vec<Fold>) {
let mut dots = HashSet::new(); let mut input_buffer = Vec::new();
input.read_to_end(&mut input_buffer).unwrap();
while let Some(line) = reader.next() { parse_input(&input_buffer).finish().unwrap().1
if line.is_empty() {
break;
}
let (_, coords) = parse_coordinates(line).unwrap();
dots.insert(coords);
}
dots
} }
fn apply_fold(dots: &mut HashSet<Coords>, fold: Fold, to_fold: &mut Vec<Coords>) { fn apply_fold(dots: &mut Vec<Coords>, fold: Fold) {
match fold { match fold {
Fold::X(coord) => dots.retain(|&(x, y)| { Fold::X(coord) => dots.iter_mut().for_each(|(x, _)| {
if x < coord { if *x >= coord {
true *x = 2 * coord - *x;
} else {
to_fold.push((2 * coord - x, y));
false
} }
}), }),
Fold::Y(coord) => dots.retain(|&(x, y)| { Fold::Y(coord) => dots.iter_mut().for_each(|(_, y)| {
if y < coord { if *y >= coord {
true *y = 2 * coord - *y;
} else {
to_fold.push((x, 2 * coord - y));
false
} }
}), }),
} }
dots.extend(to_fold.drain(..));
} }
fn print_dots(dots: &HashSet<Coords>) -> String { fn print_dots(dots: &[Coords]) -> String {
let (x, y) = dots let (width, height) = dots.iter().fold((0, 0), |(xc, yc), &(xn, yn)| {
.iter() (xc.max(xn as usize + 1), yc.max(yn as usize + 1))
.fold((0, 0), |(xc, yc), &(xn, yn)| (xc.max(xn), yc.max(yn))); });
let mut buffer = String::with_capacity((x as usize + 1) * y as usize); let mut buffer = vec![b' '; (width + 1) * height - 1];
for y in 0..=y { for &(x, y) in dots {
for x in 0..=x { buffer[x as usize + (width + 1) * y as usize] = b'#';
if dots.contains(&(x, y)) {
buffer.push('#');
} else {
buffer.push(' ');
}
}
buffer.push('\n');
} }
buffer.pop(); for line in buffer.chunks_exact_mut(width + 1) {
line[width] = b'\n';
}
buffer String::from_utf8(buffer).unwrap()
} }
pub fn part1(input: &mut dyn Read) -> String { pub fn part1(input: &mut dyn Read) -> String {
let mut reader = LineIter::new(input); let (mut dots, folds) = read_input(input);
let mut dots = read_dots(&mut reader); apply_fold(&mut dots, folds[0]);
let fold = reader.next().unwrap().parse().unwrap(); dots.sort_unstable();
apply_fold(&mut dots, fold, &mut Vec::new());
dots.len().to_string() dots.into_iter().unique().count().to_string()
} }
pub fn part2(input: &mut dyn Read) -> String { pub fn part2(input: &mut dyn Read) -> String {
let mut reader = LineIter::new(input); let (mut dots, folds) = read_input(input);
let mut dots = read_dots(&mut reader); folds
let mut to_fold = Vec::new(); .into_iter()
.for_each(|fold| apply_fold(&mut dots, fold));
LineParser::from(reader).for_each(|fold| apply_fold(&mut dots, fold, &mut to_fold));
print_dots(&dots) print_dots(&dots)
} }