|
|
# -*- coding: utf-8 -*-
|
|
|
"""
|
|
|
三国狼人杀 - 基于AgentScope的中文版狼人杀游戏
|
|
|
融合三国演义角色和传统狼人杀玩法
|
|
|
"""
|
|
|
import asyncio
|
|
|
import os
|
|
|
import random
|
|
|
from typing import List, Dict, Optional
|
|
|
|
|
|
from agentscope.agent import ReActAgent
|
|
|
from agentscope.model import DashScopeChatModel
|
|
|
from agentscope.pipeline import MsgHub, sequential_pipeline, fanout_pipeline
|
|
|
from agentscope.formatter import DashScopeMultiAgentFormatter
|
|
|
|
|
|
from prompt_cn import ChinesePrompts
|
|
|
from game_roles import GameRoles
|
|
|
from structured_output_cn import (
|
|
|
DiscussionModelCN,
|
|
|
get_vote_model_cn,
|
|
|
WitchActionModelCN,
|
|
|
get_seer_model_cn,
|
|
|
get_hunter_model_cn,
|
|
|
WerewolfKillModelCN
|
|
|
)
|
|
|
from utils_cn import (
|
|
|
check_winning_cn,
|
|
|
majority_vote_cn,
|
|
|
get_chinese_name,
|
|
|
format_player_list,
|
|
|
GameModerator,
|
|
|
MAX_GAME_ROUND,
|
|
|
MAX_DISCUSSION_ROUND,
|
|
|
)
|
|
|
|
|
|
|
|
|
class ThreeKingdomsWerewolfGame:
|
|
|
"""三国狼人杀游戏主类"""
|
|
|
|
|
|
def __init__(self):
|
|
|
self.players: Dict[str, ReActAgent] = {}
|
|
|
self.roles: Dict[str, str] = {}
|
|
|
self.moderator = GameModerator()
|
|
|
self.alive_players: List[ReActAgent] = []
|
|
|
self.werewolves: List[ReActAgent] = []
|
|
|
self.villagers: List[ReActAgent] = []
|
|
|
self.seer: List[ReActAgent] = []
|
|
|
self.witch: List[ReActAgent] = []
|
|
|
self.hunter: List[ReActAgent] = []
|
|
|
|
|
|
# 女巫道具状态
|
|
|
self.witch_has_antidote = True
|
|
|
self.witch_has_poison = True
|
|
|
|
|
|
async def create_player(self, role: str, character: str) -> ReActAgent:
|
|
|
"""创建具有三国背景的玩家"""
|
|
|
name = get_chinese_name(character)
|
|
|
self.roles[name] = role
|
|
|
|
|
|
agent = ReActAgent(
|
|
|
name=name,
|
|
|
sys_prompt=ChinesePrompts.get_role_prompt(role, character),
|
|
|
model=DashScopeChatModel(
|
|
|
model_name="qwen-max",
|
|
|
api_key=os.environ["DASHSCOPE_API_KEY"],
|
|
|
enable_thinking=True,
|
|
|
),
|
|
|
formatter=DashScopeMultiAgentFormatter(),
|
|
|
)
|
|
|
|
|
|
# 角色身份确认
|
|
|
await agent.observe(
|
|
|
await self.moderator.announce(
|
|
|
f"【{name}】你在这场三国狼人杀中扮演{GameRoles.get_role_desc(role)},"
|
|
|
f"你的角色是{character}。{GameRoles.get_role_ability(role)}"
|
|
|
)
|
|
|
)
|
|
|
|
|
|
self.players[name] = agent
|
|
|
return agent
|
|
|
|
|
|
async def setup_game(self, player_count: int = 6):
|
|
|
"""设置游戏"""
|
|
|
print("🎮 开始设置三国狼人杀游戏...")
|
|
|
|
|
|
# 获取角色配置
|
|
|
roles = GameRoles.get_standard_setup(player_count)
|
|
|
characters = random.sample([
|
|
|
"刘备", "关羽", "张飞", "诸葛亮", "赵云",
|
|
|
"曹操", "司马懿", "周瑜", "孙权"
|
|
|
], player_count)
|
|
|
|
|
|
# 创建玩家
|
|
|
for i, (role, character) in enumerate(zip(roles, characters)):
|
|
|
agent = await self.create_player(role, character)
|
|
|
self.alive_players.append(agent)
|
|
|
|
|
|
# 分配到对应阵营
|
|
|
if role == "狼人":
|
|
|
self.werewolves.append(agent)
|
|
|
elif role == "预言家":
|
|
|
self.seer.append(agent)
|
|
|
elif role == "女巫":
|
|
|
self.witch.append(agent)
|
|
|
elif role == "猎人":
|
|
|
self.hunter.append(agent)
|
|
|
else:
|
|
|
self.villagers.append(agent)
|
|
|
|
|
|
# 游戏开始公告
|
|
|
await self.moderator.announce(
|
|
|
f"三国狼人杀游戏开始!参与者:{format_player_list(self.alive_players)}"
|
|
|
)
|
|
|
|
|
|
print(f"✅ 游戏设置完成,共{len(self.alive_players)}名玩家")
|
|
|
|
|
|
async def werewolf_phase(self, round_num: int):
|
|
|
"""狼人阶段"""
|
|
|
if not self.werewolves:
|
|
|
return None
|
|
|
|
|
|
await self.moderator.announce(f"🐺 狼人请睁眼,选择今晚要击杀的目标...")
|
|
|
|
|
|
# 狼人讨论
|
|
|
async with MsgHub(
|
|
|
self.werewolves,
|
|
|
enable_auto_broadcast=True,
|
|
|
announcement=await self.moderator.announce(
|
|
|
f"狼人们,请讨论今晚的击杀目标。存活玩家:{format_player_list(self.alive_players)}"
|
|
|
),
|
|
|
) as werewolves_hub:
|
|
|
# 讨论阶段
|
|
|
for _ in range(MAX_DISCUSSION_ROUND):
|
|
|
for wolf in self.werewolves:
|
|
|
await wolf(structured_model=DiscussionModelCN)
|
|
|
|
|
|
# 投票击杀
|
|
|
werewolves_hub.set_auto_broadcast(False)
|
|
|
kill_votes = await fanout_pipeline(
|
|
|
self.werewolves,
|
|
|
msg=await self.moderator.announce("请选择击杀目标"),
|
|
|
structured_model=WerewolfKillModelCN,
|
|
|
enable_gather=False,
|
|
|
)
|
|
|
|
|
|
# 统计投票
|
|
|
votes = {}
|
|
|
for i, vote_msg in enumerate(kill_votes):
|
|
|
votes[self.werewolves[i].name] = vote_msg.metadata.get("target")
|
|
|
|
|
|
killed_player, _ = majority_vote_cn(votes)
|
|
|
return killed_player
|
|
|
|
|
|
async def seer_phase(self):
|
|
|
"""预言家阶段"""
|
|
|
if not self.seer:
|
|
|
return
|
|
|
|
|
|
seer_agent = self.seer[0]
|
|
|
await self.moderator.announce("🔮 预言家请睁眼,选择要查验的玩家...")
|
|
|
|
|
|
check_result = await seer_agent(
|
|
|
structured_model=get_seer_model_cn(self.alive_players)
|
|
|
)
|
|
|
|
|
|
target_name = check_result.metadata.get("target")
|
|
|
target_role = self.roles.get(target_name, "村民")
|
|
|
|
|
|
# 告知预言家结果
|
|
|
result_msg = f"查验结果:{target_name}是{'狼人' if target_role == '狼人' else '好人'}"
|
|
|
await seer_agent.observe(await self.moderator.announce(result_msg))
|
|
|
|
|
|
async def witch_phase(self, killed_player: str):
|
|
|
"""女巫阶段"""
|
|
|
if not self.witch:
|
|
|
return killed_player, None
|
|
|
|
|
|
witch_agent = self.witch[0]
|
|
|
await self.moderator.announce("🧙♀️ 女巫请睁眼...")
|
|
|
|
|
|
# 告知女巫死亡信息
|
|
|
death_info = f"今晚{killed_player}被狼人击杀" if killed_player else "今晚平安无事"
|
|
|
await witch_agent.observe(await self.moderator.announce(death_info))
|
|
|
|
|
|
# 女巫行动
|
|
|
witch_action = await witch_agent(structured_model=WitchActionModelCN)
|
|
|
|
|
|
saved_player = None
|
|
|
poisoned_player = None
|
|
|
|
|
|
if witch_action.metadata.get("use_antidote") and self.witch_has_antidote:
|
|
|
if killed_player:
|
|
|
saved_player = killed_player
|
|
|
self.witch_has_antidote = False
|
|
|
await witch_agent.observe(await self.moderator.announce(f"你使用解药救了{killed_player}"))
|
|
|
|
|
|
if witch_action.metadata.get("use_poison") and self.witch_has_poison:
|
|
|
poisoned_player = witch_action.metadata.get("target_name")
|
|
|
if poisoned_player:
|
|
|
self.witch_has_poison = False
|
|
|
await witch_agent.observe(await self.moderator.announce(f"你使用毒药毒杀了{poisoned_player}"))
|
|
|
|
|
|
# 确定最终死亡玩家
|
|
|
final_killed = killed_player if not saved_player else None
|
|
|
|
|
|
return final_killed, poisoned_player
|
|
|
|
|
|
async def hunter_phase(self, shot_by_hunter: str):
|
|
|
"""猎人阶段"""
|
|
|
if not self.hunter:
|
|
|
return None
|
|
|
|
|
|
hunter_agent = self.hunter[0]
|
|
|
if hunter_agent.name == shot_by_hunter:
|
|
|
await self.moderator.announce("🏹 猎人发动技能,可以带走一名玩家...")
|
|
|
|
|
|
hunter_action = await hunter_agent(
|
|
|
structured_model=get_hunter_model_cn(self.alive_players)
|
|
|
)
|
|
|
|
|
|
if hunter_action.metadata.get("shoot"):
|
|
|
target = hunter_action.metadata.get("target")
|
|
|
await self.moderator.announce(f"猎人{hunter_agent.name}开枪带走了{target}")
|
|
|
return target
|
|
|
|
|
|
return None
|
|
|
|
|
|
def update_alive_players(self, dead_players: List[str]):
|
|
|
"""更新存活玩家列表"""
|
|
|
for dead_name in dead_players:
|
|
|
if dead_name:
|
|
|
# 从存活列表移除
|
|
|
self.alive_players = [p for p in self.alive_players if p.name != dead_name]
|
|
|
# 从各阵营移除
|
|
|
self.werewolves = [p for p in self.werewolves if p.name != dead_name]
|
|
|
self.villagers = [p for p in self.villagers if p.name != dead_name]
|
|
|
self.seer = [p for p in self.seer if p.name != dead_name]
|
|
|
self.witch = [p for p in self.witch if p.name != dead_name]
|
|
|
self.hunter = [p for p in self.hunter if p.name != dead_name]
|
|
|
|
|
|
async def day_phase(self, round_num: int):
|
|
|
"""白天阶段"""
|
|
|
await self.moderator.day_announcement(round_num)
|
|
|
|
|
|
# 讨论阶段
|
|
|
async with MsgHub(
|
|
|
self.alive_players,
|
|
|
enable_auto_broadcast=True,
|
|
|
announcement=await self.moderator.announce(
|
|
|
f"现在开始自由讨论。存活玩家:{format_player_list(self.alive_players)}"
|
|
|
),
|
|
|
) as all_hub:
|
|
|
# 每人发言一轮
|
|
|
await sequential_pipeline(self.alive_players)
|
|
|
|
|
|
# 投票阶段
|
|
|
all_hub.set_auto_broadcast(False)
|
|
|
vote_msgs = await fanout_pipeline(
|
|
|
self.alive_players,
|
|
|
await self.moderator.announce("请投票选择要淘汰的玩家"),
|
|
|
structured_model=get_vote_model_cn(self.alive_players),
|
|
|
enable_gather=False,
|
|
|
)
|
|
|
|
|
|
# 统计投票
|
|
|
votes = {}
|
|
|
for i, vote_msg in enumerate(vote_msgs):
|
|
|
votes[self.alive_players[i].name] = vote_msg.metadata.get("vote")
|
|
|
|
|
|
voted_out, vote_count = majority_vote_cn(votes)
|
|
|
await self.moderator.vote_result_announcement(voted_out, vote_count)
|
|
|
|
|
|
return voted_out
|
|
|
|
|
|
async def run_game(self):
|
|
|
"""运行游戏主循环"""
|
|
|
try:
|
|
|
await self.setup_game()
|
|
|
|
|
|
for round_num in range(1, MAX_GAME_ROUND + 1):
|
|
|
print(f"\n🌙 === 第{round_num}轮游戏开始 ===")
|
|
|
|
|
|
# 夜晚阶段
|
|
|
await self.moderator.night_announcement(round_num)
|
|
|
|
|
|
# 狼人击杀
|
|
|
killed_player = await self.werewolf_phase(round_num)
|
|
|
|
|
|
# 预言家查验
|
|
|
await self.seer_phase()
|
|
|
|
|
|
# 女巫行动
|
|
|
final_killed, poisoned_player = await self.witch_phase(killed_player)
|
|
|
|
|
|
# 更新死亡玩家
|
|
|
night_deaths = [p for p in [final_killed, poisoned_player] if p]
|
|
|
self.update_alive_players(night_deaths)
|
|
|
|
|
|
# 死亡公告
|
|
|
await self.moderator.death_announcement(night_deaths)
|
|
|
|
|
|
# 检查胜利条件
|
|
|
winner = check_winning_cn(self.alive_players, self.roles)
|
|
|
if winner:
|
|
|
await self.moderator.game_over_announcement(winner)
|
|
|
return
|
|
|
|
|
|
# 白天阶段
|
|
|
voted_out = await self.day_phase(round_num)
|
|
|
|
|
|
# 猎人技能
|
|
|
hunter_shot = await self.hunter_phase(voted_out)
|
|
|
|
|
|
# 更新死亡玩家
|
|
|
day_deaths = [p for p in [voted_out, hunter_shot] if p]
|
|
|
self.update_alive_players(day_deaths)
|
|
|
|
|
|
# 检查胜利条件
|
|
|
winner = check_winning_cn(self.alive_players, self.roles)
|
|
|
if winner:
|
|
|
await self.moderator.game_over_announcement(winner)
|
|
|
return
|
|
|
|
|
|
print(f"第{round_num}轮结束,存活玩家:{format_player_list(self.alive_players)}")
|
|
|
|
|
|
except Exception as e:
|
|
|
print(f"❌ 游戏运行出错:{e}")
|
|
|
import traceback
|
|
|
traceback.print_exc()
|
|
|
|
|
|
|
|
|
async def main():
|
|
|
"""主函数"""
|
|
|
# 检查环境变量
|
|
|
if "DASHSCOPE_API_KEY" not in os.environ:
|
|
|
print("❌ 请设置环境变量 DASHSCOPE_API_KEY")
|
|
|
return
|
|
|
|
|
|
print("🎮 欢迎来到三国狼人杀!")
|
|
|
|
|
|
# 创建并运行游戏
|
|
|
game = ThreeKingdomsWerewolfGame()
|
|
|
await game.run_game()
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
asyncio.run(main())
|