GROUP123-LYT/turtle_soup_html.html
DROUP123 72bec8db47 feat: 初始化海龟汤游戏项目,包含核心逻辑、前端界面和文档
- 添加核心游戏逻辑文件structual.py,实现海龟汤游戏基本功能
- 创建HTML前端界面turtle_soup_html.html,提供交互式游戏体验
- 编写详细README.md文档,包含项目介绍、运行指南和贡献说明
- 添加项目配置文件pyproject.toml和环境变量示例
- 实现游戏数据存储功能,使用JSON保存游戏记录
- 包含15个预设海龟汤题目和关键词匹配系统
2026-01-07 15:16:59 +08:00

634 lines
26 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>