use std::{ fmt::{Debug, Display, Formatter, Write}, io::{BufRead, Cursor}, iter::repeat_n, mem::swap, ops::{Add, AddAssign, Sub}, str::FromStr, }; /// NW, N, NE, W, E, SW, S, SE pub const ADJACENT_OFFSETS: [&(i64, i64); 8] = [ &(-1, -1), &(0, -1), &(1, -1), &(-1, 0), &(1, 0), &(-1, 1), &(0, 1), &(1, 1), ]; /// NESW pub const CARDINAL_OFFSETS: [&(i64, i64); 4] = [&(0, -1), &(-1, 0), &(1, 0), &(0, 1)]; #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct Coord2d { pub x: i64, pub y: i64, } impl Debug for Coord2d { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!("({}, {})", self.x, self.y)) } } pub trait AsCoord2d { fn to_coord(self) -> Coord2d; fn x(&self) -> i64; fn y(&self) -> i64; } impl Sub for &Coord2d { type Output = Coord2d; fn sub(self, rhs: T) -> Self::Output { Coord2d { x: self.x() - rhs.x(), y: self.y() - rhs.y(), } } } impl Add for &Coord2d where T: Copy, { type Output = Coord2d; fn add(self, rhs: T) -> Self::Output { Coord2d { x: self.x() + rhs.x(), y: self.y() + rhs.y(), } } } impl Add<&T> for Coord2d { type Output = Coord2d; fn add(self, rhs: &T) -> Self::Output { Coord2d { x: self.x() + rhs.x(), y: self.y() + rhs.y(), } } } impl AsCoord2d for Coord2d { fn to_coord(self) -> Coord2d { self } fn x(&self) -> i64 { self.x } fn y(&self) -> i64 { self.y } } impl AsCoord2d for &Coord2d { fn to_coord(self) -> Coord2d { self.to_owned() } fn x(&self) -> i64 { self.x } fn y(&self) -> i64 { self.y } } impl AsCoord2d for (T, T) where T: Copy + TryInto, >::Error: Debug, { fn to_coord(self) -> Coord2d { Coord2d { x: self.0.try_into().unwrap(), y: self.1.try_into().unwrap(), } } fn x(&self) -> i64 { self.0.try_into().unwrap() } fn y(&self) -> i64 { self.1.try_into().unwrap() } } impl AsCoord2d for &(T, T) where T: Copy + TryInto, >::Error: Debug, { fn to_coord(self) -> Coord2d { Coord2d { x: self.0.try_into().unwrap(), y: self.1.try_into().unwrap(), } } fn x(&self) -> i64 { self.0.try_into().unwrap() } fn y(&self) -> i64 { self.1.try_into().unwrap() } } impl From for (i64, i64) { fn from(value: Coord2d) -> Self { (value.x, value.y) } } #[derive(Debug)] pub struct GridRowIter<'a, T> { iter: std::slice::Iter<'a, T>, } impl<'a, T: Clone + Eq + PartialEq + Debug> GridRowIter<'a, T> { fn new(grid: &'a Grid, y: i64) -> Self { let iter = grid.data[y as usize * grid.width()..(y as usize + 1) * grid.width()].iter(); Self { iter } } } impl<'a, T> Iterator for GridRowIter<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { self.iter.next() } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } #[derive(Debug)] pub struct CoordIter<'a, T> { pos: usize, grid: &'a Grid, } impl<'a, T: Clone + Eq + PartialEq + Debug> Iterator for CoordIter<'a, T> { type Item = Coord2d; fn next(&mut self) -> Option { if self.pos < self.grid.data.len() { self.pos += 1; self.grid.coord(self.pos as i64 - 1) } else { None } } } #[derive(Debug)] pub struct ItemIter<'a, T> { pos: usize, grid: &'a Grid, } #[derive(Debug, PartialEq, Eq)] pub struct Item<'a, T> { pub pos: Coord2d, pub value: &'a T, } impl<'a, T: Clone + Eq + PartialEq + Debug> Iterator for ItemIter<'a, T> { type Item = Item<'a, T>; fn next(&mut self) -> Option { if self.pos < self.grid.data.len() { self.pos += 1; Some(Item { pos: self.grid.coord(self.pos as i64 - 1).unwrap(), value: &self.grid.data[self.pos - 1], }) } else { None } } } #[derive(Debug)] pub struct GridColIter<'a, T> { grid: &'a Grid, stride: usize, cur: usize, } impl<'a, T: Clone + Eq + PartialEq + Debug> GridColIter<'a, T> { fn new(grid: &'a Grid, x: i64) -> Self { Self { grid, stride: grid.width(), cur: x as usize, } } } impl<'a, T: Clone + Eq + PartialEq + Debug> Iterator for GridColIter<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { let cur = self.cur; self.cur += self.stride; if cur < self.grid.data.len() { Some(&self.grid.data[cur]) } else { None } } fn size_hint(&self) -> (usize, Option) { (self.grid.height() - 1, Some(self.grid.height() - 1)) } } #[derive(Debug)] pub struct OffsetsIter<'a, T> { grid: &'a Grid, origin: Coord2d, cur: usize, offsets: &'a [&'a (i64, i64)], } impl<'a, T: Clone + Eq + PartialEq + Debug> Iterator for OffsetsIter<'a, T> { type Item = Item<'a, T>; fn next(&mut self) -> Option { while self.cur < self.offsets.len() { let pos = self.origin + self.offsets[self.cur]; self.cur += 1; if let Some(value) = self.grid.get(&pos) { return Some(Item { pos, value }); } } None } fn size_hint(&self) -> (usize, Option) { (0, Some(self.offsets.len() - self.cur)) } } #[derive(Clone, Eq, PartialEq, Debug)] pub struct Grid { pub data: Vec, width: i64, } impl Grid { /// Returns a new [Grid] with the same shape (width x height) as `self`, filled with `fill` pub fn same_shape(&self, fill: NT) -> Grid { Grid::with_shape(self.width(), self.height(), fill) } /// Returns a new [Grid] with the given shape (width x height), filled with `fill` pub fn with_shape(width: usize, height: usize, fill: T) -> Self { Self { data: Vec::from_iter(repeat_n(fill, width * height)), width: width as i64, } } pub fn width(&self) -> usize { self.width as usize } pub fn height(&self) -> usize { self.data.len() / self.width() } pub fn pos(&self, c: &C) -> i64 { c.y() * self.width + c.x() } pub fn coord(&self, pos: i64) -> Option { if pos < 0 || pos >= self.data.len() as i64 { None } else { Some(Coord2d { x: pos % self.width, y: pos / self.width, }) } } pub fn coord_iter<'a>(&'a self) -> CoordIter<'a, T> { CoordIter { pos: 0, grid: self } } pub fn item_iter<'a>(&'a self) -> ItemIter<'a, T> { ItemIter { pos: 0, grid: self } } pub fn is_valid(&self, c: &C) -> bool { if c.x() < 0 || c.x() >= self.width { return false; } if c.y() < 0 || c.y() as usize >= self.height() { return false; } true } fn valid_pos(&self, c: &C) -> Option { if c.x() < 0 || c.x() >= self.width { return None; } if c.y() < 0 || c.y() as usize >= self.height() { return None; } let pos = self.pos(c); if pos < 0 || pos as usize >= self.data.len() { return None; } self.pos(c).try_into().ok() } pub fn get(&self, c: &C) -> Option<&T> { match self.valid_pos(c) { Some(pos) => Some(&self.data[pos]), None => None, } } pub fn get_mut(&mut self, c: &C) -> Option<&mut T> { match self.valid_pos(c) { Some(pos) => Some(self.data.get_mut(pos).unwrap()), None => None, } } pub fn set(&mut self, c: &C, mut val: T) -> Option { match self.valid_pos(c) { Some(pos) => { swap(&mut self.data[pos], &mut val); Some(val) } None => None, } } pub fn increment<'a, A, C: AsCoord2d>(&'a mut self, c: &C, i: A) -> Option<&'a T> where T: AddAssign, { match self.valid_pos(c) { Some(pos) => { self.data[pos] += i; Some(&self.data[pos]) } None => None, } } pub fn row(&self, y: i64) -> Option<&[T]> { if y < self.height() as i64 && y >= 0 { Some(&self.data[self.pos(&(0, y)) as usize..self.pos(&(self.width, y)) as usize]) } else { None } } pub fn row_iter<'a>(&'a self, y: i64) -> Option> { if (y as usize) < self.height() { Some(GridRowIter::new(self, y)) } else { None } } pub fn col(&self, x: i64) -> Option> { self.col_iter(x).map(|iter| iter.collect()) } pub fn col_iter<'a>(&'a self, x: i64) -> Option> { if (x as usize) < self.width() { Some(GridColIter::new(self, x)) } else { None } } pub fn find(&self, needle: &T) -> Option { self.coord( self.data .iter() .enumerate() .find_map(|(pos, val)| { if val == needle { Some(pos as i64) } else { None } }) .unwrap_or(-1), ) } /// Get all of the coordinates having value `needle` pub fn find_all<'a>(&'a self, needle: &'a T) -> impl DoubleEndedIterator + 'a { self.data .iter() .enumerate() .filter_map(|(pos, val)| (*val == *needle).then_some(self.coord(pos as i64).unwrap())) } pub fn count(&self, haystack: &T) -> usize { self.data.iter().filter(|item| *item == haystack).count() } pub fn forward_slice(&self, start: &C, len: i64) -> Option<&[T]> { let pos = ( self.valid_pos(start), self.valid_pos(&(start.x() + len - 1, start.y())), ); match pos { (Some(pos1), Some(pos2)) => Some(&self.data[pos1..pos2 + 1]), _ => None, } } pub fn swap(&mut self, a: A, b: B) { if let (Some(a), Some(b)) = (self.valid_pos(&a), self.valid_pos(&b)) { self.data.swap(a, b) } } /// Return the count of neighbours (8 directions) matching predicate p pub fn adjacent_count(&self, c: &C, mut p: P) -> usize where P: FnMut(&T) -> bool, { self.adjacent_iter(c).filter(|i| p(i.value)).count() } /// Return the count of cardinal neighbours (4 directions) matching predicate p pub fn cardinal_count(&self, c: &C, mut p: P) -> usize where P: FnMut(&T) -> bool, { self.cardinal_iter(c).filter(|i| p(i.value)).count() } /// Return an iterator over the 8 neighbours of c. The iterator skips neighbouring positions outside of the grid. pub fn adjacent_iter<'a, C: AsCoord2d + Copy>(&'a self, c: &'a C) -> OffsetsIter<'a, T> { OffsetsIter { grid: self, origin: c.to_coord(), cur: 0, offsets: &ADJACENT_OFFSETS, } } /// Return an iterator over the 4 cardinal neighbours of c. The iterator skips neighbouring positions outside of the grid. pub fn cardinal_iter<'a, C: AsCoord2d + Copy>(&'a self, c: &'a C) -> OffsetsIter<'a, T> { OffsetsIter { grid: self, origin: c.to_coord(), cur: 0, offsets: &CARDINAL_OFFSETS, } } // fn window_compare_impl(&self, needle: &[T]) -> Vec<(i64, i64)> { // if (self.width as usize) < needle.len() { // return Vec::new(); // } // let mut res = Vec::new(); // for y in 0..self.height() as i64 { // let mut windows_tmp = self.row(y).unwrap().windows(needle.len()); // let windows = if REV { // windows_tmp.rev() // } else { // windows_tmp // }; // res.extend( // windows // .enumerate() // .filter_map(|(x, w)| if w == needle { Some((x as i64, y)) } else { None }), // ); // } // res // } } impl From for Grid { fn from(input: T) -> Grid { let mut data = Vec::new(); let mut width = 0; for line in input.split(b'\n').map(|i| i.unwrap()) { if width == 0 { width = line.len() as i64 } else if line.len() as i64 != width { panic!("Grids must have fixed length rows") } data.extend_from_slice(&line); } Grid { data, width } } } // Should be Grid? impl FromStr for Grid { type Err = Box; fn from_str(s: &str) -> Result { Ok(Cursor::new(s).into()) } } // impl> Display for Grid { // fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { // for y in 0..self.height() { // for x in 0..self.width() { // f.write_fmt(format_args!("{}",self.get(x as i64, y as i64).unwrap() as char))?; // } // f.write_char('\n')?; // } // f.write_char('\n') // } // } impl Display for Grid { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { for y in 0..self.height() { for x in 0..self.width() { f.write_fmt(format_args!( "{}", *self.get(&(x as i64, y as i64)).unwrap() as char ))?; } f.write_char('\n')?; } f.write_char('\n') } } impl Display for Grid { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { for y in 0..self.height() { for x in 0..self.width() { f.write_fmt(format_args!( "{}", match *self.get(&(x as i64, y as i64)).unwrap() { true => '.', false => '#', } ))?; } f.write_char('\n')?; } f.write_char('\n') } } #[cfg(test)] mod tests { use super::*; static TEST_VECTOR: &[u8] = b"ABCD EFGH IJKL FBCG"; static TEST_VECTOR_S: &str = "ABCD EFGH IJKL FBCG"; fn unchecked_load() -> Grid { Grid::from(TEST_VECTOR) } #[test] fn from_string() { let grid = unchecked_load(); assert_eq!(grid.data, "ABCDEFGHIJKLFBCG".as_bytes()); assert_eq!( TEST_VECTOR_S.parse::>().unwrap().data, "ABCDEFGHIJKLFBCG".as_bytes() ); } #[test] fn indexing() { let grid = unchecked_load(); assert_eq!(grid.get(&(0, 0)), Some(b'A').as_ref()); assert_eq!(grid.get(&(3, 3)), Some(b'G').as_ref()); assert_eq!(grid.get(&(-1, 0)), None); assert_eq!(grid.get(&(0, -1)), None); assert_eq!(grid.get(&(5, 0)), None); assert_eq!(grid.get(&(0, 5)), None); } #[test] fn forward_slice() { let grid = unchecked_load(); assert_eq!(grid.forward_slice(&(0, 0), 2), Some(b"AB".as_slice())); assert_eq!(grid.forward_slice(&(2, 0), 2), Some(b"CD".as_slice())); assert_eq!(grid.forward_slice(&(2, 0), 3), None); assert_eq!(grid.forward_slice(&(0, 2), 4), Some(b"IJKL".as_slice())); } #[test] fn row_iter() { let grid = unchecked_load(); assert_eq!( grid.row_iter(2).unwrap().collect::>(), [&b'I', &b'J', &b'K', &b'L'] ); assert!(grid.row_iter(-1).is_none()); assert!(grid.row_iter(4).is_none()); } #[test] fn col_iter() { let grid = unchecked_load(); assert_eq!( grid.col_iter(2).unwrap().collect::>(), [&b'C', &b'G', &b'K', &b'C'] ); assert!(grid.col_iter(-1).is_none()); assert!(grid.col_iter(4).is_none()); } #[test] fn find_all() { let grid = unchecked_load(); assert_eq!( grid.find_all(&b'G').collect::>(), [Coord2d { x: 2, y: 1 }, Coord2d { x: 3, y: 3 }] ) } #[test] fn neighbours_iter() { let grid = unchecked_load(); assert_eq!( grid.adjacent_iter(&(0, 1)).collect::>(), [ Item { pos: Coord2d { x: 0, y: 0 }, value: &b'A' }, Item { pos: Coord2d { x: 1, y: 0 }, value: &b'B' }, Item { pos: Coord2d { x: 1, y: 1 }, value: &b'F' }, Item { pos: Coord2d { x: 0, y: 2 }, value: &b'I' }, Item { pos: Coord2d { x: 1, y: 2 }, value: &b'J' }, ] ) } #[test] fn cardinal_iter() { let grid = unchecked_load(); assert_eq!( grid.cardinal_iter(&(0, 1)).collect::>(), [ Item { pos: Coord2d { x: 0, y: 0 }, value: &b'A' }, Item { pos: Coord2d { x: 1, y: 1 }, value: &b'F' }, Item { pos: Coord2d { x: 0, y: 2 }, value: &b'I' } ] ) } // #[test] // fn window_compare() { // let grid = unchecked_load(); // assert_eq!(grid.window_compare(b"IJKL"), &[(0, 2)]); // assert_eq!(grid.window_compare(b"BC"), &[(1, 0), (1, 3)]); // assert_eq!(grid.window_compare(b"LF").len(), 0); // } }