How DD Poker Handles Showdown and Pot Distribution: A Complete Code Walkthrough

DD Poker executes showdown through a tightly coupled sequence where the Showdown class locks the UI into a "no-fold" mode, then HoldemHand.resolve() iterates over all pots to determine winners by HandInfo scores, split chips evenly, and distribute remainder chips from the dealer button outward.

DD Poker is an open-source Texas Hold'em engine written in Java by Doug Donohoe. Understanding how the codebase handles showdown and pot distribution reveals a clean architectural separation between UI presentation and core game logic, implemented across the Showdown and HoldemHand classes.

Entering the Showdown Phase

When a hand reaches its conclusion, the game engine invokes the Showdown phase located in code/poker/src/main/java/com/donohoedigital/games/poker/Showdown.java. The process() method (lines 64‑78) performs three critical safety checks before allowing resolution:

  • Stores a reference to the current TournamentDirector to maintain game state context.
  • Forces the "no-fold" key to prevent player actions during resolution.
  • Switches the input mode to a safe "quit-save" state, ensuring no further betting actions can occur while the pot is being distributed.

This initialization guarantees that once showdown begins, the hand state is immutable from the player's perspective.

Displaying the Showdown Board

After locking the input state, Showdown.displayShowdown() (lines 99‑115) constructs the visual representation of the hand outcome. This method iterates over every seat at the table to render card visibility based on cheat options such as OPTION_CHEAT_RABBITHUNT (show river) or OPTION_CHEAT_SHOWWINNINGHAND. It also posts relevant chat messages to the game log to document the showdown progression.

Resolving Pots and Determining Winners

The core resolution logic resides in code/poker/src/main/java/com/donohoedigital/games/poker/HoldemHand.java. The resolve() method (lines 42‑53) serves as the entry point for all pot distribution:

// Assume `hand` is a populated HoldemHand instance that has completed betting rounds.
hand.setAllInShowdown(true);   // force an all‑in showdown if desired
hand.resolve();                // runs the full pot‑distribution logic

This method iterates over all pots for the hand—including the main pot and any side pots created by all-in situations—and invokes resolvePot() for each one.

Winner Detection and Hand Scoring

Inside resolvePot() (lines 19‑30), the engine performs a multi-pass analysis:

  1. Collect Hand Scores – For each player still eligible for the pot, the method reads the HandInfo score. The highest score becomes nHighScore.
  2. Expose Cards – If a player's score matches nHighScore, the card-exposure flag is set (unless the hand is uncontested and the player has not opted to show). During all-in showdowns, cards are exposed according to standard visibility rules.
  3. Build Winner List – A second pass constructs an ordered List<PokerPlayer> of winners based on button position, preserving the natural showdown order starting from the small blind.
  4. Store Results – The pot object persists the winner list via pot.setWinners(winners) (lines 65‑68).

Inspecting Winners Programmatically

After resolution, you can inspect the results through the pot objects:

int potIndex = 0;                       // main pot
Pot pot = hand.getPot(potIndex);
List<PokerPlayer> winners = pot.getWinners();  // populated by resolvePot()
for (PokerPlayer p : winners) {
    System.out.println(p.getName() + " won " + p.getPendingWin() + " chips");
}

Chip Distribution and the Odd-Chip Rule

Once winners are identified, resolvePot() calculates chip distribution in two phases.

Calculating Equal Shares

The pot's total chip count (nPot) is divided evenly among all winners. The per-player share is rounded down to the table's minimum chip denomination (nMinChip) as shown in lines 68‑76. Any remainder that is not a multiple of the minimum chip is logged as a warning for audit purposes.

Distributing Remainder Chips

Following standard Texas Hold'em rules, remainder chips are handed out one-by-one to the winners closest to the dealer button (lines 88‑95). This ensures the "odd-chip" is awarded to the earliest eligible position. During this phase, winners receive a temporary pending win amount recorded in their player state, which is later converted into a concrete win record.

Awarding Chips and Final Verification

The final distribution loop (lines 98‑110) walks all players in showdown order:

  • For winners: Calls player.wins(amount, potNumber) for standard pots, or player.overbet(...) for over-bet scenarios.
  • For non-winners: Calls player.lose(potNumber) to record their participation in the lost pot.
  • Cleanup: Clears the pending win field after crediting the player.

Sanity Checking Pot Integrity

After all winners have been paid, the method executes a verification step (lines 124‑132) comparing the total chips awarded (nTotalCheck) against the original pot size. A warning is logged if any discrepancy exists, preventing chip inflation or loss due to rounding errors.

Housekeeping and Event Firing

Following successful distribution, resolve() (lines 53‑71) performs end-of-hand housekeeping:

  • Records comprehensive hand statistics for tracking and analysis.
  • Fires an END_HAND table event via PokerTableEvent, signaling UI components to refresh.
  • Persists the hand history to the database (unless running in pure-computer simulation mode).

Configuring Cheat Options for Testing

You can override default visibility settings to force the river to display during showdown:

PokerContext ctx = game.getContext();
PokerUtils.setCheatOption(ctx, PokerConstants.OPTION_CHEAT_RABBITHUNT, true);

This option is referenced in Showdown.displayShowdown() (lines 9‑11) to determine whether to reveal community cards early.

Summary

  • Showdown initialization locks the UI via Showdown.process() to prevent player interference during resolution.
  • Winner determination occurs in HoldemHand.resolvePot(), which compares HandInfo scores and handles card exposure for both contested and uncontested pots.
  • Chip calculation divides pots evenly among winners, rounding to the table's minimum chip value (nMinChip).
  • Odd-chip distribution awards remainders starting from the dealer button position outward.
  • Integrity verification ensures the total chips awarded exactly match the pot size before firing the END_HAND event.

Frequently Asked Questions

How does DD Poker handle multiple side pots during showdown?

The HoldemHand.resolve() method iterates over every pot object associated with the hand (main pot plus any side pots created by all-in players) and calls resolvePot() individually for each. Each pot maintains its own independent winner list, allowing complex all-in scenarios to resolve correctly according to the HandInfo scores eligible for each specific pot.

What happens when pot chips cannot be divided evenly among winners?

The engine first calculates an equal share rounded down to the table's minimum chip denomination (nMinChip). Any remaining odd chips are then distributed one at a time to winners in position order starting from the dealer button, as implemented in HoldemHand.java lines 88‑95. This adheres to standard Texas Hold'em odd-chip rules.

How can developers test showdown logic without running a full hand simulation?

You can force a hand into showdown mode programmatically by calling setAllInShowdown(true) on a populated HoldemHand instance, followed by resolve(). This bypasses normal betting rounds and immediately executes the pot distribution logic, useful for unit testing winner detection and chip calculation algorithms.

Where is the showdown winner data stored after resolution?

After resolvePot() executes, the winner list is stored directly on the Pot object via pot.setWinners(winners). You can retrieve this data after resolution by calling pot.getWinners(), which returns an ordered list of PokerPlayer objects representing the winning hands for that specific pot.

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 →