use std::collections::HashSet; use std::str::FromStr; use crate::Day; pub struct Day13 { points: HashSet<(i32, i32)>, folds: Vec, } #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Debug)] enum Fold { X(i32), Y(i32), } impl FromStr for Fold { type Err = (); fn from_str(s: &str) -> Result { let r = s.rsplit(' ').next().ok_or(())?; let z = r.split('=').collect::>(); match z[0] { "x" => Ok(Fold::X(z[1].parse().map_err(|_| ())?)), "y" => Ok(Fold::Y(z[1].parse().map_err(|_| ())?)), _ => Err(()), } } } impl Day for Day13 { fn init(content: String) -> anyhow::Result { let l = content.lines(); let points = l.clone().take_while(|&x| !x.is_empty()).map(|st| st.split(',') .map(str::parse).flatten().collect::>()).map(|v| (v[0], v[1])).collect::>(); Ok(Self { points, folds: l.filter(|st| st.starts_with("fold along")).map(str::parse).flatten().collect::>(), }) } fn part1(&self) -> anyhow::Result { let fold = self.folds[0]; let map = self.points.clone(); Ok(format!("{}", flip(map, &fold).len())) } fn part2(&self) -> anyhow::Result { let r = self.folds.iter().fold(self.points.clone(), flip); let max_x = r.iter().max_by(|&(xm, _), &(xn, _)| xm.cmp(xn)).ok_or_else(|| anyhow::Error::msg("X is empty!"))?; let max_y = r.iter().max_by(|&(_, xm), &(_, xn)| xm.cmp(xn)).ok_or_else(|| anyhow::Error::msg("Y is empty!"))?; let mut z = String::new(); for y in 0..=max_y.1 { for x in 0..=max_x.0 { if r.contains(&(x, y)) { z += "█"; } else { z += " "; } } z += "\n"; } Ok(format!("\n{}", z)) } } fn flip(set: HashSet<(i32, i32)>, f: &Fold) -> HashSet<(i32, i32)> { set.iter().map(|&e| flip_one(e, *f)).collect() } fn flip_one((x, y): (i32, i32), f: Fold) -> (i32, i32) { match f { Fold::X(pos) => { let new_x = if x > pos { pos - x + pos } else { x }; (new_x, y) } Fold::Y(pos) => { let new_y = if y > pos { pos - y + pos } else { y }; (x, new_y) } } } #[cfg(test)] mod tests { use std::collections::HashSet; use crate::day13::{Day13, flip, flip_one, Fold}; use crate::day::Day; const INPUT: &str = r"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"; #[test] fn flip_test() { assert_eq!(flip_one((0, 0), Fold::X(3)), (0, 0)); assert_eq!(flip_one((3, 0), Fold::X(3)), (3, 0)); assert_eq!(flip_one((6, 0), Fold::X(3)), (0, 0)); } #[test] fn flip_set_test() { let set = HashSet::from([(0, 3), (3, 2), (6, 3)]); let r = flip(set, &Fold::X(3)); assert!(r.contains(&(0, 3))); assert!(r.contains(&(3, 2))); assert!(!r.contains(&(6, 3))); assert_eq!(r.len(), 2); } #[test] fn part1_test() -> anyhow::Result<()> { let d = Day13::init(INPUT.to_string())?; assert_eq!("17", d.part1()?); Ok(()) } #[test] fn part1_real() -> anyhow::Result<()> { let d = Day13::init(crate::load_input("13")?)?; assert_eq!("781", d.part1()?); Ok(()) } }