pub mod direction; pub mod position; pub use direction::*; use fnv::FnvHashMap as HashMap; use itertools::{join, Itertools, MinMaxResult}; pub use position::*; use std::{ fmt::Display, ops::{Index, IndexMut}, }; #[allow(clippy::len_without_is_empty)] // I mainly have this for assertions in benchmarks pub trait Grid: Index, Output = T> + IndexMut> { fn get(&self, pos: &PositionND) -> Option<&T>; fn insert>>(&mut self, pos: Pos, element: T); fn len(&self) -> usize; } #[derive(Debug, Clone, PartialEq)] pub struct HashGrid { pub fields: HashMap, T>, } impl Index> for HashGrid { type Output = T; fn index(&self, index: PositionND) -> &Self::Output { &self.fields[&index] } } impl IndexMut> for HashGrid { fn index_mut(&mut self, index: PositionND) -> &mut Self::Output { self.fields.get_mut(&index).expect("Key not in map") } } impl Grid for HashGrid { fn get(&self, pos: &PositionND) -> Option<&T> { self.fields.get(pos) } fn insert>>(&mut self, pos: Pos, t: T) { self.fields.insert(pos.into(), t); } fn len(&self) -> usize { self.fields.len() } } impl HashGrid { pub fn from_bytes_2d T + Copy>(raw: &str, mut f: F) -> HashGrid { raw.lines() .enumerate() .flat_map(move |(y, l)| l.bytes().enumerate().map(move |(x, c)| (PositionND([x as i64, y as i64]), f(c)))) .collect() } } impl std::iter::FromIterator<(PositionND, T)> for HashGrid { fn from_iter, T)>>(iter: I) -> Self { HashGrid { fields: iter.into_iter().collect() } } } #[derive(Debug, Clone, PartialEq)] pub struct VecGrid { pub fields: Vec>, } impl Grid for VecGrid { fn get(&self, pos: &PositionND<2>) -> Option<&T> { self.fields.get(pos.0[0] as usize)?.get(pos.0[1] as usize) } fn insert>>(&mut self, pos: Pos, element: T) { let PositionND([x, y]) = pos.into(); self.fields[x as usize][y as usize] = element; } fn len(&self) -> usize { self.fields.len() } } impl Index for VecGrid { type Output = T; fn index(&self, index: Position2D) -> &Self::Output { &self.fields[index.0[0] as usize][index.0[1] as usize] } } impl IndexMut for VecGrid { fn index_mut(&mut self, index: Position2D) -> &mut Self::Output { &mut self.fields[index.0[0] as usize][index.0[1] as usize] } } impl VecGrid { pub fn from_bytes_2d T + Copy>(raw: &str, f: F) -> VecGrid { VecGrid { fields: raw.lines().map(|l| l.bytes().map(f).collect()).collect() } } } pub struct Boundaries { pub x_min: i64, pub x_max: i64, pub y_min: i64, pub y_max: i64, } pub fn get_boundaries(input: &[&PositionND<2>]) -> Boundaries { let (x_min, x_max) = match input.iter().map(|p| p.0[0]).minmax() { MinMaxResult::NoElements => (0, 0), MinMaxResult::MinMax(min, max) => (min, max), MinMaxResult::OneElement(x) => (x, x), }; let (y_min, y_max) = match input.iter().map(|p| p.0[1]).minmax() { MinMaxResult::NoElements => (0, 0), MinMaxResult::MinMax(min, max) => (min, max), MinMaxResult::OneElement(x) => (x, x), }; Boundaries { x_min, x_max, y_min, y_max } } pub fn draw_ascii(coordinates: &HashMap, T>) -> String { let b = get_boundaries(&coordinates.keys().collect::>()); join( (b.y_min..=b.y_max).rev().map(|y| { (b.x_min..=b.x_max).map(|x| coordinates.get(&PositionND([x, y])).unwrap_or(&T::default()).to_string()).collect::() }), "\n", ) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add() { assert_eq!(PositionND([0, 2]) + PositionND([-1, 0]), [-1, 2].into()); assert_eq!(PositionND([0, -1]) + PositionND::from(Direction::Up), [0, 0].into()); } #[test] fn test_sub() { assert_eq!(PositionND([0, 2]) - PositionND([-1, 0]), [1, 2].into()); assert_eq!(PositionND([0, -1]) - PositionND([0, -1]), [0, 0].into()); } #[test] fn test_mul() { assert_eq!(PositionND([0, 2]) * 5, [0, 10].into()); assert_eq!(PositionND([0, -1]) * -2, [0, 2].into()); } }