Publish the all-in runout street by street

During an all-in runout, dealRemainingBoard() dealt every remaining card
without touching currentStreet, so a pre-flop all-in produced a terminal
snapshot labelled PREFLOP carrying a five-card board.

Setting currentStreet = RIVER would fix the label but leave a second problem:
the board jumped from empty to complete in a single snapshot, so the UI could
not animate the runout — the moment a poker table most needs to. Instead
dealRemainingBoard() is now suspend and publishes each street as it lands,
which keeps currentStreet honest as a consequence rather than as a special
case. Each runout street also sweeps its betting into the pot via
prepareRound(), matching the normal street transition.

Verified by reverting: the board went 0 -> 5 with the terminal snapshot
labelled PREFLOP, and three of the four new tests failed.

Tests: 48 -> 52, green on jvmTest and testAndroidHostTest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jay
2026-07-25 16:04:30 -04:00
parent eefcd5966c
commit 679b25e2c7
2 changed files with 90 additions and 1 deletions
@@ -305,7 +305,15 @@ class Table(
board.add(deck.deal())
}
private fun dealRemainingBoard(from: Street) {
/**
* Runs the board out when nobody can act any more.
*
* Publishes each street as it lands rather than dealing all five cards
* silently: an all-in runout is the moment a poker UI most needs to animate,
* and it also keeps [currentStreet] honest — otherwise a pre-flop all-in ends
* on a snapshot labelled PREFLOP that carries a five-card board.
*/
private suspend fun dealRemainingBoard(from: Street) {
var s = from
while (s != Street.RIVER) {
s = when (s) {
@@ -314,6 +322,10 @@ class Table(
Street.TURN -> { dealRiver(); Street.RIVER }
Street.RIVER -> Street.RIVER
}
currentStreet = s
// Sweep the completed betting into the pot before showing the card.
prepareRound(s)
emit(TableSnapshot.Phase.STREET_COMPLETE)
}
}