diff --git a/python/controller.py b/python/controller.py index 72f9ccd..8413609 100644 --- a/python/controller.py +++ b/python/controller.py @@ -187,6 +187,8 @@ def status(): for address, state in sorted(serial_to_web.registered_modules.items(), key=(lambda kv: kv[0].as_binary())) ] + status_dict['max_allowed_strikes'] = web_to_serial.max_allowed_strikes + status_dict['game_duration'] = web_to_serial.game_duration.total_seconds() print(status_dict) return jsonify(status_dict) diff --git a/python/static/controller.html b/python/static/controller.html index 348b61f..5fe70e7 100644 --- a/python/static/controller.html +++ b/python/static/controller.html @@ -15,6 +15,12 @@
Game state:
+ +
+
Strikes
+
+
+
diff --git a/python/static/css/controller.css b/python/static/css/controller.css index f01a61a..30f1569 100644 --- a/python/static/css/controller.css +++ b/python/static/css/controller.css @@ -94,6 +94,27 @@ button:disabled { margin: 8px; } +#strikeContainer { + text-align: center; + height: 4em; +} + +#strikeContainer .strikeIndicator { + padding: 20px; + font-family: sans-serif; + font-weight: 900 +} + +#strikeContainer .inactive { + color: #ff000033; +} + +#strikeContainer .active { + color: #ff0000; +} + + + .solved { background-color: green !important; color: white !important; diff --git a/python/static/js/controller.js b/python/static/js/controller.js index 48f9d8f..b818206 100644 --- a/python/static/js/controller.js +++ b/python/static/js/controller.js @@ -24,6 +24,12 @@ const state = { // If the alarm has been played already alarmPlayed: false, + + // Number of mistakes allowed without exploding the bomb + maxAllowedStrikes: 0, + + // Time on the timer at the beginning of a game + gameDuration: 0 }; /** @@ -75,6 +81,22 @@ function updateModules(puzzles) { modulesElement.replaceChildren(...modules); } +function updateStrikes() { + const indicators = []; + for (let idx = 0; idx < state.maxAllowedStrikes; idx++) { + let indicator = document.createElement("span"); + indicator.classList.add('strikeIndicator'); + indicator.textContent = '!'; + if (idx < state.strikes) { + indicator.classList.add('active'); + } else { + indicator.classList.add('inactive'); + } + indicators.push(indicator); + } + document.getElementById("strikeIndicatorContainer").replaceChildren(...indicators); +} + /** * Initialize the game state. */ @@ -92,11 +114,13 @@ function updateGameState() { .then((data) => { // Update the game state state.gamestate = data.gamestate; + state.gameDuration = data.game_duration; + state.maxAllowedStrikes = data.max_allowed_strikes; document.getElementById("gamestate").innerHTML = state.gamestate; // Reset the strike amount if the game is not running anymore. - if (state.gamestate != "GAME") { - state.strikeAmount = 0; + if (state.gamestate === "INACTIVE") { + state.strikes = 0; state.alarmPlayed = false; } @@ -111,19 +135,17 @@ function updateGameState() { .map((p) => p.strikes) .reduce((a, b) => a + b, 0); - // Play a "buzzer" sound when a strike is made. + // Play a "buzzer" sound when a strike is made and update total amount of strikes. if (state.strikes < newStrikes) { playSound(sounds.strike); + state.strikes = newStrikes; } - // Play a "alarm" sound when the time is at 10 seconds. + // Play a "alarm" sound when the time is at 20 seconds. if (data.timeleft <= 20 && data.timeleft > 19 && !state.alarmPlayed) { playSound(sounds.alarm); state.alarmPlayed = true; } - - // Update the total amount of strikes - state.strikes = newStrikes; } // Update the start/restart button visibility. @@ -134,6 +156,7 @@ function updateGameState() { // Update the modules updateModules(data.puzzles); + updateStrikes(); }); } @@ -173,23 +196,12 @@ function initializeSegmentDisplay() { setInterval(updateSegmentDisplay, 100); } -/** - * Update the segment display with the latest game data. - */ -function updateSegmentDisplay() { - // Do not update the timer when the game is not running. - if (state.gamestate != "GAME" || !state.estimatedTimeout) { - return; - } - - const timeLeft = (state.estimatedTimeout - new Date()) / 1000; - +function setTimeleft(timeLeft) { // Set the display to 0 when there is no time left. - if (timeLeft <= 0) { + if (isNaN(timeLeft) || timeLeft <= 0) { display.setValue("00:00.0"); return; } - const integral = Math.floor(timeLeft); const fractional = timeLeft - integral; const minutes = Math.floor(integral / 60); @@ -202,6 +214,22 @@ function updateSegmentDisplay() { display.setValue(`${minutesStr}:${secondsStr}.${fractionalStr}`); } +/** + * Update the segment display with the latest game data. + */ +function updateSegmentDisplay() { + console.log(state.gamestate); + if (state.gamestate === "INACTIVE" || state.gamestate === "INFO" || state.gamestate === "DISCOVER") { + setTimeleft(state.gameDuration); + } + // Do not update the timer when the game is not running. + else if (state.gamestate != "GAME" || !state.estimatedTimeout) { + return; + } else { + setTimeleft((state.estimatedTimeout - new Date()) / 1000); + } +} + /** * When the window is loaded. */