Update index.html
Browse files- index.html +280 -18
index.html
CHANGED
@@ -1,19 +1,281 @@
|
|
1 |
-
<!
|
2 |
-
<html>
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
</html>
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8"/>
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
6 |
+
<title>Five Algorithm Jeopardy</title>
|
7 |
+
<!-- same CSS as before -->
|
8 |
+
</head>
|
9 |
+
<body>
|
10 |
+
<h1>Five Algorithm Jeopardy</h1>
|
11 |
+
<a class="source" href="https://www.linkedin.com/pulse/foundational-machine-learning-algorithms-modern-data-science-lively-t4xje/" target="_blank">
|
12 |
+
Source: Foundational Machine Learning Algorithms for Modern Data Science (LinkedIn)
|
13 |
+
</a>
|
14 |
+
|
15 |
+
<div class="topbar">
|
16 |
+
<div id="score">Score: 0</div>
|
17 |
+
<button class="btn-reset" id="reset-btn" title="Start a fresh round">Reset Game</button>
|
18 |
+
</div>
|
19 |
+
|
20 |
+
<div class="stage">
|
21 |
+
<div id="game-board">
|
22 |
+
<div class="category">Logistic Regression</div>
|
23 |
+
<div class="category">Decision Tree</div>
|
24 |
+
<div class="category">Random Forest</div>
|
25 |
+
<div class="category">Support Vector Machine</div>
|
26 |
+
<div class="category">KNN</div>
|
27 |
+
</div>
|
28 |
+
</div>
|
29 |
+
|
30 |
+
<div id="question-display"></div>
|
31 |
+
<div id="review" style="display:none;"></div>
|
32 |
+
|
33 |
+
<audio id="dd-audio" preload="auto">
|
34 |
+
<source src="daily-double.mp3" type="audio/mpeg">
|
35 |
+
</audio>
|
36 |
+
<div id="dd-overlay" class="dd-overlay"><div class="dd-text">DAILY DOUBLE!</div></div>
|
37 |
+
|
38 |
+
<script>
|
39 |
+
const categories = [
|
40 |
+
"Logistic Regression",
|
41 |
+
"Decision Tree",
|
42 |
+
"Random Forest",
|
43 |
+
"Support Vector Machine",
|
44 |
+
"KNN"
|
45 |
+
];
|
46 |
+
// 15 questions, correct indices evenly distributed (5x0, 5x1, 5x2)
|
47 |
+
const questions = [
|
48 |
+
// Logistic Regression
|
49 |
+
[
|
50 |
+
{ q: "What does Logistic Regression output for each prediction?", a: [
|
51 |
+
"A probability between 0 and 1 using the sigmoid function",
|
52 |
+
"A cluster of nearest neighbors",
|
53 |
+
"A collection of random trees averaged together"
|
54 |
+
], correct: 0 },
|
55 |
+
{ q: "Which is a limitation of Logistic Regression?", a: [
|
56 |
+
"It automatically models non-linear decision boundaries",
|
57 |
+
"It struggles when data is not linearly separable without feature engineering",
|
58 |
+
"It cannot run on large datasets"
|
59 |
+
], correct: 1 },
|
60 |
+
{ q: "In healthcare, Logistic Regression is often used to:", a: [
|
61 |
+
"Rank feature importance in tree ensembles",
|
62 |
+
"Estimate probabilities of disease presence or risk",
|
63 |
+
"Define separating hyperplanes in high dimensions"
|
64 |
+
], correct: 2 }
|
65 |
+
],
|
66 |
+
// Decision Tree
|
67 |
+
[
|
68 |
+
{ q: "What measure is commonly used to decide splits in Decision Trees?", a: [
|
69 |
+
"Gradient descent step size",
|
70 |
+
"Gini impurity or entropy",
|
71 |
+
"Euclidean distance"
|
72 |
+
], correct: 1 },
|
73 |
+
{ q: "Which is a strength of Decision Trees?", a: [
|
74 |
+
"They are transparent and easy to explain to non-technical stakeholders",
|
75 |
+
"They are always the most accurate model for high-dimensional data",
|
76 |
+
"They require no stopping criteria and cannot overfit"
|
77 |
+
], correct: 0 },
|
78 |
+
{ q: "Why might Decision Trees be unstable?", a: [
|
79 |
+
"Because they average many weak learners",
|
80 |
+
"Because small changes in input data can change the entire tree structure",
|
81 |
+
"Because they always assume linear log-odds"
|
82 |
+
], correct: 2 }
|
83 |
+
],
|
84 |
+
// Random Forest
|
85 |
+
[
|
86 |
+
{ q: "How does a Random Forest reduce overfitting compared to a single Decision Tree?", a: [
|
87 |
+
"By using sigmoid functions",
|
88 |
+
"By averaging results across many randomly built trees",
|
89 |
+
"By calculating Euclidean distances to neighbors"
|
90 |
+
], correct: 1 },
|
91 |
+
{ q: "Which real-world task is well suited to Random Forests?", a: [
|
92 |
+
"Fraud detection with structured transaction data",
|
93 |
+
"Image recognition with deep convolutional networks",
|
94 |
+
"Building separating hyperplanes in very high-dimensional text data"
|
95 |
+
], correct: 0 },
|
96 |
+
{ q: "What is a limitation of Random Forests?", a: [
|
97 |
+
"They are always linear models",
|
98 |
+
"They are slower to train and interpret compared to a single tree",
|
99 |
+
"They cannot handle missing values"
|
100 |
+
], correct: 2 }
|
101 |
+
],
|
102 |
+
// Support Vector Machine
|
103 |
+
[
|
104 |
+
{ q: "What is the key principle behind SVM classification?", a: [
|
105 |
+
"Maximizing the margin between classes using support vectors",
|
106 |
+
"Averaging predictions from multiple trees",
|
107 |
+
"Voting from k-nearest neighbors"
|
108 |
+
], correct: 0 },
|
109 |
+
{ q: "Which hyperparameter defines how far the influence of a single training example reaches in an SVM?", a: [
|
110 |
+
"max_depth",
|
111 |
+
"gamma",
|
112 |
+
"n_neighbors"
|
113 |
+
], correct: 1 },
|
114 |
+
{ q: "In which field is SVM often applied due to high-dimensional data?", a: [
|
115 |
+
"Computer vision image classification with CNNs",
|
116 |
+
"Text classification such as spam filtering",
|
117 |
+
"Simple binary logistic models"
|
118 |
+
], correct: 2 }
|
119 |
+
],
|
120 |
+
// KNN
|
121 |
+
[
|
122 |
+
{ q: "How does KNN classify a new data point?", a: [
|
123 |
+
"By calculating the weighted sum of features and applying sigmoid",
|
124 |
+
"By looking at the majority class among its k nearest neighbors",
|
125 |
+
"By building an ensemble of trees to vote"
|
126 |
+
], correct: 1 },
|
127 |
+
{ q: "What is a major limitation of KNN?", a: [
|
128 |
+
"Prediction is slow on large datasets since distances must be calculated for all points",
|
129 |
+
"It always assumes linear decision boundaries",
|
130 |
+
"It cannot handle more than two features"
|
131 |
+
], correct: 0 },
|
132 |
+
{ q: "Which type of task is naturally suited to KNN?", a: [
|
133 |
+
"Credit scoring in finance",
|
134 |
+
"Handwriting recognition or recommender systems",
|
135 |
+
"Genome sequence modeling"
|
136 |
+
], correct: 2 }
|
137 |
+
]
|
138 |
+
];
|
139 |
+
|
140 |
+
let score = 0, answered = 0;
|
141 |
+
const total = 15;
|
142 |
+
const board = document.getElementById("game-board"),
|
143 |
+
qdisp = document.getElementById("question-display"),
|
144 |
+
sdisp = document.getElementById("score"),
|
145 |
+
review = document.getElementById("review");
|
146 |
+
const ddOverlay = document.getElementById("dd-overlay");
|
147 |
+
const ddAudio = document.getElementById("dd-audio");
|
148 |
+
const shuffleRegistry = new Map();
|
149 |
+
const missedQuestions = [];
|
150 |
+
const dailyDouble = { col: Math.floor(Math.random() * 5), row: Math.floor(Math.random() * 3) };
|
151 |
+
document.getElementById('reset-btn').addEventListener('click', () => location.reload());
|
152 |
+
|
153 |
+
function createBoard() {
|
154 |
+
for (let row = 0; row < 3; row++) {
|
155 |
+
for (let col = 0; col < 5; col++) {
|
156 |
+
const card = document.createElement("div");
|
157 |
+
card.className = "card tilt";
|
158 |
+
card.textContent = `$${(row + 1) * 100}`;
|
159 |
+
card.dataset.col = col;
|
160 |
+
card.dataset.row = row;
|
161 |
+
card.addEventListener("mousemove", (e) => {
|
162 |
+
const r = card.getBoundingClientRect();
|
163 |
+
const x = (e.clientX - r.left) / r.width;
|
164 |
+
const y = (e.clientY - r.top) / r.height;
|
165 |
+
card.style.setProperty("--ry", `${(x - 0.5) * 10}deg`);
|
166 |
+
card.style.setProperty("--rx", `${(0.5 - y) * 10}deg`);
|
167 |
+
});
|
168 |
+
card.addEventListener("mouseleave", () => {
|
169 |
+
card.style.removeProperty("--ry");
|
170 |
+
card.style.removeProperty("--rx");
|
171 |
+
});
|
172 |
+
card.onclick = () => handleCardClick(card);
|
173 |
+
board.appendChild(card);
|
174 |
+
}
|
175 |
+
}
|
176 |
+
}
|
177 |
+
function handleCardClick(card){
|
178 |
+
if (card.classList.contains("disabled")) return;
|
179 |
+
const col = +card.dataset.col;
|
180 |
+
const row = +card.dataset.row;
|
181 |
+
const isDD = (col === dailyDouble.col && row === dailyDouble.row);
|
182 |
+
if (isDD){
|
183 |
+
triggerDailyDouble(card, () => showQuestion(col, row, card, true));
|
184 |
+
} else {
|
185 |
+
showQuestion(col, row, card, false);
|
186 |
+
}
|
187 |
+
}
|
188 |
+
function triggerDailyDouble(card, onDone){
|
189 |
+
const ring = document.createElement("div");
|
190 |
+
ring.className = "flash-ring";
|
191 |
+
card.appendChild(ring);
|
192 |
+
ddAudio.currentTime = 0;
|
193 |
+
ddAudio.play().catch(()=>{});
|
194 |
+
ddOverlay.style.display = "flex";
|
195 |
+
setTimeout(() => {
|
196 |
+
ddOverlay.style.display = "none";
|
197 |
+
ring.remove();
|
198 |
+
onDone();
|
199 |
+
}, 2400);
|
200 |
+
}
|
201 |
+
function showQuestion(categoryIndex, difficulty, card, isDailyDouble){
|
202 |
+
const q = questions[categoryIndex][difficulty];
|
203 |
+
const options = q.a.map((text, origIdx) => ({ text, origIdx }));
|
204 |
+
for (let i = options.length - 1; i > 0; i--) {
|
205 |
+
const j = Math.floor(Math.random() * (i + 1));
|
206 |
+
[options[i], options[j]] = [options[j], options[i]];
|
207 |
+
}
|
208 |
+
const shuffledCorrectIndex = options.findIndex(o => o.origIdx === q.correct);
|
209 |
+
const key = `${categoryIndex}-${difficulty}`;
|
210 |
+
shuffleRegistry.set(key, { shuffledCorrectIndex, isDailyDouble });
|
211 |
+
const dailyDoubleBanner = isDailyDouble ? `<div class="daily-double-banner">π DAILY DOUBLE! π</div>` : "";
|
212 |
+
qdisp.innerHTML = `
|
213 |
+
${dailyDoubleBanner}
|
214 |
+
<h2>${categories[categoryIndex]} for $${(difficulty + 1) * 100}${isDailyDouble ? " (x2)" : ""}</h2>
|
215 |
+
<p>${q.q}</p>
|
216 |
+
<div class="answer-container">
|
217 |
+
${options.map((opt, i) =>
|
218 |
+
`<button class="answer-btn" data-ans="${i}" data-key="${key}">${opt.text}</button>`
|
219 |
+
).join("")}
|
220 |
+
</div>
|
221 |
+
`;
|
222 |
+
card.classList.add("disabled");
|
223 |
+
document.querySelectorAll('.answer-btn').forEach(btn => {
|
224 |
+
btn.addEventListener('click', () => {
|
225 |
+
if (btn.classList.contains('disabled')) return;
|
226 |
+
const chosenIndex = parseInt(btn.getAttribute('data-ans'), 10);
|
227 |
+
const k = btn.getAttribute('data-key');
|
228 |
+
checkAnswer(categoryIndex, difficulty, chosenIndex, k);
|
229 |
+
}, { once: true });
|
230 |
+
});
|
231 |
+
}
|
232 |
+
function checkAnswer(cat, diff, chosenShuffledIndex, key){
|
233 |
+
const q = questions[cat][diff];
|
234 |
+
const reg = shuffleRegistry.get(key);
|
235 |
+
const isCorrect = (chosenShuffledIndex === reg.shuffledCorrectIndex);
|
236 |
+
let val = (diff + 1) * 100;
|
237 |
+
if (reg.isDailyDouble) val *= 2;
|
238 |
+
document.querySelectorAll(".answer-btn").forEach(b => {
|
239 |
+
b.disabled = true; b.classList.add("disabled");
|
240 |
+
});
|
241 |
+
if (isCorrect) {
|
242 |
+
score += val;
|
243 |
+
qdisp.innerHTML += `<p class="feedback" style="color:green;">β
Correct! +$${val.toLocaleString()}</p>`;
|
244 |
+
} else {
|
245 |
+
score -= val;
|
246 |
+
const correctText = q.a[q.correct];
|
247 |
+
const chosenBtn = document.querySelector(`.answer-btn[data-ans="${chosenShuffledIndex}"][data-key="${key}"]`);
|
248 |
+
const chosenPretty = chosenBtn ? chosenBtn.textContent : "(your choice)";
|
249 |
+
qdisp.innerHTML += `<p class="feedback" style="color:#b00020;">β Wrong! β$${val.toLocaleString()}<br/>Correct answer: <em>${correctText}</em></p>`;
|
250 |
+
missedQuestions.push({
|
251 |
+
category: categories[cat],
|
252 |
+
value: `$${(diff+1)*100}${reg.isDailyDouble ? " (x2)" : ""}`,
|
253 |
+
question: q.q,
|
254 |
+
yourAnswer: chosenPretty,
|
255 |
+
correctAnswer: correctText
|
256 |
+
});
|
257 |
+
}
|
258 |
+
sdisp.textContent = `Score: ${score}`;
|
259 |
+
if (++answered === total) endGame();
|
260 |
+
}
|
261 |
+
function endGame(){
|
262 |
+
qdisp.innerHTML += `<h2 style="margin-top:12px">Game Over!</h2><p>Your final score: $${score.toLocaleString()}</p>`;
|
263 |
+
if (missedQuestions.length){
|
264 |
+
const items = missedQuestions.map(m =>
|
265 |
+
`<div class="missed">
|
266 |
+
<div class="q">(${m.category} β’ ${m.value}) ${m.question}</div>
|
267 |
+
<div class="a">Your answer: <em>${m.yourAnswer}</em></div>
|
268 |
+
<div class="a">Correct: <strong>${m.correctAnswer}</strong></div>
|
269 |
+
</div>`
|
270 |
+
).join("");
|
271 |
+
review.style.display = "block";
|
272 |
+
review.innerHTML = `<h3>Review: Questions to Revisit (${missedQuestions.length})</h3>${items}`;
|
273 |
+
} else {
|
274 |
+
review.style.display = "block";
|
275 |
+
review.innerHTML = `<h3>Perfect Round!</h3><p>You answered all questions correctly π</p>`;
|
276 |
+
}
|
277 |
+
}
|
278 |
+
createBoard();
|
279 |
+
</script>
|
280 |
+
</body>
|
281 |
</html>
|