shortdeck1.2
This commit is contained in:
@@ -1,12 +1,8 @@
|
||||
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
|
||||
from shortdeck_arena.game_stage import BlindConfig, GameStage, PlayerState
|
||||
import uuid
|
||||
from typing import List, Optional, Dict
|
||||
from shortdeck_arena.simulation import Simulation
|
||||
from shortdeck_arena.agent import Agent, HumanAgent
|
||||
from shortdeck_arena.game_stage import BlindConfig
|
||||
|
||||
|
||||
class ArenaGame:
|
||||
@@ -18,163 +14,153 @@ class ArenaGame:
|
||||
self.max_players = max_players
|
||||
self.sim: Optional[Simulation] = None
|
||||
|
||||
# 筹码管理
|
||||
self.stacks: List[int] = []
|
||||
|
||||
# 盲注配置
|
||||
self.blind_config = BlindConfig(small_blind, big_blind, ante=0)
|
||||
|
||||
# 游戏标识
|
||||
self.game_id = str(uuid.uuid4())
|
||||
|
||||
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)
|
||||
|
||||
def join_game(self, name) -> int:
|
||||
if len(self.agents) >= self.max_players:
|
||||
raise ValueError("Game is full")
|
||||
|
||||
player_id = len(self.agents)
|
||||
agent = HumanAgent(player_id)
|
||||
self.agents.append(agent)
|
||||
self.stacks.append(self.starting_stack)
|
||||
self.player_names.append(name)
|
||||
|
||||
if len(self.agents) == 1:
|
||||
self.sim = Simulation(self.agents, blind_config=self.blind_config)
|
||||
# 筹码默认1000
|
||||
self.sim.stacks = [self.starting_stack] * len(self.agents)
|
||||
elif self.sim:
|
||||
self.sim.agents = self.agents
|
||||
self.sim.player_states.append(self.sim.player_states[0].__class__.ACTIVE)
|
||||
self.sim.pot.append(0)
|
||||
self.sim.stacks.append(self.starting_stack)
|
||||
|
||||
# 当有至少2个玩家时,触发新一轮
|
||||
if len(self.agents) >= 2 and self.sim:
|
||||
self.sim.stacks = [self.starting_stack] * len(self.agents)
|
||||
self.sim.new_round()
|
||||
|
||||
return player_id
|
||||
|
||||
self.sim = Simulation(self.agents, self.blind_config)
|
||||
return pid
|
||||
def apply_action(self, player_id, action, amount: Optional[int] = None) -> dict:
|
||||
if not self.sim:
|
||||
return {"success": False, "message": "游戏未开始"}
|
||||
|
||||
try:
|
||||
self.sim.apply_action(player_id, action, amount)
|
||||
|
||||
self.sim.dump_data()
|
||||
|
||||
return {"success": True, "message": f"Applied {action}"}
|
||||
except Exception as e:
|
||||
return {"success": False, "message": str(e)}
|
||||
|
||||
def info(self, player_id: Optional[int] = None) -> 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": [],
|
||||
}
|
||||
return {"error": "游戏未初始化"}
|
||||
|
||||
# 更新栈大小 (扣除已投入底池的金额)
|
||||
updated_stacks = []
|
||||
for i, base_stack in enumerate(self.stacks):
|
||||
pot_contribution = self.sim.pot[i] if i < len(self.sim.pot) else 0
|
||||
updated_stacks.append(max(0, base_stack - pot_contribution))
|
||||
|
||||
for i in range(len(self.stacks)):
|
||||
if i < len(updated_stacks):
|
||||
self.stacks[i] = updated_stacks[i]
|
||||
|
||||
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:
|
||||
current_stage = self.sim.current_stage.value
|
||||
board_cards = [str(c) for c in self.sim.board_cards(current_stage)]
|
||||
except Exception:
|
||||
board_cards = []
|
||||
|
||||
# 获取可用动作信息
|
||||
actions = {}
|
||||
if (player_id is not None and player_id == self.sim.current_turn and
|
||||
self.sim.current_stage != GameStage.FINISHED):
|
||||
|
||||
call_amount = self.sim.get_call_amount(player_id)
|
||||
available_stack = updated_stacks[player_id] if player_id < len(updated_stacks) else 0
|
||||
|
||||
# 更新node_info以包含实际栈信息
|
||||
node_info = self.sim.node_info()
|
||||
node_info["bet_max"] = available_stack
|
||||
|
||||
actions = {
|
||||
"call_amount": call_amount,
|
||||
"bet_min": node_info["bet_min"],
|
||||
"bet_max": node_info["bet_max"],
|
||||
"can_check": call_amount == 0,
|
||||
"can_fold": True,
|
||||
"can_call": call_amount > 0 and call_amount <= available_stack,
|
||||
"can_bet": available_stack > call_amount,
|
||||
}
|
||||
|
||||
return {
|
||||
info_data = {
|
||||
"game_id": self.game_id,
|
||||
"players": self.player_names,
|
||||
"stacks": updated_stacks,
|
||||
"dealer_index": 0, # 简化:固定庄家位置, (优化轮询)
|
||||
"dealer_index": self.sim.dealer_position,
|
||||
"current_turn": self.sim.current_turn,
|
||||
"pot": self.sim.total_pot,
|
||||
"stage": self.sim.current_stage.value,
|
||||
"actions": actions,
|
||||
"player_cards": player_cards,
|
||||
"board_cards": board_cards,
|
||||
"player_states": [state.value for state in self.sim.player_states],
|
||||
"total_pot": self.sim.total_pot,
|
||||
"side_pots": [{"amount": pot.amount, "eligible_players": list(pot.eligible_players)}
|
||||
for pot in self.sim.get_side_pots()],
|
||||
}
|
||||
|
||||
def apply_action(self, pid: int, action: str, amount: Optional[int] = None):
|
||||
if not self.sim:
|
||||
raise ValueError("no game")
|
||||
if player_id is not None and 0 <= player_id < len(self.sim.stacks):
|
||||
try:
|
||||
player_cards = self.sim.player_cards(player_id)
|
||||
info_data["player_cards"] = [str(card) for card in player_cards]
|
||||
except Exception:
|
||||
info_data["player_cards"] = []
|
||||
|
||||
# 验证动作合法性
|
||||
if pid != self.sim.current_turn:
|
||||
raise ValueError(f"not your turn, current turn: {self.sim.current_turn}")
|
||||
|
||||
if self.sim.current_stage == GameStage.FINISHED:
|
||||
raise ValueError("game already finished")
|
||||
|
||||
# 获取玩家可用筹码
|
||||
pot_contribution = self.sim.pot[pid] if pid < len(self.sim.pot) else 0
|
||||
available_stack = self.stacks[pid] - pot_contribution
|
||||
|
||||
# 预处理动作和金额
|
||||
action = action.lower()
|
||||
|
||||
if action in ("bet", "raise") and amount is not None:
|
||||
# 限制下注金额不超过可用筹码
|
||||
if amount > available_stack:
|
||||
amount = available_stack
|
||||
# 如果全下,可能需要标记为allin
|
||||
if amount == available_stack and available_stack > 0:
|
||||
self.sim.player_states[pid] = PlayerState.ALLIN
|
||||
|
||||
elif action == "call":
|
||||
# call动作的金额验证
|
||||
call_amount = self.sim.get_call_amount(pid)
|
||||
if call_amount > available_stack:
|
||||
# 不够跟注,自动allin
|
||||
amount = available_stack
|
||||
if available_stack > 0:
|
||||
self.sim.player_states[pid] = PlayerState.ALLIN
|
||||
info_data["actions"] = self.sim.get_available_actions(player_id)
|
||||
else:
|
||||
info_data["player_cards"] = []
|
||||
info_data["actions"] = {"can_act": False, "reason": "Invalid player"}
|
||||
|
||||
try:
|
||||
self.sim.apply_action(pid, action, amount)
|
||||
except ValueError as e:
|
||||
raise ValueError(f"invalid action: {e}")
|
||||
|
||||
self.sim.dump_data(Path.cwd() / "shortdeck_arena_history.jsonl")
|
||||
board_cards = self.sim.board_cards(self.sim.current_stage.value)
|
||||
info_data["board_cards"] = [str(card) for card in board_cards]
|
||||
except Exception:
|
||||
info_data["board_cards"] = []
|
||||
|
||||
info_data["stacks"] = self.sim.stacks.copy()
|
||||
info_data["player_states"] = [state.value for state in self.sim.player_states]
|
||||
info_data["current_pot"] = self.sim.pot.copy()
|
||||
|
||||
return info_data
|
||||
|
||||
|
||||
def get_hand_strength(self, player_id: int) -> Dict:
|
||||
ranking = self.sim.evaluate_player_hand(player_id)
|
||||
if ranking is None:
|
||||
return {"error": "无法评估手牌"}
|
||||
return {
|
||||
"hand_type": ranking.hand_type.type_name,
|
||||
"description": str(ranking),
|
||||
"strength": ranking.get_strength(),
|
||||
"cards": [str(card) for card in ranking.cards]
|
||||
}
|
||||
|
||||
def check_hand_complete(self) -> bool:
|
||||
return self.sim.is_hand_complete() if self.sim else False
|
||||
|
||||
def get_winners(self) -> Dict:
|
||||
|
||||
@property
|
||||
def current_turn(self) -> int:
|
||||
if not self.sim:
|
||||
return 0
|
||||
return self.sim.current_turn
|
||||
return {"error": "游戏未初始化"}
|
||||
|
||||
if not self.sim.is_hand_complete():
|
||||
return {"error": "手牌未完成"}
|
||||
|
||||
return self.sim.complete_hand()
|
||||
|
||||
def showdown(self) -> Dict:
|
||||
if not self.sim:
|
||||
return {"error": "游戏未初始化"}
|
||||
|
||||
winners = self.sim.determine_winners()
|
||||
showdown_info = {}
|
||||
|
||||
for pid, ranking in winners.items():
|
||||
if ranking is not None:
|
||||
showdown_info[pid] = {
|
||||
"cards": [str(card) for card in self.sim.player_cards(pid)],
|
||||
"hand_type": ranking.hand_type.type_name,
|
||||
"description": str(ranking),
|
||||
"strength": ranking.get_strength()
|
||||
}
|
||||
else:
|
||||
showdown_info[pid] = {
|
||||
"cards": [str(card) for card in self.sim.player_cards(pid)],
|
||||
"hand_type": "Winner by default",
|
||||
"description": "Other players folded",
|
||||
"strength": float('inf')
|
||||
}
|
||||
|
||||
return {
|
||||
"showdown": showdown_info,
|
||||
"pot_distribution": self.sim.distribute_pot()
|
||||
}
|
||||
|
||||
@property
|
||||
def pot(self) -> int:
|
||||
if not self.sim:
|
||||
return 0
|
||||
return self.sim.total_pot
|
||||
|
||||
return self.sim.total_pot if self.sim else 0
|
||||
|
||||
@property
|
||||
def stacks(self) -> List[int]:
|
||||
return self.sim.stacks if self.sim else []
|
||||
|
||||
@property
|
||||
def current_turn(self) -> int:
|
||||
return self.sim.current_turn if self.sim else -1
|
||||
|
||||
@property
|
||||
def history(self) -> List[Dict]:
|
||||
if not self.sim:
|
||||
return []
|
||||
return self.sim.history
|
||||
return self.sim.history if self.sim else []
|
||||
Reference in New Issue
Block a user