Eric Botti commited on
Commit
c6c2b98
·
1 Parent(s): 7877562

added points system

Browse files
Files changed (3) hide show
  1. src/game.py +77 -74
  2. src/game_utils.py +4 -2
  3. src/player.py +8 -4
src/game.py CHANGED
@@ -109,15 +109,20 @@ class Game:
109
  if recipient.controller_type == "human":
110
  print(message)
111
 
112
- @staticmethod
113
- async def instructional_message(message: str, player: Player, output_format: Type[BaseModel]):
114
  """Sends a message to a specific player and gets their response."""
115
  if player.controller_type == "human":
116
- print(message)
117
  response = await player.respond_to(message, output_format)
118
  return response
119
 
120
  # The following methods are used to broadcast messages to a human.
 
 
 
 
 
 
121
  def verbose_message(self, message: str):
122
  """Sends a message for the human player to read. No response is expected."""
123
  if self.verbose:
@@ -135,11 +140,16 @@ class Game:
135
 
136
  await self.run_round()
137
 
138
- round_log = {
 
139
  "game_id": self.game_id,
140
- # "round_id": round_number,
141
- "herd_animal": random_animal(),
 
142
  }
 
 
 
143
 
144
 
145
 
@@ -149,6 +159,7 @@ class Game:
149
  # Phase I: Choose Animal and Assign Roles
150
 
151
  herd_animal = random_animal()
 
152
 
153
  chameleon_index = random_index(len(self.players))
154
  chameleon = self.players[chameleon_index]
@@ -182,96 +193,88 @@ class Game:
182
 
183
  self.game_message(f"{current_player.name}: {response.description}", current_player, exclude=True)
184
 
185
- # Phase III: Chameleon Decides if they want to guess the animal (secretly)
186
 
187
- self.game_message("All players have spoken. Now the Chameleon will decide if they want to guess the animal or not.")
188
  if self.human_index != self.chameleon_index:
189
  self.verbose_message("The Chameleon is thinking...")
190
 
191
- prompt = format_prompt("chameleon_guess_decision", player_responses=self.format_responses(exclude=chameleon.name))
192
- response = await self.instructional_message(prompt, chameleon, ChameleonGuessDecisionModel)
193
 
194
- if response.decision.lower() == "guess":
195
- chameleon_will_guess = True
196
- else:
197
- chameleon_will_guess = False
198
 
199
- # Phase IV: Chameleon Guesses Animal or All Players Vote for Chameleon
200
 
201
- if chameleon_will_guess:
202
- # Chameleon Guesses Animal
203
- self.game_message(f"{chameleon.name} has revealed themselves to be the chameleon and is guessing the animal...", chameleon, exclude=True)
204
 
205
- prompt = fetch_prompt("chameleon_guess_animal")
206
 
207
- response = await self.instructional_message(prompt, chameleon, ChameleonGuessAnimalModel)
 
 
 
 
208
 
209
- self.game_message(f"The Chameleon guesses you are pretending to be a {response.animal}", chameleon, exclude=True)
210
 
211
- if response.animal.lower() == herd_animal.lower():
212
- self.game_message(f"The Chameleon has guessed the correct animal! The Chameleon wins!")
213
- winner = "chameleon"
214
- else:
215
- self.game_message(f"The Chameleon is incorrect, the true animal is a {herd_animal}. The Herd wins!")
216
- winner = "herd"
217
 
218
- else:
219
- # All Players Vote for Chameleon
220
- self.game_message("The Chameleon has decided not to guess the animal. Now all players will vote on who they think the chameleon is.")
221
 
222
- player_votes = []
223
- for player in self.players:
224
- if player.role == "herd":
225
- if player.is_ai():
226
- self.verbose_message(f"{player.name} is thinking...")
227
 
228
- prompt = format_prompt("vote", player_responses=self.format_responses(exclude=player.name))
229
 
230
- # Get Player Vote
231
- response = await self.instructional_message(prompt, player, VoteModel)
 
232
 
233
- # check if a valid player was voted for...
 
234
 
235
- # Add Vote to Player Votes
236
- player_votes.append(response.vote)
237
- if player.is_ai():
238
- self.debug_message(f"{player.name} for {response.vote}")
239
 
240
- self.game_message("All players have voted!")
241
- self.game_message(f"Votes: {player_votes}")
 
 
242
 
243
- # Count Votes
244
- accused_player = count_chameleon_votes(player_votes)
 
 
245
 
246
- if accused_player:
247
- self.game_message(f"The Herd has accused {accused_player} of being the Chameleon!")
248
- if accused_player == self.players[self.chameleon_index].name:
249
- self.game_message(f"{accused_player} is the Chameleon! The Herd wins!")
250
- winner = "herd"
251
- else:
252
- self.game_message(f"{accused_player} is not the Chameleon! The Chameleon wins!")
253
- self.game_message(f"The real Chameleon was {chameleon.name}.")
254
- winner = "chameleon"
255
- else:
256
- self.game_message("The Herd could not come to a consensus. The Chameleon wins!")
257
- winner = "chameleon"
 
258
 
 
 
 
259
 
260
- # Phase V: Assign Points
261
- # Chameleon Wins by Correct Guess - 3 Points
262
- # Herd Wins by Failed Chameleon Guess - 1 Point (each)
263
- # Herd Wins by Correctly Guessing Chameleon - 2 points (each)
264
 
265
- # Log Game Info
266
- game_log = {
267
- "game_id": self.game_id,
268
- "start_time": self.start_time,
269
  "herd_animal": herd_animal,
270
- "number_of_players": len(self.players),
271
- "human_player": self.players[self.human_index].id if self.human_index else "None",
272
- "chameleon": self.players[self.chameleon_index].id,
273
- "winner": winner
274
  }
275
- game_log_path = os.path.join(self.log_dir, self.game_log_file.format(game_id=self.game_id))
276
-
277
- log(game_log, game_log_path)
 
109
  if recipient.controller_type == "human":
110
  print(message)
111
 
112
+ async def instructional_message(self, message: str, player: Player, output_format: Type[BaseModel]):
 
113
  """Sends a message to a specific player and gets their response."""
114
  if player.controller_type == "human":
115
+ self.human_message(message)
116
  response = await player.respond_to(message, output_format)
117
  return response
118
 
119
  # The following methods are used to broadcast messages to a human.
120
+ # They are design so that they can be overridden by a subclass for a different player interface.
121
+ @staticmethod
122
+ def human_message(self, message: str):
123
+ """Sends a message for the human player to read. No response is expected."""
124
+ print(message)
125
+
126
  def verbose_message(self, message: str):
127
  """Sends a message for the human player to read. No response is expected."""
128
  if self.verbose:
 
140
 
141
  await self.run_round()
142
 
143
+ # Log Game Info
144
+ game_log = {
145
  "game_id": self.game_id,
146
+ "start_time": self.start_time,
147
+ "number_of_players": len(self.players),
148
+ "human_player": self.players[self.human_index].id if self.human_index else "None",
149
  }
150
+ game_log_path = os.path.join(self.log_dir, self.game_log_file.format(game_id=self.game_id))
151
+
152
+ log(game_log, game_log_path)
153
 
154
 
155
 
 
159
  # Phase I: Choose Animal and Assign Roles
160
 
161
  herd_animal = random_animal()
162
+ self.debug_message(f"The secret animal is {herd_animal}.")
163
 
164
  chameleon_index = random_index(len(self.players))
165
  chameleon = self.players[chameleon_index]
 
193
 
194
  self.game_message(f"{current_player.name}: {response.description}", current_player, exclude=True)
195
 
196
+ # Phase III: Chameleon Guesses the Animal
197
 
198
+ self.game_message("All players have spoken. The Chameleon will now guess the secret animal...")
199
  if self.human_index != self.chameleon_index:
200
  self.verbose_message("The Chameleon is thinking...")
201
 
202
+ prompt = fetch_prompt("chameleon_guess_animal")
 
203
 
204
+ response = await self.instructional_message(prompt, chameleon, ChameleonGuessAnimalModel)
 
 
 
205
 
206
+ chameleon_animal_guess = response.animal
207
 
208
+ # Phase IV: The Herd Votes for who they think the Chameleon is
209
+ self.game_message("The Chameleon has guessed the animal. Now the Herd will vote on who they think the chameleon is.")
 
210
 
211
+ self.game_message("The Chameleon has decided not to guess the animal. Now all players will vote on who they think the chameleon is.")
212
 
213
+ player_votes = []
214
+ for player in self.players:
215
+ if player.role == "herd":
216
+ if player.is_ai():
217
+ self.verbose_message(f"{player.name} is thinking...")
218
 
219
+ prompt = format_prompt("vote", player_responses=self.format_responses(exclude=player.name))
220
 
221
+ # Get Player Vote
222
+ response = await self.instructional_message(prompt, player, VoteModel)
 
 
 
 
223
 
224
+ # check if a valid player was voted for...
 
 
225
 
226
+ # Add Vote to Player Votes
227
+ player_votes.append({"voter": player, "vote": response.vote})
228
+ if player.is_ai():
229
+ self.debug_message(f"{player.name} voted for {response.vote}")
 
230
 
 
231
 
232
+ self.game_message("All players have voted!")
233
+ formatted_votes = '\n'.join([f'{vote["voter"].name}: {vote["vote"]}' for vote in player_votes])
234
+ self.game_message(f"Votes:\n{formatted_votes}")
235
 
236
+ # Count Votes
237
+ accused_player = count_chameleon_votes(player_votes)
238
 
239
+ # Phase V: Assign Points
 
 
 
240
 
241
+ self.game_message(f"The round is over. Caclulating results...")
242
+ self.game_message(
243
+ f"The Chameleon was {chameleon.name}, and they guessed the secret animal was {chameleon_animal_guess}.")
244
+ self.game_message(f"The secret animal was actually was {herd_animal}.")
245
 
246
+ if accused_player:
247
+ self.game_message(f"The Herd voted for {accused_player} as the Chameleon.")
248
+ else:
249
+ self.game_message(f"The Herd could not come to a consensus.")
250
 
251
+ # Point Logic
252
+ # If the Chameleon guesses the correct animal = +1 Point to the Chameleon
253
+ if chameleon_animal_guess.lower() == herd_animal.lower():
254
+ chameleon.points += 1
255
+ # If the Chameleon guesses the incorrect animal = +1 Point to each Herd player
256
+ else:
257
+ for player in self.players:
258
+ if player.role == "herd":
259
+ player.points += 1
260
+ # If a Herd player votes for the Chameleon = +1 Point to that player
261
+ for vote in player_votes:
262
+ if vote["vote"] == chameleon.name:
263
+ vote['voter'].points += 1
264
 
265
+ # If the Herd fails to accuse the Chameleon = +1 Point to the Chameleon
266
+ if not accused_player or accused_player != chameleon.name:
267
+ chameleon.points += 1
268
 
269
+ # Check for a Winner
270
+ player_points = "\n".join([f"{player.name}: {player.points}" for player in self.players])
 
 
271
 
272
+ self.game_message(f"Current Game Score: {player_points}")
273
+
274
+ # Log Round Info
275
+ round_log = {
276
  "herd_animal": herd_animal,
277
+ "chameleon_name": self.players[self.chameleon_index].name,
278
+ "chameleon_guess": chameleon_animal_guess,
279
+ "herd_votes": player_votes,
 
280
  }
 
 
 
src/game_utils.py CHANGED
@@ -35,9 +35,11 @@ def random_index(number_of_players : int) -> int:
35
  return random.randint(0, number_of_players - 1)
36
 
37
 
38
- def count_chameleon_votes(player_votes: list[str]) -> str | None:
39
  """Counts the votes for each player."""
40
- freq = Counter(player_votes)
 
 
41
  most_voted_player, number_of_votes = freq.most_common()[0]
42
 
43
  # If one player has more than 50% of the votes, the herd accuses them of being the chameleon
 
35
  return random.randint(0, number_of_players - 1)
36
 
37
 
38
+ def count_chameleon_votes(player_votes: list[dict]) -> str | None:
39
  """Counts the votes for each player."""
40
+ votes = [vote['vote'] for vote in player_votes]
41
+
42
+ freq = Counter(votes)
43
  most_voted_player, number_of_votes = freq.most_common()[0]
44
 
45
  # If one player has more than 50% of the votes, the herd accuses them of being the chameleon
src/player.py CHANGED
@@ -1,5 +1,6 @@
1
  import os
2
  from typing import Type, Literal
 
3
 
4
  from langchain_core.runnables import Runnable, RunnableParallel, RunnableLambda, chain
5
 
@@ -16,6 +17,7 @@ from controllers import controller_from_name
16
 
17
  Role = Literal["chameleon", "herd"]
18
 
 
19
 
20
  class Player:
21
 
@@ -87,13 +89,15 @@ class Player:
87
  retries = 0
88
  try:
89
  output = await self.format_output.ainvoke({"output_format": output_format})
90
- except Exception as e:
91
- if retries > max_retries:
 
 
92
  self.add_to_history(HumanMessage(content=f"Error formatting response: {e} \n\n Please try again."))
93
  output = await self.format_output.ainvoke({"output_format": output_format})
94
- retries += 1
95
  else:
96
- print(f"Max retries reached due to Error: {e}")
97
  raise e
98
  else:
99
  # Convert the human message to the pydantic object format
 
1
  import os
2
  from typing import Type, Literal
3
+ import logging
4
 
5
  from langchain_core.runnables import Runnable, RunnableParallel, RunnableLambda, chain
6
 
 
17
 
18
  Role = Literal["chameleon", "herd"]
19
 
20
+ logging.basicConfig(level=logging.WARNING)
21
 
22
  class Player:
23
 
 
89
  retries = 0
90
  try:
91
  output = await self.format_output.ainvoke({"output_format": output_format})
92
+ except OutputParserException as e:
93
+ if retries < max_retries:
94
+ retries += 1
95
+ logging.warning(f"Player {self.id} failed to format response: {output} due to an exception: {e} \n\n Retrying {retries}/{max_retries}")
96
  self.add_to_history(HumanMessage(content=f"Error formatting response: {e} \n\n Please try again."))
97
  output = await self.format_output.ainvoke({"output_format": output_format})
98
+
99
  else:
100
+ logging.error(f"Max retries reached due to Error: {e}")
101
  raise e
102
  else:
103
  # Convert the human message to the pydantic object format