shortdeck1.3:ui and fix
This commit is contained in:
@@ -42,6 +42,9 @@ class Simulation:
|
||||
self.side_pot_manager = SidePotManager()
|
||||
self.stacks: List[int] = [1000] * len(agents) # 默认筹码
|
||||
|
||||
# 用于结算
|
||||
self.hand_completed = False
|
||||
|
||||
self.new_round()
|
||||
|
||||
def new_round(self):
|
||||
@@ -54,6 +57,7 @@ class Simulation:
|
||||
self.current_stage = GameStage.PREFLOP
|
||||
self.player_states = [PlayerState.ACTIVE] * len(self.agents)
|
||||
self.betting_round_complete = False
|
||||
self.hand_completed = False # 重置完成标志
|
||||
|
||||
# 重置下注状态
|
||||
self.pot = [0] * len(self.agents)
|
||||
@@ -141,25 +145,19 @@ class Simulation:
|
||||
return max(0, max_pot - self.pot[pid])
|
||||
|
||||
def get_min_raise_amount(self, pid) -> int:
|
||||
"""最小加注金额"""
|
||||
call_amount = self.get_call_amount(pid)
|
||||
min_raise = call_amount + max(self.last_raise_amount, self.blind_config.big_blind)
|
||||
return min_raise
|
||||
|
||||
def get_max_bet_amount(self, pid) -> int:
|
||||
"""最大下注金额(剩余筹码)"""
|
||||
if pid >= len(self.stacks):
|
||||
return 0
|
||||
return self.stacks[pid]
|
||||
|
||||
def is_all_in_amount(self, pid, amount) -> bool:
|
||||
"""检查是否为allin"""
|
||||
return amount >= self.stacks[pid]
|
||||
|
||||
def validate_bet_amount(self, pid, action, amount) -> tuple[bool, str, int]:
|
||||
"""
|
||||
验证下注金额合法性
|
||||
"""
|
||||
if pid >= len(self.stacks):
|
||||
return False, "无效玩家", amount
|
||||
|
||||
@@ -178,12 +176,16 @@ class Simulation:
|
||||
if call_amount == 0:
|
||||
return False, "不需要跟注", 0
|
||||
|
||||
# All-in call
|
||||
if call_amount >= available_stack:
|
||||
return True, "", available_stack
|
||||
|
||||
return True, "", call_amount
|
||||
|
||||
elif action == "allin":
|
||||
if available_stack <= 0:
|
||||
return False, "没有筹码进行 all-in", 0
|
||||
return True, "", available_stack
|
||||
|
||||
elif action in ["bet", "raise"]:
|
||||
if amount <= 0:
|
||||
return False, "无效下注金额", amount
|
||||
@@ -207,6 +209,9 @@ class Simulation:
|
||||
return False, "无效行为", amount
|
||||
|
||||
def get_available_actions(self, pid: int) -> dict:
|
||||
if self.current_stage in [GameStage.FINISHED, GameStage.SHOWDOWN]:
|
||||
return {"can_act": False, "reason": "游戏已结束" if self.current_stage == GameStage.FINISHED else "摊牌阶段"}
|
||||
|
||||
if pid != self.current_turn:
|
||||
return {"can_act": False, "reason": "不是你的回合"}
|
||||
|
||||
@@ -214,7 +219,7 @@ class Simulation:
|
||||
return {"can_act": False, "reason": "无效玩家"}
|
||||
|
||||
state = self.player_states[pid]
|
||||
if state in [PlayerState.FOLDED, PlayerState.ALLIN, PlayerState.OUT]:
|
||||
if state in [PlayerState.FOLDED, PlayerState.ALLIN]:
|
||||
return {"can_act": False, "reason": f"Player state: {state}"}
|
||||
|
||||
call_amount = self.get_call_amount(pid)
|
||||
@@ -225,7 +230,7 @@ class Simulation:
|
||||
"can_fold": True,
|
||||
"can_check": call_amount == 0,
|
||||
"can_call": call_amount > 0 and call_amount < available_stack,
|
||||
"can_bet": max(self.pot) == 0 and available_stack > 0,
|
||||
"can_bet": call_amount == 0 and available_stack > 0,
|
||||
"can_raise": call_amount > 0 and available_stack > call_amount,
|
||||
"can_allin": available_stack > 0,
|
||||
"call_amount": call_amount,
|
||||
@@ -241,27 +246,33 @@ class Simulation:
|
||||
"""
|
||||
检查当前下注轮是否完成
|
||||
"""
|
||||
active_players = [i for i, state in enumerate(self.player_states)
|
||||
if state in (PlayerState.ACTIVE, PlayerState.CALLED)]
|
||||
# 首先检查是否只剩一个未弃牌的玩家
|
||||
non_folded_players = [i for i, state in enumerate(self.player_states)
|
||||
if state != PlayerState.FOLDED]
|
||||
|
||||
if len(active_players) <= 1:
|
||||
if len(non_folded_players) <= 1:
|
||||
return True
|
||||
|
||||
active_or_allin_players = [i for i, state in enumerate(self.player_states)
|
||||
if state in (PlayerState.ACTIVE, PlayerState.CALLED, PlayerState.ALLIN)]
|
||||
|
||||
all_allin_or_folded = all(state in (PlayerState.ALLIN, PlayerState.FOLDED)
|
||||
for state in self.player_states)
|
||||
if all_allin_or_folded:
|
||||
return True
|
||||
|
||||
# 检查所有active玩家是否都已投入相同金额,且所有人都已经行动过
|
||||
max_pot = self.get_current_max_bet()
|
||||
|
||||
# 统计还需要行动的玩家
|
||||
players_need_action = []
|
||||
for i in active_players:
|
||||
for i in active_or_allin_players:
|
||||
# allin
|
||||
if self.player_states[i] == PlayerState.ALLIN:
|
||||
continue
|
||||
# 投入金额不足的玩家需要行动
|
||||
continue
|
||||
if self.pot[i] < max_pot:
|
||||
players_need_action.append(i)
|
||||
# Active状态的玩家如果还没有在本轮行动过,也需要行动
|
||||
elif self.player_states[i] == PlayerState.ACTIVE:
|
||||
# 在翻前,大盲玩家即使投入了足够金额,也有权行动一次
|
||||
|
||||
if (self.current_stage == GameStage.PREFLOP and
|
||||
i == self.blind_config.get_bb_position(len(self.agents), self.dealer_position)):
|
||||
# 检查大盲是否已经行动过(除了盲注)
|
||||
@@ -289,6 +300,12 @@ class Simulation:
|
||||
self.complete_hand()
|
||||
return
|
||||
|
||||
|
||||
if self.current_stage == GameStage.SHOWDOWN:
|
||||
self.current_stage = GameStage.FINISHED
|
||||
self.complete_hand()
|
||||
return
|
||||
|
||||
# 重置下注轮状态
|
||||
self.betting_round_complete = False
|
||||
|
||||
@@ -306,6 +323,7 @@ class Simulation:
|
||||
def get_next_active_player(self, start_pos) -> Optional[int]:
|
||||
for i in range(len(self.agents)):
|
||||
pos = (start_pos + i) % len(self.agents)
|
||||
# 只有ACTIVE状态的玩家可以行动,ALLIN和FOLDED的玩家不能行动
|
||||
if self.player_states[pos] == PlayerState.ACTIVE:
|
||||
return pos
|
||||
return None
|
||||
@@ -313,7 +331,7 @@ class Simulation:
|
||||
def get_side_pots(self) -> List:
|
||||
active_players = [
|
||||
i for i, state in enumerate(self.player_states)
|
||||
if state not in [PlayerState.FOLDED, PlayerState.OUT]
|
||||
if state not in [PlayerState.FOLDED]
|
||||
]
|
||||
return self.side_pot_manager.create_side_pots(active_players)
|
||||
|
||||
@@ -375,6 +393,29 @@ class Simulation:
|
||||
raise ValueError("跟注金额>0, 无法过牌,需要跟注或弃牌")
|
||||
self.player_states[pid] = PlayerState.CALLED
|
||||
|
||||
elif action == "allin":
|
||||
# all-in
|
||||
actual_amount = self.stacks[pid]
|
||||
if actual_amount <= 0:
|
||||
raise ValueError("没有可用筹码进行 all-in")
|
||||
|
||||
self.player_states[pid] = PlayerState.ALLIN
|
||||
self.pot[pid] += actual_amount
|
||||
self.stacks[pid] = 0
|
||||
self.total_pot += actual_amount
|
||||
self.side_pot_manager.add_investment(pid, actual_amount)
|
||||
|
||||
# 更新最后加注金额(如果 all-in 金额超过跟注金额)
|
||||
call_amount = self.get_call_amount(pid)
|
||||
raise_amount = actual_amount - call_amount
|
||||
if raise_amount > 0:
|
||||
self.last_raise_amount = raise_amount
|
||||
self.min_raise = raise_amount
|
||||
|
||||
for i, state in enumerate(self.player_states):
|
||||
if i != pid and state == PlayerState.CALLED:
|
||||
self.player_states[i] = PlayerState.ACTIVE
|
||||
|
||||
elif action in ("bet", "raise"):
|
||||
if amount is None:
|
||||
raise ValueError(f"{action} 需要指定金额")
|
||||
@@ -446,7 +487,6 @@ class Simulation:
|
||||
|
||||
|
||||
def evaluate_player_hand(self, pid: int) -> Optional[HandRanking]:
|
||||
"""评估玩家手牌强度"""
|
||||
if pid >= len(self.agents):
|
||||
return None
|
||||
|
||||
@@ -454,22 +494,17 @@ class Simulation:
|
||||
return None
|
||||
|
||||
try:
|
||||
# 获取玩家手牌
|
||||
player_cards = self.player_cards(pid)
|
||||
|
||||
# 获取公共牌
|
||||
board_cards = self.board_cards(self.current_stage.value)
|
||||
|
||||
# 至少需要5张牌才能评估
|
||||
all_cards = player_cards + board_cards
|
||||
if len(all_cards) < 5:
|
||||
return None
|
||||
|
||||
# 如果正好5张牌,直接评估
|
||||
|
||||
if len(all_cards) == 5:
|
||||
return HandEvaluator.evaluate5Cards(all_cards)
|
||||
|
||||
# 如果超过5张牌,找最佳组合
|
||||
|
||||
return HandEvaluator.evaluateHand(all_cards)
|
||||
|
||||
except Exception as e:
|
||||
@@ -478,7 +513,7 @@ class Simulation:
|
||||
|
||||
def get_active_players(self) -> List[int]:
|
||||
return [i for i, state in enumerate(self.player_states)
|
||||
if state not in [PlayerState.FOLDED, PlayerState.OUT]]
|
||||
if state not in [PlayerState.FOLDED]]
|
||||
|
||||
def is_hand_complete(self) -> bool:
|
||||
active_players = self.get_active_players()
|
||||
@@ -522,9 +557,8 @@ class Simulation:
|
||||
winner_id = list(winners.keys())[0]
|
||||
return {winner_id: self.total_pot}
|
||||
|
||||
# 多人摊牌,使用边池分配
|
||||
# 多人摊牌
|
||||
if len(winners) > 1:
|
||||
#转换HandRanking为数值强度
|
||||
hand_strengths = {}
|
||||
for pid, ranking in winners.items():
|
||||
if ranking is not None:
|
||||
@@ -539,16 +573,31 @@ class Simulation:
|
||||
def complete_hand(self) -> Dict:
|
||||
if not self.is_hand_complete():
|
||||
return {"complete": False, "message": "牌局未结束"}
|
||||
|
||||
if self.hand_completed:
|
||||
return {
|
||||
"complete": True,
|
||||
"winners": [],
|
||||
"winnings": {},
|
||||
"final_stacks": self.stacks.copy(),
|
||||
"showdown_hands": {},
|
||||
"message": "手牌已完成"
|
||||
}
|
||||
|
||||
winners = self.determine_winners()
|
||||
|
||||
winnings = self.distribute_pot()
|
||||
|
||||
# 更新筹码
|
||||
for pid, amount in winnings.items():
|
||||
if pid < len(self.stacks):
|
||||
self.stacks[pid] += amount
|
||||
|
||||
self.total_pot = 0
|
||||
self.pot = [0] * len(self.agents)
|
||||
self.side_pot_manager.reset()
|
||||
|
||||
self.current_stage = GameStage.FINISHED
|
||||
self.hand_completed = True
|
||||
|
||||
result = {
|
||||
"complete": True,
|
||||
@@ -557,14 +606,22 @@ class Simulation:
|
||||
"final_stacks": self.stacks.copy(),
|
||||
"showdown_hands": {}
|
||||
}
|
||||
|
||||
active_players = [i for i, state in enumerate(self.player_states)
|
||||
if state != PlayerState.FOLDED]
|
||||
|
||||
# 摊牌信息
|
||||
for pid, ranking in winners.items():
|
||||
if ranking is not None:
|
||||
for pid in active_players:
|
||||
player_hand = self.player_cards(pid)
|
||||
if len(player_hand) >= 2:
|
||||
evaluator = HandEvaluator()
|
||||
board_cards = self.board_cards()
|
||||
ranking = evaluator.evaluate(player_hand, board_cards)
|
||||
|
||||
result["showdown_hands"][pid] = {
|
||||
"cards": [str(card) for card in self.player_cards(pid)],
|
||||
"hand_type": ranking.hand_type.type_name,
|
||||
"description": str(ranking)
|
||||
"cards": [str(card) for card in player_hand],
|
||||
"hand_type": ranking.hand_type.type_name if ranking else "无牌型",
|
||||
"description": str(ranking) if ranking else "无效手牌",
|
||||
"is_winner": pid in winners
|
||||
}
|
||||
|
||||
return result
|
||||
Reference in New Issue
Block a user