Files
2025-03-07 18:34:50 +08:00

428 lines
16 KiB
Python

import random
from typing import List, Optional, Dict
from player import Player
from game_record import GameRecord, PlayerInitialState
class Game:
def __init__(self, player_configs: List[Dict[str, str]]) -> None:
"""初始化游戏
Args:
player_configs: 包含玩家配置的列表,每个配置是一个字典,包含 name 和 model 字段
"""
# 使用配置创建玩家对象
self.players = [Player(config["name"], config["model"]) for config in player_configs]
# 初始化每个玩家对其他玩家的看法
for player in self.players:
player.init_opinions(self.players)
self.deck: List[str] = []
self.target_card: Optional[str] = None
self.current_player_idx: int = random.randint(0, len(self.players) - 1)
self.last_shooter_name: Optional[str] = None
self.game_over: bool = False
# 创建游戏记录
self.game_record: GameRecord = GameRecord()
self.game_record.start_game([p.name for p in self.players])
self.round_count = 0
def _create_deck(self) -> List[str]:
"""创建并洗牌牌组"""
deck = ['Q'] * 6 + ['K'] * 6 + ['A'] * 6 + ['Joker'] * 2
random.shuffle(deck)
return deck
def deal_cards(self) -> None:
"""发牌并清空旧手牌"""
self.deck = self._create_deck()
for player in self.players:
if player.alive:
player.hand.clear()
# 每位玩家发 5 张牌
for _ in range(5):
for player in self.players:
if player.alive and self.deck:
player.hand.append(self.deck.pop())
player.print_status()
def choose_target_card(self) -> None:
"""随机选择目标牌"""
self.target_card = random.choice(['Q', 'K', 'A'])
print(f"目标牌是: {self.target_card}")
def start_round_record(self) -> None:
"""开始新的回合,并在 `GameRecord` 里记录信息"""
self.round_count += 1
starting_player = self.players[self.current_player_idx].name
player_initial_states = [
PlayerInitialState(
player_name=player.name,
bullet_position=player.bullet_position,
current_gun_position=player.current_bullet_position,
initial_hand=player.hand.copy()
)
for player in self.players if player.alive
]
# 获取当前存活的玩家
round_players = [player.name for player in self.players if player.alive]
# 创建一个深拷贝,而不是引用
player_opinions = {}
for player in self.players:
player_opinions[player.name] = {}
for target, opinion in player.opinions.items():
player_opinions[player.name][target] = opinion
self.game_record.start_round(
round_id=self.round_count,
target_card=self.target_card,
round_players=round_players,
starting_player=starting_player,
player_initial_states=player_initial_states,
player_opinions=player_opinions
)
def is_valid_play(self, cards: List[str]) -> bool:
"""
判断出牌是否符合目标牌规则:
每张牌必须为目标牌或 Joker
"""
return all(card == self.target_card or card == 'Joker' for card in cards)
def find_next_player_with_cards(self, start_idx: int) -> int:
"""返回下一个存活且有手牌的玩家索引"""
idx = start_idx
for _ in range(len(self.players)):
idx = (idx + 1) % len(self.players)
if self.players[idx].alive and self.players[idx].hand:
return idx
return start_idx # 理论上不会发生
def perform_penalty(self, player: Player) -> None:
"""
执行射击惩罚,并根据结果更新游戏状态和记录
Args:
player: 需要执行惩罚的玩家
"""
print(f"玩家 {player.name} 开枪!")
# 执行射击并获取存活状态
still_alive = player.process_penalty()
self.last_shooter_name = player.name
# 记录射击结果
self.game_record.record_shooting(
shooter_name=player.name,
bullet_hit=not still_alive # 如果玩家死亡,说明子弹命中
)
if not still_alive:
print(f"{player.name} 已死亡!")
# 检查胜利条件
if not self.check_victory():
self.reset_round(record_shooter=True)
def reset_round(self, record_shooter: bool) -> None:
"""重置当前小局"""
print("小局游戏重置,开始新的一局!")
# 在发新牌之前进行反思,并获取存活玩家列表
alive_players = self.handle_reflection()
# 重新发牌
self.deal_cards()
self.choose_target_card()
if record_shooter and self.last_shooter_name:
shooter_idx = next((i for i, p in enumerate(self.players)
if p.name == self.last_shooter_name), None)
if shooter_idx is not None and self.players[shooter_idx].alive:
self.current_player_idx = shooter_idx
else:
print(f"{self.last_shooter_name} 已死亡,顺延至下一个存活且有手牌的玩家")
self.current_player_idx = self.find_next_player_with_cards(shooter_idx or 0)
else:
self.last_shooter_name = None
self.current_player_idx = self.players.index(random.choice(alive_players))
self.start_round_record()
print(f"从 {self.players[self.current_player_idx].name} 开始新的一轮!")
def check_victory(self) -> bool:
"""
检查胜利条件(仅剩一名存活玩家时),并记录胜利者
Returns:
bool: 游戏是否结束
"""
alive_players = [p for p in self.players if p.alive]
if len(alive_players) == 1:
winner = alive_players[0]
print(f"\n{winner.name} 获胜!")
# 记录胜利者并保存游戏记录
self.game_record.finish_game(winner.name)
self.game_over = True
return True
return False
def check_other_players_no_cards(self, current_player: Player) -> bool:
"""
检查是否所有其他存活玩家都没有手牌
"""
others = [p for p in self.players if p != current_player and p.alive]
return all(not p.hand for p in others)
def handle_play_cards(self, current_player: Player, next_player: Player) -> List[str]:
"""
处理玩家出牌环节
Args:
current_player: 当前玩家
next_player: 下一个玩家
Returns:
List[str]: 返回打出的牌组
"""
# 获取当前轮次的基础信息
round_base_info = self.game_record.get_latest_round_info()
round_action_info = self.game_record.get_latest_round_actions(current_player.name, include_latest=True)
# 获取出牌决策相关信息
play_decision_info = self.game_record.get_play_decision_info(
current_player.name,
next_player.name
)
# 让当前玩家选择出牌
play_result, reasoning = current_player.choose_cards_to_play(
round_base_info,
round_action_info,
play_decision_info
)
# 记录出牌行为
self.game_record.record_play(
player_name=current_player.name,
played_cards=play_result["played_cards"].copy(),
remaining_cards=current_player.hand.copy(),
play_reason=play_result["play_reason"],
behavior=play_result["behavior"],
next_player=next_player.name,
play_thinking=reasoning
)
return play_result["played_cards"]
def handle_challenge(self, current_player: Player, next_player: Player, played_cards: List[str]) -> Player:
"""
处理玩家质疑环节
Args:
current_player: 当前玩家(被质疑者)
next_player: 下一个玩家(质疑者)
played_cards: 被质疑者打出的牌
Returns:
Player: 返回需要执行惩罚的玩家
"""
# 获取当前轮次的基础信息
round_base_info = self.game_record.get_latest_round_info()
round_action_info = self.game_record.get_latest_round_actions(next_player.name, include_latest=False)
# 获取质疑决策相关信息
challenge_decision_info = self.game_record.get_challenge_decision_info(
next_player.name,
current_player.name
)
# 获取被质疑玩家的表现
challenging_player_behavior = self.game_record.get_latest_play_behavior()
# 检查是否需要添加额外提示
extra_hint = "注意:其他玩家手牌均已打空。" if self.check_other_players_no_cards(next_player) else ""
# 让下一位玩家决定是否质疑
challenge_result, reasoning = next_player.decide_challenge(
round_base_info,
round_action_info,
challenge_decision_info,
challenging_player_behavior,
extra_hint
)
# 如果选择质疑
if challenge_result["was_challenged"]:
# 验证出牌是否合法
is_valid = self.is_valid_play(played_cards)
# 记录质疑结果
self.game_record.record_challenge(
was_challenged=True,
reason=challenge_result["challenge_reason"],
result=not is_valid, # 质疑成功意味着出牌不合法
challenge_thinking=reasoning
)
# 根据验证结果返回需要受罚的玩家
return next_player if is_valid else current_player
else:
# 记录未质疑的情况
self.game_record.record_challenge(
was_challenged=False,
reason=challenge_result["challenge_reason"],
result=None,
challenge_thinking=reasoning
)
return None
def handle_system_challenge(self, current_player: Player) -> None:
"""
处理系统自动质疑的情况
当其他所有存活玩家都没有手牌时,系统自动对当前玩家进行质疑
Args:
current_player: 当前玩家(最后一个有手牌的玩家)
"""
print(f"系统自动质疑 {current_player.name} 的手牌!")
# 记录玩家自动出牌
all_cards = current_player.hand.copy() # 复制当前手牌以供记录
current_player.hand.clear() # 清空手牌
# 记录出牌行为
self.game_record.record_play(
player_name=current_player.name,
played_cards=all_cards,
remaining_cards=[], # 剩余手牌为空列表
play_reason="最后一人,自动出牌",
behavior="无",
next_player="无",
play_thinking=""
)
# 验证出牌是否合法
is_valid = self.is_valid_play(all_cards)
# 记录系统质疑
self.game_record.record_challenge(
was_challenged=True,
reason="系统自动质疑",
result=not is_valid, # 质疑成功意味着出牌不合法
challenge_thinking=""
)
if is_valid:
print(f"系统质疑失败!{current_player.name} 的手牌符合规则。")
# 记录一个特殊的射击结果(无人射击)
self.game_record.record_shooting(
shooter_name="无",
bullet_hit=False
)
self.reset_round(record_shooter=False)
else:
print(f"系统质疑成功!{current_player.name} 的手牌违规,将执行射击惩罚。")
self.perform_penalty(current_player)
def handle_reflection(self) -> None:
"""
处理所有存活玩家的反思过程
在每轮结束时调用,让玩家对其他玩家的行为进行反思和评估
"""
# 获取所有存活玩家
alive_players = [p for p in self.players if p.alive]
alive_player_names = [p.name for p in alive_players]
# 获取当前轮次的相关信息
round_base_info = self.game_record.get_latest_round_info()
# 让每个存活的玩家进行反思
for player in alive_players:
# 获取针对当前玩家的轮次行动信息
round_action_info = self.game_record.get_latest_round_actions(player.name, include_latest=True)
# 获取针对当前玩家的轮次结果
round_result = self.game_record.get_latest_round_result(player.name)
# 执行反思
player.reflect(
alive_players=alive_player_names,
round_base_info=round_base_info,
round_action_info=round_action_info,
round_result=round_result
)
return alive_players
def play_round(self) -> None:
"""执行一轮游戏逻辑"""
current_player = self.players[self.current_player_idx]
# 当其他所有存活玩家都没有手牌时,系统自动对当前玩家进行质疑
if self.check_other_players_no_cards(current_player):
self.handle_system_challenge(current_player)
return
print(f"\n轮到 {current_player.name} 出牌, 目标牌是 {self.target_card}")
current_player.print_status()
# 找到下一位有手牌的玩家
next_idx = self.find_next_player_with_cards(self.current_player_idx)
next_player = self.players[next_idx]
# 处理出牌环节
played_cards = self.handle_play_cards(current_player, next_player)
# 处理质疑环节
if next_player != current_player:
player_to_penalize = self.handle_challenge(current_player, next_player, played_cards)
if player_to_penalize:
self.perform_penalty(player_to_penalize)
return
else:
print(f"{next_player.name} 选择不质疑,游戏继续。")
# 切换至下一玩家
self.current_player_idx = next_idx
def start_game(self) -> None:
"""启动游戏主循环"""
self.deal_cards()
self.choose_target_card()
self.start_round_record()
while not self.game_over:
self.play_round()
if __name__ == '__main__':
# 配置玩家信息, 其中model为你通过API调用的模型名称
player_configs = [
{
"name": "DeepSeek",
"model": "deepseek-r1"
},
{
"name": "ChatGPT",
"model": "o3-mini"
},
{
"name": "Claude",
"model": "claude-3.7-sonnet"
},
{
"name": "Gemini",
"model": "gemini-2.0-flash-thinking"
}
]
print("游戏开始!玩家配置如下:")
for config in player_configs:
print(f"玩家:{config['name']}, 使用模型:{config['model']}")
print("-" * 50)
# 创建游戏实例并开始游戏
game = Game(player_configs)
game.start_game()