use std::collections::HashMap; /** Contains the values on a given board as well as a bit of metadata. The actual tiles are stored in a `HashMap` to allow disparate data. */ #[derive(Clone, Debug)] pub struct Board { width: usize, height: usize, num_options: usize, tiles: HashMap<(usize, usize), usize>, } impl Board { /** Creates a new board. Panics if width or height are zero, or there are less than two options. */ pub fn new(width: usize, height: usize, num_options: usize) -> Self { assert!(width != 0); assert!(height != 0); assert!(num_options >= 2); Self { width, height, num_options, tiles: HashMap::new(), } } pub fn get_width(&self) -> usize { self.width } pub fn get_height(&self) -> usize { self.height } pub fn get_num_options(&self) -> usize { self.num_options } /** Set the value of a tile, or removes it if `None` is given. Returns the previous value or `None`. Panics if `x` or `y` are beyond the boards dimensions or `value` is greater or equal to `num_options`. */ pub fn set_tile(&mut self, x: usize, y: usize, value: Option) -> Option { assert!(x < self.width); assert!(y < self.height); if let Some(value) = value { assert!(value < self.num_options); self.tiles.insert((x, y), value) } else { self.tiles.remove(&(x, y)) } } /** Get the value of a tile, or `None` if no value is known. Panics if `x` or `y` are beyond the boards dimensions. */ pub fn get_tile(&self, x: usize, y: usize) -> Option { assert!(x < self.width); assert!(y < self.height); self.tiles.get(&(x, y)).copied() } /** Get all the values of a column, in ascending y order. If a value in one row is not present, `None` is given instead. */ pub fn get_column(&self, x: usize) -> Vec> { (0..self.height) .map(|y| self.tiles.get(&(x, y)).copied()) .collect() } /** Get all the values of a row, in ascending x order. If a value in one column is not present, `None` is given instead. */ pub fn get_row(&self, y: usize) -> Vec> { (0..self.width) .map(|x| self.tiles.get(&(x, y)).copied()) .collect() } } impl std::fmt::Display for Board { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for x in 0..self.width { for y in 0..self.height { if let Some(value) = self.tiles.get(&(x, y)) { write!(f, "{value}\t")?; } else { write!(f, "?\t")?; } } writeln!(f)?; } Ok(()) } }