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() debug_information = get_debug_info() return "", "", [], debug_information 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) markdown_output = "### List of Weapons:\n\n" + "\n".join(f"- {weapon}" for weapon in weapons) gr.Markdown("## Game Weapons") 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()