F011 — Bet Placement
| Field | Value |
|---|---|
| ID | F011 |
| Phase | 2 — Betting |
| Jira | HEDGE-133 |
| Status | TS ✅ fixed · JS ✅ verified · UMA ⬜ |
Business Rule
Players may place one or more chip-value bets on any live hand during each of the three betting windows (hole, flop, turn). Bets are accumulated per hand per stage. A hand is live if its current odds are> 1. Once a hand dies (odds = 0, not a winner) it cannot be bet
on. No bet may cause a hand’s cumulative stake to exceed £50.00 (5000 pence).
The payout at resolution is:
JavaScript Reference
JS Source:HedgeEmJavaScriptClient/odobo/src/js/
| Function | File | Responsibility |
|---|---|---|
BetOnHand() | hands.js:1210 | Click handler — routes to AddBetToHand(handIndex) |
AddBetToHand(hand) | control.js:180 | Guards + accumulates handBetValue[hand][dealStatus] |
CancelButtonPressed() | buttons.js:881 | Calls ClearRoundBets() — clears current-stage bets for all hands |
ClearRoundBets() | control.js:143 | Zeros handBetValue[hand][GetDealStatus()] for all hands |
ResolveHands() | hands.js:957 | Payout = Σ handOdds[h][s] × handBetValue[h][s] |
SetHandOdds(hand) | control.js:99 | Snapshots GetDataHandOdds(hand) into handOdds[hand][dealStatus] |
CC_DEDUCT_BETS | control.js:758 | Credits deducted in one batch after all betting windows close |
CC_ADD_WIN | control.js:772 | Win amount added to credits after resolution |
Key JS data structures
JS betting gate
JS payout formula
JS credit deduction timing
Credits are not deducted when a bet is placed. They are deducted in one batch atCC_DEDUCT_BETS (after the turn betting window closes, immediately before river reveal):
JS cancel
A single global CANCEL button callsClearRoundBets(), which zeros bets for the
current deal stage across all hands. It does not refund credits (because credits
haven’t been deducted yet).
TypeScript Implementation
File:standalone_reference_client/src/engine/GameEngine.ts
Fixes applied (F011 audit)
| Bug | JS behaviour | TS before fix | Fix applied |
|---|---|---|---|
stage field hardcoded to 0 | handBetValue[hand][dealStatus] | stage: 0 always | stage: this.dealStatus |
| No max bet cap | CC_MAX_BET = 5000 per hand | unlimited | MAX_BET_PER_HAND = 5000 constant + guard |
Payout checks isHandWinner | pays all bets at locked odds | only winner paid | removed winner check, pays all bets |
Design differences (intentional)
| Behaviour | JS | TS | Notes |
|---|---|---|---|
| Credit deduction timing | Deferred to CC_DEDUCT_BETS (batch, after turn) | Immediate on placeBet() | TS provides instant balance feedback |
| Cancel scope | Global — current stage, all hands | Per-hand — all stages for that hand | TS per-hand cancel is better UX |
TS placeBet() after fix
TS resolveBets() after fix
Dead Hand Logic
A hand is considered dead whenoddsRounded === 0 AND statusIsWinner === false.
| Check | JS | TS |
|---|---|---|
| Betting gate | GetDataHandOdds(hand) > 1 | isHandDead(handIndex) (odds=0 && !winner) |
| Payout contribution | handOdds[h][s] × handBetValue[h][s] — zero if hand was dead when stage opened | bet.oddsAtBet × bet.stakeAmount — zero because dead hands can’t be bet on |
oddsAtBet will always be > 1 for any valid bet.
Maths
Payout identity
Max bet capacity
House edge
The odds presented at each stage are computed from simulation data (percentWinOrDraw).
The payout odds (oddsRounded) are set slightly below the inverse of true probability,
providing the house margin. Example from coredata game 1, hand 2 at hole:
percentWinOrDraw = 0.3311→ true odds = 3.02oddsRounded = 3→ payout odds = 3.00- House margin ≈ 0.7%
Acceptance Criteria
| # | Criterion | JS | TS | UMA |
|---|---|---|---|---|
| AC1 | Bet placed on live hand deducts stake from credits display | N/A (deferred) | ✅ | ⬜ |
| AC2 | Dead hand cannot be bet on | ✅ | ✅ | ⬜ |
| AC3 | Chip value selection drives stake amount | ✅ | ✅ | ⬜ |
| AC4 | Cumulative bet per hand capped at 5000p | ✅ | ✅ | ⬜ |
| AC5 | Bets persisted per stage (hole/flop/turn) | ✅ | ✅ | ⬜ |
| AC6 | Payout = Σ(stakeAtStage × oddsAtStage) for all stages | ✅ | ✅ | ⬜ |
| AC7 | Cancel refunds stake; credit balance restored | ✅ | ✅ | ⬜ |
| AC8 | Playwright test covers: bet → advance → river → payout | ✅ | ✅ | ⬜ |
Version Parity
| Version | Status | Notes |
|---|---|---|
| JS (reference) | ✅ Verified | Live at hedgeem.qeetoto.com; source in HedgeEmJavaScriptClient |
| TS | ✅ Fixed | 3 bugs fixed this session (stage field, max bet cap, payout formula) |
| UMA | ⬜ Not audited | Phase 5 |
Test Coverage
| Test | Location | Versions |
|---|---|---|
| Bet deducts credits / cancel refunds | standalone_reference_client/tests/e2e/smoke.spec.ts:164 | TS |
| Full cycle: bet → river → payout | standalone_reference_client/tests/e2e/smoke.spec.ts:206 | TS |
| Cross-version F011 spec | tests/features/F011-bet-placement.spec.ts | TS, JS (pending) |
