# How DD Poker Handles Betting Rounds, Pots, and Side Pots: Engine Deep Dive

> Explore the DD Poker engine's sophisticated handling of betting rounds, pots, and side pots. Learn how it dynamically manages chip contributions and recalculates side pots for fair distribution.

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

---

**The DD Poker engine maintains a dynamic list of `Pot` objects during each betting round, automatically recalculating side pots when players go all-in by sorting contributions and redistributing chips across tiered pots at round end.**

The `dougdonohoe/ddpoker` repository implements a complete Texas Hold'em engine in Java, with sophisticated logic for managing **betting rounds**, **pot accumulation**, and **side pot creation**. The system tracks every chip movement through a hierarchy of `Pot` instances that split dynamically based on player all-in statuses.

## Core Architecture: HoldemHand, Pot, and PotInfo

The engine delegates wagering state across three tightly coupled classes that separate concerns between hand orchestration, chip storage, and calculation utilities.

### The HoldemHand Orchestrator

[`HoldemHand.java`](https://github.com/dougdonohoe/ddpoker/blob/main/HoldemHand.java) serves as the central controller for every hand. It maintains a `DMArrayList<Pot> pots_` field that represents the current pot structure, where index `0` is always the main pot and subsequent indices are side pots.

Key methods include:
- `advanceRound()` – increments the round counter and signals the current pot to reset side-bet markers
- `addToPot()` – validates bets and appends chips to the active pot
- `calcPots()` – triggers the side-pot recalculation algorithm (lines 1682–1760)
- `getCurrentPot()` – returns the `Pot` object receiving new bets
- `getTotalPotChipCount()` – aggregates chips across all pots for UI display

### Pot Object Structure

[`Pot.java`](https://github.com/dougdonohoe/ddpoker/blob/main/Pot.java) encapsulates a single wagering pool, whether main or side. Each instance tracks:
- `addChips(PokerPlayer, int)` – records player contributions
- `setSideBet(int)` – defines the maximum eligible contribution for this pot tier
- `advanceRound()` – marks the pot for a new betting street
- `hasBaseAllIn()` – checks if the base pot contains an all-in player

Side pots are instantiated with a specific `sideBet` amount that limits which players can win the pool. The constant `Pot.NO_SIDE` indicates the main pot has no contribution ceiling.

### PotInfo Sorting Utility

`PotInfo` is an inner class within `HoldemHand` that acts as a transient data structure during `calcPots()`. It implements `Comparable` to sort players by their total contribution size (`bet + ante`), enabling the algorithm to construct side pots from the smallest bet upward. The `needSide()` method flags whether a player's all-in status requires pot segmentation.

## Recording Bets and Advancing Rounds

When players act, the engine immediately updates the current pot while logging history for replay functionality.

### Adding Chips to the Current Pot

The `bet()` method delegates to `addToPot()`, which creates a `HandAction` entry and increments chip counts:

```java
public void bet(PokerPlayer player, int nChips, String sDebug) {
    addToPot(player, nChips, HandAction.ACTION_BET, sDebug);
}

```

This validates the amount against the player's stack, records the action in hand history, and invokes `Pot.addChips()` on the active pot instance returned by `getCurrentPot()`.

### Round Advancement Mechanics

At street transitions (pre-flop to flop, etc.), `advanceRound()` synchronizes state:

```java
public int advanceRound() {
    nRound_++;
    getCurrentPot().advanceRound();   // resets side-bet tracking
    // ...
}

```

The call to `advanceRound()` on the `Pot` object prepares the main pot for new betting activity by clearing previous side-bet calculations and storing the starting chip count for side-pot arithmetic.

## Side Pot Calculation Algorithm

The `calcPots()` method executes at round end (or when all-ins occur) to resegment the pot structure. This ensures players cannot win more chips than they contributed.

### Building the PotInfo List

The algorithm first constructs a `PotInfo` entry for every active player, capturing their total contribution including antes:

```java
List<PotInfo> info = new ArrayList<>();
for (int i = 0; i < getNumPlayers(); i++) {
    PokerPlayer player = getPlayerAt(i);
    int nPlayerBet = getBet(player) + getAnte(player);
    info.add(new PotInfo(player, nPlayerBet));
}

```

### Sorting and Side-Pot Creation

Players are sorted by contribution size to enable bottom-up pot construction:

```java
Collections.sort(info);

```

The engine then iterates through the sorted list, creating new `Pot` instances whenever a player’s contribution exceeds the previous side-bet threshold:

```java
int nLastSideBet = 0;
synchronized (pots_) {
    Pot pot = resetMainPotForRound();   // clears old side pots
    for (int i = 0; i < info.size(); i++) {
        PotInfo potinfo = info.get(i);
        if (potinfo.needSide() && ((i == 0 && pot.hasBaseAllIn() && potinfo.nBet == 0)
            || potinfo.nBet > nLastSideBet)) {
            pot.setSideBet(potinfo.nBet - nLastSideBet);
            pot = new Pot(nRound_, i + 1);   // create side pot
            pots_.add(pot);
            nLastSideBet = potinfo.nBet;
        }
    }
}

```

### Chip Distribution Logic

Finally, the engine distributes each player's chips across the created pots according to side-bet limits:

```java
for (Pot pot2 : pots_) {
    if (pot2.getRound() != nRound_) continue;
    int nSide = pot2.getSideBet();
    
    for (PotInfo potinfo : info) {
        if (potinfo.nBet == 0) continue;
        
        if (nSide == Pot.NO_SIDE) {
            pot2.addChips(potinfo.player, potinfo.nBet);
            potinfo.nBet = 0;
        } else {
            int nBet = Math.min(nSide, potinfo.nBet);
            pot2.addChips(potinfo.player, nBet);
            potinfo.nBet -= nBet;
        }
    }
}

```

**Main pots** contain chips eligible to all players, while **side pots** restrict eligibility to those who contributed at least the side-bet amount.

## Querying Pot State and Odds

The engine exposes pot metadata for AI decision-making and UI rendering:

- `getCurrentPot()` returns the active pot receiving new wagers
- `getNumPots()` counts distinct pots including side pots
- `getPotOdds(PokerPlayer)` (lines 777–803) calculates the ratio of call cost to total pot size

```java
// Display current pot size
Pot cur = hand.getCurrentPot();
System.out.println("Round " + cur.getRound() + " chips: " + cur.getChipCount());

// Calculate pot odds for AI strategy
float odds = hand.getPotOdds(playerC);

```

## Summary

- The DD Poker engine uses a `DMArrayList<Pot>` to represent the main pot and side pots during each betting round.
- `HoldemHand.calcPots()` automatically recalculates side pots by sorting player contributions via `PotInfo` and creating new `Pot` instances for each tier.
- Side pot creation triggers when a player goes all-in for less than the current highest bet, ensuring no player can win more than they contributed.
- `advanceRound()` synchronizes betting street transitions by updating both the hand state and the current pot's side-bet tracking.
- Pot odds and chip counts are exposed through `getPotOdds()` and `getTotalPotChipCount()` for AI and dashboard consumption.

## Frequently Asked Questions

### How does the engine determine when to create a side pot?

The engine checks the `needSide()` flag on each `PotInfo` object during `calcPots()`. When a player is all-in with a contribution smaller than the current maximum bet, the algorithm creates a new `Pot` with `setSideBet()` set to the difference, isolating the excess chips for players who contributed more.

### What happens to existing side pots when a new betting round starts?

`advanceRound()` calls `resetMainPotForRound()`, which discards side pots from the previous street and resets the main pot's side-bet marker. This ensures side pots are recalculated fresh for each betting round (pre-flop, flop, turn, river) based on new all-in scenarios.

### Can the main pot ever have a side-bet limit?

No. The main pot uses the constant `Pot.NO_SIDE` to indicate it has no contribution ceiling. All players eligible for the hand can win the main pot, while side pots (created with specific side-bet amounts) restrict eligibility to subsets of players based on their contribution levels.

### Where is the pot odds calculation implemented?

Pot odds logic resides in [`HoldemHand.java`](https://github.com/dougdonohoe/ddpoker/blob/main/HoldemHand.java) between lines 777–803, accessible via `getPotOdds(PokerPlayer player)`. This method compares the player's required call amount against `getTotalPotChipCount()` to return the probability ratio used by AI agents and the dashboard UI component [`PotOdds.java`](https://github.com/dougdonohoe/ddpoker/blob/main/PotOdds.java).