|
from typing import Optional, Type |
|
|
|
from game_utils import random_index |
|
from game_utils_chameleon import * |
|
from output_formats import * |
|
from player import ChameleonPlayer, Player |
|
from prompts import fetch_prompt, format_prompt |
|
from message import Message, MessageType |
|
from agent_interfaces import HumanAgentCLI, OpenAIAgentInterface, HumanAgentInterface |
|
from game import Game |
|
|
|
|
|
NUMBER_OF_PLAYERS = 6 |
|
WINNING_SCORE = 3 |
|
|
|
|
|
class ChameleonGame(Game): |
|
"""The main game class, handles the game logic and player interactions.""" |
|
|
|
number_of_players = NUMBER_OF_PLAYERS |
|
|
|
winning_score = WINNING_SCORE |
|
"""The Number of points required to win the game.""" |
|
|
|
def __init__(self, *args, **kwargs): |
|
|
|
super().__init__(*args, **kwargs) |
|
|
|
|
|
self.players: List[ChameleonPlayer] = [ChameleonPlayer.from_player(player) for player in self.players] |
|
|
|
|
|
self.chameleon_ids: List[str] = [] |
|
"""Record of which player was the chameleon for each round.""" |
|
self.herd_animals: List[str] = [] |
|
"""Record of what animal was the herd animal for each round.""" |
|
self.all_animal_descriptions: List[List[dict]] = [] |
|
"""Record of the animal descriptions each player has given for each round.""" |
|
self.chameleon_guesses: List[str] = [] |
|
"""Record of what animal the chameleon guessed for each round.""" |
|
self.herd_vote_tallies: List[List[dict]] = [] |
|
"""Record of the votes of each herd member for the chameleon for each round.""" |
|
|
|
|
|
|
|
@property |
|
def chameleon(self) -> ChameleonPlayer: |
|
"""Returns the current chameleon.""" |
|
return self.player_from_id(self.chameleon_ids[-1]) |
|
|
|
@property |
|
def herd_animal(self) -> str: |
|
"""Returns the current herd animal.""" |
|
return self.herd_animals[-1] |
|
|
|
@property |
|
def round_animal_descriptions(self) -> List[dict]: |
|
"""Returns the current animal descriptions.""" |
|
return self.all_animal_descriptions[-1] |
|
|
|
@property |
|
def chameleon_guess(self) -> str: |
|
"""Returns the current chameleon guess.""" |
|
return self.chameleon_guesses[-1] |
|
|
|
@property |
|
def herd_vote_tally(self) -> List[dict]: |
|
"""Returns the current herd vote tally.""" |
|
return self.herd_vote_tallies[-1] |
|
|
|
@property |
|
def round_number(self) -> int: |
|
"""Returns the current round number.""" |
|
return len(self.herd_animals) |
|
|
|
def format_animal_descriptions(self, exclude: Player = None) -> str: |
|
"""Formats the animal description responses of the players into a single string.""" |
|
formatted_responses = "" |
|
for response in self.round_animal_descriptions: |
|
|
|
if response["player_id"] != exclude.id: |
|
player = self.player_from_id(response["player_id"]) |
|
formatted_responses += f" - {player.name}: {response['description']}\n" |
|
|
|
return formatted_responses |
|
|
|
async def run_game(self): |
|
"""Sets up the game. This includes assigning roles and gathering player names.""" |
|
self.game_message(fetch_prompt("game_rules")) |
|
|
|
self.setup_round() |
|
|
|
self.run_round() |
|
|
|
self.resolve_round() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def setup_round(self): |
|
"""Sets up the round. This includes assigning roles and gathering player names.""" |
|
|
|
herd_animal = random_animal() |
|
self.herd_animals.append(herd_animal) |
|
self.debug_message(f"The secret animal is {herd_animal}.") |
|
|
|
|
|
chameleon_index = random_index(len(self.players)) |
|
self.chameleon_ids.append(self.players[chameleon_index].id) |
|
|
|
for i, player in enumerate(self.players): |
|
if i == chameleon_index: |
|
player.assign_role("chameleon") |
|
self.game_message(fetch_prompt("assign_chameleon"), player) |
|
self.debug_message(f"{player.name} is the Chameleon!") |
|
else: |
|
player.assign_role("herd") |
|
self.game_message(format_prompt("assign_herd", herd_animal=herd_animal), player) |
|
|
|
|
|
self.all_animal_descriptions.append([]) |
|
|
|
|
|
self.herd_vote_tallies.append([]) |
|
|
|
self.game_message(f"Each player will now take turns describing themselves:") |
|
|
|
def run_round(self): |
|
"""Starts the round.""" |
|
|
|
for current_player in self.players: |
|
self.game_message(fetch_prompt("player_describe_animal"), current_player) |
|
self.player_turn_animal_description(current_player) |
|
|
|
|
|
self.game_message("All players have spoken. The Chameleon will now guess the secret animal...") |
|
player_responses = self.format_animal_descriptions(exclude=self.chameleon) |
|
self.game_message(format_prompt("chameleon_guess_animal", player_responses=player_responses), self.chameleon) |
|
self.player_turn_chameleon_guess(self.chameleon) |
|
|
|
|
|
for current_player in self.players: |
|
if current_player.role == "herd": |
|
player_responses = self.format_animal_descriptions(exclude=current_player) |
|
self.game_message(format_prompt("vote", player_responses=player_responses), current_player) |
|
self.player_turn_herd_vote(current_player) |
|
|
|
def player_turn_animal_description(self, player: Player): |
|
"""Handles a player's turn to describe themselves.""" |
|
if player.interface.is_ai: |
|
self.verbose_message(f"{player.name} is thinking...") |
|
|
|
prompt = fetch_prompt("player_describe_animal") |
|
|
|
|
|
response = player.interface.generate_formatted_response(AnimalDescriptionFormat) |
|
|
|
self.round_animal_descriptions.append({"player_id": player.id, "description": response.description}) |
|
|
|
self.game_message(f"{player.name}: {response.description}", player, exclude=True) |
|
|
|
def player_turn_chameleon_guess(self, chameleon: Player): |
|
"""Handles the Chameleon's turn to guess the secret animal.""" |
|
|
|
if chameleon.interface.is_ai or self.observer: |
|
self.verbose_message("The Chameleon is thinking...") |
|
|
|
response = chameleon.interface.generate_formatted_response(ChameleonGuessFormat) |
|
|
|
self.game_message("The Chameleon has guessed the animal. Now the Herd will vote on who they think the chameleon is.") |
|
|
|
self.chameleon_guesses.append(response.animal) |
|
|
|
def player_turn_herd_vote(self, player: Player): |
|
"""Handles a player's turn to vote for the Chameleon.""" |
|
if player.interface.is_ai: |
|
self.verbose_message(f"{player.name} is thinking...") |
|
|
|
|
|
additional_fields = {"player_names": [p.name for p in self.players if p != player]} |
|
response = player.interface.generate_formatted_response(HerdVoteFormat, additional_fields=additional_fields) |
|
|
|
self.debug_message(f"{player.name} voted for {response.vote}", recipient=player, exclude=True) |
|
|
|
voted_for_player = self.player_from_name(response.vote) |
|
|
|
|
|
self.herd_vote_tally.append({"voter_id": player.id, "voted_for_id": voted_for_player.id}) |
|
|
|
def resolve_round(self): |
|
"""Resolves the round, assigns points, and prints the results.""" |
|
self.game_message("All players have voted!") |
|
for vote in self.herd_vote_tally: |
|
voter = self.player_from_id(vote["voter_id"]) |
|
voted_for = self.player_from_id(vote["voted_for_id"]) |
|
self.game_message(f"{voter.name} voted for {voted_for.name}") |
|
|
|
accused_player_id = count_chameleon_votes(self.herd_vote_tally) |
|
|
|
self.game_message(f"The round is over. Calculating results...") |
|
self.game_message( |
|
f"The Chameleon was {self.chameleon.name}, and they guessed the secret animal was {self.chameleon_guess}.") |
|
self.game_message(f"The secret animal was actually was {self.herd_animal}.") |
|
|
|
if accused_player_id: |
|
accused_name = self.player_from_id(accused_player_id).name |
|
self.game_message(f"The Herd voted for {accused_name} as the Chameleon.") |
|
else: |
|
self.game_message(f"The Herd could not come to a consensus.") |
|
|
|
|
|
|
|
if self.chameleon_guess.lower() == self.herd_animal.lower(): |
|
self.chameleon.points += 1 |
|
|
|
|
|
else: |
|
for player in self.players: |
|
if player.role == "herd": |
|
player.points += 1 |
|
|
|
for vote in self.herd_vote_tally: |
|
if vote["voted_for_id"] == self.chameleon.id: |
|
self.player_from_id(vote['voter_id']).points += 1 |
|
|
|
|
|
if not accused_player_id or accused_player_id != self.chameleon.id: |
|
self.chameleon.points += 1 |
|
|
|
|
|
player_points = "\n".join([f"{player.name}: {player.points}" for player in self.players]) |
|
self.game_message(f"Current Game Score:\n{player_points}") |