Implement Scrabble move generator (DAWG) with English and Russian rules
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.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
// Package graph provides thin, reusable helpers over a dafsa Cursor that the move
|
||||
// generator builds on. It keeps the rest of the solver from depending on dafsa
|
||||
// traversal details directly.
|
||||
package graph
|
||||
|
||||
import dawg "github.com/iliadenisov/dafsa"
|
||||
|
||||
// Spell follows the given alphabet indices from the cursor's root. It returns the
|
||||
// state reached, whether that state is accepting, and whether the whole path exists.
|
||||
// When ok is false the path ran into a missing edge; n and final are meaningless.
|
||||
func Spell(c *dawg.Cursor, indices []byte) (n dawg.Node, final, ok bool) {
|
||||
n = c.Root()
|
||||
final = c.Final(n)
|
||||
for _, ix := range indices {
|
||||
n, final, ok = c.Next(n, ix)
|
||||
if !ok {
|
||||
return n, false, false
|
||||
}
|
||||
}
|
||||
return n, final, true
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package graph_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/iliadenisov/alphabet"
|
||||
dawg "github.com/iliadenisov/dafsa"
|
||||
|
||||
"scrabble-solver/internal/graph"
|
||||
)
|
||||
|
||||
// TestSpellSmoke also exercises the go.mod replace => ../dafsa wiring and the new
|
||||
// dafsa traversal API end-to-end from the solver module.
|
||||
func TestSpellSmoke(t *testing.T) {
|
||||
d := dawg.New(alphabet.Latin())
|
||||
for _, w := range []string{"cat", "cats", "dog"} {
|
||||
if err := d.Add(w); err != nil {
|
||||
t.Fatalf("Add(%q): %v", w, err)
|
||||
}
|
||||
}
|
||||
c, err := dawg.NewCursor(d.Finish())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
enc := func(s string) []byte {
|
||||
b, err := alphabet.Latin().Encode(s)
|
||||
if err != nil {
|
||||
t.Fatalf("Encode(%q): %v", s, err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
if _, final, ok := graph.Spell(c, enc("cat")); !ok || !final {
|
||||
t.Errorf("Spell(cat): ok=%v final=%v, want both true", ok, final)
|
||||
}
|
||||
if _, final, ok := graph.Spell(c, enc("ca")); !ok || final {
|
||||
t.Errorf("Spell(ca): ok=%v final=%v, want ok=true final=false", ok, final)
|
||||
}
|
||||
if _, _, ok := graph.Spell(c, enc("xyz")); ok {
|
||||
t.Errorf("Spell(xyz): ok=true, want false")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user