From 35af6af2983cdf844298740f7afd578ca6231d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E9=B9=8F=E7=BF=B0?= Date: Wed, 7 Jan 2026 16:36:54 +0800 Subject: [PATCH] Initial commit: Add murder mystery game with DeepSeek API integration --- .gitignore | 4 + .python-version | 1 + README.md | 82 +++++++++ plan.md | 14 ++ plan1.md | 235 +++++++++++++++++++++++++ pyproject.toml | 7 + test.py | 442 ++++++++++++++++++++++++++++++++++++++++++++++++ uv.lock | 8 + 8 files changed, 793 insertions(+) create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 README.md create mode 100644 plan.md create mode 100644 plan1.md create mode 100644 pyproject.toml create mode 100644 test.py create mode 100644 uv.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..56a8178 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.env +.venv/ +__pycache__/ +.DS_Store \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/README.md b/README.md new file mode 100644 index 0000000..b0a0364 --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# 密室杀人侦破 - 网页推理游戏 + +一个基于AI生成的密室杀人推理游戏,玩家需要通过选择不同的选项来推进案件调查,最终达到100%进度结案。 + +## 项目特色 + +- 🎮 **20个独立场景**,每个场景4个选项 +- 🤖 **AI生成剧情**,使用DeepSeek API自动生成场景和选项 +- ⚠️ **危机系统**,连续危险选择会触发特殊状态 +- 🏆 **多结局系统**,根据选择决定案件结局 +- 📱 **响应式设计**,支持移动端和桌面端 +- 💾 **进度保存**,自动保存游戏状态 + +## 游戏机制 + +### 选项类型 +- **正确选项**:正常推进剧情,进度+5~10% +- **错误选项**:可能触发坏结局,进度+5~10% +- **未知选项**:进度不变,需要谨慎判断 +- **危险选项**:可能触发最坏结局,进度+8~10% + +### 危机系统 +连续选择3次危险选项会触发危机状态: +- **紧张**:增加2个无关选项 +- **焦虑**:选项文本可能被隐藏 +- **恐慌**:界面特效和文字抖动 + +### 结局判定 +- **好结局**:正确找出真凶 +- **坏结局**:找到表面罪犯(错误+危险选择≥7次) +- **最坏结局**:被误认为罪犯(危险选择≥4次) + +## 项目结构 + +``` +密室杀人侦破/ +├── index.html # 主页面 +├── styles.css # 样式文件 +├── game.js # 游戏逻辑 +├── config.js # 配置文件 +├── plan1.md # 详细开发计划 +└── README.md # 项目说明 +``` + +## 快速开始 + +1. 直接打开 `index.html` 文件即可开始游戏 +2. 点击"开始推理"按钮进入游戏 +3. 阅读场景描述,选择你认为正确的选项 +4. 观察进度条变化,谨慎选择避免触发危机 +5. 达到100%进度时查看案件结局 + +## 技术栈 + +- **前端**: HTML5, CSS3, JavaScript (ES6+) +- **样式**: 响应式设计,CSS动画 +- **API**: DeepSeek AI API +- **存储**: LocalStorage + +## 安全说明 + +⚠️ **重要**: 当前版本在前端代码中硬编码了API密钥,这在实际部署中是不安全的。建议: + +1. 使用环境变量管理API密钥 +2. 通过后端代理调用API +3. 部署时移除硬编码的密钥 + +## 开发计划 + +详细开发计划请参考 [plan1.md](plan1.md) 文件。 + +## 扩展功能 + +未来可考虑添加: +- 多案件系统 +- 成就系统 +- 社交分享功能 +- 语音朗读功能 + +## 许可证 + +本项目仅供学习使用。 \ No newline at end of file diff --git a/plan.md b/plan.md new file mode 100644 index 0000000..bd76d77 --- /dev/null +++ b/plan.md @@ -0,0 +1,14 @@ +我正在制作一个网页游戏项目,名作“密室杀人侦破”,使用deepseek的api钥匙来自动生成足够长的剧情和选项对应的文案,其中DEEPSEEK_API_KEY=sk-f42302a1e8ff4825a19e313db79a391b +有三个主要功能:1,给出一个密室杀人案件,并且具有自动推进和演算剧情的效果,2,给出4个选项帮助玩家推进剧情,3,根据玩家的选项来推进整体进度,进度抵达100%时结案。 +交互流程:用户点开网站,看见背景介绍和案件基本信息,点击“开始推理”可以正式开始。推理过程中可以选择4个选项中的一个进行行动。每个选项都有不同的效果,会导致进度的推进或倒退。玩家需要根据当前的情况和选项的效果来判断下一步该选哪个选项。当进度抵达100%时,会显示案件的结论。 +总计20个场景,每个场景有4个选项,玩家不知道每个选项的具体效果,只能根据选项和剧情的文字来判断。每个选项都有不同的效果。分别为: +1,正确选项:进度增加5到10%,玩家可以继续推理。 +2,错误选项:进度增加5到10%,结局可能会出现坏结局选项,玩家需要更加谨慎选择。 +3,未知:进度保持不变,玩家需要更加注意自己的选择。 +4,危险:进度增加8到10%,结局可能出现最坏的情况,玩家需要更加谨慎选择。 +当玩家连续选择危险选项超过3次时,会触发危机提示,提示玩家注意自己的选择,并且随机触发以下负面效果: +1,紧张:增加无关选项两个,玩家需要更加谨慎选择。 +2,焦虑:所有选项成功率趋于平均化,玩家需要更加注意自己的选择。 +3,恐慌:修改界面颜色为黑色,红色还有白色,并且增加选项文字抖动效果。 +当结局场景时,只会出现一个选项,若错误选项和危险选项被选择的次数总计超过7次,则好结局选项被替换坏结局选项,同时若危险选项被选择的次数超过4次,则额外导致坏结局选项被替换成最坏的结局选项。 +好结局为你成功辨别了真凶,坏结局为你找到了表面上的罪犯,但似乎真凶另有其人,最坏结局为你不但没有找出真凶,还被其他人当作罪犯。 \ No newline at end of file diff --git a/plan1.md b/plan1.md new file mode 100644 index 0000000..340fd06 --- /dev/null +++ b/plan1.md @@ -0,0 +1,235 @@ +# 密室杀人侦破 - 详细开发计划 + +## 项目概述 + +**项目名称**: 密室杀人侦破 +**项目类型**: 网页推理游戏 +**核心功能**: 使用DeepSeek API自动生成剧情和选项文案 +**API密钥**: sk-f42302a1e8ff4825a19e313db79a391b + +## 游戏核心机制 + +### 1. 基本游戏流程 +- **场景数量**: 20个独立场景 +- **选项数量**: 每个场景4个选项 +- **进度系统**: 0-100%进度条,达到100%时结案 +- **结局类型**: 好结局、坏结局、最坏结局 + +### 2. 选项类型与效果 + +#### 选项分类(玩家不可见) +1. **正确选项** + - 进度增加: 5-10% + - 效果: 正常推进剧情,无负面效果 + +2. **错误选项** + - 进度增加: 5-10% + - 效果: 可能触发坏结局条件 + +3. **未知选项** + - 进度变化: 保持不变 + - 效果: 需要玩家谨慎判断 + +4. **危险选项** + - 进度增加: 8-10% + - 效果: 可能触发最坏结局条件 + +### 3. 危机系统 + +#### 触发条件 +- 连续选择危险选项 ≥ 3次 + +#### 危机效果(随机触发一种) +1. **紧张状态** + - 增加2个无关选项 + - 选项总数变为6个 + +2. **焦虑状态** + - 所有选项文本有概率变为黑框不可见状态 + - 降低判断难度 + +3. **恐慌状态** + - 界面颜色变为黑、红、白 + - 选项文字出现抖动效果 + +### 4. 结局判定系统 + +#### 结局条件 +- **好结局**: 正确辨别真凶 +- **坏结局**: 找到表面罪犯,真凶另有其人 +- **最坏结局**: 被误认为罪犯 + +#### 结局触发规则 +- 错误 + 危险选项选择次数 ≥ 7次: 好结局 → 坏结局 +- 危险选项选择次数 ≥ 4次: 坏结局 → 最坏结局 + +## 技术架构设计 + +### 1. 前端技术栈 +- **框架**: React/Vue.js +- **样式**: CSS3 + 动画效果 +- **状态管理**: Redux/Vuex +- **路由**: React Router/Vue Router + +### 2. 后端技术栈 +- **语言**: Node.js/Python +- **API集成**: DeepSeek API调用 +- **数据存储**: 本地存储/简单数据库 + +### 3. API集成设计 + +#### DeepSeek API调用策略 +```javascript +// 场景生成API调用 +const generateScene = async (currentProgress, previousChoices) => { + const prompt = ` + 生成第${currentScene}个密室杀人场景: + 当前进度:${currentProgress}% + 玩家选择历史:${previousChoices} + 请生成: + 1. 场景描述(200-300字) + 2. 4个选项文案(每个选项20-30字) + 3. 选项类型标注(正确/错误/未知/危险) + `; + + return await callDeepSeekAPI(prompt); +}; +``` + +#### 剧情连贯性保证 +- 保存玩家选择历史 +- 基于历史生成连贯剧情 +- 维护角色关系和证据链 + +## 用户界面设计 + +### 1. 页面结构 +1. **首页** + - 游戏标题和背景介绍 + - 案件基本信息 + - "开始推理"按钮 + +2. **游戏主界面** + - 顶部: 进度条显示 + - 中部: 当前场景描述 + - 底部: 4个选项按钮 + - 侧边: 证据收集区域(可选) + +3. **结局页面** + - 案件结论展示 + - 结局评价 + - 重新开始按钮 + +### 2. 视觉设计 +- **主色调**: 暗色系(营造神秘氛围) +- **字体**: 易读的衬线字体 +- **动画**: 平滑的过渡效果 +- **响应式**: 适配移动端和桌面端 + +## 开发阶段规划 + +### 第一阶段:基础框架搭建(1-2周) +- [ ] 项目初始化 +- [ ] 基础页面结构 +- [ ] 简单的状态管理 +- [ ] DeepSeek API集成测试 + +### 第二阶段:核心功能实现(2-3周) +- [ ] 场景生成系统 +- [ ] 选项选择逻辑 +- [ ] 进度管理系统 +- [ ] 基础UI界面 + +### 第三阶段:高级功能开发(2周) +- [ ] 危机系统实现 +- [ ] 结局判定系统 +- [ ] 视觉特效(恐慌状态) +- [ ] 数据持久化 + +### 第四阶段:优化和测试(1周) +- [ ] 性能优化 +- [ ] 用户体验测试 +- [ ] Bug修复 +- [ ] 部署上线 + +## 数据结构和状态管理 + +### 1. 游戏状态对象 +```javascript +const gameState = { + currentScene: 1, + progress: 0, + choicesHistory: [], + dangerCount: 0, + errorCount: 0, + crisisActive: false, + crisisType: null, + endingType: 'good' // good/bad/worst +}; +``` + +### 2. 场景数据结构 +```javascript +const sceneData = { + id: 1, + description: "场景描述文本", + options: [ + { text: "选项1", type: "correct", effect: 8 }, + { text: "选项2", type: "danger", effect: 10 }, + { text: "选项3", type: "unknown", effect: 0 }, + { text: "选项4", type: "error", effect: 6 } + ] +}; +``` + +## 安全注意事项 + +### API密钥安全 +- 不要在前端代码中硬编码API密钥 +- 使用环境变量或后端代理 +- 考虑使用服务器端API调用 + +### 用户数据保护 +- 仅存储必要的游戏进度数据 +- 不收集个人敏感信息 +- 提供数据清除功能 + +## 扩展功能(可选) + +### 1. 多案件系统 +- 不同难度级别的案件 +- 随机生成案件内容 +- 案件收藏和重玩功能 + +### 2. 成就系统 +- 完美推理成就 +- 快速通关成就 +- 特殊结局成就 + +### 3. 社交功能 +- 成绩分享 +- 排行榜系统 +- 好友挑战 + +## 风险评估 + +### 技术风险 +1. **API稳定性**: DeepSeek API服务中断 + - 应对: 本地缓存备用剧情 + +2. **生成质量**: AI生成内容不符合预期 + - 应对: 人工审核+内容过滤 + +### 用户体验风险 +1. **难度平衡**: 游戏过于简单或困难 + - 应对: 动态难度调整机制 + +2. **剧情连贯性**: 场景之间逻辑断裂 + - 应对: 强化上下文关联生成 + +--- + +**项目负责人**: 朱鹏翰 +**预计完成时间**: 6-8周 +**技术难度**: 中等 +**创新点**: AI生成剧情 + 动态结局系统 \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ce58573 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "ai-work" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [] diff --git a/test.py b/test.py new file mode 100644 index 0000000..b6ae5a4 --- /dev/null +++ b/test.py @@ -0,0 +1,442 @@ +import streamlit as st +import os +from openai import OpenAI +from dotenv import load_dotenv +import json +import time +from typing import Dict, List, Optional + +load_dotenv() +client = OpenAI(api_key=os.getenv("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com") + +# 全局状态管理 +if 'current_scene' not in st.session_state: + st.session_state.current_scene = 1 +if 'previous_choices' not in st.session_state: + st.session_state.previous_choices = [] +if 'scene_data' not in st.session_state: + st.session_state.scene_data = None +if 'game_ended' not in st.session_state: + st.session_state.game_ended = False +if 'ending_type' not in st.session_state: + st.session_state.ending_type = None + +def determine_ending(choices_history: List[Dict]) -> str: + """根据选择历史判定结局类型""" + + # 统计选项类型数量 + error_count = 0 + danger_count = 0 + + for choice in choices_history: + if choice["option_type"] == "error": + error_count += 1 + elif choice["option_type"] == "danger": + danger_count += 1 + + # 根据plan1.md中的规则判定结局 + # 规则1: 错误 + 危险选项选择次数 ≥ 7次: 好结局 → 坏结局 + # 规则2: 危险选项选择次数 ≥ 4次: 坏结局 → 最坏结局 + + if danger_count >= 4: + return "worst" # 最坏结局 + elif (error_count + danger_count) >= 7: + return "bad" # 坏结局 + else: + return "good" # 好结局 + + +def get_ending_description(ending_type: str) -> Dict: + """获取结局描述""" + + endings = { + "good": { + "title": "🎉 完美破案 - 好结局", + "description": """ + 经过缜密的调查,你成功找出了真凶! + + **案件真相**: + 真凶是李明的商业合作伙伴王强。由于商业纠纷,王强利用李明书房内的密道进入现场作案。 + 现场发现的财务文件揭示了两人之间的利益冲突,而书房内的隐藏摄像头记录下了关键证据。 + + **评价**: + 你的推理能力和观察力令人钦佩,成功还原了案件真相! + """, + "color": "success" + }, + "bad": { + "title": "😔 误判真凶 - 坏结局", + "description": """ + 你找到了表面上的罪犯,但真凶另有其人。 + + **案件真相**: + 你错误地将管家李叔认定为凶手,但实际上真凶是李明的妻子张美丽。 + 她因财产纠纷策划了这起谋杀,并巧妙地将嫌疑转移给了管家。 + + **评价**: + 虽然找到了部分线索,但关键的证据被忽略了,真凶逍遥法外。 + """, + "color": "warning" + }, + "worst": { + "title": "💀 被误认为罪犯 - 最坏结局", + "description": """ + 由于错误的调查方向,你被误认为是凶手! + + **案件真相**: + 你的调查行为引起了警方的怀疑,现场留下的指纹和监控录像被错误解读。 + 真凶趁机逃脱,而你却成为了替罪羊。 + + **评价**: + 过于冒险的调查选择导致了严重的后果,需要更加谨慎地处理案件。 + """, + "color": "error" + } + } + + return endings.get(ending_type, endings["good"]) + + +class GameSceneGenerator: + """游戏场景生成器""" + + def __init__(self): + self.api_key = os.getenv("DEEPSEEK_API_KEY") + if not self.api_key: + st.error("未找到DEEPSEEK_API_KEY环境变量") + st.stop() + + self.client = OpenAI(api_key=self.api_key, base_url="https://api.deepseek.com") + self.model = "deepseek-chat" + + def generate_scene(self, scene_number: int, previous_choices: List[str] = None) -> Dict: + """生成游戏场景""" + + if previous_choices is None: + previous_choices = [] + + # 构建提示词 + prompt = self._build_prompt(scene_number, previous_choices) + + try: + # 调用DeepSeek API + response = self._call_api(prompt) + + # 解析响应 + scene_data = self._parse_response(response, scene_number) + + return scene_data + + except Exception as e: + st.error(f"生成场景失败: {e}") + return self._get_fallback_scene(scene_number) + + def _build_prompt(self, scene_number: int, previous_choices: List[str]) -> str: + """构建API提示词""" + + history_text = "" + if previous_choices: + history_text = f"玩家之前的调查选择:{', '.join(previous_choices)}" + + prompt = f""" +请为密室杀人推理游戏生成第{scene_number}个场景。 + +案件背景:富豪李明在书房被谋杀,现场是密室状态。门窗从内部锁住,没有明显的外部入侵痕迹。 + +{history_text} + +请生成以下内容: +1. 场景描述(150-250字):详细描述当前调查现场的环境、发现的线索和可疑之处 +2. 4个调查选项:每个选项应该是合理的调查行动,长度20-40字 + +选项类型说明: +- 正确选项:能够推进案件调查的正确选择 +- 错误选项:看似合理但会误导调查的选择 +- 危险选项:可能带来风险或触发坏结局的选择 +- 未知选项:结果不确定,需要玩家谨慎判断的选择 + +请严格按照以下JSON格式返回: +{{ + "scene_number": {scene_number}, + "description": "详细的场景描述文本", + "options": [ + {{"text": "选项1文本", "type": "correct"}}, + {{"text": "选项2文本", "type": "error"}}, + {{"text": "选项3文本", "type": "danger"}}, + {{"text": "选项4文本", "type": "unknown"}} + ] +}} + +请确保: +- 场景描述符合推理游戏的氛围 +- 选项文本清晰明确,具有可操作性 +- JSON格式正确无误 +""" + + return prompt + + def _call_api(self, prompt: str) -> str: + """调用DeepSeek API""" + + try: + response = self.client.chat.completions.create( + model=self.model, + messages=[ + {"role": "system", "content": "你是一个专业的推理游戏设计师,擅长创作悬疑推理场景和调查选项。"}, + {"role": "user", "content": prompt} + ], + max_tokens=2000, + temperature=0.7 + ) + + return response.choices[0].message.content + + except Exception as e: + raise Exception(f"API调用失败: {e}") + + def _parse_response(self, response: str, scene_number: int) -> Dict: + """解析API响应""" + + # 尝试从响应中提取JSON + try: + # 查找JSON开始和结束位置 + start_idx = response.find('{') + end_idx = response.rfind('}') + 1 + + if start_idx == -1 or end_idx == 0: + raise ValueError("响应中未找到有效的JSON数据") + + json_str = response[start_idx:end_idx] + scene_data = json.loads(json_str) + + # 验证必需字段 + required_fields = ["scene_number", "description", "options"] + for field in required_fields: + if field not in scene_data: + raise ValueError(f"缺少必需字段: {field}") + + # 验证选项格式 + if len(scene_data["options"]) != 4: + raise ValueError("选项数量必须为4个") + + for option in scene_data["options"]: + if "text" not in option or "type" not in option: + raise ValueError("选项格式不正确") + + return scene_data + + except Exception as e: + st.warning(f"JSON解析失败,使用备用场景: {e}") + return self._get_fallback_scene(scene_number) + + def _get_fallback_scene(self, scene_number: int) -> Dict: + """获取备用场景(当API失败时使用)""" + + fallback_scenes = { + 1: { + "scene_number": 1, + "description": "你站在富豪李明书房门口。书房门紧闭,透过门缝可以看到里面一片狼藉。书桌上的文件散落一地,一盏台灯倒在桌角。空气中弥漫着淡淡的血腥味。", + "options": [ + {"text": "仔细检查书房门锁和周围环境", "type": "correct"}, + {"text": "立即破门而入查看情况", "type": "danger"}, + {"text": "先询问管家案发时的情况", "type": "error"}, + {"text": "检查窗户是否从内部锁住", "type": "unknown"} + ] + }, + 2: { + "scene_number": 2, + "description": "进入书房后,你看到李明倒在书桌旁的地毯上,胸口插着一把匕首。书房窗户紧闭,窗帘半拉着。书桌上散落着一些财务报表和信件。", + "options": [ + {"text": "仔细检查尸体和凶器上的指纹", "type": "correct"}, + {"text": "立即搜查书房寻找隐藏线索", "type": "danger"}, + {"text": "询问第一个发现尸体的人", "type": "error"}, + {"text": "检查书房内的监控设备", "type": "unknown"} + ] + } + } + + return fallback_scenes.get(scene_number, fallback_scenes[1]) + +def main(): + """主函数 - Streamlit应用""" + + st.set_page_config( + page_title="密室杀人侦破 - 推理游戏", + page_icon="🔍", + layout="wide" + ) + + # 初始化生成器 + generator = GameSceneGenerator() + + # 游戏标题 + st.title("🔍 密室杀人侦破") + st.markdown("---") + + # 游戏状态显示 + col_status1, col_status2, col_status3 = st.columns([1, 1, 1]) + + with col_status1: + st.metric("当前场景", st.session_state.current_scene) + + with col_status2: + st.metric("选择记录", len(st.session_state.previous_choices)) + + with col_status3: + progress = min((st.session_state.current_scene - 1) * 5, 100) + st.metric("调查进度", f"{progress}%") + + st.markdown("---") + + # 游戏主界面 + if st.session_state.game_ended: + # 显示结局页面 + ending_info = get_ending_description(st.session_state.ending_type) + + st.subheader(ending_info["title"]) + + if ending_info["color"] == "success": + st.success(ending_info["description"]) + elif ending_info["color"] == "warning": + st.warning(ending_info["description"]) + else: + st.error(ending_info["description"]) + + st.markdown("---") + + # 显示统计信息 + col1, col2, col3 = st.columns(3) + + with col1: + total_choices = len(st.session_state.previous_choices) + st.metric("总选择次数", total_choices) + + with col2: + error_count = sum(1 for c in st.session_state.previous_choices if c["option_type"] == "error") + st.metric("错误选择", error_count) + + with col3: + danger_count = sum(1 for c in st.session_state.previous_choices if c["option_type"] == "danger") + st.metric("危险选择", danger_count) + + # 重新开始按钮 + if st.button("🔄 重新开始游戏", use_container_width=True, type="primary"): + st.session_state.current_scene = 1 + st.session_state.previous_choices = [] + st.session_state.scene_data = None + st.session_state.game_ended = False + st.session_state.ending_type = None + st.rerun() + + elif st.session_state.scene_data is None: + # 显示开始界面 + st.subheader("案件背景") + st.info(""" + 富豪李明在书房被谋杀,现场是密室状态。门窗从内部锁住,没有明显的外部入侵痕迹。 + 书房内发现李明倒在书桌旁,胸口插着一把匕首。现场没有发现凶手的明显痕迹。 + + 你需要通过调查各个场景,收集线索,最终找出真凶。 + """) + + if st.button("🚀 开始调查", use_container_width=True, type="primary"): + with st.spinner("正在生成第一个场景..."): + # 生成场景一 + scene_data = generator.generate_scene(1) + st.session_state.scene_data = scene_data + st.session_state.current_scene = 1 + st.rerun() + else: + # 显示当前场景 + scene_data = st.session_state.scene_data + + # 显示场景描述 + st.subheader(f"场景 {st.session_state.current_scene}") + st.write(scene_data["description"]) + + st.markdown("---") + + # 显示选项(统一外观) + st.subheader("调查行动") + + # 场景20的特殊处理 + if st.session_state.current_scene == 20: + # 显示"最后推断"按钮 + if st.button("🔍 最后推断", use_container_width=True, type="primary"): + # 判定结局 + ending_type = determine_ending(st.session_state.previous_choices) + st.session_state.ending_type = ending_type + st.session_state.game_ended = True + st.rerun() + else: + # 正常场景的选项按钮 + for i, option in enumerate(scene_data["options"], 1): + # 使用统一的按钮样式,隐藏类型信息 + if st.button( + f"{option['text']}", + key=f"option_{i}_{st.session_state.current_scene}", + use_container_width=True + ): + # 记录选择 + choice_record = { + "scene": st.session_state.current_scene, + "option_text": option["text"], + "option_type": option["type"] + } + st.session_state.previous_choices.append(choice_record) + + # 生成下一场景 + with st.spinner("正在进入下一场景..."): + next_scene = st.session_state.current_scene + 1 + if next_scene <= 20: + new_scene_data = generator.generate_scene( + next_scene, + [choice["option_text"] for choice in st.session_state.previous_choices] + ) + st.session_state.scene_data = new_scene_data + st.session_state.current_scene = next_scene + else: + # 游戏结束 + st.session_state.scene_data = None + st.session_state.current_scene = 1 + st.session_state.previous_choices = [] + st.rerun() + + # 显示选择历史 + if st.session_state.previous_choices: + st.markdown("---") + st.subheader("调查记录") + + for i, choice in enumerate(st.session_state.previous_choices, 1): + st.write(f"**场景{choice['scene']}**: {choice['option_text']}") + + # 侧边栏 - 游戏控制 + st.sidebar.header("游戏控制") + + if st.session_state.scene_data or st.session_state.game_ended: + if st.sidebar.button("🔄 重新开始", use_container_width=True): + st.session_state.scene_data = None + st.session_state.current_scene = 1 + st.session_state.previous_choices = [] + st.session_state.game_ended = False + st.session_state.ending_type = None + st.rerun() + + # 侧边栏 - API状态 + st.sidebar.markdown("---") + st.sidebar.subheader("API状态") + + if generator.api_key: + st.sidebar.success("✅ API密钥已配置") + else: + st.sidebar.error("❌ API密钥未找到") + + st.sidebar.info(""" + **游戏说明**: + - 点击"开始调查"开始游戏 + - 双击选项进入下一场景 + - 所有选项外观一致,需要谨慎选择 + - 选择记录会影响后续剧情发展 + """) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..3e4c3f3 --- /dev/null +++ b/uv.lock @@ -0,0 +1,8 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "ai-work" +version = "0.1.0" +source = { virtual = "." }