Skip to content

Commit 57960df

Browse files
committed
Add Texas Hold'em arena with classic and short-deck variants
1 parent beb66dd commit 57960df

6 files changed

Lines changed: 226 additions & 0 deletions

File tree

codeclash/arenas/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from codeclash.arenas.huskybench.huskybench import HuskyBenchArena
1010
from codeclash.arenas.robocode.robocode import RoboCodeArena
1111
from codeclash.arenas.robotrumble.robotrumble import RobotRumbleArena
12+
from codeclash.arenas.texasholdem.texasholdem import ShortDeckHoldemArena, TexasHoldemArena
1213

1314
ARENAS = [
1415
BattleCodeArena,
@@ -21,6 +22,8 @@
2122
HuskyBenchArena,
2223
RoboCodeArena,
2324
RobotRumbleArena,
25+
ShortDeckHoldemArena,
26+
TexasHoldemArena,
2427
]
2528

2629

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM ubuntu:22.04
2+
3+
ENV DEBIAN_FRONTEND=noninteractive
4+
5+
RUN apt-get update \
6+
&& apt-get install -y --no-install-recommends \
7+
python3.10 python3-pip python-is-python3 wget git build-essential \
8+
&& rm -rf /var/lib/apt/lists/*
9+
10+
RUN git clone https://github.com/CodeClash-ai/TexasHoldem.git /workspace \
11+
&& cd /workspace \
12+
&& git remote set-url origin https://github.com/CodeClash-ai/TexasHoldem.git
13+
14+
WORKDIR /workspace

codeclash/arenas/texasholdem/__init__.py

Whitespace-only changes.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import re
2+
3+
from codeclash.agents.player import Player
4+
from codeclash.arenas.arena import CodeArena, RoundStats
5+
from codeclash.constants import RESULT_TIE
6+
from codeclash.utils.environment import assert_zero_exit_code
7+
8+
LOG_FILE = "result.log"
9+
10+
11+
class TexasHoldemArena(CodeArena):
12+
name: str = "TexasHoldem"
13+
submission: str = "main.py"
14+
variant: str = "classic" # Can be overridden by subclasses
15+
description: str = """Texas Hold'em is a heads-up (2-player) No-Limit poker game where each player receives 2 private hole cards and shares 5 community cards.
16+
17+
Players bet based on hand strength across 4 betting rounds (preflop, flop, turn, river).
18+
The best 5-card hand from the 7 available cards wins the pot.
19+
20+
Your bot must implement a `get_move(state)` function that returns one of:
21+
- 'fold': Give up the hand
22+
- 'check': Pass when no bet to call
23+
- 'call': Match the current bet
24+
- 'raise <amount>': Raise to a specified total amount
25+
- 'all_in': Bet all remaining chips
26+
27+
The state object contains:
28+
- hole_cards: Your 2 private cards (e.g., ['As', 'Kh'])
29+
- community_cards: Current board cards (0-5)
30+
- pot: Total pot size
31+
- current_bet: Amount to call
32+
- player_stack: Your remaining chips
33+
- opponent_stack: Opponent's chips
34+
- player_bet: Your bet this round
35+
- opponent_bet: Opponent's bet this round
36+
- position: 'button' or 'big_blind'
37+
- round_name: 'preflop', 'flop', 'turn', 'river'
38+
- min_raise: Minimum raise amount
39+
- is_first_action: True if first to act this betting round
40+
- variant: 'classic' or 'short_deck'
41+
42+
Cards use 2-character notation: rank (23456789TJQKA) + suit (cdhs).
43+
Example: 'As' = Ace of spades, 'Th' = Ten of hearts.
44+
45+
Hand rankings (highest to lowest):
46+
1. Royal Flush 2. Straight Flush 3. Four of a Kind 4. Full House
47+
5. Flush 6. Straight 7. Three of a Kind 8. Two Pair 9. Pair 10. High Card
48+
"""
49+
50+
def __init__(self, config, **kwargs):
51+
super().__init__(config, **kwargs)
52+
assert len(config["players"]) == 2, "TexasHoldem requires exactly 2 players"
53+
54+
def execute_round(self, agents: list[Player]) -> None:
55+
args = [f"/{agent.name}/{self.submission}" for agent in agents]
56+
variant_arg = f"--variant {self.variant}" if self.variant else ""
57+
cmd = f"python engine.py {' '.join(args)} -r {self.game_config['sims_per_round']} {variant_arg} > {self.log_env / LOG_FILE};"
58+
self.logger.info(f"Running game: {cmd}")
59+
assert_zero_exit_code(self.environment.execute(cmd))
60+
61+
def get_results(self, agents: list[Player], round_num: int, stats: RoundStats):
62+
with open(self.log_round(round_num) / LOG_FILE) as f:
63+
round_log = f.read()
64+
lines = round_log.split("FINAL_RESULTS")[-1].splitlines()
65+
66+
scores = {}
67+
for line in lines:
68+
match = re.search(r"Bot\_(\d)\_main:\s(\d+)\srounds\swon", line)
69+
if match:
70+
bot_id = match.group(1)
71+
rounds_won = int(match.group(2))
72+
scores[agents[int(bot_id) - 1].name] = rounds_won
73+
74+
draw_match = re.search(r"Draws:\s(\d+)", round_log)
75+
if draw_match and int(draw_match.group(1)) > 0:
76+
scores[RESULT_TIE] = int(draw_match.group(1))
77+
78+
stats.winner = max(scores, key=scores.get) if scores else "unknown"
79+
stats.scores = scores
80+
for player, score in scores.items():
81+
if player != RESULT_TIE:
82+
stats.player_stats[player].score = score
83+
84+
def validate_code(self, agent: Player) -> tuple[bool, str | None]:
85+
if self.submission not in agent.environment.execute("ls")["output"]:
86+
return False, f"No {self.submission} file found"
87+
88+
bot_content = agent.environment.execute(f"cat {self.submission}")["output"]
89+
if "def get_move(" not in bot_content:
90+
return False, "Missing required function: def get_move(state)"
91+
92+
return True, None
93+
94+
95+
class ShortDeckHoldemArena(TexasHoldemArena):
96+
"""Short-deck (Six-plus) Hold'em variant with 36-card deck."""
97+
98+
name: str = "ShortDeckHoldem"
99+
variant: str = "short_deck"
100+
description: str = """Short-deck (Six-plus) Hold'em is a heads-up (2-player) No-Limit poker variant using a 36-card deck (6 through Ace, removing 2-5).
101+
102+
Key differences from classic Texas Hold'em:
103+
- 36-card deck (ranks 6-A only, no 2-5)
104+
- FLUSH BEATS FULL HOUSE (flushes are harder to make with fewer cards per suit)
105+
- A-6-7-8-9 is the lowest straight (wheel)
106+
107+
Your bot must implement a `get_move(state)` function that returns one of:
108+
- 'fold': Give up the hand
109+
- 'check': Pass when no bet to call
110+
- 'call': Match the current bet
111+
- 'raise <amount>': Raise to a specified total amount
112+
- 'all_in': Bet all remaining chips
113+
114+
The state object contains:
115+
- hole_cards: Your 2 private cards (e.g., ['As', 'Kh'])
116+
- community_cards: Current board cards (0-5)
117+
- pot: Total pot size
118+
- current_bet: Amount to call
119+
- player_stack: Your remaining chips
120+
- opponent_stack: Opponent's chips
121+
- player_bet: Your bet this round
122+
- opponent_bet: Opponent's bet this round
123+
- position: 'button' or 'big_blind'
124+
- round_name: 'preflop', 'flop', 'turn', 'river'
125+
- min_raise: Minimum raise amount
126+
- is_first_action: True if first to act this betting round
127+
- variant: 'short_deck'
128+
129+
Cards use 2-character notation: rank (6789TJQKA) + suit (cdhs).
130+
Example: 'As' = Ace of spades, 'Th' = Ten of hearts, '6c' = Six of clubs.
131+
132+
Short-deck hand rankings (highest to lowest):
133+
1. Royal Flush 2. Straight Flush 3. Four of a Kind 4. FLUSH (beats full house!)
134+
5. Full House 6. Straight 7. Three of a Kind 8. Two Pair 9. Pair 10. High Card
135+
"""

configs/test/shortdeckholdem.yaml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
tournament:
2+
rounds: 3
3+
game:
4+
name: ShortDeckHoldem
5+
sims_per_round: 50
6+
players:
7+
- agent: dummy
8+
name: p1
9+
- agent: dummy
10+
name: p2
11+
prompts:
12+
game_description: |
13+
You are a software developer ({{player_id}}) competing in a Short-deck (Six-plus) Hold'em poker coding game.
14+
15+
The game is played in {{rounds}} rounds. For every round, you (and your competitor) edit program code that controls your poker bot. This is round {{round}}.
16+
After you and your competitor finish editing your codebases, the game is run automatically.
17+
18+
IMPORTANT: This is SHORT-DECK Hold'em with key differences from classic:
19+
- 36-card deck (only 6 through Ace, no 2-5)
20+
- FLUSH BEATS FULL HOUSE (flushes are harder to make)
21+
- A-6-7-8-9 is the lowest straight (wheel)
22+
23+
Your bot must implement a `get_move(state)` function that receives the game state and returns an action:
24+
- 'fold': Give up the hand
25+
- 'check': Pass when no bet to call
26+
- 'call': Match the current bet
27+
- 'raise <amount>': Raise to a specified total amount
28+
- 'all_in': Bet all remaining chips
29+
30+
The state object contains:
31+
- hole_cards: Your 2 private cards (e.g., ['As', 'Kh'])
32+
- community_cards: Current board cards (0-5)
33+
- pot: Total pot size
34+
- current_bet: Amount to call
35+
- player_stack/opponent_stack: Chip counts
36+
- position: 'button' or 'big_blind'
37+
- round_name: 'preflop', 'flop', 'turn', 'river'
38+
- variant: 'short_deck'
39+
40+
Adjust your strategy for the modified hand rankings!

configs/test/texasholdem.yaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
tournament:
2+
rounds: 3
3+
game:
4+
name: TexasHoldem
5+
sims_per_round: 50
6+
players:
7+
- agent: dummy
8+
name: p1
9+
- agent: dummy
10+
name: p2
11+
prompts:
12+
game_description: |
13+
You are a software developer ({{player_id}}) competing in a Texas Hold'em poker coding game.
14+
15+
The game is played in {{rounds}} rounds. For every round, you (and your competitor) edit program code that controls your poker bot. This is round {{round}}.
16+
After you and your competitor finish editing your codebases, the game is run automatically.
17+
18+
Your bot must implement a `get_move(state)` function that receives the game state and returns an action:
19+
- 'fold': Give up the hand
20+
- 'check': Pass when no bet to call
21+
- 'call': Match the current bet
22+
- 'raise <amount>': Raise to a specified total amount
23+
- 'all_in': Bet all remaining chips
24+
25+
The state object contains:
26+
- hole_cards: Your 2 private cards (e.g., ['As', 'Kh'])
27+
- community_cards: Current board cards (0-5)
28+
- pot: Total pot size
29+
- current_bet: Amount to call
30+
- player_stack/opponent_stack: Chip counts
31+
- position: 'button' or 'big_blind'
32+
- round_name: 'preflop', 'flop', 'turn', 'river'
33+
34+
Focus on hand selection, position awareness, and proper bet sizing.

0 commit comments

Comments
 (0)