exchanging with one tile in the bag#96
Merged
Merged
Conversation
The previous PEG-1 commit declined any config where an exchange is legal against the one-tile bag, because the enumeration modeled only plays and passes. Spanish is exactly that case (its exchange_tile_limit is 1, so a lone bag tile can still be exchanged), so it was declined rather than solved. Solve it now, and narrow the decline to the few configs that genuinely cannot be solved this way. With one tile in the bag an exchange is a one-for-one swap: return a rack tile, draw the lone bag tile, and the returned tile becomes the new (still fully known) one-tile bag with the opponent to move. So the bag stays size one and the position stays fully known -- the game is finite and exactly solvable. one_in_bag_minimax_ex enumerates each distinct rack tile as the returned tile alongside the plays and the pass. A pass leaves both racks untouched, so a run of passes resolves to the same leftover point margin no matter how many -- the plain solver's two-pass shortcut. An exchange CHANGES a rack, so that shortcut is no longer valid: the game only ends once enough scoreless turns pile up. This tracks the consecutive-pass and consecutive-zero counts and ends the game by the config's num_passes_to_end / num_zeros_to_end rule (a pass advances both counts; an exchange, which counts as a zero here, resets the pass count and advances the zero count). A node's value now depends on how close that end already is, so (mover, both racks, bag tile, pass count, zero count) is memoized; the board never changes in the bag phase, so it stays out of the key. The empty-bag solver and the no-exchange path are untouched, so every English and empty-bag value is byte-identical. The decline now fires only when an exchange is legal but scoreless turns can never force an end (so an exchange chain need not terminate) -- an honest "not solvable" instead of a wrong number. This is exact GIVEN the same per-hypothesis clairvoyance the PEG enumeration already assumes: within each guessed bag tile the mover is treated as knowing it. Exchange fits that model unchanged. Tests add a Spanish-style config (English tiles, one-tile exchange, short scoreless end) and check the fast search against an independent un-memoized reference for both movers and several bag tiles; a hand-checkable position where swapping a lone Q for a bag A beats passing; and that a solvable config is answered while an unsolvable one is declined. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Document the one-in-bag exchange support: the one-for-one swap that keeps the bag at a single known tile, why passes can be collapsed to their leftover margin but exchanges cannot (so the search counts scoreless turns and ends by the config's rule), what the memo key holds, and the per-hypothesis clairvoyance the value assumes -- an upper bound on what a real player can guarantee, the same assumption every pre-endgame solver makes. Also note the one case still declined: an exchange-legal config whose scoreless turns never force an end. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
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.
Summary
when only one tile is left in the bag, some configs (Spanish among them) allow that tile to be exchanged, and the previous PEG-1 solver declined every such config because its enumeration modeled only plays and passes. this teaches the one-in-bag solver to handle the exchange. with one tile in the bag an exchange is a one-for-one swap: return a rack tile, draw the lone bag tile, and the returned tile becomes the new fully-known one-tile bag with the opponent to move -- so the bag stays size one, the position stays fully known, and the game is still finite and exactly solvable. one_in_bag_minimax_ex now enumerates each distinct rack tile as the returned tile alongside the plays and the pass, and the decline is narrowed to only the configs that genuinely cannot be solved this way. the second commit documents all of this in lab/endgame.txt.
Changes
Correctness
the empty-bag solver and the no-exchange path are untouched, so every English and empty-bag value is byte-identical. the result is exact given the same per-hypothesis clairvoyance the PEG enumeration already assumes (within each guessed bag tile the mover is treated as knowing it), an upper bound on what a real player can guarantee and the same assumption every pre-endgame solver makes. tests add a Spanish-style config (English tiles, one-tile exchange, short scoreless end) and check the fast search against an independent un-memoized reference for both movers and several bag tiles; a hand-checkable position where swapping a lone Q for a bag A beats passing; and that a solvable config is answered while an unsolvable one is declined.