// Game State Management
viewof gameState = {
const container = html`<div id="game-container"></div>`;
// All scenario-protocol pairs with explanations
const allScenarios = [
// Power constraints
{
id: 1,
scenario: "Battery-powered sensor sending small temperature data every hour over 5km range",
correctProtocol: "LoRaWAN",
explanation: "LoRaWAN excels at long-range (2-15km), low-power applications with small, infrequent data transmissions. The ultra-low power consumption enables 5-10 year battery life.",
category: "Power + Long Range",
difficulty: "easy"
},
{
id: 2,
scenario: "Fitness tracker syncing step count to smartphone throughout the day",
correctProtocol: "BLE",
explanation: "Bluetooth Low Energy is perfect for wearables: ultra-low power, smartphone compatible, short range (10-100m), and optimized for small periodic data transfers.",
category: "Power + Wearables",
difficulty: "easy"
},
{
id: 3,
scenario: "Smart watch with heart rate monitor needing 2+ week battery life",
correctProtocol: "BLE",
explanation: "BLE's ultra-low power consumption (typically <15mA peak) enables weeks of operation on small batteries while maintaining continuous smartphone connectivity.",
category: "Power + Wearables",
difficulty: "medium"
},
{
id: 4,
scenario: "Water meter in remote rural area reporting daily readings for 10 years without maintenance",
correctProtocol: "Sigfox",
explanation: "Sigfox offers extreme range (up to 50km), minimal power consumption, and is designed for simple telemetry with 140 uplink messages per day - perfect for utility meters.",
category: "Power + Remote",
difficulty: "hard"
},
// Real-time requirements
{
id: 5,
scenario: "Industrial robot arm requiring <10ms response time for safety interlocks",
correctProtocol: "5G",
explanation: "5G's Ultra-Reliable Low-Latency Communication (URLLC) delivers <1ms latency with 99.999% reliability - essential for time-critical industrial automation and safety systems.",
category: "Real-time + Industrial",
difficulty: "medium"
},
{
id: 6,
scenario: "Home automation hub receiving instant push notifications from cloud",
correctProtocol: "MQTT",
explanation: "MQTT's publish-subscribe model with persistent connections enables instant push delivery. QoS levels ensure reliable message delivery for smart home events.",
category: "Real-time + Smart Home",
difficulty: "easy"
},
{
id: 7,
scenario: "Live dashboard displaying factory sensor data with sub-second updates",
correctProtocol: "WebSocket",
explanation: "WebSocket maintains persistent bi-directional connections, eliminating HTTP polling overhead. Ideal for real-time dashboards requiring continuous data streams.",
category: "Real-time + Monitoring",
difficulty: "medium"
},
{
id: 8,
scenario: "Autonomous vehicle sharing position data with nearby vehicles",
correctProtocol: "5G",
explanation: "5G V2X (Vehicle-to-Everything) provides ultra-low latency (<10ms), high reliability, and supports high-speed mobility - critical for autonomous vehicle coordination.",
category: "Real-time + Automotive",
difficulty: "hard"
},
// Constrained devices
{
id: 9,
scenario: "Microcontroller with 64KB RAM communicating with cloud over unreliable cellular network",
correctProtocol: "CoAP",
explanation: "CoAP is specifically designed for constrained devices: tiny code footprint, UDP-based (minimal overhead), built-in retransmission, and REST-like simplicity.",
category: "Constrained + Cloud",
difficulty: "medium"
},
{
id: 10,
scenario: "IoT device fleet requiring remote firmware updates and configuration management",
correctProtocol: "LwM2M",
explanation: "LwM2M (Lightweight M2M) is built on CoAP and provides standardized device management: firmware updates, configuration, monitoring, and bootstrap for constrained devices.",
category: "Constrained + Management",
difficulty: "hard"
},
{
id: 11,
scenario: "Tiny sensor node with 10KB code space sending data to local gateway",
correctProtocol: "CoAP",
explanation: "CoAP implementations can fit in <10KB, use UDP for minimal overhead, and provide reliable delivery with confirmable messages - ideal for resource-constrained sensors.",
category: "Constrained + Local",
difficulty: "medium"
},
// Mesh networking
{
id: 12,
scenario: "100+ light switches and dimmers throughout a large office building",
correctProtocol: "Zigbee",
explanation: "Zigbee's self-healing mesh supports 65,000+ nodes, extends range through relay nodes, and has mature lighting profiles (Zigbee Light Link). Low power enables battery-powered switches.",
category: "Mesh + Building",
difficulty: "easy"
},
{
id: 13,
scenario: "Smart home devices from different manufacturers needing seamless interoperability",
correctProtocol: "Thread",
explanation: "Thread is IPv6-native and forms the foundation of Matter standard. Mesh networking, no single point of failure, and vendor-agnostic interoperability through standard IP.",
category: "Mesh + Interoperability",
difficulty: "medium"
},
{
id: 14,
scenario: "Home security system with battery-powered door/window sensors and alarm panel",
correctProtocol: "Z-Wave",
explanation: "Z-Wave operates on sub-GHz frequencies (less interference than 2.4GHz), has excellent building penetration, strong security ecosystem, and proven reliability for security applications.",
category: "Mesh + Security",
difficulty: "hard"
},
// Long range
{
id: 15,
scenario: "Agricultural soil sensors spread across 500-acre farm with no cellular coverage",
correctProtocol: "LoRaWAN",
explanation: "LoRaWAN reaches 15+ km in rural areas, operates on unlicensed spectrum (no cellular needed), and sensors can run for years on batteries. Star-of-stars topology simplifies deployment.",
category: "Long Range + Agriculture",
difficulty: "easy"
},
{
id: 16,
scenario: "Smart parking sensors deployed throughout a city with guaranteed cellular coverage",
correctProtocol: "NB-IoT",
explanation: "NB-IoT leverages existing cellular infrastructure, provides deep indoor/underground coverage, handles stationary devices efficiently, and offers carrier-grade security and reliability.",
category: "Long Range + Urban",
difficulty: "medium"
},
{
id: 17,
scenario: "Fleet tracking devices on delivery trucks needing voice capability for driver communication",
correctProtocol: "LTE-M",
explanation: "LTE-M (Cat-M1) uniquely supports VoLTE voice in addition to data. Better mobility handling than NB-IoT, higher bandwidth, and reliable coverage on highways.",
category: "Long Range + Mobile",
difficulty: "hard"
},
// High bandwidth
{
id: 18,
scenario: "Security camera streaming 1080p video to local NVR",
correctProtocol: "Wi-Fi",
explanation: "Wi-Fi provides 50-300+ Mbps throughput needed for HD video streaming. Power via PoE available with Wi-Fi access points. Ubiquitous infrastructure support.",
category: "High Bandwidth + Video",
difficulty: "easy"
},
{
id: 19,
scenario: "AR glasses streaming 4K content with minimal latency for immersive experience",
correctProtocol: "5G",
explanation: "5G's enhanced Mobile Broadband (eMBB) delivers 1-10 Gbps with <10ms latency. Network slicing can guarantee bandwidth for AR/VR applications.",
category: "High Bandwidth + AR/VR",
difficulty: "medium"
},
{
id: 20,
scenario: "Industrial machine vision system analyzing high-resolution images at production line speed",
correctProtocol: "Ethernet",
explanation: "Industrial Ethernet provides deterministic timing, 1-10 Gbps bandwidth, and highest reliability. No interference concerns. Time-Sensitive Networking (TSN) ensures real-time performance.",
category: "High Bandwidth + Industrial",
difficulty: "hard"
},
// Special cases
{
id: 21,
scenario: "Contactless payment terminal at retail checkout",
correctProtocol: "NFC",
explanation: "NFC provides instant connection at touch range (<4cm), inherent security through proximity, and universal smartphone/card support. ISO 14443 ensures payment standard compliance.",
category: "Payment + Touch",
difficulty: "easy"
},
{
id: 22,
scenario: "Warehouse tracking 10,000+ inventory items with passive tags",
correctProtocol: "UHF RFID",
explanation: "UHF RFID tags cost <$0.10, require no battery, and readers can scan hundreds of tags simultaneously at up to 12m range. Perfect for high-volume asset tracking.",
category: "Tracking + Passive",
difficulty: "medium"
},
{
id: 23,
scenario: "Critical hospital patient monitors requiring zero packet loss and guaranteed bandwidth",
correctProtocol: "Ethernet",
explanation: "Wired Ethernet eliminates wireless interference risks, provides guaranteed bandwidth, and offers highest reliability. PoE simplifies medical device power. Essential for life-critical applications.",
category: "Healthcare + Critical",
difficulty: "medium"
},
{
id: 24,
scenario: "Sensor nodes forming ad-hoc network in disaster response scenario without infrastructure",
correctProtocol: "Thread",
explanation: "Thread's mesh automatically forms without central infrastructure, self-heals when nodes fail, and provides IPv6 end-to-end connectivity. No gateway single-point-of-failure.",
category: "Ad-hoc + Emergency",
difficulty: "hard"
}
];
// Protocol options for different difficulties
const protocolSets = {
easy: ["LoRaWAN", "BLE", "MQTT", "Wi-Fi", "Zigbee", "NFC"],
medium: ["LoRaWAN", "BLE", "MQTT", "Wi-Fi", "Zigbee", "NFC", "CoAP", "NB-IoT", "Thread", "5G"],
hard: ["LoRaWAN", "BLE", "MQTT", "Wi-Fi", "Zigbee", "NFC", "CoAP", "NB-IoT", "Thread", "5G", "LTE-M", "Sigfox", "LwM2M", "Z-Wave", "WebSocket", "UHF RFID", "Ethernet"]
};
// Game configuration
let state = {
difficulty: "easy",
currentRound: 0,
totalRounds: 5,
score: 0,
timeRemaining: 0,
timerInterval: null,
scenarios: [],
currentScenario: null,
selectedProtocol: null,
answers: [],
gamePhase: "setup", // setup, playing, feedback, complete
streak: 0,
bestStreak: 0
};
// Time limits per difficulty (in seconds)
const timeLimits = { easy: 30, medium: 25, hard: 20 };
const questionsPerDifficulty = { easy: 5, medium: 7, hard: 10 };
// Fisher-Yates shuffle
function shuffle(array) {
const arr = [...array];
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
// Get scenarios for current difficulty
function getScenariosForDifficulty(diff) {
let filtered;
if (diff === "easy") {
filtered = allScenarios.filter(s => s.difficulty === "easy");
} else if (diff === "medium") {
filtered = allScenarios.filter(s => s.difficulty === "easy" || s.difficulty === "medium");
} else {
filtered = allScenarios;
}
return shuffle(filtered).slice(0, questionsPerDifficulty[diff]);
}
// Get protocol options for current question
function getProtocolOptions(scenario, diff) {
const correct = scenario.correctProtocol;
let pool = protocolSets[diff].filter(p => p !== correct);
const numOptions = diff === "easy" ? 2 : diff === "medium" ? 4 : 6;
const wrongOptions = shuffle(pool).slice(0, numOptions - 1);
return shuffle([correct, ...wrongOptions]);
}
// Start timer
function startTimer() {
state.timeRemaining = timeLimits[state.difficulty];
if (state.timerInterval) clearInterval(state.timerInterval);
state.timerInterval = setInterval(() => {
state.timeRemaining--;
render();
if (state.timeRemaining <= 0) {
clearInterval(state.timerInterval);
handleTimeout();
}
}, 1000);
}
// Stop timer
function stopTimer() {
if (state.timerInterval) {
clearInterval(state.timerInterval);
state.timerInterval = null;
}
}
// Handle timeout
function handleTimeout() {
state.answers.push({
scenario: state.currentScenario,
selected: null,
correct: state.currentScenario.correctProtocol,
isCorrect: false,
timedOut: true
});
state.streak = 0;
state.gamePhase = "feedback";
state.selectedProtocol = null;
render();
}
// Handle protocol selection
function selectProtocol(protocol) {
stopTimer();
state.selectedProtocol = protocol;
const isCorrect = protocol === state.currentScenario.correctProtocol;
// Calculate score with time bonus
let pointsEarned = 0;
if (isCorrect) {
const basePoints = state.difficulty === "easy" ? 10 : state.difficulty === "medium" ? 15 : 20;
const timeBonus = Math.floor(state.timeRemaining * 0.5);
const streakBonus = state.streak * 2;
pointsEarned = basePoints + timeBonus + streakBonus;
state.score += pointsEarned;
state.streak++;
if (state.streak > state.bestStreak) state.bestStreak = state.streak;
} else {
state.streak = 0;
}
state.answers.push({
scenario: state.currentScenario,
selected: protocol,
correct: state.currentScenario.correctProtocol,
isCorrect: isCorrect,
pointsEarned: pointsEarned,
timeRemaining: state.timeRemaining
});
state.gamePhase = "feedback";
render();
}
// Move to next question
function nextQuestion() {
state.currentRound++;
if (state.currentRound >= state.scenarios.length) {
state.gamePhase = "complete";
render();
return;
}
state.currentScenario = state.scenarios[state.currentRound];
state.selectedProtocol = null;
state.gamePhase = "playing";
startTimer();
render();
}
// Start game
function startGame(difficulty) {
state = {
difficulty: difficulty,
currentRound: 0,
totalRounds: questionsPerDifficulty[difficulty],
score: 0,
timeRemaining: timeLimits[difficulty],
timerInterval: null,
scenarios: getScenariosForDifficulty(difficulty),
currentScenario: null,
selectedProtocol: null,
answers: [],
gamePhase: "playing",
streak: 0,
bestStreak: 0
};
state.currentScenario = state.scenarios[0];
startTimer();
render();
}
// Reset game
function resetGame() {
stopTimer();
state = {
difficulty: "easy",
currentRound: 0,
totalRounds: 5,
score: 0,
timeRemaining: 0,
timerInterval: null,
scenarios: [],
currentScenario: null,
selectedProtocol: null,
answers: [],
gamePhase: "setup",
streak: 0,
bestStreak: 0
};
render();
}
// Render function
function render() {
const difficultyColors = {
easy: "#27AE60",
medium: "#E67E22",
hard: "#E74C3C"
};
if (state.gamePhase === "setup") {
container.innerHTML = `
<div style="background: linear-gradient(135deg, #2C3E50 0%, #1A252F 100%); padding: 30px; border-radius: 15px; color: white; text-align: center;">
<h3 style="margin-top: 0; font-size: 1.8em;">Protocol Matching Game</h3>
<p style="font-size: 1.1em; opacity: 0.9; margin-bottom: 25px;">Test your IoT protocol knowledge! Match real-world scenarios to the best protocol.</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 30px 0;">
<div style="background: rgba(39, 174, 96, 0.2); padding: 20px; border-radius: 12px; border: 2px solid #27AE60; cursor: pointer; transition: transform 0.2s;"
onmouseover="this.style.transform='scale(1.05)'"
onmouseout="this.style.transform='scale(1)'"
onclick="window.startProtocolGame('easy')">
<h4 style="color: #27AE60; margin: 0 0 10px 0;">Easy Mode</h4>
<p style="margin: 0; font-size: 0.9em;">5 questions, 2 choices each<br/>30 seconds per question</p>
</div>
<div style="background: rgba(230, 126, 34, 0.2); padding: 20px; border-radius: 12px; border: 2px solid #E67E22; cursor: pointer; transition: transform 0.2s;"
onmouseover="this.style.transform='scale(1.05)'"
onmouseout="this.style.transform='scale(1)'"
onclick="window.startProtocolGame('medium')">
<h4 style="color: #E67E22; margin: 0 0 10px 0;">Medium Mode</h4>
<p style="margin: 0; font-size: 0.9em;">7 questions, 4 choices each<br/>25 seconds per question</p>
</div>
<div style="background: rgba(231, 76, 60, 0.2); padding: 20px; border-radius: 12px; border: 2px solid #E74C3C; cursor: pointer; transition: transform 0.2s;"
onmouseover="this.style.transform='scale(1.05)'"
onmouseout="this.style.transform='scale(1)'"
onclick="window.startProtocolGame('hard')">
<h4 style="color: #E74C3C; margin: 0 0 10px 0;">Hard Mode</h4>
<p style="margin: 0; font-size: 0.9em;">10 questions, 6 choices each<br/>20 seconds per question</p>
</div>
</div>
<div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px; margin-top: 20px;">
<strong>Scoring:</strong> Base points + Time bonus + Streak bonus
</div>
</div>
`;
} else if (state.gamePhase === "playing") {
const options = getProtocolOptions(state.currentScenario, state.difficulty);
const timerColor = state.timeRemaining > 10 ? "#27AE60" : state.timeRemaining > 5 ? "#E67E22" : "#E74C3C";
container.innerHTML = `
<div style="background: #f8f9fa; padding: 25px; border-radius: 15px; border: 2px solid #2C3E50;">
<!-- Header -->
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; flex-wrap: wrap; gap: 10px;">
<div style="display: flex; align-items: center; gap: 15px;">
<span style="background: #2C3E50; color: white; padding: 8px 15px; border-radius: 20px; font-weight: bold;">
Question ${state.currentRound + 1}/${state.scenarios.length}
</span>
<span style="background: ${difficultyColors[state.difficulty]}; color: white; padding: 8px 15px; border-radius: 20px; text-transform: capitalize;">
${state.difficulty}
</span>
</div>
<div style="display: flex; align-items: center; gap: 15px;">
<span style="background: #16A085; color: white; padding: 8px 15px; border-radius: 20px;">
Score: ${state.score}
</span>
${state.streak > 1 ? `<span style="background: #9B59B6; color: white; padding: 8px 12px; border-radius: 20px; animation: pulse 1s infinite;">Streak: ${state.streak}x</span>` : ''}
<span style="background: ${timerColor}; color: white; padding: 8px 15px; border-radius: 20px; font-weight: bold; min-width: 50px; text-align: center;">
${state.timeRemaining}s
</span>
</div>
</div>
<!-- Progress bar -->
<div style="background: #e0e0e0; height: 6px; border-radius: 3px; margin-bottom: 25px; overflow: hidden;">
<div style="background: #16A085; height: 100%; width: ${((state.currentRound) / state.scenarios.length) * 100}%; transition: width 0.3s;"></div>
</div>
<!-- Scenario -->
<div style="background: linear-gradient(135deg, #2C3E50 0%, #1A252F 100%); padding: 25px; border-radius: 12px; color: white; margin-bottom: 25px;">
<div style="font-size: 0.85em; color: #16A085; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 1px;">
${state.currentScenario.category}
</div>
<p style="font-size: 1.15em; line-height: 1.6; margin: 0;">
${state.currentScenario.scenario}
</p>
</div>
<!-- Protocol Options -->
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 15px;">
${options.map(protocol => `
<button onclick="window.selectProtocolAnswer('${protocol}')"
style="background: white; border: 3px solid #2C3E50; padding: 20px; border-radius: 12px; font-size: 1.1em; font-weight: bold; color: #2C3E50; cursor: pointer; transition: all 0.2s;"
onmouseover="this.style.background='#2C3E50'; this.style.color='white'; this.style.transform='translateY(-3px)'; this.style.boxShadow='0 6px 20px rgba(0,0,0,0.15)'"
onmouseout="this.style.background='white'; this.style.color='#2C3E50'; this.style.transform='translateY(0)'; this.style.boxShadow='none'">
${protocol}
</button>
`).join('')}
</div>
</div>
<style>
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
</style>
`;
} else if (state.gamePhase === "feedback") {
const lastAnswer = state.answers[state.answers.length - 1];
const isCorrect = lastAnswer.isCorrect;
const timedOut = lastAnswer.timedOut;
container.innerHTML = `
<div style="background: ${isCorrect ? 'linear-gradient(135deg, #D5F4E6 0%, #A9DFBF 100%)' : 'linear-gradient(135deg, #FADBD8 0%, #F1948A 100%)'}; padding: 30px; border-radius: 15px; border: 3px solid ${isCorrect ? '#27AE60' : '#E74C3C'};">
<!-- Result Header -->
<div style="text-align: center; margin-bottom: 25px;">
<div style="font-size: 3em; margin-bottom: 10px;">${isCorrect ? '✔' : timedOut ? '⌛' : '✘'}</div>
<h3 style="margin: 0; color: ${isCorrect ? '#27AE60' : '#E74C3C'}; font-size: 1.8em;">
${isCorrect ? 'Correct!' : timedOut ? 'Time\'s Up!' : 'Not Quite Right'}
</h3>
${isCorrect ? `<p style="color: #27AE60; font-weight: bold; margin: 10px 0 0 0;">+${lastAnswer.pointsEarned} points (${lastAnswer.timeRemaining}s time bonus)</p>` : ''}
</div>
<!-- Scenario Recap -->
<div style="background: white; padding: 20px; border-radius: 10px; margin-bottom: 20px;">
<div style="font-size: 0.85em; color: #7F8C8D; margin-bottom: 8px; text-transform: uppercase;">Scenario</div>
<p style="margin: 0; color: #2C3E50; line-height: 1.5;">${state.currentScenario.scenario}</p>
</div>
<!-- Answer Details -->
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 20px;">
${!isCorrect && !timedOut ? `
<div style="background: rgba(231, 76, 60, 0.1); padding: 15px; border-radius: 10px; border: 2px solid #E74C3C;">
<div style="font-size: 0.85em; color: #E74C3C; margin-bottom: 5px;">Your Answer</div>
<div style="font-weight: bold; color: #E74C3C; font-size: 1.2em;">${lastAnswer.selected}</div>
</div>
` : ''}
<div style="background: rgba(39, 174, 96, 0.1); padding: 15px; border-radius: 10px; border: 2px solid #27AE60;">
<div style="font-size: 0.85em; color: #27AE60; margin-bottom: 5px;">Correct Answer</div>
<div style="font-weight: bold; color: #27AE60; font-size: 1.2em;">${state.currentScenario.correctProtocol}</div>
</div>
</div>
<!-- Explanation -->
<div style="background: #2C3E50; color: white; padding: 20px; border-radius: 10px; margin-bottom: 25px;">
<div style="font-size: 0.85em; color: #16A085; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 1px;">Why ${state.currentScenario.correctProtocol}?</div>
<p style="margin: 0; line-height: 1.6;">${state.currentScenario.explanation}</p>
</div>
<!-- Current Score -->
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 15px;">
<div style="display: flex; gap: 15px;">
<span style="background: #16A085; color: white; padding: 10px 20px; border-radius: 25px; font-weight: bold;">
Score: ${state.score}
</span>
<span style="background: #2C3E50; color: white; padding: 10px 20px; border-radius: 25px;">
${state.currentRound + 1}/${state.scenarios.length} Complete
</span>
</div>
<button onclick="window.nextProtocolQuestion()"
style="background: #16A085; color: white; border: none; padding: 12px 30px; border-radius: 25px; font-size: 1.1em; font-weight: bold; cursor: pointer; transition: background 0.2s;"
onmouseover="this.style.background='#138D75'"
onmouseout="this.style.background='#16A085'">
${state.currentRound + 1 < state.scenarios.length ? 'Next Question' : 'See Results'} →
</button>
</div>
</div>
`;
} else if (state.gamePhase === "complete") {
const correctCount = state.answers.filter(a => a.isCorrect).length;
const percentage = Math.round((correctCount / state.answers.length) * 100);
const grade = percentage >= 90 ? "A" : percentage >= 80 ? "B" : percentage >= 70 ? "C" : percentage >= 60 ? "D" : "F";
const gradeColor = percentage >= 80 ? "#27AE60" : percentage >= 60 ? "#E67E22" : "#E74C3C";
// Learning summary by category
const categoryStats = {};
state.answers.forEach(a => {
const cat = a.scenario.category;
if (!categoryStats[cat]) categoryStats[cat] = { correct: 0, total: 0 };
categoryStats[cat].total++;
if (a.isCorrect) categoryStats[cat].correct++;
});
container.innerHTML = `
<div style="background: linear-gradient(135deg, #2C3E50 0%, #1A252F 100%); padding: 30px; border-radius: 15px; color: white;">
<!-- Header -->
<div style="text-align: center; margin-bottom: 30px;">
<h3 style="margin: 0 0 10px 0; font-size: 2em;">Game Complete!</h3>
<p style="opacity: 0.8; margin: 0;">Here's how you did on ${state.difficulty} mode</p>
</div>
<!-- Score Summary -->
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 20px; margin-bottom: 30px;">
<div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 12px; text-align: center;">
<div style="font-size: 3em; font-weight: bold; color: ${gradeColor};">${grade}</div>
<div style="font-size: 0.9em; opacity: 0.8;">Grade</div>
</div>
<div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 12px; text-align: center;">
<div style="font-size: 2.5em; font-weight: bold; color: #16A085;">${state.score}</div>
<div style="font-size: 0.9em; opacity: 0.8;">Total Score</div>
</div>
<div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 12px; text-align: center;">
<div style="font-size: 2.5em; font-weight: bold;">${correctCount}/${state.answers.length}</div>
<div style="font-size: 0.9em; opacity: 0.8;">Correct (${percentage}%)</div>
</div>
<div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 12px; text-align: center;">
<div style="font-size: 2.5em; font-weight: bold; color: #9B59B6;">${state.bestStreak}</div>
<div style="font-size: 0.9em; opacity: 0.8;">Best Streak</div>
</div>
</div>
<!-- Category Breakdown -->
<div style="background: rgba(255,255,255,0.05); padding: 20px; border-radius: 12px; margin-bottom: 25px;">
<h4 style="margin: 0 0 15px 0; color: #16A085;">Performance by Category</h4>
<div style="display: grid; gap: 10px;">
${Object.entries(categoryStats).map(([cat, stats]) => `
<div style="display: flex; justify-content: space-between; align-items: center; padding: 10px; background: rgba(255,255,255,0.05); border-radius: 8px;">
<span>${cat}</span>
<span style="color: ${stats.correct === stats.total ? '#27AE60' : stats.correct > 0 ? '#E67E22' : '#E74C3C'}; font-weight: bold;">
${stats.correct}/${stats.total}
</span>
</div>
`).join('')}
</div>
</div>
<!-- Key Learning Points -->
<div style="background: rgba(22, 160, 133, 0.2); padding: 20px; border-radius: 12px; margin-bottom: 25px; border: 1px solid #16A085;">
<h4 style="margin: 0 0 15px 0; color: #16A085;">Key Learning Points</h4>
<ul style="margin: 0; padding-left: 20px; line-height: 1.8;">
<li><strong>Long range + Low power:</strong> LoRaWAN, Sigfox, NB-IoT are designed for battery-powered sensors over kilometers</li>
<li><strong>Real-time critical:</strong> 5G URLLC and wired Ethernet provide sub-10ms latency guarantees</li>
<li><strong>Constrained devices:</strong> CoAP and LwM2M minimize resource requirements for tiny MCUs</li>
<li><strong>Mesh networking:</strong> Zigbee, Thread, and Z-Wave enable self-healing networks without central infrastructure</li>
<li><strong>High bandwidth:</strong> Wi-Fi and 5G handle video and streaming; Ethernet for deterministic industrial needs</li>
</ul>
</div>
<!-- Missed Questions Review -->
${state.answers.filter(a => !a.isCorrect).length > 0 ? `
<div style="background: rgba(231, 76, 60, 0.1); padding: 20px; border-radius: 12px; margin-bottom: 25px; border: 1px solid rgba(231, 76, 60, 0.3);">
<h4 style="margin: 0 0 15px 0; color: #E74C3C;">Review Missed Questions</h4>
${state.answers.filter(a => !a.isCorrect).map(a => `
<div style="background: rgba(255,255,255,0.05); padding: 15px; border-radius: 8px; margin-bottom: 10px;">
<p style="margin: 0 0 10px 0; font-size: 0.95em; opacity: 0.9;">${a.scenario.scenario}</p>
<div style="display: flex; gap: 15px; flex-wrap: wrap; font-size: 0.9em;">
${a.selected ? `<span style="color: #E74C3C;">Your answer: ${a.selected}</span>` : '<span style="color: #E74C3C;">Timed out</span>'}
<span style="color: #27AE60;">Correct: ${a.correct}</span>
</div>
</div>
`).join('')}
</div>
` : `
<div style="background: rgba(39, 174, 96, 0.2); padding: 20px; border-radius: 12px; margin-bottom: 25px; border: 1px solid #27AE60; text-align: center;">
<span style="font-size: 2em;">🏆</span>
<h4 style="margin: 10px 0 0 0; color: #27AE60;">Perfect Score!</h4>
<p style="margin: 5px 0 0 0; opacity: 0.9;">You answered every question correctly. Excellent protocol knowledge!</p>
</div>
`}
<!-- Action Buttons -->
<div style="display: flex; justify-content: center; gap: 15px; flex-wrap: wrap;">
<button onclick="window.resetProtocolGame()"
style="background: transparent; color: white; border: 2px solid white; padding: 12px 30px; border-radius: 25px; font-size: 1em; cursor: pointer; transition: all 0.2s;"
onmouseover="this.style.background='white'; this.style.color='#2C3E50'"
onmouseout="this.style.background='transparent'; this.style.color='white'">
Change Difficulty
</button>
<button onclick="window.startProtocolGame('${state.difficulty}')"
style="background: #16A085; color: white; border: none; padding: 12px 30px; border-radius: 25px; font-size: 1em; font-weight: bold; cursor: pointer; transition: background 0.2s;"
onmouseover="this.style.background='#138D75'"
onmouseout="this.style.background='#16A085'">
Play Again
</button>
${state.difficulty !== "hard" ? `
<button onclick="window.startProtocolGame('${state.difficulty === 'easy' ? 'medium' : 'hard'}')"
style="background: #E67E22; color: white; border: none; padding: 12px 30px; border-radius: 25px; font-size: 1em; font-weight: bold; cursor: pointer; transition: background 0.2s;"
onmouseover="this.style.background='#D35400'"
onmouseout="this.style.background='#E67E22'">
Try ${state.difficulty === 'easy' ? 'Medium' : 'Hard'} Mode
</button>
` : ''}
</div>
</div>
`;
}
}
// Expose functions globally for onclick handlers
window.startProtocolGame = startGame;
window.selectProtocolAnswer = selectProtocol;
window.nextProtocolQuestion = nextQuestion;
window.resetProtocolGame = resetGame;
// Initial render
render();
return container;
}