Skip to main content

Business Rule

A game of Texas HedgeEm progresses through five stages in a fixed sequence. The player cannot skip or reverse stages.
  1. Pre-deal — The table is idle. Only the Deal button is visible. No cards are shown.
  2. Hole (PRE-FLOP) — Two hole cards are dealt to each hand. All hole cards are face-up. Five community card positions are visible as face-down card backs. Betting is open on all live hands.
  3. Flop (POST-FLOP) — Three community cards are turned face-up. Betting remains open.
  4. Turn — A fourth community card is turned face-up. Betting remains open.
  5. River — The fifth and final community card is turned face-up. Betting closes. All bets are resolved and winnings paid out. The Deal button reappears for the next game.
The player advances from Hole → Flop → Turn → River by clicking the Advance button once per stage.

Stage naming (design document vs implementation)

The game concept document uses these stage names:
Design doc nameHedgeEm dealStatusCards visible at betting open
ANTE-BETPre-deal (−1)None — not yet implemented
PRE-FLOPHole (0)Hole cards only
POST-FLOPFlop (1)Hole + 3 community
TURNTurn (2)Hole + 4 community
(no betting)River (3)All 5 community
The design doc states the default configuration is POST-FLOP only (single betting stage). The reference implementations use all three betting stages (PRE-FLOP / POST-FLOP / TURN) — this is an operator configuration choice. See F014 for multi-stage betting and F032 for operator configuration.

Technical Design

State representation

StagedealStatusGameStateEnum
Pre-deal-1
Hole0STATUS_HOLE = 6
Flop1STATUS_FLOP = 10
Turn2STATUS_TURN = 11
River3STATUS_RIVER = 12
GameStateEnum values match the original C# server enum_game_state — they are stored in HandStageInfo.enumGameState in every game record.

State transitions (TypeScript — GameEngine)

advance(): boolean {
  if (this.dealStatus >= 3) return false; // already at river
  this.dealStatus++;
  return true;
}

get isGameOver(): boolean {
  return this.dealStatus >= 3;
}
advance() returns false at river so the caller knows not to update UI.

Deal flow (GameScene._onDeal)

click Deal button
  → engine.loadLocalGame() or engine.loadApiGame()
  → engine.advance()       // dealStatus: -1 → 0 (hole)
  → _render()
  → advanceBtn.setVisible(true)
  → dealBtn.setVisible(false)

Advance flow (GameScene._onAdvance)

click Advance button
  → engine.advance()       // dealStatus: N → N+1
  → _render()
  → if engine.isGameOver:
      → engine.resolveBets()
      → advanceBtn.setVisible(false)
      → dealBtn.setVisible(true)
      → _render()           // re-render to show WIN / final state

Community card reveal schedule

const communityReveal = [0, 3, 1, 1]; // cards revealed per stage: pre→hole→flop→turn→river
let revealed = 0;
for (let s = 0; s <= dealStatus; s++) revealed += communityReveal[s];
// dealStatus 0 (hole):  revealed = 0  — all face-down
// dealStatus 1 (flop):  revealed = 3  — flop 1,2,3 face-up
// dealStatus 2 (turn):  revealed = 4  — + turn face-up
// dealStatus 3 (river): revealed = 5  — all face-up

Hole card reveal rule

// HEDGE-135: all hole cards face-up once dealt
const faceUp = true;
All hole cards for all hands are face-up at the hole stage (and remain so). This matches the JS reference client which reveals all cards via CC_DEAL_CARDx states before opening the betting window. Note: an earlier version of this doc stated only hand 0 was revealed at hole. That was incorrect and was fixed by HEDGE-135.

HandStageInfo indexing

GameRecord.handStageInfoList is a flat array of n × 4 entries (n = number of hands, 4 stages). The entry for a given (stage, hand) is at index dealStatus * n + handIndex.
private _infoAt(hand: number): HandStageInfo | undefined {
  if (this.dealStatus < 0) return undefined;
  const n = this.record.numberOfHands;
  return this.record.handStageInfoList[this.dealStatus * n + hand];
}

Acceptance Criteria

  • On page load, only the Deal button is visible; no cards or hand panels are shown
  • Clicking Deal transitions to hole stage: cards appear, Advance button visible, Deal button hidden
  • At hole stage: hand 0 hole cards are face-up; hands 1–3 hole cards are face-down card backs
  • At hole stage: all 5 community card slots show face-down card backs
  • Clicking Advance at hole → flop: 3 community cards revealed face-up; all hole cards now face-up
  • Clicking Advance at flop → turn: 4th community card revealed; 5th still face-down
  • Clicking Advance at turn → river: all 5 community cards face-up; Deal button reappears
  • advance() returns false at river and does not increment dealStatus beyond 3
  • isGameOver is true only at dealStatus === 3
  • resolveBets() is called exactly once, after the river advance

Version Parity

VersionStatusNotes
JavaScript⬜ Not yet auditedReference implementation at hedgeem.qeetoto.com
TypeScript✅ ImplementedGameEngine.advance(), GameScene._onDeal(), GameScene._onAdvance()
UMA⬜ Not yet startedGDK-wrapped, same stage logic expected

Known Discrepancies

None documented yet.

Test Coverage

Playwright spec: tests/features/F004-game-stages.spec.ts — not yet created. Existing partial coverage in standalone_reference_client/tests/e2e/smoke.spec.ts:
  • screenshot: full game cycle — deal → flop → turn → river — covers stage transitions visually
  • betting: full cycle — asserts dealStatus === 3 and gameOver === true at river
Unit test coverage in standalone_reference_client/tests/unit/GameEngine.test.ts:
  • advances to hole (0) on first advance()
  • advances through all four stages
  • returns false from advance() when already at river
  • isGameOver is true at river