package scrabble import ( "testing" "github.com/iliadenisov/alphabet" "scrabble-solver/board" "scrabble-solver/internal/dictdawg" "scrabble-solver/internal/wordlist" ) func newTestSolver(t *testing.T) *Solver { t.Helper() words := wordlist.Encode(testWords, alphabet.Latin(), 2, 15) f, err := dictdawg.Build(alphabet.Latin(), words) if err != nil { t.Fatal(err) } return NewSolver(plainRulesShared, f) } func TestSolverGenerateMovesRanked(t *testing.T) { s := newTestSolver(t) b := board.New(s.rules.Rows, s.rules.Cols) moves := s.GenerateMoves(b, makeRack("cat", 0), Both) if len(moves) == 0 { t.Fatal("no first moves generated") } for i := 1; i < len(moves); i++ { if moves[i-1].Score < moves[i].Score { t.Fatalf("moves not ranked: %d before %d", moves[i-1].Score, moves[i].Score) } } } func TestSolverValidatePlay(t *testing.T) { s := newTestSolver(t) // indices: c=2 a=0 t=19 z=25 cat := []Placement{{Row: 3, Col: 2, Letter: 2}, {Row: 3, Col: 3, Letter: 0}, {Row: 3, Col: 4, Letter: 19}} // First move through the centre (3,3) is legal. if _, err := s.ValidatePlay(board.New(s.rules.Rows, s.rules.Cols), Horizontal, cat); err != nil { t.Errorf("valid first move rejected: %v", err) } // First move that misses the centre is rejected. off := []Placement{{Row: 0, Col: 0, Letter: 2}, {Row: 0, Col: 1, Letter: 0}, {Row: 0, Col: 2, Letter: 19}} if _, err := s.ValidatePlay(board.New(s.rules.Rows, s.rules.Cols), Horizontal, off); err == nil { t.Error("first move off the centre was accepted") } // A non-word ("caz") is rejected. caz := []Placement{{Row: 3, Col: 2, Letter: 2}, {Row: 3, Col: 3, Letter: 0}, {Row: 3, Col: 4, Letter: 25}} if _, err := s.ValidatePlay(board.New(s.rules.Rows, s.rules.Cols), Horizontal, caz); err == nil { t.Error("non-word 'caz' was accepted") } // A disconnected play on a non-empty board is rejected. b := board.New(s.rules.Rows, s.rules.Cols) placeWord(b, 3, 2, Horizontal, "cat") disc := []Placement{{Row: 0, Col: 0, Letter: 0}, {Row: 0, Col: 1, Letter: 18}} // "as" far away if _, err := s.ValidatePlay(b, Horizontal, disc); err == nil { t.Error("disconnected play was accepted") } // Extending "cat" to "cats" connects and is a word. cats := []Placement{{Row: 3, Col: 5, Letter: 18}} // s after cat if m, err := s.ValidatePlay(b, Horizontal, cats); err != nil { t.Errorf("valid extension rejected: %v", err) } else if string(m.Main.Letters) != string([]byte{2, 0, 19, 18}) { t.Errorf("main word = %v, want cats", m.Main.Letters) } } func TestSolverScorePlay(t *testing.T) { s := newTestSolver(t) b := board.New(s.rules.Rows, s.rules.Cols) m, err := s.ScorePlay(b, Horizontal, []Placement{ {Row: 3, Col: 2, Letter: 2}, {Row: 3, Col: 3, Letter: 0}, {Row: 3, Col: 4, Letter: 19}, }) if err != nil { t.Fatal(err) } if m.Score != 5 { // c3 a1 t1, no premiums on the plain board t.Errorf("cat score = %d, want 5", m.Score) } }