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,56 @@
|
||||
package scrabble
|
||||
|
||||
import (
|
||||
"scrabble-solver/board"
|
||||
"scrabble-solver/rack"
|
||||
"scrabble-solver/rules"
|
||||
)
|
||||
|
||||
// generateBoth runs an across-generator on the board (for horizontal plays) and on its
|
||||
// transpose (for vertical plays), as selected by mode, then scores and de-duplicates the
|
||||
// results. runAcross reports placements in the coordinates of the board it is given; for
|
||||
// the transpose pass they are mapped back to the real board.
|
||||
func generateBoth(b *board.Board, rs *rules.Ruleset, rk rack.Rack, mode Mode,
|
||||
runAcross func(bd *board.Board, rk rack.Rack, emit func([]Placement))) []Move {
|
||||
|
||||
rk = rk.Clone() // generation mutates the rack in place and restores it
|
||||
var moves []Move
|
||||
seen := make(map[string]struct{})
|
||||
emit := func(dir Direction, placements []Placement) {
|
||||
key := moveKey(dir, placements)
|
||||
if _, dup := seen[key]; dup {
|
||||
return
|
||||
}
|
||||
m, err := Evaluate(b, rs, dir, placements)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
moves = append(moves, m)
|
||||
}
|
||||
|
||||
if mode.Includes(Horizontal) {
|
||||
runAcross(b, rk, func(p []Placement) { emit(Horizontal, p) })
|
||||
}
|
||||
if mode.Includes(Vertical) {
|
||||
tb := b.Transpose()
|
||||
runAcross(tb, rk, func(p []Placement) {
|
||||
rp := make([]Placement, len(p))
|
||||
for i, pl := range p {
|
||||
rp[i] = Placement{Row: pl.Col, Col: pl.Row, Letter: pl.Letter, Blank: pl.Blank}
|
||||
}
|
||||
emit(Vertical, rp)
|
||||
})
|
||||
}
|
||||
return moves
|
||||
}
|
||||
|
||||
// centerFor returns the centre square in bd's coordinates. bd is either the real board
|
||||
// or its transpose; the ruleset stores the centre on the real board.
|
||||
func centerFor(bd *board.Board, rs *rules.Ruleset) (row, col int) {
|
||||
r, c := rs.Center/rs.Cols, rs.Center%rs.Cols
|
||||
if bd.Rows() == rs.Rows && bd.Cols() == rs.Cols {
|
||||
return r, c
|
||||
}
|
||||
return c, r // transposed
|
||||
}
|
||||
Reference in New Issue
Block a user