Merge pull request #365 from mannaandpoem/manna_werewolf_game

Werewolf game modify moderator and add seer role and action
This commit is contained in:
garylin2099 2023-09-29 10:24:05 +08:00 committed by GitHub
commit 50d9ad4270
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 442 additions and 59 deletions

View file

@ -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,
}

View file

@ -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": "Its 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. Dont 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())

View 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

View 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.
Dont 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 its dark: Your talking content with moderator is confidential. You
neednt 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 witchs
poison and guard cannot protect the same player on two consecutive nights. Villagers cant 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 cant 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 youre cheating other players. Only give the players name
when making a decision/voting, and dont 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}.
Youre 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 players 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: ...
'''

View file

@ -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

View file

@ -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 # 初始状态为活着

View file

@ -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]

View 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

View file

@ -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)