From 6f14579f3a2b2d21f99ad4c479ae4221391bc25e Mon Sep 17 00:00:00 2001 From: Joel Therrien Date: Sat, 29 Jul 2023 17:31:55 -0700 Subject: [PATCH] Catch errors in word-finding; add start of scoring --- src/lib.rs | 191 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 174 insertions(+), 17 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4563ae5..4aeea9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -156,6 +156,44 @@ struct Word<'a> { coords: Coordinates, } +impl <'a> Word<'a> { + + fn calculate_score(&self) -> u32{ + let mut multiplier = 1; + let mut unmultiplied_score = 0; + + for cell in self.cells.as_slice() { + let cell_value = cell.value.unwrap(); + if cell_value.ephemeral { + let cell_multiplier = + match cell.cell_type { + CellType::Normal => {1} + CellType::DoubleWord => { + multiplier *= 2; + 1 + } + CellType::DoubleLetter => {2} + CellType::TripleLetter => {3} + CellType::TripleWord => { + multiplier *= 3; + 1 + } + CellType::Start => { + multiplier *= 2; + 1 + } + }; + unmultiplied_score += cell_value.points * cell_multiplier; + } else { + // no cell multiplier unfortunately + unmultiplied_score += cell_value.points; + } + } + + unmultiplied_score * multiplier + } +} + impl Board { pub fn new() -> Self { let mut cells = Vec::new(); @@ -243,6 +281,22 @@ impl Board { } } + /* + fn calculate_scores(&self, dictionary: &Dictionary) -> Result<(Vec<(Word, u32)>, u32), &str> { + let words = self.find_played_words()?; + let mut words_and_scores = Vec::new(); + let mut total_score = 0; + + for word in words { + let mut word_score = 0; + let mut word_multiplier = 1; + + + } + + todo!() + }*/ + fn find_played_words(&self) -> Result, &str> { // We don't assume that the move is valid, so let's first establish that @@ -290,11 +344,13 @@ impl Board { starting_coords = main_word.coords; let mut words = Vec::new(); + let mut observed_tiles_played = 0; // 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 { + observed_tiles_played += 1; let side_word = self.find_word_at_position(cell.coordinates, direction.invert()); match side_word { None => {} @@ -307,10 +363,33 @@ impl Board { } } + // there are tiles not part of the main word + if observed_tiles_played != tiles_played { + return Err("Played tiles cannot have empty gap"); + } + words.push(main_word); - Ok(words) + // need to verify that the play is 'anchored' + let mut anchored = false; + 'outer: for word in words.as_slice() { + for cell in word.cells.as_slice() { + // either one of the letters + if !cell.value.as_ref().unwrap().ephemeral || (cell.coordinates.0 == GRID_LENGTH / 2 && cell.coordinates.1 == GRID_LENGTH / 2){ + anchored = true; + break 'outer; + } + } + } + + if anchored { + Ok(words) + } else { + return Err("Played tiles must be anchored to something") + } + + } fn find_word_at_position(&self, mut start_coords: Coordinates, direction: Direction) -> Option { @@ -555,6 +634,78 @@ mod tests { + } + + #[test] + fn test_word_finding_anchor() { + let mut board = Board::new(); + + fn make_letter(x: char, ephemeral: bool) -> Letter { + Letter { + text: x, + points: 0, + ephemeral, + is_blank: false, + } + } + + board.get_cell_mut(Coordinates(8, 6)).unwrap().value = Some(make_letter('J', true)); + 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(make_letter('L', true)); + + let words = board.find_played_words(); + match words { + Ok(_) => {panic!("Expected the not-anchored error")} + Err(x) => {assert_eq!(x, "Played tiles must be anchored to something")} + } + + // Adding anchor + board.get_cell_mut(Coordinates(7, 6)).unwrap().value = Some(make_letter('I', false)); + assert!(board.find_played_words().is_ok()); + + board = Board::new(); + + // we go through center so this is anchored + board.get_cell_mut(Coordinates(7, 7)).unwrap().value = Some(make_letter('J', true)); + board.get_cell_mut(Coordinates(8, 7)).unwrap().value = Some(make_letter('O', true)); + board.get_cell_mut(Coordinates(9, 7)).unwrap().value = Some(make_letter('E', true)); + board.get_cell_mut(Coordinates(10, 7)).unwrap().value = Some(make_letter('L', true)); + + assert!(board.find_played_words().is_ok()); + } + + #[test] + fn test_word_finding_with_break() { + // Verify that if I play my tiles on one row or column but with a break in-between I get an error + + let mut board = Board::new(); + + fn make_letter(x: char, ephemeral: bool) -> Letter { + Letter { + text: x, + points: 0, + ephemeral, + is_blank: false, + } + } + + 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(8, 11)).unwrap().value = Some(make_letter('I', true)); + board.get_cell_mut(Coordinates(8, 12)).unwrap().value = Some(Letter::new_fixed('S', 0)); + + let words = board.find_played_words(); + match words { + Ok(_) => {panic!("Expected to find an error!")} + Err(x) => { + assert_eq!(x, "Played tiles cannot have empty gap") + } + } + } @@ -562,10 +713,10 @@ mod tests { fn test_word_finding_whole_board() { let mut board = Board::new(); - fn make_letter(x: char, ephemeral: bool) -> Letter { + fn make_letter(x: char, ephemeral: bool, points: u32) -> Letter { Letter { text: x, - points: 0, + points, ephemeral, is_blank: false, } @@ -577,19 +728,18 @@ mod tests { 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(8, 6)).unwrap().value = Some(Letter::new_fixed('J', 8)); + board.get_cell_mut(Coordinates(8, 7)).unwrap().value = Some(make_letter('O', true, 1)); + board.get_cell_mut(Coordinates(8, 8)).unwrap().value = Some(make_letter('E', true, 1)); + board.get_cell_mut(Coordinates(8, 9)).unwrap().value = Some(Letter::new_fixed( 'L', 1)); + board.get_cell_mut(Coordinates(0, 0)).unwrap().value = Some(Letter::new_fixed('I', 1)); + board.get_cell_mut(Coordinates(1, 0)).unwrap().value = Some(Letter::new_fixed('S', 1)); - 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(3, 0)).unwrap().value = Some(Letter::new_fixed('C', 3)); + board.get_cell_mut(Coordinates(4, 0)).unwrap().value = Some(Letter::new_fixed('O', 1)); + board.get_cell_mut(Coordinates(5, 0)).unwrap().value = Some(Letter::new_fixed('O', 1)); + board.get_cell_mut(Coordinates(6, 0)).unwrap().value = Some(Letter::new_fixed('L', 1)); fn word_to_text(word: &Word) -> String { let mut text = String::with_capacity(word.cells.len()); @@ -601,12 +751,15 @@ mod tests { } fn check_board(board: &mut Board, inverted: bool) { + println!("{}", board); 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"); + + assert_eq!(word.calculate_score(), 8 + 1 + 2 + 1); } Err(e) => { panic!("Expected to find a word to play; found error {}", e) } } @@ -625,8 +778,8 @@ mod tests { 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)); + board.get_cell_mut(maybe_invert(Coordinates(9, 8))).unwrap().value = Some(Letter::new_fixed('G', 2)); + board.get_cell_mut(maybe_invert(Coordinates(10, 8))).unwrap().value = Some(Letter::new_fixed('G', 2)); let word = board.find_word_at_position(Coordinates(8, 8), maybe_invert_direction(Direction::Row)); match word { @@ -636,6 +789,7 @@ mod tests { assert_eq!(x.coords.1, 8); assert_eq!(word_to_text(&x), "EGG"); + assert_eq!(x.calculate_score(), 2 + 2 + 2); } } @@ -646,13 +800,16 @@ mod tests { assert_eq!(x.len(), 2); let word = x.get(0).unwrap(); assert_eq!(word_to_text(word), "EGG"); + assert_eq!(word.calculate_score(), 2 + 2 + 2); let word = x.get(1).unwrap(); assert_eq!(word_to_text(word), "JOEL"); + assert_eq!(word.calculate_score(), 8 + 1 + 2 + 1); } 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)); + // replace one of the 'G' in EGG with an ephemeral to trigger an error + board.get_cell_mut(maybe_invert(Coordinates(9, 8))).unwrap().value = Some(make_letter('G', true, 2)); let words = board.find_played_words(); match words {