"""AI 玩家提示词:身份人设、决策 JSON、公开发言。""" from __future__ import annotations import json RULES = """【狼人杀规则简述】 - 夜晚顺序:守卫守护 → 狼人袭击 → 预言家查验 → 女巫用药(解药/毒药各一瓶,每夜限一瓶,首夜可自救)。 - 守卫不能连续两晚守同一人;「同守同救」(守卫和女巫同时保护被袭者)会导致该玩家死亡。 - 白天:警长竞选(首日)→ 轮流发言 → 全员投票放逐(平票无人出局,警长票计1.5票)。 - 猎人被投票或被狼人杀死时可开枪带走一人(被毒杀不能开枪)。 - 胜负:狼人杀光所有神职或所有村民(屠边)即获胜;好人放逐所有狼人则获胜。""" KIND_QUESTIONS = { "kill": ("作为狼人,选择今晚袭击的目标", "通常优先袭击跳预言家的玩家、发言逻辑威胁大的玩家;也要考虑票型,避开会被怀疑的队友策略。"), "protect": ("作为守卫,选择今晚守护的玩家", "通常守护跳预言家的玩家或你认为重要的神职;也可以空守。"), "check": ("作为预言家,选择今晚查验的玩家", "优先验身份模糊、立场可疑的玩家,积累信息。"), "witch_save": ("作为女巫,决定是否使用解药救人", "如果被袭者是跳预言家的关键角色或你自己(首夜),通常值得救;也要提防狼人骗药。"), "witch_poison": ("作为女巫,决定是否使用毒药毒杀一人", "只有在有较强把握某人是狼时才用毒,误毒好人会严重伤害好人阵营;可以不用。"), "shoot": ("作为猎人,选择开枪带走的目标", "带走你认为最像狼的玩家;没把握也可以不开枪。"), "elect": ("决定是否上警竞选警长", "跳预言家通常应该上警来报查验;狼人可以选择不上警隐藏身份或悍跳预言家。"), "sheriff_vote": ("投票选出警长", "把票投给你认为能带领好人、发言可信的候选人。"), "badge": ("作为死亡的警长,决定警徽流向", "通常把警徽交给最信任的好人(比如你验过的好人);也可以撕毁警徽表达态度。"), "vote": ("投票放逐一名玩家", "根据发言、逻辑和票型投出你最怀疑的狼人;没把握可以弃票。"), } def _state_text(ctx: dict) -> str: alive = "、".join(f"{p['seat']}号{p['name']}" for p in ctx["alive"]) priv = "\n".join(ctx["private"]) if ctx["private"] else "无" return ( f"【当前局面】第{ctx['day']}天,{ctx['phase_text']}\n" f"【存活玩家】{alive}\n" f"【你的私有信息】\n{priv}\n" f"【对局记录(公开部分,最近在前为旧)】\n{ctx['log_text'] or '(暂无)'}" ) def decision_messages(kind: str, ctx: dict) -> tuple[str, str]: title, tip = KIND_QUESTIONS.get(kind, (kind, "")) options = ctx["options"] opts_text = "、".join(str(o) + "号" for o in options) if options else "无" allow_skip = kind in ("witch_save", "witch_poison", "shoot", "elect", "badge", "vote", "protect", "kill") system = ( f"你正在玩一局狼人杀。你的名字是「{ctx['name']}」,座位号是{ctx['seat']}号。\n" f"你的身份:{ctx['role_name']} —— {ctx['role_desc']}\n" f"你的性格:{ctx['persona'] or '普通玩家'}\n" f"{RULES}\n" "你是一个真实的人类玩家。永远不要提及你是AI、模型或程序。" ) user = ( f"{_state_text(ctx)}\n\n" f"【你的决策】{title}。\n{tip}\n" f"可选目标(座位号):{opts_text}\n" + ("你可以选择放弃(target 为 null)。\n" if allow_skip else "必须选择一个目标。\n") + '只输出 JSON:{"target": 座位号数字或null, "reason": "不超过30字的理由"}' ) return system, user def speech_messages(ctx: dict) -> tuple[str, str]: purpose = ctx.get("purpose") or "day" if purpose == "election": task = "现在是警长竞选阶段,请发表你的竞选发言(表决心、报身份或藏身份,符合你的策略)。" else: task = "轮到你发言了。请分析当前局势,表达你的身份立场、怀疑对象和投票意向。" system = ( f"你正在玩一局狼人杀。你的名字是「{ctx['name']}」,座位号是{ctx['seat']}号。\n" f"你的身份:{ctx['role_name']} —— {ctx['role_desc']}\n" f"你的性格:{ctx['persona'] or '普通玩家'}\n" f"{RULES}\n" "你是一个真实的人类玩家。永远不要提及你是AI、模型或程序。" "发言要口语化、简短自然,像真人打字,可以引用别人的发言和票型,不要罗列条目。" ) user = f"{_state_text(ctx)}\n\n【任务】{task}\n直接输出发言内容本身(2~5句话),不要任何前缀、引号或解释。" return system, user def options_json(options: list) -> str: return json.dumps(options, ensure_ascii=False)