mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-06-11 15:15:18 +02:00
rm redundant & add random game setup
This commit is contained in:
parent
4d39bb2815
commit
fa88e44521
10 changed files with 95 additions and 162 deletions
|
|
@ -13,8 +13,9 @@ STEP_INSTRUCTIONS = {
|
|||
1: {"content": "Guard, please open your eyes!",
|
||||
"send_to": "Moderator", # for moderator to continuen speaking
|
||||
"restricted_to": ""},
|
||||
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 ...""",
|
||||
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: Protect ...""",
|
||||
"send_to": "Guard",
|
||||
"restricted_to": "Moderator,Guard"},
|
||||
3: {"content": "Guard, close your eyes",
|
||||
|
|
@ -26,7 +27,7 @@ STEP_INSTRUCTIONS = {
|
|||
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:
|
||||
{living_players}. For example: I kill ...""",
|
||||
{living_players}. For example: Kill ...""",
|
||||
"send_to": "Werewolf",
|
||||
"restricted_to": "Moderator,Werewolf"},
|
||||
6: {"content": "Werewolves, close your eyes",
|
||||
|
|
@ -35,12 +36,13 @@ STEP_INSTRUCTIONS = {
|
|||
7: {"content": "Witch, please open your eyes!",
|
||||
"send_to": "Moderator",
|
||||
"restricted_to": ""},
|
||||
8: {"content": """Witch, tonight {player_hunted} has been killed by the werewolves.
|
||||
You have a bottle of antidote, would you like to save him/her? If so, say "Save", else, say "Pass".""",
|
||||
8: {"content": """Witch, tonight {player_hunted} has been killed by the werewolves.
|
||||
You have a bottle of antidote, would you like to save him/her? If so, say "Save", else, say "Pass".""",
|
||||
"send_to": "Witch",
|
||||
"restricted_to": "Moderator,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 so, say "Poison PlayerX", where X is the player index, else, say "Pass".""",
|
||||
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 so, say "Poison PlayerX", where X is the player index, else, say "Pass".""",
|
||||
"send_to": "Witch",
|
||||
"restricted_to": "Moderator,Witch"}, #
|
||||
10: {"content": "Witch, close your eyes",
|
||||
|
|
@ -49,8 +51,8 @@ STEP_INSTRUCTIONS = {
|
|||
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}.""",
|
||||
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": "Moderator,Seer"},
|
||||
13: {"content": "Seer, close your eyes",
|
||||
|
|
@ -77,33 +79,6 @@ STEP_INSTRUCTIONS = {
|
|||
"restricted_to": ""},
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
@ -123,9 +98,9 @@ class InstructSpeak(Action):
|
|||
if "{werewolf_players}" in content:
|
||||
content = content.format(werewolf_players=",".join(werewolf_players))
|
||||
if "{player_hunted}" in content:
|
||||
player_hunted = "No one" if not player_hunted else player_hunted
|
||||
content = content.format(player_hunted=player_hunted)
|
||||
if "{player_current_dead}" in content:
|
||||
player_current_dead = "No one" if not player_current_dead else player_current_dead
|
||||
content = content.format(player_current_dead=player_current_dead)
|
||||
|
||||
return content, instruction_info["send_to"], instruction_info["restricted_to"]
|
||||
|
|
@ -137,43 +112,6 @@ class ParseSpeak(Action):
|
|||
async def run(self):
|
||||
pass
|
||||
|
||||
class SummarizeNight(Action):
|
||||
"""consider all events at night, conclude which player dies (can be a peaceful night)"""
|
||||
|
||||
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"""
|
||||
|
||||
|
|
@ -205,12 +143,9 @@ class AnnounceGameResult(Action):
|
|||
async def run(self, winner: str):
|
||||
return f"Game over! The winner is the {winner}"
|
||||
|
||||
|
||||
async def main():
|
||||
rst1 = await SummarizeDay().run({"Player1": 0, "Player2": 0, "Player3": 0, "Player4": 0})
|
||||
rst2 = await SummarizeNight().run({"killed_by_werewolves": "Player1"})
|
||||
print(rst1)
|
||||
print(rst2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import re
|
||||
|
||||
from metagpt.roles import Role
|
||||
from metagpt.schema import Message
|
||||
from metagpt.logs import logger
|
||||
|
|
@ -9,67 +11,55 @@ class BasePlayer(Role):
|
|||
self,
|
||||
name: str = "PlayerXYZ",
|
||||
profile: str = "BasePlayer",
|
||||
team: str = "good guys",
|
||||
special_action_names: list[str] = [],
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(name, profile, **kwargs)
|
||||
self._init_actions([Speak])
|
||||
self._watch([InstructSpeak])
|
||||
self.team = team
|
||||
# 调用 get_status() 来检查存活状态,并通过 set_status() 更新状态。
|
||||
# 通过 set_status() 更新状态。
|
||||
self.status = 0 # 0代表活着,1代表死亡
|
||||
|
||||
|
||||
# 技能和监听配置
|
||||
self._watch([InstructSpeak]) # 监听Moderator的指令以做行动
|
||||
special_actions = [ACTIONS[action_name] for action_name in special_action_names]
|
||||
capable_actions = [Speak] + special_actions
|
||||
self._init_actions(capable_actions) # 给角色赋予行动技能
|
||||
self.special_actions = special_actions
|
||||
|
||||
|
||||
async def _observe(self) -> int:
|
||||
if self.status == 1:
|
||||
# 死者不再参与游戏
|
||||
return 0
|
||||
|
||||
|
||||
await super()._observe()
|
||||
# 只有发给全体的("")或发给自己的(self.profile)消息需要走下面的_react流程,
|
||||
# 其他的收听到即可,不用做动作
|
||||
self._rc.news = [msg for msg in self._rc.news if msg.send_to in ["", self.profile]]
|
||||
return len(self._rc.news)
|
||||
|
||||
|
||||
async def _think(self):
|
||||
news = self._rc.news[0]
|
||||
assert news.cause_by == InstructSpeak # 消息为来自Moderator的指令时,才去做动作
|
||||
if not news.restricted_to:
|
||||
# 消息接收范围为全体角色的,做公开发言(发表投票观点也算发言)
|
||||
self._rc.todo = Speak()
|
||||
elif self.profile in news.restricted_to.split(","): # FIXME: hard code to split, restricted为"Moderator"或"Moderator,角色profile"
|
||||
elif self.profile in news.restricted_to.split(","):
|
||||
# FIXME: hard code to split, restricted为"Moderator"或"Moderator,角色profile"
|
||||
# Moderator加密发给自己的,意味着要执行角色的特殊动作
|
||||
self._rc.todo = self.special_actions[0]()
|
||||
|
||||
async def _act(self):
|
||||
"""每个角色要改写此函数以实现该角色的动作"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def get_all_memories(self) -> str:
|
||||
memories = self._rc.memory.get()
|
||||
time_stamp_pattern = r'[0-9]+ \| '
|
||||
# NOTE: 除Moderator外,其他角色使用memory,只能用m.sent_from(玩家名)不能用m.role(玩家角色),因为他们不知道说话者的身份
|
||||
memories = [f"{m.sent_from}: {m.content}" for m in memories]
|
||||
memories = [f"{m.sent_from}: {re.sub(time_stamp_pattern, '', m.content)}" for m in memories] # regex去掉时间戳
|
||||
memories = "\n".join(memories)
|
||||
return memories
|
||||
|
||||
def get_name(self):
|
||||
return self.name
|
||||
|
||||
def get_profile(self):
|
||||
return self.profile
|
||||
|
||||
def get_team(self):
|
||||
return self.team
|
||||
|
||||
def get_status(self):
|
||||
return self.status
|
||||
|
||||
def set_status(self, new_status):
|
||||
self.status = new_status
|
||||
|
|
|
|||
|
|
@ -8,12 +8,11 @@ class Guard(BasePlayer):
|
|||
self,
|
||||
name: str = "",
|
||||
profile: str = "Guard",
|
||||
team: str = "good guys",
|
||||
special_action_names: list[str] = ["Protect"],
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(name, profile, team, special_action_names, **kwargs)
|
||||
|
||||
super().__init__(name, profile, special_action_names, **kwargs)
|
||||
|
||||
async def _act(self):
|
||||
# todo为_think时确定的,有两种情况,Speak或Protect
|
||||
todo = self._rc.todo
|
||||
|
|
@ -30,7 +29,7 @@ class Guard(BasePlayer):
|
|||
content=rsp, role=self.profile, sent_from=self.name,
|
||||
cause_by=Speak, send_to="", restricted_to="",
|
||||
)
|
||||
|
||||
|
||||
elif isinstance(todo, Protect):
|
||||
rsp = await todo.run(context=memories)
|
||||
msg = Message(
|
||||
|
|
@ -38,9 +37,7 @@ class Guard(BasePlayer):
|
|||
cause_by=Protect, send_to="",
|
||||
restricted_to=f"Moderator,{self.profile}", # 给Moderator发送守卫要保护的人加密消息
|
||||
)
|
||||
|
||||
|
||||
logger.info(f"{self._setting}: {rsp}")
|
||||
|
||||
return msg
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import asyncio
|
||||
import re
|
||||
from collections import Counter
|
||||
|
||||
|
|
@ -13,9 +12,6 @@ from metagpt.actions import BossRequirement as UserRequirement
|
|||
|
||||
|
||||
class Moderator(Role):
|
||||
# 游戏状态属性
|
||||
is_game_over = False
|
||||
winner = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
@ -35,7 +31,7 @@ class Moderator(Role):
|
|||
self.winner = None
|
||||
self.witch_poison_left = 1
|
||||
self.witch_antidote_left = 1
|
||||
|
||||
|
||||
# player states of current night
|
||||
self.player_hunted = None
|
||||
self.player_protected = None
|
||||
|
|
@ -48,7 +44,7 @@ class Moderator(Role):
|
|||
self.werewolf_players = re.findall(r"Player[0-9]+: Werewolf", game_setup)
|
||||
self.werewolf_players = [p.replace(": Werewolf", "") for p in self.werewolf_players]
|
||||
self.good_guys = [p for p in self.living_players if p not in self.werewolf_players]
|
||||
|
||||
|
||||
def update_player_status(self, player_names: list[str]):
|
||||
if not player_names:
|
||||
return
|
||||
|
|
@ -72,15 +68,15 @@ class Moderator(Role):
|
|||
logger.info(self.step_idx)
|
||||
|
||||
latest_msg = memories[-1]
|
||||
latest_msg_content = latest_msg.content
|
||||
|
||||
match = re.search(r"Player[0-9]+", latest_msg.content[-10:])
|
||||
match = re.search(r"Player[0-9]+", latest_msg_content[-10:]) # FIXME: hard code truncation
|
||||
target = match.group(0) if match else ""
|
||||
|
||||
# default return
|
||||
msg_content = "Understood"
|
||||
restricted_to = ""
|
||||
|
||||
source_role = latest_msg.role
|
||||
msg_cause_by = latest_msg.cause_by
|
||||
if msg_cause_by == Hunt:
|
||||
self.player_hunted = target
|
||||
|
|
@ -94,14 +90,18 @@ class Moderator(Role):
|
|||
msg_content = f"{target} is a good guy"
|
||||
restricted_to = "Moderator,Seer"
|
||||
elif msg_cause_by == Save:
|
||||
if not self.witch_antidote_left and latest_msg.content != "Pass":
|
||||
if "pass" in latest_msg_content.lower():
|
||||
pass
|
||||
elif not self.witch_antidote_left:
|
||||
msg_content = "You have no antidote left and thus can not save the player"
|
||||
restricted_to = "Moderator,Witch"
|
||||
else:
|
||||
self.witch_antidote_left -= 1
|
||||
self.is_hunted_player_saved = latest_msg.content == "Save"
|
||||
self.is_hunted_player_saved = True
|
||||
elif msg_cause_by == Poison:
|
||||
if not self.witch_poison_left and latest_msg.content != "Pass":
|
||||
if "pass" in latest_msg_content.lower():
|
||||
pass
|
||||
elif not self.witch_poison_left:
|
||||
msg_content = "You have no poison left and thus can not poison the player"
|
||||
restricted_to = "Moderator,Witch"
|
||||
else:
|
||||
|
|
@ -140,7 +140,7 @@ class Moderator(Role):
|
|||
self.living_players = [p for p in self.living_players if p not in self.player_current_dead]
|
||||
self.update_player_status(self.player_current_dead)
|
||||
msg_content = "Voting done"
|
||||
|
||||
|
||||
# game's termination condition
|
||||
living_werewolf = [p for p in self.werewolf_players if p in self.living_players]
|
||||
living_good_guys = [p for p in self.good_guys if p in self.living_players]
|
||||
|
|
@ -156,7 +156,7 @@ class Moderator(Role):
|
|||
if self.winner is not None:
|
||||
self._rc.todo = AnnounceGameResult()
|
||||
return
|
||||
|
||||
|
||||
latest_msg = self._rc.memory.get()[-1]
|
||||
if latest_msg.role in ["User"]:
|
||||
# 上一轮消息是用户指令,解析用户指令,开始游戏
|
||||
|
|
@ -181,18 +181,19 @@ class Moderator(Role):
|
|||
# print("*" * 10, f"{self._setting}'s current memories: {memories}", "*" * 10)
|
||||
if self.step_idx % len(STEP_INSTRUCTIONS) == 0 or self.winner is not None:
|
||||
# 进行完一夜一日的循环,打印一次完整发言历史
|
||||
logger.info("a night and day cycle completed, examine all history")
|
||||
print(self.get_all_memories())
|
||||
|
||||
# 根据_think的结果,执行InstructSpeak还是ParseSpeak, 并将结果返回
|
||||
if isinstance(todo, InstructSpeak):
|
||||
msg_content, msg_to_send_to, msg_restriced_to = await self._instruct_speak()
|
||||
msg_content = f"Step {self.step_idx}: {msg_content}" # HACK: 加一个unique的step_idx避免记忆的自动去重
|
||||
# msg_content = f"Step {self.step_idx}: {msg_content}" # HACK: 加一个unique的step_idx避免记忆的自动去重
|
||||
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)
|
||||
|
||||
elif isinstance(todo, ParseSpeak):
|
||||
msg_content, msg_restriced_to = await self._parse_speak(memories)
|
||||
msg_content = f"Step {self.step_idx}: {msg_content}" # HACK: 加一个unique的step_idx避免记忆的自动去重
|
||||
# msg_content = f"Step {self.step_idx}: {msg_content}" # HACK: 加一个unique的step_idx避免记忆的自动去重
|
||||
msg = Message(content=msg_content, role=self.profile, sent_from=self.name,
|
||||
cause_by=ParseSpeak, send_to="", restricted_to=msg_restriced_to)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,10 @@ class Seer(BasePlayer):
|
|||
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)
|
||||
super().__init__(name, profile, special_action_names, **kwargs)
|
||||
|
||||
async def _act(self):
|
||||
todo = self._rc.todo
|
||||
|
|
|
|||
|
|
@ -8,11 +8,10 @@ class Villager(BasePlayer):
|
|||
self,
|
||||
name: str = "",
|
||||
profile: str = "Villager",
|
||||
team: str = "good guys",
|
||||
special_action_names: list[str] = [],
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(name, profile, team, special_action_names, **kwargs)
|
||||
super().__init__(name, profile, special_action_names, **kwargs)
|
||||
|
||||
async def _act(self):
|
||||
|
||||
|
|
@ -34,5 +33,5 @@ class Villager(BasePlayer):
|
|||
)
|
||||
|
||||
logger.info(f"{self._setting}: {rsp}")
|
||||
|
||||
|
||||
return msg
|
||||
|
|
|
|||
|
|
@ -8,12 +8,11 @@ class Werewolf(BasePlayer):
|
|||
self,
|
||||
name: str = "",
|
||||
profile: str = "Werewolf",
|
||||
team: str = "werewolves",
|
||||
special_action_names: list[str] = ["Hunt"],
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(name, profile, team, special_action_names, **kwargs)
|
||||
|
||||
super().__init__(name, profile, special_action_names, **kwargs)
|
||||
|
||||
async def _act(self):
|
||||
# todo为_think时确定的,有两种情况,Speak或Hunt
|
||||
todo = self._rc.todo
|
||||
|
|
@ -30,7 +29,7 @@ class Werewolf(BasePlayer):
|
|||
content=rsp, role=self.profile, sent_from=self.name,
|
||||
cause_by=Speak, send_to="", restricted_to="",
|
||||
)
|
||||
|
||||
|
||||
elif isinstance(todo, Hunt):
|
||||
rsp = await todo.run(context=memories)
|
||||
msg = Message(
|
||||
|
|
@ -38,7 +37,7 @@ class Werewolf(BasePlayer):
|
|||
cause_by=Hunt, send_to="",
|
||||
restricted_to=f"Moderator,{self.profile}", # 给Moderator及狼阵营发送要杀的人的加密消息
|
||||
)
|
||||
|
||||
|
||||
logger.info(f"{self._setting}: {rsp}")
|
||||
|
||||
return msg
|
||||
|
|
|
|||
|
|
@ -8,12 +8,11 @@ class Witch(BasePlayer):
|
|||
self,
|
||||
name: str = "",
|
||||
profile: str = "Witch",
|
||||
team: str = "good guys",
|
||||
special_action_names: list[str] = ["Save", "Poison"],
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(name, profile, team, special_action_names, **kwargs)
|
||||
|
||||
super().__init__(name, profile, special_action_names, **kwargs)
|
||||
|
||||
async def _think(self):
|
||||
# 女巫涉及两个特殊技能,因此在此需要改写_think进行路由
|
||||
news = self._rc.news[0]
|
||||
|
|
@ -21,7 +20,8 @@ class Witch(BasePlayer):
|
|||
if not news.restricted_to:
|
||||
# 消息接收范围为全体角色的,做公开发言(发表投票观点也算发言)
|
||||
self._rc.todo = Speak()
|
||||
elif self.profile in news.restricted_to.split(","): # FIXME: hard code to split, restricted为"Moderator"或"Moderator,角色profile"
|
||||
elif self.profile in news.restricted_to.split(","):
|
||||
# FIXME: hard code to split, restricted为"Moderator"或"Moderator,角色profile"
|
||||
# Moderator加密发给自己的,意味着要执行角色的特殊动作
|
||||
# 这里用关键词进行动作的选择,需要Moderator侧的指令进行配合
|
||||
if "save" in news.content.lower():
|
||||
|
|
|
|||
|
|
@ -1,46 +1,45 @@
|
|||
import asyncio
|
||||
import platform
|
||||
import fire
|
||||
import random
|
||||
|
||||
from examples.werewolf_game.werewolf_game import WerewolfGame
|
||||
from examples.werewolf_game.roles import Moderator, Villager, Werewolf, Guard, Seer, Witch
|
||||
|
||||
DEFAULT_PLAYER_SETUP = """
|
||||
Game setup:
|
||||
Player1: Villager,
|
||||
Player2: Villager,
|
||||
Player3: Werewolf,
|
||||
Player4: Werewolf,
|
||||
Player5: Guard,
|
||||
Player6: Seer,
|
||||
Player7: Witch.
|
||||
"""
|
||||
def init_game_setup(shuffle=False):
|
||||
roles = [
|
||||
Villager,
|
||||
Villager,
|
||||
Werewolf,
|
||||
Werewolf,
|
||||
Guard,
|
||||
Seer,
|
||||
Witch
|
||||
]
|
||||
if shuffle:
|
||||
random.seed(2023)
|
||||
random.shuffle(roles)
|
||||
players = [role(name=f"Player{i+1}") for i, role in enumerate(roles)]
|
||||
game_setup = ["Game setup:"] + [f"{player.name}: {player.profile}," for player in players]
|
||||
game_setup = "\n".join(game_setup)
|
||||
return game_setup, players
|
||||
|
||||
async def start_game(idea: str = DEFAULT_PLAYER_SETUP, investment: float = 3.0, n_round: int = 5):
|
||||
async def start_game(investment: float = 3.0, n_round: int = 5):
|
||||
game = WerewolfGame()
|
||||
game.hire([
|
||||
Moderator(),
|
||||
Villager(name="Player1"),
|
||||
Villager(name="Player2"),
|
||||
Werewolf(name="Player3"),
|
||||
Werewolf(name="Player4"),
|
||||
Guard(name="Player5"),
|
||||
Seer(name="Player6"),
|
||||
Witch(name="Player7"),
|
||||
])
|
||||
game_setup, players = init_game_setup(shuffle=True)
|
||||
players = [Moderator()] + players
|
||||
game.hire(players)
|
||||
game.invest(investment)
|
||||
game.start_project(idea)
|
||||
game.start_project(game_setup)
|
||||
await game.run(n_round=n_round)
|
||||
|
||||
|
||||
def main(idea: str = DEFAULT_PLAYER_SETUP, investment: float = 3.0, n_round: int = 100):
|
||||
def main(investment: float = 3.0, n_round: int = 100):
|
||||
"""
|
||||
:param idea: game config instructions
|
||||
:param investment: contribute a certain dollar amount to watch the debate
|
||||
:param n_round: maximum rounds of the debate
|
||||
:return:
|
||||
"""
|
||||
asyncio.run(start_game(idea, investment, n_round))
|
||||
asyncio.run(start_game(investment, n_round))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -5,6 +5,20 @@ from metagpt.schema import Message
|
|||
|
||||
class WerewolfEnvironment(Environment):
|
||||
|
||||
timestamp: int = 0
|
||||
|
||||
def publish_message(self, message: Message, add_timestamp: bool = True):
|
||||
"""向当前环境发布信息
|
||||
Post information to the current environment
|
||||
"""
|
||||
# self.message_queue.put(message)
|
||||
if add_timestamp:
|
||||
# 因消息内容可能重复,例如,连续两晚杀同一个人,
|
||||
# 因此需要加一个unique的time_stamp以使得相同的message在加入记忆时不被自动去重
|
||||
message.content = f"{self.timestamp} | " + message.content
|
||||
self.memory.add(message)
|
||||
self.history += f"\n{message}"
|
||||
|
||||
async def run(self, k=1):
|
||||
"""处理一次所有信息的运行,各角色顺序执行
|
||||
Process all Role runs at once
|
||||
|
|
@ -12,6 +26,7 @@ class WerewolfEnvironment(Environment):
|
|||
for _ in range(k):
|
||||
for role in self.roles.values():
|
||||
await role.run()
|
||||
self.timestamp += 1
|
||||
|
||||
class WerewolfGame(SoftwareCompany):
|
||||
"""Use the "software company paradigm" to hold a werewolf game"""
|
||||
|
|
@ -24,4 +39,3 @@ class WerewolfGame(SoftwareCompany):
|
|||
self.environment.publish_message(
|
||||
Message(role="User", content=idea, cause_by=UserRequirement, restricted_to="Moderator")
|
||||
)
|
||||
print("a")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue