mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-05-05 05:42:37 +02:00
Merge pull request #365 from mannaandpoem/manna_werewolf_game
Werewolf game modify moderator and add seer role and action
This commit is contained in:
commit
50d9ad4270
9 changed files with 442 additions and 59 deletions
|
|
@ -1,8 +1,10 @@
|
|||
from examples.werewolf_game.actions.moderator_actions import InstructSpeak
|
||||
from examples.werewolf_game.actions.common_actions import Speak
|
||||
from examples.werewolf_game.actions.werewolf_actions import Hunt
|
||||
from examples.werewolf_game.actions.seer_actions import Verify
|
||||
|
||||
ACTIONS = {
|
||||
"Speak": Speak,
|
||||
"Hunt": Hunt,
|
||||
"Verify": Verify,
|
||||
}
|
||||
|
|
@ -1,49 +1,267 @@
|
|||
import asyncio
|
||||
import collections
|
||||
from random import random
|
||||
|
||||
from metagpt.actions import Action
|
||||
|
||||
STAGE_INSTRUCTIONS = {
|
||||
STEP_INSTRUCTIONS = {
|
||||
# 上帝需要介入的全部步骤和对应指令
|
||||
# The 1-st night
|
||||
0: {"content": "It’s dark, everyone close your eyes. I will talk with you/your team secretly at night.",
|
||||
"send_to": "Moderator", # for moderator to continuen speaking
|
||||
"send_to": "Moderator", # for moderator to continuen speaking
|
||||
"restricted_to": ""},
|
||||
1: {"content": "Werewolves, please open your eyes!",
|
||||
"send_to": "Moderator", # for moderator to continuen speaking
|
||||
1: {"content": "Guard, please open your eyes!",
|
||||
"send_to": "Moderator", # for moderator to continuen speaking
|
||||
"restricted_to": ""},
|
||||
2: {"content": """Werewolves, I secretly tell you that Player 3 and Player 4 are
|
||||
2: {"content": """Guard, now tell me who you protect tonight?
|
||||
You only choose one from the following living options please: {living_players}. Or you can pass. For example: I protect ...""",
|
||||
"send_to": "Guard",
|
||||
"restricted_to": "Guard"},
|
||||
3: {"content": "Guard, close your eyes",
|
||||
"send_to": "Moderator",
|
||||
"restricted_to": ""},
|
||||
4: {"content": "Werewolves, please open your eyes!",
|
||||
"send_to": "Moderator",
|
||||
"restricted_to": ""},
|
||||
5: {"content": """Werewolves, I secretly tell you that {werewolf_players} are
|
||||
all of the 2 werewolves! Keep in mind you are teammates. The rest players are not werewolves.
|
||||
choose one from the following living options please:
|
||||
[Player 1, Player2]. """, # send to werewolf restrictedly for a response
|
||||
{living_players}. For example: I kill ...""",
|
||||
"send_to": "Werewolf",
|
||||
"restricted_to": "Werewolf"},
|
||||
3: {"content": "Werewolves, close your eyes",
|
||||
"send_to": "Moderator", # for moderator to continuen speaking
|
||||
6: {"content": "Werewolves, close your eyes",
|
||||
"send_to": "Moderator",
|
||||
"restricted_to": ""},
|
||||
4: {"content": """It's daytime. No one dies last night. Now freely talk about roles of other players with each other based on your observation and reflection
|
||||
with few sentences. Decide whether to reveal your identity based on your reflection.""",
|
||||
"send_to": "", # send to all to speak in daytime
|
||||
"restricted_to": ""}
|
||||
7: {"content": "Witch, please open your eyes!",
|
||||
"send_to": "Moderator",
|
||||
"restricted_to": ""},
|
||||
8: {"content": """Witch, tonight {killed_player} has been killed by the werewolves.
|
||||
You have a bottle of antidote, would you like to save him/her? If not, simply Pass.""",
|
||||
"send_to": "Witch",
|
||||
"restricted_to": "Witch"}, # 要先判断女巫是否有解药,再去询问女巫是否使用解药救人
|
||||
9: {"content": """Witch, you also have a bottle of poison, would you like to use it to kill one of the living players?
|
||||
Choose one from the following living options: {living_players}. If not, simply Pass.""",
|
||||
"send_to": "Witch",
|
||||
"restricted_to": "Witch"}, #
|
||||
10: {"content": "Witch, close your eyes",
|
||||
"send_to": "Moderator",
|
||||
"restricted_to": ""},
|
||||
11: {"content": "Seer, please open your eyes!",
|
||||
"send_to": "Moderator",
|
||||
"restricted_to": ""},
|
||||
12: {"content": """Seer, you can check one player's identity. Who are you going to verify its identity tonight?
|
||||
Choose only one from the following living options:{living_players}.""",
|
||||
"send_to": "Seer",
|
||||
"restricted_to": "Seer"},
|
||||
13: {"content": "Seer, close your eyes",
|
||||
"send_to": "Moderator",
|
||||
"restricted_to": ""},
|
||||
# The 1-st daytime
|
||||
14: {"content": """It's daytime. Everyone woke up except those who had been killed.""",
|
||||
"send_to": "Moderator",
|
||||
"restricted_to": ""},
|
||||
15: {"content": "{killed_player} was killed last night. Or, it was a peaceful night and no one died!",
|
||||
"send_to": "Moderator",
|
||||
"restricted_to": ""},
|
||||
16: {"content": """Now freely talk about roles of other players with each other based on your observation and
|
||||
reflection with few sentences. Decide whether to reveal your identity based on your reflection.""",
|
||||
"send_to": "", # send to all to speak in daytime
|
||||
"restricted_to": ""},
|
||||
17: {"content": """Now vote and tell me who you think is the werewolf. Don’t mention your role.
|
||||
You only choose one from the following living options please:
|
||||
{living_players}. Or you can pass. For example: I vote to kill ...""",
|
||||
"send_to": "",
|
||||
"restricted_to": ""},
|
||||
18: {"content": """{voted_out_player} was eliminated.""",
|
||||
"send_to": "Moderator",
|
||||
"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):
|
||||
super().__init__(name, context, llm)
|
||||
|
||||
async def run(self, context, stage_idx):
|
||||
return STAGE_INSTRUCTIONS[stage_idx]
|
||||
async def run(self, step_idx, living_players, werewolf_players, killed_player, voted_out_player):
|
||||
instruction_info = STEP_INSTRUCTIONS.get(step_idx, {
|
||||
"content": "Unknown instruction.",
|
||||
"send_to": "",
|
||||
"restricted_to": ""
|
||||
})
|
||||
content = instruction_info["content"]
|
||||
if "{living_players}" in content and "{werewolf_players}" in content:
|
||||
content = content.format(living_players=",".join(living_players),
|
||||
werewolf_players=",".join(werewolf_players))
|
||||
if "{living_players}" in content:
|
||||
content = content.format(living_players=",".join(living_players))
|
||||
if "{werewolf_players}" in content:
|
||||
content = content.format(werewolf_players=",".join(werewolf_players))
|
||||
if "{killed_player}" in content:
|
||||
content = content.format(killed_player=killed_player)
|
||||
if "{voted_out_player}" in content:
|
||||
content = content.format(voted_out_player=voted_out_player)
|
||||
|
||||
return content, instruction_info["send_to"], instruction_info["restricted_to"]
|
||||
|
||||
|
||||
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):
|
||||
"""consider all events at night, conclude which player dies (can be a peaceful night)"""
|
||||
pass
|
||||
|
||||
def __init__(self, name="SummarizeNight", context=None, llm=None):
|
||||
super().__init__(name, context, llm)
|
||||
|
||||
async def run(self, events):
|
||||
# 假设events是一个字典,代表夜晚发生的多个事件,key是事件类型,value是该事件对应的玩家
|
||||
# 例如被狼人杀的玩家:{"killed_by_werewolves": "Player1"}
|
||||
# 被守卫守护的玩家:{"protected_by_guard": "Player2"}
|
||||
# 被女巫救的玩家:{"saved_by_witch": "Player3"}
|
||||
# 被女巫毒的玩家:{"poisoned_by_witch": "Player4"}
|
||||
# 被预言家查验的玩家:{"verified_by_seer": "Player5"}
|
||||
# 若没有事件发生,则events为空字典
|
||||
killed_by_werewolves = events.get("killed_by_werewolves", "")
|
||||
protected_by_guard = events.get("protected_by_guard", "")
|
||||
saved_by_witch = events.get("saved_by_witch", "")
|
||||
poisoned_by_witch = events.get("poisoned_by_witch", "")
|
||||
|
||||
# 若狼人杀的人和守卫守的人是同一个人,那么该人就会活着;
|
||||
if protected_by_guard and killed_by_werewolves and protected_by_guard == killed_by_werewolves:
|
||||
return "It was a peaceful night. No one was killed."
|
||||
|
||||
# 若守卫和女巫都救了同一个人,那么该人就会死
|
||||
if protected_by_guard and saved_by_witch and protected_by_guard == saved_by_witch:
|
||||
return f"{protected_by_guard} was killed by the werewolves."
|
||||
|
||||
if saved_by_witch:
|
||||
return f"{saved_by_witch} was saved by the witch."
|
||||
|
||||
if poisoned_by_witch:
|
||||
return f"{poisoned_by_witch} was poisoned by the witch."
|
||||
|
||||
if killed_by_werewolves:
|
||||
return f"{killed_by_werewolves} was killed by the werewolves."
|
||||
|
||||
|
||||
class SummarizeDay(Action):
|
||||
"""consider all votes at day, conclude which player dies"""
|
||||
pass
|
||||
|
||||
def __init__(self, name="SummarizeDay", context=None, llm=None):
|
||||
super().__init__(name, context, llm)
|
||||
|
||||
async def run(self, votes):
|
||||
# 假设votes是一个字典,代表白天投票的结果,key是被投票的玩家,value是得票数
|
||||
# 例如:{"Player1": 2, "Player2": 1, "Player3": 1, "Player4": 0}
|
||||
# 表示Player1得到2票,Player2和Player3各得到1票,Player4得到0票
|
||||
# 若平票,则随机选一个人出局
|
||||
if not votes:
|
||||
return "No votes were cast. No one was killed."
|
||||
|
||||
max_votes = max(votes.values())
|
||||
players_with_max_votes = [player for player, vote_count in votes.items() if vote_count == max_votes]
|
||||
|
||||
if len(players_with_max_votes) == 1:
|
||||
eliminated_player = players_with_max_votes[0]
|
||||
return f"{eliminated_player} was voted out and eliminated."
|
||||
else:
|
||||
# 若平票,则随机选一个人出局
|
||||
eliminated_player = players_with_max_votes[int(random() * len(players_with_max_votes))]
|
||||
return f"There was a tie in the votes. {eliminated_player} was randomly chosen and eliminated."
|
||||
|
||||
|
||||
class AnnounceGameResult(Action):
|
||||
|
||||
async def run(self, winner: str):
|
||||
return f"Game over! The winner is {winner}"
|
||||
|
||||
|
||||
async def main():
|
||||
rst1 = await SummarizeDay().run({"Player1": 0, "Player2": 0, "Player3": 0, "Player4": 0})
|
||||
rst2 = await SummarizeNight().run({"killed_by_werewolves": "Player1"})
|
||||
# 表示第2个步骤的指令,living_players是所有活着的玩家,werewolf_players是所有狼人,killed_player是被杀的玩家,voted_out_player是被投票出局的玩家
|
||||
rst3 = await InstructSpeak().run(2, ["Player1", "Player2", "Player3", "Player4"],
|
||||
["Player3", "Player4"], "Player4", "Player3")
|
||||
print(rst1)
|
||||
print(rst2)
|
||||
print(rst3)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
|
|
|
|||
22
examples/werewolf_game/actions/seer_actions.py
Normal file
22
examples/werewolf_game/actions/seer_actions.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
from metagpt.actions import Action
|
||||
|
||||
|
||||
class Verify(Action):
|
||||
"""Action: Seer verifies a player's identity at night"""
|
||||
|
||||
PROMPT_TEMPLATE = """
|
||||
It's a werewolf game and you are a seer.
|
||||
You can choose to verify the identity of a player.
|
||||
Here's the game history:
|
||||
{context}.
|
||||
Now, choose one player to verify:
|
||||
"""
|
||||
|
||||
def __init__(self, name="Verify", context=None, llm=None):
|
||||
super().__init__(name, context, llm)
|
||||
|
||||
async def run(self, context: str):
|
||||
prompt = self.PROMPT_TEMPLATE.format(context=context)
|
||||
rsp = await self._aask(prompt)
|
||||
|
||||
return rsp
|
||||
85
examples/werewolf_game/prompts/prompts.py
Normal file
85
examples/werewolf_game/prompts/prompts.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# 论文中出现的提示语,利用了思维链
|
||||
# 1. 基于游戏规则和对话内容,选择5个问题
|
||||
# 2. 生成2个问题
|
||||
# 3. 生成可能的回答
|
||||
# 4. 进行反思
|
||||
# 5. 生成最终回复
|
||||
|
||||
GAME_RULE = '''You are playing a game called the Werewolf with some other players. This game is based on text conversations. Here are
|
||||
the game rules: Roles: The moderator is also host, he organised this game and you need to answer his instructions correctly.
|
||||
Don’t talk with the moderator. There are five roles in the game, werewolf, villager, seer, guard and witch. There are two
|
||||
alternate phases in this game, daytime and dark. When it’s dark: Your talking content with moderator is confidential. You
|
||||
needn’t worry about other players and moderator knowing what you say and do. No need to worry about suspicions from
|
||||
others during the night. If you are werewolf, you can know what your teammates want to kill and you should vote one player
|
||||
to kill based on your analysis. Player who receives the most votes after all werewolves voting will be killed. No one will be
|
||||
killed if there is no consensus! If you are witch, you have a bottle of antidote that can save a player targeted by werewolves
|
||||
after dark, and a bottle of poison that can poison a player after dark. Both poison and antidote can be used only once. If you
|
||||
are seer, you can verify whether a player is a werewolf every night, which is a very important thing. If you are guard, you
|
||||
can protect a player every night to prevent the player from being killed by werewolves, but guard cannot resist the witch’s
|
||||
poison and guard cannot protect the same player on two consecutive nights. Villagers can’t do anything at night. During the
|
||||
daytime: you discuss with all players including your enemies. At the end of the discussion, players vote to eliminate one
|
||||
player they suspect of being a werewolf. The player with the most votes will be eliminated. The moderator will tell who is
|
||||
killed, otherwise there is no one killed. Note that villager, seer, guard and witch are all in villager side, they have the same
|
||||
objective. Objectives: If you are werewolf, your goal is to cooperate with other werewolves to kill all players who are not
|
||||
werewolves at last. If you are not werewolf, you need to kill all werewolves with your partner once you find out that certain
|
||||
players are suspicious to be werewolves. This could greatly improve your chances of winning, although it is somewhat risky.If
|
||||
one player is killed, he can’t do anything anymore and will be out of the game. Tips: To complete the objective: During
|
||||
night, you should analyze and use your ability correctly. During daytime, you need to reason carefully about the roles of other
|
||||
players and be careful not to reveal your own role casually unless you’re cheating other players. Only give the player’s name
|
||||
when making a decision/voting, and don’t generate other players’ conversation.Reasoning based on facts you have observed
|
||||
and you cannot perceive information (such as acoustic info) other than text. You are Player {name}, the {profile}.
|
||||
You’re playing with 6 other players. Do not pretend you are other players or the moderator.
|
||||
'''
|
||||
|
||||
SELECT_QUESTIONS = '''
|
||||
Now its the {t}-th {day_or_night}. Given the game rules and conversations above, assuming you are {agent_name}, the
|
||||
{role}, and to complete the instructions of the moderator, you need to think about a few questions clearly first, so that you can
|
||||
make an accurate decision on the next step. Choose only five that you think are the most important in the current situation
|
||||
from the list of questions below: {questions_prepared_for_specific_role} Please repeat the five important questions of your
|
||||
choice, separating them with ‘##’.
|
||||
'''
|
||||
|
||||
# 为特定的角色,准备的问题
|
||||
questions_prepared_for_specific_role_sample = '''
|
||||
1. What is my player name and what is my role? What is my final objective in this game?
|
||||
2. Based on the chat history, can you guess what some players’ role might be?
|
||||
3. What is the current phase, daytime or night? what should I do at this phase according to the game rules?
|
||||
4. Based on the conversation and my inference, who is most likely to be an alive werewolf?
|
||||
5. I want to know who the most suspicious player, and why?
|
||||
6. I also want to know if any player’s behavior has changed suspiciously compared to the previous days, and if so, who and why?
|
||||
7. What is the best strategy I should use right now to uncover werewolves without revealing my own role? Should I accuse someone directly, ask probing questions, or stay silent for now?
|
||||
8. Have any players claimed specific roles that can be verified or disputed?
|
||||
'''
|
||||
|
||||
ASK_QUESTIONS = '''
|
||||
Now its the {t}-th {day_or_night}. Given the game rules and conversations above, assuming you are {agent_name}, the
|
||||
{role}, and to complete the instructions of the moderator, you need to think about a few questions clearly first, so that you can
|
||||
make an accurate decision on the next step. {selected_questions} Do not answer these queations. In addition to the above
|
||||
questions, please make a bold guess, what else do you want to know about the current situation? Please ask two important
|
||||
questions in first person, separating them with ‘##’.
|
||||
'''
|
||||
|
||||
GENERATE_POSSIBLE_ANSWER = '''
|
||||
Now its the {t}-th {day_or_night}. Given the game rules and conversations above, assuming you are {agent_name}, the
|
||||
{role}, for question: {question} There are some possible answers: {candidate_answers} Generate the correct answer
|
||||
based on the context. If there is not direct answer, you should think and generate the answer based on the context. No need to
|
||||
give options. The answer should in first person using no more than 2 sentences and without any analysis and item numbers.
|
||||
'''
|
||||
|
||||
REFLECTION = '''
|
||||
Now its the {t}-th {day_or_night}. Assuming you are {agent_name}, the {role}, what insights can you summarize
|
||||
with few sentences based on the above conversations and {At} in heart for helping continue the talking and achieving your
|
||||
objective? For example: As the {role}, I observed that... I think that... But I am... So...
|
||||
'''
|
||||
|
||||
# 得到最终的回复,再抽取出最终的content
|
||||
GENERATE_FINAL_RESPONSE = '''
|
||||
Now its the {t}-th {day_or_night}. Think about what to say based on the game rules and context, especially the just now
|
||||
reflection {R}.
|
||||
Give your step-by-step thought process and your derived consise talking content (no more than 2 sentences) at last, separating them with ‘##’.
|
||||
For example:
|
||||
## Thought process
|
||||
My step-by-step thought process:...
|
||||
## Content
|
||||
My concise talking content: ...
|
||||
'''
|
||||
|
|
@ -2,3 +2,4 @@ from examples.werewolf_game.roles.base_player import BasePlayer
|
|||
from examples.werewolf_game.roles.moderator import Moderator
|
||||
from examples.werewolf_game.roles.villager import Villager
|
||||
from examples.werewolf_game.roles.werewolf import Werewolf
|
||||
from examples.werewolf_game.roles.seer import Seer
|
||||
|
|
|
|||
|
|
@ -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 # 初始状态为活着
|
||||
|
||||
|
|
|
|||
|
|
@ -4,50 +4,66 @@ from metagpt.roles import Role
|
|||
from metagpt.schema import Message
|
||||
from metagpt.logs import logger
|
||||
from examples.werewolf_game.actions.moderator_actions import (
|
||||
InstructSpeak, ParseSpeak, AnnounceGameResult, STAGE_INSTRUCTIONS
|
||||
InstructSpeak, ParseSpeak, AnnounceGameResult, STEP_INSTRUCTIONS
|
||||
)
|
||||
from metagpt.actions import BossRequirement as UserRequirement
|
||||
|
||||
class Moderator(Role):
|
||||
|
||||
class Moderator(Role):
|
||||
# 游戏状态属性
|
||||
is_game_over = False
|
||||
winner = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "Moderator",
|
||||
profile: str = "Moderator",
|
||||
**kwargs,
|
||||
self,
|
||||
name: str = "Moderator",
|
||||
profile: str = "Moderator",
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(name, profile, **kwargs)
|
||||
self._watch([UserRequirement, InstructSpeak, ParseSpeak])
|
||||
self._init_actions([InstructSpeak, ParseSpeak, AnnounceGameResult])
|
||||
self.stage_idx = 0
|
||||
|
||||
self.step_idx = 0
|
||||
self.living_players = ["Player1", "Player2", "Player3", "Player4", "Player5"]
|
||||
self.werewolf_players = ["Player1", "Player2"]
|
||||
self.good_guys = ["Player3", "Player4", "Player5"]
|
||||
self.dead_players = [] # 夜晚阶段,死掉的玩家
|
||||
# 假设votes代表白天投票的结果,key是被投票的玩家,value是得票数
|
||||
self.votes = {"Player1": 1, "Player2": 2, "Player3": 1, "Player4": 0, "Player5": 0}
|
||||
|
||||
async def _instruct_speak(self):
|
||||
stage_idx = self.stage_idx % len(STAGE_INSTRUCTIONS)
|
||||
step_idx = self.step_idx % len(STEP_INSTRUCTIONS)
|
||||
self.step_idx += 1
|
||||
return await InstructSpeak().run(step_idx,
|
||||
living_players=self.living_players,
|
||||
werewolf_players=self.werewolf_players,
|
||||
killed_player=self.dead_players,
|
||||
voted_out_player="Player3")
|
||||
|
||||
stage_info = await InstructSpeak().run(context="", stage_idx=stage_idx)
|
||||
async def _parse_speak(self, memories, env):
|
||||
|
||||
self.stage_idx += 1
|
||||
self.dead_players, vote_player, parse_info = await ParseSpeak().run(dead_history=self.dead_players,
|
||||
context=memories, env=env)
|
||||
|
||||
return stage_info["content"], stage_info["send_to"], stage_info["restricted_to"]
|
||||
|
||||
async def _parse_speak(self):
|
||||
# 解析玩家消息并返回结果
|
||||
parse_result = await ParseSpeak().run()
|
||||
|
||||
# 理解结果,更新各角色状态、游戏状态
|
||||
|
||||
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):
|
||||
|
||||
if self.is_game_over:
|
||||
self._rc.todo = AnnounceGameResult()
|
||||
return
|
||||
|
||||
|
||||
# 确定当前是需要InstructSpeak还是ParseSpeak. 通过判断当前流程状态变量,以及消息的cause_by属性
|
||||
# 0: InstructSpeak, 1: ParseSpeak,且需要判断消息的cause_by属性
|
||||
if self._rc.memory.get()[-1].role in ["User", self.profile]:
|
||||
|
|
@ -55,7 +71,7 @@ class Moderator(Role):
|
|||
# 2. 上一轮消息是Moderator自己的指令,继续发出指令,一个事情可以分几条消息来说
|
||||
# 3. 上一轮消息是Moderator自己的解析消息,一个阶段结束,发出新一个阶段的指令
|
||||
self._rc.todo = InstructSpeak()
|
||||
|
||||
|
||||
else:
|
||||
# 上一轮消息是游戏角色的发言,解析角色的发言
|
||||
self._rc.todo = ParseSpeak()
|
||||
|
|
@ -71,20 +87,21 @@ class Moderator(Role):
|
|||
if isinstance(todo, InstructSpeak):
|
||||
msg_content, msg_to_send_to, msg_restriced_to = await self._instruct_speak()
|
||||
msg = Message(content=msg_content, role=self.profile, sent_from=self.name,
|
||||
cause_by=InstructSpeak, send_to=msg_to_send_to, restricted_to=msg_restriced_to)
|
||||
|
||||
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)
|
||||
msg = Message(content=msg_content, role=self.profile, sent_from=self.name, cause_by=AnnounceGameResult)
|
||||
|
||||
|
||||
logger.info(f"{self._setting}: {msg_content}")
|
||||
|
||||
|
||||
return msg
|
||||
|
||||
|
||||
def get_all_memories(self) -> str:
|
||||
memories = self._rc.memory.get()
|
||||
memories = [str(m) for m in memories]
|
||||
|
|
|
|||
44
examples/werewolf_game/roles/seer.py
Normal file
44
examples/werewolf_game/roles/seer.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
from examples.werewolf_game.actions.seer_actions import Verify
|
||||
from examples.werewolf_game.roles.base_player import BasePlayer
|
||||
from examples.werewolf_game.actions import Speak
|
||||
from metagpt.schema import Message
|
||||
from metagpt.logs import logger
|
||||
|
||||
|
||||
class Seer(BasePlayer):
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "",
|
||||
profile: str = "Seer",
|
||||
team: str = "good guys",
|
||||
special_action_names: list[str] = ["Verify"],
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(name, profile, team, special_action_names, **kwargs)
|
||||
|
||||
async def _act(self):
|
||||
todo = self._rc.todo
|
||||
logger.info(f"{self._setting}: ready to {str(todo)}")
|
||||
|
||||
memories = self.get_all_memories()
|
||||
print("*" * 10, f"{self._setting}'s current memories: {memories}", "*" * 10)
|
||||
|
||||
# 基于todo的类型,调用不同的action
|
||||
if isinstance(todo, Speak):
|
||||
rsp = await todo.run(profile=self.profile, context=memories)
|
||||
msg = Message(
|
||||
content=rsp, role=self.profile, sent_from=self.name,
|
||||
cause_by=Speak, send_to="", restricted_to="",
|
||||
)
|
||||
|
||||
elif isinstance(todo, Verify):
|
||||
rsp = await todo.run(context=memories)
|
||||
msg = Message(
|
||||
content=rsp, role=self.profile, sent_from=self.name,
|
||||
cause_by=Verify, send_to="",
|
||||
restricted_to="Moderator",
|
||||
)
|
||||
|
||||
logger.info(f"{self._setting}: {rsp}")
|
||||
|
||||
return msg
|
||||
|
|
@ -3,14 +3,15 @@ import platform
|
|||
import fire
|
||||
|
||||
from examples.werewolf_game.werewolf_game import WerewolfGame
|
||||
from examples.werewolf_game.roles import Moderator, Villager, Werewolf
|
||||
from examples.werewolf_game.roles import Moderator, Villager, Werewolf, Seer
|
||||
|
||||
DEFAULT_PLAYER_SETUP = """
|
||||
Game setup:
|
||||
Player1: Villager,
|
||||
Player2: Villager,
|
||||
Player3: Werewolf,
|
||||
Player4: Werewolf.
|
||||
Player4: Werewolf,
|
||||
Player5: Seer.
|
||||
"""
|
||||
|
||||
async def start_game(idea: str = DEFAULT_PLAYER_SETUP, investment: float = 3.0, n_round: int = 5):
|
||||
|
|
@ -21,6 +22,7 @@ async def start_game(idea: str = DEFAULT_PLAYER_SETUP, investment: float = 3.0,
|
|||
Villager(name="Player2"),
|
||||
Werewolf(name="Player3"),
|
||||
Werewolf(name="Player4"),
|
||||
Seer(name="Player5"),
|
||||
])
|
||||
game.invest(investment)
|
||||
game.start_project(idea)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue