Skip to content

Heads-up games do not follow standard blind / action-order rules #82

@jianrontan

Description

@jianrontan

Summary

In a 2-player (heads-up) game, PyPokerEngine assigns the blinds and the
betting order the same way it does for 3+ player tables. This violates the
standard rules of heads-up poker.

Standard heads-up rules

  • The dealer button posts the small blind (the non-button player posts the
    big blind).
  • Preflop: the button/small blind acts first.
  • Postflop: the button/small blind acts last (the big blind acts first).

Current (incorrect) behavior

_steal_money_from_poor_player always picks the small blind as the seat to
the left of the button
(dealer_btn + 1):

search_targets = search_targets[table.dealer_btn+1:table.dealer_btn+1+len(players)]

For 3+ players this is correct, but for 2 players it makes the non-button
player the small blind — the opposite of the standard rule.

The action order in RoundManager is derived from the same assumption:

  • __start_street seeds the first postflop actor from sb_pos().
  • __preflop advances two seats from the small blind.

Both are correct for 3+ players but wrong heads-up, where the button must act
first preflop and last postflop.

Impact

  • Every heads-up hand posts the blinds on the wrong players.
  • Preflop and postflop betting order is inverted heads-up.
  • This logic is duplicated in two places, so both code paths are affected:
    • pypokerengine/engine/dealer.py — used by api.game.start_poker
    • pypokerengine/api/emulator.py — used by the Emulator

This is significant for anyone training agents on heads-up play (e.g. CFR),
since the game tree differs from a correct heads-up game.

Steps to reproduce

from pypokerengine.api.game import setup_config, start_poker
from examples.players.fold_man import FoldMan

config = setup_config(max_round=1, initial_stack=100, small_blind_amount=10)
config.register_player("p1", FoldMan())   # seat 0, on the button (dealer_btn = 0)
config.register_player("p2", FoldMan())   # seat 1
result = start_poker(config)
# Expected (heads-up rules): p1 is the small blind and acts first preflop.
# Actual: p2 is treated as the small blind.

Expected behavior

In heads-up play the button posts the small blind, acts first preflop, and
acts last postflop. 3+ player behavior should be unchanged.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions