Refactor; add word searching
This commit is contained in:
parent
c842119254
commit
4f5de6e52e
3 changed files with 322 additions and 48 deletions
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "WordGrid"
|
||||
name = "word_grid"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
|
344
src/lib.rs
344
src/lib.rs
|
@ -7,10 +7,67 @@ pub const GRID_LENGTH: u8 = 15;
|
|||
pub const TRAY_LENGTH: u8 = 7;
|
||||
pub const ALL_LETTERS_BONUS: u32 = 50;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Direction {
|
||||
Row, Column
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
fn invert(&self) -> Self {
|
||||
match &self {
|
||||
Direction::Row => {Direction::Column}
|
||||
Direction::Column => {Direction::Row}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Coordinates (pub u8, pub u8);
|
||||
|
||||
impl Coordinates {
|
||||
fn add(&self, direction: Direction, i: i8) -> Option<Self> {
|
||||
let proposed = match direction {
|
||||
Direction::Column => {(self.0 as i8, self.1 as i8+i)}
|
||||
Direction::Row => {(self.0 as i8+i, self.1 as i8)}
|
||||
};
|
||||
|
||||
if proposed.0 < 0 || proposed.0 >= GRID_LENGTH as i8 || proposed.1 < 0 || proposed.1 >= GRID_LENGTH as i8 {
|
||||
None
|
||||
} else{
|
||||
Some(Coordinates(proposed.0 as u8, proposed.1 as u8))
|
||||
}
|
||||
}
|
||||
|
||||
fn increment(&self, direction: Direction) -> Option<Self>{
|
||||
self.add(direction, 1)
|
||||
}
|
||||
|
||||
fn decrement(&self, direction: Direction) -> Option<Self>{
|
||||
self.add(direction, -1)
|
||||
}
|
||||
|
||||
fn map_to_index(&self) -> usize {
|
||||
(self.0 + GRID_LENGTH*self.1) as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Letter {
|
||||
FixedLetter{text: char, points: u32},
|
||||
Blank(char)
|
||||
pub struct Letter {
|
||||
text: char,
|
||||
points: u32,
|
||||
ephemeral: bool,
|
||||
is_blank: bool,
|
||||
}
|
||||
|
||||
impl Letter {
|
||||
pub fn new_fixed(text: char, points: u32) -> Self {
|
||||
Letter {
|
||||
text,
|
||||
points,
|
||||
ephemeral: false,
|
||||
is_blank: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -27,12 +84,9 @@ pub enum CellType {
|
|||
pub struct Cell {
|
||||
pub value: Option<Letter>,
|
||||
cell_type: CellType,
|
||||
coordinates: Coordinates,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Board {
|
||||
cells: Vec<Cell>,
|
||||
}
|
||||
|
||||
pub struct Dictionary {
|
||||
words: Vec<String>,
|
||||
|
@ -92,6 +146,16 @@ impl Dictionary {
|
|||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Board {
|
||||
cells: Vec<Cell>,
|
||||
}
|
||||
|
||||
struct Word<'a> {
|
||||
cells: Vec<&'a Cell>,
|
||||
coords: Coordinates,
|
||||
}
|
||||
|
||||
impl Board {
|
||||
pub fn new() -> Self {
|
||||
let mut cells = Vec::new();
|
||||
|
@ -112,10 +176,10 @@ impl Board {
|
|||
}
|
||||
}
|
||||
|
||||
for i in 0..GRID_LENGTH {
|
||||
let i = map_to_corner(i);
|
||||
for j in 0..GRID_LENGTH {
|
||||
let j = map_to_corner(j);
|
||||
for i_orig in 0..GRID_LENGTH {
|
||||
let i = map_to_corner(i_orig);
|
||||
for j_orig in 0..GRID_LENGTH {
|
||||
let j = map_to_corner(j_orig);
|
||||
|
||||
let mut typee = CellType::Normal;
|
||||
|
||||
|
@ -152,6 +216,7 @@ impl Board {
|
|||
cells.push(Cell {
|
||||
cell_type: typee,
|
||||
value: None,
|
||||
coordinates: Coordinates(i, j),
|
||||
})
|
||||
|
||||
}
|
||||
|
@ -160,23 +225,129 @@ impl Board {
|
|||
Board {cells}
|
||||
}
|
||||
|
||||
pub fn get_cell(&self, x: u8, y: u8) -> Result<&Cell, &str> {
|
||||
if x >= GRID_LENGTH || y >= GRID_LENGTH {
|
||||
pub fn get_cell(&self, coordinates: Coordinates) -> Result<&Cell, &str> {
|
||||
if coordinates.0 >= GRID_LENGTH || coordinates.1 >= 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())
|
||||
let index = coordinates.map_to_index();
|
||||
Ok(self.cells.get(index).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cell_mut(&mut self, x: u8, y: u8) -> Result<&mut Cell, &str> {
|
||||
if x >= GRID_LENGTH || y >= GRID_LENGTH {
|
||||
pub fn get_cell_mut(&mut self, coordinates: Coordinates) -> Result<&mut Cell, &str> {
|
||||
if coordinates.0 >= GRID_LENGTH || coordinates.1 >= 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())
|
||||
let index = coordinates.map_to_index();
|
||||
Ok(self.cells.get_mut(index).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn score_move(&self) -> Result<u32, &str> {
|
||||
// We don't assume that the move is valid, so let's first establish that
|
||||
|
||||
|
||||
// Let's first establish what rows and columns tiles were played in
|
||||
let mut rows_played = HashSet::with_capacity(15);
|
||||
let mut columns_played = HashSet::with_capacity(15);
|
||||
let mut tiles_played = 0;
|
||||
|
||||
for x in 0..GRID_LENGTH {
|
||||
for y in 0..GRID_LENGTH {
|
||||
let coords = Coordinates(x, y);
|
||||
let cell = self.get_cell(coords).unwrap();
|
||||
match &cell.value {
|
||||
Some(value) => {
|
||||
if value.ephemeral {
|
||||
rows_played.insert(x);
|
||||
columns_played.insert(y);
|
||||
tiles_played += 1;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if rows_played.is_empty() {
|
||||
return Err("Tiles need to be played")
|
||||
} else if rows_played.len() > 1 && columns_played.len() > 1 {
|
||||
return Err("Tiles need to be played on one row or column")
|
||||
}
|
||||
|
||||
let direction = if rows_played.len() > 1 {
|
||||
Direction::Column
|
||||
} else {
|
||||
Direction::Row
|
||||
};
|
||||
|
||||
let starting_row = *rows_played.iter().min().unwrap();
|
||||
let starting_column = *columns_played.iter().min().unwrap();
|
||||
|
||||
let mut starting_coords = Coordinates(starting_row, starting_column);
|
||||
|
||||
|
||||
|
||||
// At this point we now know that we're at the start of the word and we have the direction.
|
||||
// Now we'll head forward and look for every word that intersects one of the played tiles
|
||||
|
||||
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn find_word(&self, mut start_coords: Coordinates, direction: Direction) -> Option<Word> {
|
||||
// let's see how far we can backtrack to the start of the word
|
||||
let mut times_moved = 0;
|
||||
loop {
|
||||
let one_back = start_coords.add(direction, -times_moved);
|
||||
match one_back {
|
||||
None => { break }
|
||||
Some(new_coords) => {
|
||||
let cell = self.get_cell(new_coords).unwrap();
|
||||
if cell.value.is_some(){
|
||||
times_moved += 1;
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if times_moved == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
start_coords = start_coords.add(direction, -times_moved + 1).unwrap();
|
||||
|
||||
// since we moved and we know that start_coords has started on a letter, we know we have a word
|
||||
// we'll now keep track of the cells that form it
|
||||
let mut cells = Vec::with_capacity(GRID_LENGTH as usize);
|
||||
cells.push(self.get_cell(start_coords).unwrap());
|
||||
|
||||
loop {
|
||||
let position = start_coords.add(direction, cells.len() as i8);
|
||||
match position {
|
||||
None => {break}
|
||||
Some(x) => {
|
||||
let cell = self.get_cell(x).unwrap();
|
||||
match cell.value {
|
||||
None => {break}
|
||||
Some(_) => {
|
||||
cells.push(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(Word {
|
||||
cells,
|
||||
coords: start_coords,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl fmt::Display for Board {
|
||||
|
@ -192,9 +363,10 @@ impl fmt::Display for Board {
|
|||
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 coords = Coordinates(x, y);
|
||||
|
||||
let cell = self.get_cell(coords).unwrap();
|
||||
|
||||
let color = match cell.cell_type {
|
||||
CellType::Normal => {normal}
|
||||
|
@ -207,10 +379,7 @@ impl fmt::Display for Board {
|
|||
|
||||
let content = match &cell.value {
|
||||
None => {' '}
|
||||
Some(letter) => {*match letter {
|
||||
Letter::FixedLetter {text, points} => {text},
|
||||
Letter::Blank(text) => {text}
|
||||
}}
|
||||
Some(letter) => {letter.text}
|
||||
};
|
||||
|
||||
|
||||
|
@ -237,18 +406,123 @@ mod tests {
|
|||
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(Coordinates(0, 0)).unwrap().cell_type, CellType::TripleWord));
|
||||
assert!(matches!(board.get_cell(Coordinates(1, 0)).unwrap().cell_type, CellType::Normal));
|
||||
assert!(matches!(board.get_cell(Coordinates(0, 1)).unwrap().cell_type, CellType::Normal));
|
||||
assert!(matches!(board.get_cell(Coordinates(1, 1)).unwrap().cell_type, CellType::DoubleWord));
|
||||
|
||||
assert!(matches!(board.get_cell(Coordinates(13, 13)).unwrap().cell_type, CellType::DoubleWord));
|
||||
assert!(matches!(board.get_cell(Coordinates(14, 14)).unwrap().cell_type, CellType::TripleWord));
|
||||
assert!(matches!(board.get_cell(Coordinates(11, 14)).unwrap().cell_type, CellType::DoubleLetter));
|
||||
|
||||
assert!(matches!(board.get_cell(Coordinates(7, 7)).unwrap().cell_type, CellType::Start));
|
||||
assert!(matches!(board.get_cell(Coordinates(8, 6)).unwrap().cell_type, CellType::DoubleLetter));
|
||||
assert!(matches!(board.get_cell(Coordinates(5, 9)).unwrap().cell_type, CellType::TripleLetter));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_word_finding() {
|
||||
let mut board = Board::new();
|
||||
|
||||
board.get_cell_mut(Coordinates(8, 6)).unwrap().value = Some(Letter::new_fixed('J', 0));
|
||||
board.get_cell_mut(Coordinates(8, 7)).unwrap().value = Some(Letter::new_fixed('O', 0));
|
||||
board.get_cell_mut(Coordinates(8, 8)).unwrap().value = Some(Letter::new_fixed( 'E', 0));
|
||||
board.get_cell_mut(Coordinates(8, 9)).unwrap().value = Some(Letter::new_fixed( 'L', 0));
|
||||
|
||||
board.get_cell_mut(Coordinates(0, 0)).unwrap().value = Some(Letter::new_fixed('I', 0));
|
||||
board.get_cell_mut(Coordinates(1, 0)).unwrap().value = Some(Letter::new_fixed('S', 0));
|
||||
|
||||
board.get_cell_mut(Coordinates(3, 0)).unwrap().value = Some(Letter::new_fixed('C', 0));
|
||||
board.get_cell_mut(Coordinates(4, 0)).unwrap().value = Some(Letter::new_fixed('O', 0));
|
||||
board.get_cell_mut(Coordinates(5, 0)).unwrap().value = Some(Letter::new_fixed('O', 0));
|
||||
board.get_cell_mut(Coordinates(6, 0)).unwrap().value = Some(Letter::new_fixed('L', 0));
|
||||
|
||||
|
||||
board.get_cell_mut(Coordinates(9, 8)).unwrap().value = Some(Letter::new_fixed( 'G', 0));
|
||||
board.get_cell_mut(Coordinates(10, 8)).unwrap().value = Some(Letter::new_fixed( 'G', 0));
|
||||
|
||||
fn word_to_text(word: Word) -> String {
|
||||
let mut text = String::with_capacity(word.cells.len());
|
||||
for cell in word.cells {
|
||||
text.push(cell.value.as_ref().unwrap().text);
|
||||
}
|
||||
|
||||
text
|
||||
}
|
||||
|
||||
for x in vec![6, 7, 8, 9] {
|
||||
println!("x is {}", x);
|
||||
let first_word = board.find_word(Coordinates(8, x), Direction::Column);
|
||||
match first_word {
|
||||
None => {panic!("Expected to find word JOEL")}
|
||||
Some(x) => {
|
||||
assert_eq!(x.coords.0, 8);
|
||||
assert_eq!(x.coords.1, 6);
|
||||
|
||||
assert_eq!(word_to_text(x), "JOEL");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let single_letter_word = board.find_word(Coordinates(8, 9), Direction::Row);
|
||||
match single_letter_word {
|
||||
None => {panic!("Expected to find letter L")}
|
||||
Some(x) => {
|
||||
assert_eq!(x.coords.0, 8);
|
||||
assert_eq!(x.coords.1, 9);
|
||||
|
||||
assert_eq!(word_to_text(x), "L");
|
||||
}
|
||||
}
|
||||
|
||||
for x in vec![0, 1] {
|
||||
println!("x is {}", x);
|
||||
let word = board.find_word(Coordinates(x, 0), Direction::Row);
|
||||
match word {
|
||||
None => {panic!("Expected to find word IS")}
|
||||
Some(x) => {
|
||||
assert_eq!(x.coords.0, 0);
|
||||
assert_eq!(x.coords.1, 0);
|
||||
|
||||
assert_eq!(word_to_text(x), "IS");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for x in vec![3, 4, 5, 6] {
|
||||
println!("x is {}", x);
|
||||
let word = board.find_word(Coordinates(x, 0), Direction::Row);
|
||||
match word {
|
||||
None => {panic!("Expected to find word COOL")}
|
||||
Some(x) => {
|
||||
assert_eq!(x.coords.0, 3);
|
||||
assert_eq!(x.coords.1, 0);
|
||||
|
||||
assert_eq!(word_to_text(x), "COOL");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let no_word = board.find_word(Coordinates(2, 0), Direction::Row);
|
||||
assert!(no_word.is_none());
|
||||
|
||||
let word = board.find_word(Coordinates(10, 8), Direction::Row);
|
||||
match word {
|
||||
None => {panic!("Expected to find word EGG")}
|
||||
Some(x) => {
|
||||
assert_eq!(x.coords.0, 8);
|
||||
assert_eq!(x.coords.1, 8);
|
||||
|
||||
assert_eq!(word_to_text(x), "EGG");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -1,21 +1,21 @@
|
|||
use WordGrid::{Board, Letter};
|
||||
use word_grid::{Board, Coordinates, Letter};
|
||||
|
||||
fn main() {
|
||||
let mut board = Board::new();
|
||||
|
||||
let c1 = board.get_cell_mut(8, 6).unwrap();
|
||||
c1.value = Some(Letter::FixedLetter {text: 'J', points: 0});
|
||||
board.get_cell_mut(8, 7).unwrap().value = Some(Letter::FixedLetter {text: 'o', points: 0});
|
||||
board.get_cell_mut(8, 8).unwrap().value = Some(Letter::FixedLetter {text: 'e', points: 0});
|
||||
board.get_cell_mut(8, 9).unwrap().value = Some(Letter::FixedLetter {text: 'l', points: 0});
|
||||
let c1 = board.get_cell_mut(Coordinates(8, 6)).unwrap();
|
||||
c1.value = Some(Letter::new_fixed( 'J', 0));
|
||||
board.get_cell_mut(Coordinates(8, 7)).unwrap().value = Some(Letter::new_fixed('o', 0));
|
||||
board.get_cell_mut(Coordinates(8, 8)).unwrap().value = Some(Letter::new_fixed( 'e', 0));
|
||||
board.get_cell_mut(Coordinates(8, 9)).unwrap().value = Some(Letter::new_fixed( 'l', 0));
|
||||
|
||||
board.get_cell_mut(9, 8).unwrap().value = Some(Letter::FixedLetter {text: 'i', points: 0});
|
||||
board.get_cell_mut(9, 9).unwrap().value = Some(Letter::FixedLetter {text: 's', points: 0});
|
||||
board.get_cell_mut(Coordinates(9, 8)).unwrap().value = Some(Letter::new_fixed( 'i', 0));
|
||||
board.get_cell_mut(Coordinates(9, 9)).unwrap().value = Some(Letter::new_fixed( 's', 0));
|
||||
|
||||
board.get_cell_mut(10, 8).unwrap().value = Some(Letter::FixedLetter {text: 'c', points: 0});
|
||||
board.get_cell_mut(10, 9).unwrap().value = Some(Letter::FixedLetter {text: 'o', points: 0});
|
||||
board.get_cell_mut(10, 10).unwrap().value = Some(Letter::FixedLetter {text: 'o', points: 0});
|
||||
board.get_cell_mut(10, 11).unwrap().value = Some(Letter::FixedLetter {text: 'l', points: 0});
|
||||
board.get_cell_mut(Coordinates(10, 8)).unwrap().value = Some(Letter::new_fixed( 'c', 0));
|
||||
board.get_cell_mut(Coordinates(10, 9)).unwrap().value = Some(Letter::new_fixed( 'o', 0));
|
||||
board.get_cell_mut(Coordinates(10, 10)).unwrap().value = Some(Letter::new_fixed( 'o', 0));
|
||||
board.get_cell_mut(Coordinates(10, 11)).unwrap().value = Some(Letter::new_fixed( 'l', 0));
|
||||
|
||||
println!("{}", board);
|
||||
|
||||
|
|
Loading…
Reference in a new issue