feat: 添加HTML版本海龟汤游戏并优化Python版本
refactor: 重构Python版本代码,增加API错误处理和默认题目功能 docs: 更新README文档,添加开发心得和运行指南 chore: 添加项目依赖到requirements.txt
This commit is contained in:
parent
72bec8db47
commit
7144087596
15
README.md
15
README.md
@ -4,14 +4,19 @@
|
||||
|
||||
| 姓名 | 学号 | 主要贡献 (具体分工) |
|
||||
| ---- | ---- | ------------------ |
|
||||
| 张三 | 2023xxxx | (组长) 核心逻辑开发、Prompt 编写 |
|
||||
| 李四 | 2023xxxx | 前端界面设计、HTML版本开发 |
|
||||
| 王五 | 2023xxxx | 文档撰写、测试与Bug修复 |
|
||||
| 徐艺桐 | 2412131221 | (组长) 核心逻辑开发、代码编写、创意提供与整合|
|
||||
| 陈晓璐 | 2410511126 | 前端界面设计、HTML版本开发、撰写心得|
|
||||
| 尚小雅 | 2410511127 | 前期工作台配置、后期测试与Bug修复|
|
||||
|
||||
## 项目简介 & 运行指南
|
||||
|
||||
### 简介
|
||||
这是一个海龟汤游戏项目,提供了一个交互式平台,用户可以通过提问来猜测海龟汤题目的答案。项目包含HTML版本和Python版本,支持15个精心设计的海龟汤题目,包括代孕等主题,具有完善的关键词匹配系统和现代化的用户界面。
|
||||
这是一个海龟汤游戏项目,提供了一个交互式平台,用户可以通过提问来猜测海龟汤题目的答案。项目包含HTML版本海龟汤和python版本(需配置)也可以直接运行structaul.py游玩文字版,可AI编写也可以自定义海龟汤题目,包括许多主题,具有完善的关键词匹配系统和现代化的用户界面。
|
||||
|
||||
### 开发心得
|
||||
开发海龟汤推理游戏,解决传统游戏中主持人负担重、体验不均和故事有限等痛点。通过AI担任主持人,玩家可随时游戏,推理爱好者能获得无限新故事。
|
||||
首次用AI编码时颇为震撼,它快速生成出清晰的状态管理代码,但随后发现其缺乏业务理解,需不断调校提示词。最惊喜的是,在描述“提问匹配逻辑”后,AI不仅实现关键词匹配,还建议加入语义相似度计算。而最挫败的是实现进度保存时,AI反复给出简单方案,忽略隔离、兼容等系统设计问题,甚至有时无法理解我的要求,最终仍需自主重构。它无法前瞻性地考虑未来故事数据结构变更带来的“兼容性”挑战。它给出的往往是教科书式的、孤立的代码块,而非一个考虑了数据流、错误边界和长期演进的稳健系统设计。
|
||||
这段经历让我反思:AI时代,程序员的核心竞争力正转向问题定义、系统架构与调试能力。AI擅长执行明确指令,但无法替代人类对复杂系统的理解、对用户体验的洞察以及伦理责任的把握。我们不再是单纯的代码实现者,更是人机协作的架构师——善于拆解问题、设计稳健架构,并以创造力和判断力确保最终质量。技术工具会进化,但人类的深度思考与综合判断,始终无可替代。AI不会让程序员失业,但它会重新定义什么才是“优秀”的程序员。那些只会写基础代码的程序员可能会被替代。
|
||||
|
||||
### 如何运行
|
||||
|
||||
@ -51,7 +56,7 @@
|
||||
- 使用"查看答案"按钮直接查看完整答案
|
||||
|
||||
2. **题目特点**:
|
||||
- 包含15个多样化的海龟汤题目
|
||||
- 包含多个多样化的海龟汤题目
|
||||
- 涵盖代孕、犯罪、情感等多种主题
|
||||
- 每个题目都有详细的关键词匹配系统
|
||||
|
||||
|
||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
openai>=2.14.0
|
||||
python-dotenv>=1.2.1
|
||||
98
structual.py
98
structual.py
@ -10,16 +10,32 @@ import traceback
|
||||
load_dotenv()
|
||||
|
||||
# 初始化OpenAI客户端
|
||||
client = None
|
||||
try:
|
||||
api_key = os.getenv("DEEPSEEK_API_KEY")
|
||||
if not api_key or api_key.strip() == "your_api_key_here" or api_key.strip() == "*****":
|
||||
print("API密钥未配置,请在.env文件中设置DEEPSEEK_API_KEY")
|
||||
user_input = input("是否继续使用默认题目?(y/n): ")
|
||||
if user_input.lower() != "y":
|
||||
print("程序已退出。")
|
||||
exit(1)
|
||||
else:
|
||||
print("将使用默认题目继续游戏。")
|
||||
else:
|
||||
client = OpenAI(
|
||||
api_key=os.getenv("DEEPSEEK_API_KEY"),
|
||||
api_key=api_key,
|
||||
base_url="https://api.deepseek.com"
|
||||
)
|
||||
print(f"API客户端初始化成功,使用的API Key: {os.getenv('DEEPSEEK_API_KEY')[:10]}...")
|
||||
print(f"API客户端初始化成功,使用的API Key: {api_key[:10]}...")
|
||||
except Exception as e:
|
||||
print(f"API客户端初始化失败: {e}")
|
||||
traceback.print_exc()
|
||||
user_input = input("是否使用默认题目继续游戏?(y/n): ")
|
||||
if user_input.lower() != "y":
|
||||
print("程序已退出。")
|
||||
exit(1)
|
||||
else:
|
||||
print("将使用默认题目继续游戏。")
|
||||
|
||||
# 游戏数据存储文件
|
||||
GAME_DATA_FILE = "turtle_soup_data.json"
|
||||
@ -65,17 +81,49 @@ class TurtleSoupGame:
|
||||
"result": "未完成"
|
||||
}
|
||||
|
||||
def generate_story(self, story_type="随机"):
|
||||
def generate_story(self, theme=None, difficulty=None):
|
||||
"""生成海龟汤题目"""
|
||||
print("正在生成海龟汤题目...")
|
||||
|
||||
# 检查API客户端是否可用
|
||||
if client is None:
|
||||
print("⚠️ API客户端未初始化,将使用默认题目...")
|
||||
# 使用默认题目
|
||||
self.story = "一个男人走进一家餐厅,点了一份海龟汤。喝了一口后,他放下汤匙,默默地付了钱离开了餐厅。"
|
||||
self.solution = "这个男人曾经遭遇海难,和同伴被困在荒岛上。为了生存,同伴们煮了海龟汤给他喝,并告诉他这是他妻子的肉。获救后,他在餐厅喝到真正的海龟汤,才知道自己被骗了,同伴们牺牲了自己来救他。"
|
||||
|
||||
# 更新会话数据
|
||||
self.session_data["story"] = self.story
|
||||
self.session_data["solution"] = self.solution
|
||||
self.session_data["game_type"] = "默认"
|
||||
self.session_data["is_custom"] = False
|
||||
|
||||
print("✅ 海龟汤题目生成成功 ✅")
|
||||
print(f"\n🥣 汤面:{self.story}\n")
|
||||
print("🔍 你可以通过提问来猜测故事的真相,游戏中我只会回答'是'、'否'或'无关'。")
|
||||
print("📋 输入'答案'查看完整故事,输入'新题'生成新的题目,输入'结束'退出游戏。\n")
|
||||
return
|
||||
|
||||
retry_count = 0
|
||||
max_retries = 3
|
||||
|
||||
while retry_count < max_retries:
|
||||
try:
|
||||
# 构建系统提示词
|
||||
system_prompt = "你是一个海龟汤游戏的出题者。请生成一个有趣且有挑战性的海龟汤题目,包含两个部分:1) 简短的奇怪情境描述(2-3句话);2) 完整的故事解释(包含事件的前因后果)。请确保情境足够引人入胜,让玩家有探索的欲望。"
|
||||
|
||||
# 添加主题和难度设置
|
||||
if theme:
|
||||
system_prompt += f"\n\n主题要求:{theme}"
|
||||
if difficulty:
|
||||
system_prompt += f"\n\n难度要求:{difficulty}"
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="deepseek-chat",
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "你是一个海龟汤游戏的出题者。请生成一个有趣且有挑战性的海龟汤题目,包含两个部分:1) 简短的奇怪情境描述(2-3句话);2) 完整的故事解释(包含事件的前因后果)。请确保情境足够引人入胜,让玩家有探索的欲望。"
|
||||
"content": system_prompt
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
@ -88,55 +136,51 @@ class TurtleSoupGame:
|
||||
|
||||
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()
|
||||
|
||||
# 质量验证
|
||||
if len(scenario_part) < 10 or len(solution_part) < 50:
|
||||
print("⚠️ 生成的内容质量不足,重试中...")
|
||||
retry_count += 1
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
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["game_type"] = f"{theme if theme else '随机'}-{difficulty if difficulty else '随机'}"
|
||||
self.session_data["is_custom"] = False
|
||||
|
||||
print("✅ 海龟汤题目生成成功 ✅")
|
||||
print(f"\n🥣 汤面:{self.story}\n")
|
||||
print("🔍 你可以通过提问来猜测故事的真相,游戏中我只会回答'是'、'否'或'无关'。")
|
||||
print("📋 输入'答案'查看完整故事,输入'新题'生成新的题目,输入'结束'退出游戏。\n")
|
||||
return
|
||||
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")
|
||||
print("⚠️ 生成的内容格式不符合要求,重试中...")
|
||||
retry_count += 1
|
||||
time.sleep(1)
|
||||
except Exception as e:
|
||||
print(f"❌ 生成题目时出错: {e}")
|
||||
print("❌ 详细错误信息:")
|
||||
traceback.print_exc()
|
||||
# 使用默认题目作为备选
|
||||
print("\n⚠️ 使用默认题目...")
|
||||
print(f"❌ 生成题目时出错 (尝试 {retry_count+1}/{max_retries}): {e}")
|
||||
retry_count += 1
|
||||
time.sleep(1)
|
||||
|
||||
# 所有重试都失败,使用默认题目
|
||||
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["game_type"] = "默认"
|
||||
self.session_data["is_custom"] = False
|
||||
|
||||
print("✅ 海龟汤题目生成成功 ✅")
|
||||
|
||||
@ -1,634 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>🐢 海龟汤游戏</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Microsoft YaHei', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.game-container {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
max-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.game-header {
|
||||
background: linear-gradient(135deg, #4a6fa5 0%, #16222a 100%);
|
||||
color: white;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.game-header h1 {
|
||||
font-size: 2.5em;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.game-header p {
|
||||
font-size: 1.1em;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.game-main {
|
||||
flex: 1;
|
||||
padding: 30px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.soup-section {
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.soup-section h2 {
|
||||
color: #4a6fa5;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.soup-text {
|
||||
background: #f8f9fa;
|
||||
padding: 25px;
|
||||
border-radius: 15px;
|
||||
font-size: 1.2em;
|
||||
line-height: 1.8;
|
||||
border-left: 5px solid #4a6fa5;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.chat-section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.chat-section h2 {
|
||||
color: #4a6fa5;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.chat-history {
|
||||
background: #f8f9fa;
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chat-message {
|
||||
margin-bottom: 15px;
|
||||
padding: 12px 18px;
|
||||
border-radius: 18px;
|
||||
max-width: 80%;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.user-message {
|
||||
background: #4a6fa5;
|
||||
color: white;
|
||||
margin-left: auto;
|
||||
border-bottom-right-radius: 6px;
|
||||
}
|
||||
|
||||
.bot-message {
|
||||
background: #e9ecef;
|
||||
color: #495057;
|
||||
margin-right: auto;
|
||||
border-bottom-left-radius: 6px;
|
||||
}
|
||||
|
||||
.system-message {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
text-align: center;
|
||||
margin: 0 auto 15px;
|
||||
max-width: 90%;
|
||||
border-radius: 12px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
#question-input {
|
||||
flex: 1;
|
||||
padding: 15px;
|
||||
border: 2px solid #ced4da;
|
||||
border-radius: 25px;
|
||||
font-size: 1em;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
#question-input:focus {
|
||||
outline: none;
|
||||
border-color: #4a6fa5;
|
||||
box-shadow: 0 0 0 0.2rem rgba(74, 111, 165, 0.25);
|
||||
}
|
||||
|
||||
#send-button {
|
||||
padding: 15px 30px;
|
||||
background: linear-gradient(135deg, #4a6fa5, #16222a);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
#send-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 15px rgba(74, 111, 165, 0.4);
|
||||
}
|
||||
|
||||
#send-button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.game-controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.control-button {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
padding: 12px;
|
||||
background: linear-gradient(135deg, #6c757d, #495057);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.control-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(108, 117, 125, 0.4);
|
||||
}
|
||||
|
||||
.new-game-btn {
|
||||
background: linear-gradient(135deg, #28a745, #218838);
|
||||
}
|
||||
|
||||
.hint-btn {
|
||||
background: linear-gradient(135deg, #ffc107, #e0a800);
|
||||
}
|
||||
|
||||
.answer-btn {
|
||||
background: linear-gradient(135deg, #dc3545, #c82333);
|
||||
}
|
||||
|
||||
.guess-btn {
|
||||
background: linear-gradient(135deg, #fd7e14, #e0a800);
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
border-top: 1px solid #dee2e6;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.game-container {
|
||||
margin: 10px;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.game-header {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.game-header h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.game-main {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.control-button {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="game-container">
|
||||
<header class="game-header">
|
||||
<h1>🐢 海龟汤游戏</h1>
|
||||
<p>通过提问来猜测故事的真相,我只会回答「是」、「否」或「无关」</p>
|
||||
</header>
|
||||
|
||||
<main class="game-main">
|
||||
<section class="soup-section">
|
||||
<h2>🥣 汤面</h2>
|
||||
<div class="soup-text" id="soup-text">点击「新游戏」开始游戏</div>
|
||||
</section>
|
||||
|
||||
<section class="chat-section">
|
||||
<h2>💬 对话</h2>
|
||||
<div class="chat-history" id="chat-history">
|
||||
<div class="system-message">欢迎来到海龟汤游戏!点击「新游戏」开始,或者查看规则。</div>
|
||||
</div>
|
||||
|
||||
<div class="input-section">
|
||||
<input type="text" id="question-input" placeholder="请输入你的问题..." autocomplete="off">
|
||||
<button id="send-button">发送</button>
|
||||
</div>
|
||||
|
||||
<div class="game-controls">
|
||||
<button class="control-button new-game-btn" id="new-game-btn">新游戏</button>
|
||||
<button class="control-button hint-btn" id="hint-btn">提示</button>
|
||||
<button class="control-button answer-btn" id="answer-btn">查看答案</button>
|
||||
<button class="control-button guess-btn" id="guess-btn">我来猜测</button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<p>🐢 海龟汤游戏 | 直接在浏览器中游玩</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 初始化函数,确保页面加载完成后执行所有DOM操作
|
||||
window.addEventListener('load', () => {
|
||||
// 海龟汤题目库
|
||||
const DEFAULT_STORIES = [
|
||||
{
|
||||
scenario: "一个男人走进一家餐厅,点了一份海龟汤。喝了一口后,他放下汤匙,默默地付了钱离开了餐厅。",
|
||||
solution: "这个男人曾经遭遇海难,和同伴被困在荒岛上。为了生存,同伴们煮了海龟汤给他喝,并告诉他这是他妻子的肉。获救后,他在餐厅喝到真正的海龟汤,才知道自己被骗了,同伴们牺牲了自己来救他。"
|
||||
},
|
||||
{
|
||||
scenario: "一个女人从窗户跳了出去,却没有受伤。她回头看了看,然后笑了。",
|
||||
solution: "这个女人是一名囚犯,她被关在飞机的货舱里。当飞机飞行时,她打开了飞机上的紧急出口窗户,利用降落伞安全着陆。回头看到飞机继续飞行,她知道自己成功逃脱了,所以笑了。"
|
||||
},
|
||||
{
|
||||
scenario: "一个人下雨天走在路上,没有打伞,头发却没有湿。他是怎么做到的?",
|
||||
solution: "这个人是秃头,没有头发,所以头发不会湿。"
|
||||
},
|
||||
{
|
||||
scenario: "一个小偷晚上潜入一栋房子,偷了一些贵重物品。当他准备离开时,听到了敲门声, subsequent邻居死了。",
|
||||
solution: "小偷在偷东西时,不小心碰倒了楼梯上的花瓶,花瓶滚下楼梯,发出很大的声响。邻居听到声响后出来查看,在下楼梯时被滚下来的花瓶绊倒,摔死了。"
|
||||
},
|
||||
{
|
||||
scenario: "一个人在沙漠中发现了一个瓶子,打开后却死亡了。",
|
||||
solution: "这个瓶子是一个地雷的触发装置。当这个人打开瓶子时,触发了地雷,导致他死亡。"
|
||||
},
|
||||
{
|
||||
scenario: "一个小女孩在生日宴会上吹灭了蜡烛,她的父母却哭了。",
|
||||
solution: "小女孩患有绝症,医生告诉她的父母她只能活到七岁。今天是她的七岁生日,当她吹灭蜡烛时,她的父母知道这可能是她最后一个生日了,所以哭了。"
|
||||
},
|
||||
{
|
||||
scenario: "一个消防员在火灾现场救出了所有人,当他准备离开时,按下了电梯按钮,却突然哭了。",
|
||||
solution: "消防员在火灾中救出了所有人,但在最后一次进入火场时,他的队友被困在了里面。当他按下电梯按钮,准备离开时,看到了电梯里显示的楼层,意识到队友所在的楼层已经被大火吞噬,队友已经牺牲,所以哭了。"
|
||||
},
|
||||
{
|
||||
scenario: "一个女人在图书馆工作,每天都会在一本书里夹一张纸条。有一天,她没有夹纸条,她的丈夫失踪了。",
|
||||
solution: "女人的丈夫是一名记者,正在调查一个犯罪团伙。他每天都会到图书馆找女人,通过书中的纸条传递信息。有一天,犯罪团伙发现了他们的计划,绑架了丈夫,并威胁女人如果再传递信息就杀死丈夫。所以女人不敢再夹纸条了。"
|
||||
},
|
||||
{
|
||||
scenario: "一个猎人在森林中开枪,结果却被捕了。",
|
||||
solution: "猎人在森林中打猎时,误将一名徒步旅行者当成了猎物开枪打死了。他因为过失杀人而被捕。"
|
||||
},
|
||||
{
|
||||
scenario: "一个人买了一瓶饮料,看到瓶盖里写着「再来一瓶」,却哭了。",
|
||||
solution: "这个人患有癌症,需要昂贵的治疗费用。他的家庭已经负债累累,当他看到瓶盖里的「再来一瓶」时,想起自己的病情和家庭的困境,感到绝望,所以哭了。"
|
||||
},
|
||||
{
|
||||
scenario: "一个火车站的工作人员,每天都会在站台上做一个奇怪的手势,火车司机看到后就会停车。",
|
||||
solution: "这个工作人员负责在站台上保护乘客的安全。有一天,一个小孩跑到了铁轨上,工作人员看到后,做出了紧急停车的手势。火车司机看到后立即停车,避免了悲剧的发生。从那以后,工作人员每天都会做这个手势,提醒火车司机注意安全。"
|
||||
},
|
||||
{
|
||||
scenario: "一个女人在医院里生下了一个孩子,但是她却没有见过这个孩子的父亲。",
|
||||
solution: "这个女人是一名代孕妈妈。一对不能生育的夫妇雇佣她代孕,孩子的生物学父亲是这对夫妇中的丈夫,但女人从未见过他。"
|
||||
},
|
||||
{
|
||||
scenario: "一个警察在咖啡馆里喝咖啡,突然打翻了杯子,然后冲出了咖啡馆。",
|
||||
solution: "警察正在追捕一名通缉犯。当他在咖啡馆里喝咖啡时,看到通缉犯走进了咖啡馆。他不小心打翻了杯子,然后立即冲出咖啡馆去追捕通缉犯。"
|
||||
},
|
||||
{
|
||||
scenario: "一个男人在马路上行走,突然心脏病发作。一辆路过的汽车紧急刹车,却导致了他的死亡。",
|
||||
solution: "男人心脏病发作时,一辆汽车紧急刹车,发出很大的声响。这个声响导致男人受到惊吓,心脏病加重,最终死亡。"
|
||||
},
|
||||
{
|
||||
scenario: "一个人在海边散步,捡到了一个漂流瓶。当他打开瓶子看到里面的内容后,跳海自杀了。",
|
||||
solution: "这个人患有抑郁症,曾经在绝望中写了一封遗书,装进漂流瓶里扔进了大海。几年后,他的病情有所好转,开始重新面对生活。当他在海边捡到这个漂流瓶,看到自己几年前写的遗书时,想起了过去的痛苦,感到绝望,所以跳海自杀了。"
|
||||
}
|
||||
];
|
||||
|
||||
// 关键词列表
|
||||
const YES_KEYWORDS = [
|
||||
// 海难荒岛系列
|
||||
"海难", "荒岛", "同伴", "牺牲", "妻子", "肉", "获救", "被困", "生存",
|
||||
// 囚犯飞机系列
|
||||
"囚犯", "飞机", "窗户", "失压", "密封", "机舱",
|
||||
// 秃头系列
|
||||
"秃头", "头发", "湿",
|
||||
// 小偷系列
|
||||
"小偷", "楼梯", "摔", "敲门", "邻居",
|
||||
// 地雷系列
|
||||
"地雷", "触发", "沙漠", "瓶子",
|
||||
// 绝症生日系列
|
||||
"绝症", "生日", "蜡烛", "提前", "父母", "哭泣",
|
||||
// 消防员系列
|
||||
"消防员", "火灾", "队友", "牺牲", "电梯", "楼层", "哭泣",
|
||||
// 图书馆记者系列
|
||||
"图书馆", "纸条", "丈夫", "失踪", "记者", "绑架", "囚禁", "犯罪团伙",
|
||||
// 猎人误伤系列
|
||||
"猎人", "误伤", "森林", "徒步旅行", "法律责任", "枪声",
|
||||
// 癌症治疗系列
|
||||
"癌症", "治疗费用", "再来一瓶", "承受能力", "昂贵",
|
||||
// 火车站救孩子系列
|
||||
"火车站", "工作人员", "救孩子", "刹车", "站台", "手势",
|
||||
// 代孕系列
|
||||
"代孕妈妈", "代孕", "不能生育", "夫妇", "医院", "孩子", "父亲", "生物学父亲",
|
||||
// 警察通缉犯系列
|
||||
"警察", "通缉犯", "追捕", "咖啡馆", "打翻", "冲出",
|
||||
// 心脏病发作系列
|
||||
"心脏病", "紧急刹车", "巧合", "外伤", "过马路", "发作",
|
||||
// 漂流瓶自杀系列
|
||||
"漂流瓶", "自杀", "抑郁症", "荒岛", "幸存", "命运的讽刺", "跳海", "海浪"
|
||||
];
|
||||
|
||||
const NO_KEYWORDS = [
|
||||
"毒药", "刀", "枪", "炸弹", "爆炸",
|
||||
"中毒", "淹死", "电击", "触电",
|
||||
"刺杀", "枪杀", "投毒", "谋杀",
|
||||
"自杀死", "他杀死", "掐死", "勒死"
|
||||
];
|
||||
|
||||
// 游戏状态
|
||||
let currentStory = null;
|
||||
let currentSolution = null;
|
||||
let gameOver = false;
|
||||
|
||||
// DOM元素 - 在页面加载完成后获取
|
||||
const soupText = document.getElementById('soup-text');
|
||||
const chatHistory = document.getElementById('chat-history');
|
||||
const questionInput = document.getElementById('question-input');
|
||||
const sendButton = document.getElementById('send-button');
|
||||
const newGameBtn = document.getElementById('new-game-btn');
|
||||
const hintBtn = document.getElementById('hint-btn');
|
||||
const answerBtn = document.getElementById('answer-btn');
|
||||
const guessBtn = document.getElementById('guess-btn');
|
||||
|
||||
// 初始化游戏
|
||||
function initGame() {
|
||||
// 随机选择一个题目
|
||||
const randomIndex = Math.floor(Math.random() * DEFAULT_STORIES.length);
|
||||
const story = DEFAULT_STORIES[randomIndex];
|
||||
|
||||
currentStory = story.scenario;
|
||||
currentSolution = story.solution;
|
||||
gameOver = false;
|
||||
|
||||
// 更新界面
|
||||
updateSoupText();
|
||||
clearChatHistory();
|
||||
addSystemMessage('游戏开始!请开始提问,我只会回答「是」、「否」或「无关」。');
|
||||
}
|
||||
|
||||
// 更新汤面显示
|
||||
function updateSoupText() {
|
||||
soupText.textContent = currentStory;
|
||||
}
|
||||
|
||||
// 清空聊天历史
|
||||
function clearChatHistory() {
|
||||
chatHistory.innerHTML = '';
|
||||
}
|
||||
|
||||
// 添加系统消息
|
||||
function addSystemMessage(message) {
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = 'system-message';
|
||||
messageDiv.textContent = message;
|
||||
chatHistory.appendChild(messageDiv);
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
// 添加用户消息
|
||||
function addUserMessage(message) {
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = 'chat-message user-message';
|
||||
messageDiv.textContent = message;
|
||||
chatHistory.appendChild(messageDiv);
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
// 添加机器人消息
|
||||
function addBotMessage(message) {
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = 'chat-message bot-message';
|
||||
messageDiv.textContent = message;
|
||||
chatHistory.appendChild(messageDiv);
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
// 滚动到底部
|
||||
function scrollToBottom() {
|
||||
chatHistory.scrollTop = chatHistory.scrollHeight;
|
||||
}
|
||||
|
||||
// 回答问题
|
||||
function answerQuestion(question) {
|
||||
if (gameOver) return "游戏已结束,请点击「新游戏」重新开始。";
|
||||
|
||||
const questionLower = question.toLowerCase();
|
||||
const solutionLower = currentSolution.toLowerCase();
|
||||
|
||||
// 检查命令
|
||||
if (questionLower.includes("答案") || questionLower.includes("真相")) {
|
||||
revealAnswer();
|
||||
return;
|
||||
}
|
||||
|
||||
if (questionLower.includes("提示") || questionLower.includes("线索")) {
|
||||
giveHint();
|
||||
return;
|
||||
}
|
||||
|
||||
// 关键词匹配
|
||||
const questionYesKeywords = YES_KEYWORDS.filter(keyword => questionLower.includes(keyword));
|
||||
|
||||
if (questionYesKeywords.length > 0) {
|
||||
// 检查这些关键词是否在答案中出现
|
||||
for (const keyword of questionYesKeywords) {
|
||||
if (solutionLower.includes(keyword)) {
|
||||
return "是";
|
||||
}
|
||||
}
|
||||
return "否";
|
||||
} else if (NO_KEYWORDS.some(keyword => questionLower.includes(keyword))) {
|
||||
return "否";
|
||||
} else {
|
||||
return "无关";
|
||||
}
|
||||
}
|
||||
|
||||
// 给出提示
|
||||
function giveHint() {
|
||||
if (!currentSolution) return;
|
||||
|
||||
const solution = currentSolution;
|
||||
let hint = "";
|
||||
|
||||
// 简单的提示生成逻辑
|
||||
if (solution.includes("海难")) {
|
||||
hint = "这个故事与海难和生存有关。";
|
||||
} else if (solution.includes("囚犯")) {
|
||||
hint = "这个故事与囚犯和飞机有关。";
|
||||
} else if (solution.includes("秃头")) {
|
||||
hint = "这个故事与头发有关。";
|
||||
} else if (solution.includes("小偷")) {
|
||||
hint = "这个故事与小偷和邻居有关。";
|
||||
} else if (solution.includes("地雷")) {
|
||||
hint = "这个故事与沙漠和危险物品有关。";
|
||||
} else if (solution.includes("绝症")) {
|
||||
hint = "这个故事与疾病和生日有关。";
|
||||
} else if (solution.includes("消防员")) {
|
||||
hint = "这个故事与火灾救援有关。";
|
||||
} else if (solution.includes("图书馆")) {
|
||||
hint = "这个故事与图书馆和信息传递有关。";
|
||||
} else if (solution.includes("猎人")) {
|
||||
hint = "这个故事与打猎和意外有关。";
|
||||
} else if (solution.includes("癌症")) {
|
||||
hint = "这个故事与疾病和经济困难有关。";
|
||||
} else if (solution.includes("火车站")) {
|
||||
hint = "这个故事与火车站和安全有关。";
|
||||
} else if (solution.includes("代孕")) {
|
||||
hint = "这个故事与生育和医院有关。";
|
||||
} else if (solution.includes("警察")) {
|
||||
hint = "这个故事与警察和追捕有关。";
|
||||
} else if (solution.includes("心脏病")) {
|
||||
hint = "这个故事与疾病和交通事故有关。";
|
||||
} else if (solution.includes("漂流瓶")) {
|
||||
hint = "这个故事与海洋和心理健康有关。";
|
||||
} else {
|
||||
hint = "关注故事中的关键人物和事件之间的联系。";
|
||||
}
|
||||
|
||||
addSystemMessage(`💡 提示:${hint}`);
|
||||
}
|
||||
|
||||
// 显示答案
|
||||
function revealAnswer() {
|
||||
if (!currentSolution) return;
|
||||
|
||||
gameOver = true;
|
||||
addSystemMessage(`🔓 答案:${currentSolution}`);
|
||||
addSystemMessage('游戏结束!点击「新游戏」开始新的一局。');
|
||||
}
|
||||
|
||||
// 处理猜测
|
||||
function handleGuess() {
|
||||
const guess = prompt("请输入你的猜测:");
|
||||
if (guess) {
|
||||
addUserMessage(`猜测:${guess}`);
|
||||
|
||||
const guessLower = guess.toLowerCase();
|
||||
const solutionLower = currentSolution.toLowerCase();
|
||||
|
||||
// 简单的猜测评估逻辑
|
||||
if (solutionLower.includes(guessLower)) {
|
||||
addBotMessage("恭喜你!猜测正确!");
|
||||
revealAnswer();
|
||||
} else {
|
||||
addBotMessage("猜测不正确,请继续提问。");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 发送问题
|
||||
function sendQuestion() {
|
||||
const question = questionInput.value.trim();
|
||||
if (!question) return;
|
||||
|
||||
addUserMessage(question);
|
||||
questionInput.value = '';
|
||||
|
||||
const answer = answerQuestion(question);
|
||||
if (answer) {
|
||||
addBotMessage(answer);
|
||||
}
|
||||
}
|
||||
|
||||
// 事件监听器 - 在页面加载完成后绑定
|
||||
sendButton.addEventListener('click', sendQuestion);
|
||||
|
||||
questionInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
sendQuestion();
|
||||
}
|
||||
});
|
||||
|
||||
newGameBtn.addEventListener('click', initGame);
|
||||
|
||||
hintBtn.addEventListener('click', () => {
|
||||
if (!currentStory) {
|
||||
addSystemMessage('请先点击「新游戏」开始游戏。');
|
||||
} else {
|
||||
giveHint();
|
||||
}
|
||||
});
|
||||
|
||||
answerBtn.addEventListener('click', () => {
|
||||
if (!currentStory) {
|
||||
addSystemMessage('请先点击「新游戏」开始游戏。');
|
||||
} else {
|
||||
revealAnswer();
|
||||
}
|
||||
});
|
||||
|
||||
guessBtn.addEventListener('click', () => {
|
||||
if (!currentStory) {
|
||||
addSystemMessage('请先点击「新游戏」开始游戏。');
|
||||
} else {
|
||||
handleGuess();
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化 - 添加欢迎消息
|
||||
addSystemMessage('欢迎来到海龟汤游戏!点击「新游戏」开始游戏。');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
860
turtle_soup_html_v2.html
Normal file
860
turtle_soup_html_v2.html
Normal file
@ -0,0 +1,860 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>海龟汤游戏</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: '微软雅黑', Arial, sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background-color: white;
|
||||
min-height: 100vh;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #ff6b35;
|
||||
margin-bottom: 30px;
|
||||
font-size: 2.5em;
|
||||
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.game-info {
|
||||
background-color: #f8f9fa;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
border-left: 4px solid #ff6b35;
|
||||
}
|
||||
|
||||
.story-section {
|
||||
background-color: #fff3cd;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
border: 2px solid #ffeeba;
|
||||
}
|
||||
|
||||
.story-title {
|
||||
font-weight: bold;
|
||||
color: #856404;
|
||||
margin-bottom: 10px;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.story-content {
|
||||
font-size: 1.1em;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.chat-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chat-history {
|
||||
height: 300px;
|
||||
overflow-y: auto;
|
||||
background-color: #f8f9fa;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 15px;
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.user-message {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.bot-message {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
padding: 10px 15px;
|
||||
border-radius: 18px;
|
||||
max-width: 80%;
|
||||
}
|
||||
|
||||
.user-message .message-content {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.bot-message .message-content {
|
||||
background-color: #e9ecef;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
font-size: 0.8em;
|
||||
color: #6c757d;
|
||||
margin-top: 5px;
|
||||
margin-right: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
#question-input {
|
||||
flex: 1;
|
||||
padding: 12px;
|
||||
border: 2px solid #dee2e6;
|
||||
border-radius: 25px;
|
||||
font-size: 1em;
|
||||
outline: none;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
#question-input:focus {
|
||||
border-color: #007bff;
|
||||
}
|
||||
|
||||
#send-btn {
|
||||
padding: 12px 24px;
|
||||
background-color: #ff6b35;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
font-size: 1em;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
#send-btn:hover {
|
||||
background-color: #ee5a24;
|
||||
}
|
||||
|
||||
.command-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.cmd-btn {
|
||||
padding: 8px 16px;
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 15px;
|
||||
font-size: 0.9em;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.cmd-btn:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: white;
|
||||
margin: 15% auto;
|
||||
padding: 30px;
|
||||
border: 1px solid #888;
|
||||
border-radius: 10px;
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.close {
|
||||
color: #aaa;
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close:hover {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input[type="text"], textarea {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 5px;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: 100px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
.game-over {
|
||||
background-color: #d4edda;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
border: 2px solid #c3e6cb;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.stats-section {
|
||||
background-color: #d1ecf1;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #17a2b8;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.chat-history::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.chat-history::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.chat-history::-webkit-scrollbar-thumb {
|
||||
background: #888;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.chat-history::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.story-content {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.chat-history {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.command-buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.cmd-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
max-width: 90%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🐢 海龟汤游戏 🐢</h1>
|
||||
|
||||
<div class="game-info">
|
||||
<p>🔍 海龟汤是一种情境猜谜游戏,我会给出一个奇怪的情境,你可以通过提问来猜测故事的真相。</p>
|
||||
<p>💬 我只会回答'是'、'否'或'无关'。</p>
|
||||
<p>📋 输入'答案'查看完整故事,输入'新题'生成新的题目,输入'自定义'创建自己的题目,</p>
|
||||
<p>📋 输入'提示'获取线索,输入'猜测 内容'尝试猜测答案,输入'结束'退出游戏。</p>
|
||||
</div>
|
||||
|
||||
<div class="stats-section">
|
||||
<div id="stats"></div>
|
||||
</div>
|
||||
|
||||
<div class="story-section">
|
||||
<div class="story-title">🥣 汤面</div>
|
||||
<div class="story-content" id="story-content">请选择游戏模式开始游戏</div>
|
||||
</div>
|
||||
|
||||
<div id="game-over" class="game-over" style="display: none;">
|
||||
<h3 id="game-over-message"></h3>
|
||||
<p id="solution-display"></p>
|
||||
<button class="cmd-btn" onclick="startNewGame()">开始新游戏</button>
|
||||
</div>
|
||||
|
||||
<div class="command-buttons">
|
||||
<button class="cmd-btn" onclick="selectMode(1)">AI生成题目</button>
|
||||
<button class="cmd-btn" onclick="selectMode(2)">自定义题目</button>
|
||||
<button class="cmd-btn" onclick="showHint()">获取提示</button>
|
||||
<button class="cmd-btn" onclick="showSolution()">查看答案</button>
|
||||
<button class="cmd-btn" onclick="startNewGame()">新题</button>
|
||||
<button class="cmd-btn" onclick="endGame()">结束游戏</button>
|
||||
</div>
|
||||
|
||||
<div class="chat-section">
|
||||
<div class="chat-history" id="chat-history"></div>
|
||||
<div class="input-section">
|
||||
<input type="text" id="question-input" placeholder="请输入问题或命令...">
|
||||
<button id="send-btn" onclick="sendMessage()">发送</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 自定义题目模态框 -->
|
||||
<div id="custom-modal" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close" onclick="closeModal()">×</span>
|
||||
<h2>创建自定义题目</h2>
|
||||
<div class="form-group">
|
||||
<label for="custom-story">汤面(奇怪的情境)</label>
|
||||
<textarea id="custom-story" placeholder="请输入汤面..."></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="custom-solution">汤底(完整解释)</label>
|
||||
<textarea id="custom-solution" placeholder="请输入汤底..."></textarea>
|
||||
</div>
|
||||
<button class="btn-primary" onclick="saveCustomStory()">保存题目</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 游戏状态
|
||||
let gameState = {
|
||||
story: null,
|
||||
solution: null,
|
||||
gameOver: false,
|
||||
hints: [],
|
||||
questions: [],
|
||||
guesses: [],
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
result: "未完成",
|
||||
isCustom: false
|
||||
};
|
||||
|
||||
// 预设题目列表
|
||||
const presetStories = [
|
||||
{
|
||||
story: "一个男人走进一家餐厅,点了一份海龟汤。喝了一口后,他放下汤匙,默默地付了钱离开了餐厅。",
|
||||
solution: "这个男人曾经遭遇海难,和同伴被困在荒岛上。为了生存,同伴们煮了海龟汤给他喝,并告诉他这是他妻子的肉。获救后,他在餐厅喝到真正的海龟汤,才知道自己被骗了,同伴们牺牲了自己来救他。",
|
||||
keywords: ["海难", "荒岛", "同伴", "牺牲", "被骗"]
|
||||
},
|
||||
{
|
||||
story: "一个女人独居在公寓里,一天晚上她熄灭了灯准备睡觉。第二天早上,她打开报纸,看到一条消息后跳楼自杀了。",
|
||||
solution: "这个女人是灯塔管理员,她熄灭的不是自己家的灯,而是灯塔的灯。结果导致一艘船在夜间触礁沉没,船上所有人都遇难了。看到报纸上的这个消息后,她因为内疚而自杀了。",
|
||||
keywords: ["灯塔管理员", "灯塔", "船", "触礁", "遇难"]
|
||||
},
|
||||
{
|
||||
story: "一个人在雨中行走,没有带任何雨具,头发却一点也没湿。",
|
||||
solution: "这个人是光头,没有头发,所以即使在雨中行走,头发也不会湿。",
|
||||
keywords: ["光头", "没有头发"]
|
||||
}
|
||||
];
|
||||
|
||||
// 初始化游戏
|
||||
function initGame() {
|
||||
loadGameStats();
|
||||
updateStats();
|
||||
addMessage("系统", "欢迎来到海龟汤游戏!请选择游戏模式开始游戏。");
|
||||
}
|
||||
|
||||
// 选择游戏模式
|
||||
function selectMode(mode) {
|
||||
if (mode === 1) {
|
||||
// 生成AI题目(使用预设题目)
|
||||
generateStory();
|
||||
} else if (mode === 2) {
|
||||
// 显示自定义题目模态框
|
||||
openModal();
|
||||
}
|
||||
}
|
||||
|
||||
// 生成题目
|
||||
function generateStory() {
|
||||
// 随机选择一个预设题目
|
||||
const randomIndex = Math.floor(Math.random() * presetStories.length);
|
||||
const selectedStory = presetStories[randomIndex];
|
||||
|
||||
gameState.story = selectedStory.story;
|
||||
gameState.solution = selectedStory.solution;
|
||||
gameState.isCustom = false;
|
||||
gameState.gameOver = false;
|
||||
gameState.startTime = new Date().toISOString();
|
||||
gameState.endTime = null;
|
||||
gameState.result = "未完成";
|
||||
gameState.hints = [];
|
||||
gameState.questions = [];
|
||||
gameState.guesses = [];
|
||||
|
||||
// 更新界面
|
||||
document.getElementById('story-content').textContent = gameState.story;
|
||||
document.getElementById('game-over').style.display = 'none';
|
||||
|
||||
addMessage("系统", `汤面:${gameState.story}`);
|
||||
addMessage("系统", "你可以通过提问来猜测故事的真相,游戏中我只会回答'是'、'否'或'无关'。");
|
||||
}
|
||||
|
||||
// 打开自定义题目模态框
|
||||
function openModal() {
|
||||
document.getElementById('custom-modal').style.display = 'block';
|
||||
}
|
||||
|
||||
// 关闭自定义题目模态框
|
||||
function closeModal() {
|
||||
document.getElementById('custom-modal').style.display = 'none';
|
||||
// 清空表单
|
||||
document.getElementById('custom-story').value = '';
|
||||
document.getElementById('custom-solution').value = '';
|
||||
}
|
||||
|
||||
// 保存自定义题目
|
||||
function saveCustomStory() {
|
||||
const customStory = document.getElementById('custom-story').value.trim();
|
||||
const customSolution = document.getElementById('custom-solution').value.trim();
|
||||
|
||||
if (!customStory || !customSolution) {
|
||||
alert("汤面和汤底不能为空!");
|
||||
return;
|
||||
}
|
||||
|
||||
gameState.story = customStory;
|
||||
gameState.solution = customSolution;
|
||||
gameState.isCustom = true;
|
||||
gameState.gameOver = false;
|
||||
gameState.startTime = new Date().toISOString();
|
||||
gameState.endTime = null;
|
||||
gameState.result = "未完成";
|
||||
gameState.hints = [];
|
||||
gameState.questions = [];
|
||||
gameState.guesses = [];
|
||||
|
||||
// 更新界面
|
||||
document.getElementById('story-content').textContent = gameState.story;
|
||||
document.getElementById('game-over').style.display = 'none';
|
||||
closeModal();
|
||||
|
||||
addMessage("系统", `汤面:${gameState.story}`);
|
||||
addMessage("系统", "自定义题目创建成功!你可以通过提问来猜测故事的真相。");
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
function sendMessage() {
|
||||
const input = document.getElementById('question-input');
|
||||
const message = input.value.trim();
|
||||
|
||||
if (!message) return;
|
||||
|
||||
// 清空输入框
|
||||
input.value = '';
|
||||
|
||||
if (!gameState.story) {
|
||||
addMessage("系统", "请先选择游戏模式开始游戏!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (gameState.gameOver) {
|
||||
addMessage("系统", "游戏已结束,请开始新游戏!");
|
||||
return;
|
||||
}
|
||||
|
||||
addMessage("我", message);
|
||||
|
||||
// 处理命令
|
||||
const lowerMessage = message.toLowerCase();
|
||||
|
||||
if (lowerMessage.includes("答案") || lowerMessage.includes("查看答案")) {
|
||||
showSolution();
|
||||
return;
|
||||
}
|
||||
|
||||
if (lowerMessage.includes("结束") || lowerMessage.includes("退出")) {
|
||||
endGame();
|
||||
return;
|
||||
}
|
||||
|
||||
if (lowerMessage.includes("新题") || lowerMessage.includes("重新开始") || lowerMessage.includes("换一个")) {
|
||||
startNewGame();
|
||||
return;
|
||||
}
|
||||
|
||||
if (lowerMessage.includes("自定义") || lowerMessage.includes("创建题目")) {
|
||||
openModal();
|
||||
return;
|
||||
}
|
||||
|
||||
if (lowerMessage.includes("提示") || lowerMessage.includes("线索")) {
|
||||
showHint();
|
||||
return;
|
||||
}
|
||||
|
||||
if (lowerMessage.startsWith("猜测") || lowerMessage.startsWith("我猜")) {
|
||||
const guess = message.substring(2).trim();
|
||||
handleGuess(guess);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理普通问题
|
||||
handleQuestion(message);
|
||||
}
|
||||
|
||||
// 处理问题
|
||||
function handleQuestion(question) {
|
||||
// 记录问题
|
||||
gameState.questions.push({
|
||||
question: question,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// 简单的回答逻辑
|
||||
let answer = "无关";
|
||||
|
||||
// 如果问题包含汤底中的关键词,回答"是"或"否"
|
||||
// 这里使用简单的规则,实际应用中可以更复杂
|
||||
if (gameState.solution.toLowerCase().includes(question.toLowerCase())) {
|
||||
answer = "是";
|
||||
} else if (gameState.solution.toLowerCase().includes(question.toLowerCase().replace("不", "")) ||
|
||||
gameState.solution.toLowerCase().includes(question.toLowerCase().replace("不是", "是"))) {
|
||||
answer = "否";
|
||||
}
|
||||
|
||||
// 记录回答
|
||||
gameState.questions[gameState.questions.length - 1].answer = answer;
|
||||
|
||||
addMessage("AI", answer);
|
||||
}
|
||||
|
||||
// 处理猜测
|
||||
function handleGuess(guess) {
|
||||
// 记录猜测
|
||||
gameState.guesses.push({
|
||||
guess: guess,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// 简单的猜测评估逻辑
|
||||
const lowerGuess = guess.toLowerCase();
|
||||
const lowerSolution = gameState.solution.toLowerCase();
|
||||
|
||||
// 检查是否包含所有关键词
|
||||
const keywords = getKeywords(gameState.solution);
|
||||
let matchCount = 0;
|
||||
|
||||
keywords.forEach(keyword => {
|
||||
if (lowerGuess.includes(keyword)) {
|
||||
matchCount++;
|
||||
}
|
||||
});
|
||||
|
||||
// 评估结果
|
||||
if (matchCount >= keywords.length * 0.7) {
|
||||
// 完全正确
|
||||
addMessage("系统", "🎉 恭喜你!你的猜测完全正确!");
|
||||
showSolution();
|
||||
} else if (matchCount >= keywords.length * 0.3) {
|
||||
// 部分正确
|
||||
addMessage("系统", "👍 你的猜测部分正确,但还有一些关键信息没有猜出来。");
|
||||
addMessage("系统", "🔍 继续提问,获取更多线索吧!");
|
||||
} else {
|
||||
// 错误
|
||||
addMessage("系统", "👎 你的猜测不正确,再试一次吧!");
|
||||
addMessage("系统", "🔍 继续提问,获取更多线索吧!");
|
||||
}
|
||||
}
|
||||
|
||||
// 提取关键词
|
||||
function getKeywords(text) {
|
||||
// 简单的关键词提取(移除常见停用词,取名词)
|
||||
const stopWords = ['的', '了', '和', '是', '在', '有', '我', '你', '他', '她', '它', '们', '这', '那', '个', '件', '条', '只', '对', '于', '就', '也', '都', '但', '而', '所以', '因为', '如果', '虽然', '但是', '然而', '不过'];
|
||||
|
||||
// 简单分词(这里使用空格分词,实际应用中可以使用更复杂的分词方法)
|
||||
let words = text.split(/[\s,。?!,.;?!]+/).filter(word => word.length > 1);
|
||||
|
||||
// 移除停用词
|
||||
words = words.filter(word => !stopWords.includes(word));
|
||||
|
||||
// 返回前10个关键词
|
||||
return words.slice(0, 10);
|
||||
}
|
||||
|
||||
// 显示提示
|
||||
function showHint() {
|
||||
if (!gameState.solution) {
|
||||
addMessage("系统", "请先选择游戏模式开始游戏!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (gameState.gameOver) {
|
||||
addMessage("系统", "游戏已结束,无法获取提示!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 生成提示(基于汤底的部分内容)
|
||||
let hint = "";
|
||||
const solutionWords = gameState.solution.split(/\s+/);
|
||||
|
||||
if (gameState.hints.length === 0) {
|
||||
// 第一个提示:提供人物信息
|
||||
hint = "提示1:故事中涉及到的主要人物是" + getMainCharacter(gameState.solution) + "。";
|
||||
} else if (gameState.hints.length === 1) {
|
||||
// 第二个提示:提供事件发生的地点
|
||||
hint = "提示2:故事发生在" + getLocation(gameState.solution) + "。";
|
||||
} else if (gameState.hints.length === 2) {
|
||||
// 第三个提示:提供关键物品
|
||||
hint = "提示3:故事中的关键物品是" + getKeyItem(gameState.solution) + "。";
|
||||
} else {
|
||||
// 没有更多提示
|
||||
hint = "没有更多提示了,请尝试自己思考!";
|
||||
}
|
||||
|
||||
if (!gameState.hints.includes(hint)) {
|
||||
gameState.hints.push(hint);
|
||||
}
|
||||
|
||||
addMessage("系统", hint);
|
||||
}
|
||||
|
||||
// 辅助函数:获取主要人物
|
||||
function getMainCharacter(solution) {
|
||||
// 简单的人物提取,实际应用中可以更复杂
|
||||
const possibleCharacters = ["男人", "女人", "男孩", "女孩", "老人", "孩子", "他", "她", "他们", "她们"];
|
||||
|
||||
for (let character of possibleCharacters) {
|
||||
if (solution.includes(character)) {
|
||||
return character;
|
||||
}
|
||||
}
|
||||
|
||||
return "一个人";
|
||||
}
|
||||
|
||||
// 辅助函数:获取地点
|
||||
function getLocation(solution) {
|
||||
// 简单的地点提取,实际应用中可以更复杂
|
||||
const possibleLocations = ["餐厅", "家里", "医院", "海滩", "森林", "办公室", "学校", "公寓", "灯塔"];
|
||||
|
||||
for (let location of possibleLocations) {
|
||||
if (solution.includes(location)) {
|
||||
return location;
|
||||
}
|
||||
}
|
||||
|
||||
return "某个地方";
|
||||
}
|
||||
|
||||
// 辅助函数:获取关键物品
|
||||
function getKeyItem(solution) {
|
||||
// 简单的物品提取,实际应用中可以更复杂
|
||||
const possibleItems = ["海龟汤", "灯", "雨伞", "报纸", "手机", "钥匙", "汤勺", "笔", "纸"];
|
||||
|
||||
for (let item of possibleItems) {
|
||||
if (solution.includes(item)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return "某个物品";
|
||||
}
|
||||
|
||||
// 显示答案
|
||||
function showSolution() {
|
||||
if (!gameState.solution) {
|
||||
addMessage("系统", "请先选择游戏模式开始游戏!");
|
||||
return;
|
||||
}
|
||||
|
||||
gameState.gameOver = true;
|
||||
gameState.endTime = new Date().toISOString();
|
||||
gameState.result = "查看答案";
|
||||
|
||||
// 显示答案
|
||||
const gameOverMessage = document.getElementById('game-over-message');
|
||||
const solutionDisplay = document.getElementById('solution-display');
|
||||
|
||||
gameOverMessage.textContent = "游戏结束!汤底如下:";
|
||||
solutionDisplay.innerHTML = `<strong>汤底:</strong>${gameState.solution}`;
|
||||
document.getElementById('game-over').style.display = 'block';
|
||||
|
||||
addMessage("系统", `汤底:${gameState.solution}`);
|
||||
|
||||
// 保存游戏数据
|
||||
saveGameSession();
|
||||
}
|
||||
|
||||
// 结束游戏
|
||||
function endGame() {
|
||||
gameState.gameOver = true;
|
||||
gameState.endTime = new Date().toISOString();
|
||||
gameState.result = "中途退出";
|
||||
|
||||
addMessage("系统", "游戏已结束,感谢参与!");
|
||||
|
||||
// 保存游戏数据
|
||||
saveGameSession();
|
||||
}
|
||||
|
||||
// 开始新游戏
|
||||
function startNewGame() {
|
||||
// 清空聊天记录
|
||||
document.getElementById('chat-history').innerHTML = '';
|
||||
|
||||
addMessage("系统", "正在准备新游戏...");
|
||||
|
||||
// 生成新题目
|
||||
generateStory();
|
||||
}
|
||||
|
||||
// 保存游戏会话
|
||||
function saveGameSession() {
|
||||
// 获取现有游戏数据
|
||||
let gameData = JSON.parse(localStorage.getItem('turtleSoupData')) || {
|
||||
games: [],
|
||||
statistics: {
|
||||
totalGames: 0,
|
||||
totalQuestions: 0,
|
||||
winRate: 0
|
||||
}
|
||||
};
|
||||
|
||||
// 添加当前游戏数据
|
||||
gameData.games.push({
|
||||
...gameState,
|
||||
sessionId: `game_${Date.now()}`
|
||||
});
|
||||
|
||||
// 更新统计信息
|
||||
const totalGames = gameData.games.length;
|
||||
const wonGames = gameData.games.filter(game => game.result === "胜利" || game.result === "查看答案").length;
|
||||
const totalQuestions = gameData.games.reduce((sum, game) => sum + game.questions.length, 0);
|
||||
|
||||
gameData.statistics = {
|
||||
totalGames: totalGames,
|
||||
totalQuestions: totalQuestions,
|
||||
winRate: wonGames / totalGames
|
||||
};
|
||||
|
||||
// 保存数据到本地存储
|
||||
localStorage.setItem('turtleSoupData', JSON.stringify(gameData));
|
||||
|
||||
// 更新统计信息显示
|
||||
updateStats();
|
||||
}
|
||||
|
||||
// 加载游戏统计信息
|
||||
function loadGameStats() {
|
||||
const gameData = JSON.parse(localStorage.getItem('turtleSoupData')) || {
|
||||
games: [],
|
||||
statistics: {
|
||||
totalGames: 0,
|
||||
totalQuestions: 0,
|
||||
winRate: 0
|
||||
}
|
||||
};
|
||||
|
||||
return gameData.statistics;
|
||||
}
|
||||
|
||||
// 更新统计信息显示
|
||||
function updateStats() {
|
||||
const stats = loadGameStats();
|
||||
const statsElement = document.getElementById('stats');
|
||||
|
||||
statsElement.innerHTML = `
|
||||
<p>📊 游戏统计:</p>
|
||||
<p>总游戏数:${stats.totalGames}</p>
|
||||
<p>总提问数:${stats.totalQuestions}</p>
|
||||
<p>完成率:${(stats.winRate * 100).toFixed(1)}%</p>
|
||||
`;
|
||||
}
|
||||
|
||||
// 添加消息到聊天记录
|
||||
function addMessage(sender, content) {
|
||||
const chatHistory = document.getElementById('chat-history');
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = `message ${sender === "我" ? "user-message" : "bot-message"}`;
|
||||
|
||||
const messageContentDiv = document.createElement('div');
|
||||
messageContentDiv.className = 'message-content';
|
||||
messageContentDiv.textContent = content;
|
||||
|
||||
const messageTimeDiv = document.createElement('div');
|
||||
messageTimeDiv.className = 'message-time';
|
||||
messageTimeDiv.textContent = new Date().toLocaleTimeString();
|
||||
|
||||
messageDiv.appendChild(messageContentDiv);
|
||||
messageDiv.appendChild(messageTimeDiv);
|
||||
chatHistory.appendChild(messageDiv);
|
||||
|
||||
// 滚动到底部
|
||||
chatHistory.scrollTop = chatHistory.scrollHeight;
|
||||
}
|
||||
|
||||
// 键盘事件监听
|
||||
document.getElementById('question-input').addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
|
||||
// 关闭模态框(点击外部区域)
|
||||
window.onclick = function(event) {
|
||||
const modal = document.getElementById('custom-modal');
|
||||
if (event.target === modal) {
|
||||
closeModal();
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化游戏
|
||||
document.addEventListener('DOMContentLoaded', initGame);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user