Files
scrabble-solver/scrabble/gen.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

57 lines
1.8 KiB
Go

package scrabble
import (
"gitea.iliadenisov.ru/developer/scrabble-solver/board"
"gitea.iliadenisov.ru/developer/scrabble-solver/rack"
"gitea.iliadenisov.ru/developer/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
}