Files
scrabble-game/backend/internal/engine/decode_test.go
T
Ilia Denisov ec435c0e7f
Tests · Go / test (push) Successful in 8s
Tests · Integration / integration (push) Successful in 11s
Tests · Go / test (pull_request) Successful in 8s
Tests · Integration / integration (pull_request) Successful in 11s
Stage 14: solver & dictionary split — consume published module + DAWG artifact (TODO-1/TODO-2)
- backend/go.mod pins gitea.iliadenisov.ru/developer/scrabble-solver v1.0.0; the engine's
  imports use the published module path; go.work drops the solver replace (GOPRIVATE fetches
  it directly from Gitea). The solver's wordlist/dictdawg are now public packages.
- CI (go-unit, integration): drop the solver sibling-clone, set GOPRIVATE, and download the
  dictionary DAWG release artifact (scrabble-dawg-<DICT_VERSION>.tar.gz from the new
  scrabble-dictionary repo) for BACKEND_DICT_DIR.
- Docs: ARCHITECTURE §5/§11/§13/§14 + backend/README updated to the published-module +
  release-artifact model. PLAN.md re-scoped Stage 14 to the split and added Stages 15 (deploy
  infra & test contour), 16 (prod contour), 17 (dual Telegram bots); TODO-1/TODO-2 marked done.
2026-06-04 20:00:36 +02:00

69 lines
1.9 KiB
Go

package engine
import (
"testing"
"gitea.iliadenisov.ru/developer/scrabble-solver/scrabble"
)
// blankCellFlag is the bit board cells set for a blank tile (board.go encoding).
const blankCellFlag byte = 0x80
// TestDecodeBlankPlayAndReplay places "cat" with the C drawn from a blank, then
// checks the decoded record keeps the concrete letter and the blank flag, and
// that ReplayBoard — using only the ruleset, no dictionary — reproduces the
// blank on the board.
func TestDecodeBlankPlayAndReplay(t *testing.T) {
g := newEnglishGame(t, 1)
rs := g.rules
row, col := centre(rs)
idx := func(s string) byte {
t.Helper()
i, err := rs.Alphabet.Index(s)
if err != nil {
t.Fatalf("index %q: %v", s, err)
}
return i
}
ps := []scrabble.Placement{
{Row: row, Col: col, Letter: idx("c"), Blank: true},
{Row: row, Col: col + 1, Letter: idx("a")},
{Row: row, Col: col + 2, Letter: idx("t")},
}
move, err := g.solver.ValidatePlay(g.board, scrabble.Horizontal, ps)
if err != nil {
t.Fatalf("validate: %v", err)
}
rec := g.recordPlay(0, move)
if rec.Action != ActionPlay || len(rec.Tiles) != 3 {
t.Fatalf("record = %+v, want a 3-tile play", rec)
}
if blank := rec.Tiles[0]; blank.Letter != "c" || !blank.Blank {
t.Errorf("blank tile = %+v, want letter \"c\" with Blank=true", blank)
}
if rec.Tiles[1].Blank || rec.Tiles[1].Letter != "a" {
t.Errorf("second tile = %+v, want plain \"a\"", rec.Tiles[1])
}
if len(rec.Words) == 0 || rec.Words[0] != "cat" {
t.Errorf("words = %v, want main word \"cat\"", rec.Words)
}
rs2, err := Ruleset(VariantEnglish)
if err != nil {
t.Fatalf("ruleset: %v", err)
}
b, err := ReplayBoard(rs2, []MoveRecord{rec})
if err != nil {
t.Fatalf("replay: %v", err)
}
if b.At(row, col)&blankCellFlag == 0 {
t.Error("replayed centre cell lost its blank flag")
}
if !b.Filled(row, col+1) || b.At(row, col+1)&blankCellFlag != 0 {
t.Error("replayed \"a\" cell should be a filled, non-blank tile")
}
}