use advent_of_code::include_aoc; use advent_of_code::{ strings::{convert_to_array, line_to_char}, Kartesian, KartesianDirection, MaximumFromMap, Table, }; include_aoc!(2024, 04); #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] struct TextPos { start: Kartesian, end: Kartesian, } impl TextPos { fn center(self) -> Kartesian { (self.start + self.end) / 2 } } fn get_x(list: Vec) -> usize { let mut found = Vec::<(TextPos, TextPos)>::new(); let (mut higher, mut lower); for outer in list.clone() { for inner in list.clone() { if outer == inner { continue; } higher = outer.max(inner); lower = outer.min(inner); if lower.center() == higher.center() { let diff = lower.start.diff(higher.start).0; if diff.x == 0 && diff.y == 2 || diff.y == 0 && diff.x == 2 { let tuple = (lower, higher); if !found.contains(&tuple) { found.push((lower, higher)); } } } } } found.len() } fn follow_text(data: &Table, start_point: Kartesian, direction: KartesianDirection, current_pos: Kartesian, chars: Vec, list: &mut Vec) { let mut iter = chars.iter().cloned(); match iter.next() { None => list.push(TextPos { start: start_point, end: current_pos, }), Some(c) => match current_pos.move_dir_max(direction.vector_abs(), direction, Kartesian::maximum(data)) { None => return, Some(new) => { if data[new.x][new.y] != c { return; } follow_text(data, start_point, direction, new, iter.collect(), list) }, }, } } fn find_text(data: Table, text: &str) -> Vec { let length = data[0].len(); let height = data.len(); let mut iter = text.chars(); let first = iter.next().unwrap(); let chars: Vec = iter.collect(); let mut coords = Vec::new(); let mut start_point; for row in 0..height { for column in 0..length { start_point = Kartesian::new(row, column); if data[start_point.x][start_point.y] != first { continue; } for dir in KartesianDirection::iter(false, true, true) { follow_text(&data, start_point, dir, start_point, chars.clone(), &mut coords); } } } coords } fn main() { let mut coords; let table = convert_to_array::<_, _, '\n'>(DATA, line_to_char); coords = find_text(table.clone(), "XMAS"); println!("There are {} XMAS in text", coords.len()); coords = find_text(table.clone(), "MAS"); let amount_cross = get_x(coords.clone()); println!("There are {} MAS in text, are {} crossed", coords.len(), amount_cross); } #[cfg(test)] mod test { use super::*; #[test] fn test_crossing() { let cases = [ ( TextPos { start: Kartesian::new(0, 1), end: Kartesian::new(2, 1), length: 3, }, TextPos { start: Kartesian::new(1, 0), end: Kartesian::new(1, 2), length: 3, }, true, ), ]; for case in cases { assert_eq!(case.0.crossing(&&case.1), case.2) } } }