# How DD Poker Manages Player Actions During Each Betting Round: A Deep Dive into TournamentDirector

> Discover how DD Poker manages player actions in betting rounds via TournamentDirector, processing human, AI, and remote decisions using the immutable HandAction object.

- Repository: [Doug Donohoe/ddpoker](https://github.com/dougdonohoe/ddpoker)
- Tags: deep-dive
- Published: 2026-02-28

---

**During each betting round, DD Poker sequentially processes seated players through `TournamentDirector.doBetting`, routing human decisions to the UI, AI decisions to probabilistic logic, and remote actions over the network via the immutable `HandAction` object.**

Managing player actions during each betting round is the core responsibility of the DD Poker game engine. The open-source repository `dougdonohoe/ddpoker` implements a deterministic state machine that handles local humans, AI opponents, and remote network players through a unified interface. Understanding how the game manages player actions during each betting round reveals the orchestration between the tournament director, hand state, and action representation objects.

## The Betting Round Orchestrator in TournamentDirector

The entire betting flow originates in `TournamentDirector.doBetting()`, located in [`code/poker/src/main/java/com/donohoedigital/games/poker/online/TournamentDirector.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/poker/src/main/java/com/donohoedigital/games/poker/online/TournamentDirector.java). This method implements a sequential processor that examines each player, determines their eligibility to act, and routes the decision to the appropriate handler.

### Selecting the Current Player

The engine identifies whose turn it is by invoking `HoldemHand.getCurrentPlayerInitIndex()`. This method initializes the player order for the first betting round and returns the index of the current actor. According to the source code in lines 45-52 of [`TournamentDirector.java`](https://github.com/dougdonohoe/ddpoker/blob/main/TournamentDirector.java), this call establishes the rotation sequence that persists throughout the hand.

```java
// From TournamentDirector.doBetting()
PokerPlayer cur = table.getHoldemHand().getCurrentPlayerInitIndex();

```

### Handling Sit-Out and Demo Players

Before presenting action options, the engine checks if the player is marked as sitting out or if a demo user has exhausted their allocated time. When either condition is true, the system automatically generates a `HandAction` with `ACTION_FOLD` and processes it via `doHandAction()`. This automated folding occurs in lines 59-66 of [`TournamentDirector.java`](https://github.com/dougdonohoe/ddpoker/blob/main/TournamentDirector.java), ensuring inactive players do not stall the game.

```java
if (cur.isSittingOut() || demoExpired) {
    HandAction fold = new HandAction(cur, table.getHoldemHand().getRound(),
                                     HandAction.ACTION_FOLD, 0,
                                     HandAction.FOLD_SITTING_OUT, "sittingout");
    doHandAction(fold, false, false, false);
}

```

## Processing Different Player Types

The engine distinguishes between three player categories during action resolution: local humans, AI-controlled opponents, and remote network participants.

### Local Human Players and UI Integration

When the current player is locally controlled and the table represents the active UI window, the engine pauses execution and transfers control to the user interface. This is achieved by setting the next phase to `"TD.Bet"` via `ret.setPhaseToRun("TD.Bet")` (lines 71-80). The tournament director remains in this state until the human player clicks a button in the client interface, at which point the resulting `HandAction` is injected back into the processing loop.

### AI Decision Making with PlayerAction

For computer-controlled players on non-current tables, the engine immediately queries the AI for a decision using `PokerPlayer.getAction(false)`. This method returns a `HandAction` object populated by the lightweight `PlayerAction` class, which generates probabilistic decisions based on supplied percentage weights.

The `PlayerAction` constructor (located in [`code/ddpoker/src/main/java/com/ddpoker/holdem/PlayerAction.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/ddpoker/src/main/java/com/ddpoker/holdem/PlayerAction.java), lines 54-78) implements a random roll mechanism that selects actions based on cumulative probability buckets for fold, check, call, and bet.

```java
// Creating a probabilistic AI action
// 30% chance to bet, 20% to call, 30% to check, 20% to fold
PlayerAction ai = PlayerAction.act(20, 30, 20, 30);
System.out.println("AI selected: " + ai);   // e.g. "Bet (bet 30%):"

```

The resulting action is immediately applied through `doHandAction()` without UI intervention, allowing rapid simulation of hands across multiple computer-only tables.

### Remote Players in Network Games

When the host runs a networked game, remote opponents trigger a different pathway. The host adds the player to a wait list using `table.addWait(cur)` and sets `ret.setRunOnClient(true)` to instruct the client to present its own UI (lines 99-106). The host then pauses execution until the remote client transmits a `HandAction` back to the server, which is processed identically to local actions.

## Representing Actions with the HandAction Class

Every player decision is encapsulated in an immutable **`HandAction`** object defined in [`code/poker/src/main/java/com/donohoedigital/games/poker/HandAction.java`](https://github.com/dougdonohoe/ddpoker/blob/main/code/poker/src/main/java/com/donohoedigital/games/poker/HandAction.java) (lines 55-66). This data structure records:

- The **player** reference
- The **round** identifier (preflop, flop, turn, river)
- The **action type** (`ACTION_FOLD`, `ACTION_CHECK`, `ACTION_CALL`, `ACTION_BET`, `ACTION_RAISE`)
- The **amount** and **sub-amount** (distinguishing between total bet size and call portion of a raise)
- An optional debug string for tracing

```java
// Manual construction of a HandAction for a human bet
PokerPlayer human = player;                  // player that clicked "Bet $50"
int round = HoldemHand.ROUND_FLOP;
int amount = 50;
HandAction bet = new HandAction(human, round,
                               HandAction.ACTION_BET,
                               amount, 0,
                               "user pressed Bet");
doHandAction(bet, false, false, false);

```

## State Transitions and Round Advancement

After processing the current player's action, `doBetting()` evaluates whether to continue the current round or transition to the next phase. The method may invoke `doBettingAllComputer()` to synchronize tables where all participants are AI-controlled, then determines the next table state via `nextBettingState()` (lines 110-127). Valid transitions include:

- **`BETTING`** – Continue the current betting round with the next player
- **`COMMUNITY`** – Deal community cards (flop, turn, or river)
- **`PRE_SHOWDOWN`** – Prepare for card revelation if only one player remains

The `HoldemHand` class maintains the internal state machine, automatically advancing the player index after each action until the hand reaches completion.

## Summary

- **`TournamentDirector.doBetting()`** serves as the central coordinator for managing player actions during each betting round, iterating through seated players sequentially.
- **Player selection** begins with `HoldemHand.getCurrentPlayerInitIndex()`, which establishes and maintains the betting order throughout the hand.
- **Inactive players** are handled automatically through forced fold actions when sitting out or when demo time expires.
- **Local humans** trigger UI phases (`"TD.Bet"`), while **AI players** receive immediate probabilistic decisions from `PlayerAction`, and **remote players** enter a wait state pending network transmission.
- All actions are represented by immutable **`HandAction`** objects that capture player, round, action type, amounts, and debugging metadata.

## Frequently Asked Questions

### What class manages the betting round flow in DD Poker?

The **`TournamentDirector`** class in [`TournamentDirector.java`](https://github.com/dougdonohoe/ddpoker/blob/main/TournamentDirector.java) manages the entire betting round flow. Its `doBetting()` method implements the main loop that selects players, determines available actions, and routes decisions to the appropriate handlers for humans, AI, or remote participants.

### How does DD Poker handle players who are sitting out?

When `isSittingOut()` returns true or a demo player's time expires, the engine automatically constructs a `HandAction` with `ACTION_FOLD` and the `FOLD_SITTING_OUT` sub-type. This action is immediately processed through `doHandAction()`, removing the player from the current hand without requiring UI interaction.

### What determines whether the UI or AI controls a player's action?

The system checks `PokerPlayer.isLocallyControlled()` and `PokerTable.isCurrent()`. If both conditions are true for a human player, the phase is set to `"TD.Bet"` to trigger the UI. If the player is a computer on a non-current table, `getAction(false)` invokes the AI logic immediately, bypassing the interface entirely.

### How are player actions represented internally in the codebase?

Player actions are represented by the **`HandAction`** class, an immutable data structure defined in [`HandAction.java`](https://github.com/dougdonohoe/ddpoker/blob/main/HandAction.java). It encapsulates the player reference, betting round, action type constants (`ACTION_FOLD`, `ACTION_BET`, etc.), monetary amounts, and optional debug strings, providing a serializable record of every decision made during the hand.