Eric Botti commited on
Commit
c2392fe
·
1 Parent(s): 1d85643

redid messaging system

Browse files
Files changed (5) hide show
  1. src/controllers.py +4 -4
  2. src/game.py +90 -17
  3. src/main.py +6 -4
  4. src/player.py +9 -2
  5. src/prompts.py +7 -7
src/controllers.py CHANGED
@@ -6,11 +6,11 @@ from langchain_core.messages import AIMessage
6
 
7
 
8
  def player_input(prompt):
9
- print(prompt)
10
  # even though they are human, we still need to return an AIMessage, since the HumanMessages are from the GameMaster
11
  response = AIMessage(content=input())
12
  return response
13
 
 
14
 
15
  def controller_from_name(name: str):
16
  if name == "tgi":
@@ -19,9 +19,9 @@ def controller_from_name(name: str):
19
  api_key=os.environ['HF_API_TOKEN']
20
  )
21
  elif name == "openai":
22
- return ChatOpenAI(model="gpt-3.5-turbo")
23
- # elif name == "ollama":
24
- # return ollama_controller
25
  elif name == "human":
26
  return RunnableLambda(player_input)
27
  else:
 
6
 
7
 
8
  def player_input(prompt):
 
9
  # even though they are human, we still need to return an AIMessage, since the HumanMessages are from the GameMaster
10
  response = AIMessage(content=input())
11
  return response
12
 
13
+ MAX_TOKENS = 20
14
 
15
  def controller_from_name(name: str):
16
  if name == "tgi":
 
19
  api_key=os.environ['HF_API_TOKEN']
20
  )
21
  elif name == "openai":
22
+ return ChatOpenAI(model="gpt-3.5-turbo", max_tokens=MAX_TOKENS)
23
+ elif name == "ollama":
24
+ return ChatOpenAI(model="mistral", openai_api_key="ollama", openai_api_base="http://localhost:11434/v1", max_tokens=MAX_TOKENS)
25
  elif name == "human":
26
  return RunnableLambda(player_input)
27
  else:
src/game.py CHANGED
@@ -1,6 +1,8 @@
1
  import os
2
  from datetime import datetime
3
 
 
 
4
  from game_utils import *
5
  from models import *
6
  from player import Player
@@ -9,6 +11,7 @@ from prompts import fetch_prompt, format_prompt
9
  # Default Values
10
  NUMBER_OF_PLAYERS = 5
11
 
 
12
 
13
  class Game:
14
  log_dir = os.path.join(os.pardir, "experiments")
@@ -17,26 +20,32 @@ class Game:
17
 
18
  def __init__(
19
  self,
 
20
  human_name: str = None,
21
- number_of_players: int = NUMBER_OF_PLAYERS
22
  ):
23
 
 
 
 
24
  # Game ID
25
  self.game_id = game_id()
26
  self.start_time = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
27
  self.log_dir = os.path.join(self.log_dir, f"{self.start_time}-{self.game_id}")
28
  os.makedirs(self.log_dir, exist_ok=True)
29
 
 
 
 
30
  # Gather Player Names
31
  if human_name:
32
  ai_names = random_names(number_of_players - 1, human_name)
33
  self.human_index = random_index(number_of_players)
 
34
  else:
35
  ai_names = random_names(number_of_players)
36
  self.human_index = None
37
-
38
- # Choose Chameleon
39
- self.chameleon_index = random_index(number_of_players)
40
 
41
  # Add Players
42
  self.players = []
@@ -78,13 +87,46 @@ class Game:
78
 
79
  return formatted_responses
80
 
81
- def get_player_names(self) -> list[str]:
82
- """Returns the names of the players."""
83
- return [player.name for player in self.players]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
  async def start(self):
86
  """Starts the game."""
87
- # print("Welcome to Chameleon! This is a social deduction game powered by LLMs.")
 
88
 
89
  self.player_responses = []
90
  herd_animal = random_animal()
@@ -93,21 +135,33 @@ class Game:
93
 
94
  while not winner:
95
  # Phase I: Collect Player Animal Descriptions
96
- for player in self.players:
97
- if player.role == "chameleon":
 
 
 
 
 
98
  prompt = format_prompt("chameleon_animal", player_responses=self.format_responses())
99
  else:
100
  prompt = format_prompt("herd_animal", animal=herd_animal, player_responses=self.format_responses())
101
 
102
  # Get Player Animal Description
103
- response = await player.respond_to(prompt, AnimalDescriptionModel)
 
 
 
 
104
 
105
- self.player_responses.append({"sender": player.name, "response": response.description})
106
 
107
  # Phase II: Chameleon Decides if they want to guess the animal (secretly)
108
- prompt = format_prompt("chameleon_guess_decision", player_responses=self.format_responses())
 
 
109
 
110
- response = await self.players[self.chameleon_index].respond_to(prompt, ChameleonGuessDecisionModel)
 
 
111
 
112
  if response.decision.lower() == "guess":
113
  chameleon_will_guess = True
@@ -117,20 +171,33 @@ class Game:
117
  # Phase III: Chameleon Guesses Animal or All Players Vote for Chameleon
118
  if chameleon_will_guess:
119
  # Chameleon Guesses Animal
 
 
120
  prompt = fetch_prompt("chameleon_guess_animal")
121
 
122
  response = await self.players[self.chameleon_index].respond_to(prompt, ChameleonGuessAnimalModel)
123
 
 
 
124
  if response.animal.lower() == herd_animal.lower():
 
125
  winner = "chameleon"
126
  else:
 
127
  winner = "herd"
128
 
 
129
  else:
130
  # All Players Vote for Chameleon
 
 
131
  player_votes = []
132
  for player in self.players:
133
- prompt = format_prompt("vote", player_responses=self.format_responses())
 
 
 
 
134
 
135
  # Get Player Vote
136
  response = await player.respond_to(prompt, VoteModel)
@@ -140,18 +207,24 @@ class Game:
140
  # Add Vote to Player Votes
141
  player_votes.append(response.vote)
142
 
143
- print(player_votes)
 
144
 
145
  # Count Votes
146
  accused_player = count_chameleon_votes(player_votes)
147
 
148
  if accused_player:
 
149
  if accused_player == self.players[self.chameleon_index].name:
 
150
  winner = "herd"
151
  else:
 
 
152
  winner = "chameleon"
 
 
153
 
154
- print(f"Game Over! The {winner} wins!")
155
 
156
  # Assign Points
157
  # Chameleon Wins - 3 Points
 
1
  import os
2
  from datetime import datetime
3
 
4
+ from colorama import Fore, Style
5
+
6
  from game_utils import *
7
  from models import *
8
  from player import Player
 
11
  # Default Values
12
  NUMBER_OF_PLAYERS = 5
13
 
14
+ game_type = "streamlit"
15
 
16
  class Game:
17
  log_dir = os.path.join(os.pardir, "experiments")
 
20
 
21
  def __init__(
22
  self,
23
+ number_of_players: int = NUMBER_OF_PLAYERS,
24
  human_name: str = None,
25
+ verbose: bool = False # If there is a human player game will always be verbose
26
  ):
27
 
28
+ # This function is used to broadcast messages to the human player.
29
+ # They are purely informative and do not affect the game.
30
+
31
  # Game ID
32
  self.game_id = game_id()
33
  self.start_time = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
34
  self.log_dir = os.path.join(self.log_dir, f"{self.start_time}-{self.game_id}")
35
  os.makedirs(self.log_dir, exist_ok=True)
36
 
37
+ # Choose Chameleon
38
+ self.chameleon_index = random_index(number_of_players)
39
+
40
  # Gather Player Names
41
  if human_name:
42
  ai_names = random_names(number_of_players - 1, human_name)
43
  self.human_index = random_index(number_of_players)
44
+ self.verbose = True
45
  else:
46
  ai_names = random_names(number_of_players)
47
  self.human_index = None
48
+ self.verbose = verbose
 
 
49
 
50
  # Add Players
51
  self.players = []
 
87
 
88
  return formatted_responses
89
 
90
+
91
+ def game_message(self, message: str, excluded_player: Player = None):
92
+ """Sends a message to all players. No response is expected."""
93
+ for player in self.players:
94
+ if player != excluded_player:
95
+ player.prompt_queue.append(message)
96
+ if player.controller_type == "human":
97
+ print(message)
98
+
99
+ @staticmethod
100
+ async def instructional_message(message: str, player: Player, output_format: Type[BaseModel]):
101
+ """Sends a message to a specific player and gets their response."""
102
+
103
+ response = await player.respond_to(message, output_format)
104
+ return response
105
+
106
+ # The following methods are used to broadcast messages to a human.
107
+ def verbose_message(self, message: str):
108
+ """Sends a message for the human player to read. No response is expected."""
109
+ if self.verbose:
110
+ print(Fore.GREEN + message + Style.RESET_ALL)
111
+
112
+ def debug_message(self, message: str):
113
+ """Sends a message for a human observer. These messages contain secret information about the players such as their role."""
114
+ if self.debug:
115
+ print(Fore.YELLOW + message + Style.RESET_ALL)
116
+
117
+ # def game_setup(self):
118
+ # """Sets up the game. This includes assigning roles and gathering player names."""
119
+ # self.verbose_message("Setting up the game...")
120
+ #
121
+ # for i, player in enumerate(self.players):
122
+ # if player.controller_type != "human":
123
+ # self.verbose_message(f"Player {i + 1}: {player.name} - {player.role}")
124
+
125
 
126
  async def start(self):
127
  """Starts the game."""
128
+ self.verbose_message(("Welcome to Chameleon! This is a social deduction game powered by LLMs."))
129
+ self.game_message(fetch_prompt("game_rules"))
130
 
131
  self.player_responses = []
132
  herd_animal = random_animal()
 
135
 
136
  while not winner:
137
  # Phase I: Collect Player Animal Descriptions
138
+ self.game_message(f"Each player will now take turns describing themselves.")
139
+ for current_player in self.players:
140
+ if current_player.controller_type != "human":
141
+ self.verbose_message(f"It's {current_player.name}'s turn to describe the animal.")
142
+ self.verbose_message(f"{current_player.name} is thinking...")
143
+
144
+ if current_player.role == "chameleon":
145
  prompt = format_prompt("chameleon_animal", player_responses=self.format_responses())
146
  else:
147
  prompt = format_prompt("herd_animal", animal=herd_animal, player_responses=self.format_responses())
148
 
149
  # Get Player Animal Description
150
+ response = await self.instructional_message(prompt, current_player, AnimalDescriptionModel)
151
+
152
+ self.player_responses.append({"sender": current_player.name, "response": response.description})
153
+
154
+ self.game_message(f"{current_player.name}: {response.description}", current_player)
155
 
 
156
 
157
  # Phase II: Chameleon Decides if they want to guess the animal (secretly)
158
+ self.game_message("All players have spoken. Now the chameleon will decide if they want to guess the animal or not.")
159
+ if self.human_index != self.chameleon_index:
160
+ self.verbose_message("The chameleon is thinking...")
161
 
162
+ chameleon = self.players[self.chameleon_index]
163
+ prompt = format_prompt("chameleon_guess_decision", player_responses=self.format_responses(exclude=chameleon.name))
164
+ response = await chameleon.respond_to(prompt, ChameleonGuessDecisionModel)
165
 
166
  if response.decision.lower() == "guess":
167
  chameleon_will_guess = True
 
171
  # Phase III: Chameleon Guesses Animal or All Players Vote for Chameleon
172
  if chameleon_will_guess:
173
  # Chameleon Guesses Animal
174
+ self.game_message(f"{chameleon.name} has revealed themselves to be the chameleon and is guessing the animal...", chameleon)
175
+
176
  prompt = fetch_prompt("chameleon_guess_animal")
177
 
178
  response = await self.players[self.chameleon_index].respond_to(prompt, ChameleonGuessAnimalModel)
179
 
180
+ self.game_message(f"The chameleon guesses: {response.animal}")
181
+
182
  if response.animal.lower() == herd_animal.lower():
183
+ self.game_message(f"The Chameleon has guessed the correct animal! The Chameleon wins!")
184
  winner = "chameleon"
185
  else:
186
+ self.game_message(f"The Chameleon is incorrect, the true animal is a {herd_animal}. The Herd wins!")
187
  winner = "herd"
188
 
189
+
190
  else:
191
  # All Players Vote for Chameleon
192
+ self.game_message("The chameleon has decided not to guess the animal. Now all players will vote on who they think the chameleon is.")
193
+
194
  player_votes = []
195
  for player in self.players:
196
+ if player.controller_type != "human":
197
+ self.verbose_message(f"It's {player.name}'s turn to vote.")
198
+ self.verbose_message(f"{player.name} is thinking...")
199
+
200
+ prompt = format_prompt("vote", player_responses=self.format_responses(exclude=player.name))
201
 
202
  # Get Player Vote
203
  response = await player.respond_to(prompt, VoteModel)
 
207
  # Add Vote to Player Votes
208
  player_votes.append(response.vote)
209
 
210
+ self.game_message("All players have voted!")
211
+ self.game_message(f"Votes: {player_votes}")
212
 
213
  # Count Votes
214
  accused_player = count_chameleon_votes(player_votes)
215
 
216
  if accused_player:
217
+ self.game_message(f"The Herd has accused {accused_player} of being the Chameleon!")
218
  if accused_player == self.players[self.chameleon_index].name:
219
+ self.game_message(f"{accused_player} is the Chameleon! The Herd wins!")
220
  winner = "herd"
221
  else:
222
+ self.game_message(f"{accused_player} is not the Chameleon! The Chameleon wins!")
223
+ self.game_message(f"The real Chameleon was {chameleon.name}.")
224
  winner = "chameleon"
225
+ else:
226
+ self.game_message("The Herd could not come to a consensus. We will play another round!")
227
 
 
228
 
229
  # Assign Points
230
  # Chameleon Wins - 3 Points
src/main.py CHANGED
@@ -1,14 +1,16 @@
1
  from game import Game
 
2
  import asyncio
3
  from player import Player
4
 
5
  def main():
6
- print("Please Enter your name:")
7
  name = input()
8
 
9
- game = Game(human_name=name)
10
-
11
- # game = Game()
 
12
 
13
  asyncio.run(game.start())
14
 
 
1
  from game import Game
2
+ from player import Player
3
  import asyncio
4
  from player import Player
5
 
6
  def main():
7
+ print("Please Enter your name, or leave blank to run an AI only game")
8
  name = input()
9
 
10
+ if name:
11
+ game = Game(human_name=name)
12
+ else:
13
+ game = Game()
14
 
15
  asyncio.run(game.start())
16
 
src/player.py CHANGED
@@ -12,17 +12,18 @@ from pydantic import BaseModel
12
  from game_utils import log
13
  from controllers import controller_from_name
14
 
 
15
  class Player:
16
  def __init__(
17
  self,
18
  name: str,
19
  controller: str,
20
  role: str,
21
- id: str = None,
22
  log_filepath: str = None
23
  ):
24
  self.name = name
25
- self.id = id
26
 
27
  if controller == "human":
28
  self.controller_type = "human"
@@ -34,6 +35,8 @@ class Player:
34
  self.role = role
35
  self.messages = []
36
 
 
 
37
  self.log_filepath = log_filepath
38
 
39
  if log_filepath:
@@ -54,6 +57,10 @@ class Player:
54
 
55
  async def respond_to(self, prompt: str, output_format: Type[BaseModel], max_retries=3):
56
  """Makes the player respond to a prompt. Returns the response in the specified format."""
 
 
 
 
57
  message = HumanMessage(content=prompt)
58
  output = await self.generate.ainvoke(message)
59
  if self.controller_type == "ai":
 
12
  from game_utils import log
13
  from controllers import controller_from_name
14
 
15
+
16
  class Player:
17
  def __init__(
18
  self,
19
  name: str,
20
  controller: str,
21
  role: str,
22
+ player_id: str = None,
23
  log_filepath: str = None
24
  ):
25
  self.name = name
26
+ self.id = player_id
27
 
28
  if controller == "human":
29
  self.controller_type = "human"
 
35
  self.role = role
36
  self.messages = []
37
 
38
+ self.prompt_queue = []
39
+
40
  self.log_filepath = log_filepath
41
 
42
  if log_filepath:
 
57
 
58
  async def respond_to(self, prompt: str, output_format: Type[BaseModel], max_retries=3):
59
  """Makes the player respond to a prompt. Returns the response in the specified format."""
60
+ if self.prompt_queue:
61
+ # If there are prompts in the queue, add them to the current prompt
62
+ prompt = "\n".join(self.prompt_queue + [prompt])
63
+
64
  message = HumanMessage(content=prompt)
65
  output = await self.generate.ainvoke(message)
66
  if self.controller_type == "ai":
src/prompts.py CHANGED
@@ -64,9 +64,10 @@ class Task:
64
  _game_rules = '''\
65
  GAME RULES: You are playing a social deduction game where every player pretends the be the same animal.
66
  During the round players go around the room and make an "I"-statement as if they were the animal.
67
- All players know what animal they are pretending to be, except one who is known as the Chameleon. The Chameleon and must blend in by providing details about the animal using context from other players.
68
- The other players must be careful not to give away too much information with their responses so that Chameleon cannot guess the animal. After all players have spoken, they vote on who they think the Chameleon is.
69
-
 
70
  '''
71
 
72
  _herd_animal = """\
@@ -83,8 +84,6 @@ You don't know what animal the other players are, your goal is to deduce it usin
83
  Starting with "I" describe yourself in 10 words or less as if you are the same animal as the other players.
84
  If no one else has said anything try to say something generic that could be true of any animals.
85
  If the other players realize you are the Chameleon you will LOSE.
86
- Previously Mentioned Descriptions:
87
- {player_responses}
88
  Your Response: """
89
 
90
  _all_responses = """\
@@ -109,8 +108,9 @@ Now it is time to vote. Choose from the players above who you think the Chameleo
109
  """
110
 
111
  prompts = {
112
- "herd_animal": _game_rules + _herd_animal,
113
- "chameleon_animal": _game_rules + _chameleon_animal,
 
114
  "chameleon_guess_decision": _all_responses + _chameleon_guess_decision,
115
  "chameleon_guess_animal": _chameleon_guess_animal,
116
  "vote": _all_responses + _vote_prompt
 
64
  _game_rules = '''\
65
  GAME RULES: You are playing a social deduction game where every player pretends the be the same animal.
66
  During the round players go around the room and make an "I"-statement as if they were the animal.
67
+ All players know what animal they are pretending to be, except one who is known as the Chameleon.
68
+ The Chameleon and must blend in by providing details about the animal using context from other players.
69
+ The other players must be careful not to give away too much information with their responses so that Chameleon cannot guess the animal.
70
+ After all players have spoken, they vote on who they think the Chameleon is. \
71
  '''
72
 
73
  _herd_animal = """\
 
84
  Starting with "I" describe yourself in 10 words or less as if you are the same animal as the other players.
85
  If no one else has said anything try to say something generic that could be true of any animals.
86
  If the other players realize you are the Chameleon you will LOSE.
 
 
87
  Your Response: """
88
 
89
  _all_responses = """\
 
108
  """
109
 
110
  prompts = {
111
+ "game_rules": _game_rules,
112
+ "herd_animal": _herd_animal,
113
+ "chameleon_animal": _chameleon_animal,
114
  "chameleon_guess_decision": _all_responses + _chameleon_guess_decision,
115
  "chameleon_guess_animal": _chameleon_guess_animal,
116
  "vote": _all_responses + _vote_prompt