scrabble: add single-word-per-turn rule via PlayOptions
Add an optional "single word per turn" rule: only the main word along the
play direction is validated and scored; perpendicular cross-words are ignored
— not formed, validated, scored, or used to constrain move generation. The
zero PlayOptions stays standard Scrabble, so existing callers are unchanged.
- PlayOptions{IgnoreCrossWords} threaded through new EvaluateOpts,
Solver.{ScorePlay,ValidatePlay,GenerateMoves}Opts and the DAWG generator
(relaxed cross-sets via fullSet, main-word-only scoring).
- connected() tests perpendicular adjacency directly instead of via
Move.Cross, so an all-new main word touching the board only sideways still
connects when cross-words are suppressed (behaviour-preserving for standard
play).
- Tests: focused corner-case suite (solver_opts_test.go) and a single-word
GCG fixture; the 17 real-game GCG fixtures stay green as the standard-rules
regression guard.
This commit is contained in:
+5
-5
@@ -10,8 +10,8 @@ import (
|
||||
// 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 {
|
||||
func generateBoth(b *board.Board, rs *rules.Ruleset, rk rack.Rack, mode Mode, opts PlayOptions,
|
||||
runAcross func(bd *board.Board, rk rack.Rack, opts PlayOptions, emit func([]Placement))) []Move {
|
||||
|
||||
rk = rk.Clone() // generation mutates the rack in place and restores it
|
||||
var moves []Move
|
||||
@@ -21,7 +21,7 @@ func generateBoth(b *board.Board, rs *rules.Ruleset, rk rack.Rack, mode Mode,
|
||||
if _, dup := seen[key]; dup {
|
||||
return
|
||||
}
|
||||
m, err := Evaluate(b, rs, dir, placements)
|
||||
m, err := EvaluateOpts(b, rs, dir, placements, opts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -30,11 +30,11 @@ func generateBoth(b *board.Board, rs *rules.Ruleset, rk rack.Rack, mode Mode,
|
||||
}
|
||||
|
||||
if mode.Includes(Horizontal) {
|
||||
runAcross(b, rk, func(p []Placement) { emit(Horizontal, p) })
|
||||
runAcross(b, rk, opts, func(p []Placement) { emit(Horizontal, p) })
|
||||
}
|
||||
if mode.Includes(Vertical) {
|
||||
tb := b.Transpose()
|
||||
runAcross(tb, rk, func(p []Placement) {
|
||||
runAcross(tb, rk, opts, 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}
|
||||
|
||||
Reference in New Issue
Block a user