<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>d-分离 (d-Separation) 练习</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: #f4f7f9;
color: #333;
line-height: 1.6;
display: flex;
justify-content: center;
padding: 20px;
}
.container {
max-width: 800px;
width: 100%;
background-color: #fff;
padding: 20px 30px;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
h1, h2 {
text-align: center;
color: #0056b3;
}
.introduction {
background-color: #e7f3ff;
border-left: 5px solid #0056b3;
padding: 15px;
margin: 20px 0;
border-radius: 5px;
}
#quiz-area {
margin-top: 20px;
}
#graph-container {
text-align: center;
margin-bottom: 20px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 8px;
background-color: #fafafa;
min-height: 250px;
}
#graph-svg {
max-width: 100%;
height: auto;
}
#question {
font-size: 1.3em;
font-weight: bold;
text-align: center;
margin-bottom: 25px;
}
.answer-buttons {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 20px;
}
.btn {
padding: 12px 25px;
font-size: 1em;
cursor: pointer;
border: none;
border-radius: 5px;
color: white;
transition: background-color 0.3s, transform 0.1s;
}
.btn:hover {
opacity: 0.9;
transform: translateY(-2px);
}
.btn:disabled {
cursor: not-allowed;
opacity: 0.6;
}
#yes-btn { background-color: #28a745; }
#no-btn { background-color: #dc3545; }
#next-btn {
display: block;
margin: 20px auto 0;
background-color: #007bff;
display: none;
}
#feedback {
padding: 15px;
border-radius: 5px;
margin-top: 20px;
display: none;
}
#feedback.correct {
background-color: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
}
#feedback.incorrect {
background-color: #f8d7da;
border: 1px solid #f5c6cb;
color: #721c24;
}
#progress {
text-align: center;
font-size: 0.9em;
color: #666;
margin-top: 10px;
}
.explanation {
margin-top: 10px;
font-size: 0.95em;
text-align: left;
}
.explanation ul {
padding-left: 20px;
}
.explanation strong {
color: #0056b3;
}
#final-score {
text-align: center;
font-size: 1.5em;
padding: 40px 20px;
}
</style>
</head>
<body>
<div class="container">
<h1>d-分离 (d-Separation) 练习</h1>
<div class="introduction">
<p><strong>欢迎来到 d-分离练习!</strong></p>
<p>d-分离是判断贝叶斯网络中变量是否条件独立的重要工具。请根据下图和问题,判断两个节点集是否在给定条件下 d-分离(条件独立)。</p>
<ul>
<li><strong>d-分离 (d-separated)</strong>: 两个节点之间所有的路径都被**阻断 (blocked)**。</li>
<li><strong>d-连接 (d-connected)</strong>: 两个节点之间至少存在一条**未被阻断 (unblocked)** 的路径。</li>
</ul>
</div>
<div id="quiz-area">
<div id="progress"></div>
<div id="graph-container">
<svg id="graph-svg" width="600" height="250" viewBox="0 0 600 250"></svg>
</div>
<p id="question"></p>
<div class="answer-buttons">
<button class="btn" id="yes-btn">是 (d-分离)</button>
<button class="btn" id="no-btn">否 (d-连接)</button>
</div>
<div id="feedback"></div>
<button class="btn" id="next-btn">下一题</button>
</div>
<div id="final-score" style="display: none;"></div>
</div>
<script>
const questions = [
// --- 基本规则 ---
{ // 1. 链式结构:被观察
graph: { nodes: [{id:'A', x:100, y:125}, {id:'B', x:300, y:125}, {id:'C', x:500, y:125}], edges: [{from:'A', to:'B'}, {from:'B', to:'C'}] },
query: { X: 'A', Y: 'C', Z: ['B'] },
answer: 'yes',
explanation: `路径 A → B → C 是一条链式结构。因为中间节点 B 在条件集 Z 中(被观察),所以该路径被阻断。这是图中唯一的路径,因此 A 和 C 在给定 B 的条件下是 d-分离的。`
},
{ // 2. 链式结构:未被观察
graph: { nodes: [{id:'A', x:100, y:125}, {id:'B', x:300, y:125}, {id:'C', x:500, y:125}], edges: [{from:'A', to:'B'}, {from:'B', to:'C'}] },
query: { X: 'A', Y: 'C', Z: [] },
answer: 'no',
explanation: `路径 A → B → C 是一条链式结构。因为中间节点 B 不在条件集 Z 中(未被观察),所以该路径是连通的。因此 A 和 C 在给定空集的条件下是 d-连接的。`
},
{ // 3. 分叉结构:被观察
graph: { nodes: [{id:'A', x:100, y:125}, {id:'B', x:300, y:50}, {id:'C', x:500, y:125}], edges: [{from:'B', to:'A'}, {from:'B', to:'C'}] },
query: { X: 'A', Y: 'C', Z: ['B'] },
answer: 'yes',
explanation: `路径 A ← B → C 是一条分叉结构。因为共同原因 B 在条件集 Z 中(被观察),所以该路径被阻断。这是唯一的路径,因此 A 和 C 在给定 B 的条件下是 d-分离的。`
},
{ // 4. 分叉结构:未被观察
graph: { nodes: [{id:'A', x:100, y:125}, {id:'B', x:300, y:50}, {id:'C', x:500, y:125}], edges: [{from:'B', to:'A'}, {from:'B', to:'C'}] },
query: { X: 'A', Y: 'C', Z: [] },
answer: 'no',
explanation: `路径 A ← B → C 是一条分叉结构。因为共同原因 B 不在条件集 Z 中(未被观察),所以该路径是连通的。因此 A 和 C 在给定空集的条件下是 d-连接的。`
},
{ // 5. 对撞结构:未被观察
graph: { nodes: [{id:'A', x:100, y:50}, {id:'B', x:300, y:125}, {id:'C', x:500, y:50}], edges: [{from:'A', to:'B'}, {from:'C', to:'B'}] },
query: { X: 'A', Y: 'C', Z: [] },
answer: 'yes',
explanation: `路径 A → B ← C 是一条对撞结构。因为对撞节点 B 及其后代都不在条件集 Z 中,所以该路径默认被阻断。这是唯一的路径,因此 A 和 C 在给定空集的条件下是 d-分离的。`
},
{ // 6. 对撞结构:被观察
graph: { nodes: [{id:'A', x:100, y:50}, {id:'B', x:300, y:125}, {id:'C', x:500, y:50}], edges: [{from:'A', to:'B'}, {from:'C', to:'B'}] },
query: { X: 'A', Y: 'C', Z: ['B'] },
answer: 'no',
explanation: `路径 A → B ← C 是一条对撞结构。因为对撞节点 B 在条件集 Z 中(被观察),所以该路径被打开(连通)。这被称为“解释消除效应”。因此 A 和 C 在给定 B 的条件下是 d-连接的。`
},
{ // 7. 对撞结构:后代被观察
graph: { nodes: [{id:'A', x:100, y:50}, {id:'B', x:300, y:50}, {id:'C', x:500, y:50}, {id:'D', x:300, y:200}], edges: [{from:'A', to:'B'}, {from:'C', to:'B'}, {from:'B', to:'D'}] },
query: { X: 'A', Y: 'C', Z: ['D'] },
answer: 'no',
explanation: `路径 A → B ← C 是一条对撞结构。虽然对撞节点 B 不在条件集 Z 中,但它的后代 D 在 Z 中。观察对撞节点的后代同样会打开这条路径。因此 A 和 C 在给定 D 的条件下是 d-连接的。`
},
// --- 组合情况 ---
{ // 8. 链 + 对撞
graph: { nodes: [{id:'A', x:50, y:125}, {id:'B', x:200, y:125}, {id:'C', x:350, y:50}, {id:'D', x:500, y:125}], edges: [{from:'A', to:'B'}, {from:'C', to:'B'}, {from:'C', to:'D'}] },
query: { X: 'A', Y: 'D', Z: ['C'] },
answer: 'yes',
explanation: `存在唯一路径 A → B ← C → D。我们逐段分析:
<ul>
<li>在 B 节点:A → B ← C 是一个对撞结构。由于对撞节点 B 未被观察,这条路径在 B 处被阻断。</li>
<li>在 C 节点:B ← C → D 是一个分叉结构。由于分叉点 C 被观察,这条路径在 C 处也被阻断。</li>
</ul>
因为这条路径上至少有一个点(实际上是两个点)阻断了信息流,所以 A 和 D 在给定 C 的条件下是 d-分离的。`
},
{ // 9. 同上,但观察 B
graph: { nodes: [{id:'A', x:50, y:125}, {id:'B', x:200, y:125}, {id:'C', x:350, y:50}, {id:'D', x:500, y:125}], edges: [{from:'A', to:'B'}, {from:'C', to:'B'}, {from:'C', to:'D'}] },
query: { X: 'A', Y: 'D', Z: ['B'] },
answer: 'no',
explanation: `存在唯一路径 A → B ← C → D。我们分析这条路径:
<ul>
<li>在 B 节点:A → B ← C 是一个对撞结构。由于对撞节点 B 被观察,这条路径在 B 处被打开(连通)。</li>
<li>在 C 节点:B ← C → D 是一个分叉结构。分叉点 C 未被观察,所以路径在 C 处也是连通的。</li>
</ul>
因为整条路径上没有被阻断的点,所以 A 和 D 在给定 B 的条件下是 d-连接的。`
},
{ // 10. M 型图
graph: { nodes: [{id:'A', x:150, y:50}, {id:'B', x:450, y:50}, {id:'C', x:150, y:200}, {id:'D', x:450, y:200}], edges: [{from:'A', to:'C'}, {from:'A', to:'D'}, {from:'B', to:'C'}, {from:'B', to:'D'}] },
query: { X: 'A', Y: 'B', Z: ['C'] },
answer: 'no',
explanation: `这里有两条路径连接 A 和 B:
<ul>
<li>路径 1: A → D ← B。这是一个对撞结构。对撞节点 D 未被观察,所以此路径被<strong>阻断</strong>。</li>
<li>路径 2: A → C ← B。这是一个对撞结构。对撞节点 C 在条件集 Z 中被观察,所以此路径被<strong>打开(连通)</strong>。</li>
</ul>
因为存在至少一条连通的路径(路径 2),所以 A 和 B 在给定 C 的条件下是 d-连接的。`
},
{ // 11. M 型图,观察两个
graph: { nodes: [{id:'A', x:150, y:50}, {id:'B', x:450, y:50}, {id:'C', x:150, y:200}, {id:'D', x:450, y:200}], edges: [{from:'A', to:'C'}, {from:'A', to:'D'}, {from:'B', to:'C'}, {from:'B', to:'D'}] },
query: { X: 'A', Y: 'B', Z: ['C', 'D'] },
answer: 'no',
explanation: `这里有两条路径连接 A 和 B:
<ul>
<li>路径 1: A → D ← B。对撞结构。对撞节点 D 被观察,路径<strong>打开(连通)</strong>。</li>
<li>路径 2: A → C ← B。对撞结构。对撞节点 C 被观察,路径<strong>打开(连通)</strong>。</li>
</ul>
因为两条路径都由于观察了对撞节点而打开,所以 A 和 B 在给定 C 和 D 的条件下是 d-连接的。`
},
{ // 12. 复杂图 1
graph: { nodes: [{id:'A',x:50,y:125},{id:'B',x:200,y:50},{id:'C',x:200,y:200},{id:'D',x:350,y:125},{id:'E',x:500,y:125}], edges: [{from:'A',to:'B'},{from:'A',to:'C'},{from:'B',to:'D'},{from:'C',to:'D'},{from:'D',to:'E'}] },
query: { X: 'A', Y: 'E', Z: ['D'] },
answer: 'yes',
explanation: `要判断 A 和 E 是否 d-分离,我们需要检查所有从 A 到 E 的路径。所有路径都必须经过 D。
<ul>
<li>路径 1: A → B → D → E。这是一条链式结构。中间节点 D 被观察,所以路径在 D 处被<strong>阻断</strong>。</li>
<li>路径 2: A → C → D → E。这也是一条链式结构。中间节点 D 被观察,所以路径在 D 处被<strong>阻断</strong>。</li>
</ul>
因为所有从 A 到 E 的路径都被 D 阻断,所以 A 和 E 在给定 D 的条件下是 d-分离的。`
},
{ // 13. 复杂图 1, 观察 A
graph: { nodes: [{id:'A',x:50,y:125},{id:'B',x:200,y:50},{id:'C',x:200,y:200},{id:'D',x:350,y:125},{id:'E',x:500,y:125}], edges: [{from:'A',to:'B'},{from:'A',to:'C'},{from:'B',to:'D'},{from:'C',to:'D'},{from:'D',to:'E'}] },
query: { X: 'B', Y: 'C', Z: ['A'] },
answer: 'yes',
explanation: `这里有两条路径连接 B 和 C:
<ul>
<li>路径 1: B ← A → C。这是一个分叉结构。分叉点 A 在条件集 Z 中被观察,所以此路径被<strong>阻断</strong>。</li>
<li>路径 2: B → D ← C。这是一个对撞结构。对撞节点 D 及其后代 E 都未被观察,所以此路径被<strong>阻断</strong>。</li>
</ul>
因为所有路径都被阻断,所以 B 和 C 在给定 A 的条件下是 d-分离的。`
},
{ // 14. 复杂图 1, 观察 D
graph: { nodes: [{id:'A',x:50,y:125},{id:'B',x:200,y:50},{id:'C',x:200,y:200},{id:'D',x:350,y:125},{id:'E',x:500,y:125}], edges: [{from:'A',to:'B'},{from:'A',to:'C'},{from:'B',to:'D'},{from:'C',to:'D'},{from:'D',to:'E'}] },
query: { X: 'B', Y: 'C', Z: ['D'] },
answer: 'no',
explanation: `这里有两条路径连接 B 和 C:
<ul>
<li>路径 1: B ← A → C。这是一个分叉结构。分叉点 A 未被观察,所以此路径是<strong>连通的</strong>。</li>
<li>路径 2: B → D ← C。这是一个对撞结构。对撞节点 D 被观察,所以此路径也被<strong>打开(连通)</strong>。</li>
</ul>
因为存在连通路径(实际上两条都连通),所以 B 和 C 在给定 D 的条件下是 d-连接的。`
},
{ // 15. 经典诱导依赖图
graph: { nodes: [{id:'Rain',x:150,y:50},{id:'Sprinkler',x:450,y:50},{id:'Grass Wet',x:300,y:150}], edges: [{from:'Rain',to:'Grass Wet'},{from:'Sprinkler',to:'Grass Wet'}] },
query: { X: 'Rain', Y: 'Sprinkler', Z: [] },
answer: 'yes',
explanation: `路径 Rain → Grass Wet ← Sprinkler 是一个对撞结构。因为对撞节点 Grass Wet 及其后代(无)都未被观察,所以路径被阻断。因此,在不知道草地是否湿润的情况下,下雨和洒水器是独立的(d-分离)。`
},
{ // 16. 经典诱导依赖图,观察结果
graph: { nodes: [{id:'Rain',x:150,y:50},{id:'Sprinkler',x:450,y:50},{id:'Grass Wet',x:300,y:150}], edges: [{from:'Rain',to:'Grass Wet'},{from:'Sprinkler',to:'Grass Wet'}] },
query: { X: 'Rain', Y: 'Sprinkler', Z: ['Grass Wet'] },
answer: 'no',
explanation: `路径 Rain → Grass Wet ← Sprinkler 是一个对撞结构。因为对撞节点 Grass Wet 被观察了,所以路径被打开(连通)。这意味着,如果我们观察到草地是湿的,那么下雨和洒水器就变得相关了(例如,如果没下雨,那很可能是洒水器开了)。因此它们是 d-连接的。`
},
{ // 17. 更长的链
graph: { nodes: [{id:'A',x:50,y:125},{id:'B',x:180,y:125},{id:'C',x:310,y:125},{id:'D',x:440,y:125},{id:'E',x:570,y:125}], edges: [{from:'A',to:'B'},{from:'B',to:'C'},{from:'C',to:'D'},{from:'D',to:'E'}] },
query: { X: 'A', Y: 'E', Z: ['C'] },
answer: 'yes',
explanation: `唯一的路径是 A → B → C → D → E。这是一个链式结构。因为中间节点 C 在条件集 Z 中被观察,所以信息流在 C 点被阻断。因此,A 和 E 在给定 C 的条件下是 d-分离的。`
},
{ // 18. 对撞与分叉混合
graph: { nodes: [{id:'A',x:50, y:125}, {id:'B',x:200, y:125}, {id:'C',x:350, y:50}, {id:'D',x:350, y:200}, {id:'E', x:500, y:125}], edges: [{from:'A', to:'B'}, {from:'B',to:'C'}, {from:'B',to:'D'}, {from:'C',to:'E'}, {from:'D',to:'E'}] },
query: { X: 'A', Y: 'E', Z: [] },
answer: 'no',
explanation: `我们检查从 A 到 E 的所有路径,给定 Z=∅。
<ul>
<li>路径 1: A → B → C → E。路径上的 B 和 C 都是链式节点,且都未被观察。路径<strong>连通</strong>。</li>
<li>路径 2: A → B → D → E。路径上的 B 和 D 都是链式节点,且都未被观察。路径<strong>连通</strong>。</li>
</ul>
因为存在连通路径,所以 A 和 E 在给定空集的条件下是 d-连接的。注意,C→E←D 中的 E 是对撞节点,但它不是 A 到 E 路径上的中间节点,不影响判断。`
},
{ // 19. 钻石图
graph: { nodes: [{id:'A',x:100,y:125},{id:'B',x:250,y:50},{id:'C',x:400,y:125},{id:'D',x:250,y:200}], edges: [{from:'A',to:'B'},{from:'B',to:'C'},{from:'A',to:'D'},{from:'D',to:'C'}] },
query: { X: 'B', Y: 'D', Z: ['A', 'C'] },
answer: 'no',
explanation: `这里有两条路径连接 B 和 D:
<ul>
<li>路径 1: B ← A → D。这是一个分叉结构。分叉点 A 在 Z 中被观察,所以此路径被<strong>阻断</strong>。</li>
<li>路径 2: B → C ← D。这是一个对撞结构。对撞点 C 在 Z 中被观察,所以此路径被<strong>打开(连通)</strong>。</li>
</ul>
因为存在一条连通的路径(路径 2),所以 B 和 D 在给定 A 和 C 的条件下是 d-连接的。`
},
{ // 20. 钻石图 (最终版)
graph: { nodes: [{id:'A',x:100,y:125},{id:'B',x:250,y:50},{id:'C',x:400,y:125},{id:'D',x:250,y:200}], edges: [{from:'A',to:'B'},{from:'B',to:'C'},{from:'A',to:'D'},{from:'D',to:'C'}] },
query: { X: 'B', Y: 'D', Z: ['A'] },
answer: 'yes',
explanation: `这里有两条路径连接 B 和 D:
<ul>
<li>路径 1: B ← A → D。这是一个分叉结构。因为分叉点 A 在条件集 Z 中(被观察),所以此路径被 <strong>阻断</strong>。</li>
<li>路径 2: B → C ← D。这是一个对撞结构。因为对撞点 C 及其后代(无)都不在条件集 Z 中,所以此路径被 <strong>阻断</strong>。</li>
</ul>
因为所有从 B 到 D 的路径都被阻断了,所以 B 和 D 在给定 A 的条件下是 d-分离的。`
}
];
const quizArea = document.getElementById('quiz-area');
const finalScoreDiv = document.getElementById('final-score');
const progressEl = document.getElementById('progress');
const questionEl = document.getElementById('question');
const svgEl = document.getElementById('graph-svg');
const feedbackEl = document.getElementById('feedback');
const yesBtn = document.getElementById('yes-btn');
const noBtn = document.getElementById('no-btn');
const nextBtn = document.getElementById('next-btn');
let currentQuestionIndex = 0;
let score = 0;
const NODE_RADIUS = 25; // Define node radius as a constant
function loadQuestion() {
if (currentQuestionIndex >= questions.length) {
showFinalScore();
return;
}
const currentQuestion = questions[currentQuestionIndex];
const { graph, query } = currentQuestion;
feedbackEl.style.display = 'none';
feedbackEl.className = '';
nextBtn.style.display = 'none';
yesBtn.disabled = false;
noBtn.disabled = false;
progressEl.textContent = `问题 ${currentQuestionIndex + 1} / ${questions.length}`;
let z_str = query.Z.length > 0 ? `{${query.Z.join(', ')}}` : '∅ (空集)';
questionEl.innerHTML = `在下图中,给定条件集 Z = ${z_str},请问节点 <strong>${query.X}</strong> 和 <strong>${query.Y}</strong> 是否 d-分离?`;
drawGraph(graph, query);
}
function drawGraph(graph, query) {
svgEl.innerHTML = '';
const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
const marker = document.createElementNS('http://www.w3.org/2000/svg', 'marker');
marker.setAttribute('id', 'arrow');
marker.setAttribute('viewBox', '0 -5 10 10');
marker.setAttribute('refX', '10'); // Point of the arrow
marker.setAttribute('refY', '0');
marker.setAttribute('markerWidth', '6');
marker.setAttribute('markerHeight', '6');
marker.setAttribute('orient', 'auto');
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d', 'M0,-5L10,0L0,5');
path.setAttribute('fill', '#666');
marker.appendChild(path);
defs.appendChild(marker);
svgEl.appendChild(defs);
// Draw edges first, so nodes are rendered on top
graph.edges.forEach(edge => {
const fromNode = graph.nodes.find(n => n.id === edge.from);
const toNode = graph.nodes.find(n => n.id === edge.to);
// *** FIX: Calculate line end point on the circle's edge, not center ***
const dx = toNode.x - fromNode.x;
const dy = toNode.y - fromNode.y;
const dist = Math.sqrt(dx * dx + dy * dy);
// Avoid division by zero
if (dist === 0) return;
// Calculate the new endpoint (shortened by the radius)
const endX = toNode.x - (dx / dist) * NODE_RADIUS;
const endY = toNode.y - (dy / dist) * NODE_RADIUS;
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', fromNode.x);
line.setAttribute('y1', fromNode.y);
line.setAttribute('x2', endX); // Use calculated end point
line.setAttribute('y2', endY); // Use calculated end point
line.setAttribute('stroke', '#666');
line.setAttribute('stroke-width', 2);
line.setAttribute('marker-end', 'url(#arrow)');
svgEl.appendChild(line);
});
// Draw nodes and labels
graph.nodes.forEach(node => {
const isQueryNode = node.id === query.X || node.id === query.Y;
const isGivenNode = query.Z.includes(node.id);
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
circle.setAttribute('cx', node.x);
circle.setAttribute('cy', node.y);
circle.setAttribute('r', NODE_RADIUS);
circle.setAttribute('stroke', isQueryNode ? '#0056b3' : '#333');
circle.setAttribute('stroke-width', 3);
circle.setAttribute('fill', isGivenNode ? '#ffc107' : '#e7f3ff');
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
text.setAttribute('x', node.x);
text.setAttribute('y', node.y + 7);
text.setAttribute('text-anchor', 'middle');
text.setAttribute('font-size', '18px');
text.setAttribute('font-weight', 'bold');
text.textContent = node.id;
svgEl.appendChild(circle);
svgEl.appendChild(text);
});
}
function handleAnswer(userAnswer) {
const currentQuestion = questions[currentQuestionIndex];
const isCorrect = userAnswer === currentQuestion.answer;
if (isCorrect) {
score++;
feedbackEl.className = 'correct';
feedbackEl.innerHTML = `<strong>正确!</strong> <div class="explanation">${currentQuestion.explanation}</div>`;
} else {
feedbackEl.className = 'incorrect';
feedbackEl.innerHTML = `<strong>错误。</strong> 正确答案是 "${currentQuestion.answer === 'yes' ? '是 (d-分离)' : '否 (d-连接)'}"。<div class="explanation">${currentQuestion.explanation}</div>`;
}
feedbackEl.style.display = 'block';
yesBtn.disabled = true;
noBtn.disabled = true;
nextBtn.style.display = 'block';
}
function showFinalScore() {
quizArea.style.display = 'none';
finalScoreDiv.style.display = 'block';
finalScoreDiv.innerHTML = `<h2>练习完成!</h2>
<p>你的最终得分是:${score} / ${questions.length}</p>
<p>${score > questions.length * 0.8 ? '太棒了!你对 d-分离的理解非常深入。' : (score > questions.length * 0.5 ? '不错!再多练习几次就能完全掌握了。' : '继续努力!建议回顾一下 d-分离的三条规则。')}</p>
<button class="btn" onclick="location.reload()" style="background-color:#007bff">再做一次</button>`;
}
yesBtn.addEventListener('click', () => handleAnswer('yes'));
noBtn.addEventListener('click', () => handleAnswer('no'));
nextBtn.addEventListener('click', () => {
currentQuestionIndex++;
loadQuestion();
});
document.addEventListener('DOMContentLoaded', loadQuestion);
</script>
</body>
</html>