112 lines
3.4 KiB
Python
112 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import List, Dict, Optional
|
|
from pathlib import Path
|
|
|
|
from shortdeck_arena.simulation import Simulation
|
|
from shortdeck_arena.agent import HumanAgent
|
|
import uuid
|
|
|
|
|
|
class ArenaGame:
|
|
def __init__(self, starting_stack: int = 1000, max_players: int = 6):
|
|
self.agents = []
|
|
self.player_names: List[str] = []
|
|
self.starting_stack = starting_stack
|
|
self.max_players = max_players
|
|
self.sim: Optional[Simulation] = None
|
|
self.stacks: List[int] = []
|
|
self.current_turn: int = 0
|
|
self.pot: int = 0
|
|
self.game_id = [str(name) for name in self.player_names]
|
|
|
|
def join_game(self, name: str) -> int:
|
|
if len(self.player_names) >= self.max_players:
|
|
raise ValueError("table full")
|
|
pid = len(self.player_names)
|
|
self.player_names.append(name)
|
|
agent = HumanAgent(pid)
|
|
self.agents.append(agent)
|
|
self.stacks.append(self.starting_stack)
|
|
|
|
self.sim = Simulation(self.agents)
|
|
return pid
|
|
|
|
def info(self, player_id) -> Dict:
|
|
|
|
if not self.sim:
|
|
return {
|
|
"game_id": self.game_id,
|
|
"players": self.player_names,
|
|
"stacks": [],
|
|
"dealer_index": 0,
|
|
"current_turn": 0,
|
|
"pot": 0,
|
|
"stage": "preflop",
|
|
"actions": {},
|
|
"player_cards": [],
|
|
"board_cards": [],
|
|
}
|
|
|
|
player_cards = []
|
|
board_cards = []
|
|
try:
|
|
if player_id is not None and 0 <= player_id < len(self.agents):
|
|
player_cards = [str(c) for c in self.sim.player_cards(player_id)]
|
|
except Exception:
|
|
player_cards = []
|
|
|
|
try:
|
|
|
|
board_cards = [str(c) for c in self.sim.board_cards("river")]
|
|
except Exception:
|
|
board_cards = []
|
|
|
|
return {
|
|
"game_id": self.game_id,
|
|
"players": self.player_names,
|
|
"stacks": list(self.stacks),
|
|
"dealer_index": 0,
|
|
"current_turn": self.current_turn,
|
|
"pot": self.pot,
|
|
"stage": "preflop",
|
|
"actions": {"bet_min": 1, "bet_max": 100},
|
|
"player_cards": player_cards,
|
|
"board_cards": board_cards,
|
|
}
|
|
|
|
def apply_action(self, pid: int, action: str, amount: Optional[int] = None):
|
|
if not self.sim:
|
|
raise ValueError("no game")
|
|
if pid != self.current_turn:
|
|
raise ValueError("not your turn")
|
|
|
|
action = action.lower()
|
|
if action == "check":
|
|
pass
|
|
elif action == "bet":
|
|
if amount is None:
|
|
raise ValueError("bet requires amount")
|
|
if amount < 0:
|
|
raise ValueError("invalid amount")
|
|
if amount > self.stacks[pid]:
|
|
|
|
amount = self.stacks[pid]
|
|
self.stacks[pid] -= amount
|
|
self.pot += amount
|
|
elif action == "fold":
|
|
self.stacks[pid] = 0
|
|
else:
|
|
raise ValueError(f"unknown action: {action}")
|
|
|
|
self.sim.apply_action(pid, action, amount)
|
|
self.sim.dump_data(Path.cwd() / "shortdeck_arena_history.jsonl")
|
|
if len(self.agents) > 0:
|
|
self.current_turn = (self.current_turn + 1) % len(self.agents)
|
|
|
|
@property
|
|
def history(self) -> List[Dict]:
|
|
if not self.sim:
|
|
return []
|
|
return self.sim.history
|