From: Benjamin Auder <benjamin.auder@somewhere> Date: Mon, 14 Jan 2019 15:52:55 +0000 (+0100) Subject: Fix moveList + progress on game navigation system X-Git-Url: https://git.auder.net/variants/Chakart/doc/css/current/scripts/DESCRIPTION?a=commitdiff_plain;h=7d9e99bc177972c5af8b1b45f4bfb043d8306f30;p=vchess.git Fix moveList + progress on game navigation system --- diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js index 4b0b4705..7db4cfde 100644 --- a/public/javascripts/base_rules.js +++ b/public/javascripts/base_rules.js @@ -1053,6 +1053,8 @@ class ChessRules move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo) if (V.HasEnpassant) this.epSquares.push( this.getEpSquare(move) ); + if (!move.color) + move.color = this.turn; //for interface V.PlayOnBoard(this.board, move); this.turn = V.GetOppCol(this.turn); this.movesCount++; diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js index 594c8c22..4048a3d4 100644 --- a/public/javascripts/components/game.js +++ b/public/javascripts/components/game.js @@ -24,8 +24,10 @@ Vue.component('my-game', { vr: null, //VariantRules object, describing the game state + rules endgameMessage: "", orientation: "w", + fenStart: "", moves: [], //TODO: initialize if gameId is defined... + cursor: 0, // orientation :: button flip // userColor: given by gameId, or fen (if no game Id) // gameOver: known if gameId; otherwise assue false @@ -47,6 +49,7 @@ Vue.component('my-game', { return this.allowChat && this.mode=='human' && this.score != '*'; }, showMoves: function() { + return true; return this.allowMovelist && window.innerWidth >= 768; }, showFen: function() { @@ -81,6 +84,13 @@ Vue.component('my-game', { </my-chat> <my-board v-bind:vr="vr" :mode="mode" :orientation="orientation" :user-color="mycolor" @play-move="play"> </my-board> + <div class="button-group"> + <button @click="() => play()">Play</button> + <button @click="() => undo()">Undo</button> + <button @click="flip">Flip</button> + <button @click="gotoBegin">GotoBegin</button> + <button @click="gotoEnd">GotoEnd</button> + </div> <div v-if="showFen && !!vr" id="fen-div" class="section-content"> <p id="fen-string" class="text-center"> {{ vr.getFen() }} @@ -93,18 +103,18 @@ Vue.component('my-game', { {{ translate("Download PGN") }} </button> </div> - <my-move-list v-if="showMoves" :moves="moves"> + <my-move-list v-if="showMoves" :moves="moves" :cursor="cursor" @goto-move="gotoMove"> </my-move-list> </div> `, created: function() { - -// console.log(this.fen); -// console.log(this.gameId); if (!!this.gameId) this.loadGame(); else if (!!this.fen) + { this.vr = new VariantRules(this.fen); + this.fenStart = this.fen; + } // TODO: after game, archive in indexedDB // TODO: this events listener is central. Refactor ? How ? const socketMessageListener = msg => { @@ -316,12 +326,25 @@ Vue.component('my-game', { }, 250); }, play: function(move, programmatic) { + // Forbid playing outside analyze mode when cursor isn't at moves.length-1 + if (this.mode != "analyze" && this.cursor < this.moves.length-1) + return; + let navigate = !move; + if (navigate) + { + if (this.cursor == this.moves.length) + return; //no more moves + move = this.moves[this.cursor]; + } if (!!programmatic) //computer or human opponent return this.animateMove(move); // Not programmatic, or animation is over if (!move.notation) move.notation = this.vr.getNotation(move); + if (!move.color) + move.color = this.vr.turn; this.vr.play(move); + this.cursor++; if (!move.fen) move.fen = this.vr.getFen(); if (this.sound == 2) @@ -337,10 +360,13 @@ Vue.component('my-game', { // Send the move to web worker (including his own moves) this.compWorker.postMessage(["newmove",move]); } - if (this.score == "*" || this.mode == "analyze") + if (!navigate && (this.score == "*" || this.mode == "analyze")) { - // Stack move on movesList - this.moves.push(move); + // Stack move on movesList at current cursor + if (this.cursor == this.moves.length) + this.moves.push(move); + else + this.moves = this.moves.slice(0,this.cursor-1).concat([move]); } // Is opponent in check? this.incheck = this.vr.getCheckSquares(this.vr.turn); @@ -355,18 +381,45 @@ Vue.component('my-game', { } else if (this.mode == "computer" && this.vr.turn != this.userColor) this.playComputerMove(); + if (navigate) + this.$children[0].$forceUpdate(); //TODO!? }, undo: function(move) { + let navigate = !move; + if (navigate) + { + if (this.cursor == 0) + return; //no more moves + move = this.moves[this.cursor-1]; + } this.vr.undo(move); + this.cursor--; + if (navigate) + this.$children[0].$forceUpdate(); //TODO!? if (this.sound == 2) new Audio("/sounds/undo.mp3").play().catch(err => {}); this.incheck = this.vr.getCheckSquares(this.vr.turn); - if (this.mode == "analyze") + if (!navigate && this.mode == "analyze") this.moves.pop(); + if (navigate) + this.$forceUpdate(); //TODO!? + }, + gotoMove: function(index) { + this.vr = new VariantRules(this.moves[index].fen); + this.cursor = index+1; + }, + gotoBegin: function() { + this.vr = new VariantRules(this.fenStart); + this.cursor = 0; + }, + gotoEnd: function() { + this.gotoMove(this.moves.length-1); + }, + flip: function() { + this.orientation = V.GetNextCol(this.orientation); }, }, }) -// cursor + ........ //TODO: confirm dialog with "opponent offers draw", avec possible bouton "prevent future offers" + bouton "proposer nulle" //+ bouton "abort" avec score == "?" + demander confirmation pour toutes ces actions, //comme sur lichess diff --git a/public/javascripts/components/moveList.js b/public/javascripts/components/moveList.js index 3687864c..b3c4255f 100644 --- a/public/javascripts/components/moveList.js +++ b/public/javascripts/components/moveList.js @@ -1,12 +1,12 @@ //TODO: component for moves list on the right // TODO: generic "getPGN" in the same way (following move.color) Vue.component('my-move-list', { - props: ["moves"], //TODO: other props for e.g. players names + connected indicator + props: ["moves","cursor"], //TODO: other props for e.g. players names + connected indicator // --> we could also add turn indicator here + // + missing "cursor" prop data: function() { return { - // if oppid == "computer" then mode = "computer" (otherwise human) - myid: "", //TODO + something: "", //TODO }; }, // TODO: extend rendering for more than 2 colors: would be a parameter @@ -14,8 +14,9 @@ Vue.component('my-move-list', { if (this.moves.length == 0) return; const nbColors = 2; - if (this.moves[0].color == "black") - this.moves.unshift({color: "white", notation: "..."}); + // TODO: name colors "white", "black", "red", "yellow" ? + if (this.moves[0].color == "b") + this.moves.unshift({color: "w", notation: "..."}); let tableContent = []; let moveCounter = 0; let tableRow = undefined; @@ -23,45 +24,62 @@ Vue.component('my-move-list', { let curCellContent = ""; for (let i=0; i<this.moves.length; i++) { - if (this.moves[i].color == "white") + if (this.moves[i].color == "w") { - if (i == 0 || i>0 && this.moves[i-1].color=="black") + if (i == 0 || i>0 && this.moves[i-1].color=="b") { if (!!tableRow) + { + tableRow.children = moveCells; tableContent.push(tableRow); + } moveCells = [ h( "td", - { attrs: { innerHTML: (++moveCounter) + "." } } + { domProps: { innerHTML: (++moveCounter) + "." } } ) ]; tableRow = h( "tr", - { }, - moveCells + { } ); curCellContent = ""; } - curCellContent += this.moves[i].notation + ","; + } + curCellContent += this.moves[i].notation; + if (i < this.moves.length-1 && this.moves[i+1].color == this.moves[i].color) + curCellContent += ","; + else //color change + { moveCells.push( h( "td", - { attrs: .............. + { + domProps: { innerHTML: curCellContent }, + on: { click: () => this.gotoMove(i) }, + "class": { "highlight-lm": this.cursor-1 == i }, + } + ) + ); + curCellContent = ""; } } // Complete last row, which might not be full: - if (tableRow.length-1 < nbColors) + if (moveCells.length-1 < nbColors) { const delta = nbColors - (moveCells.length-1); for (let i=0; i<delta; i++) { moveCells.push( - "td" - { attrs: { innerHTML: "" } } + h( + "td", + { domProps: { innerHTML: "" } } + ) ); } - tableContent.push(tableRow); } + tableRow.children = moveCells; + tableContent.push(tableRow); const movesTable = h( "table", { }, @@ -69,6 +87,9 @@ Vue.component('my-move-list', { ); return movesTable; }, -// methods: { -// }, -} + methods: { + gotoMove: function(index) { + this.$emit("goto-move", index); + }, + }, +}) diff --git a/public/javascripts/variant.js b/public/javascripts/variant.js index 74c4298c..f074baeb 100644 --- a/public/javascripts/variant.js +++ b/public/javascripts/variant.js @@ -12,7 +12,7 @@ new Vue({ userColor: "w", allowChat: false, - allowMovelist: false, + allowMovelist: true, fen: V.GenRandInitFen(), }, created: function() { diff --git a/public/javascripts/variants/Marseille.js b/public/javascripts/variants/Marseille.js index 1adb83f4..d38d177f 100644 --- a/public/javascripts/variants/Marseille.js +++ b/public/javascripts/variants/Marseille.js @@ -19,8 +19,6 @@ class MarseilleRules extends ChessRules getTurnFen() { - if (this.startAtFirstMove && this.moves.length==0) - return "w"; return this.turn + this.subTurn; } @@ -56,9 +54,8 @@ class MarseilleRules extends ChessRules // Extract subTurn from turn indicator: "w" (first move), or // "w1" or "w2" white subturn 1 or 2, and same for black const fullTurn = V.ParseFen(fen).turn; - this.startAtFirstMove = (fullTurn == "w"); this.turn = fullTurn[0]; - this.subTurn = (fullTurn[1] || 1); + this.subTurn = (fullTurn[1] || 0); //"w0" = special code for first move in game } getPotentialPawnMoves([x,y]) @@ -144,18 +141,13 @@ class MarseilleRules extends ChessRules return moves; } - play(move, ingame) + play(move) { - if (!!ingame) - { - move.notation = [this.getNotation(move), this.getLongNotation(move)]; - // In this special case, we also need the "move color": - move.color = this.turn; - } move.flags = JSON.stringify(this.aggregateFlags()); + move.turn = this.turn + this.subturn; V.PlayOnBoard(this.board, move); const epSq = this.getEpSquare(move); - if (this.startAtFirstMove && this.moves.length == 0) + if (this.subTurn == 0) //first move in game { this.turn = "b"; this.epSquares.push([epSq]); @@ -179,40 +171,22 @@ class MarseilleRules extends ChessRules this.epSquares.push([epSq]); this.subTurn = 3 - this.subTurn; } - this.moves.push(move); this.updateVariables(move); - if (!!ingame) - move.hash = hex_md5(this.getFen()); } undo(move) { this.disaggregateFlags(JSON.parse(move.flags)); V.UndoOnBoard(this.board, move); - if (this.startAtFirstMove && this.moves.length == 1) - { - this.turn = "w"; + if (move.turn[1] == '0' || move.checkOnSubturn1 || this.subTurn == 2) this.epSquares.pop(); - } - else if (move.checkOnSubturn1) + else //this.subTurn == 1 { - this.turn = V.GetOppCol(this.turn); - this.subTurn = 1; - this.epSquares.pop(); - } - else - { - if (this.subTurn == 1) - { - this.turn = V.GetOppCol(this.turn); - let lastEpsq = this.epSquares[this.epSquares.length-1]; - lastEpsq.pop(); - } - else - this.epSquares.pop(); - this.subTurn = 3 - this.subTurn; + let lastEpsq = this.epSquares[this.epSquares.length-1]; + lastEpsq.pop(); } - this.moves.pop(); + this.turn = move.turn[0]; + this.subTurn = parseInt(move.turn[1]); this.unupdateVariables(move); } @@ -319,6 +293,7 @@ class MarseilleRules extends ChessRules return selected; } + // TODO: put this generic version elsewhere getPGN(mycolor, score, fenStart, mode) { let pgn = ""; diff --git a/public/stylesheets/variant.sass b/public/stylesheets/variant.sass index 78570856..299320df 100644 --- a/public/stylesheets/variant.sass +++ b/public/stylesheets/variant.sass @@ -67,6 +67,9 @@ label.drawer-toggle // Game section: +td.highlight-lm + background-color: purple + button.play height: 24px margin: 0 diff --git a/views/variant.pug b/views/variant.pug index dbdbabae..76d79ae8 100644 --- a/views/variant.pug +++ b/views/variant.pug @@ -54,5 +54,6 @@ block javascripts script(src="/javascripts/components/board.js") //script(src="/javascripts/components/problemPreview.js") //script(src="/javascripts/components/problems.js") + script(src="/javascripts/components/moveList.js") script(src="/javascripts/components/game.js") script(src="/javascripts/variant.js")