From 5184c5b52010e720b0a4b79afd17dc547f3b0172 Mon Sep 17 00:00:00 2001 From: Joel Therrien Date: Fri, 28 Jul 2023 21:53:01 -0700 Subject: [PATCH] Add full word finding --- src/lib.rs | 191 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 177 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f051e15..4563ae5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,7 +51,7 @@ impl Coordinates { } } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub struct Letter { text: char, points: u32, @@ -216,7 +216,7 @@ impl Board { cells.push(Cell { cell_type: typee, value: None, - coordinates: Coordinates(i, j), + coordinates: Coordinates(j_orig, i_orig), }) } @@ -243,7 +243,7 @@ impl Board { } } - pub fn score_move(&self) -> Result { + fn find_played_words(&self) -> Result, &str> { // We don't assume that the move is valid, so let's first establish that @@ -276,27 +276,44 @@ impl Board { } let direction = if rows_played.len() > 1 { - Direction::Column - } else { Direction::Row + } else { + Direction::Column }; 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); + let main_word = self.find_word_at_position(starting_coords, direction).unwrap(); + starting_coords = main_word.coords; + let mut words = Vec::new(); // 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 + for cell in main_word.cells.as_slice() { + if cell.value.as_ref().unwrap().ephemeral { + let side_word = self.find_word_at_position(cell.coordinates, direction.invert()); + match side_word { + None => {} + Some(side_word) => { + if side_word.cells.len() > 1 { + words.push(side_word); + } + } + } + } + } + words.push(main_word); - todo!() + Ok(words) } - fn find_word(&self, mut start_coords: Coordinates, direction: Direction) -> Option { + fn find_word_at_position(&self, mut start_coords: Coordinates, direction: Direction) -> Option { // let's see how far we can backtrack to the start of the word let mut times_moved = 0; loop { @@ -421,7 +438,22 @@ mod tests { } #[test] - fn test_word_finding() { + fn test_cell_coordinates() { + let board = Board::new(); + + for x in 0..GRID_LENGTH { + for y in 0..GRID_LENGTH { + let cell = board.get_cell(Coordinates(x, y)).unwrap(); + let coords = cell.coordinates; + + assert_eq!(x, coords.0); + assert_eq!(y, coords.1); + } + } + } + + #[test] + fn test_word_finding_at_position() { let mut board = Board::new(); board.get_cell_mut(Coordinates(8, 6)).unwrap().value = Some(Letter::new_fixed('J', 0)); @@ -452,7 +484,7 @@ mod tests { for x in vec![6, 7, 8, 9] { println!("x is {}", x); - let first_word = board.find_word(Coordinates(8, x), Direction::Column); + let first_word = board.find_word_at_position(Coordinates(8, x), Direction::Column); match first_word { None => {panic!("Expected to find word JOEL")} Some(x) => { @@ -465,7 +497,7 @@ mod tests { } } - let single_letter_word = board.find_word(Coordinates(8, 9), Direction::Row); + let single_letter_word = board.find_word_at_position(Coordinates(8, 9), Direction::Row); match single_letter_word { None => {panic!("Expected to find letter L")} Some(x) => { @@ -478,7 +510,7 @@ mod tests { for x in vec![0, 1] { println!("x is {}", x); - let word = board.find_word(Coordinates(x, 0), Direction::Row); + let word = board.find_word_at_position(Coordinates(x, 0), Direction::Row); match word { None => {panic!("Expected to find word IS")} Some(x) => { @@ -493,7 +525,7 @@ mod tests { for x in vec![3, 4, 5, 6] { println!("x is {}", x); - let word = board.find_word(Coordinates(x, 0), Direction::Row); + let word = board.find_word_at_position(Coordinates(x, 0), Direction::Row); match word { None => {panic!("Expected to find word COOL")} Some(x) => { @@ -506,10 +538,10 @@ mod tests { } } - let no_word = board.find_word(Coordinates(2, 0), Direction::Row); + let no_word = board.find_word_at_position(Coordinates(2, 0), Direction::Row); assert!(no_word.is_none()); - let word = board.find_word(Coordinates(10, 8), Direction::Row); + let word = board.find_word_at_position(Coordinates(10, 8), Direction::Row); match word { None => {panic!("Expected to find word EGG")} Some(x) => { @@ -523,6 +555,137 @@ mod tests { + } + + + #[test] + fn test_word_finding_whole_board() { + let mut board = Board::new(); + + fn make_letter(x: char, ephemeral: bool) -> Letter { + Letter { + text: x, + points: 0, + ephemeral, + is_blank: false, + } + } + + let words = board.find_played_words(); + match words { + Ok(_) => {panic!("Expected to find no words")} + Err(x) => {assert_eq!(x, "Tiles need to be played")} + } + + 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(make_letter('O', true)); + board.get_cell_mut(Coordinates(8, 8)).unwrap().value = Some(make_letter('E', true)); + 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)); + + fn word_to_text(word: &Word) -> String { + let mut text = String::with_capacity(word.cells.len()); + for cell in word.cells.as_slice() { + text.push(cell.value.as_ref().unwrap().text); + } + + text + } + + fn check_board(board: &mut Board, inverted: bool) { + let words = board.find_played_words(); + match words { + Ok(x) => { + assert_eq!(x.len(), 1); + let word = x.get(0).unwrap(); + assert_eq!(word_to_text(word), "JOEL"); + } + Err(e) => { panic!("Expected to find a word to play; found error {}", e) } + } + + let maybe_invert = |coords: Coordinates| { + if inverted { + return Coordinates(coords.1, coords.0); + } + return coords; + }; + + let maybe_invert_direction = |direction: Direction| { + if inverted { + return direction.invert(); + } + return direction; + }; + + board.get_cell_mut(maybe_invert(Coordinates(9, 8))).unwrap().value = Some(Letter::new_fixed('G', 0)); + board.get_cell_mut(maybe_invert(Coordinates(10, 8))).unwrap().value = Some(Letter::new_fixed('G', 0)); + + let word = board.find_word_at_position(Coordinates(8, 8), maybe_invert_direction(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"); + + } + } + + let words = board.find_played_words(); + match words { + Ok(x) => { + assert_eq!(x.len(), 2); + let word = x.get(0).unwrap(); + assert_eq!(word_to_text(word), "EGG"); + + let word = x.get(1).unwrap(); + assert_eq!(word_to_text(word), "JOEL"); + } + Err(e) => { panic!("Expected to find a word to play; found error {}", e) } + } + board.get_cell_mut(maybe_invert(Coordinates(9, 8))).unwrap().value = Some(make_letter('G', true)); + + let words = board.find_played_words(); + match words { + Ok(_) => { panic!("Expected error as we played tiles in multiple rows and columns") } + Err(e) => { assert_eq!(e, "Tiles need to be played on one row or column") } + } + } + + // make a copy of the board now with x and y swapped + let mut inverted_board = Board::new(); + + for x in 0..GRID_LENGTH { + for y in 0..GRID_LENGTH { + let cell_original = board.get_cell(Coordinates(x, y)).unwrap(); + let cell_new = inverted_board.get_cell_mut(Coordinates(y, x)).unwrap(); + + match &cell_original.value { + None => {} + Some(x) => { + cell_new.value = Some(*x); + } + } + + } + } + + println!("Checking original board"); + check_board(&mut board, false); + + println!("Checking inverted board"); + check_board(&mut inverted_board, true); + + } #[test]