fix(engine): EvaluatePlay honors the single-word rule
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m5s
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m5s
The move preview (EvaluatePlay) validated under standard rules — it called ValidatePlay without the game's play options — so under the single-word rule it rejected a play whose only flaw was incidental invalid perpendicular cross-words, even though SubmitPlay accepts it. The UI gates the submit button on the preview, so such a play (e.g. КРАН bridging an existing Р on the test contour) could not be made. Pass g.playOpts() via ValidatePlayOpts, mirroring Play, so the preview's legality and score match submission. Robots are unaffected — they search via GenerateMovesOpts and submit via Play, both already opts-aware — and a regression test asserts that too.
This commit is contained in:
@@ -92,10 +92,12 @@ func (g *Game) SubmitExchange(tiles []string) (MoveRecord, error) {
|
||||
|
||||
// EvaluatePlay scores and validates a tentative play without committing it,
|
||||
// backing the unlimited "what would my next move score, and is it legal?" tool.
|
||||
// It infers the play's orientation from the tiles and the board exactly as
|
||||
// SubmitPlay does, then returns the decoded move (placed tiles, the words it
|
||||
// forms, its orientation and its score) or ErrIllegalPlay when the solver
|
||||
// rejects it. The board, racks, bag and turn are left untouched.
|
||||
// It infers the play's orientation from the tiles and the board and applies the
|
||||
// game's play options exactly as SubmitPlay does, so under the single-word rule
|
||||
// perpendicular cross-words are ignored: the preview's legality and score then
|
||||
// match what submitting the play would yield. It returns the decoded move (placed
|
||||
// tiles, the words it forms, its orientation and its score) or ErrIllegalPlay when
|
||||
// the solver rejects it. The board, racks, bag and turn are left untouched.
|
||||
func (g *Game) EvaluatePlay(tiles []TileRecord) (MoveRecord, error) {
|
||||
if g.over {
|
||||
return MoveRecord{}, ErrGameOver
|
||||
@@ -104,7 +106,7 @@ func (g *Game) EvaluatePlay(tiles []TileRecord) (MoveRecord, error) {
|
||||
if err != nil {
|
||||
return MoveRecord{}, err
|
||||
}
|
||||
move, err := g.solver.ValidatePlay(g.board, resolveDirection(g.board, placements), placements)
|
||||
move, err := g.solver.ValidatePlayOpts(g.board, resolveDirection(g.board, placements), placements, g.playOpts())
|
||||
if err != nil {
|
||||
return MoveRecord{}, fmt.Errorf("%w: %v", ErrIllegalPlay, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user