cluedo / app.py
ilyassh's picture
Update app.py
7f7adcc verified
raw
history blame
22.2 kB
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},
{"description": "charming but evasive", "hot_headed": False},
{"description": "intellectual and nervous", "hot_headed": False},
{"description": "gracious but secretive", "hot_headed": False},
{"description": "amiable yet deceptive", "hot_headed": False},
{"description": "hot-headed and impulsive", "hot_headed": True},
{"description": "calm and collected", "hot_headed": False},
{"description": "mysterious and aloof", "hot_headed": False},
{"description": "jovial but cunning", "hot_headed": False},
{"description": "nervous and jittery", "hot_headed": False},
{"description": "sarcastic and witty", "hot_headed": False},
{"description": "arrogant and dismissive", "hot_headed": True}
]
'''
anger_level_descriptions = {
0: "calm",
1: "slightly annoyed",
2: "annoyed",
3: "frustrated",
4: "angry",
5: "very angry",
6: "furious"
}
'''
def get_anger_description(anger_level):
if anger_level <= 1:
return "calm"
elif anger_level == 2:
return "slightly annoyed"
elif anger_level == 3:
return "annoyed"
elif anger_level == 4:
return "frustrated"
elif anger_level == 5:
return "angry"
elif anger_level == 6:
return "very angry"
else:
return "furious"
def get_trust_description(trust_level):
if trust_level <= 2:
return "distrustful"
elif trust_level <= 4:
return "untrusting"
elif trust_level == 5:
return "neutral"
elif trust_level <= 7:
return "trusting"
elif trust_level <= 9:
return "very trusting"
else:
return "completely trusting"
suspects = {}
game_state = {
"murderer": "",
"weapon": "",
"location": "",
"is_game_over": False,
"clues": [],
"history": [],
"accused": False,
"turns": 0,
"searched_locations": [],
"eavesdropped": False,
"bluffed": False,
"player_questions": []
}
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
game_state["player_questions"] = []
random.shuffle(possible_personalities)
motives = ["inheritance", "jealousy", "revenge", "secret affair", "business rivalry", "blackmail"]
relationships = ["friends", "colleagues", "family", "rivals", "acquaintances"]
non_murderer_names = [name for name in suspect_names if name != game_state["murderer"]]
non_murderer_alibi_locations = random.sample(locations, len(non_murderer_names))
for i, suspect_name in enumerate(non_murderer_names):
suspect = {
"name": suspect_name,
"is_murderer": False,
"personality": possible_personalities[i % len(possible_personalities)]["description"],
"hot_headed": possible_personalities[i % len(possible_personalities)]["hot_headed"],
"anger_level": 0,
"trust_level": 5,
"alibi_location": non_murderer_alibi_locations[i],
"alibi_with": [],
"knowledge": {},
"motive": random.choice(motives),
"relationships": {},
"backstory": "",
"suspects": None
}
suspects[suspect_name] = suspect
murderer_alibi_location = random.choice(non_murderer_alibi_locations)
murderer = {
"name": game_state["murderer"],
"is_murderer": True,
"personality": possible_personalities[len(non_murderer_names) % len(possible_personalities)]["description"],
"hot_headed": possible_personalities[len(non_murderer_names) % len(possible_personalities)]["hot_headed"],
"anger_level": 0,
"trust_level": 5,
"alibi_location": murderer_alibi_location,
"alibi_with": [],
"knowledge": {},
"motive": random.choice(motives),
"relationships": {},
"backstory": "",
"suspects": None
}
suspects[game_state["murderer"]] = murderer
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) #отношения между ИИ
suspect["suspects"] = random.choice([
s for s in other_suspects
if s not in suspect["alibi_with"]]) #если были в одной локации, то ИИ не подозревает их
suspect["backstory"] = generate_backstory(suspect)
suspect["knowledge"]["others_locations"] = generate_others_locations_knowledge(suspect)
for suspect in suspects.values():
suspect["knowledge"]["self"] = generate_knowledge(suspect)
def generate_backstory(suspect):
backstory = f"{suspect['name']} has a backstory involving {suspect['motive']}."
return backstory
def generate_knowledge(suspect):
details = f"You have a motive of {suspect['motive']}. {suspect['backstory']}"
relationship_details = ". ".join([f"You are {relation} with {other}"
for other, relation in suspect["relationships"].items()])
if suspect["is_murderer"]:
knowledge = f"You are the murderer. You claim you were in the {suspect['alibi_location']} during the murder, but you were actually committing the crime in the {game_state['location']}. You need to 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
elif other["name"] == game_state["murderer"]:
knowledge[other["name"]] = "you have no idea where they were." #никто не знает где был убийца
else:
chance = random.random()
if chance < 0.5:
knowledge[other["name"]] = f"was in the {other['alibi_location']}." #50 знает где был другой игрок
elif chance < 0.75:
knowledge[other["name"]] = f"you are not sure where they were, but they might have been in the {random.choice(locations)}." #25 не уверен, говорит рандомную информацию
else:
knowledge[other["name"]] = "you have no idea where they were." #25 не знает
return knowledge
def analyze_tone(player_input):
accusatory_words = ["did you", "murderer", "kill", "guilty", "crime", "weapon", "suspect"] #бэйсик тон детекшн, можно прикрутить сентимент анализ
if any(word in player_input.lower() for word in accusatory_words):
return "accusatory"
else:
return "neutral"
def get_ai_response(suspect_name, player_input):
suspect = suspects[suspect_name]
game_state["turns"] += 1 #пусть будет
game_state["player_questions"].append(player_input)
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
if suspect["anger_level"] > 6:
suspect["anger_level"] = 6
if suspect["anger_level"] >= 3 and suspect["hot_headed"]:
suspect["personality"] = "agitated and defensive" #меняем персоналити изза того что чел агрессивный
personality = suspect["personality"]
knowledge = suspect["knowledge"]["self"]
others_knowledge = suspect["knowledge"]["others_locations"]
anger_description = get_anger_description(suspect["anger_level"])
trust_description = get_trust_description(suspect["trust_level"]) #достаём всю инфу
previous_questions = " ".join(game_state["player_questions"][-3:]) #контекст
system_prompt = f"You are {suspect_name}, who is {personality}. {knowledge}"
for other_name, info in others_knowledge.items():
system_prompt += f" You know that {other_name} {info}"
system_prompt += f" You suspect that {suspect['suspects']} might be involved due to {suspect['motive']}."
system_prompt += f" Your current emotional state is {anger_description}, and your level of trust towards the detective is {trust_description}."
if suspect["is_murderer"]:
system_prompt += " You are the murderer and will lie to protect yourself."
system_prompt += f" Previously, the detective asked: {previous_questions}"
user_message = f"The detective asks: \"{player_input}\" As {suspect_name}, respond in first person, staying in character. Provide a detailed response, and consider any previous interactions. If you lie, include subtle hints like hesitations or contradictions."
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, selected_suspects): #парсим инпуты
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:
if not selected_suspects:
return "Please select at least one suspect to question.", game_state["history"]
elif len(selected_suspects) == 1:
suspect_name = selected_suspects[0]
ai_response = get_ai_response(suspect_name, player_input)
game_state["history"].append((suspect_name, ai_response))
return ai_response, game_state["history"]
else:
ai_response = get_group_response(selected_suspects, player_input)
game_state["history"].append(("Group", ai_response))
return ai_response, game_state["history"]
def search_location(location): #тут 100% можно что-то докрутить, очень базовый функционал
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']}!"
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 think {suspects[suspect1]['suspects']} might be involved. They had a motive because of {suspects[suspects[suspect1]['suspects']]['motive']}.'"
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
success = random.choice([True, False])
if success:
return "You successfully detect that the suspect is lying!"
else:
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, selected_suspects):
response, history = process_input(player_input, selected_suspects)
chat_history = ""
for speaker, text in history:
chat_history += f"**{speaker}:** {text}\n\n"
return chat_history
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"Suspects: {suspect['suspects']}\n"
info += f"Relationships: {suspect['relationships']}\n"
info += f"Backstory: {suspect['backstory']}\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("# Cluedo")
with gr.Tabs():
with gr.TabItem("Game"):
with gr.Row():
with gr.Column():
suspect_selection = gr.CheckboxGroup(choices=suspect_names, label="Select Suspects to Question")
player_input = gr.Textbox(lines=1, label="Your Input")
send_button = gr.Button("Send")
restart_button = gr.Button("Restart Game")
chatbox = gr.Textbox(lines=20, label="Chat History", interactive=False)
with gr.TabItem("Instructions"):
gr.Markdown("## How to Play")
gr.Markdown("""
- **Ask a Question**: Type your question in the input box.
- **Select Suspects**: Choose one or more suspects to question by selecting them.
- If you select multiple suspects, they will be questioned as a group.
- **Other Actions**:
- **Search a Location**: Type `"Search [Location]"` to search a room for clues.
- **Eavesdrop on Suspects**: Type `"Eavesdrop"` to overhear conversations.
- **Bluff a Suspect**: Type `"Bluff [Suspect]"` to try and trick a suspect into revealing information.
- **Analyze a Response**: Type `"Analyze"` to attempt to detect lies.
- **Reveal Your Deductions**: Type `"Reveal: [Your deductions]"` to present your findings before accusing.
- **Make an Accusation**: Include the word `"accuse"` in your statement to accuse a suspect.
- **Note**: Be careful with your actions; they may affect suspects' trust and anger levels.
""")
markdown_output = "### List of Locations:\n\n" + "\n".join(f"- {location}" for location in locations)
gr.Markdown("## Game Locations")
gr.Markdown(markdown_output)
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, selected_suspects):
chat_history = chat_interface(player_input, selected_suspects)
debug_information = get_debug_info()
return chat_history, "", [], debug_information
send_button.click(send_message, inputs=[player_input, suspect_selection], outputs=[chatbox, player_input, suspect_selection, debug_info])
restart_button.click(fn=restart_game, inputs=None, outputs=[chatbox, player_input, suspect_selection, debug_info])
initialize_game()
demo.launch()