use std::fmt; use std::fmt::{Formatter, Write}; pub const GRID_LENGTH: u8 = 15; pub const TRAY_LENGTH: u8 = 7; pub const ALL_LETTERS_BONUS: u32 = 50; #[derive(Debug)] pub enum Letter { FixedLetter{text: char, points: u32}, Blank(char) } #[derive(Debug)] pub enum CellType { Normal, DoubleWord, DoubleLetter, TripleLetter, TripleWord, Start, } #[derive(Debug)] pub struct Cell { pub value: Option, cell_type: CellType, } #[derive(Debug)] pub struct Board { cells: Vec, } impl Board { pub fn new() -> Self { let mut cells = Vec::new(); /// Since the board is symmetrical in both directions for the purposes of our logic we can keep our coordinates in one corner /// /// # Arguments /// /// * `x`: A coordinate /// /// returns: u8 The coordinate mapped onto the lower-half fn map_to_corner(x: u8) -> u8 { return if x > GRID_LENGTH / 2 { GRID_LENGTH - x - 1 } else { x } } for i in 0..GRID_LENGTH { let i = map_to_corner(i); for j in 0..GRID_LENGTH { let j = map_to_corner(j); let mut typee = CellType::Normal; // double word scores are diagonals if i == j { typee = CellType::DoubleWord; } // Triple letters if (i % 4 == 1) && j % 4 == 1 && !(i == 1 && j == 1) { typee = CellType::TripleLetter; } // Double letters if (i % 4 == 2) && (j % 4 == 2) && !( i == 2 && j == 2 ) { typee = CellType::DoubleLetter; } if (i.min(j) == 0 && i.max(j) == 3) || (i.min(j)==3 && i.max(j) == 7) { typee = CellType::DoubleLetter; } // Triple word scores if (i % 7 == 0) && (j % 7 == 0) { typee = CellType::TripleWord; } // Start if i == 7 && j == 7 { typee = CellType::Start; } cells.push(Cell { cell_type: typee, value: None, }) } } Board {cells} } pub fn get_cell(&self, x: u8, y: u8) -> Result<&Cell, &str> { if x >= GRID_LENGTH || y >= GRID_LENGTH { Err("x & y must be within the board's coordinates") } else { let coord = (x + GRID_LENGTH*y) as usize; Ok(self.cells.get(coord).unwrap()) } } pub fn get_cell_mut(&mut self, x: u8, y: u8) -> Result<&mut Cell, &str> { if x >= GRID_LENGTH || y >= GRID_LENGTH { Err("x & y must be within the board's coordinates") } else { let coord = (x + GRID_LENGTH*y) as usize; Ok(self.cells.get_mut(coord).unwrap()) } } } impl fmt::Display for Board { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut str = String::new(); let normal = "\x1b[48;5;174m\x1b[38;5;0m"; let triple_word = "\x1b[48;5;196m\x1b[38;5;0m"; let double_word = "\x1b[48;5;204m\x1b[38;5;0m"; let triple_letter = "\x1b[48;5;21m\x1b[38;5;15m"; let double_letter = "\x1b[48;5;51m\x1b[38;5;0m"; str.write_char('\n').unwrap(); for x in 0..GRID_LENGTH { for y in 0..GRID_LENGTH { let cell = self.get_cell(x, y).unwrap(); let color = match cell.cell_type { CellType::Normal => {normal} CellType::DoubleWord => {double_word} CellType::DoubleLetter => {double_letter} CellType::TripleLetter => {triple_letter} CellType::TripleWord => {triple_word} CellType::Start => {double_word} }; let content = match &cell.value { None => {' '} Some(letter) => {*match letter { Letter::FixedLetter {text, points} => {text}, Letter::Blank(text) => {text} }} }; str.write_str(color).unwrap(); str.write_char(content).unwrap(); } str.write_str("\x1b[0m\n").unwrap(); } write!(f, "{}", str) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_cell_types() { let board = Board::new(); assert!(matches!(board.get_cell(0, 0).unwrap().cell_type, CellType::TripleWord)); assert!(matches!(board.get_cell(1, 0).unwrap().cell_type, CellType::Normal)); assert!(matches!(board.get_cell(0, 1).unwrap().cell_type, CellType::Normal)); assert!(matches!(board.get_cell(1, 1).unwrap().cell_type, CellType::DoubleWord)); assert!(matches!(board.get_cell(13, 13).unwrap().cell_type, CellType::DoubleWord)); assert!(matches!(board.get_cell(14, 14).unwrap().cell_type, CellType::TripleWord)); assert!(matches!(board.get_cell(11, 14).unwrap().cell_type, CellType::DoubleLetter)); assert!(matches!(board.get_cell(7, 7).unwrap().cell_type, CellType::Start)); assert!(matches!(board.get_cell(8, 6).unwrap().cell_type, CellType::DoubleLetter)); assert!(matches!(board.get_cell(5, 9).unwrap().cell_type, CellType::TripleLetter)); } }