X-Git-Url: https://git.auder.net/?p=vchess.git;a=blobdiff_plain;f=client%2Fsrc%2Fcomponents%2FBaseGame.vue;h=2517c78ab9832ed7685c4d6181bc8f8c15d25bf9;hp=471c139c14252c85c2207d17f361dee5864606da;hb=d54f6261c9e30f4eabb402ad301dd5c5e40fb656;hpb=b0a0468aa6f436f2ad4962492561ef430a3bc15c diff --git a/client/src/components/BaseGame.vue b/client/src/components/BaseGame.vue index 471c139c..2517c78a 100644 --- a/client/src/components/BaseGame.vue +++ b/client/src/components/BaseGame.vue @@ -11,6 +11,7 @@ div#baseGame #gameContainer #boardContainer Board( + ref="board" :vr="vr" :last-move="lastMove" :analyze="game.mode=='analyze'" @@ -29,6 +30,11 @@ div#baseGame img.inline(src="/images/icons/play_rev.svg") button(v-if="canFlip" @click="flip()") img.inline(src="/images/icons/flip.svg") + button( + @click="runAutoplay()" + :class="{'in-autoplay': autoplay}" + ) + img.inline(src="/images/icons/autoplay.svg") button(@click="play()") img.inline(src="/images/icons/play.svg") button(@click="gotoEnd()") @@ -82,6 +88,8 @@ export default { firstMoveNumber: 0, //for printing incheck: [], //for Board inMultimove: false, + autoplay: false, + autoplayLoop: null, inPlay: false, stackToPlay: [] }; @@ -95,15 +103,13 @@ export default { showTurn: function() { return ( this.game.score == '*' && - this.vr && - (this.vr.showMoves != "all" || !this.vr.canFlip) + !!this.vr && this.vr.showTurn ); }, turn: function() { - if (!this.vr) - return ""; + if (!this.vr) return ""; if (this.vr.showMoves != "all") - return this.st.tr[(this.vr.turn == 'w' ? "White" : "Black") + " to move"] + return this.st.tr[(this.vr.turn == 'w' ? "White" : "Black") + " to move"]; // Cannot flip: racing king or circular chess return this.vr.movesCount == 0 && this.game.mycolor == "w" ? this.st.tr["It's your turn!"] @@ -134,6 +140,9 @@ export default { document.getElementById("eogDiv") .addEventListener("click", processModalClick); }, + beforeDestroy: function() { + if (!!this.autoplayLoop) clearInterval(this.autoplayLoop); + }, methods: { focusBg: function() { document.getElementById("baseGame").focus(); @@ -178,12 +187,15 @@ export default { const parsedFen = V.ParseFen(game.fenStart); const firstMoveColor = parsedFen.turn; this.firstMoveNumber = Math.floor(parsedFen.movesCount / 2); + let L = this.moves.length; this.moves.forEach(move => { // Strategy working also for multi-moves: if (!Array.isArray(move)) move = [move]; - move.forEach(m => { + move.forEach((m,idx) => { m.notation = this.vr.getNotation(m); this.vr.play(m); + if (idx < L - 1 && this.vr.getCheckSquares(this.vr.turn).length > 0) + m.notation += "+"; }); }); if (firstMoveColor == "b") { @@ -194,9 +206,17 @@ export default { end: { x: -1, y: -1 }, fen: game.fenStart }); + L++; } this.positionCursorTo(this.moves.length - 1); this.incheck = this.vr.getCheckSquares(this.vr.turn); + const score = this.vr.getCurrentScore(); + if (L > 0 && this.moves[L - 1].notation != "...") { + if (["1-0","0-1"].includes(score)) + this.moves[L - 1].notation += "#"; + else if (this.vr.getCheckSquares(this.vr.turn).length > 0) + this.moves[L - 1].notation += "+"; + } }, positionCursorTo: function(index) { this.cursor = index; @@ -251,6 +271,29 @@ export default { this.endgameMessage = message; document.getElementById("modalEog").checked = true; }, + runAutoplay: function() { + const infinitePlay = () => { + if (this.cursor == this.moves.length - 1) { + clearInterval(this.autoplayLoop); + this.autoplayLoop = null; + this.autoplay = false; + return; + } + if (this.inPlay || this.inMultimove) + // Wait next tick + return; + this.play(); + }; + if (this.autoplay) { + this.autoplay = false; + clearInterval(this.autoplayLoop); + this.autoplayLoop = null; + } else { + this.autoplay = true; + infinitePlay(); + this.autoplayLoop = setInterval(infinitePlay, 1500); + } + }, // Animate an elementary move animateMove: function(move, callback) { let startSquare = document.getElementById(getSquareId(move.start)); @@ -291,12 +334,14 @@ export default { if (this.game.mode == "analyze") { this.$emit( "fenchange", - this.lastMove ? this.lastMove.fen : this.game.fenStart + !!this.lastMove ? this.lastMove.fen : this.game.fenStart ); } }, // "light": if gotoMove() or gotoEnd() play: function(move, received, light, noemit) { + // Freeze while choices are shown: + if (this.$refs["board"].choices.length > 0) return; if (!!noemit) { if (this.inPlay) { // Received moves in observed games can arrive too fast: @@ -307,27 +352,32 @@ export default { } const navigate = !move; const playSubmove = (smove) => { - if (!navigate) smove.notation = this.vr.getNotation(smove); + smove.notation = this.vr.getNotation(smove); this.vr.play(smove); - if (!navigate) { - if (!this.inMultimove) { + this.lastMove = smove; + if (!this.inMultimove) { + // Condition is "!navigate" but we mean "!this.autoplay" + if (!navigate) { if (this.cursor < this.moves.length - 1) this.moves = this.moves.slice(0, this.cursor + 1); this.moves.push(smove); - this.inMultimove = true; //potentially - this.cursor++; - } else { - // Already in the middle of a multi-move - const L = this.moves.length; - if (!Array.isArray(this.moves[L-1])) - this.$set(this.moves, L-1, [this.moves[L-1], smove]); - else - this.$set(this.moves, L-1, this.moves.concat([smove])); } + this.inMultimove = true; //potentially + this.cursor++; + } else if (!navigate) { + // Already in the middle of a multi-move + const L = this.moves.length; + if (!Array.isArray(this.moves[L-1])) + this.$set(this.moves, L-1, [this.moves[L-1], smove]); + else + this.$set(this.moves, L-1, this.moves.concat([smove])); } }; const playMove = () => { - const animate = V.ShowMoves == "all" && (received || navigate); + const animate = ( + V.ShowMoves == "all" && + (this.autoplay || !!received) + ); if (!Array.isArray(move)) move = [move]; let moveIdx = 0; let self = this; @@ -348,6 +398,21 @@ export default { } })(); }; + const computeScore = () => { + const score = this.vr.getCurrentScore(); + if (!navigate) { + if (["1-0","0-1"].includes(score)) + this.lastMove.notation += "#"; + else if (this.vr.getCheckSquares(this.vr.turn).length > 0) + this.lastMove.notation += "+"; + } + if (score != "*" && this.game.mode == "analyze") { + const message = getScoreMessage(score); + // Just show score on screen (allow undo) + this.showEndgameMsg(score + " . " + this.st.tr[message]); + } + return score; + }; const afterMove = (smove, initurn) => { if (this.vr.turn != initurn) { // Turn has changed: move is complete @@ -356,25 +421,17 @@ export default { smove.fen = this.vr.getFen(); // Is opponent in check? this.incheck = this.vr.getCheckSquares(this.vr.turn); - this.lastMove = smove; this.emitFenIfAnalyze(); this.inMultimove = false; - if (!noemit) { - var score = this.vr.getCurrentScore(); - if (score != "*" && this.game.mode == "analyze") { - const message = getScoreMessage(score); - // Just show score on screen (allow undo) - this.showEndgameMsg(score + " . " + this.st.tr[message]); - } - } - if (!navigate && this.game.mode != "analyze") { - const L = this.moves.length; - if (!noemit) + this.score = computeScore(); + if (this.game.mode != "analyze" && !navigate) { + if (!noemit) { // Post-processing (e.g. computer play). + const L = this.moves.length; // NOTE: always emit the score, even in unfinished, // to tell Game::processMove() that it's not a received move. - this.$emit("newmove", this.moves[L-1], { score: score }); - else { + this.$emit("newmove", this.moves[L-1], { score: this.score }); + } else { this.inPlay = false; if (this.stackToPlay.length > 0) // Move(s) arrived in-between @@ -388,22 +445,25 @@ export default { // The move to navigate to is necessarily full: if (this.cursor == this.moves.length - 1) return; //no more moves move = this.moves[this.cursor + 1]; - if (light) { - // Just play the move, nothing else: + if (!this.autoplay) { + // Just play the move: if (!Array.isArray(move)) move = [move]; for (let i=0; i < move.length; i++) this.vr.play(move[i]); + if (!light) { + this.lastMove = move[move.length-1]; + this.incheck = this.vr.getCheckSquares(this.vr.turn); + this.score = computeScore(); + this.emitFenIfAnalyze(); + } + this.cursor++; + return; } - else { - playMove(); - this.emitFenIfAnalyze(); - } - this.cursor++; - return; } // Forbid playing outside analyze mode, except if move is received. // Sufficient condition because Board already knows which turn it is. if ( this.game.mode != "analyze" && + !navigate && !received && (this.game.score != "*" || this.cursor < this.moves.length - 1) ) { @@ -430,6 +490,8 @@ export default { }, // "light": if gotoMove() or gotoBegin() undo: function(move, light) { + // Freeze while choices are shown: + if (this.$refs["board"].choices.length > 0) return; if (this.inMultimove) { this.cancelCurrentMultimove(); this.incheck = this.vr.getCheckSquares(this.vr.turn); @@ -452,6 +514,7 @@ export default { } }, gotoMove: function(index) { + if (this.$refs["board"].choices.length > 0) return; if (this.inMultimove) this.cancelCurrentMultimove(); if (index == this.cursor) return; if (index < this.cursor) { @@ -469,6 +532,7 @@ export default { this.emitFenIfAnalyze(); }, gotoBegin: function() { + if (this.$refs["board"].choices.length > 0) return; if (this.inMultimove) this.cancelCurrentMultimove(); const minCursor = this.moves.length > 0 && this.moves[0].notation == "..." @@ -480,11 +544,13 @@ export default { this.emitFenIfAnalyze(); }, gotoEnd: function() { + if (this.$refs["board"].choices.length > 0) return; if (this.cursor == this.moves.length - 1) return; this.gotoMove(this.moves.length - 1); this.emitFenIfAnalyze(); }, flip: function() { + if (this.$refs["board"].choices.length > 0) return; this.orientation = V.GetOppCol(this.orientation); } } @@ -515,8 +581,11 @@ export default { padding-top: 5px padding-bottom: 5px +.in-autoplay + background-color: #FACF8C + img.inline - height: 24px + height: 22px padding-top: 5px @media screen and (max-width: 767px) height: 18px