15c7959d96
A Go library that returns every legal play ranked by score and scores or validates plays, using the Appel-Jacobson DAWG algorithm over github.com/iliadenisov/dafsa v1.1.0. - DAWG move generation (across / down / both), full tournament scoring with a per-tile breakdown; public Solver: GenerateMoves (ranked), ScorePlay, ValidatePlay. - Rulesets: English Scrabble, Russian Scrabble, Эрудит (parameterizable Ruleset). - cmd/builddict (build the DAWG from the dictionaries submodule), cmd/stress (self-play benchmark), selfplay engine; brute-force test oracle. - A GADDAG was implemented, benchmarked and removed (the DAWG was smaller and faster for a scoring solver); see RESULTS.md and ALGORITHM.md.
93 lines
2.0 KiB
Go
93 lines
2.0 KiB
Go
package board_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/iliadenisov/alphabet"
|
|
|
|
"scrabble-solver/board"
|
|
"scrabble-solver/internal/encoding"
|
|
)
|
|
|
|
func TestParseAndAccess(t *testing.T) {
|
|
b, err := board.Parse([]string{
|
|
"cat",
|
|
"o..",
|
|
"W..", // blank standing for 'w'
|
|
}, alphabet.Latin())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if b.Rows() != 3 || b.Cols() != 3 {
|
|
t.Fatalf("size = %dx%d, want 3x3", b.Rows(), b.Cols())
|
|
}
|
|
if b.IsEmpty() {
|
|
t.Error("IsEmpty = true for a non-empty board")
|
|
}
|
|
if !b.Empty(1, 1) {
|
|
t.Error("(1,1) should be empty")
|
|
}
|
|
if !b.Filled(0, 0) {
|
|
t.Error("(0,0) should be filled")
|
|
}
|
|
|
|
// 'c' = index 2, normal tile.
|
|
if got := b.At(0, 0); got != encoding.Cell(2, false) {
|
|
t.Errorf("At(0,0) = %#x, want %#x", got, encoding.Cell(2, false))
|
|
}
|
|
if encoding.IsBlank(b.At(0, 0)) {
|
|
t.Error("(0,0) wrongly marked blank")
|
|
}
|
|
// 'W' = blank for index 22.
|
|
if got := b.At(2, 0); got != encoding.Cell(22, true) {
|
|
t.Errorf("At(2,0) = %#x, want blank w", got)
|
|
}
|
|
if !encoding.IsBlank(b.At(2, 0)) {
|
|
t.Error("(2,0) should be a blank")
|
|
}
|
|
}
|
|
|
|
func TestNewIsEmpty(t *testing.T) {
|
|
if !board.New(15, 15).IsEmpty() {
|
|
t.Error("new board not empty")
|
|
}
|
|
}
|
|
|
|
func TestTranspose(t *testing.T) {
|
|
b, _ := board.Parse([]string{
|
|
"ab",
|
|
"..",
|
|
"cd",
|
|
}, alphabet.Latin())
|
|
tr := b.Transpose()
|
|
if tr.Rows() != 2 || tr.Cols() != 3 {
|
|
t.Fatalf("transpose size = %dx%d, want 2x3", tr.Rows(), tr.Cols())
|
|
}
|
|
if tr.At(0, 0) != b.At(0, 0) || tr.At(1, 0) != b.At(0, 1) || tr.At(0, 2) != b.At(2, 0) {
|
|
t.Error("transpose did not swap coordinates")
|
|
}
|
|
|
|
// Transposing twice restores the original.
|
|
back := tr.Transpose()
|
|
for r := range b.Rows() {
|
|
for c := range b.Cols() {
|
|
if back.At(r, c) != b.At(r, c) {
|
|
t.Fatalf("double transpose differs at (%d,%d)", r, c)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestClone(t *testing.T) {
|
|
b := board.New(3, 3)
|
|
b.Set(1, 1, encoding.Cell(0, false))
|
|
cp := b.Clone()
|
|
cp.Set(0, 0, encoding.Cell(1, false))
|
|
if !b.Empty(0, 0) {
|
|
t.Error("mutating clone changed the original")
|
|
}
|
|
if cp.Empty(1, 1) {
|
|
t.Error("clone lost original content")
|
|
}
|
|
}
|