Skip to content

Commit

Permalink
Merge pull request #2 from dirkbrnd/v2
Browse files Browse the repository at this point in the history
V0.2.0 - Addition of counter actions
  • Loading branch information
dirkbrnd authored Dec 16, 2023
2 parents ae0422b + 20e113d commit 13e8b60
Show file tree
Hide file tree
Showing 8 changed files with 279 additions and 117 deletions.
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[flake8]
ignore = W293
ignore = W293,W291
max-line-length = 120
max-complexity = 12
3 changes: 0 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,18 @@ repos:
- id: isort
args: ["--profile", "black"]
name: isort
exclude: scripts
entry: poetry run isort
language: system
types: [python]

- id: black
name: black
exclude: scripts
entry: poetry run black
language: system
types: [python]

- id: flake8
name: flake8
entry: poetry run flake8
exclude: scripts
language: system
types: [python]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ git clone https://github.com/dirkbrnd/Resistance-Coup-Autogen.git
poetry install
```

3. Launch!
3. Launch and watch the AI agents play the game!

```sh
python coup.py
Expand Down
61 changes: 42 additions & 19 deletions coup.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,53 @@
import random
import sys

from autogen import GroupChat, GroupChatManager, UserProxyAgent, config_list_from_dotenv, AssistantAgent
from autogen.agentchat.contrib.gpt_assistant_agent import GPTAssistantAgent
from autogen import (
AssistantAgent,
GroupChat,
GroupChatManager,
UserProxyAgent,
config_list_from_dotenv,
)

from src.ai.agents import create_player_agent, create_game_master_agent, create_user_proxy
from src.ai.agents import (
create_game_master_agent,
create_player_agent,
create_user_proxy,
)
from src.handler.game_handler import ResistanceCoupGameHandler

# SEED = 42
config_list = config_list_from_dotenv(
dotenv_file_path='.env',
filter_dict={
"model": {
"gpt-4",
# "gpt-3.5-turbo",
}
}
)
dotenv_file_path=".env",
filter_dict={
"model": {
"gpt-4",
# "gpt-3.5-turbo",
}
},
)


def main():
# Create game handler with 5 players
handler = ResistanceCoupGameHandler(5)
# Create game handler with 3 players
handler = ResistanceCoupGameHandler(3)
print(f"First player is {handler.current_player}")

# Create AI players
agent_players = []
for ind, player in enumerate(handler.players):
agent_players.append(create_player_agent(name=player.name, other_player_names=[other_player.name for other_player in handler.players if other_player.name != player.name],
cards=player.cards, strategy=player.strategy, handler=handler, config_list=config_list))
agent_players.append(
create_player_agent(
name=player.name,
other_player_names=[
other_player.name
for other_player in handler.players
if other_player.name != player.name
],
cards=player.cards,
strategy=player.strategy,
handler=handler,
config_list=config_list,
)
)

# Game master
game_master: AssistantAgent = create_game_master_agent(handler, config_list)
Expand All @@ -37,15 +56,19 @@ def main():
user_proxy: UserProxyAgent = create_user_proxy(config_list)

# Define group chat
group_chat = GroupChat(agents=[user_proxy, game_master, *agent_players], messages=[], admin_name=game_master.name, max_round=100)
group_chat = GroupChat(
agents=[user_proxy, game_master, *agent_players],
messages=[],
admin_name=game_master.name,
max_round=1000,
)
manager = GroupChatManager(groupchat=group_chat, llm_config={"config_list": config_list})

task = """
Play a game of The Resistance: Coup until there is a single winner.
"""

game_master.initiate_chat(manager, message=task)
# handler.perform_action(ActionType.income)
print("GAME OVER")


Expand Down
149 changes: 114 additions & 35 deletions src/ai/agents.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
from autogen import UserProxyAgent, AssistantAgent
from autogen.agentchat.contrib.gpt_assistant_agent import GPTAssistantAgent
from autogen import AssistantAgent, UserProxyAgent

from src.handler.game_handler import ResistanceCoupGameHandler
from src.models.action import ActionType
from src.models.card import Card
from src.models.player import PlayerStrategy



# SEED = 42

def create_user_proxy(config_list: list) -> UserProxyAgent:
llm_config = {
"config_list": config_list,
# "seed": SEED,
"temperature": 0,
}

Expand All @@ -21,7 +18,8 @@ def create_user_proxy(config_list: list) -> UserProxyAgent:
llm_config=llm_config,
is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"),
system_message="""
You are facilitating a game of The Resistance: Coup between five players. Respond with TERMINATE once the game has a winner.
You are facilitating a game of The Resistance: Coup between five players.
Respond with TERMINATE once the game has a winner.
At the start of the game, you will inform the starting player that it is their turn.
In between each player's turn you have to retrieve the game state and provide it to the players.
""",
Expand All @@ -33,34 +31,39 @@ def create_user_proxy(config_list: list) -> UserProxyAgent:
return user_proxy


def create_game_master_agent(handler: ResistanceCoupGameHandler, config_list: list) -> AssistantAgent:
def create_game_master_agent(
handler: ResistanceCoupGameHandler, config_list: list
) -> AssistantAgent:
llm_config = {
"config_list": config_list,
# "seed": SEED,
"temperature": 1,
"functions": [
{
"name": "get_game_state",
"description": "Get the current state of the game",
"parameters": {
"type": "object",
"properties": {
},
}
"properties": {},
},
},
]
],
}

instructions = f"""
You are the game master in a game of The Resistance: Coup between {handler.number_of_players} players.
You are the game master in a game of The Resistance: Coup between {len(handler.players)} players.
At the start of the game, you will inform the starting player that it is their turn and announce to everyone the starting game state.
At the start of the game, you will inform the starting player that it is their turn and announce to
everyone the starting game state.
In between each player's turn you have to retrieve the game state and provide it to the current player.
Make sure the correct player is taking their next turn based on the game state.
Players can counter another player's action if action_can_be_countered is "True" after they
tried to perform an action.
Once there is only one active player left in the game, you can declare the game is over and we have a winner.
Don't start another game after it has ended. Don't offer to the other players to play another game.
"""

game_master = AssistantAgent(
Expand All @@ -71,17 +74,22 @@ def create_game_master_agent(handler: ResistanceCoupGameHandler, config_list: li
"get_game_state": handler.get_game_state,
},
# max_consecutive_auto_reply=100,
description="The game master in a game of The Resistance Coup"
description="The game master in a game of The Resistance Coup",
)
return game_master


def create_player_agent(name: str, other_player_names: list[str], cards: list[Card], strategy: str,
handler: ResistanceCoupGameHandler, config_list: list) -> AssistantAgent:
def create_player_agent(
name: str,
other_player_names: list[str],
cards: list[Card],
strategy: PlayerStrategy,
handler: ResistanceCoupGameHandler,
config_list: list,
) -> AssistantAgent:
llm_config = {
"config_list": config_list,
# "seed": SEED,
"temperature": 0.3,
"temperature": 0.5,
"functions": [
{
"name": "perform_action",
Expand All @@ -92,46 +100,115 @@ def create_player_agent(name: str, other_player_names: list[str], cards: list[Ca
"player_name": {
"type": "string",
"description": "Send your own name.",
"enum": [name]
"enum": [name],
},
"action_name": {
"type": "string",
"description": "The name of the action to perform.",
"enum": [ActionType.income, ActionType.foreign_aid, ActionType.tax, ActionType.coup,
ActionType.steal, ActionType.assassinate, ActionType.exchange]
"enum": [
ActionType.income,
ActionType.foreign_aid,
ActionType.tax,
ActionType.coup,
ActionType.steal,
ActionType.assassinate,
ActionType.exchange,
],
},
"target_player_name": {
"type": "string",
"description": "The player name to target.",
},
},
"required": [
"player_name",
"action_name"
]
}
}
]
"required": ["player_name", "action_name"],
},
},
{
"name": "counter_action",
"description": "Counter the previous action that was performed",
"parameters": {
"type": "object",
"properties": {
"countering_player_name": {
"type": "string",
"description": "Send your own name.",
"enum": [name],
},
},
"required": ["countering_player_name"],
},
},
{
"name": "execute_action",
"description": "Execute the action that was performed and complete the turn.",
"parameters": {
"type": "object",
"properties": {
"player_name": {
"type": "string",
"description": "Send your own name.",
"enum": [name],
},
"action_name": {
"type": "string",
"description": "The name of the action to perform.",
"enum": [
ActionType.income,
ActionType.foreign_aid,
ActionType.tax,
ActionType.coup,
ActionType.steal,
ActionType.assassinate,
ActionType.exchange,
],
},
"target_player_name": {
"type": "string",
"description": "The player name to target.",
},
},
"required": ["player_name", "action_name"],
},
},
],
}

if strategy == PlayerStrategy.aggressive:
strategy_str = ("Your strategy is to play aggressive. Try to assassinate, coup, or steal as soon as you can. "
"Feel free to bluff, but be careful because it can be challenged. If you keep getting blocked,"
"rather get income on your next turn, before playing aggressive again.")
elif strategy == PlayerStrategy.conservative:
strategy_str = ("Your strategy is to play conservative. "
"Build up your money, avoid bluffing, and wait for the opportunity to perform a coup.")
else:
strategy_str = ("Your strategy is to perform a coup as soon as you can and gather money as "
"quickly as possible.")

instructions = f"""Your name is {name} and you are a player in the game The Resistance: Coup.
You are playing against {", ".join(other_player_names)}.
You start with a {str(cards[0])} card and a {str(cards[1])} card, as well as 2 coins.
On your turn you have to pick a valid action based on your current available cards and coins. Also provide your own name to the function.
On your turn you have to pick a valid action based on your current available cards and coins.
Also provide your own name to the function.
Never announce what cards you have, they are secret.
If your action was invalid, you have to pick another action. However feel free to bluff and perform an action even if you don't have the card.
If your action was invalid, you have to pick another action. However feel free to bluff and perform an
action even if you don't have the card, but be careful because it could be challenged.
The possible actions are {[ActionType.income, ActionType.foreign_aid, ActionType.tax, ActionType.coup, ActionType.steal, ActionType.assassinate, ActionType.exchange]}
You can counter another player's action if action_can_be_countered is "True",
after they tried to perform their action.
If no one counters your action, you have the call "execute_action" to complete the turn.
If after perform_action you find that turn_complete is "True", you don't have to execute your action.
You also chit-chat with your opponent when you communicate an action to light up the mood.
You should ensure both you and your opponents are making valid actions. Also that everyone is only taking actions when it is their turn.
You should ensure both you and your opponents are making valid actions.
Also that everyone is only taking actions when it is their turn.
Your strategy should be to play {strategy}.
{strategy_str}
Don't hoard up coins, but rather try the assassinate or coup actions when you have a chance.
Expand All @@ -145,9 +222,11 @@ def create_player_agent(name: str, other_player_names: list[str], cards: list[Ca
llm_config=llm_config,
function_map={
"perform_action": handler.perform_action,
"counter_action": handler.counter_action,
"execute_action": handler.execute_action,
},
max_consecutive_auto_reply=100,
description=f"The player named {name} the game of The Resistance Coup"
description=f"The player named {name} the game of The Resistance Coup",
)

return player
Loading

0 comments on commit 13e8b60

Please sign in to comment.