Files
scrabble-solver/scrabble/crossset_test.go
T
Ilia Denisov 256999b42c Publish as versioned Gitea module; move dictionary pipeline out
- Rename module to gitea.iliadenisov.ru/developer/scrabble-solver so it can be
  consumed as a versioned dependency (no go.work replace / CI clone).
- De-internalize wordlist and dictdawg as public packages.
- Remove cmd/builddict, dictprep/, the dictionaries submodule and the dawg
  Makefile: the word-list parsing and DAWG build now live in the separate
  scrabble-dictionary repository, which publishes the DAWG set as a release artifact.
- internal/dict loads the committed dawg/en_sowpods.dawg fixture for cmd/stress.
- Update README/CLAUDE docs accordingly.
2026-06-04 19:11:46 +02:00

76 lines
2.0 KiB
Go

package scrabble
import (
"testing"
"github.com/iliadenisov/alphabet"
dawg "github.com/iliadenisov/dafsa"
"gitea.iliadenisov.ru/developer/scrabble-solver/dictdawg"
"gitea.iliadenisov.ru/developer/scrabble-solver/wordlist"
)
func bruteCrossSet(words [][]byte, above, below []byte, size int) letterSet {
set := make(map[string]bool, len(words))
for _, w := range words {
set[string(w)] = true
}
var out letterSet
for x := range size {
w := make([]byte, 0, len(above)+1+len(below))
w = append(w, above...)
w = append(w, byte(x))
w = append(w, below...)
if set[string(w)] {
out |= letterSet(1) << uint(x)
}
}
return out
}
func TestDAWGCrossSetMatchesBruteForce(t *testing.T) {
const size = 26
words := wordlist.Encode(
[]string{"cat", "cot", "cut", "cap", "cab", "at", "it"},
alphabet.Latin(), 2, 15)
finder, err := dictdawg.Build(alphabet.Latin(), words)
if err != nil {
t.Fatal(err)
}
cur, err := dawg.NewCursor(finder)
if err != nil {
t.Fatal(err)
}
cases := []struct {
name string
above, below []byte
}{
{"c_t", []byte{2}, []byte{19}}, // expect {a,o,u}
{"_t", nil, []byte{19}}, // expect {a,i}
{"c_", []byte{2}, nil}, // expect {} (no two-letter c-words)
{"a_t", []byte{0}, []byte{19}}, // expect {}
}
for _, tc := range cases {
want := bruteCrossSet(words, tc.above, tc.below, size)
if got := dawgCrossSet(cur, tc.above, tc.below, size); got != want {
t.Errorf("%s: dawgCrossSet = %026b, want %026b", tc.name, got, want)
}
}
// c_t must be exactly {a(0), o(14), u(20)}.
want := letterSet(0)
for _, x := range []byte{0, 14, 20} {
want |= letterSet(1) << x
}
if got := dawgCrossSet(cur, []byte{2}, []byte{19}, size); got != want {
t.Errorf("c_t cross-set = %026b, want {a,o,u} = %026b", got, want)
}
// No perpendicular neighbours: every letter is allowed.
if got := dawgCrossSet(cur, nil, nil, size); got != fullSet(size) {
t.Errorf("empty context = %026b, want full", got)
}
}