F015 — Payout Calculation
| Field | Value |
|---|---|
| ID | F015 |
| Phase | 2 — Betting |
| Jira | HEDGE-136 |
| Status | TS ✅ fixed · JS ⬜ · UMA ⬜ |
Business Rule
At river, the game resolves all bets:- Winning hand only: bets on losing hands and dead hands contribute £0
- Locked-in odds:
oddsAtBetis theoddsRoundedvalue at the moment the bet was placed, not the river odds - All stages included: hole, flop, and turn bets on the winner all pay out
- Credits:
credits = (initialCredits − totalBet) + payoutat end of round
Worked Example (from Game Concept Document)
“If a hand has a 25% chance of winning this is first converted to European odds (4:1) then a house margin is subtracted of (say) 3% so odds become 3.88:1. If the player bets £1 on the hand and it wins they will receive £3.88 payout. If the player has four successive bets of same value statistically only one will win; thus in this case RTP = payout/bet = 3.88/4 = 0.97.”
| Value | |
|---|---|
| Win probability | 25% |
| Fair odds | 4.00 |
| House margin | 3% |
oddsAtBet | 3.88 |
| Stake | £1.00 (100p) |
| Payout if wins | £3.88 (388p) |
| RTP (1 in 4 wins) | 3.88 / 4 = 97% |
JavaScript Reference
JS Source:HedgeEmJavaScriptClient/odobo/src/js/hands.js
GetHandOdds(hand, stage) returns the locked-in odds for that hand at that betting stage —
non-zero only for the winning hand; 0 for losing and dead hands. This is why the JS loop over all
hands naturally produces 0 for non-winners without an explicit winner check.
GetHandBet(hand, stage) returns the bet placed on that hand at that stage (0 if no bet).
Credit deduction and award (control.js):
CC_DEDUCT_BETS (after all betting). TS deducts
per-bet immediately in placeBet(). Both produce the same net credit result.
TypeScript Implementation
File:standalone_reference_client/src/engine/GameEngine.ts
isHandWinner(hand) reads statusIsWinner from the game record at dealStatus=3 (river).
Bug fixed (HEDGE-136)
The previous implementation paid ALL bets regardless of hand outcome. Winning-hand filter was missing.Acceptance Criteria
| # | Criterion | JS | TS | UMA |
|---|---|---|---|---|
| AC1 | Bet on winning hand → credits increase by stake × oddsAtBet | ⬜ | ✅ | ⬜ |
| AC2 | Bet on losing hand only → winAmount = 0 | ⬜ | ✅ | ⬜ |
| AC3 | Bets on all hands (3-hand game) → winAmount > 0 | ⬜ | ✅ | ⬜ |
| AC4 | credits = (startCredits − totalBet) + winAmount at river | ⬜ | ✅ | ⬜ |
| AC5 | Bets on dead hand → contribute £0 to payout | ⬜ | ✅ | ⬜ |
Version Parity
| Version | Status | Notes |
|---|---|---|
| JS (reference) | ⬜ Not audited | GetHandOdds returns 0 for non-winners implicitly |
| TS | ✅ Fixed | HEDGE-136 — explicit isHandWinner guard added |
| UMA | ⬜ Not audited | Phase 5 |
Test Coverage
| Test | Location |
|---|---|
| Full cycle — winner payout | standalone_reference_client/tests/e2e/smoke.spec.ts — betting: full cycle |
| Payout feature spec | tests/features/F015-payout-calculation.spec.ts (to add) |
