use std::cmp::min; use aoc_2023::*; use itertools::iproduct; type Output1 = i32; type Output2 = Output1; struct Pattern { map: Vec<Vec<char>>, } impl From<&[String]> for Pattern { fn from(value: &[String]) -> Self { Self { map: value.iter().map(|l| l.chars().collect_vec()).collect_vec(), } } } impl Pattern { fn check_vertically(&self, allowed_error: usize) -> Option<i32> { (0..self.map[0].len() - 1) .find(|&x| { let d = min(x + 1, self.map[0].len() - x - 1); iproduct!(0..self.map.len(), (0..d)) .filter(|&(y, dx)| self.map[y][x - dx] != self.map[y][x + dx + 1]) .count() == allowed_error }) .map(|x| (x + 1) as i32) } fn check_horizontally(&self, allowed_error: usize) -> Option<i32> { (0..self.map.len() - 1) .find(|&y| { let d = min(y + 1, self.map.len() - y - 1); iproduct!(0..self.map[y].len(), (0..d)) .filter(|&(x, dy)| self.map[y - dy][x] != self.map[y + dy + 1][x]) .count() == allowed_error }) .map(|y| 100 * (y + 1) as i32) } fn score(&self, allowed_error: usize) -> i32 { if let Some(s) = self.check_vertically(allowed_error) { s } else if let Some(s) = self.check_horizontally(allowed_error) { s } else { unreachable!() } } } struct Day13 { patterns: Vec<Pattern>, } impl Solution<Output1, Output2> for Day13 { fn new<P: AsRef<Path>>(pathname: P) -> Self { let lines: Vec<String> = file_to_lines(pathname); let patterns = lines .split(|l| l.is_empty()) .map(|m| m.into()) .collect_vec(); Self { patterns } } fn part_1(&mut self) -> Output1 { self.patterns.iter().map(|p| p.score(0)).sum() } fn part_2(&mut self) -> Output2 { self.patterns.iter().map(|p| p.score(1)).sum() } } fn main() -> Result<()> { // Day13::run("sample") Day13::main() } test_sample!(day_13, Day13, 405, 400);