From e5140fd6a581febc7461dff952a6bee71daf659d Mon Sep 17 00:00:00 2001 From: mannaandpoem <1580466765@qq.com> Date: Tue, 26 Sep 2023 23:05:45 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86ivan=E5=86=99?= =?UTF-8?q?=E7=9A=84parse=20speak?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../actions/moderator_actions.py | 79 ++++++++++++++++++- examples/werewolf_game/roles/base_player.py | 10 +-- examples/werewolf_game/roles/moderator.py | 33 +++++--- 3 files changed, 100 insertions(+), 22 deletions(-) diff --git a/examples/werewolf_game/actions/moderator_actions.py b/examples/werewolf_game/actions/moderator_actions.py index 78b6fd1af..5fd3ede93 100644 --- a/examples/werewolf_game/actions/moderator_actions.py +++ b/examples/werewolf_game/actions/moderator_actions.py @@ -1,4 +1,5 @@ import asyncio +import collections from random import random from metagpt.actions import Action @@ -77,6 +78,42 @@ STEP_INSTRUCTIONS = { "restricted_to": ""}, } +ROLE_STATES = { + # 存活状态 + 0: "Alive", # 开场 + 1: "Dead", # 结束 + 2: "Protected", # 被保护 + 3: "Poisoned", # 被毒 + 4: "Saved", # 被救 + 5: "Killed" # 被刀 +} + +VOTE_PROMPT = """ + Welcome to the daytime discussion phase in the Werewolf game. + + During the day, players discuss and share information about who they suspect might be a werewolf. + Players can also cast their votes to eliminate a player they believe is a werewolf. + + Here are the conversations from the daytime: + + {vote_message} + + Now it's time to cast your votes. + + You can vote for a player by typing their name. + Example: "Vote for Player2" + + Here are the voting options: +""" + +PARSE_INSTRUCTIONS = { + 0: "Now it's time to vote", + 1: "The {winner} have won! They successfully eliminated all the {loser}}." + "The game has ended. Thank you for playing Werewolf!", + 2: "The night has ended, and it's time to reveal the casualties." + "During the night, the Werewolves made their move. Unfortunately, they targeted {PlayerName}, who is now dead." +} + class InstructSpeak(Action): def __init__(self, name="InstructSpeak", context=None, llm=None): @@ -105,8 +142,46 @@ class InstructSpeak(Action): class ParseSpeak(Action): - async def run(self): - return "" + def __init__(self, name="ParseSpeak", context=None, llm=None): + super().__init__(name, context, llm) + self.daytime_info = collections.defaultdict(list) + self.night_info = collections.defaultdict(list) + self.vote_message = [] + + async def run(self, dead_history, context, env): + + for m in env.memory.get(): + role = m.sent_from if hasattr(m, 'sent_from') else "" + content = m.content if hasattr(m, 'content') else "" + target = m.sent_to if hasattr(m, 'sent_to') else "" + restricted = m.restricted_to if hasattr(m, 'restricted_to') else "" + if target == 'all': + self.daytime_info[role] = [content, target, restricted] + else: + self.night_info[role] = [content, target, restricted] + + # collect info from the night and identify the dead player + for role in self.night_info: + if "kill" in self.night_info[role][0] and self.night_info[role][1]: + target = self.night_info[role][1] + print("env.get_roles[target]", env, env.env.roles) + env.env.roles[target].set_status(ROLE_STATES[5]) + for role in self.night_info: + if ("save" or "guard") in self.night_info[role][0]: + save_target = self.night_info[role][1] + if save_target == target: + env.env.roles[target].set_status(ROLE_STATES[0]) + else: + dead_history.append(target) + + # collect message from the daytime and identify the vote player + for role in self.daytime_info: + self.vote_message += f"\n{self.daytime_info[role][0]}" + + vote_player = await self.llm.aask(VOTE_PROMPT.format(vote_message=self.vote_message)) + dead_history.append(vote_player) + + return dead_history, vote_player, PARSE_INSTRUCTIONS class SummarizeNight(Action): diff --git a/examples/werewolf_game/roles/base_player.py b/examples/werewolf_game/roles/base_player.py index 05daa9797..c3e60372c 100644 --- a/examples/werewolf_game/roles/base_player.py +++ b/examples/werewolf_game/roles/base_player.py @@ -3,14 +3,6 @@ from metagpt.schema import Message from metagpt.logs import logger from examples.werewolf_game.actions import ACTIONS, Speak, InstructSpeak -ROLE_STATES = { - # 存活状态 - 0: "Alive", # 开场 - 1: "Dead", # 结束 - 2: "Protected", # 被保护 - 3: "Poisoned", # 被毒 - 4: "Saved", # 被救 -} class BasePlayer(Role): def __init__( @@ -24,7 +16,7 @@ class BasePlayer(Role): super().__init__(name, profile, **kwargs) self._init_actions([Speak]) self._watch([InstructSpeak]) - self.team = team + self.team = team # 调用 get_status() 来检查存活状态,并通过 set_status() 更新状态。 self.status = 0 # 初始状态为活着 diff --git a/examples/werewolf_game/roles/moderator.py b/examples/werewolf_game/roles/moderator.py index 2069c5f41..4c1334b3c 100644 --- a/examples/werewolf_game/roles/moderator.py +++ b/examples/werewolf_game/roles/moderator.py @@ -26,8 +26,8 @@ class Moderator(Role): self.step_idx = 0 self.living_players = ["Player1", "Player2", "Player3", "Player4", "Player5"] self.werewolf_players = ["Player1", "Player2"] - self.killed_player = "Player4" # 夜晚阶段,死掉的玩家 - self.voted_out_player = "Player3" # 白天阶段,被投票出局的玩家 + self.good_guys = ["Player3", "Player4", "Player5"] + self.dead_players = [] # 夜晚阶段,死掉的玩家 # 假设votes代表白天投票的结果,key是被投票的玩家,value是得票数 self.votes = {"Player1": 1, "Player2": 2, "Player3": 1, "Player4": 0, "Player5": 0} @@ -37,16 +37,26 @@ class Moderator(Role): return await InstructSpeak().run(step_idx, living_players=self.living_players, werewolf_players=self.werewolf_players, - killed_player=self.killed_player, - voted_out_player=self.voted_out_player) + killed_player=self.dead_players, + voted_out_player="Player3") - async def _parse_speak(self): - # 解析玩家消息并返回结果 - parse_result = await ParseSpeak().run() + async def _parse_speak(self, memories, env): - # 理解结果,更新各角色状态、游戏状态 + self.dead_players, vote_player, parse_info = await ParseSpeak().run(dead_history=self.dead_players, + context=memories, env=env) - return "Player message processed" + # decide to move the game into the next phase + if not vote_player: + msg_content, send_to = parse_info[0], self.profile + # game's termination condition + elif all(item in self.dead_players for item in self.werewolf_players) or all( + item in self.dead_players for item in self.good_guys): + self.is_game_over = True + msg_content, send_to = parse_info[1], "all" + else: + # game's termination condition + msg_content, send_to = parse_info[2], "" + return msg_content, send_to async def _think(self): @@ -80,8 +90,9 @@ class Moderator(Role): cause_by=InstructSpeak, send_to=msg_to_send_to, restricted_to=msg_restriced_to) elif isinstance(todo, ParseSpeak): - msg_content = await self._parse_speak() - msg = Message(content=msg_content, role=self.profile, sent_from=self.name, cause_by=ParseSpeak) + msg_content, send_to = await self._parse_speak(memories, self._rc) + msg = Message(content=msg_content, role=self.profile, sent_from=self.name, cause_by=ParseSpeak, + send_to=send_to) elif isinstance(todo, AnnounceGameResult): msg_content = await AnnounceGameResult().run(winner=self.winner)