//! Day 13 use grid::Grid; use itertools::Itertools; pub const DAY: u8 = 13; pub const TITLE: &str = "Point of Incidence"; #[derive(Debug, Clone, Copy)] enum Reflection { Horizontal(usize), Vertical(usize), } impl Reflection { fn n(self) -> usize { match self { Reflection::Horizontal(n) => n * 100, Reflection::Vertical(n) => n, } } } fn parse(input: Vec) -> Vec> { input .split(|l| l.is_empty()) .map(|gl| { let mut grid = Grid::new(gl.len(), gl[0].len()); for (y, line) in gl.iter().enumerate() { for (x, c) in line.chars().enumerate() { if c == '#' { grid[(y, x)] = true; } } } grid }) .collect() } fn find_reflection(grid: &Grid, smudge: bool) -> Option { let cols = grid .iter_cols() .map(|col| { col.fold(0_u64, |mut acc, x| { if *x { acc |= 1; } acc <<= 1; acc }) }) .collect_vec(); if let Some(x) = find_ref(&cols, smudge) { return Some(Reflection::Vertical(x)); } let rows = grid .iter_rows() .map(|col| { col.fold(0_u64, |mut acc, x| { if *x { acc |= 1; } acc <<= 1; acc }) }) .collect_vec(); if let Some(y) = find_ref(&rows, smudge) { return Some(Reflection::Horizontal(y)); } None } fn find_ref(v: &[u64], smudge: bool) -> Option { if smudge { find_ref_smudge(v) } else { find_ref_normal(v) } } fn find_ref_normal(v: &[u64]) -> Option { let mut last = 0; for (i, n) in v.iter().copied().enumerate() { if n == last { // Could be mirror position if v[..i - 1] .iter() .rev() .zip(v[i + 1..].iter()) .all(|(a, b)| a == b) { return Some(i); } } last = n; } None } fn find_ref_smudge(v: &[u64]) -> Option { let mut last = 0; for (i, n) in v.iter().copied().enumerate() { if i == 0 { } else if n == last { // Could be mirror position with 1 smudge let mut got_smudge = false; let mut ok = true; for (a, b) in v[..i - 1].iter().rev().zip(v[i + 1..].iter()) { if !got_smudge && smudge_eq(*a, *b) { got_smudge = true; } else if a != b { ok = false; break; } } if ok && got_smudge { return Some(i); } } else if smudge_eq(n, last) { // Could be mirror position if v[..i - 1] .iter() .rev() .zip(v[i + 1..].iter()) .all(|(a, b)| a == b) { return Some(i); } } last = n; } None } fn smudge_eq(a: u64, b: u64) -> bool { let d = a ^ b; d > 0 && (d & (d - 1)) == 0 } pub fn part1(input: Vec) -> String { let grids = parse(input); grids .iter() .map(|g| find_reflection(g, false).unwrap().n()) .sum::() .to_string() } pub fn part2(input: Vec) -> String { let grids = parse(input); grids .iter() .map(|g| find_reflection(g, true).unwrap().n()) .sum::() .to_string() } #[cfg(test)] mod tests { use super::*; #[test] fn t_smudge_eq() { assert!(smudge_eq(1, 0)); assert!(smudge_eq(0b10001100, 0b10000100)); assert!(!smudge_eq(0b10001100, 0b10001100)); } #[test] fn t_example1() { assert_eq!(part1(crate::read_example(DAY, 1)), "405"); } #[test] fn t_part1() { assert_eq!(part1(crate::read_input(DAY)), "35210"); } #[test] fn t_example2() { assert_eq!(part2(crate::read_example(DAY, 2)), "400"); } #[test] fn t_part2() { assert_eq!(part2(crate::read_input(DAY)), "31974"); } }