use nom::{ branch::alt, bytes::complete::tag, character::complete::{char as nom_char, line_ending, u32 as nom_u32}, combinator::map, multi::{many0, separated_list1}, sequence::tuple, IResult, }; use std::{collections::BTreeSet, fmt, fs}; fn main() -> Result<(), Box> { let input = fs::read_to_string("inputs/day_13.txt")?; let mut page = parse_page(&input).unwrap().1; page.do_next_fold(); dbg!(page.count_points()); while page.do_next_fold() {} println!("{}", page); Ok(()) } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] struct Point { x: u32, y: u32, } #[derive(Debug)] enum Fold { X(u32), Y(u32), } #[derive(Debug)] struct Page { points: BTreeSet, folds: Vec, } impl Page { fn do_next_fold(&mut self) -> bool { let fold = self.folds.pop(); match fold { Some(Fold::X(x)) => { self.points = std::mem::take(&mut self.points) .into_iter() .filter(|point| point.x != x) .map(|point| { if point.x > x { Point { x: x - (point.x - x), y: point.y, } } else { point } }) .collect(); true } Some(Fold::Y(y)) => { self.points = std::mem::take(&mut self.points) .into_iter() .filter(|point| point.y != y) .map(|point| { if point.y > y { Point { x: point.x, y: y - (point.y - y), } } else { point } }) .collect(); true } None => false, } } fn count_points(&self) -> usize { self.points.len() } } impl fmt::Display for Page { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { let width = self.points.iter().map(|p| p.x).max().unwrap_or(0); let height = self.points.iter().map(|p| p.y).max().unwrap_or(0); for y in 0..=height { for x in 0..=width { let p = Point { x, y }; if self.points.contains(&p) { write!(f, "#")?; } else { write!(f, ".")?; } } writeln!(f)?; } Ok(()) } } fn parse_page(input: &str) -> IResult<&str, Page> { let (input, points) = separated_list1(line_ending, parse_point)(input)?; let (input, _) = many0(line_ending)(input)?; let (input, mut folds) = separated_list1(line_ending, parse_fold)(input)?; folds.reverse(); Ok(( input, Page { points: points.into_iter().collect(), folds, }, )) } fn parse_fold(input: &str) -> IResult<&str, Fold> { alt(( map(tuple((tag("fold along x="), nom_u32)), |(_, val)| { Fold::X(val) }), map(tuple((tag("fold along y="), nom_u32)), |(_, val)| { Fold::Y(val) }), ))(input) } fn parse_point(input: &str) -> IResult<&str, Point> { map(tuple((nom_u32, nom_char(','), nom_u32)), |(x, _, y)| { Point { x, y } })(input) }