diff --git a/data_src/index.html b/data_src/index.html index edeed5b7104e84e7135f1e8a1318bbbf5604384f..d44984476ab0624284db1748bfcd6878c8f9ab73 100644 --- a/data_src/index.html +++ b/data_src/index.html @@ -26,12 +26,12 @@ <div class="row mb-3 px-3"> <label for="rangeBrightness" class="col-form-label">Brightness</label> - <input type="range" class="form-range rangeConfig" id="rangeBrightness" data-name="brightness" min="1" + <input type="range" class="form-range rangeSetting" id="rangeBrightness" data-name="brightness" min="1" max="100" steps="1" disabled> </div> <div class="row mb-3 px-3"> <label for="rangeSpeed" class="col-form-label">Speed</label> - <input type="range" class="form-range rangeConfig" id="rangeSpeed" data-name="interval" min="1" max="100" + <input type="range" class="form-range rangeSetting" id="rangeSpeed" data-name="interval" min="1" max="100" steps="10" disabled> </div> <div class="row mb-3 px-3 mt-5"> @@ -44,10 +44,8 @@ <div class="card-footer text-body-secondary py-3"> <div class="row"> <div class="col-6"> - <button type="button" class="btn btn-sm btn-success btnAction" data-action="load" disabled>Load</button> - </div> - <div class="col-6"> - <button type="button" class="btn btn-sm btn-success btnAction" data-action="save" disabled>Save</button> + <button type="button" class="btn btn-sm btn-success btnAction me-1" data-action="loadSettings" disabled>Load</button> + <button type="button" class="btn btn-sm btn-success btnAction" data-action="saveSettings" disabled>Save</button> </div> <div class="col-6 text-end"> <button type="button" class="btn btn-sm btn-outline-secondary me-1" data-bs-toggle="collapse" @@ -70,7 +68,7 @@ </h5> <div class="card-body"> - <textarea id="wsMessages" class="form-control mb-2 font-monospace" rows="10"></textarea> + <textarea id="wsMessages" class="form-control mb-2 font-monospace" rows="10" spellcheck="false"></textarea> <div id="input_div"> <div class="input-group"> <span class="input-group-text">➡️</span> diff --git a/data_src/main.js b/data_src/main.js index 28d07bfa600650c2e2f9a38388afda4ab833cc04..fe3f97b492c1afa1af45181da71b74b264686ecd 100644 --- a/data_src/main.js +++ b/data_src/main.js @@ -1,6 +1,6 @@ var wsUrl = 'ws://' + document.location.host + '/ws'; -var config = { +var settings = { brightness: 0, interval: 0, }; @@ -11,7 +11,7 @@ $(document).ready(function () { startSocket(); }); -$(document).on('input change', '.rangeConfig', function () { +$(document).on('change', '.rangeSetting', function () { var setting = $(this).data('name'); var value = $(this).val(); switch (setting) { @@ -22,38 +22,35 @@ $(document).on('input change', '.rangeConfig', function () { value = mapRange(value, 1, 100, 1000, 10); break; } - config[setting] = value; + settings[setting] = value; var payload = { - 'config': config + 'settings': settings }; wsSend(payload); }); $('.btnAction').on('click', function () { var action = $(this).data('action'); - var payload = { - 'action': action - }; - wsSend(payload); + sendAction(action); }); -$("#prompt").onkeydown = function (e) { - if (e.keyCode == 13 && $("#prompt").value != "") { +$("#prompt").on('keydown', function (e) { + if (e.keyCode == 13 && $("#prompt").val() != "") { var val = $("#prompt").val(); - wsSend(val); + wsSend(JSON.parse(val)); $("#prompt").val(""); } -} +}); function uiEnabled(enabled) { if (enabled) { $('#spinner').addClass('invisible'); $('.btnAction').prop('disabled', false); - $('.rangeConfig').prop('disabled', false); + $('.rangeSetting').prop('disabled', false); } else { $('#spinner').removeClass('invisible'); $('.btnAction').prop('disabled', true); - $('.rangeConfig').prop('disabled', true); + $('.rangeSetting').prop('disabled', true); } } @@ -63,14 +60,11 @@ function addMessage(msg) { txt.scrollTop(txt[0].scrollHeight - txt.height()); } -function wsSend(msg) { - uiEnabled(false); - var json = JSON.stringify(msg); - ws.send(msg).done(function () { - addMessage("➡️ " + json); - uiEnabled(true); +function wsSend(obj) { + ws.send(obj).done(function () { + addMessage("➡️ " + JSON.stringify(obj)); }).fail(function (e) { - addMessage("❗️ " + json); + addMessage("❗️ " + JSON.stringify(obj)); }); } @@ -78,11 +72,13 @@ var ws; function startSocket() { ws = $.simpleWebSocket({ url: wsUrl, + timeout: 5000, onOpen: function (e) { - console.log("WS: connected"); - addMessage("✅ connected"); - getConfig(); - uiEnabled(true); + ws.isConnected(function (connected) { + console.log("WS: connected"); + addMessage("✅ connected"); + sendAction('getSettings'); + }); }, onClose: function (e) { uiEnabled(false); @@ -97,28 +93,27 @@ function startSocket() { }); ws.listen(function (data) { - console.log(data); - var json = JSON.stringify(data); - var msg = "⬅️ " + json; - addMessage(msg); - if (data.config) { - loadConfig(data.config); + addMessage("⬅️ " + JSON.stringify(data)); + if (data.settings) { + loadSettings(data.settings); } }); } -function getConfig() { +function sendAction(action) { var payload = { - 'action': 'load' + 'action': action }; wsSend(payload); } -function loadConfig(data) { - config.brightness = data.brightness; - config.interval = data.interval; +function loadSettings(data) { + settings.brightness = data.brightness; + settings.interval = data.interval; + uiEnabled(true); $('#rangeBrightness').val(mapRange(data.brightness, 8, 255, 1, 100)); $('#rangeSpeed').val(mapRange(data.interval, 1000, 10, 1, 100)); + sendAction('getHighscores'); } function mapRange(number, inMin, inMax, outMin, outMax) { diff --git a/src/gameoflife.cpp b/src/gameoflife.cpp index 58995c62fda1936892a00e3a73699df5d9db0901..f26874938b581f9b6f4e341c644408750ff94961 100644 --- a/src/gameoflife.cpp +++ b/src/gameoflife.cpp @@ -3,6 +3,7 @@ #include "gameoflife.h" #include "network.h" #include "utils.h" +#include "settings.h" // https://github.com/Stavrosfil/game-of-life-esp32 @@ -162,6 +163,10 @@ void gameLoop() finalTicks = currentTick - noEvolutionTicksLimit; currentMillis = millis(); showEndScreen(finalTicks); + sendGameStats(gameEra, finalTicks, cellsAliveNow); + if(updateHighscores(gameEra, finalTicks, cellsAliveNow)) { + sendHighscores(); + } gameOver = true; } } diff --git a/src/network.cpp b/src/network.cpp index 77abde2befa27739b4813a81554d12894502a222..cdbc9b0f6b637397c8a74d8af0cb1cd54548b1f5 100644 --- a/src/network.cpp +++ b/src/network.cpp @@ -181,15 +181,6 @@ void onTelnetInput(String str) } } -DynamicJsonDocument getConfigJson() -{ - DynamicJsonDocument doc(200); - JsonObject config = doc.createNestedObject("config"); - config["brightness"] = defaultBrightness; - config["interval"] = gameInterval; - return doc; -} - void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { if (type == WS_EVT_CONNECT) @@ -236,19 +227,23 @@ void handleJson(uint8_t *data) return; } - if (doc.containsKey("config")) + if (doc.containsKey("settings")) { - updateConfig(doc); + updateSettings(doc); } if (doc.containsKey("action")) { - if (doc["action"] == "load") + if (doc["action"] == "getSettings") + { + sendSettings(); + } + else if (doc["action"] == "loadSettings") { loadSettings(); - sendConfig(); + sendSettings(); } - else if (doc["action"] == "save") + else if (doc["action"] == "saveSettings") { saveSettings(); } @@ -256,6 +251,10 @@ void handleJson(uint8_t *data) { addGlider(); } + else if (doc["action"] == "getHighscores") + { + sendHighscores(); + } else if (doc["action"] == "reboot") { ESP.restart(); @@ -263,16 +262,46 @@ void handleJson(uint8_t *data) } } -void updateConfig(StaticJsonDocument<200U> doc) +void updateSettings(StaticJsonDocument<200U> doc) { - gameInterval = doc["config"]["interval"]; - defaultBrightness = doc["config"]["brightness"]; + gameInterval = doc["settings"]["interval"]; + defaultBrightness = doc["settings"]["brightness"]; displayBrightness(defaultBrightness); } -void sendConfig() +void sendSettings() { - DynamicJsonDocument doc = getConfigJson(); + DynamicJsonDocument doc(200); + JsonObject settings = doc.createNestedObject("settings"); + settings["brightness"] = defaultBrightness; + settings["interval"] = gameInterval; + size_t strsize = measureJson(doc) + 1; + char json[strsize]; + serializeJson(doc, json, strsize); + ws.textAll(json); +} + +void sendHighscores() +{ + DynamicJsonDocument doc(200); + JsonObject highscores = doc.createNestedObject("highscores"); + Highscores hs = getHighscores(); + highscores["games"] = hs.games; + highscores["ticks"] = hs.ticks; + highscores["cells"] = hs.cells; + size_t strsize = measureJson(doc) + 1; + char json[strsize]; + serializeJson(doc, json, strsize); + ws.textAll(json); +} + +void sendGameStats(int game, int ticks, int cells) +{ + DynamicJsonDocument doc(200); + JsonObject stats = doc.createNestedObject("stats"); + stats["game"] = game; + stats["ticks"] = ticks; + stats["cells"] = cells; size_t strsize = measureJson(doc) + 1; char json[strsize]; serializeJson(doc, json, strsize); diff --git a/src/network.h b/src/network.h index d9d85db938bda748f73e6f93f1bb1e3ae66b99ec..b57b5b7afd867e1bd0a8faf5020a4a9ca9b88c03 100644 --- a/src/network.h +++ b/src/network.h @@ -31,7 +31,9 @@ void onTelnetConnectionAttempt(String ip); void onTelnetInput(String str); void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len); void handleJson(uint8_t* data); -void updateConfig(StaticJsonDocument<200U> doc); -void sendConfig(); +void updateSettings(StaticJsonDocument<200U> doc); +void sendSettings(); +void sendHighscores(); +void sendGameStats(int game, int ticks, int cells); #endif diff --git a/src/settings.cpp b/src/settings.cpp index c9fc9d3bac629985cab9cca603f2170b908b195f..a7c7875d9001e8c6189d0cfa522cd7a9f1ee85aa 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -18,3 +18,33 @@ void saveSettings() { preferences.putUInt("interval", gameInterval); preferences.end(); } + +bool updateHighscores(int games, int ticks, int cells) { + bool changed = false; + Highscores hs = getHighscores(); + preferences.begin("highscores", false); + if (games > hs.games) { + preferences.putUInt("games", games); + changed = true; + } + if (ticks > hs.ticks) { + preferences.putUInt("ticks", ticks); + changed = true; + } + if (cells > hs.cells) { + preferences.putUInt("cells", cells); + changed = true; + } + preferences.end(); + return changed; +} + +Highscores getHighscores() { + Highscores hs; + preferences.begin("highscores", true); + hs.games = preferences.getUInt("games", 0); + hs.ticks = preferences.getUInt("ticks", 0); + hs.cells = preferences.getUInt("cells", 0); + preferences.end(); + return hs; +} diff --git a/src/settings.h b/src/settings.h index 189c02ed788189b6d348f947722bf6b4698d6160..05a0964a8b8edbcfb8c7ea033b0a0f6f888d0926 100644 --- a/src/settings.h +++ b/src/settings.h @@ -8,7 +8,15 @@ extern int defaultBrightness; extern int gameInterval; +typedef struct { + int games; + int ticks; + int cells; +} Highscores; + void loadSettings(); void saveSettings(); +bool updateHighscores(int games, int ticks, int cells); +Highscores getHighscores(); #endif