fix: enforce must-use-both-dice and must-use-larger-die rules#129
Merged
Conversation
A player could complete a turn using fewer dice than the rules require (e.g. roll [5,2], play only the 5 and strand the 2 even though playing the 2 first then the 5 uses both). pureMove validated against the post-move state, where the stranded die was already consumed, so the alternative- sequence search always found nothing. Compute the maximum dice any legal ordering can use from the turn-start board in Play.initialize, persist it on the play (maxDiceUsable, dieValuesPlayableAtStart), and enforce it in pureMove once no ready moves remain: throw MustUseBothDiceError when fewer dice were used than possible, and MustUseLargerDieError for the either-but-not-both case. Also: - Make the Sim engine rule-compliant (try candidates best-first, fall back on rule rejection) so simulations only play legal moves. - Fix the Sim LCG normalization: (s & 0xffffffff) is signed in JS and produced negative values, yielding invalid die values once the seeded RNG was wired into Dice for deterministic runs. - Add an optional injectable random source to Dice (default unchanged) so simulations are deterministic per seed.
The must-use-both-dice enforcement reads play.maxDiceUsable / dieValuesPlayableAtStart, added in backgammon-types 1.0.3. Lockfile regeneration is deferred until 1.0.3 is published to npm.
….0.4 Resolves the pre-existing npm ci drift (lock pinned rc.1/rc.3 against ^1.0.0 ranges) and points at the published backgammon-types 1.0.3 that carries the maxDiceUsable fields.
This was referenced Jun 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
In production game
01727ded-7a6a-4125-8729-02f44a6b60db, a player rolled[5,2]and was allowed to play only the 5 (cc7→cc2), recording the 2 as ano-move— illegal. A legal sequence using both dice existed: play the 2 first (cc7→cc5), then the 5 (cc23→cc18). The rules require using as many dice as legally possible.Root cause:
Play.pureMovevalidated against the post-move state (result.newBoard/result.newPlay), where the stranded die was already consumed. The alternative-sequence explorer only inspectsreadymoves and found none, so it always reported the single-die play as valid.Fix
Play.initializecomputes the maximum dice any legal ordering can use from the turn-start board (reusingtestSequenceDiceUsage) and persists it on the play (maxDiceUsable,dieValuesPlayableAtStart). Covers doubles.Play.pureMoveenforces it once no ready moves remain:MustUseBothDiceErrorwhen fewer dice were used than possible;MustUseLargerDieErrorfor the either-but-not-both case.Incidental fixes surfaced by the enforcement
(s & 0xffffffff)is signed in JS and produced negative values → invalid die values once the seeded RNG was wired intoDice. Fixed normalization tos / 0x100000000.Dicegained an optional injectable random source (default = cryptorandomInt, unchanged) so simulations are deterministic per seed; this makes the previously-flakysimulate-until-winreliable.Tests
must-use-both-dice-rule.test.ts: the exact production position (both directions), doubles, and larger-die — 9 tests.Depends on
nodots/backgammon-types PR #66 (adds the two
BackgammonPlayfields).