Eric Botti commited on
Commit
c6447fa
·
1 Parent(s): 7275c7f

streamlit game updates

Browse files
Files changed (4) hide show
  1. src/agent_interfaces.py +18 -10
  2. src/app.py +31 -54
  3. src/game.py +3 -0
  4. src/game_chameleon.py +40 -18
src/agent_interfaces.py CHANGED
@@ -63,11 +63,15 @@ class BaseAgentInterface:
63
 
64
  # Generate response methods - These do not take a message as input and only use the current message history
65
 
66
- def generate_response(self) -> Message:
67
  """Generates a response based on the current messages in the history."""
68
- response = Message(type="agent", content=self._generate())
69
- self.add_message(response)
70
- return response
 
 
 
 
71
 
72
  def generate_formatted_response(
73
  self,
@@ -158,14 +162,18 @@ class HumanAgentInterface(BaseAgentInterface):
158
  output_format: Type[OutputFormatModel],
159
  additional_fields: dict = None,
160
  max_retries: int = 3
161
- ) -> OutputFormatModel:
162
  """For Human agents, we can trust them enough to format their own responses... for now"""
163
  response = self.generate_response()
164
- # only works because current outputs have only 1 field...
165
- fields = {output_format.model_fields.copy().popitem()[0]: response.content}
166
- if additional_fields:
167
- fields.update(additional_fields)
168
- output = output_format.model_validate(fields)
 
 
 
 
169
 
170
  return output
171
 
 
63
 
64
  # Generate response methods - These do not take a message as input and only use the current message history
65
 
66
+ def generate_response(self) -> Message | None:
67
  """Generates a response based on the current messages in the history."""
68
+ content = self._generate()
69
+ if content:
70
+ response = Message(type="agent", content=content)
71
+ self.add_message(response)
72
+ return response
73
+ else:
74
+ return None
75
 
76
  def generate_formatted_response(
77
  self,
 
162
  output_format: Type[OutputFormatModel],
163
  additional_fields: dict = None,
164
  max_retries: int = 3
165
+ ) -> OutputFormatModel | None:
166
  """For Human agents, we can trust them enough to format their own responses... for now"""
167
  response = self.generate_response()
168
+
169
+ if response:
170
+ # only works because current outputs have only 1 field...
171
+ fields = {output_format.model_fields.copy().popitem()[0]: response.content}
172
+ if additional_fields:
173
+ fields.update(additional_fields)
174
+ output = output_format.model_validate(fields)
175
+ else:
176
+ output = None
177
 
178
  return output
179
 
src/app.py CHANGED
@@ -22,8 +22,7 @@ def display_message(message: Message):
22
 
23
  if "messages" not in session_state:
24
  session_state.messages = []
25
- session_state.awaiting_human_input = False
26
- session_state.game_state = "game_start"
27
 
28
 
29
  class StreamlitInterface(HumanAgentInterface):
@@ -33,7 +32,9 @@ class StreamlitInterface(HumanAgentInterface):
33
  display_message(message)
34
 
35
  def _generate(self) -> str:
36
- return session_state.user_input
 
 
37
 
38
 
39
  class StreamlitChameleonGame(ChameleonGame):
@@ -42,76 +43,50 @@ class StreamlitChameleonGame(ChameleonGame):
42
  def run_game(self):
43
  """Starts the game."""
44
 
45
- if session_state.game_state == "game_start":
46
  self.game_message(fetch_prompt("game_rules"))
47
- session_state.game_state = "setup_round"
48
- if session_state.game_state == "setup_round":
49
  self.setup_round()
50
- session_state.game_state = "animal_description"
51
- if session_state.game_state in ["animal_description", "chameleon_guess", "herd_vote"]:
52
  self.run_round()
53
- if session_state.game_state == "resolve_round":
54
  self.resolve_round()
55
- session_state.game_state = "setup_round"
56
 
57
  def run_round(self):
58
  """Starts the round."""
59
 
60
  # Phase I: Collect Player Animal Descriptions
61
- if session_state.game_state == "animal_description":
62
  for current_player in self.players:
63
  if current_player.id not in [animal_description['player_id'] for animal_description in self.round_animal_descriptions]:
64
- if current_player.interface.is_human:
65
- if not session_state.awaiting_human_input:
66
- self.game_message(fetch_prompt("player_describe_animal"), current_player)
67
- session_state.awaiting_human_input = True
68
- break
69
- else:
70
- self.player_turn_animal_description(current_player)
71
- session_state.awaiting_human_input = False
72
- else:
73
- self.game_message(fetch_prompt("player_describe_animal"), current_player)
74
- self.player_turn_animal_description(current_player)
75
  if len(self.round_animal_descriptions) == len(self.players):
76
- session_state.game_state = "chameleon_guess"
77
- session_state.awaiting_human_input = False
78
 
79
  # Phase II: Chameleon Guesses the Animal
80
- if session_state.game_state == "chameleon_guess":
81
- self.game_message("All players have spoken. The Chameleon will now guess the secret animal...")
82
- player_responses = self.format_animal_descriptions(exclude=self.chameleon)
83
- self.game_message(format_prompt("chameleon_guess_animal", player_responses=player_responses), self.chameleon)
84
- if self.human_player().role == "chameleon":
85
- if not session_state.awaiting_human_input:
86
- session_state.awaiting_human_input = True
87
- else:
88
- self.player_turn_chameleon_guess(self.chameleon)
89
- session_state.awaiting_human_input = False
90
- else:
91
- self.player_turn_chameleon_guess(self.chameleon)
92
- session_state.awaiting_human_input = False
93
-
94
- session_state.game_state = "herd_vote"
95
 
96
  # Phase III: The Herd Votes for who they think the Chameleon is
97
- if session_state.game_state == "herd_vote":
98
  for current_player in self.players:
99
  if current_player.role == "herd" and current_player.id not in [vote['voter_id'] for vote in self.herd_vote_tally]:
100
- player_responses = self.format_animal_descriptions(exclude=current_player)
101
- if current_player.interface.is_human:
102
- if not session_state.awaiting_human_input:
103
- self.game_message(format_prompt("vote", player_responses=player_responses), current_player)
104
- session_state.awaiting_human_input = True
105
- break
106
- else:
107
- self.player_turn_herd_vote(current_player)
108
- session_state.awaiting_human_input = False
109
- else:
110
- self.game_message(format_prompt("vote", player_responses=player_responses), current_player)
111
- self.player_turn_herd_vote(current_player)
112
 
113
  if len(self.herd_vote_tally) == len(self.players) - 1:
114
- session_state.game_state = "resolve_round"
115
 
116
 
117
  # Streamlit App
@@ -142,7 +117,9 @@ with center:
142
  if user_input:
143
  if "game" not in st.session_state:
144
  st.session_state.game = StreamlitChameleonGame.from_human_name(user_input, StreamlitInterface)
145
- session_state.user_input = user_input
 
 
146
  st.session_state.game.run_game()
147
 
148
  st.markdown("#")
 
22
 
23
  if "messages" not in session_state:
24
  session_state.messages = []
25
+ session_state.user_input = None
 
26
 
27
 
28
  class StreamlitInterface(HumanAgentInterface):
 
32
  display_message(message)
33
 
34
  def _generate(self) -> str:
35
+ response = session_state.user_input
36
+ session_state.user_input = None
37
+ return response
38
 
39
 
40
  class StreamlitChameleonGame(ChameleonGame):
 
43
  def run_game(self):
44
  """Starts the game."""
45
 
46
+ if self.game_state == "game_start":
47
  self.game_message(fetch_prompt("game_rules"))
48
+ self.game_state = "setup_round"
49
+ if self.game_state == "setup_round":
50
  self.setup_round()
51
+ self.game_state = "animal_description"
52
+ if self.game_state in ["animal_description", "chameleon_guess", "herd_vote"]:
53
  self.run_round()
54
+ if self.game_state == "resolve_round":
55
  self.resolve_round()
56
+ self.game_state = "setup_round"
57
 
58
  def run_round(self):
59
  """Starts the round."""
60
 
61
  # Phase I: Collect Player Animal Descriptions
62
+ if self.game_state == "animal_description":
63
  for current_player in self.players:
64
  if current_player.id not in [animal_description['player_id'] for animal_description in self.round_animal_descriptions]:
65
+
66
+ response = self.player_turn_animal_description(current_player)
67
+
68
+ if not response:
69
+ break
70
+
 
 
 
 
 
71
  if len(self.round_animal_descriptions) == len(self.players):
72
+ self.game_state = "chameleon_guess"
 
73
 
74
  # Phase II: Chameleon Guesses the Animal
75
+ if self.game_state == "chameleon_guess":
76
+ self.player_turn_chameleon_guess(self.chameleon)
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
  # Phase III: The Herd Votes for who they think the Chameleon is
79
+ if self.game_state == "herd_vote":
80
  for current_player in self.players:
81
  if current_player.role == "herd" and current_player.id not in [vote['voter_id'] for vote in self.herd_vote_tally]:
82
+
83
+ response = self.player_turn_herd_vote(current_player)
84
+
85
+ if not response:
86
+ break
 
 
 
 
 
 
 
87
 
88
  if len(self.herd_vote_tally) == len(self.players) - 1:
89
+ self.game_state = "resolve_round"
90
 
91
 
92
  # Streamlit App
 
117
  if user_input:
118
  if "game" not in st.session_state:
119
  st.session_state.game = StreamlitChameleonGame.from_human_name(user_input, StreamlitInterface)
120
+ else:
121
+ session_state.user_input = user_input
122
+
123
  st.session_state.game.run_game()
124
 
125
  st.markdown("#")
src/game.py CHANGED
@@ -28,6 +28,9 @@ class Game:
28
 
29
  self.winner_id: str | None = None
30
  """The id of the player who has won the game."""
 
 
 
31
 
32
  def player_from_id(self, player_id: str) -> Player:
33
  """Returns a player from their ID."""
 
28
 
29
  self.winner_id: str | None = None
30
  """The id of the player who has won the game."""
31
+ self.game_state: str = "game_start"
32
+ """Keeps track of the current state of the game."""
33
+ self.awaiting_input: bool = False
34
 
35
  def player_from_id(self, player_id: str) -> Player:
36
  """Returns a player from their ID."""
src/game_chameleon.py CHANGED
@@ -152,45 +152,67 @@ class ChameleonGame(Game):
152
 
153
  def player_turn_animal_description(self, player: Player):
154
  """Handles a player's turn to describe themselves."""
155
- if player.interface.is_ai:
156
- self.verbose_message(f"{player.name} is thinking...")
157
-
158
- prompt = fetch_prompt("player_describe_animal")
159
 
160
  # Get Player Animal Description
161
  response = player.interface.generate_formatted_response(AnimalDescriptionFormat)
162
 
163
- self.round_animal_descriptions.append({"player_id": player.id, "description": response.description})
 
 
 
 
 
164
 
165
- self.game_message(f"{player.name}: {response.description}", player, exclude=True)
166
 
167
  def player_turn_chameleon_guess(self, chameleon: Player):
168
  """Handles the Chameleon's turn to guess the secret animal."""
169
-
170
- if chameleon.interface.is_ai or self.observer:
171
- self.verbose_message("The Chameleon is thinking...")
 
 
 
172
 
173
  response = chameleon.interface.generate_formatted_response(ChameleonGuessFormat)
174
 
175
- self.game_message("The Chameleon has guessed the animal. Now the Herd will vote on who they think the chameleon is.")
176
-
177
- self.chameleon_guesses.append(response.animal)
 
 
 
 
 
 
178
 
179
  def player_turn_herd_vote(self, player: Player):
180
  """Handles a player's turn to vote for the Chameleon."""
181
- if player.interface.is_ai:
182
- self.verbose_message(f"{player.name} is thinking...")
 
 
183
 
184
  # Get Player Vote
185
  additional_fields = {"player_names": [p.name for p in self.players if p != player]}
186
  response = player.interface.generate_formatted_response(HerdVoteFormat, additional_fields=additional_fields)
187
 
188
- self.debug_message(f"{player.name} voted for {response.vote}", recipient=player, exclude=True)
 
 
 
189
 
190
- voted_for_player = self.player_from_name(response.vote)
 
 
 
 
 
191
 
192
- # Add Vote to Player Votes
193
- self.herd_vote_tally.append({"voter_id": player.id, "voted_for_id": voted_for_player.id})
194
 
195
  def resolve_round(self):
196
  """Resolves the round, assigns points, and prints the results."""
 
152
 
153
  def player_turn_animal_description(self, player: Player):
154
  """Handles a player's turn to describe themselves."""
155
+ if not self.awaiting_input:
156
+ self.verbose_message(f"{player.name} is thinking...", recipient=player, exclude=True)
157
+ self.game_message(fetch_prompt("player_describe_animal"), player)
 
158
 
159
  # Get Player Animal Description
160
  response = player.interface.generate_formatted_response(AnimalDescriptionFormat)
161
 
162
+ if response:
163
+ self.round_animal_descriptions.append({"player_id": player.id, "description": response.description})
164
+ self.game_message(f"{player.name}: {response.description}", player, exclude=True)
165
+ self.awaiting_input = False
166
+ else:
167
+ self.awaiting_input = True
168
 
169
+ return response
170
 
171
  def player_turn_chameleon_guess(self, chameleon: Player):
172
  """Handles the Chameleon's turn to guess the secret animal."""
173
+ if not self.awaiting_input:
174
+ self.game_message("All players have spoken. The Chameleon will now guess the secret animal...")
175
+ self.verbose_message("The Chameleon is thinking...", recipient=chameleon, exclude=True)
176
+ player_responses = self.format_animal_descriptions(exclude=self.chameleon)
177
+ self.game_message(format_prompt("chameleon_guess_animal", player_responses=player_responses),
178
+ self.chameleon)
179
 
180
  response = chameleon.interface.generate_formatted_response(ChameleonGuessFormat)
181
 
182
+ if response:
183
+ self.chameleon_guesses.append(response.animal)
184
+ self.game_message(
185
+ "The Chameleon has guessed the animal. Now the Herd will vote on who they think the chameleon is.")
186
+ self.awaiting_input = False
187
+ self.game_state = "herd_vote"
188
+ else:
189
+ # Await input and do not proceed to the next phase
190
+ self.awaiting_input = True
191
 
192
  def player_turn_herd_vote(self, player: Player):
193
  """Handles a player's turn to vote for the Chameleon."""
194
+ if not self.awaiting_input:
195
+ self.verbose_message(f"{player.name} is thinking...", recipient=player, exclude=True)
196
+ player_responses = self.format_animal_descriptions(exclude=player)
197
+ self.game_message(format_prompt("vote", player_responses=player_responses), player)
198
 
199
  # Get Player Vote
200
  additional_fields = {"player_names": [p.name for p in self.players if p != player]}
201
  response = player.interface.generate_formatted_response(HerdVoteFormat, additional_fields=additional_fields)
202
 
203
+ if response:
204
+ self.debug_message(f"{player.name} voted for {response.vote}", recipient=player, exclude=True)
205
+
206
+ voted_for_player = self.player_from_name(response.vote)
207
 
208
+ player_vote = {"voter_id": player.id, "voted_for_id": voted_for_player.id}
209
+
210
+ self.herd_vote_tally.append(player_vote)
211
+ self.awaiting_input = False
212
+ else:
213
+ self.awaiting_input = True
214
 
215
+ return response
 
216
 
217
  def resolve_round(self):
218
  """Resolves the round, assigns points, and prints the results."""