- 添加核心游戏逻辑文件structual.py,实现海龟汤游戏基本功能 - 创建HTML前端界面turtle_soup_html.html,提供交互式游戏体验 - 编写详细README.md文档,包含项目介绍、运行指南和贡献说明 - 添加项目配置文件pyproject.toml和环境变量示例 - 实现游戏数据存储功能,使用JSON保存游戏记录 - 包含15个预设海龟汤题目和关键词匹配系统
445 lines
20 KiB
Python
445 lines
20 KiB
Python
import os
|
||
import json
|
||
import time
|
||
from datetime import datetime
|
||
from openai import OpenAI
|
||
from dotenv import load_dotenv
|
||
import traceback
|
||
|
||
# 加载环境变量
|
||
load_dotenv()
|
||
|
||
# 初始化OpenAI客户端
|
||
try:
|
||
client = OpenAI(
|
||
api_key=os.getenv("DEEPSEEK_API_KEY"),
|
||
base_url="https://api.deepseek.com"
|
||
)
|
||
print(f"API客户端初始化成功,使用的API Key: {os.getenv('DEEPSEEK_API_KEY')[:10]}...")
|
||
except Exception as e:
|
||
print(f"API客户端初始化失败: {e}")
|
||
traceback.print_exc()
|
||
exit(1)
|
||
|
||
# 游戏数据存储文件
|
||
GAME_DATA_FILE = "turtle_soup_data.json"
|
||
|
||
# 确保数据文件存在
|
||
def ensure_data_file_exists():
|
||
if not os.path.exists(GAME_DATA_FILE):
|
||
with open(GAME_DATA_FILE, "w", encoding="utf-8") as f:
|
||
json.dump({"games": [], "statistics": {"total_games": 0, "total_questions": 0, "win_rate": 0.0}}, f, ensure_ascii=False, indent=2)
|
||
|
||
# 加载游戏数据
|
||
def load_game_data():
|
||
ensure_data_file_exists()
|
||
with open(GAME_DATA_FILE, "r", encoding="utf-8") as f:
|
||
return json.load(f)
|
||
|
||
# 保存游戏数据
|
||
def save_game_data(data):
|
||
with open(GAME_DATA_FILE, "w", encoding="utf-8") as f:
|
||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||
|
||
class TurtleSoupGame:
|
||
"""海龟汤游戏类"""
|
||
|
||
def __init__(self):
|
||
self.story = None
|
||
self.solution = None
|
||
self.game_over = False
|
||
self.session_id = str(int(time.time()))
|
||
self.game_start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||
self.game_end_time = None
|
||
self.session_data = {
|
||
"session_id": self.session_id,
|
||
"start_time": self.game_start_time,
|
||
"end_time": None,
|
||
"story": None,
|
||
"solution": None,
|
||
"game_type": "",
|
||
"is_custom": False,
|
||
"questions": [],
|
||
"guesses": [],
|
||
"hints": [],
|
||
"result": "未完成"
|
||
}
|
||
|
||
def generate_story(self, story_type="随机"):
|
||
"""生成海龟汤题目"""
|
||
print("正在生成海龟汤题目...")
|
||
|
||
try:
|
||
response = client.chat.completions.create(
|
||
model="deepseek-chat",
|
||
messages=[
|
||
{
|
||
"role": "system",
|
||
"content": "你是一个海龟汤游戏的出题者。请生成一个有趣且有挑战性的海龟汤题目,包含两个部分:1) 简短的奇怪情境描述(2-3句话);2) 完整的故事解释(包含事件的前因后果)。请确保情境足够引人入胜,让玩家有探索的欲望。"
|
||
},
|
||
{
|
||
"role": "user",
|
||
"content": "生成一个海龟汤题目,用中文回答。格式要求:\n情境:[情境描述]\n答案:[完整解释]"
|
||
}
|
||
],
|
||
temperature=1.3,
|
||
timeout=30 # 添加30秒超时设置
|
||
)
|
||
|
||
print("API调用成功,正在解析结果...")
|
||
result = response.choices[0].message.content.strip()
|
||
print(f"\nAPI返回的原始内容: {result[:100]}...\n")
|
||
|
||
# 解析生成的内容
|
||
if "情境:" in result and "答案:" in result:
|
||
scenario_part = result.split("情境:")[1].split("答案:")[0].strip()
|
||
solution_part = result.split("答案:")[1].strip()
|
||
|
||
self.story = scenario_part
|
||
self.solution = solution_part
|
||
|
||
# 更新会话数据
|
||
self.session_data["story"] = self.story
|
||
self.session_data["solution"] = self.solution
|
||
self.session_data["game_type"] = story_type
|
||
self.session_data["is_custom"] = False
|
||
|
||
print("✅ 海龟汤题目生成成功 ✅")
|
||
print(f"\n🥣 汤面:{self.story}\n")
|
||
print("🔍 你可以通过提问来猜测故事的真相,游戏中我只会回答'是'、'否'或'无关'。")
|
||
print("📋 输入'答案'查看完整故事,输入'新题'生成新的题目,输入'结束'退出游戏。\n")
|
||
else:
|
||
print("⚠️ 生成的内容格式不符合要求,使用默认题目...")
|
||
# 使用默认题目
|
||
self.story = "一个男人走进一家餐厅,点了一份海龟汤。喝了一口后,他放下汤匙,默默地付了钱离开了餐厅。"
|
||
self.solution = "这个男人曾经遭遇海难,和同伴被困在荒岛上。为了生存,同伴们煮了海龟汤给他喝,并告诉他这是他妻子的肉。获救后,他在餐厅喝到真正的海龟汤,才知道自己被骗了,同伴们牺牲了自己来救他。"
|
||
|
||
# 更新会话数据
|
||
self.session_data["story"] = self.story
|
||
self.session_data["solution"] = self.solution
|
||
self.session_data["game_type"] = story_type
|
||
self.session_data["is_custom"] = False
|
||
|
||
print("✅ 海龟汤题目生成成功 ✅")
|
||
print(f"\n🥣 汤面:{self.story}\n")
|
||
print("🔍 你可以通过提问来猜测故事的真相,游戏中我只会回答'是'、'否'或'无关'。")
|
||
print("📋 输入'答案'查看完整故事,输入'新题'生成新的题目,输入'结束'退出游戏。\n")
|
||
except Exception as e:
|
||
print(f"❌ 生成题目时出错: {e}")
|
||
print("❌ 详细错误信息:")
|
||
traceback.print_exc()
|
||
# 使用默认题目作为备选
|
||
print("\n⚠️ 使用默认题目...")
|
||
self.story = "一个男人走进一家餐厅,点了一份海龟汤。喝了一口后,他放下汤匙,默默地付了钱离开了餐厅。"
|
||
self.solution = "这个男人曾经遭遇海难,和同伴被困在荒岛上。为了生存,同伴们煮了海龟汤给他喝,并告诉他这是他妻子的肉。获救后,他在餐厅喝到真正的海龟汤,才知道自己被骗了,同伴们牺牲了自己来救他。"
|
||
|
||
# 更新会话数据
|
||
self.session_data["story"] = self.story
|
||
self.session_data["solution"] = self.solution
|
||
self.session_data["game_type"] = story_type
|
||
self.session_data["is_custom"] = False
|
||
|
||
print("✅ 海龟汤题目生成成功 ✅")
|
||
print(f"\n🥣 汤面:{self.story}\n")
|
||
print("🔍 你可以通过提问来猜测故事的真相,游戏中我只会回答'是'、'否'或'无关'。")
|
||
print("📋 输入'答案'查看完整故事,输入'新题'生成新的题目,输入'结束'退出游戏。\n")
|
||
|
||
def guess_solution(self, guess):
|
||
"""处理玩家的答案猜测"""
|
||
print(f"\n🎯 你的猜测:{guess}")
|
||
print("🤔 正在评估你的猜测...")
|
||
|
||
# 记录猜测
|
||
self.session_data["guesses"].append({
|
||
"guess": guess,
|
||
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||
})
|
||
|
||
try:
|
||
# 使用AI评估玩家的猜测
|
||
response = client.chat.completions.create(
|
||
model="deepseek-chat",
|
||
messages=[
|
||
{
|
||
"role": "system",
|
||
"content": f"你是一个海龟汤游戏的裁判。以下是海龟汤的完整汤底:{self.solution}\n\n现在玩家提出了一个猜测,请你根据汤底内容,评估玩家的猜测是否正确。\n\n评估标准:\n1. 如果玩家的猜测准确包含了汤底的核心信息(主要人物、关键事件、因果关系),回答'正确'\n2. 如果玩家的猜测部分正确,但缺少关键信息,回答'部分正确'\n3. 如果玩家的猜测完全错误,回答'错误'\n\n请只返回'正确'、'部分正确'或'错误',不要添加任何其他解释。"
|
||
},
|
||
{
|
||
"role": "user",
|
||
"content": guess
|
||
}
|
||
],
|
||
temperature=0.3
|
||
)
|
||
|
||
result = response.choices[0].message.content.strip()
|
||
|
||
# 根据评估结果显示反馈
|
||
if result == "正确":
|
||
print("\n🎉 恭喜你!你的猜测完全正确!")
|
||
print(f"🔓 === 完整答案(汤底)=== 🔓")
|
||
print(f"🍲 汤底:{self.solution}\n")
|
||
self.game_over = True
|
||
self.session_data["result"] = "胜利"
|
||
self.save_game_session()
|
||
elif result == "部分正确":
|
||
print("\n👍 你的猜测部分正确,但还有一些关键信息没有猜出来。")
|
||
print("🔍 继续提问,获取更多线索吧!\n")
|
||
else:
|
||
print("\n👎 你的猜测不正确,再试一次吧!")
|
||
print("🔍 继续提问,获取更多线索吧!\n")
|
||
|
||
except Exception as e:
|
||
print(f"\n❌ 评估猜测时出错: {e}")
|
||
traceback.print_exc()
|
||
print("🔍 继续提问,获取更多线索吧!\n")
|
||
|
||
def get_hint(self):
|
||
"""生成游戏提示"""
|
||
print("\n💡 正在生成提示...")
|
||
|
||
try:
|
||
# 使用AI从汤底中生成提示
|
||
response = client.chat.completions.create(
|
||
model="deepseek-chat",
|
||
messages=[
|
||
{
|
||
"role": "system",
|
||
"content": f"你是一个海龟汤游戏的裁判。以下是海龟汤的完整汤底:{self.solution}\n\n请从汤底中提取1-2条关键线索,这些线索应该能够帮助玩家更好地理解故事,但又不会直接泄露答案。\n\n线索要求:\n1. 只提供事实性信息,不包含解释\n2. 使用简洁的语言\n3. 不要提到汤底的最终结局\n4. 每条线索以'提示X:'开头"
|
||
},
|
||
{
|
||
"role": "user",
|
||
"content": "请生成1-2条线索"
|
||
}
|
||
],
|
||
temperature=0.5
|
||
)
|
||
|
||
hint = response.choices[0].message.content.strip()
|
||
print(f"\n🎯 提示:\n{hint}\n")
|
||
|
||
# 记录提示
|
||
self.session_data["hints"].append({
|
||
"hint": hint,
|
||
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||
})
|
||
|
||
except Exception as e:
|
||
print(f"\n❌ 生成提示时出错: {e}")
|
||
traceback.print_exc()
|
||
# 如果AI生成失败,提供一个通用提示
|
||
generic_hint = "提示1:关注汤面中的异常细节\n提示2:考虑人物之间的关系和动机"
|
||
print(f"\n🎯 提示:\n{generic_hint}\n")
|
||
|
||
# 记录提示
|
||
self.session_data["hints"].append({
|
||
"hint": generic_hint,
|
||
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||
})
|
||
|
||
def answer_question(self, question):
|
||
"""回答用户的问题"""
|
||
# 检查是否是查看答案命令
|
||
if question.strip().lower() in ["答案", "查看答案"]:
|
||
print(f"\n🔓 === 完整答案(汤底)=== 🔓")
|
||
print(f"🍲 汤底:{self.solution}\n")
|
||
self.game_over = True
|
||
self.session_data["result"] = "查看答案"
|
||
self.save_game_session()
|
||
return
|
||
|
||
# 检查是否是结束游戏命令
|
||
if question.strip().lower() in ["结束", "退出"]:
|
||
print("\n👋 游戏已结束,感谢参与!")
|
||
self.game_over = True
|
||
self.session_data["result"] = "中途退出"
|
||
self.save_game_session()
|
||
return
|
||
|
||
# 检查是否是生成新题命令
|
||
if question.strip().lower() in ["新题", "重新开始", "生成新题", "换一个"]:
|
||
print("\n🔄 正在生成新的海龟汤题目...\n")
|
||
self.save_game_session() # 保存当前游戏
|
||
self.__init__() # 重置游戏状态
|
||
self.generate_story()
|
||
return
|
||
|
||
# 检查是否是创建自定义题目命令
|
||
if question.strip().lower() in ["自定义", "创建题目", "自己出题"]:
|
||
self.create_custom_story()
|
||
return
|
||
|
||
# 检查是否是获取提示命令
|
||
if question.strip().lower() in ["提示", "线索", "给个提示", "给点线索"]:
|
||
self.get_hint()
|
||
return
|
||
|
||
# 检查是否是猜测答案命令
|
||
if question.strip().lower().startswith("猜测") or question.strip().lower().startswith("我猜"):
|
||
# 提取猜测内容(去除命令前缀)
|
||
guess = question.strip()[2:].strip()
|
||
if guess:
|
||
self.guess_solution(guess)
|
||
else:
|
||
print("\n⚠️ 请输入你的猜测内容,例如:猜测 小明是个摄影师\n")
|
||
return
|
||
|
||
# 记录问题
|
||
self.session_data["questions"].append({
|
||
"question": question,
|
||
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||
})
|
||
|
||
try:
|
||
# 使用AI回答问题
|
||
response = client.chat.completions.create(
|
||
model="deepseek-chat",
|
||
messages=[
|
||
{
|
||
"role": "system",
|
||
"content": f"你是一个海龟汤游戏的裁判。以下是海龟汤的完整故事:{self.solution}\n\n现在玩家提出了一个问题,请你根据故事内容,只能用'是'、'否'或'无关'来回答。如果问题直接或间接涉及到故事的关键信息,回答'是'或'否';如果问题与故事完全无关,回答'无关'。"
|
||
},
|
||
{
|
||
"role": "user",
|
||
"content": question
|
||
}
|
||
],
|
||
temperature=0.3 # 降低温度,使回答更准确
|
||
)
|
||
|
||
answer = response.choices[0].message.content.strip()
|
||
|
||
# 确保回答符合要求
|
||
if answer not in ["是", "否", "无关"]:
|
||
# 如果回答不符合要求,尝试解析
|
||
if "是" in answer:
|
||
answer = "是"
|
||
elif "否" in answer:
|
||
answer = "否"
|
||
else:
|
||
answer = "无关"
|
||
|
||
# 记录回答
|
||
self.session_data["questions"][-1]["answer"] = answer
|
||
|
||
return answer
|
||
except Exception as e:
|
||
print(f"回答问题时出错: {e}")
|
||
traceback.print_exc()
|
||
return "无关"
|
||
|
||
def save_game_session(self):
|
||
"""保存游戏会话数据到JSON文件"""
|
||
self.game_end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||
self.session_data["end_time"] = self.game_end_time
|
||
|
||
# 加载现有数据
|
||
ensure_data_file_exists()
|
||
game_data = load_game_data()
|
||
|
||
# 添加当前游戏数据
|
||
game_data["games"].append(self.session_data)
|
||
|
||
# 更新统计信息
|
||
total_games = len(game_data["games"])
|
||
won_games = sum(1 for game in game_data["games"] if game["result"] == "胜利")
|
||
total_questions = sum(len(game["questions"]) for game in game_data["games"])
|
||
|
||
game_data["statistics"] = {
|
||
"total_games": total_games,
|
||
"total_questions": total_questions,
|
||
"win_rate": won_games / total_games if total_games > 0 else 0.0
|
||
}
|
||
|
||
# 保存数据
|
||
save_game_data(game_data)
|
||
print("📊 游戏数据已保存!")
|
||
|
||
def create_custom_story(self):
|
||
"""创建自定义题目"""
|
||
print("\n📝 创建自定义海龟汤题目")
|
||
|
||
try:
|
||
# 获取用户输入的汤面
|
||
story = input("请输入汤面(奇怪的情境): ")
|
||
if not story.strip():
|
||
print("⚠️ 汤面不能为空,请重新输入!\n")
|
||
return
|
||
|
||
# 获取用户输入的汤底
|
||
solution = input("请输入汤底(完整解释): ")
|
||
if not solution.strip():
|
||
print("⚠️ 汤底不能为空,请重新输入!\n")
|
||
return
|
||
|
||
self.story = story.strip()
|
||
self.solution = solution.strip()
|
||
|
||
# 更新会话数据
|
||
self.session_data["story"] = self.story
|
||
self.session_data["solution"] = self.solution
|
||
self.session_data["game_type"] = "自定义"
|
||
self.session_data["is_custom"] = True
|
||
|
||
print("\n✅ 自定义题目创建成功!")
|
||
print(f"🥣 汤面:{self.story}\n")
|
||
print("🔍 你可以通过提问来猜测故事的真相,游戏中我只会回答'是'、'否'或'无关'。")
|
||
print("📋 输入'答案'查看完整故事,输入'新题'生成新的题目,输入'结束'退出游戏。\n")
|
||
|
||
except Exception as e:
|
||
print(f"\n❌ 创建自定义题目时出错: {e}")
|
||
traceback.print_exc()
|
||
|
||
def play(self):
|
||
"""开始游戏"""
|
||
print("📜 🐢 ================== 海龟汤游戏 ================== 🐢 📜")
|
||
print("🔍 海龟汤是一种情境猜谜游戏,我会给出一个奇怪的情境,你可以通过提问来猜测故事的真相。")
|
||
print("💬 我只会回答'是'、'否'或'无关'。")
|
||
print("📋 输入'答案'查看完整故事,输入'新题'生成新的题目,输入'自定义'创建自己的题目,")
|
||
print("📋 输入'提示'获取线索,输入'猜测 内容'尝试猜测答案,输入'结束'退出游戏。\n")
|
||
|
||
# 确保数据文件存在
|
||
ensure_data_file_exists()
|
||
|
||
# 游戏模式选择
|
||
while True:
|
||
print("🎮 游戏模式选择:")
|
||
print("1. 使用AI生成的题目")
|
||
print("2. 自定义题目")
|
||
|
||
choice = input("请选择模式 (1/2): ")
|
||
if choice == "1":
|
||
# 生成题目
|
||
self.generate_story()
|
||
break
|
||
elif choice == "2":
|
||
# 创建自定义题目
|
||
self.create_custom_story()
|
||
break
|
||
else:
|
||
print("⚠️ 无效的选择,请重新输入!\n")
|
||
|
||
# 游戏主循环
|
||
while not self.game_over:
|
||
try:
|
||
question = input("👤 请提问:")
|
||
if not question.strip():
|
||
continue
|
||
|
||
answer = self.answer_question(question)
|
||
if not self.game_over: # 如果游戏还没结束且有回答
|
||
print(f"🤖 回答:{answer}\n")
|
||
except Exception as e:
|
||
print(f"❌ 游戏交互时出错: {e}")
|
||
traceback.print_exc()
|
||
continue
|
||
|
||
if __name__ == "__main__":
|
||
try:
|
||
game = TurtleSoupGame()
|
||
game.play()
|
||
except KeyboardInterrupt:
|
||
print("\n\n游戏已中断,感谢参与!")
|
||
except Exception as e:
|
||
print(f"\n游戏出错:{e}")
|
||
traceback.print_exc() |