init
This commit is contained in:
81
AlgoriAgent/examples/game_werewolf/README.md
Normal file
81
AlgoriAgent/examples/game_werewolf/README.md
Normal file
@@ -0,0 +1,81 @@
|
||||
|
||||
# Werewolf Game in AgentScope
|
||||
|
||||
This example demonstrates how to use AgentScope to play the Werewolf game, where six agents play against each other as werewolves and villagers.
|
||||
You will learn the following features in AgentScope:
|
||||
|
||||
- How to make an agent respond with **different specified fields** in **different situations** in AgentScope (Like a finite state machine!)
|
||||
- How to use **msghub** and **pipeline** in AgentScope to construct a game with **COMPLEX SOP**
|
||||
|
||||
## Background
|
||||
|
||||
The werewolf game involves a complex SOP with multiple roles and different phases. In these phases, players with different roles should take different actions, e.g. discussion, voting, checking roles (seer), using potions (witch), and so on.
|
||||
|
||||
Therefore, for an agent in werewolf game, it should be able to switch its status according to the game phase and its role, and respond accordingly.
|
||||
Of course, we can hard code the SOP (or finite state machine) within the agent, but we expect the agent to be more **flexible**, **intelligent**, and **adaptive**, which means **the agent shouldn't be designed for a specific game only**. It should be able to adapt to different tasks and SOPs!
|
||||
|
||||
To achieve this goal, in AgentScope, we use a built-in [DictDialogAgent](https://github.com/modelscope/agentscope/blob/main/src/agentscope/agents/dict_dialog_agent.py) class, together with a [parser](https://modelscope.github.io/agentscope/en/tutorial/203-parser.html) module to construct the werewolf game.
|
||||
|
||||
Moreover, the [pipeline and msghub](https://modelscope.github.io/agentscope/en/tutorial/202-pipeline.html) in AgentScope enable us to easily construct a complex SOP with multiple agents. We hope the implementation is concise, clear and readable!
|
||||
|
||||
More details please refer to our tutorial:
|
||||
- [Parser](https://modelscope.github.io/agentscope/en/tutorial/203-parser.html)
|
||||
- [Pipeline and msghub](https://modelscope.github.io/agentscope/en/tutorial/202-pipeline.html)
|
||||
|
||||
|
||||
|
||||
|
||||
https://github.com/DavdGao/AgentScope/assets/102287034/86951418-e1cc-486b-a3dc-b237a0108994
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Tested Models
|
||||
|
||||
This example has been tested with the following models:
|
||||
- dashscope_chat (qwen-turbo)
|
||||
- ollama_chat (llama3_8b)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To run this example, you need to:
|
||||
- Set your API key in `./configs/model_configs.json`
|
||||
|
||||
[Optional] To play the game in person, see [werewolf.py](werewolf.py) for the complete code.
|
||||
|
||||
|
||||
## Code Snippets
|
||||
|
||||
### About Pipeline and MsgHub
|
||||
|
||||
The following code is the implementation of daytime discussion in Werewolf. With msghub and pipeline, it's very easy to program a discussion among agents.
|
||||
|
||||
```python
|
||||
# ...
|
||||
with msghub(survivors, announcement=hints) as hub:
|
||||
# discuss
|
||||
set_parsers(survivors, Prompts.survivors_discuss_parser)
|
||||
x = sequentialpipeline(survivors)
|
||||
# ...
|
||||
```
|
||||
|
||||
### About Parser
|
||||
|
||||
The parser is used to specify the required fields in the agent's response and how to handle them. Here's an example of a parser configuration:
|
||||
|
||||
```python
|
||||
to_wolves_vote = "Which player do you vote to kill?"
|
||||
|
||||
wolves_vote_parser = MarkdownJsonDictParser(
|
||||
content_hint={
|
||||
"thought": "what you thought",
|
||||
"vote": "player_name",
|
||||
},
|
||||
required_keys=["thought", "vote"],
|
||||
keys_to_memory="vote",
|
||||
keys_to_content="vote",
|
||||
)
|
||||
```
|
||||
|
||||
In this example, the `MarkdownJsonDictParser` is used to parse the agent's response. The `content_hint` parameter specifies the expected fields and their descriptions. The `required_keys` parameter indicates the mandatory fields in the response. The `keys_to_memory` and `keys_to_content` parameters determine which fields should be stored in memory and included in the content of the returned message, respectively.
|
||||
@@ -0,0 +1,56 @@
|
||||
[
|
||||
{
|
||||
"class": "DictDialogAgent",
|
||||
"args": {
|
||||
"name": "Player1",
|
||||
"sys_prompt": "Act as a player in a werewolf game. You are Player1 and\nthere are totally 6 players, named Player1, Player2, Player3, Player4, Player5 and Player6.\n\nPLAYER ROLES:\nIn werewolf game, players are divided into two werewolves, two villagers, one seer and one witch. Note only werewolves know who are their teammates.\nWerewolves: They know their teammates' identities and attempt to eliminate a villager each night while trying to remain undetected.\nVillagers: They do not know who the werewolves are and must work together during the day to deduce who the werewolves might be and vote to eliminate them.\nSeer: A villager with the ability to learn the true identity of one player each night. This role is crucial for the villagers to gain information.\nWitch: A character who has a one-time ability to save a player from being eliminated at night (sometimes this is a potion of life) and a one-time ability to eliminate a player at night (a potion of death).\n\nGAME RULE:\nThe game is consisted of two phases: night phase and day phase. The two phases are repeated until werewolf or villager win the game.\n1. Night Phase: During the night, the werewolves discuss and vote for a player to eliminate. Special roles also perform their actions at this time (e.g., the Seer chooses a player to learn their role, the witch chooses a decide if save the player).\n2. Day Phase: During the day, all surviving players discuss who they suspect might be a werewolf. No one reveals their role unless it serves a strategic purpose. After the discussion, a vote is taken, and the player with the most votes is \"lynched\" or eliminated from the game.\n\nVICTORY CONDITION:\nFor werewolves, they win the game if the number of werewolves is equal to or greater than the number of remaining villagers.\nFor villagers, they win if they identify and eliminate all of the werewolves in the group.\n\nCONSTRAINTS:\n1. Your response should be in the first person.\n2. This is a conversational game. You should response only based on the conversation history and your strategy.\n\nYou are playing werewolf in this game.\n",
|
||||
"model_config_name": "gpt-4",
|
||||
"use_memory": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"class": "DictDialogAgent",
|
||||
"args": {
|
||||
"name": "Player2",
|
||||
"sys_prompt": "Act as a player in a werewolf game. You are Player2 and\nthere are totally 6 players, named Player1, Player2, Player3, Player4, Player5 and Player6.\n\nPLAYER ROLES:\nIn werewolf game, players are divided into two werewolves, two villagers, one seer and one witch. Note only werewolves know who are their teammates.\nWerewolves: They know their teammates' identities and attempt to eliminate a villager each night while trying to remain undetected.\nVillagers: They do not know who the werewolves are and must work together during the day to deduce who the werewolves might be and vote to eliminate them.\nSeer: A villager with the ability to learn the true identity of one player each night. This role is crucial for the villagers to gain information.\nWitch: A character who has a one-time ability to save a player from being eliminated at night (sometimes this is a potion of life) and a one-time ability to eliminate a player at night (a potion of death).\n\nGAME RULE:\nThe game is consisted of two phases: night phase and day phase. The two phases are repeated until werewolf or villager win the game.\n1. Night Phase: During the night, the werewolves discuss and vote for a player to eliminate. Special roles also perform their actions at this time (e.g., the Seer chooses a player to learn their role, the witch chooses a decide if save the player).\n2. Day Phase: During the day, all surviving players discuss who they suspect might be a werewolf. No one reveals their role unless it serves a strategic purpose. After the discussion, a vote is taken, and the player with the most votes is \"lynched\" or eliminated from the game.\n\nVICTORY CONDITION:\nFor werewolves, they win the game if the number of werewolves is equal to or greater than the number of remaining villagers.\nFor villagers, they win if they identify and eliminate all of the werewolves in the group.\n\nCONSTRAINTS:\n1. Your response should be in the first person.\n2. This is a conversational game. You should response only based on the conversation history and your strategy.\n\nYou are playing werewolf in this game.\n",
|
||||
"model_config_name": "gpt-4",
|
||||
"use_memory": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"class": "DictDialogAgent",
|
||||
"args": {
|
||||
"name": "Player3",
|
||||
"sys_prompt": "Act as a player in a werewolf game. You are Player3 and\nthere are totally 6 players, named Player1, Player2, Player3, Player4, Player5 and Player6.\n\nPLAYER ROLES:\nIn werewolf game, players are divided into two werewolves, two villagers, one seer and one witch. Note only werewolves know who are their teammates.\nWerewolves: They know their teammates' identities and attempt to eliminate a villager each night while trying to remain undetected.\nVillagers: They do not know who the werewolves are and must work together during the day to deduce who the werewolves might be and vote to eliminate them.\nSeer: A villager with the ability to learn the true identity of one player each night. This role is crucial for the villagers to gain information.\nWitch: A character who has a one-time ability to save a player from being eliminated at night (sometimes this is a potion of life) and a one-time ability to eliminate a player at night (a potion of death).\n\nGAME RULE:\nThe game is consisted of two phases: night phase and day phase. The two phases are repeated until werewolf or villager win the game.\n1. Night Phase: During the night, the werewolves discuss and vote for a player to eliminate. Special roles also perform their actions at this time (e.g., the Seer chooses a player to learn their role, the witch chooses a decide if save the player).\n2. Day Phase: During the day, all surviving players discuss who they suspect might be a werewolf. No one reveals their role unless it serves a strategic purpose. After the discussion, a vote is taken, and the player with the most votes is \"lynched\" or eliminated from the game.\n\nVICTORY CONDITION:\nFor werewolves, they win the game if the number of werewolves is equal to or greater than the number of remaining villagers.\nFor villagers, they win if they identify and eliminate all of the werewolves in the group.\n\nCONSTRAINTS:\n1. Your response should be in the first person.\n2. This is a conversational game. You should response only based on the conversation history and your strategy.\n\nYou are playing villager in this game.\n",
|
||||
"model_config_name": "gpt-4",
|
||||
"use_memory": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"class": "DictDialogAgent",
|
||||
"args": {
|
||||
"name": "Player4",
|
||||
"sys_prompt": "Act as a player in a werewolf game. You are Player4 and\nthere are totally 6 players, named Player1, Player2, Player3, Player4, Player5 and Player6.\n\nPLAYER ROLES:\nIn werewolf game, players are divided into two werewolves, two villagers, one seer and one witch. Note only werewolves know who are their teammates.\nWerewolves: They know their teammates' identities and attempt to eliminate a villager each night while trying to remain undetected.\nVillagers: They do not know who the werewolves are and must work together during the day to deduce who the werewolves might be and vote to eliminate them.\nSeer: A villager with the ability to learn the true identity of one player each night. This role is crucial for the villagers to gain information.\nWitch: A character who has a one-time ability to save a player from being eliminated at night (sometimes this is a potion of life) and a one-time ability to eliminate a player at night (a potion of death).\n\nGAME RULE:\nThe game is consisted of two phases: night phase and day phase. The two phases are repeated until werewolf or villager win the game.\n1. Night Phase: During the night, the werewolves discuss and vote for a player to eliminate. Special roles also perform their actions at this time (e.g., the Seer chooses a player to learn their role, the witch chooses a decide if save the player).\n2. Day Phase: During the day, all surviving players discuss who they suspect might be a werewolf. No one reveals their role unless it serves a strategic purpose. After the discussion, a vote is taken, and the player with the most votes is \"lynched\" or eliminated from the game.\n\nVICTORY CONDITION:\nFor werewolves, they win the game if the number of werewolves is equal to or greater than the number of remaining villagers.\nFor villagers, they win if they identify and eliminate all of the werewolves in the group.\n\nCONSTRAINTS:\n1. Your response should be in the first person.\n2. This is a conversational game. You should response only based on the conversation history and your strategy.\n\nYou are playing villager in this game.\n",
|
||||
"model_config_name": "gpt-4",
|
||||
"use_memory": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"class": "DictDialogAgent",
|
||||
"args": {
|
||||
"name": "Player5",
|
||||
"sys_prompt": "Act as a player in a werewolf game. You are Player5 and\nthere are totally 6 players, named Player1, Player2, Player3, Player4, Player5 and Player6.\n\nPLAYER ROLES:\nIn werewolf game, players are divided into two werewolves, two villagers, one seer and one witch. Note only werewolves know who are their teammates.\nWerewolves: They know their teammates' identities and attempt to eliminate a villager each night while trying to remain undetected.\nVillagers: They do not know who the werewolves are and must work together during the day to deduce who the werewolves might be and vote to eliminate them.\nSeer: A villager with the ability to learn the true identity of one player each night. This role is crucial for the villagers to gain information.\nWitch: A character who has a one-time ability to save a player from being eliminated at night (sometimes this is a potion of life) and a one-time ability to eliminate a player at night (a potion of death).\n\nGAME RULE:\nThe game is consisted of two phases: night phase and day phase. The two phases are repeated until werewolf or villager win the game.\n1. Night Phase: During the night, the werewolves discuss and vote for a player to eliminate. Special roles also perform their actions at this time (e.g., the Seer chooses a player to learn their role, the witch chooses a decide if save the player).\n2. Day Phase: During the day, all surviving players discuss who they suspect might be a werewolf. No one reveals their role unless it serves a strategic purpose. After the discussion, a vote is taken, and the player with the most votes is \"lynched\" or eliminated from the game.\n\nVICTORY CONDITION:\nFor werewolves, they win the game if the number of werewolves is equal to or greater than the number of remaining villagers.\nFor villagers, they win if they identify and eliminate all of the werewolves in the group.\n\nCONSTRAINTS:\n1. Your response should be in the first person.\n2. This is a conversational game. You should response only based on the conversation history and your strategy.\n\nYou are playing seer in this game.\n",
|
||||
"model_config_name": "gpt-4",
|
||||
"use_memory": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"class": "DictDialogAgent",
|
||||
"args": {
|
||||
"name": "Player6",
|
||||
"sys_prompt": "Act as a player in a werewolf game. You are Player6 and\nthere are totally 6 players, named Player1, Player2, Player3, Player4, Player5 and Player6.\n\nPLAYER ROLES:\nIn werewolf game, players are divided into two werewolves, two villagers, one seer and one witch. Note only werewolves know who are their teammates.\nWerewolves: They know their teammates' identities and attempt to eliminate a villager each night while trying to remain undetected.\nVillagers: They do not know who the werewolves are and must work together during the day to deduce who the werewolves might be and vote to eliminate them.\nSeer: A villager with the ability to learn the true identity of one player each night. This role is crucial for the villagers to gain information.\nWitch: A character who has a one-time ability to save a player from being eliminated at night (sometimes this is a potion of life) and a one-time ability to eliminate a player at night (a potion of death).\n\nGAME RULE:\nThe game is consisted of two phases: night phase and day phase. The two phases are repeated until werewolf or villager win the game.\n1. Night Phase: During the night, the werewolves discuss and vote for a player to eliminate. Special roles also perform their actions at this time (e.g., the Seer chooses a player to learn their role, the witch chooses a decide if save the player).\n2. Day Phase: During the day, all surviving players discuss who they suspect might be a werewolf. No one reveals their role unless it serves a strategic purpose. After the discussion, a vote is taken, and the player with the most votes is \"lynched\" or eliminated from the game.\n\nVICTORY CONDITION:\nFor werewolves, they win the game if the number of werewolves is equal to or greater than the number of remaining villagers.\nFor villagers, they win if they identify and eliminate all of the werewolves in the group.\n\nCONSTRAINTS:\n1. Your response should be in the first person.\n2. This is a conversational game. You should response only based on the conversation history and your strategy.\n\nYou are playing witch in this game.\n",
|
||||
"model_config_name": "gpt-4",
|
||||
"use_memory": true
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
[
|
||||
{
|
||||
"model_type": "openai_chat",
|
||||
"config_name": "gpt-4",
|
||||
"model_name": "gpt-4",
|
||||
"api_key": "xxx",
|
||||
"organization": "xxx",
|
||||
"generate_args": {
|
||||
"temperature": 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"model_type": "post_api_chat",
|
||||
"config_name": "my_post_api",
|
||||
"api_url": "https://xxx",
|
||||
"headers": {},
|
||||
"json_args": {}
|
||||
}
|
||||
]
|
||||
152
AlgoriAgent/examples/game_werewolf/prompt.py
Normal file
152
AlgoriAgent/examples/game_werewolf/prompt.py
Normal file
@@ -0,0 +1,152 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Used to record prompts, will be replaced by configuration"""
|
||||
from agentscope.parsers.json_object_parser import MarkdownJsonDictParser
|
||||
|
||||
|
||||
class Prompts:
|
||||
"""Prompts for werewolf game"""
|
||||
|
||||
to_wolves = (
|
||||
"{}, if you are the only werewolf, eliminate a player. Otherwise, "
|
||||
"discuss with your teammates and reach an agreement."
|
||||
)
|
||||
|
||||
wolves_discuss_parser = MarkdownJsonDictParser(
|
||||
content_hint={
|
||||
"thought": "what you thought",
|
||||
"speak": "what you speak",
|
||||
"finish_discussion": "whether the discussion reached an "
|
||||
"agreement or not (true/false)",
|
||||
},
|
||||
required_keys=["thought", "speak", "finish_discussion"],
|
||||
keys_to_memory="speak",
|
||||
keys_to_content="speak",
|
||||
keys_to_metadata=["finish_discussion"],
|
||||
)
|
||||
|
||||
to_wolves_vote = "Which player do you vote to kill?"
|
||||
|
||||
wolves_vote_parser = MarkdownJsonDictParser(
|
||||
content_hint={
|
||||
"thought": "what you thought",
|
||||
"vote": "player_name",
|
||||
},
|
||||
required_keys=["thought", "vote"],
|
||||
keys_to_memory="vote",
|
||||
keys_to_content="vote",
|
||||
)
|
||||
|
||||
to_wolves_res = "The player with the most votes is {}."
|
||||
|
||||
to_witch_resurrect = (
|
||||
"{witch_name}, you're the witch. Tonight {dead_name} is eliminated. "
|
||||
"Would you like to resurrect {dead_name}?"
|
||||
)
|
||||
|
||||
to_witch_resurrect_no = "The witch has chosen not to resurrect the player."
|
||||
to_witch_resurrect_yes = "The witch has chosen to resurrect the player."
|
||||
|
||||
witch_resurrect_parser = MarkdownJsonDictParser(
|
||||
content_hint={
|
||||
"thought": "what you thought",
|
||||
"speak": "whether to resurrect the player and the reason",
|
||||
"resurrect": "whether to resurrect the player or not (true/false)",
|
||||
},
|
||||
required_keys=["thought", "speak", "resurrect"],
|
||||
keys_to_memory="speak",
|
||||
keys_to_content="speak",
|
||||
keys_to_metadata=["resurrect"],
|
||||
)
|
||||
|
||||
to_witch_poison = (
|
||||
"Would you like to eliminate one player? If yes, "
|
||||
"specify the player_name."
|
||||
)
|
||||
|
||||
witch_poison_parser = MarkdownJsonDictParser(
|
||||
content_hint={
|
||||
"thought": "what you thought",
|
||||
"speak": "what you speak",
|
||||
"eliminate": "whether to eliminate a player or not (true/false)",
|
||||
},
|
||||
required_keys=["thought", "speak", "eliminate"],
|
||||
keys_to_memory="speak",
|
||||
keys_to_content="speak",
|
||||
keys_to_metadata=["eliminate"],
|
||||
)
|
||||
|
||||
to_seer = (
|
||||
"{}, you're the seer. Which player in {} would you like to check "
|
||||
"tonight?"
|
||||
)
|
||||
|
||||
seer_parser = MarkdownJsonDictParser(
|
||||
content_hint={
|
||||
"thought": "what you thought",
|
||||
"speak": "player_name",
|
||||
},
|
||||
required_keys=["thought", "speak"],
|
||||
keys_to_memory="speak",
|
||||
keys_to_content="speak",
|
||||
)
|
||||
|
||||
to_seer_result = "Okay, the role of {} is a {}."
|
||||
|
||||
to_all_danger = (
|
||||
"The day is coming, all the players open your eyes. Last night, "
|
||||
"the following player(s) has been eliminated: {}."
|
||||
)
|
||||
|
||||
to_all_peace = (
|
||||
"The day is coming, all the players open your eyes. Last night is "
|
||||
"peaceful, no player is eliminated."
|
||||
)
|
||||
|
||||
to_all_discuss = (
|
||||
"Now the alive players are {}. Given the game rules and your role, "
|
||||
"based on the situation and the information you gain, to vote a "
|
||||
"player eliminated among alive players and to win the game, what do "
|
||||
"you want to say to others? You can decide whether to reveal your "
|
||||
"role."
|
||||
)
|
||||
|
||||
survivors_discuss_parser = MarkdownJsonDictParser(
|
||||
content_hint={
|
||||
"thought": "what you thought",
|
||||
"speak": "what you speak",
|
||||
},
|
||||
required_keys=["thought", "speak"],
|
||||
keys_to_memory="speak",
|
||||
keys_to_content="speak",
|
||||
)
|
||||
|
||||
survivors_vote_parser = MarkdownJsonDictParser(
|
||||
content_hint={
|
||||
"thought": "what you thought",
|
||||
"vote": "player_name",
|
||||
},
|
||||
required_keys=["thought", "vote"],
|
||||
keys_to_memory="vote",
|
||||
keys_to_content="vote",
|
||||
)
|
||||
|
||||
to_all_vote = (
|
||||
"Given the game rules and your role, based on the situation and the"
|
||||
" information you gain, to win the game, it's time to vote one player"
|
||||
" eliminated among the alive players. Which player do you vote to "
|
||||
"kill?"
|
||||
)
|
||||
|
||||
to_all_res = "{} has been voted out."
|
||||
|
||||
to_all_wolf_win = (
|
||||
"The werewolves have prevailed and taken over the village. Better "
|
||||
"luck next time!"
|
||||
)
|
||||
|
||||
to_all_village_win = (
|
||||
"The game is over. The werewolves have been defeated, and the village "
|
||||
"is safe once again!"
|
||||
)
|
||||
|
||||
to_all_continue = "The game goes on."
|
||||
149
AlgoriAgent/examples/game_werewolf/werewolf.py
Normal file
149
AlgoriAgent/examples/game_werewolf/werewolf.py
Normal file
@@ -0,0 +1,149 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""A werewolf game implemented by agentscope."""
|
||||
from functools import partial
|
||||
|
||||
from prompt import Prompts
|
||||
from werewolf_utils import (
|
||||
check_winning,
|
||||
update_alive_players,
|
||||
majority_vote,
|
||||
extract_name_and_id,
|
||||
n2s,
|
||||
set_parsers,
|
||||
)
|
||||
from agentscope.message import Msg
|
||||
from agentscope.msghub import msghub
|
||||
from agentscope.pipelines.functional import sequentialpipeline
|
||||
import agentscope
|
||||
|
||||
|
||||
# pylint: disable=too-many-statements
|
||||
def main() -> None:
|
||||
"""werewolf game"""
|
||||
# default settings
|
||||
HostMsg = partial(Msg, name="Moderator", role="assistant", echo=True)
|
||||
healing, poison = True, True
|
||||
MAX_WEREWOLF_DISCUSSION_ROUND = 3
|
||||
MAX_GAME_ROUND = 6
|
||||
# read model and agent configs, and initialize agents automatically
|
||||
survivors = agentscope.init(
|
||||
model_configs="./configs/model_configs.json",
|
||||
agent_configs="./configs/agent_configs.json",
|
||||
project="Werewolf",
|
||||
)
|
||||
|
||||
roles = ["werewolf", "werewolf", "villager", "villager", "seer", "witch"]
|
||||
wolves, witch, seer = survivors[:2], survivors[-1], survivors[-2]
|
||||
|
||||
# start the game
|
||||
for _ in range(1, MAX_GAME_ROUND + 1):
|
||||
# night phase, werewolves discuss
|
||||
hint = HostMsg(content=Prompts.to_wolves.format(n2s(wolves)))
|
||||
with msghub(wolves, announcement=hint) as hub:
|
||||
set_parsers(wolves, Prompts.wolves_discuss_parser)
|
||||
for _ in range(MAX_WEREWOLF_DISCUSSION_ROUND):
|
||||
x = sequentialpipeline(wolves)
|
||||
if x.metadata.get("finish_discussion", False):
|
||||
break
|
||||
|
||||
# werewolves vote
|
||||
set_parsers(wolves, Prompts.wolves_vote_parser)
|
||||
hint = HostMsg(content=Prompts.to_wolves_vote)
|
||||
votes = [
|
||||
extract_name_and_id(wolf(hint).content)[0] for wolf in wolves
|
||||
]
|
||||
# broadcast the result to werewolves
|
||||
dead_player = [majority_vote(votes)]
|
||||
hub.broadcast(
|
||||
HostMsg(content=Prompts.to_wolves_res.format(dead_player[0])),
|
||||
)
|
||||
|
||||
# witch
|
||||
healing_used_tonight = False
|
||||
if witch in survivors:
|
||||
if healing:
|
||||
hint = HostMsg(
|
||||
content=Prompts.to_witch_resurrect.format_map(
|
||||
{
|
||||
"witch_name": witch.name,
|
||||
"dead_name": dead_player[0],
|
||||
},
|
||||
),
|
||||
)
|
||||
set_parsers(witch, Prompts.witch_resurrect_parser)
|
||||
if witch(hint).metadata.get("resurrect", False):
|
||||
healing_used_tonight = True
|
||||
dead_player.pop()
|
||||
healing = False
|
||||
HostMsg(content=Prompts.to_witch_resurrect_yes)
|
||||
else:
|
||||
HostMsg(content=Prompts.to_witch_resurrect_no)
|
||||
|
||||
if poison and not healing_used_tonight:
|
||||
set_parsers(witch, Prompts.witch_poison_parser)
|
||||
x = witch(HostMsg(content=Prompts.to_witch_poison))
|
||||
if x.metadata.get("eliminate", False):
|
||||
dead_player.append(extract_name_and_id(x.content)[0])
|
||||
poison = False
|
||||
|
||||
# seer
|
||||
if seer in survivors:
|
||||
hint = HostMsg(
|
||||
content=Prompts.to_seer.format(seer.name, n2s(survivors)),
|
||||
)
|
||||
set_parsers(seer, Prompts.seer_parser)
|
||||
x = seer(hint)
|
||||
|
||||
player, idx = extract_name_and_id(x.content)
|
||||
role = "werewolf" if roles[idx] == "werewolf" else "villager"
|
||||
hint = HostMsg(content=Prompts.to_seer_result.format(player, role))
|
||||
seer.observe(hint)
|
||||
|
||||
survivors, wolves = update_alive_players(
|
||||
survivors,
|
||||
wolves,
|
||||
dead_player,
|
||||
)
|
||||
if check_winning(survivors, wolves, "Moderator"):
|
||||
break
|
||||
|
||||
# daytime discussion
|
||||
content = (
|
||||
Prompts.to_all_danger.format(n2s(dead_player))
|
||||
if dead_player
|
||||
else Prompts.to_all_peace
|
||||
)
|
||||
hints = [
|
||||
HostMsg(content=content),
|
||||
HostMsg(content=Prompts.to_all_discuss.format(n2s(survivors))),
|
||||
]
|
||||
with msghub(survivors, announcement=hints) as hub:
|
||||
# discuss
|
||||
set_parsers(survivors, Prompts.survivors_discuss_parser)
|
||||
x = sequentialpipeline(survivors)
|
||||
|
||||
# vote
|
||||
set_parsers(survivors, Prompts.survivors_vote_parser)
|
||||
hint = HostMsg(content=Prompts.to_all_vote.format(n2s(survivors)))
|
||||
votes = [
|
||||
extract_name_and_id(_(hint).content)[0] for _ in survivors
|
||||
]
|
||||
vote_res = majority_vote(votes)
|
||||
# broadcast the result to all players
|
||||
result = HostMsg(content=Prompts.to_all_res.format(vote_res))
|
||||
hub.broadcast(result)
|
||||
|
||||
survivors, wolves = update_alive_players(
|
||||
survivors,
|
||||
wolves,
|
||||
vote_res,
|
||||
)
|
||||
|
||||
if check_winning(survivors, wolves, "Moderator"):
|
||||
break
|
||||
|
||||
hub.broadcast(HostMsg(content=Prompts.to_all_continue))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
86
AlgoriAgent/examples/game_werewolf/werewolf_utils.py
Normal file
86
AlgoriAgent/examples/game_werewolf/werewolf_utils.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""utils."""
|
||||
import re
|
||||
from typing import Union, Any, Sequence
|
||||
|
||||
import numpy as np
|
||||
from loguru import logger
|
||||
|
||||
from prompt import Prompts
|
||||
from agentscope.agents import AgentBase
|
||||
from agentscope.message import Msg
|
||||
|
||||
|
||||
def check_winning(alive_agents: list, wolf_agents: list, host: str) -> bool:
|
||||
"""check which group wins"""
|
||||
if len(wolf_agents) * 2 >= len(alive_agents):
|
||||
msg = Msg(host, Prompts.to_all_wolf_win, role="assistant")
|
||||
logger.chat(msg)
|
||||
return True
|
||||
if alive_agents and not wolf_agents:
|
||||
msg = Msg(host, Prompts.to_all_village_win, role="assistant")
|
||||
logger.chat(msg)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def update_alive_players(
|
||||
survivors: Sequence[AgentBase],
|
||||
wolves: Sequence[AgentBase],
|
||||
dead_names: Union[str, list[str]],
|
||||
) -> tuple[list, list]:
|
||||
"""update the list of alive agents"""
|
||||
if not isinstance(dead_names, list):
|
||||
dead_names = [dead_names]
|
||||
return [_ for _ in survivors if _.name not in dead_names], [
|
||||
_ for _ in wolves if _.name not in dead_names
|
||||
]
|
||||
|
||||
|
||||
def majority_vote(votes: list) -> Any:
|
||||
"""majority_vote function"""
|
||||
votes_valid = [item for item in votes if item != "Abstain"]
|
||||
# Count the votes excluding abstentions.
|
||||
unit, counts = np.unique(votes_valid, return_counts=True)
|
||||
return unit[np.argmax(counts)]
|
||||
|
||||
|
||||
def extract_name_and_id(name: str) -> tuple[str, int]:
|
||||
"""extract player name and id from a string"""
|
||||
try:
|
||||
name = re.search(r"\b[Pp]layer\d+\b", name).group(0)
|
||||
idx = int(re.search(r"[Pp]layer(\d+)", name).group(1)) - 1
|
||||
except AttributeError:
|
||||
# In case Player remains silent or speaks to abstain.
|
||||
logger.warning(f"vote: invalid name {name}, set to Abstain")
|
||||
name = "Abstain"
|
||||
idx = -1
|
||||
return name, idx
|
||||
|
||||
|
||||
def n2s(agents: Sequence[Union[AgentBase, str]]) -> str:
|
||||
"""combine agent names into a string, and use "and" to connect the last
|
||||
two names."""
|
||||
|
||||
def _get_name(agent_: Union[AgentBase, str]) -> str:
|
||||
return agent_.name if isinstance(agent_, AgentBase) else agent_
|
||||
|
||||
if len(agents) == 1:
|
||||
return _get_name(agents[0])
|
||||
|
||||
return (
|
||||
", ".join([_get_name(_) for _ in agents[:-1]])
|
||||
+ " and "
|
||||
+ _get_name(agents[-1])
|
||||
)
|
||||
|
||||
|
||||
def set_parsers(
|
||||
agents: Union[AgentBase, list[AgentBase]],
|
||||
parser_name: str,
|
||||
) -> None:
|
||||
"""Add parser to agents"""
|
||||
if not isinstance(agents, list):
|
||||
agents = [agents]
|
||||
for agent in agents:
|
||||
agent.set_parser(parser_name)
|
||||
Reference in New Issue
Block a user