2023-07-22 23:43:49 +00:00
|
|
|
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;
|
2023-07-22 03:18:06 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
2023-07-22 23:43:49 +00:00
|
|
|
pub enum Letter {
|
2023-07-22 03:18:06 +00:00
|
|
|
FixedLetter{text: char, points: u32},
|
|
|
|
Blank(char)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2023-07-22 23:43:49 +00:00
|
|
|
pub enum CellType {
|
2023-07-22 03:18:06 +00:00
|
|
|
Normal,
|
|
|
|
DoubleWord,
|
|
|
|
DoubleLetter,
|
|
|
|
TripleLetter,
|
|
|
|
TripleWord,
|
|
|
|
Start,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2023-07-22 23:43:49 +00:00
|
|
|
pub struct Cell {
|
|
|
|
pub value: Option<Letter>,
|
2023-07-22 03:18:06 +00:00
|
|
|
cell_type: CellType,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2023-07-22 23:43:49 +00:00
|
|
|
pub struct Board {
|
2023-07-22 03:18:06 +00:00
|
|
|
cells: Vec<Cell>,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Board {
|
2023-07-22 23:43:49 +00:00
|
|
|
pub fn new() -> Self {
|
2023-07-22 03:18:06 +00:00
|
|
|
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}
|
|
|
|
}
|
|
|
|
|
2023-07-22 23:43:49 +00:00
|
|
|
pub fn get_cell(&self, x: u8, y: u8) -> Result<&Cell, &str> {
|
2023-07-22 03:18:06 +00:00
|
|
|
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())
|
|
|
|
}
|
|
|
|
}
|
2023-07-22 23:43:49 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
}
|
2023-07-22 03:18:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[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));
|
|
|
|
}
|
|
|
|
}
|