#[derive(Clone, Copy)] pub struct Tile { value: Option, } impl Tile { pub fn new_with_value(value: usize) -> Self { Self { value: Some(value) } } pub fn new_empty() -> Self { Self { value: None } } pub fn value(&self) -> Option { self.value.clone() } pub fn is_empty(&self) -> bool { if let Some(_) = self.value { false } else { true } } } /// /// displayability /// impl Tile { const TILE_LENGTH: usize = 7; const TILE_HEIGHT: usize = 3; pub fn display(&self) -> String { match self.value { Some(value) => Self::display_number(value), None => [ // empty tile " ", " ", " ", ] .join("\n"), } } pub fn display_number(value: usize) -> String { let result = [ // number tile "┌─────┐", &Self::pad_both(value.to_string(), Self::TILE_LENGTH), "└─────┘", ] .join("\n"); result } fn pad_both(text: String, length: usize) -> String { let mut text = text; while text.len() < length { text = format!(" {text} "); } if text.len() > length { (&text)[..length].to_string() } else { text } } } #[derive(Clone)] pub struct Grid { size: usize, tiles: Vec>, } impl Grid { /// /// constructor /// pub fn new(size: usize) -> Self { let tiles = (0..size) .map(|_| (0..size).map(|_| Tile::new_empty()).collect()) .collect(); Self { size, tiles } } /// /// set the value of the tile at the selected position /// pub fn set(&mut self, (x, y): (usize, usize), value: Option) { self.tiles[y][x] = if let Some(value) = value { Tile::new_with_value(value) } else { Tile::new_empty() }; } /// /// get a tile if the position is in the grid /// pub fn get(&self, (x, y): (usize, usize)) -> Option<&Tile> { match self.tiles.get(y).map(|row| row.get(x)) { Some(Some(tile)) => Some(tile), _ => None, } } /// /// get the value of a tile if the position is in the grid and the tile has a value /// pub fn get_val(&self, (x, y): (usize, usize)) -> Option { match self.get((x, y)).map(|tile| tile.value()) { Some(Some(value)) => Some(value), _ => None, } } /// /// get the size of the grid /// pub fn size(&self) -> usize { self.size } /// /// move a tile over another one, replace the previously occupied place by an empty tile and overrides the destination /// pub fn move_tile(&mut self, (src_x, src_y): (usize, usize), (dst_x, dst_y): (usize, usize)) { let src = self.tiles[src_y][src_x].clone(); self.tiles[dst_y][dst_x] = src; self.tiles[src_y][src_x] = Tile::new_empty(); } } /// /// displayability /// impl Grid { /// 0: '┘' /// /// 1: '┐' /// /// 2: '┌' /// /// 3: '└' /// /// 4: '┼' /// /// 5: '─' /// /// 6: '├' /// /// 7: '┤' /// /// 8: '┴' /// /// 9: '┬' /// /// 10: '│' const DISPLAY_CHAR: [&'static str; 11] = ["┘", "┐", "┌", "└", "┼", "─", "├", "┤", "┴", "┬", "│"]; /// /// returns a string of multiple lines representing the grid /// pub fn display(&self) -> String { let tiles: Vec> = self .tiles .iter() .map(|row| row.iter().map(|tile| tile.display()).collect()) .collect(); let row_representations: Vec<_> = tiles .iter() .map(|row_representation| { let mut row_lines = (0..Tile::TILE_HEIGHT).map(|_| vec![]).collect::>(); // push every item lines in [`row_lines`] for item_representation in row_representation { item_representation .split('\n') .into_iter() .zip(row_lines.iter_mut()) .for_each(|(item_line, row_line)| row_line.push(item_line.to_string())); } // join lines of [`row_lines`] let row_lines = row_lines .iter_mut() .map(|line_parts| line_parts.join(Self::DISPLAY_CHAR[10]).to_string()) .map(|line| [Self::DISPLAY_CHAR[10], &line, Self::DISPLAY_CHAR[10]].join("")) .collect::>(); row_lines.join("\n") }) .collect(); [ self.first_grid_display_line(), row_representations.join(&self.between_grid_display_line()), self.last_grid_display_line(), ] .join("\n") } fn first_grid_display_line(&self) -> String { let middle = (0..self.size) .map(|_| Self::DISPLAY_CHAR[5].repeat(Tile::TILE_LENGTH)) .collect::>() .join(Self::DISPLAY_CHAR[9]); [Self::DISPLAY_CHAR[2], &middle, Self::DISPLAY_CHAR[1]].join("") } fn between_grid_display_line(&self) -> String { let middle = (0..self.size) .map(|_| Self::DISPLAY_CHAR[5].repeat(Tile::TILE_LENGTH)) .collect::>() .join(Self::DISPLAY_CHAR[4]); [ "\n", Self::DISPLAY_CHAR[6], &middle, Self::DISPLAY_CHAR[7], "\n", ] .join("") } fn last_grid_display_line(&self) -> String { let middle = (0..self.size) .map(|_| Self::DISPLAY_CHAR[5].repeat(Tile::TILE_LENGTH)) .collect::>() .join(Self::DISPLAY_CHAR[8]); [Self::DISPLAY_CHAR[3], &middle, Self::DISPLAY_CHAR[0], "\n"].join("") } }