cluedo / app.py
ilyassh's picture
Update app.py
6491daf verified
raw
history blame
19.7 kB
# murder_mystery.py
import random
import gradio as gr
from huggingface_hub import InferenceClient
model_name = "Qwen/Qwen2.5-72B-Instruct"
client = InferenceClient(model_name)
def llm_inference(messages):
eos_token = "<|endoftext|>"
output = client.chat.completions.create(
messages=messages,
stream=False,
temperature=0.7,
top_p=0.1,
max_tokens=512,
stop=[eos_token]
)
response = ''
for choice in output.choices:
response += choice['message']['content']
return response
suspect_names = ["Colonel Mustard", "Miss Scarlett", "Professor Plum", "Mrs. Peacock", "Mr. Green", "Dr. Orchid"]
weapons = ["Candlestick", "Dagger", "Lead Pipe", "Revolver", "Rope", "Wrench", "Poison"]
locations = ["Kitchen", "Ballroom", "Conservatory", "Dining Room", "Library", "Study", "Billiard Room", "Lounge"]
possible_personalities = [
{"description": "stern and suspicious", "hot_headed": False, "anger_threshold": 5},
{"description": "charming but evasive", "hot_headed": False, "anger_threshold": 7},
{"description": "intellectual and nervous", "hot_headed": False, "anger_threshold": 6},
{"description": "gracious but secretive", "hot_headed": False, "anger_threshold": 6},
{"description": "amiable yet deceptive", "hot_headed": False, "anger_threshold": 8},
{"description": "hot-headed and impulsive", "hot_headed": True, "anger_threshold": 3},
{"description": "calm and collected", "hot_headed": False, "anger_threshold": 9},
{"description": "mysterious and aloof", "hot_headed": False, "anger_threshold": 5},
{"description": "jovial but cunning", "hot_headed": False, "anger_threshold": 7},
{"description": "nervous and jittery", "hot_headed": False, "anger_threshold": 5},
{"description": "sarcastic and witty", "hot_headed": False, "anger_threshold": 6},
{"description": "arrogant and dismissive", "hot_headed": True, "anger_threshold": 4}
]
suspects = {}
game_state = {
"murderer": "",
"weapon": "",
"location": "",
"is_game_over": False,
"clues": [],
"history": [],
"accused": False,
"turns": 0,
"searched_locations": [],
"eavesdropped": False,
"bluffed": False
}
def initialize_game():
game_state["murderer"] = random.choice(suspect_names)
game_state["weapon"] = random.choice(weapons)
game_state["location"] = random.choice(locations)
game_state["is_game_over"] = False
game_state["clues"] = []
game_state["history"] = []
game_state["accused"] = False
game_state["turns"] = 0
game_state["searched_locations"] = []
game_state["eavesdropped"] = False
game_state["bluffed"] = False
random.shuffle(possible_personalities)
alibi_locations = random.sample(locations, len(suspect_names))
motives = ["inheritance", "jealousy", "revenge", "secret affair", "business rivalry", "blackmail"]
relationships = ["friends", "colleagues", "family", "rivals", "acquaintances"]
for i, suspect_name in enumerate(suspect_names):
suspect = {
"name": suspect_name,
"is_murderer": suspect_name == game_state["murderer"],
"personality": possible_personalities[i % len(possible_personalities)]["description"],
"hot_headed": possible_personalities[i % len(possible_personalities)]["hot_headed"],
"anger_threshold": possible_personalities[i % len(possible_personalities)]["anger_threshold"],
"anger_level": 0,
"trust_level": 5,
"alibi_location": alibi_locations[i],
"alibi_with": [],
"knowledge": {},
"motive": random.choice(motives),
"relationships": {}
}
suspects[suspect_name] = suspect
for suspect in suspects.values():
others_in_same_location = [s["name"] for s in suspects.values() if s["alibi_location"] == suspect["alibi_location"] and s["name"] != suspect["name"]]
suspect["alibi_with"] = others_in_same_location
for suspect in suspects.values():
other_suspects = [s["name"] for s in suspects.values() if s["name"] != suspect["name"]]
for other in other_suspects:
suspect["relationships"][other] = random.choice(relationships)
# Add knowledge about others' locations
suspect["knowledge"]["others_locations"] = generate_others_locations_knowledge(suspect)
for suspect in suspects.values():
suspect["knowledge"]["self"] = generate_knowledge(suspect)
print(f"Debug: Murderer is {game_state['murderer']}, Weapon is {game_state['weapon']}, Location is {game_state['location']}")
for suspect in suspects.values():
print(f"Debug: {suspect['name']} - Personality: {suspect['personality']}, Hot-headed: {suspect['hot_headed']}, Anger Threshold: {suspect['anger_threshold']}, Motive: {suspect['motive']}, Relationships: {suspect['relationships']}")
def generate_knowledge(suspect):
details = f"You have a motive of {suspect['motive']}."
relationship_details = ". ".join([f"You are {relation} with {other}" for other, relation in suspect["relationships"].items()])
if suspect["is_murderer"]:
# Murderer lies about their alibi
suspect["alibi_location"] = random.choice([loc for loc in locations if loc != game_state["location"]])
suspect["alibi_with"] = []
knowledge = f"You are the murderer. Lie about your alibi and deflect suspicion. {details} {relationship_details}"
else:
knowledge = f"You were in the {suspect['alibi_location']} with {', '.join(suspect['alibi_with'])} during the murder. {details} {relationship_details}"
if random.choice([True, False]):
knowledge += f" You know that the weapon is the {game_state['weapon']}."
if random.choice([True, False]):
knowledge += f" You know that the location of the murder was the {game_state['location']}."
return knowledge
def generate_others_locations_knowledge(suspect):
knowledge = {}
for other in suspects.values():
if other["name"] == suspect["name"]:
continue
chance = random.random()
if chance < 0.5:
# They know where the other was
knowledge[other["name"]] = f"They were in the {other['alibi_location']}."
elif chance < 0.75:
# They are unsure
knowledge[other["name"]] = "You are not sure where they were."
else:
# They don't know
knowledge[other["name"]] = "You have no idea where they were."
return knowledge
def get_ai_response(suspect_name, player_input):
suspect = suspects[suspect_name]
game_state["turns"] += 1
tone = analyze_tone(player_input)
if "accuse" in player_input.lower() or any(word in player_input.lower() for word in ["murderer", "kill", "guilty"]):
suspect["trust_level"] -= 1
else:
suspect["trust_level"] += 1
suspect["trust_level"] = max(0, min(10, suspect["trust_level"]))
if tone == "accusatory":
suspect["anger_level"] += 2
suspect["trust_level"] -= 1
else:
suspect["anger_level"] += 1
personality = suspect["personality"]
knowledge = suspect["knowledge"]["self"]
others_knowledge = suspect["knowledge"]["others_locations"]
anger_level = suspect["anger_level"]
hot_headed = suspect["hot_headed"]
trust_level = suspect["trust_level"]
system_prompt = f"You are {suspect_name}, who is {personality}. {knowledge}"
# Include knowledge about others
for other_name, info in others_knowledge.items():
system_prompt += f" You know that {other_name} {info}"
if hot_headed and anger_level >= suspect["anger_threshold"]:
system_prompt += f" You are extremely angry and may respond aggressively."
if suspect["is_murderer"]:
system_prompt += " You are the murderer and will lie to protect yourself."
if trust_level >= 7:
system_prompt += " You trust the detective and are willing to share information."
elif trust_level <= 3:
system_prompt += " You do not trust the detective and may withhold information."
user_message = f"The detective asks: \"{player_input}\" As {suspect_name}, respond in first person, staying in character."
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_message}
]
response = llm_inference(messages)
return response.strip()
def get_group_response(suspect_names_list, player_input):
responses = []
for suspect_name in suspect_names_list:
response = get_ai_response(suspect_name, player_input)
responses.append(f"**{suspect_name}:** {response.strip()}")
return "\n\n".join(responses)
def process_input(player_input):
if game_state["is_game_over"]:
return "The game is over. Please restart to play again.", game_state["history"]
if game_state["accused"]:
return "You have already made an accusation. Please restart to play again.", game_state["history"]
game_state["history"].append(("Detective", player_input))
if player_input.lower().startswith("search"):
location = player_input[7:].strip()
result = search_location(location)
game_state["history"].append(("System", result))
return result, game_state["history"]
elif player_input.lower().startswith("eavesdrop"):
result = eavesdrop()
game_state["history"].append(("System", result))
return result, game_state["history"]
elif player_input.lower().startswith("bluff"):
result = bluff(player_input)
game_state["history"].append(("System", result))
return result, game_state["history"]
elif player_input.lower().startswith("analyze"):
result = analyze_response()
game_state["history"].append(("System", result))
return result, game_state["history"]
elif player_input.lower().startswith("reveal"):
result = endgame_reveal(player_input)
game_state["history"].append(("System", result))
return result, game_state["history"]
elif "accuse" in player_input.lower():
game_state["accused"] = True
result = handle_accusation(player_input)
game_state["history"].append(("System", result))
return result, game_state["history"]
else:
mentioned_suspects = [suspect_name for suspect_name in suspect_names if suspect_name.lower() in player_input.lower()]
if len(mentioned_suspects) == 0:
return "Please direct your question to a suspect.", game_state["history"]
elif len(mentioned_suspects) == 1:
suspect_name = mentioned_suspects[0]
ai_response = get_ai_response(suspect_name, player_input)
game_state["history"].append((suspect_name, ai_response))
extract_clues(ai_response)
return ai_response, game_state["history"]
else:
ai_response = get_group_response(mentioned_suspects, player_input)
game_state["history"].append(("Group", ai_response))
extract_clues(ai_response)
return ai_response, game_state["history"]
def extract_clues(ai_response):
clues = []
for weapon in weapons:
if weapon.lower() in ai_response.lower() and weapon != game_state["weapon"]:
clues.append(f"The weapon is not the {weapon}.")
for location in locations:
if location.lower() in ai_response.lower() and location != game_state["location"]:
clues.append(f"The location is not the {location}.")
for suspect_name in suspect_names:
if suspect_name.lower() in ai_response.lower() and suspect_name != game_state["murderer"]:
clues.append(f"{suspect_name} is not the murderer.")
for clue in clues:
if clue not in game_state["clues"]:
game_state["clues"].append(clue)
def search_location(location):
if location not in locations:
return "That location does not exist."
if location in game_state["searched_locations"]:
return "You have already searched this location."
game_state["searched_locations"].append(location)
game_state["turns"] += 1
if location == game_state["location"]:
found_weapon = f"You found the {game_state['weapon']}!"
game_state["clues"].append(found_weapon)
return f"You search the {location} and {found_weapon}"
else:
return f"You search the {location} but find nothing of interest."
def eavesdrop():
if game_state["eavesdropped"]:
return "You have already eavesdropped once."
game_state["eavesdropped"] = True
game_state["turns"] += 1
suspect1, suspect2 = random.sample(suspect_names, 2)
conversation = f"{suspect1} whispers to {suspect2}: 'I hope the detective doesn't find out about our {suspects[suspect1]['relationships'][suspect2]}.'"
extract_clues(conversation)
return f"You overhear a conversation: {conversation}"
def bluff(player_input):
game_state["bluffed"] = True
game_state["turns"] += 1
mentioned_suspects = [suspect_name for suspect_name in suspect_names if suspect_name.lower() in player_input.lower()]
if not mentioned_suspects:
return "You need to specify a suspect to bluff."
suspect_name = mentioned_suspects[0]
suspect = suspects[suspect_name]
suspect["trust_level"] -= 2
if suspect["trust_level"] <= 3:
return f"{suspect_name} doesn't believe your bluff and now trusts you even less."
else:
response = f"{suspect_name} seems unsettled by your claim and might reveal more information."
return response
def analyze_response():
game_state["turns"] += 1
puzzle = "Solve this riddle to analyze the suspect's response: What has keys but can't open locks?"
correct_answer = "piano"
# For the purpose of this code, we'll assume the player always fails
return "You fail to detect any lies."
def handle_accusation(player_input):
suspect_guess = None
weapon_guess = None
location_guess = None
for suspect_name in suspect_names:
if suspect_name.lower() in player_input.lower():
suspect_guess = suspect_name
break
for weapon in weapons:
if weapon.lower() in player_input.lower():
weapon_guess = weapon
break
for location in locations:
if location.lower() in player_input.lower():
location_guess = location
break
if (suspect_guess == game_state["murderer"] and
weapon_guess == game_state["weapon"] and
location_guess == game_state["location"]):
game_state["is_game_over"] = True
return f"Congratulations! You solved the case. It was {suspect_guess} with the {weapon_guess} in the {location_guess}."
else:
game_state["is_game_over"] = True
return f"Incorrect accusation! You lose. The real murderer was {game_state['murderer']} with the {game_state['weapon']} in the {game_state['location']}."
def endgame_reveal(player_input):
game_state["turns"] += 1
correctness = 0
if game_state["murderer"].lower() in player_input.lower():
correctness += 1
if game_state["weapon"].lower() in player_input.lower():
correctness += 1
if game_state["location"].lower() in player_input.lower():
correctness += 1
if correctness == 3:
game_state["is_game_over"] = True
return "Your deductions are spot on! The murderer confesses and the case is closed."
else:
game_state["is_game_over"] = True
return "Your deductions have inaccuracies. The murderer denies your claims and escapes justice."
def chat_interface(player_input):
response, history = process_input(player_input)
chat_history = ""
for speaker, text in history:
chat_history += f"**{speaker}:** {text}\n\n"
clues_display = "\n".join(game_state["clues"])
return chat_history, clues_display
def get_debug_info():
debug_info = ""
debug_info += f"Murderer: {game_state['murderer']}\n"
debug_info += f"Weapon: {game_state['weapon']}\n"
debug_info += f"Location: {game_state['location']}\n\n"
for suspect in suspects.values():
info = f"Name: {suspect['name']}\n"
info += f"Personality: {suspect['personality']}\n"
info += f"Hot-headed: {suspect['hot_headed']}\n"
info += f"Anger Level: {suspect['anger_level']}\n"
info += f"Trust Level: {suspect['trust_level']}\n"
info += f"Is Murderer: {suspect['is_murderer']}\n"
info += f"Alibi Location: {suspect['alibi_location']}\n"
info += f"Alibi With: {', '.join(suspect['alibi_with'])}\n"
info += f"Motive: {suspect['motive']}\n"
info += f"Relationships: {suspect['relationships']}\n"
info += f"Knowledge: {suspect['knowledge']}\n\n"
debug_info += info
return debug_info
def restart_game():
initialize_game()
return "", "", ""
with gr.Blocks() as demo:
gr.Markdown("# Murder Mystery Game")
with gr.Tabs():
with gr.TabItem("Game"):
with gr.Row():
with gr.Column():
gr.Markdown("## Commands")
gr.Markdown("- **Ask a question to a suspect**: Include the suspect's name in your question.\n Example: *\"Miss Scarlett, where were you during the evening?\"*")
gr.Markdown("- **Interrogate multiple suspects**: Include multiple suspects' names in your question.\n Example: *\"Colonel Mustard and Mrs. Peacock, what can you tell me about the night?\"*")
gr.Markdown("- **Search a location**: Type *\"Search [Location]\"*.\n Example: *\"Search Kitchen\"*")
gr.Markdown("- **Eavesdrop on suspects**: Type *\"Eavesdrop\"*")
gr.Markdown("- **Bluff a suspect**: Type *\"Bluff [Suspect]\"*")
gr.Markdown("- **Analyze a response**: Type *\"Analyze\"*")
gr.Markdown("- **Reveal your deductions**: Type *\"Reveal: [Your deductions]\"*")
gr.Markdown("- **Make an accusation**: Include the word *\"accuse\"*.\n Example: *\"I accuse Colonel Mustard with the Rope in the Study.\"*")
player_input = gr.Textbox(lines=1, label="Your Input")
send_button = gr.Button("Send")
restart_button = gr.Button("Restart Game")
chatbox = gr.Textbox(lines=15, label="Chat History", interactive=False)
with gr.Column():
gr.Markdown("## Suspects")
gr.Markdown(', '.join(suspect_names))
gr.Markdown("## Weapons")
gr.Markdown(', '.join(weapons))
gr.Markdown("## Locations")
gr.Markdown(', '.join(locations))
clues = gr.Textbox(lines=15, label="Discovered Clues", interactive=False)
with gr.TabItem("Debug"):
debug_info = gr.Textbox(lines=30, label="Debug Information", interactive=False)
update_debug_button = gr.Button("Update Debug Info")
update_debug_button.click(fn=lambda: get_debug_info(), inputs=None, outputs=debug_info)
def send_message(player_input):
chat_history, clues_display = chat_interface(player_input)
debug_information = get_debug_info()
return chat_history, "", clues_display, debug_information
send_button.click(send_message, inputs=player_input, outputs=[chatbox, player_input, clues, debug_info])
restart_button.click(fn=restart_game, inputs=None, outputs=[chatbox, clues, debug_info])
initialize_game()
demo.launch()