File size: 6,215 Bytes
09ee734 c2392fe 975158e 2f9cbc8 270b042 09ee734 8d942c4 dfdde45 ae85ba5 09ee734 ae85ba5 09ee734 0c31321 2f9cbc8 09ee734 2f9cbc8 09ee734 3ea5035 270b042 8d942c4 3ea5035 270b042 46ba8c8 1373c22 2f9cbc8 1373c22 46ba8c8 1373c22 46ba8c8 2f9cbc8 46ba8c8 2f9cbc8 c6c2b98 1373c22 dfdde45 1373c22 5de0b8a 1373c22 5de0b8a 1373c22 c6c2b98 1373c22 7877562 8d942c4 09ee734 1373c22 7877562 1373c22 7877562 1373c22 5de0b8a 1373c22 2f9cbc8 7877562 1373c22 8d942c4 2f9cbc8 8d942c4 7877562 8d942c4 2f9cbc8 1373c22 2f9cbc8 8d942c4 46ba8c8 2f9cbc8 09ee734 bee27cc 1373c22 c6c2b98 1373c22 975158e 09ee734 3ea5035 c6c2b98 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
from typing import Optional, Type, List, ClassVar
from pydantic import BaseModel, Field
from game_utils import *
from message import Message, MessageType, AgentMessage
from agent_interfaces import HumanAgentCLI, OpenAIAgentInterface, HumanAgentInterface
from player import Player
from data_collection import save
# Abstracting the Game Class is a WIP so that future games can be added
class Game(BaseModel):
"""Base class for all games."""
# Required
game_id: str
"""The unique id of the game."""
players: List[Player] = Field(exclude=True)
"""The players in the game."""
observer: Optional[Player]
"""An observer who can see all public messages, but doesn't actually play."""
# Default
winner_id: str | None = None
"""The id of the player who has won the game."""
game_state: str = Field("game_start", exclude=True)
"""Keeps track of the current state of the game."""
awaiting_input: bool = Field(False, exclude=True)
"""Whether the game is currently awaiting input from a player."""
# Class Variables
number_of_players: ClassVar[int]
"""The number of players in the game."""
player_class: ClassVar[Type[Player]] = Player
"""The class of the player used in the game."""
def player_from_id(self, player_id: str) -> Player:
"""Returns a player from their ID."""
return next((player for player in self.players if player.player_id == player_id), None)
def player_from_name(self, name: str) -> Player:
"""Returns a player from their name."""
return next((player for player in self.players if player.name == name), None)
def game_message(
self,
content: str,
recipient: Player | List[Player] | None = None, # If None, message is broadcast to all players
exclude: bool = False, # If True, the message is broadcast to all players except the chosen player
message_type: MessageType = "info"
):
"""
Sends a message to a player or all players.
If no recipient is specified, the message is broadcast to all players.
If exclude is True, the message is broadcast to all players except the recipient.
Some message types are only available to player with access (e.g. verbose, debug).
"""
if exclude or not recipient:
# These are public messages, exclude is used to exclude the sender from the recipient list.
recipients = [player for player in self.players if player != recipient]
if self.observer:
recipients.append(self.observer)
else:
if isinstance(recipient, Player):
recipients = [recipient]
else:
recipients = recipient
message = Message(type=message_type, content=content)
recipient_ids = []
for player in recipients:
if player.can_receive_message(message_type):
player.interface.add_message(message)
recipient_ids.append(player.player_id)
agent_message = AgentMessage.from_message(message, recipient_ids, self.game_id)
save(agent_message)
def verbose_message(self, content: str, **kwargs):
"""
Sends a verbose message to all players capable of receiving them.
Verbose messages are used to communicate in real time what is happening that cannot be seen publicly.
Ex: "Abby is thinking..."
"""
self.game_message(content, **kwargs, message_type="verbose")
def debug_message(self, content: str, **kwargs):
"""
Sends a debug message to all players capable of receiving them.
Debug messages usually contain secret information and should only be sent when it wouldn't spoil the game.
Ex: "Abby is the chameleon."
"""
self.game_message(content, **kwargs, message_type="debug")
def run_game(self):
"""Runs the game."""
raise NotImplementedError("The run_game method must be implemented by the subclass.")
def end_game(self):
"""Ends the game and declares a winner."""
for player in self.players:
save(player)
save(self)
@classmethod
def from_human_name(
cls, human_name: str = None,
human_interface: Type[HumanAgentInterface] = HumanAgentCLI,
human_message_level: str = "verbose"
):
"""
Instantiates a game with a human player if a name is provided.
Otherwise, the game is instantiated with all AI players and an observer.
"""
game_id = generate_game_id()
# Gather Player Names
if human_name:
ai_names = random_names(cls.number_of_players - 1, human_name)
human_index = random_index(cls.number_of_players)
else:
ai_names = random_names(cls.number_of_players)
human_index = None
# Add Players
players = []
for i in range(0, cls.number_of_players):
player_dict = {"game_id": game_id}
if human_index == i:
player_dict["name"] = human_name
player_id = f"{game_id}-human"
player_dict["interface"] = human_interface(agent_id=player_id, game_id=game_id)
player_dict["message_level"] = human_message_level
else:
player_dict["name"] = ai_names.pop()
player_id = f"{game_id}-{player_dict['name']}"
# all AI players use the OpenAI interface for now - this can be changed in the future
player_dict["interface"] = OpenAIAgentInterface(agent_id=player_id, game_id=game_id)
player_dict["message_level"] = "info"
player_dict["player_id"] = player_id
players.append(cls.player_class(**player_dict))
# Add Observer - an Agent who can see all the messages, but doesn't actually play
if human_index is None:
observer = Player.observer(game_id, interface_type=human_interface)
else:
observer = None
return cls(game_id=game_id, players=players, observer=observer)
|