From 98b94cc3504937ae0af29a265394dfe52ea1ee67 Mon Sep 17 00:00:00 2001 From: Benjamin Auder <benjamin.auder@somewhere> Date: Thu, 20 Feb 2020 21:10:39 +0100 Subject: [PATCH] Store state of games VS computer --- client/src/components/ComputerGame.vue | 59 +++++++++++------- client/src/translations/about/fr.pug | 2 +- client/src/utils/compgameStorage.js | 85 ++++++++++++++++++++++++++ client/src/utils/gameStorage.js | 1 - client/src/views/Rules.vue | 15 ++++- 5 files changed, 135 insertions(+), 27 deletions(-) create mode 100644 client/src/utils/compgameStorage.js diff --git a/client/src/components/ComputerGame.vue b/client/src/components/ComputerGame.vue index a5e03b31..c05eea05 100644 --- a/client/src/components/ComputerGame.vue +++ b/client/src/components/ComputerGame.vue @@ -11,6 +11,7 @@ BaseGame( <script> import BaseGame from "@/components/BaseGame.vue"; import { store } from "@/store"; +import { CompgameStorage } from "@/utils/compgameStorage"; import Worker from "worker-loader!@/playCompMove"; export default { name: "my-computer-game", @@ -31,11 +32,6 @@ export default { compWorker: null }; }, - watch: { - "gameInfo.fen": function() { - this.launchGame(); - }, - }, created: function() { // Computer moves web worker logic: this.compWorker = new Worker(); @@ -57,36 +53,41 @@ export default { let moveIdx = 0; let self = this; (function executeMove() { + // NOTE: BaseGame::play() will trigger processMove() here self.$refs["basegame"].play(compMove[moveIdx++], "received"); if (moveIdx >= compMove.length) { self.compThink = false; if (self.game.score != "*") - //user action + // User action self.$emit("game-stopped"); } else setTimeout(executeMove, 500 + animDelay); })(); }, delay); }; - if (this.gameInfo.fen) this.launchGame(); }, methods: { - launchGame: function() { + launchGame: function(game) { this.compWorker.postMessage(["scripts", this.gameInfo.vname]); - this.compWorker.postMessage(["init", this.gameInfo.fen]); - this.vr = new V(this.gameInfo.fen); - const mycolor = Math.random() < 0.5 ? "w" : "b"; - let players = [{ name: "Myself" }, { name: "Computer" }]; - if (mycolor == "b") players = players.reverse(); + if (!game) { + game = { + vname: this.gameInfo.vname, + fenStart: V.GenRandInitFen(), + mycolor: Math.random() < 0.5 ? "w" : "b", + moves: [] + }; + game.fen = game.fenStart; + if (this.gameInfo.mode == "versus") + CompgameStorage.add(game); + } + this.compWorker.postMessage(["init", game.fen]); + this.vr = new V(game.fen); + game.players = [{ name: "Myself" }, { name: "Computer" }]; + if (game.myColor == "b") game.players = game.players.reverse(); + game.score = "*"; //finished games are removed this.currentUrl = document.location.href; //to avoid playing outside page - // NOTE: fen and fenStart are redundant in game object - this.game = Object.assign({}, this.gameInfo, { - fenStart: this.gameInfo.fen, - players: players, - mycolor: mycolor, - score: "*" - }); - this.compWorker.postMessage(["init", this.gameInfo.fen]); - if (mycolor != "w" || this.gameInfo.mode == "auto") + this.game = game; + this.compWorker.postMessage(["init", game.fen]); + if (this.gameInfo.mode == "auto" || game.mycolor != this.vr.turn) this.playComputerMove(); }, // NOTE: a "goto" action could lead to an error when comp is thinking, @@ -107,6 +108,20 @@ export default { ) { this.playComputerMove(); } + // Finally, update storage: + if (this.gameInfo.mode == "versus") { + const allowed_fields = ["appear", "vanish", "start", "end"]; + const filtered_move = Object.keys(move) + .filter(key => allowed_fields.includes(key)) + .reduce((obj, key) => { + obj[key] = move[key]; + return obj; + }, {}); + CompgameStorage.update(this.gameInfo.vname, { + move: filtered_move, + fen: move.fen + }); + } }, gameOver: function(score, scoreMsg) { this.game.score = score; diff --git a/client/src/translations/about/fr.pug b/client/src/translations/about/fr.pug index b38cf095..b7092f02 100644 --- a/client/src/translations/about/fr.pug +++ b/client/src/translations/about/fr.pug @@ -1,7 +1,7 @@ h3 Histoire p. - Ce site est né à la suite d'une discussion avec Patrick bernier, qui m'avait + Ce site est né à la suite d'une discussion avec Patrick Bernier, qui m'avait parlé d'une variante de son invention : l'Ãchiqueté ('Checkered' ici), impliquant des pièces pouvant être jouées par les deux camps. à l'époque en 2012, j'ai développé un premier petit site diff --git a/client/src/utils/compgameStorage.js b/client/src/utils/compgameStorage.js new file mode 100644 index 00000000..05a20749 --- /dev/null +++ b/client/src/utils/compgameStorage.js @@ -0,0 +1,85 @@ +// (Comp)Game object: { +// // Static informations: +// vname: string (this is the ID) +// fenStart: string, +// mycolor: "w" or "b" +// // Game (dynamic) state: +// fen: string, +// moves: array of Move objects, +// } + +import { store } from "@/store"; + +function dbOperation(callback) { + let db = null; + let DBOpenRequest = window.indexedDB.open("vchess_comp", 4); + + DBOpenRequest.onerror = function(event) { + alert(store.state.tr["Database error: stop private browsing, or update your browser"]); + callback("error",null); + }; + + DBOpenRequest.onsuccess = function(event) { + db = DBOpenRequest.result; + callback(null,db); + db.close(); + }; + + DBOpenRequest.onupgradeneeded = function(event) { + let db = event.target.result; + let objectStore = db.createObjectStore("compgames", { keyPath: "vname" }); + }; +} + +export const CompgameStorage = { + add: function(game) { + dbOperation((err,db) => { + if (err) + return; + let transaction = db.transaction("compgames", "readwrite"); + let objectStore = transaction.objectStore("compgames"); + objectStore.add(game); + }); + }, + + // obj: move and/or fen + update: function(gameId, obj) { + dbOperation((err,db) => { + let objectStore = db + .transaction("compgames", "readwrite") + .objectStore("compgames"); + objectStore.get(gameId).onsuccess = function(event) { + // Ignoring error silently: shouldn't happen now. TODO? + if (event.target.result) { + const game = event.target.result; + Object.keys(obj).forEach(k => { + if (k == "move") game.moves.push(obj[k]); + else game[k] = obj[k]; + }); + objectStore.put(game); //save updated data + } + }; + }); + }, + + // Retrieve any game from its identifier (variant name) + // NOTE: need callback because result is obtained asynchronously + get: function(gameId, callback) { + dbOperation((err,db) => { + let objectStore = db.transaction("compgames").objectStore("compgames"); + objectStore.get(gameId).onsuccess = function(event) { + callback(event.target.result); + }; + }); + }, + + // Delete a game in indexedDB + remove: function(gameId) { + dbOperation((err,db) => { + if (!err) { + let transaction = db.transaction(["compgames"], "readwrite"); + transaction.objectStore("compgames").delete(gameId); + } + }); + } +}; diff --git a/client/src/utils/gameStorage.js b/client/src/utils/gameStorage.js index cc7dca5b..ce609af0 100644 --- a/client/src/utils/gameStorage.js +++ b/client/src/utils/gameStorage.js @@ -7,7 +7,6 @@ // cadence: string, // increment: integer (seconds), // type: string ("live" or "corr") -// imported: boolean (optional, default false) // // Game (dynamic) state: // fen: string, // moves: array of Move objects, diff --git a/client/src/views/Rules.vue b/client/src/views/Rules.vue index f864e9e4..00202e64 100644 --- a/client/src/views/Rules.vue +++ b/client/src/views/Rules.vue @@ -40,6 +40,7 @@ main import ComputerGame from "@/components/ComputerGame.vue"; import { store } from "@/store"; import { getDiagram } from "@/utils/printDiagram"; +import { CompgameStorage } from "@/utils/compgameStorage"; export default { name: "my-rules", components: { @@ -54,7 +55,6 @@ export default { gameInfo: { vname: "", mode: "versus", - fen: "" } }; }, @@ -118,15 +118,24 @@ export default { this.gameInProgress = true; this.display = "computer"; this.gameInfo.mode = mode; - this.$set(this.gameInfo, "fen", V.GenRandInitFen()); + if (this.gameInfo.mode == "versus") { + CompgameStorage.get(this.gameInfo.vname, (game) => { + // NOTE: game might be null + this.$refs["compgame"].launchGame(game); + }); + } else { + this.$refs["compgame"].launchGame(); + } }, - // user is willing to stop the game: + // user wants to stop the game: stopGame: function() { this.$refs["compgame"].gameOver("?", "Undetermined result"); }, // The game is effectively stopped: gameStopped: function() { this.gameInProgress = false; + if (this.gameInfo.mode == "versus") + CompgameStorage.remove(this.gameInfo.vname); }, gotoAnalyze: function() { this.$router.push( -- 2.44.0