Update index.html
Browse files- index.html +208 -53
index.html
CHANGED
|
@@ -5,7 +5,149 @@
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
| 6 |
<title>Simple Networking Jeopardy</title>
|
| 7 |
<style>
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
</style>
|
| 10 |
</head>
|
| 11 |
<body>
|
|
@@ -47,109 +189,110 @@
|
|
| 47 |
"Private DNS"
|
| 48 |
];
|
| 49 |
|
|
|
|
| 50 |
const questions = [
|
| 51 |
// Hub & Spoke
|
| 52 |
[
|
| 53 |
-
{ q: "What is the
|
| 54 |
a: [
|
| 55 |
-
"
|
| 56 |
-
"
|
| 57 |
-
"
|
| 58 |
], correct: 0 },
|
| 59 |
{ q: "Which network typically hosts shared services like Azure Firewall and DNS?",
|
| 60 |
a: [
|
| 61 |
"Hub network",
|
| 62 |
"Spoke network",
|
| 63 |
-
"
|
| 64 |
], correct: 0 },
|
| 65 |
-
{ q: "Why
|
| 66 |
a: [
|
| 67 |
-
"
|
| 68 |
-
"
|
| 69 |
-
"To disable
|
| 70 |
], correct: 0 }
|
| 71 |
],
|
| 72 |
// ASG & NSG
|
| 73 |
[
|
| 74 |
-
{ q: "What
|
| 75 |
a: [
|
| 76 |
-
"
|
| 77 |
-
"
|
| 78 |
-
"
|
| 79 |
], correct: 0 },
|
| 80 |
-
{ q: "What
|
| 81 |
a: [
|
| 82 |
-
"
|
| 83 |
-
"
|
| 84 |
-
"
|
| 85 |
], correct: 0 },
|
| 86 |
-
{ q: "Why
|
| 87 |
a: [
|
| 88 |
-
"
|
| 89 |
-
"To increase DNS
|
| 90 |
-
"To
|
| 91 |
], correct: 0 }
|
| 92 |
],
|
| 93 |
// Firewall
|
| 94 |
[
|
| 95 |
-
{ q: "Where
|
| 96 |
a: [
|
| 97 |
"In the hub virtual network",
|
| 98 |
-
"
|
| 99 |
-
"
|
| 100 |
], correct: 0 },
|
| 101 |
-
{ q: "What is
|
| 102 |
a: [
|
| 103 |
-
"
|
| 104 |
-
"
|
| 105 |
-
"
|
| 106 |
], correct: 0 },
|
| 107 |
-
{ q: "Why
|
| 108 |
a: [
|
| 109 |
-
"
|
| 110 |
-
"
|
| 111 |
-
"
|
| 112 |
], correct: 0 }
|
| 113 |
],
|
| 114 |
// Routing
|
| 115 |
[
|
| 116 |
-
{ q: "What Azure feature
|
| 117 |
a: [
|
| 118 |
"User-defined route (UDR) tables",
|
| 119 |
-
"
|
| 120 |
-
"
|
| 121 |
], correct: 0 },
|
| 122 |
-
{ q: "Where should the
|
| 123 |
a: [
|
| 124 |
"Frontend and backend subnets",
|
| 125 |
-
"Only the hub
|
| 126 |
-
"
|
| 127 |
], correct: 0 },
|
| 128 |
-
{ q: "Why route all outbound traffic
|
| 129 |
a: [
|
| 130 |
-
"
|
| 131 |
-
"To
|
| 132 |
-
"To bypass Azure Policy
|
| 133 |
], correct: 0 }
|
| 134 |
],
|
| 135 |
// Private DNS
|
| 136 |
[
|
| 137 |
{ q: "What is the purpose of a Private DNS Zone?",
|
| 138 |
a: [
|
| 139 |
-
"
|
| 140 |
-
"
|
| 141 |
-
"
|
| 142 |
], correct: 0 },
|
| 143 |
-
{ q: "Which record type is used to resolve
|
| 144 |
a: [
|
| 145 |
"A-record",
|
| 146 |
"MX-record",
|
| 147 |
-
"
|
| 148 |
], correct: 0 },
|
| 149 |
-
{ q: "Why link the Private DNS Zone to the
|
| 150 |
a: [
|
| 151 |
-
"To allow
|
| 152 |
-
"To publish
|
| 153 |
"To disable DNS forwarding"
|
| 154 |
], correct: 0 }
|
| 155 |
]
|
|
@@ -157,6 +300,7 @@
|
|
| 157 |
|
| 158 |
let score = 0, answered = 0;
|
| 159 |
const total = 15;
|
|
|
|
| 160 |
const board = document.getElementById("game-board"),
|
| 161 |
qdisp = document.getElementById("question-display"),
|
| 162 |
sdisp = document.getElementById("score"),
|
|
@@ -165,10 +309,14 @@
|
|
| 165 |
const ddAudio = document.getElementById("dd-audio");
|
| 166 |
const shuffleRegistry = new Map();
|
| 167 |
const missedQuestions = [];
|
|
|
|
|
|
|
| 168 |
const dailyDouble = { col: Math.floor(Math.random() * 5), row: Math.floor(Math.random() * 3) };
|
|
|
|
| 169 |
document.getElementById('reset-btn').addEventListener('click', () => location.reload());
|
| 170 |
|
| 171 |
function createBoard() {
|
|
|
|
| 172 |
for (let row = 0; row < 3; row++) {
|
| 173 |
for (let col = 0; col < 5; col++) {
|
| 174 |
const card = document.createElement("div");
|
|
@@ -191,6 +339,7 @@
|
|
| 191 |
board.appendChild(card);
|
| 192 |
}
|
| 193 |
}
|
|
|
|
| 194 |
document.querySelector('.stage').style.visibility = 'visible';
|
| 195 |
}
|
| 196 |
|
|
@@ -210,8 +359,7 @@
|
|
| 210 |
const ring = document.createElement("div");
|
| 211 |
ring.className = "flash-ring";
|
| 212 |
card.appendChild(ring);
|
| 213 |
-
ddAudio.currentTime = 0;
|
| 214 |
-
ddAudio.play().catch(()=>{});
|
| 215 |
ddOverlay.classList.add('active');
|
| 216 |
ddOverlay.style.display = "flex";
|
| 217 |
setTimeout(() => {
|
|
@@ -225,6 +373,7 @@
|
|
| 225 |
function showQuestion(categoryIndex, difficulty, card, isDailyDouble){
|
| 226 |
const q = questions[categoryIndex][difficulty];
|
| 227 |
const options = q.a.map((text, origIdx) => ({ text, origIdx }));
|
|
|
|
| 228 |
for (let i = options.length - 1; i > 0; i--) {
|
| 229 |
const j = Math.floor(Math.random() * (i + 1));
|
| 230 |
[options[i], options[j]] = [options[j], options[i]];
|
|
@@ -232,6 +381,7 @@
|
|
| 232 |
const shuffledCorrectIndex = options.findIndex(o => o.origIdx === q.correct);
|
| 233 |
const key = `${categoryIndex}-${difficulty}`;
|
| 234 |
shuffleRegistry.set(key, { shuffledCorrectIndex, isDailyDouble });
|
|
|
|
| 235 |
const dailyDoubleBanner = isDailyDouble ? `<div class="daily-double-banner">🎉 DAILY DOUBLE! 🎉</div>` : "";
|
| 236 |
qdisp.innerHTML = `
|
| 237 |
${dailyDoubleBanner}
|
|
@@ -243,6 +393,7 @@
|
|
| 243 |
).join("")}
|
| 244 |
</div>
|
| 245 |
`;
|
|
|
|
| 246 |
card.classList.add("disabled");
|
| 247 |
document.querySelectorAll('.answer-btn').forEach(btn => {
|
| 248 |
btn.addEventListener('click', () => {
|
|
@@ -260,9 +411,11 @@
|
|
| 260 |
const isCorrect = (chosenShuffledIndex === reg.shuffledCorrectIndex);
|
| 261 |
let val = (diff + 1) * 100;
|
| 262 |
if (reg.isDailyDouble) val *= 2;
|
|
|
|
| 263 |
document.querySelectorAll(".answer-btn").forEach(b => {
|
| 264 |
b.disabled = true; b.classList.add("disabled");
|
| 265 |
});
|
|
|
|
| 266 |
if (isCorrect) {
|
| 267 |
score += val;
|
| 268 |
qdisp.innerHTML += `<p class="feedback" style="color:green;">✅ Correct! +$${val.toLocaleString()}</p>`;
|
|
@@ -280,6 +433,7 @@
|
|
| 280 |
correctAnswer: correctText
|
| 281 |
});
|
| 282 |
}
|
|
|
|
| 283 |
sdisp.textContent = `Score: ${score}`;
|
| 284 |
if (++answered === total) endGame();
|
| 285 |
}
|
|
@@ -302,6 +456,7 @@
|
|
| 302 |
}
|
| 303 |
}
|
| 304 |
|
|
|
|
| 305 |
createBoard();
|
| 306 |
</script>
|
| 307 |
</body>
|
|
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
| 6 |
<title>Simple Networking Jeopardy</title>
|
| 7 |
<style>
|
| 8 |
+
:root{
|
| 9 |
+
--blue:#0a49a6;
|
| 10 |
+
--blue-dark:#073a84;
|
| 11 |
+
--gold:#ffd24a;
|
| 12 |
+
--board-gap:10px;
|
| 13 |
+
--card-depth:28px;
|
| 14 |
+
}
|
| 15 |
+
*{box-sizing:border-box}
|
| 16 |
+
body{
|
| 17 |
+
font-family: system-ui, Arial, sans-serif;
|
| 18 |
+
display:flex;flex-direction:column;align-items:center;
|
| 19 |
+
background: radial-gradient(1200px 700px at 50% -200px, #f2f6ff 0%, #e9efff 30%, #cfdafc 100%) fixed;
|
| 20 |
+
margin:0; min-height:100vh;
|
| 21 |
+
}
|
| 22 |
+
h1{ color:#173e8c; margin:22px 0 6px; text-shadow:0 2px 0 #fff;}
|
| 23 |
+
a.source{ font-size:14px; color:#334; margin-bottom:8px; text-decoration:none}
|
| 24 |
+
a.source:hover{ text-decoration:underline }
|
| 25 |
+
.topbar {
|
| 26 |
+
width:min(1100px,95vw);
|
| 27 |
+
display:flex;
|
| 28 |
+
justify-content:space-between;
|
| 29 |
+
align-items:center;
|
| 30 |
+
gap:10px;
|
| 31 |
+
margin-bottom:8px;
|
| 32 |
+
}
|
| 33 |
+
.btn-reset{
|
| 34 |
+
padding:8px 12px; border-radius:10px; font-weight:800; cursor:pointer;
|
| 35 |
+
border:2px solid #0d3a85; color:#fff; background:linear-gradient(180deg,#1362ff,#0d3a85);
|
| 36 |
+
box-shadow:0 8px 18px rgba(0,0,0,.25);
|
| 37 |
+
}
|
| 38 |
+
.btn-reset:hover{ transform: translateY(-1px) }
|
| 39 |
+
.btn-reset:active{ transform: translateY(0) }
|
| 40 |
+
|
| 41 |
+
/* Stage + board */
|
| 42 |
+
.stage{ perspective: 1200px; width:min(1100px, 95vw); visibility:hidden; }
|
| 43 |
+
#game-board{
|
| 44 |
+
display:grid;
|
| 45 |
+
grid-template-columns: repeat(5, 1fr);
|
| 46 |
+
grid-auto-rows: 120px;
|
| 47 |
+
gap: var(--board-gap);
|
| 48 |
+
padding: var(--board-gap);
|
| 49 |
+
border-radius:18px;
|
| 50 |
+
background: linear-gradient(180deg, #0e1a3a, #01060f);
|
| 51 |
+
box-shadow:
|
| 52 |
+
0 20px 45px rgba(0,0,0,.35),
|
| 53 |
+
inset 0 0 0 4px #000;
|
| 54 |
+
transform: rotateX(8deg);
|
| 55 |
+
transform-origin: top center;
|
| 56 |
+
}
|
| 57 |
+
.category, .card{
|
| 58 |
+
position:relative;
|
| 59 |
+
border-radius:14px;
|
| 60 |
+
user-select:none;
|
| 61 |
+
transform-style: preserve-3d;
|
| 62 |
+
transition: transform .2s ease, box-shadow .2s ease, filter .2s ease;
|
| 63 |
+
box-shadow:
|
| 64 |
+
0 var(--card-depth) calc(var(--card-depth) + 12px) rgba(0,0,0,.35),
|
| 65 |
+
inset 0 0 0 1px rgba(255,255,255,.15);
|
| 66 |
+
}
|
| 67 |
+
.category{
|
| 68 |
+
background: linear-gradient(180deg, #004fbf 0%, #003d93 60%, #003684 100%);
|
| 69 |
+
color:#fff; font-weight:800; text-transform:uppercase; letter-spacing:.3px;
|
| 70 |
+
display:flex; justify-content:center; align-items:center; text-align:center;
|
| 71 |
+
border:1px solid #00122c;
|
| 72 |
+
text-shadow:0 2px 0 rgba(0,0,0,.35);
|
| 73 |
+
padding:8px;
|
| 74 |
+
}
|
| 75 |
+
.card{
|
| 76 |
+
cursor:pointer; font-weight:900; color: var(--gold);
|
| 77 |
+
background:
|
| 78 |
+
radial-gradient(80% 80% at 50% 25%, rgba(255,255,255,.18), rgba(255,255,255,0) 60%),
|
| 79 |
+
linear-gradient(180deg, var(--blue) 0%, var(--blue-dark) 100%);
|
| 80 |
+
border:1px solid #031b3f;
|
| 81 |
+
display:flex; justify-content:center; align-items:center; text-align:center;
|
| 82 |
+
font-size: clamp(16px, 2.4vw, 26px);
|
| 83 |
+
text-shadow: 0 2px 0 #000, 0 0 10px rgba(255,210,74,.35);
|
| 84 |
+
padding:10px;
|
| 85 |
+
}
|
| 86 |
+
.card.tilt:hover{
|
| 87 |
+
transform: translateZ(10px) rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg));
|
| 88 |
+
box-shadow:
|
| 89 |
+
0 24px 48px rgba(0,0,0,.4),
|
| 90 |
+
0 0 24px rgba(255,210,74,.15),
|
| 91 |
+
inset 0 0 0 1px rgba(255,255,255,.18);
|
| 92 |
+
filter: saturate(1.1);
|
| 93 |
+
}
|
| 94 |
+
.card:active{ transform: translateZ(0) scale(.985); box-shadow: 0 12px 18px rgba(0,0,0,.45), inset 0 0 0 1px rgba(255,255,255,.12); }
|
| 95 |
+
.card.disabled{ cursor:default; color:#bfc7da; background: linear-gradient(180deg,#6b7aa6,#4e5b85); text-shadow:none; filter:grayscale(.2) brightness(.92); }
|
| 96 |
+
|
| 97 |
+
#question-display{ width:min(1000px, 92vw); text-align:center; margin:16px 0 6px; }
|
| 98 |
+
#question-display h2{ margin:8px 0 6px; color:#0d2b6f }
|
| 99 |
+
#question-display p { font-size: 22px; line-height: 1.4; }
|
| 100 |
+
#score{ font-size:24px; font-weight:800; color:#0d2b6f; margin:0 0 8px 0 }
|
| 101 |
+
|
| 102 |
+
.answer-container{ display:flex; justify-content:center; flex-wrap:wrap; gap:10px; margin-top:14px }
|
| 103 |
+
.answer-btn{
|
| 104 |
+
margin:0; padding:10px 16px; font-size:18px; cursor:pointer;
|
| 105 |
+
border:2px solid #0d3a85; border-radius:10px; font-weight:800; color:#fff;
|
| 106 |
+
background: linear-gradient(180deg,#1362ff,#0d3a85);
|
| 107 |
+
box-shadow: 0 8px 18px rgba(0,0,0,.25);
|
| 108 |
+
transition: transform .12s ease, box-shadow .12s ease, filter .12s ease;
|
| 109 |
+
}
|
| 110 |
+
.answer-btn:hover{ transform: translateY(-2px); box-shadow:0 12px 26px rgba(0,0,0,.28) }
|
| 111 |
+
.answer-btn:active{ transform: translateY(0); box-shadow:0 8px 18px rgba(0,0,0,.25) }
|
| 112 |
+
.answer-btn.disabled{ background:#aab4cc; color:#445; cursor:not-allowed; border-color:#889 }
|
| 113 |
+
.feedback{ margin-top:10px; font-size:18px; font-weight:800 }
|
| 114 |
+
|
| 115 |
+
.dd-overlay{
|
| 116 |
+
position: fixed; inset:0; display:none; align-items:center; justify-content:center;
|
| 117 |
+
z-index: 9999; pointer-events:none;
|
| 118 |
+
background: radial-gradient(60% 60% at 50% 50%, rgba(255,255,255,.08), rgba(0,0,0,.7));
|
| 119 |
+
}
|
| 120 |
+
.dd-overlay.active{ animation: dd-bg 1.2s ease-in-out 2; }
|
| 121 |
+
.dd-text{
|
| 122 |
+
font-size: clamp(40px, 7vw, 120px);
|
| 123 |
+
font-weight: 900; color:#ffe17a; letter-spacing:2px;
|
| 124 |
+
text-shadow:
|
| 125 |
+
0 0 12px rgba(255,225,122,.9),
|
| 126 |
+
0 0 40px rgba(255,225,122,.6),
|
| 127 |
+
0 6px 0 #000;
|
| 128 |
+
animation: flash 1.2s steps(2, jump-none) 2, wobble .9s ease-in-out 2;
|
| 129 |
+
}
|
| 130 |
+
@keyframes flash{ 0%,49%{opacity:1;filter:drop-shadow(0 0 24px rgba(255,225,122,.85));} 50%,100%{opacity:0;filter:none;} }
|
| 131 |
+
@keyframes wobble{ 0%{transform:scale(1) rotate(0)} 50%{transform:scale(1.06) rotate(-2deg)} 100%{transform:scale(1) rotate(0)} }
|
| 132 |
+
@keyframes dd-bg{ 0%{background: radial-gradient(40% 40% at 50% 50%, rgba(255,255,255,.12), rgba(0,0,0,.9));} 100%{background: radial-gradient(60% 60% at 50% 50%, rgba(255,255,255,.08), rgba(0,0,0,.7));} }
|
| 133 |
+
.flash-ring{ position:absolute; inset:-3px; border-radius:14px; pointer-events:none; box-shadow:0 0 0 3px rgba(255,225,122,.95), 0 0 30px 6px rgba(255,225,122,.6); animation: ring 1.2s ease-in-out 2; }
|
| 134 |
+
@keyframes ring{ 0%{opacity:1;transform:scale(1)} 50%{opacity:.1;transform:scale(.96)} 100%{opacity:1;transform:scale(1)} }
|
| 135 |
+
|
| 136 |
+
.daily-double-banner{ color:#b30000; font-weight:900; font-size:22px; margin:8px 0 }
|
| 137 |
+
|
| 138 |
+
#review{
|
| 139 |
+
width:min(1000px, 92vw);
|
| 140 |
+
margin:14px 0 24px 0;
|
| 141 |
+
padding:12px;
|
| 142 |
+
background:#fff;
|
| 143 |
+
border:1px solid #ccd3e0;
|
| 144 |
+
border-radius:12px;
|
| 145 |
+
box-shadow:0 6px 20px rgba(0,0,0,.08);
|
| 146 |
+
}
|
| 147 |
+
#review h3{ margin:0 0 8px 0; color:#0d2b6f }
|
| 148 |
+
.missed{ margin:8px 0; text-align:left }
|
| 149 |
+
.missed .q{ font-weight:700 }
|
| 150 |
+
.missed .a{ margin-left:8px }
|
| 151 |
</style>
|
| 152 |
</head>
|
| 153 |
<body>
|
|
|
|
| 189 |
"Private DNS"
|
| 190 |
];
|
| 191 |
|
| 192 |
+
// 15 questions (3 per category). Correct answers randomized at click time.
|
| 193 |
const questions = [
|
| 194 |
// Hub & Spoke
|
| 195 |
[
|
| 196 |
+
{ q: "What is the main benefit of a hub-and-spoke network in Azure?",
|
| 197 |
a: [
|
| 198 |
+
"Isolation of workloads with centralized security & management",
|
| 199 |
+
"Bypassing all security controls for faster internet access",
|
| 200 |
+
"Eliminating the need for subnets entirely"
|
| 201 |
], correct: 0 },
|
| 202 |
{ q: "Which network typically hosts shared services like Azure Firewall and DNS?",
|
| 203 |
a: [
|
| 204 |
"Hub network",
|
| 205 |
"Spoke network",
|
| 206 |
+
"Frontend subnet"
|
| 207 |
], correct: 0 },
|
| 208 |
+
{ q: "Why use VNet peering between hub and spoke?",
|
| 209 |
a: [
|
| 210 |
+
"Secure communication between shared and application networks",
|
| 211 |
+
"Automatic public exposure of private endpoints",
|
| 212 |
+
"To disable routing between subnets"
|
| 213 |
], correct: 0 }
|
| 214 |
],
|
| 215 |
// ASG & NSG
|
| 216 |
[
|
| 217 |
+
{ q: "What does an Application Security Group (ASG) help you do?",
|
| 218 |
a: [
|
| 219 |
+
"Group similar VMs to simplify rule management",
|
| 220 |
+
"Create DNS zones for private name resolution",
|
| 221 |
+
"Encrypt disks at rest automatically"
|
| 222 |
], correct: 0 },
|
| 223 |
+
{ q: "What is the primary role of a Network Security Group (NSG)?",
|
| 224 |
a: [
|
| 225 |
+
"Filter inbound/outbound traffic with allow/deny rules",
|
| 226 |
+
"Provide deep packet inspection and threat intel",
|
| 227 |
+
"Host a reverse proxy for web apps"
|
| 228 |
], correct: 0 },
|
| 229 |
+
{ q: "Why combine ASGs and NSGs on subnets and NICs?",
|
| 230 |
a: [
|
| 231 |
+
"Fine-grained segmentation and blocking of unauthorized tier-to-tier access",
|
| 232 |
+
"To increase public DNS response times",
|
| 233 |
+
"To bypass route tables"
|
| 234 |
], correct: 0 }
|
| 235 |
],
|
| 236 |
// Firewall
|
| 237 |
[
|
| 238 |
+
{ q: "Where is Azure Firewall placed in this design?",
|
| 239 |
a: [
|
| 240 |
"In the hub virtual network",
|
| 241 |
+
"Inside every spoke VM",
|
| 242 |
+
"In a public subnet of each spoke"
|
| 243 |
], correct: 0 },
|
| 244 |
+
{ q: "What is a typical use of Azure Firewall application rules?",
|
| 245 |
a: [
|
| 246 |
+
"Permit outbound FQDNs (e.g., Azure DevOps) for updates",
|
| 247 |
+
"Create A-records for VM names",
|
| 248 |
+
"Auto-scale a VM scale set"
|
| 249 |
], correct: 0 },
|
| 250 |
+
{ q: "Why centralize protection with Azure Firewall?",
|
| 251 |
a: [
|
| 252 |
+
"Deep inspection, threat intelligence, logging, and compliance",
|
| 253 |
+
"To remove the need for any NSGs",
|
| 254 |
+
"To disable hub-and-spoke peering"
|
| 255 |
], correct: 0 }
|
| 256 |
],
|
| 257 |
// Routing
|
| 258 |
[
|
| 259 |
+
{ q: "What Azure feature forces outbound traffic to pass through the firewall?",
|
| 260 |
a: [
|
| 261 |
"User-defined route (UDR) tables",
|
| 262 |
+
"Private endpoint policies",
|
| 263 |
+
"NSG flow logs"
|
| 264 |
], correct: 0 },
|
| 265 |
+
{ q: "Where should the route table be associated to enforce inspection?",
|
| 266 |
a: [
|
| 267 |
"Frontend and backend subnets",
|
| 268 |
+
"Only the hub VNet",
|
| 269 |
+
"The Private DNS zone"
|
| 270 |
], correct: 0 },
|
| 271 |
+
{ q: "Why route all outbound traffic via the firewall?",
|
| 272 |
a: [
|
| 273 |
+
"Consistent policy enforcement and full visibility of egress",
|
| 274 |
+
"To speed up public internet browsing",
|
| 275 |
+
"To bypass Azure Policy"
|
| 276 |
], correct: 0 }
|
| 277 |
],
|
| 278 |
// Private DNS
|
| 279 |
[
|
| 280 |
{ q: "What is the purpose of a Private DNS Zone?",
|
| 281 |
a: [
|
| 282 |
+
"Internal name resolution without public exposure",
|
| 283 |
+
"Hosting WAF policies",
|
| 284 |
+
"Publicly advertising internal hostnames"
|
| 285 |
], correct: 0 },
|
| 286 |
+
{ q: "Which record type is commonly used to resolve VM names internally?",
|
| 287 |
a: [
|
| 288 |
"A-record",
|
| 289 |
"MX-record",
|
| 290 |
+
"NS-record"
|
| 291 |
], correct: 0 },
|
| 292 |
+
{ q: "Why link the Private DNS Zone to the VNet (and enable auto-registration)?",
|
| 293 |
a: [
|
| 294 |
+
"To allow internal name resolution and automatic record management",
|
| 295 |
+
"To publish records to the public internet",
|
| 296 |
"To disable DNS forwarding"
|
| 297 |
], correct: 0 }
|
| 298 |
]
|
|
|
|
| 300 |
|
| 301 |
let score = 0, answered = 0;
|
| 302 |
const total = 15;
|
| 303 |
+
|
| 304 |
const board = document.getElementById("game-board"),
|
| 305 |
qdisp = document.getElementById("question-display"),
|
| 306 |
sdisp = document.getElementById("score"),
|
|
|
|
| 309 |
const ddAudio = document.getElementById("dd-audio");
|
| 310 |
const shuffleRegistry = new Map();
|
| 311 |
const missedQuestions = [];
|
| 312 |
+
|
| 313 |
+
// Random Daily Double position
|
| 314 |
const dailyDouble = { col: Math.floor(Math.random() * 5), row: Math.floor(Math.random() * 3) };
|
| 315 |
+
|
| 316 |
document.getElementById('reset-btn').addEventListener('click', () => location.reload());
|
| 317 |
|
| 318 |
function createBoard() {
|
| 319 |
+
// Inject 3 rows x 5 cols of value cards
|
| 320 |
for (let row = 0; row < 3; row++) {
|
| 321 |
for (let col = 0; col < 5; col++) {
|
| 322 |
const card = document.createElement("div");
|
|
|
|
| 339 |
board.appendChild(card);
|
| 340 |
}
|
| 341 |
}
|
| 342 |
+
// Reveal after injection to avoid flash
|
| 343 |
document.querySelector('.stage').style.visibility = 'visible';
|
| 344 |
}
|
| 345 |
|
|
|
|
| 359 |
const ring = document.createElement("div");
|
| 360 |
ring.className = "flash-ring";
|
| 361 |
card.appendChild(ring);
|
| 362 |
+
if (ddAudio) { try { ddAudio.currentTime = 0; ddAudio.play().catch(()=>{}); } catch(e){} }
|
|
|
|
| 363 |
ddOverlay.classList.add('active');
|
| 364 |
ddOverlay.style.display = "flex";
|
| 365 |
setTimeout(() => {
|
|
|
|
| 373 |
function showQuestion(categoryIndex, difficulty, card, isDailyDouble){
|
| 374 |
const q = questions[categoryIndex][difficulty];
|
| 375 |
const options = q.a.map((text, origIdx) => ({ text, origIdx }));
|
| 376 |
+
// Shuffle choices so the correct answer position is random each time
|
| 377 |
for (let i = options.length - 1; i > 0; i--) {
|
| 378 |
const j = Math.floor(Math.random() * (i + 1));
|
| 379 |
[options[i], options[j]] = [options[j], options[i]];
|
|
|
|
| 381 |
const shuffledCorrectIndex = options.findIndex(o => o.origIdx === q.correct);
|
| 382 |
const key = `${categoryIndex}-${difficulty}`;
|
| 383 |
shuffleRegistry.set(key, { shuffledCorrectIndex, isDailyDouble });
|
| 384 |
+
|
| 385 |
const dailyDoubleBanner = isDailyDouble ? `<div class="daily-double-banner">🎉 DAILY DOUBLE! 🎉</div>` : "";
|
| 386 |
qdisp.innerHTML = `
|
| 387 |
${dailyDoubleBanner}
|
|
|
|
| 393 |
).join("")}
|
| 394 |
</div>
|
| 395 |
`;
|
| 396 |
+
|
| 397 |
card.classList.add("disabled");
|
| 398 |
document.querySelectorAll('.answer-btn').forEach(btn => {
|
| 399 |
btn.addEventListener('click', () => {
|
|
|
|
| 411 |
const isCorrect = (chosenShuffledIndex === reg.shuffledCorrectIndex);
|
| 412 |
let val = (diff + 1) * 100;
|
| 413 |
if (reg.isDailyDouble) val *= 2;
|
| 414 |
+
|
| 415 |
document.querySelectorAll(".answer-btn").forEach(b => {
|
| 416 |
b.disabled = true; b.classList.add("disabled");
|
| 417 |
});
|
| 418 |
+
|
| 419 |
if (isCorrect) {
|
| 420 |
score += val;
|
| 421 |
qdisp.innerHTML += `<p class="feedback" style="color:green;">✅ Correct! +$${val.toLocaleString()}</p>`;
|
|
|
|
| 433 |
correctAnswer: correctText
|
| 434 |
});
|
| 435 |
}
|
| 436 |
+
|
| 437 |
sdisp.textContent = `Score: ${score}`;
|
| 438 |
if (++answered === total) endGame();
|
| 439 |
}
|
|
|
|
| 456 |
}
|
| 457 |
}
|
| 458 |
|
| 459 |
+
// Build board on load
|
| 460 |
createBoard();
|
| 461 |
</script>
|
| 462 |
</body>
|