File size: 14,016 Bytes
6605c56 09ee734 1373c22 6605c56 1373c22 09ee734 1373c22 09ee734 1373c22 09ee734 1373c22 09ee734 6605c56 09ee734 1373c22 6605c56 1373c22 2f9cbc8 1373c22 f975b0f 21c7651 f975b0f 8d942c4 6605c56 8d942c4 6605c56 f975b0f 1373c22 f975b0f 8d942c4 1373c22 f975b0f 1373c22 f975b0f cc401d9 f975b0f 8d942c4 1373c22 f975b0f 1373c22 6605c56 1373c22 2f9cbc8 1373c22 c6447fa 1373c22 c6447fa 8d942c4 c6447fa 1373c22 c6447fa 1373c22 c6447fa cc401d9 c6447fa 1373c22 c6447fa 1373c22 c6447fa 1373c22 c6447fa 8d942c4 1373c22 c6447fa 1373c22 c6447fa 1373c22 6605c56 1373c22 8d942c4 1373c22 8d942c4 1373c22 6605c56 8d942c4 6605c56 |
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 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
from collections import Counter
from typing import ClassVar
from game_utils import random_index
from output_formats import *
from player import ChameleonPlayer, Player
from prompts import fetch_prompt, format_prompt
from game import Game
# Default Values
NUMBER_OF_PLAYERS = 6
WINNING_SCORE = 7
AVAILABLE_ANIMALS = ["Dog", "Cat", "Mouse", "Hamster", "Monkey", "Rabbit", "Fox", "Bear", "Panda", "Koala", "Tiger",
"Lion", "Cow", "Pig", "Frog", "Owl", "Duck", "Chicken", "Butterfly", "Turtle", "Snake", "Octopus",
"Squid", "Hedgehog", "Elephant", "Rhinoceros", "Zebra", "Crocodile", "Whale", "Dolphin", "Camel",
"Giraffe", "Deer", "Gorilla", "Goat", "Llama", "Horse", "Unicorn", "Flamingo", "Skunk", "Shark"]
class ChameleonGame(Game):
"""The main game class, handles the game logic and player interactions."""
# Defaults
winning_score: int = WINNING_SCORE
"""The Number of points required to win the game."""
available_animals: List[str] = Field(AVAILABLE_ANIMALS, exclude=True)
"""The list of animals that can be chosen as the secret animal."""
chameleon_ids: List[str] = []
"""Record of which player was the chameleon for each round."""
herd_animals: List[str] = []
"""Record of what animal was the herd animal for each round."""
all_animal_descriptions: List[List[dict]] = []
"""Record of the animal descriptions each player has given for each round."""
chameleon_guesses: List[str] = []
"""Record of what animal the chameleon guessed for each round."""
herd_vote_tallies: List[List[dict]] = []
"""Record of the votes of each herd member for the chameleon for each round."""
# Class Variables
number_of_players: ClassVar[int] = NUMBER_OF_PLAYERS
"""The number of players in the game."""
player_class: ClassVar[Type[Player]] = ChameleonPlayer
"""The class of the player used in the game."""
@property
def chameleon(self) -> ChameleonPlayer:
"""Returns the current chameleon."""
return self.player_from_id(self.chameleon_ids[-1])
@property
def chameleon_id(self) -> str:
"""Returns the current chameleon's id."""
return 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]
def run_game(self):
"""Starts the game."""
# Check if the game has not been won
if self.game_state != "game_end":
if self.game_state == "game_start":
self.game_message(fetch_prompt("game_rules"), message_type="system")
self.game_state = "setup_round"
if self.game_state == "setup_round":
self.setup_round()
self.game_state = "animal_description"
if self.game_state in ["animal_description", "chameleon_guess", "herd_vote"]:
self.run_round()
if self.game_state == "resolve_round":
self.resolve_round()
points = [player.points for player in self.players]
if max(points) >= self.winning_score:
self.game_state = "game_end"
self.winner_id = self.players[points.index(max(points))].player_id
winner = self.player_from_id(self.winner_id)
self.game_message(f"The game is over {winner.name} has won!")
self.end_game()
else:
self.game_state = "setup_round"
# Go back to start
self.game_message(f"No player has won yet, the game will end when a player reaches {self.winning_score} points.")
self.game_message(f"Starting a new round...")
random.shuffle(self.players)
self.run_game()
def run_round(self):
"""Starts the round."""
# Phase I: Collect Player Animal Descriptions
if self.game_state == "animal_description":
for current_player in self.players:
if current_player.player_id not in [animal_description['player_id'] for animal_description in
self.round_animal_descriptions]:
response = self.player_turn_animal_description(current_player)
if not response:
break
if len(self.round_animal_descriptions) == len(self.players):
self.game_state = "chameleon_guess"
# Phase II: Chameleon Guesses the Animal
if self.game_state == "chameleon_guess":
self.player_turn_chameleon_guess(self.chameleon)
# Phase III: The Herd Votes for who they think the Chameleon is
if self.game_state == "herd_vote":
if not self.awaiting_input:
self.verbose_message("The Herd is voting...")
for current_player in self.players:
if current_player.role == "herd" and current_player.player_id not in [vote['voter_id'] for vote in
self.herd_vote_tally]:
response = self.player_turn_herd_vote(current_player)
if not response:
break
if len(self.herd_vote_tally) == len(self.players) - 1:
self.game_state = "resolve_round"
def setup_round(self):
"""Sets up the round. This includes assigning roles and gathering player names."""
# Choose Animal
herd_animal = self.random_animal()
self.herd_animals.append(herd_animal)
self.debug_message(f"The secret animal is {herd_animal}.")
# Assign Roles
chameleon_index = random_index(len(self.players))
chameleon = self.players[chameleon_index]
self.chameleon_ids.append(chameleon.player_id)
self.game_message(fetch_prompt("assign_chameleon"), chameleon)
herd = []
for i, player in enumerate(self.players):
if i == chameleon_index:
player.assign_role("chameleon")
self.debug_message(f"{player.name} is the Chameleon!")
else:
player.assign_role("herd")
herd.append(player)
self.game_message(format_prompt("assign_herd", herd_animal=herd_animal), herd)
for i, player in enumerate(self.players):
if i == chameleon_index:
player.assign_role("chameleon")
self.debug_message(f"{player.name} is the Chameleon!")
else:
player.assign_role("herd")
# Empty Animal Descriptions
self.all_animal_descriptions.append([])
# Empty Tally for Votes
self.herd_vote_tallies.append([])
self.game_message(f"Each player will now take turns describing themselves:")
def player_turn_animal_description(self, player: Player):
"""Handles a player's turn to describe themselves."""
if not self.awaiting_input:
self.verbose_message(f"{player.name} is thinking...", recipient=player, exclude=True)
self.game_message(fetch_prompt("player_describe_animal"), player)
# Get Player Animal Description
response = player.interface.generate_formatted_response(AnimalDescriptionFormat)
if response:
self.round_animal_descriptions.append({"player_id": player.player_id, "description": response.description})
self.game_message(f"{player.name}: {response.description}", player, exclude=True)
self.awaiting_input = False
else:
self.awaiting_input = True
return response
def player_turn_chameleon_guess(self, chameleon: Player):
"""Handles the Chameleon's turn to guess the secret animal."""
if not self.awaiting_input:
self.game_message("All players have spoken. The Chameleon will now guess the secret animal...")
self.verbose_message("The Chameleon is guessing...", recipient=chameleon, exclude=True)
player_responses = self.format_animal_descriptions(exclude=self.chameleon)
self.game_message(format_prompt("chameleon_guess_animal", player_responses=player_responses),
self.chameleon)
response = chameleon.interface.generate_formatted_response(ChameleonGuessFormat)
if response:
self.chameleon_guesses.append(response.animal)
self.game_message(
"The Chameleon has guessed the animal. Now the Herd will vote on who they think the chameleon is.")
self.awaiting_input = False
self.game_state = "herd_vote"
else:
# Await input and do not proceed to the next phase
self.awaiting_input = True
def player_turn_herd_vote(self, player: Player):
"""Handles a player's turn to vote for the Chameleon."""
if not self.awaiting_input:
player_responses = self.format_animal_descriptions(exclude=player)
self.game_message(format_prompt("vote", player_responses=player_responses), player)
# Get Player Vote
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)
if response:
self.debug_message(f"{player.name} voted for {response.vote}", recipient=player, exclude=True)
voted_for_player = self.player_from_name(response.vote)
player_vote = {"voter_id": player.player_id, "voted_for_id": voted_for_player.player_id}
self.herd_vote_tally.append(player_vote)
self.awaiting_input = False
else:
self.awaiting_input = True
return response
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 = self.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.")
# Point Logic
# If the Chameleon guesses the correct animal = +1 Point to the Chameleon
if self.chameleon_guess.lower() == self.herd_animal.lower():
self.chameleon.points += 1
# If the Chameleon guesses the incorrect animal = +1 Point to each Herd player
else:
for player in self.players:
if player.role == "herd":
player.points += 1
# If a Herd player votes for the Chameleon = +1 Point to that player
for vote in self.herd_vote_tally:
if vote["voted_for_id"] == self.chameleon.player_id:
self.player_from_id(vote['voter_id']).points += 1
# If the Herd fails to accuse the Chameleon = +1 Point to the Chameleon
if not accused_player_id or accused_player_id != self.chameleon.player_id:
self.chameleon.points += 1
# Print Scores
player_points = "\n".join([f"{player.name}: {player.points}" for player in self.players])
self.game_message(f"Current Game Score:\n{player_points}")
def random_animal(self) -> str:
"""Returns a random animal from the list of available animals, and removes it from the list."""
animal = random.choice(self.available_animals)
self.available_animals.remove(animal)
return animal
@staticmethod
def count_chameleon_votes(player_votes: list[dict]) -> str | None:
"""Counts the votes for each player."""
votes = [vote['voted_for_id'] for vote in player_votes]
freq = Counter(votes)
most_voted_player, number_of_votes = freq.most_common()[0]
# If one player has more than 50% of the votes, the herd accuses them of being the chameleon
if number_of_votes / len(player_votes) >= 0.5:
return most_voted_player
else:
return None
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:
# Used to exclude the player who is currently responding, so they don't vote for themselves like a fool
if response["player_id"] != exclude.player_id:
player = self.player_from_id(response["player_id"])
formatted_responses += f" - {player.name}: {response['description']}\n"
return formatted_responses |