pub mod direction { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Direction { Up, Right, Down, Left, } use Direction::*; impl Direction { pub fn iter_all() -> impl Iterator { [Up, Right, Down, Left].into_iter() } pub fn to_offset(&self) -> (isize, isize) { match self { Up => (-1, 0), Right => (0, 1), Down => (1, 0), Left => (0, -1), } } pub fn rotate(&self) -> Self { match self { Up => Right, Right => Down, Down => Left, Left => Up, } } pub fn around(&self) -> impl Iterator + use<'_> { Self::iter_all().filter(|&x| x != self.opposite()) } pub fn opposite(&self) -> Self { match self { Up => Down, Right => Left, Down => Up, Left => Right, } } } } pub mod matrix { pub type Pos = (usize, usize); pub type PosDelta = (isize, isize); #[derive(Clone)] pub struct Matrix { dots: Vec>, pub limit: Pos, } impl Matrix { pub fn new(text: &str, parse: F) -> Self where F: Fn(char) -> T, { let dots: Vec> = text .lines() .map(|row| row.chars().map(&parse).collect()) .collect(); let limit = (dots.len(), dots[0].len()); Self { dots, limit } } pub fn get(&self, (x, y): Pos) -> &T { &self.dots[x][y] } pub fn set(&mut self, (x, y): Pos, dot: T) { self.dots[x][y] = dot; } pub fn in_bounds(&self, (x, y): PosDelta) -> Option { if x >= 0 && y >= 0 && x < self.limit.0 as isize && y < self.limit.1 as isize { Some((x as usize, y as usize)) } else { None } } // todo use in_bounds pub fn pos_move(&self, (x, y): Pos, (dx, dy): PosDelta) -> Option { let x2 = x as isize + dx; if x2 < 0 || x2 >= self.limit.0 as isize { return None; } let y2 = y as isize + dy; if y2 < 0 || y2 >= self.limit.1 as isize { return None; } Some((x2 as usize, y2 as usize)) } } use std::fmt; impl fmt::Display for Matrix where T: fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for row in &self.dots { writeln!( f, "{}", row.iter().map(|c| c.to_string()).collect::() )?; } fmt::Result::Ok(()) } } } pub fn read_input(day: &str) -> String { let input_file = format!("inputs/{day}"); std::fs::read_to_string(input_file).unwrap() } pub fn run_day(day: &str, p1: S1, p2: S2) -> (String, String) where S1: FnOnce(&str) -> String, S2: FnOnce(&str) -> String, { let input = read_input(day); let p1r = p1(&input); let p2r = p2(&input); println!("==== DAY {day}"); println!("Result (P1): {}", p1r); println!("Result (P2): {}", p2r); (p1r, p2r) }