Emit synthetic SHOW actions for summary-only card reveals#107
Merged
ggratte merged 1 commit intoItalyToast:masterfrom Apr 26, 2026
Merged
Emit synthetic SHOW actions for summary-only card reveals#107ggratte merged 1 commit intoItalyToast:masterfrom
ggratte merged 1 commit intoItalyToast:masterfrom
Conversation
What
----
Add a `EmitSummaryShowdownActions(handLines, hand)` virtual hook on
`HandHistoryParserFastImpl`, called between `FinalizeHandHistory` and
`ShowdownAnalyzer.Populate`. PokerStars overrides it to append a synthetic
`HandAction(name, SHOW, Street.Showdown)` for two cases the action stream
otherwise misses:
* PokerStars uncontested-winner flash — summary "Seat X: name (button)
showed [..]" with no *** SHOW DOWN *** section.
* PPPoker showdown — *** SHOW DOWN *** contains only the collection
line; cards appear exclusively in summary "Seat X: id showed [..]".
A `HashSet` guard prevents double-emission when a real SHOW already
exists. The mucked-summary case is intentionally skipped: every
"Seat X: name mucked [..]" line is already paired with a "name: mucks
hand" inside SHOW DOWN that emits a real MUCKS via
`ParseMiscShowdownLine`.
Why
---
Commit 1fb5309 framed `ShowdownAnalyzer` as deriving `WentToShowdown`
and `Player.RevealAction` purely from the action stream. The earlier
attempt at this fix pre-set `RevealAction` on `Player` from inside
`ParsePlayers` and added an `else: keep` branch to the analyzer, which
breaks that contract and — for PPPoker — labels at-showdown reveals as
`ShownVoluntarily` because the analyzer's `WentToShowdown ?
ShownAtShowdown : ShownVoluntarily` mapping never runs.
Emitting the missing actions in the parser instead lets the analyzer
keep its single-source-of-truth model: `SHOW + WentToShowdown ⇒
ShownAtShowdown` (PPPoker), `SHOW + !WentToShowdown ⇒ ShownVoluntarily`
(uncontested flash) — both fall out automatically.
Tests
-----
Two new integration tests in `PokerStarsFastParserActionTests`:
* `PPPokerShowdownHand_RevealActionsDerivedFromSummary` — exercises
the existing PPPoker `ShowdownHand.txt` fixture; asserts both
showing players get `ShownAtShowdown` (the value the prior approach
got wrong).
* `UncontestedWinnerFlash_RevealsAsShownVoluntarily` — uses a new
minimal fixture (`ExtraHands/UncontestedWinnerFlash.txt`); asserts
the flasher gets `ShownVoluntarily` and `WentToShowdown` is false.
Full `HandHistories.Parser.UnitTests` suite: 1023 passed / 0 failed /
386 skipped on macOS, up from the post-1fb5309 baseline of 994 / 27.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
ShowdownAnalyzer's contract from Add showdown status and card-reveal action to parsed hands #105 (the action stream is the single source of truth forWentToShowdownandPlayer.RevealAction) by closing two gaps via the action stream itself instead of a side channel onPlayer.EmitSummaryShowdownActions(handLines, hand)virtual hook onHandHistoryParserFastImpl, called betweenFinalizeHandHistoryandShowdownAnalyzer.Populate. Default no-op.HandAction(name, SHOW, Street.Showdown)for two cases the action stream otherwise misses:Seat X: name (button) showed [..]with no*** SHOW DOWN ***section. Analyzer derivesShownVoluntarily(becauseWentToShowdown == false).*** SHOW DOWN ***contains only the collection line; cards appear exclusively in summarySeat X: id showed [..]. Analyzer derivesShownAtShowdown(becauseWentToShowdown == true).HashSetguard prevents double-emission when a real SHOW already exists.Seat X: name mucked [..]is already paired with aname: mucks handline inside SHOW DOWN that emits a realMUCKSviaParseMiscShowdownLine.Why not pre-set
RevealActiononPlayerdirectly?That alternative was attempted locally and discarded. It (a) breaks #105's "analyzer derives everything from the action stream" contract, (b) labels PPPoker reveals as
ShownVoluntarilybecause the analyzer'sWentToShowdown ? ShownAtShowdown : ShownVoluntarilymapping never runs whenRevealActionis already set, and (c) requires a newelse: keepbranch on the analyzer. Emitting the missing actions instead lets the analyzer keep its single-source-of-truth model and gets the PPPoker label right for free.Test plan
PPPokerShowdownHand_RevealActionsDerivedFromSummary— exercises the existingShowdownHand.txtfixture; asserts both showing players getShownAtShowdown(the value the discarded approach got wrong).UncontestedWinnerFlash_RevealsAsShownVoluntarily— uses a new minimal fixture (ExtraHands/UncontestedWinnerFlash.txt); asserts the flasher getsShownVoluntarilyandWentToShowdownisfalse.HandHistories.Parser.UnitTestssuite on macOS: 1023 passed / 0 failed / 386 skipped (skips are pre-existing platform-conditional). Up from the post-Add showdown status and card-reveal action to parsed hands #105 baseline of 994 / 27.HandHistories.Objects.UnitTests: 51 / 51 pass.🤖 Generated with Claude Code