```html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>🔍 解密游戏框架</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Roboto, system-ui, sans-serif;
}
body {
background: linear-gradient(145deg, #0b1a2b 0%, #1a2f3f 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 16px;
}
.game-container {
max-width: 680px;
width: 100%;
background: rgba(28, 40, 51, 0.85);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border-radius: 48px;
padding: 28px 24px 32px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.6), inset 0 1px 2px rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.04);
transition: all 0.2s ease;
}
h1 {
font-size: 2rem;
font-weight: 500;
letter-spacing: 2px;
color: #d4e9ff;
text-shadow: 0 2px 6px rgba(0, 20, 40, 0.7);
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
padding-bottom: 14px;
}
h1 small {
font-size: 0.9rem;
font-weight: 300;
color: #8bb4d0;
margin-left: auto;
letter-spacing: 0.3px;
}
.puzzle-panel {
background: #1f3345;
border-radius: 32px;
padding: 28px 24px;
margin-bottom: 28px;
box-shadow: inset 0 6px 12px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(255, 255, 255, 0.02);
border: 1px solid #2f4a5e;
}
.puzzle-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
color: #aac3d6;
font-size: 0.9rem;
letter-spacing: 0.4px;
}
.puzzle-id {
background: #0d1e2b;
padding: 4px 16px;
border-radius: 40px;
font-weight: 500;
color: #b3d6f0;
border: 1px solid #2a4b5e;
}
.puzzle-text {
font-size: 1.3rem;
line-height: 1.6;
color: #ebf5ff;
font-weight: 400;
padding: 8px 0 4px;
text-shadow: 0 1px 2px rgba(0,0,0,0.3);
min-height: 4.2rem;
}
.clue-box {
background: #132433;
border-radius: 20px;
padding: 14px 20px;
margin: 18px 0 12px;
border-left: 5px solid #4b8bb8;
color: #cfdef0;
font-size: 1rem;
box-shadow: inset 0 2px 6px rgba(0,0,0,0.4);
}
.clue-box span {
font-weight: 300;
color: #95bedb;
margin-right: 8px;
}
.input-area {
display: flex;
flex-direction: column;
gap: 14px;
margin-top: 18px;
}
.input-row {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.input-row input {
flex: 2;
min-width: 180px;
background: #0f1f2d;
border: 1px solid #2f4a5e;
border-radius: 48px;
padding: 14px 22px;
font-size: 1rem;
color: #e5f0fa;
outline: none;
transition: 0.25s;
box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.5);
}
.input-row input:focus {
border-color: #5b9bc7;
background: #122b3b;
box-shadow: 0 0 0 3px rgba(70, 140, 200, 0.3), inset 0 4px 8px rgba(0,0,0,0.6);
}
.input-row input::placeholder {
color: #6a8aa3;
font-weight: 300;
letter-spacing: 0.3px;
}
.action-group {
display: flex;
flex: 1;
gap: 10px;
flex-wrap: wrap;
}
.btn {
background: #2f4d63;
border: none;
border-radius: 48px;
padding: 12px 24px;
font-weight: 500;
font-size: 1rem;
color: #f0faff;
cursor: pointer;
transition: all 0.15s ease;
box-shadow: 0 6px 0 #0e1e2b, 0 4px 12px rgba(0, 0, 0, 0.4);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
flex: 1;
min-width: 100px;
letter-spacing: 0.5px;
border: 1px solid #4c7088;
}
.btn:active {
transform: translateY(4px);
box-shadow: 0 2px 0 #0e1e2b;
}
.btn-primary {
background: #3d7b9e;
border-color: #6aa9cc;
box-shadow: 0 6px 0 #1b4053, 0 4px 12px rgba(0, 0, 0, 0.4);
}
.btn-success {
background: #3a8b6e;
border-color: #5eb894;
box-shadow: 0 6px 0 #1d4d3b, 0 4px 12px rgba(0, 0, 0, 0.4);
}
.btn-warning {
background: #b5793a;
border-color: #dba35a;
box-shadow: 0 6px 0 #704a1f, 0 4px 12px rgba(0, 0, 0, 0.4);
}
.btn:disabled {
opacity: 0.5;
transform: translateY(2px);
box-shadow: 0 3px 0 #0e1e2b;
pointer-events: none;
}
.message-box {
margin-top: 18px;
padding: 12px 18px;
border-radius: 30px;
background: #10212e;
color: #b6d6ed;
border: 1px solid #2a4a5e;
font-size: 0.95rem;
min-height: 3.2rem;
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
box-shadow: inset 0 2px 6px rgba(0,0,0,0.4);
}
.message-box .emoji {
font-size: 1.4rem;
}
.status-badge {
margin-left: auto;
background: #1d3545;
padding: 4px 14px;
border-radius: 40px;
font-size: 0.8rem;
color: #9abdd9;
border: 1px solid #355d74;
}
.footer-actions {
display: flex;
gap: 12px;
margin-top: 24px;
flex-wrap: wrap;
}
.footer-actions .btn {
flex: 1;
min-width: 100px;
}
.progress-indicator {
display: flex;
justify-content: center;
gap: 12px;
margin: 16px 0 6px;
}
.dot {
width: 12px;
height: 12px;
border-radius: 20px;
background: #2a475b;
border: 1px solid #3e6279;
transition: 0.2s;
}
.dot.active {
background: #6ab0d9;
border-color: #a2d4f0;
box-shadow: 0 0 12px #3f94c2;
}
.dot.solved {
background: #5cb58b;
border-color: #8fd6b0;
}
@media (max-width: 480px) {
.game-container { padding: 16px; }
.puzzle-panel { padding: 20px 16px; }
.puzzle-text { font-size: 1.1rem; }
.input-row input { min-width: 140px; }
.btn { padding: 12px 14px; min-width: 70px; }
}
</style>
</head>
<body>
<div class="game-container" id="app">
<h1>
🕵️ 解密·框架
<small id="puzzleCounter">1 / 4</small>
</h1>
<div class="puzzle-panel">
<div class="puzzle-header">
<span class="puzzle-id" id="puzzleId"># 谜题 1</span>
<span class="status-badge" id="statusBadge">🔒 未解</span>
</div>
<div class="puzzle-text" id="puzzleText">
点击「线索」或输入答案...
</div>
<div class="clue-box" id="clueBox">
<span>💡 线索</span> <span id="clueText">输入框内输入答案, 点击验证</span>
</div>
<div class="input-area">
<div class="input-row">
<input type="text" id="answerInput" placeholder="输入你的答案..." autocomplete="off" />
<div class="action-group">
<button class="btn btn-primary" id="checkBtn">🔎 验证</button>
<button class="btn btn-warning" id="hintBtn">💬 线索</button>
</div>
</div>
</div>
<div class="message-box" id="messageBox">
<span class="emoji">📌</span>
<span id="messageText">准备开始解谜</span>
<span class="status-badge" id="detailBadge">⚡</span>
</div>
</div>
<!-- 进度点 -->
<div class="progress-indicator" id="progressDots"></div>
<div class="footer-actions">
<button class="btn" id="resetBtn">🔄 重置进度</button>
<button class="btn btn-success" id="nextBtn">▶ 下一题</button>
</div>
</div>
<script>
(function() {
// ---------- 谜题库 ----------
const PUZZLES = [
{
id: 1,
text: '我生于黑暗,却在光中消逝。我随你而行,却从不说话。我是什么?',
clue: '想想你的影子... 或者更具体的,夜晚的伙伴。',
answer: '影子',
// 可接受多种答案(转小写比较)
normalize: (s) => s.trim().toLowerCase().replace(/\s+/g, '')
},
{
id: 2,
text: '我有城市,没有房屋;有森林,没有树木;有河流,没有水滴。我是什么?',
clue: '一张纸上的世界,却能装下整个地球。',
answer: '地图',
normalize: (s) => s.trim().toLowerCase().replace(/\s+/g, '')
},
{
id: 3,
text: '我越老越年轻,但永远不会死去。我存在每个地方,但从未被看见。我是?',
clue: '无形的流逝,每个瞬间都在改变。',
answer: '时间',
normalize: (s) => s.trim().toLowerCase().replace(/\s+/g, '')
},
{
id: 4,
text: '我拥有钥匙却打不开锁,我有空间却没有房间,你可以进入却无法离开。我是什么?',
clue: '键盘上的世界,每天都会遇见。',
answer: '键盘',
normalize: (s) => s.trim().toLowerCase().replace(/\s+/g, '')
}
];
// ---------- 状态 ----------
let currentIndex = 0; // 0-based
let solvedStatus = false, false, false, false;
let gameCompleted = false;
// DOM 元素
const puzzleTextEl = document.getElementById('puzzleText');
const puzzleIdEl = document.getElementById('puzzleId');
const clueTextEl = document.getElementById('clueText');
const statusBadge = document.getElementById('statusBadge');
const answerInput = document.getElementById('answerInput');
const checkBtn = document.getElementById('checkBtn');
const hintBtn = document.getElementById('hintBtn');
const nextBtn = document.getElementById('nextBtn');
const resetBtn = document.getElementById('resetBtn');
const messageText = document.getElementById('messageText');
const messageBox = document.getElementById('messageBox');
const puzzleCounter = document.getElementById('puzzleCounter');
const progressDots = document.getElementById('progressDots');
const detailBadge = document.getElementById('detailBadge');
// ---------- 辅助函数 ----------
function renderProgressDots() {
progressDots.innerHTML = '';
for (let i = 0; i < PUZZLES.length; i++) {
const dot = document.createElement('span');
dot.className = 'dot';
if (i === currentIndex) dot.classList.add('active');
if (solvedStatusi) dot.classList.add('solved');
progressDots.appendChild(dot);
}
}
// 更新界面 (根据 currentIndex 和 solvedStatus)
function updatePuzzleUI() {
const puzzle = PUZZLEScurrentIndex;
const isSolved = solvedStatuscurrentIndex;
// 文本
puzzleTextEl.textContent = puzzle.text;
puzzleIdEl.textContent = `# 谜题 ${puzzle.id}`;
// 只显示当前谜题的线索(点击线索按钮会刷新)
clueTextEl.textContent = puzzle.clue;
// 状态徽章
if (isSolved) {
statusBadge.innerHTML = '✅ 已解';
statusBadge.style.color = '#8cd4b0';
} else {
statusBadge.innerHTML = '🔒 未解';
statusBadge.style.color = '#9abdd9';
}
// 更新计数器
const solvedCount = solvedStatus.filter(v => v === true).length;
puzzleCounter.textContent = `{currentIndex+1} / {PUZZLES.length}`;
// 更新进度点
renderProgressDots();
// 如果全部解开,特殊显示
if (solvedCount === PUZZLES.length && !gameCompleted) {
gameCompleted = true;
messageText.textContent = '🎉 恭喜!所有谜题已解开! 你太棒了!';
detailBadge.textContent = '🏆';
messageBox.style.borderColor = '#5cb58b';
// 自动把下一题禁用? 但下一题按钮可以循环,但已经完成所有谜题
} else {
// 重置消息(如果完成状态被清除)
if (gameCompleted && solvedCount < PUZZLES.length) {
gameCompleted = false;
messageBox.style.borderColor = '#2a4a5e';
messageText.textContent = '继续解谜吧...';
detailBadge.textContent = '⚡';
}
}
// 如果当前谜题已解,输入框可置灰? 但依然可以输入,但验证会提示已解。
// 让输入框保持可用,但验证时会有提示。
answerInput.value = '';
answerInput.focus();
// 联动 nextBtn 禁用逻辑:如果当前谜题未解,nextBtn 不能直接跳转(但允许强制跳转?我们设定为可以跳,但会提示)
// 但设计上允许自由跳转,但是解锁下一个谜题建议必须解开当前? 我们采用自由跳转,但给出提醒。
// 但最好保留自由,不然会卡住。 我们就自由,但验证时判断。
}
// 显示消息
function setMessage(text, emoji = '📌', isSuccess = false, isError = false) {
messageText.textContent = text;
document.querySelector('.message-box .emoji').textContent = emoji;
if (isSuccess) {
messageBox.style.borderLeft = '5px solid #5cb58b';
} else if (isError) {
messageBox.style.borderLeft = '5px solid #c97a5a';
} else {
messageBox.style.borderLeft = '5px solid #4b8bb8';
}
}
// 检查当前谜题答案
function checkAnswer() {
const puzzle = PUZZLEScurrentIndex;
const userAnswer = answerInput.value;
if (!userAnswer.trim()) {
setMessage('请输入答案再验证', '✍️', false, true);
return;
}
const normalize = puzzle.normalize || ((s) => s.trim().toLowerCase().replace(/\s+/g, ''));
const normalizedUser = normalize(userAnswer);
const normalizedCorrect = normalize(puzzle.answer);
if (normalizedUser === normalizedCorrect) {
// 正确!
if (!solvedStatuscurrentIndex) {
solvedStatuscurrentIndex = true;
setMessage(`✅ 完全正确!谜题 ${puzzle.id} 已解开!`, '🎯', true, false);
// 更新UI
updatePuzzleUI();
// 检查是否全部完成
if (solvedStatus.every(v => v === true)) {
setMessage('🎉 恭喜!所有谜题已解开! 你太棒了!', '🏆', true, false);
gameCompleted = true;
detailBadge.textContent = '🏆';
} else {
// 解开后自动显示下一个? 可以给用户选择, 但我们不自动跳转,提示下一题可用
setMessage(`✅ 谜题 ${puzzle.id} 已解开!点击「下一题」继续`, '👉', true, false);
}
} else {
setMessage(`⏳ 谜题 ${puzzle.id} 已经解开过了,试试下一题吧`, '🔄', false, false);
}
} else {
setMessage(`❌ 答案不对,再想想或者查看线索`, '🤔', false, true);
}
}
// 显示线索 (把线索放入消息区)
function showHint() {
const puzzle = PUZZLEScurrentIndex;
const clue = puzzle.clue;
setMessage(`💡 线索: ${clue}`, '🔍', false, false);
// 同时更新clueBox
clueTextEl.textContent = clue;
}
// 跳转到下一题 (循环)
function goToNextPuzzle() {
if (solvedStatus.every(v => v === true)) {
setMessage('🎉 所有谜题已解!你可以重置重新挑战', '🌟', false, false);
return;
}
// 如果当前未解,可以提示,但允许跳转,我们提供警告并跳转
if (!solvedStatuscurrentIndex) {
setMessage(`⚠️ 当前谜题尚未解开,但你可以自由跳转`, '🔄', false, true);
} else {
setMessage(`➡️ 进入下一题`, '📖', false, false);
}
// 循环到下一个未解谜题? 更自然:直接下一个,循环
let nextIndex = (currentIndex + 1) % PUZZLES.length;
// 如果下一个已经解了并且不是所有都解,找最近的未解,但循环太复杂,简单递增
// 但为了避免一直显示已解,跳过已解? 使用户体验更好:尝试找最近的未解谜题
let attempts = 0;
while (solvedStatusnextIndex && attempts < PUZZLES.length) {
nextIndex = (nextIndex + 1) % PUZZLES.length;
attempts++;
}
// 如果所有都解了,上面已经处理了
currentIndex = nextIndex;
updatePuzzleUI();
// 清空消息
if (!solvedStatuscurrentIndex) {
setMessage(`🔍 谜题 ${PUZZLEScurrentIndex.id} 待解`, '📌', false, false);
} else {
setMessage(`✅ 谜题 ${PUZZLEScurrentIndex.id} 已解,继续挑战`, '🎯', false, false);
}
}
// 重置进度
function resetGame() {
solvedStatus = false, false, false, false;
currentIndex = 0;
gameCompleted = false;
messageBox.style.borderLeft = '5px solid #4b8bb8';
updatePuzzleUI();
setMessage('🔄 已重置所有谜题,重新开始吧', '🔄', false, false);
detailBadge.textContent = '⚡';
answerInput.focus();
}
// ---------- 绑定事件 ----------
function init() {
// 渲染初始
updatePuzzleUI();
setMessage('输入答案,点击验证或按 Enter', '📌', false, false);
// 按钮事件
checkBtn.addEventListener('click', checkAnswer);
hintBtn.addEventListener('click', showHint);
nextBtn.addEventListener('click', goToNextPuzzle);
resetBtn.addEventListener('click', resetGame);
// 回车键
answerInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
checkAnswer();
}
});
// 点击输入框的时候如果有消息提示可以清除? 但是保留
answerInput.addEventListener('focus', () => {
// 小细节,如果当前已解,可以提示
if (solvedStatuscurrentIndex) {
setMessage(`💡 谜题 ${PUZZLEScurrentIndex.id} 已解,试试下一题`, '📌', false, false);
}
});
// 当点击进度点可以跳转? 简单交互: 点进度点跳转到对应谜题 (用户体验)
progressDots.addEventListener('click', (e) => {
const dot = e.target.closest('.dot');
if (!dot) return;
const index = Array.from(progressDots.children).indexOf(dot);
if (index === -1 || index === currentIndex) return;
// 跳转
currentIndex = index;
updatePuzzleUI();
const puzzle = PUZZLEScurrentIndex;
if (solvedStatuscurrentIndex) {
setMessage(`✅ 谜题 ${puzzle.id} 已解`, '📌', false, false);
} else {
setMessage(`🔍 谜题 ${puzzle.id} 待解`, '📌', false, false);
}
answerInput.focus();
});
}
// 页面完全加载后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>
</body>
</html>
```
玩法与交互说明
玩家将依次挑战N个谜题,通过输入答案并验证来推进游戏。整个框架围绕"解谜-反馈-推进"的核心循环设计。
· 谜题与线索:每个谜题都配有文字描述和一条可查看的线索。可以在输入框中作答,或点击"线索"按钮获取提示。
· 验证与反馈:输入答案后点击"验证"或按回车键,系统会立即判断对错,并通过消息区域和状态徽章给出清晰反馈。
· 进度与导航:顶部的进度点和计数器会显示您的整体进度。通过"下一题"按钮或点击进度点自由切换谜题,"重置进度"按钮则可随时重新开始。