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

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 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 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:

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:

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:

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:

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:

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:

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
// 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →