diff --git a/idiom_game.py b/idiom_game.py new file mode 100644 index 0000000..10a76e1 --- /dev/null +++ b/idiom_game.py @@ -0,0 +1,191 @@ +import streamlit as st +import requests +from pydantic import BaseModel, Field +from dotenv import load_dotenv +import os +import json + +# 加载环境变量 +load_dotenv() + +# 设置API密钥 +DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "sk-b04b9134730c4375a5789e2e00978ee0") +API_BASE_URL = "https://api.deepseek.com/v1/chat/completions" + +# 定义Pydantic数据模型 +class IdiomResponse(BaseModel): + idiom: str = Field(..., description="成语") + meaning: str = Field(..., description="成语含义") + pinyin: str = Field(..., description="拼音") + explanation: str = Field(..., description="详细解释") + +class GameState(BaseModel): + current_idiom: str = Field(default="", description="当前成语") + history: list = Field(default_factory=list, description="成语接龙历史") + score: int = Field(default=0, description="得分") + is_game_over: bool = Field(default=False, description="游戏是否结束") + +# 初始化游戏状态 +if "game_state" not in st.session_state: + st.session_state.game_state = GameState() + +# 调用DeepSeek API生成下一个成语 +def get_next_idiom(last_idiom: str) -> IdiomResponse: + headers = { + "Authorization": f"Bearer {DEEPSEEK_API_KEY}", + "Content-Type": "application/json" + } + + # 构建提示词 + prompt = f"请你作为一个成语接龙游戏的助手,根据最后一个成语的最后一个字,生成下一个成语。上一个成语是:{last_idiom}。请严格按照JSON格式返回:{{\"idiom\": \"成语\", \"meaning\": \"简短含义\", \"pinyin\": \"拼音\", \"explanation\": \"详细解释\"}},不要添加任何其他内容。" + + payload = { + "model": "deepseek-chat", + "messages": [ + {"role": "system", "content": "你是一个中文成语专家,精通成语接龙游戏。"}, + {"role": "user", "content": prompt} + ], + "temperature": 0.3, + "response_format": {"type": "json_object"} + } + + response = requests.post(API_BASE_URL, headers=headers, json=payload, timeout=30.0) + response.raise_for_status() + data = response.json() + + # 解析API响应 + content = data["choices"][0]["message"]["content"] + return IdiomResponse.model_validate_json(content) + +# 检查用户输入的成语是否有效 +def validate_user_idiom(user_idiom: str, last_idiom: str) -> bool: + if len(user_idiom) != 4: + return False + + if last_idiom and user_idiom[0] != last_idiom[-1]: + return False + + return True + +# 重置游戏 +def reset_game(): + st.session_state.game_state = GameState() + +# 主应用界面 +st.title("🎯 成语接龙游戏") +st.write("与AI一起玩成语接龙,看看谁的成语储备更丰富!") + +# 游戏控制区 +col1, col2 = st.columns(2) +with col1: + if st.button("🎮 开始新游戏", type="primary"): + reset_game() + +with col2: + if st.button("📊 查看规则"): + st.info(""" + **成语接龙规则:** + 1. 游戏开始时,AI会先出一个成语 + 2. 玩家需要接一个以AI成语最后一个字开头的四字成语 + 3. AI会继续接玩家的成语 + 4. 如此循环,直到一方无法接出成语 + 5. 每成功接一个成语,得分+1 + """) + +# 游戏状态显示 +st.markdown("---") + +# 初始状态:AI先出成语 +if not st.session_state.game_state.current_idiom and not st.session_state.game_state.is_game_over: + with st.spinner("🤖 AI正在思考第一个成语..."): + try: + # 使用一个默认的起始成语让AI开始 + initial_idiom = get_next_idiom("开门见山") + st.session_state.game_state.current_idiom = initial_idiom.idiom + st.session_state.game_state.history.append({ + "role": "ai", + "idiom": initial_idiom.idiom, + "meaning": initial_idiom.meaning, + "pinyin": initial_idiom.pinyin, + "explanation": initial_idiom.explanation + }) + except Exception as e: + st.error(f"🤖 AI出错了:{str(e)}") + st.session_state.game_state.is_game_over = True + +# 游戏进行中 +if st.session_state.game_state.current_idiom and not st.session_state.game_state.is_game_over: + # 显示当前成语 + current = st.session_state.game_state.history[-1] + st.markdown(f"\n**🤖 AI 出的成语:** {current['idiom']}") + st.caption(f"拼音:{current['pinyin']} | 含义:{current['meaning']}") + + with st.expander("查看详细解释"): + st.write(current['explanation']) + + # 用户输入区 + user_input = st.text_input( + f"\n请接一个以'**{st.session_state.game_state.current_idiom[-1]}**'开头的四字成语:", + placeholder="例如:xxx", + key="user_idiom_input" + ) + + # 提交按钮 + if st.button("✅ 提交", key="submit_btn"): + if validate_user_idiom(user_input, st.session_state.game_state.current_idiom): + # 验证通过,记录用户的成语 + st.session_state.game_state.history.append({ + "role": "user", + "idiom": user_input, + "meaning": "", + "pinyin": "" + }) + st.session_state.game_state.score += 1 + + # AI 接成语 + with st.spinner("🤖 AI正在思考..."): + try: + next_idiom = get_next_idiom(user_input) + st.session_state.game_state.current_idiom = next_idiom.idiom + st.session_state.game_state.history.append({ + "role": "ai", + "idiom": next_idiom.idiom, + "meaning": next_idiom.meaning, + "pinyin": next_idiom.pinyin, + "explanation": next_idiom.explanation + }) + st.rerun() + except Exception as e: + st.success("🎉 AI接不上来,你赢了!") + st.session_state.game_state.is_game_over = True + else: + st.error("❌ 无效的成语!请确保是四字成语且第一个字与上一个成语的最后一个字相同。") + +# 游戏结束 +if st.session_state.game_state.is_game_over: + st.markdown("\n---") + st.success(f"🎉 游戏结束!你的得分:{st.session_state.game_state.score}") + + # 显示历史记录 + if st.session_state.game_state.history: + st.markdown("\n📝 **游戏历史:**") + for i, item in enumerate(st.session_state.game_state.history): + role = "🤖 AI" if item["role"] == "ai" else "👤 你" + st.write(f"{i+1}. {role}: {item['idiom']}") + +# 侧边栏信息 +with st.sidebar: + st.markdown("## 📊 游戏统计") + st.write(f"**当前得分:** {st.session_state.game_state.score}") + st.write(f"**接龙次数:** {len(st.session_state.game_state.history)}") + + if st.session_state.game_state.history: + st.markdown("\n## 📖 最近成语") + for i, item in enumerate(reversed(st.session_state.game_state.history[-5:])): + role = "🤖" if item["role"] == "ai" else "👤" + st.write(f"{role} {item['idiom']}") + + st.markdown("\n## 📌 提示") + st.write("- 确保输入的是标准四字成语") + st.write("- 注意汉字的正确写法") + st.write("- 可以使用同音字进行接龙") diff --git a/idiom_game_qa.py b/idiom_game_qa.py new file mode 100644 index 0000000..cc05af5 --- /dev/null +++ b/idiom_game_qa.py @@ -0,0 +1,256 @@ +import streamlit as st +import requests +from pydantic import BaseModel, Field +from dotenv import load_dotenv +import os +import json + +# 加载环境变量 +load_dotenv() + +# 设置API密钥 +DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "sk-b04b9134730c4375a5789e2e00978ee0") +API_BASE_URL = "https://api.deepseek.com/v1/chat/completions" + +# 定义Pydantic数据模型 +class IdiomResponse(BaseModel): + idiom: str = Field(..., description="成语") + meaning: str = Field(..., description="成语含义") + pinyin: str = Field(..., description="拼音") + explanation: str = Field(..., description="详细解释") + +class GameState(BaseModel): + current_idiom: str = Field(default="", description="当前成语") + history: list = Field(default_factory=list, description="成语接龙历史") + score: int = Field(default=0, description="得分") + is_game_over: bool = Field(default=False, description="游戏是否结束") + +# 初始化游戏状态 +if "game_state" not in st.session_state: + st.session_state.game_state = GameState() + +# 调用DeepSeek API生成下一个成语 +def get_next_idiom(last_idiom: str) -> IdiomResponse: + headers = { + "Authorization": f"Bearer {DEEPSEEK_API_KEY}", + "Content-Type": "application/json" + } + + # 构建提示词 + prompt = f"请你作为一个成语接龙游戏的助手,根据最后一个成语的最后一个字,生成下一个成语。上一个成语是:{last_idiom}。请严格按照JSON格式返回:{{\"idiom\": \"成语\", \"meaning\": \"简短含义\", \"pinyin\": \"拼音\", \"explanation\": \"详细解释\"}},不要添加任何其他内容。" + + payload = { + "model": "deepseek-chat", + "messages": [ + {"role": "system", "content": "你是一个中文成语专家,精通成语接龙游戏。"}, + {"role": "user", "content": prompt} + ], + "temperature": 0.3, + "response_format": {"type": "json_object"} + } + + response = requests.post(API_BASE_URL, headers=headers, json=payload, timeout=30.0) + response.raise_for_status() + data = response.json() + + # 解析API响应 + content = data["choices"][0]["message"]["content"] + return IdiomResponse.model_validate_json(content) + +# 检查用户输入的成语是否有效 +def validate_user_idiom(user_idiom: str, last_idiom: str) -> bool: + if len(user_idiom) != 4: + return False + + if last_idiom and user_idiom[0] != last_idiom[-1]: + return False + + return True + +# 检测用户输入是否为问题 +def is_question(user_input: str) -> bool: + question_markers = ['?', '?', '吗', '呢', '吧', '什么', '怎么', '为什么', '何时', '何地', '谁', '哪个'] + return any(marker in user_input for marker in question_markers) + +# 处理用户问题 +def handle_question(user_question: str) -> str: + headers = { + "Authorization": f"Bearer {DEEPSEEK_API_KEY}", + "Content-Type": "application/json" + } + + payload = { + "model": "deepseek-chat", + "messages": [ + {"role": "system", "content": "你是一个中文成语专家,同时也是一个知识渊博的助手。你可以进行成语接龙游戏,也可以回答各种问题。"}, + {"role": "user", "content": user_question} + ], + "temperature": 0.7 + } + + response = requests.post(API_BASE_URL, headers=headers, json=payload, timeout=30.0) + response.raise_for_status() + data = response.json() + + return data["choices"][0]["message"]["content"] + +# 重置游戏 +def reset_game(): + st.session_state.game_state = GameState() + +# 主应用界面 +st.title("🎯 成语接龙游戏") +st.write("与AI一起玩成语接龙,看看谁的成语储备更丰富!你也可以随时向AI提问。") + +# 游戏控制区 +col1, col2 = st.columns(2) +with col1: + if st.button("🎮 开始新游戏", type="primary"): + reset_game() + +with col2: + if st.button("📊 查看规则"): + st.info(""" + **成语接龙规则:** + 1. 游戏开始时,AI会先出一个成语 + 2. 玩家需要接一个以AI成语最后一个字开头的四字成语 + 3. AI会继续接玩家的成语 + 4. 如此循环,直到一方无法接出成语 + 5. 每成功接一个成语,得分+1 + + **提问功能:** + - 随时可以向AI提问,输入包含问号或疑问词的句子 + - AI会回答你的问题,然后继续游戏 + """) + +# 游戏状态显示 +st.markdown("---") + +# 初始状态:AI先出成语 +if not st.session_state.game_state.current_idiom and not st.session_state.game_state.is_game_over: + with st.spinner("🤖 AI正在思考第一个成语..."): + try: + # 使用一个默认的起始成语让AI开始 + initial_idiom = get_next_idiom("开门见山") + st.session_state.game_state.current_idiom = initial_idiom.idiom + st.session_state.game_state.history.append({ + "role": "ai", + "type": "idiom", + "idiom": initial_idiom.idiom, + "meaning": initial_idiom.meaning, + "pinyin": initial_idiom.pinyin, + "explanation": initial_idiom.explanation + }) + except Exception as e: + st.error(f"🤖 AI出错了:{str(e)}") + st.session_state.game_state.is_game_over = True + +# 游戏进行中 +if st.session_state.game_state.current_idiom and not st.session_state.game_state.is_game_over: + # 显示当前成语 + current = st.session_state.game_state.history[-1] + if current["type"] == "idiom": + st.markdown(f"\n**🤖 AI 出的成语:** {current['idiom']}") + st.caption(f"拼音:{current['pinyin']} | 含义:{current['meaning']}") + + with st.expander("查看详细解释"): + st.write(current['explanation']) + elif current["type"] == "answer": + st.markdown(f"\n**🤖 AI 的回答:** {current['content']}") + + # 用户输入区 + user_input = st.text_input( + f"\n请接一个以'**{st.session_state.game_state.current_idiom[-1]}**'开头的四字成语,或向AI提问:", + placeholder="例如:xxx 或 你好吗?", + key="user_idiom_input" + ) + + # 提交按钮 + if st.button("✅ 提交", key="submit_btn"): + # 检测是否为问题 + if is_question(user_input): + # 处理问题 + with st.spinner("🤖 AI正在思考..."): + try: + answer = handle_question(user_input) + st.session_state.game_state.history.append({ + "role": "user", + "type": "question", + "content": user_input + }) + st.session_state.game_state.history.append({ + "role": "ai", + "type": "answer", + "content": answer + }) + st.rerun() + except Exception as e: + st.error(f"🤖 AI出错了:{str(e)}") + else: + # 处理成语接龙 + if validate_user_idiom(user_input, st.session_state.game_state.current_idiom): + # 验证通过,记录用户的成语 + st.session_state.game_state.history.append({ + "role": "user", + "type": "idiom", + "idiom": user_input, + "meaning": "", + "pinyin": "" + }) + st.session_state.game_state.score += 1 + + # AI 接成语 + with st.spinner("🤖 AI正在思考..."): + try: + next_idiom = get_next_idiom(user_input) + st.session_state.game_state.current_idiom = next_idiom.idiom + st.session_state.game_state.history.append({ + "role": "ai", + "type": "idiom", + "idiom": next_idiom.idiom, + "meaning": next_idiom.meaning, + "pinyin": next_idiom.pinyin, + "explanation": next_idiom.explanation + }) + st.rerun() + except Exception as e: + st.success("🎉 AI接不上来,你赢了!") + st.session_state.game_state.is_game_over = True + else: + st.error("❌ 无效的成语!请确保是四字成语且第一个字与上一个成语的最后一个字相同。") + +# 游戏结束 +if st.session_state.game_state.is_game_over: + st.markdown("\n---") + st.success(f"🎉 游戏结束!你的得分:{st.session_state.game_state.score}") + + # 显示历史记录 + if st.session_state.game_state.history: + st.markdown("\n📝 **游戏历史:**") + for i, item in enumerate(st.session_state.game_state.history): + role = "🤖 AI" if item["role"] == "ai" else "👤 你" + if item["type"] == "idiom": + st.write(f"{i+1}. {role}: {item['idiom']}") + else: + st.write(f"{i+1}. {role}: {item['content']}") + +# 侧边栏信息 +with st.sidebar: + st.markdown("## 📊 游戏统计") + st.write(f"**当前得分:** {st.session_state.game_state.score}") + st.write(f"**接龙次数:** {len([item for item in st.session_state.game_state.history if item.get('type') == 'idiom'])}") + + if st.session_state.game_state.history: + st.markdown("\n## 📖 最近互动") + for i, item in enumerate(reversed(st.session_state.game_state.history[-5:])): + role = "🤖" if item["role"] == "ai" else "👤" + if item["type"] == "idiom": + st.write(f"{role} {item['idiom']}") + else: + st.write(f"{role} {item['content'][:30]}...") + + st.markdown("\n## 📌 提示") + st.write("- 确保输入的是标准四字成语") + st.write("- 注意汉字的正确写法") + st.write("- 可以使用同音字进行接龙") + st.write("- 随时可以向AI提问") \ No newline at end of file diff --git a/my_ai_app.egg-info/PKG-INFO b/my_ai_app.egg-info/PKG-INFO new file mode 100644 index 0000000..56094f7 --- /dev/null +++ b/my_ai_app.egg-info/PKG-INFO @@ -0,0 +1,70 @@ +Metadata-Version: 2.4 +Name: my-ai-app +Version: 0.1.0 +Summary: AI-powered Gomoku game with web interface +Requires-Python: >=3.12 +Description-Content-Type: text/markdown + +| 姓名 | 学号 | 角色 | 职责 | +|--------|------------|----------|--------------------------| +| 周诗晗 | 2411020112 | 组长 | 代码测试与 Bug、文档撰写 | +| 肖舒妍 | 2411020206 | 组员 | 界面设计、Prompt 编写 | + +2.2 项目简介 & 运行指南 +# 简介 +本成语接龙互动系统聚焦青年群体成语积累薄弱、传统成语接龙游戏参与门槛高且趣味性不足的痛点,通过 AI 辅助谐音匹配、实时提示及轻量化交互设计,以游戏化形式降低成语学习门槛,实现传统文化的趣味传承。 +# 如何运行 +安装依赖: uv sync +配置 Key: 复制项目根目录下的.env.example文件并重命名为.env,在文件内填入 DeepSeek API Key(用于 AI 成语生成功能) +启动项目: uv run streamlit run app.py +访问游戏:启动成功后,在浏览器打开终端输出的本地链接(默认:http://localhost:8501)即可进入成语接龙界面 + +《成语接龙》课程设计开发心得 📝 +本次课程设计以“成语接龙互动系统”为主题,核心出发点是解决 “青年群体成语积累薄弱” 与 “传统成语游戏趣味性不足” 两大问题。我们希望借助互动形式传承传统文化,同时通过 AI 辅助功能 降低参与门槛,让用户在轻量化游戏中熟悉并掌握成语。 + +🤖 AI 协作体验 +初次借助 AI 辅助开发时,其快速落地能力令人印象深刻: + +仅通过描述 “实现基于汉字谐音的成语匹配逻辑”,AI 即可生成拼音映射表与同音校验函数,大幅缩短基础功能开发周期。 + +最有效的 Prompt 示例: + +“结合 Streamlit 框架,实现包含中国风界面、游戏状态管理、AI 成语生成的完整交互系统。” +该指令不仅覆盖了前端样式(如渐变卡片、动画效果),还嵌入了游戏计时器、历史记录管理等核心逻辑,直接实现了 “功能完整性” 与 “用户体验” 的结合。 + +⚠️ 遇到的问题与优化 +在 AI 协作过程中,我们也遇到一些实际问题: + +成语重复问题 + +初始 Prompt 未限定 “过滤已使用成语”,导致 AI 返回结果频繁重复。 + +解决方案:补充 “排除历史成语列表” 约束条件后得以修复。 + +成语库匹配问题 + +AI 生成的新成语与本地成语库不匹配。 + +解决方案:通过 “自动将有效新成语补充至本地库” 逻辑进行修复。 + +这些经历让我意识到:AI 输出的内容仍需结合实际场景进行针对性校验与调整。 + +💡 核心感悟 +若完全脱离 AI 辅助,我们虽可独立完成基础接龙逻辑,但 Streamlit 前端美化、API 接口对接 等环节的开发周期将显著延长,且界面交互的精细化程度会大打折扣。 + +这让我更加明确: + +AI 是提升开发效率的工具,但开发者的核心竞争力仍在于 “需求拆解能力” 与 “场景化决策能力”。 + +例如:AI 不会主动考虑 “用户卡壳时需要提示功能”,但结合实际使用场景,我们主动增设了 “提示按钮”,这正是从用户需求出发的设计。 + +✅ 总结 +本次开发让我们清晰了 AI 时代的开发思路: +以用户需求为核心 → 拆解功能模块与体验细节 → 借助 AI 实现代码落地 → 自身聚焦于需求合理性与流程完整性的把控。 + +🌟 让传统文化在趣味互动中传承,让技术为体验赋能。 + +## 项目状态 +- ✅ 代码修复完成 +- ✅ 应用运行正常 +- ✅ 敏感信息已保护 diff --git a/my_ai_app.egg-info/SOURCES.txt b/my_ai_app.egg-info/SOURCES.txt new file mode 100644 index 0000000..c8778ce --- /dev/null +++ b/my_ai_app.egg-info/SOURCES.txt @@ -0,0 +1,7 @@ +README.md +idiom_solitaire.py +pyproject.toml +my_ai_app.egg-info/PKG-INFO +my_ai_app.egg-info/SOURCES.txt +my_ai_app.egg-info/dependency_links.txt +my_ai_app.egg-info/top_level.txt \ No newline at end of file diff --git a/my_ai_app.egg-info/dependency_links.txt b/my_ai_app.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/my_ai_app.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/my_ai_app.egg-info/top_level.txt b/my_ai_app.egg-info/top_level.txt new file mode 100644 index 0000000..df6e990 --- /dev/null +++ b/my_ai_app.egg-info/top_level.txt @@ -0,0 +1 @@ +idiom_solitaire diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..fa3afb9 --- /dev/null +++ b/uv.lock @@ -0,0 +1,8 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "my-ai-app" +version = "0.1.0" +source = { editable = "." }