From: Benjamin Auder Date: Mon, 6 Apr 2020 22:19:41 +0000 (+0200) Subject: Improve autoplay, debug move reception while autoplay and/or analyze is on. Add Ambig... X-Git-Url: https://git.auder.net/doc/html/img/logo_Westcastle.png?a=commitdiff_plain;h=5b18515f0b7dbfab8a2770d9b0fc7aace09267dc;p=vchess.git Improve autoplay, debug move reception while autoplay and/or analyze is on. Add Ambiguous chess beta --- diff --git a/client/public/images/pieces/Ambiguous/bc.svg b/client/public/images/pieces/Ambiguous/bc.svg new file mode 100644 index 00000000..03c42738 --- /dev/null +++ b/client/public/images/pieces/Ambiguous/bc.svg @@ -0,0 +1,94 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Ambiguous/bg.svg b/client/public/images/pieces/Ambiguous/bg.svg new file mode 100644 index 00000000..498fad6d --- /dev/null +++ b/client/public/images/pieces/Ambiguous/bg.svg @@ -0,0 +1,75 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/client/public/images/pieces/Ambiguous/bl.svg b/client/public/images/pieces/Ambiguous/bl.svg new file mode 100644 index 00000000..965d8dec --- /dev/null +++ b/client/public/images/pieces/Ambiguous/bl.svg @@ -0,0 +1,119 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Ambiguous/bo.svg b/client/public/images/pieces/Ambiguous/bo.svg new file mode 100644 index 00000000..a3e425a5 --- /dev/null +++ b/client/public/images/pieces/Ambiguous/bo.svg @@ -0,0 +1,99 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Ambiguous/bs.svg b/client/public/images/pieces/Ambiguous/bs.svg new file mode 100644 index 00000000..fab7c213 --- /dev/null +++ b/client/public/images/pieces/Ambiguous/bs.svg @@ -0,0 +1,62 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/client/public/images/pieces/Ambiguous/bt.svg b/client/public/images/pieces/Ambiguous/bt.svg new file mode 100644 index 00000000..311d5fd7 --- /dev/null +++ b/client/public/images/pieces/Ambiguous/bt.svg @@ -0,0 +1,109 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Ambiguous/bu.svg b/client/public/images/pieces/Ambiguous/bu.svg new file mode 100644 index 00000000..bce3eed1 --- /dev/null +++ b/client/public/images/pieces/Ambiguous/bu.svg @@ -0,0 +1,94 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Ambiguous/wc.svg b/client/public/images/pieces/Ambiguous/wc.svg new file mode 100644 index 00000000..b51dea5a --- /dev/null +++ b/client/public/images/pieces/Ambiguous/wc.svg @@ -0,0 +1,126 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Ambiguous/wg.svg b/client/public/images/pieces/Ambiguous/wg.svg new file mode 100644 index 00000000..bcf16295 --- /dev/null +++ b/client/public/images/pieces/Ambiguous/wg.svg @@ -0,0 +1,70 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/client/public/images/pieces/Ambiguous/wl.svg b/client/public/images/pieces/Ambiguous/wl.svg new file mode 100644 index 00000000..322c54c3 --- /dev/null +++ b/client/public/images/pieces/Ambiguous/wl.svg @@ -0,0 +1,124 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Ambiguous/wo.svg b/client/public/images/pieces/Ambiguous/wo.svg new file mode 100644 index 00000000..0e3bd47e --- /dev/null +++ b/client/public/images/pieces/Ambiguous/wo.svg @@ -0,0 +1,71 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/client/public/images/pieces/Ambiguous/ws.svg b/client/public/images/pieces/Ambiguous/ws.svg new file mode 100644 index 00000000..dfcd791e --- /dev/null +++ b/client/public/images/pieces/Ambiguous/ws.svg @@ -0,0 +1,71 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/client/public/images/pieces/Ambiguous/wt.svg b/client/public/images/pieces/Ambiguous/wt.svg new file mode 100644 index 00000000..e2a5a230 --- /dev/null +++ b/client/public/images/pieces/Ambiguous/wt.svg @@ -0,0 +1,204 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Ambiguous/wu.svg b/client/public/images/pieces/Ambiguous/wu.svg new file mode 100644 index 00000000..b2bfd221 --- /dev/null +++ b/client/public/images/pieces/Ambiguous/wu.svg @@ -0,0 +1,128 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/src/components/BaseGame.vue b/client/src/components/BaseGame.vue index fdbd2c3c..8cb1f68a 100644 --- a/client/src/components/BaseGame.vue +++ b/client/src/components/BaseGame.vue @@ -94,7 +94,6 @@ export default { incheck: [], //for Board inMultimove: false, autoplay: false, - autoplayLoop: null, inPlay: false, stackToPlay: [] }; @@ -164,7 +163,8 @@ export default { .addEventListener("click", processModalClick); }, beforeDestroy: function() { - if (!!this.autoplayLoop) clearInterval(this.autoplayLoop); + // TODO: probably not required + this.autoplay = false; }, methods: { focusBg: function() { @@ -216,6 +216,10 @@ export default { const firstMoveColor = parsedFen.turn; this.firstMoveNumber = Math.floor(parsedFen.movesCount / 2) + 1; let L = this.moves.length; + if (L == 0) { + this.incheck = []; + this.score = "*"; + } this.moves.forEach((move,idx) => { // Strategy working also for multi-moves: if (!Array.isArray(move)) move = [move]; @@ -229,8 +233,8 @@ export default { if (idxM == Lm - 1) m.fen = this.vr.getFen(); if (idx == L - 1 && idxM == Lm - 1) { this.incheck = checkSquares; - const score = this.vr.getCurrentScore(); - if (["1-0", "0-1"].includes(score)) m.notation += "#"; + this.score = this.vr.getCurrentScore(); + if (["1-0", "0-1"].includes(this.score)) m.notation += "#"; } }); }); @@ -333,32 +337,15 @@ export default { 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 { + if (this.stackToPlay.length > 0) + // Move(s) arrived in-between + this.play(this.stackToPlay.pop(), "received"); + } + else if (this.cursor < this.moves.length - 1) { this.autoplay = true; - setTimeout( - () => { - infinitePlay(); - this.autoplayLoop = setInterval(infinitePlay, 1500); - }, - // Small delay otherwise the first move is played too fast - 500 - ); + this.play(); } }, // Animate an elementary move @@ -417,7 +404,7 @@ export default { if (!!move) this.play(move); }, // "light": if gotoMove() or gotoEnd() - play: function(move, received, light, noemit) { + play: function(move, received, light) { // Freeze while choices are shown: if (this.$refs["board"].choices.length > 0) return; const navigate = !move; @@ -432,18 +419,17 @@ export default { return; } if (!!received) { - if (this.mode == "analyze") this.toggleAnalyze(); - if (this.cursor < this.moves.length - 1) - // To play a received move, cursor must be at the end of the game: - this.gotoEnd(); - } - if (!!noemit) { - if (this.inPlay) { - // Received moves in observed games can arrive too fast: + if (this.autoplay || this.inPlay) { + // Received moves while autoplaying are stacked, + // and in observed games they could arrive too fast: this.stackToPlay.unshift(move); return; } this.inPlay = true; + if (this.mode == "analyze") this.toggleAnalyze(); + if (this.cursor < this.moves.length - 1) + // To play a received move, cursor must be at the end of the game: + this.gotoEnd(); } // The board may show some the possible moves: (TODO: bad solution) this.$refs["board"].resetCurrentAttempt(); @@ -494,8 +480,7 @@ export default { if (animate && smove.start.x >= 0) { self.animateMove(smove, () => { playSubmove(smove); - if (moveIdx < move.length) - setTimeout(executeMove, 500); + if (moveIdx < move.length) setTimeout(executeMove, 500); else afterMove(smove, initurn); }); } else { @@ -508,7 +493,7 @@ export default { const computeScore = () => { const score = this.vr.getCurrentScore(); if (!navigate) { - if (["1-0","0-1"].includes(score)) { + if (["1-0", "0-1"].includes(score)) { if (Array.isArray(this.lastMove)) { const L = this.lastMove.length; this.lastMove[L - 1].notation += "#"; @@ -532,18 +517,27 @@ export default { this.emitFenIfAnalyze(); this.inMultimove = false; this.score = computeScore(); + if (this.autoplay) { + if (this.cursor < this.moves.length - 1) + setTimeout(this.play, 1000); + else { + this.autoplay = false; + if (this.stackToPlay.length > 0) + // Move(s) arrived in-between + this.play(this.stackToPlay.pop(), "received"); + } + } if (this.mode != "analyze" && !navigate) { - if (!noemit && this.mode != "analyze") { + if (!received) { // 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. + // NOTE: always emit the score, even in unfinished this.$emit("newmove", this.moves[L-1], { score: this.score }); } else { this.inPlay = false; if (this.stackToPlay.length > 0) // Move(s) arrived in-between - this.play(this.stackToPlay.pop(), received, light, noemit); + this.play(this.stackToPlay.pop(), "received"); } } } diff --git a/client/src/components/ComputerGame.vue b/client/src/components/ComputerGame.vue index 44bfb6a6..ad56543e 100644 --- a/client/src/components/ComputerGame.vue +++ b/client/src/components/ComputerGame.vue @@ -43,8 +43,8 @@ export default { let self = this; setTimeout(() => { if (this.currentUrl != document.location.href) return; //page change - // NOTE: BaseGame::play() will trigger processMove() here self.$refs["basegame"].play(compMove, "received"); + self.processMove(compMove); self.compThink = false; if (self.game.score != "*") // User action @@ -89,6 +89,7 @@ export default { processMove: function(move, scoreObj) { playMove(move, this.vr); // This move could have ended the game: + if (!scoreObj) scoreObj = { score: this.vr.getCurrentScore() }; if (scoreObj.score != "*") { this.gameOver(scoreObj.score); return; diff --git a/client/src/components/GameList.vue b/client/src/components/GameList.vue index 5596e448..d81a1de7 100644 --- a/client/src/components/GameList.vue +++ b/client/src/components/GameList.vue @@ -64,8 +64,9 @@ export default { if ( this.st.user.sid == g.players[0].sid || this.st.user.id == g.players[0].id - ) + ) { return g.players[1].name || "@nonymous"; + } return g.players[0].name || "@nonymous"; }, sortedGames: function() { diff --git a/client/src/translations/about/en.pug b/client/src/translations/about/en.pug index 4f08d9ad..7023912d 100644 --- a/client/src/translations/about/en.pug +++ b/client/src/translations/about/en.pug @@ -55,3 +55,4 @@ h3 Related links a(href="https://www.facebook.com/groups/592562551198628") A Facebook group a(href="http://www.zillions-of-games.com/") zillions-of-games.com a(href="https://en.wikipedia.org/wiki/Fairy_chess_piece") Fairy chess pieces + a(href="http://abrobecker.free.fr/chess/fairyblitz.htm") fairyblitz.htm diff --git a/client/src/translations/about/es.pug b/client/src/translations/about/es.pug index 1dc2b9bc..2c5860f6 100644 --- a/client/src/translations/about/es.pug +++ b/client/src/translations/about/es.pug @@ -53,3 +53,4 @@ h3 Enlaces relacionados a(href="http://www.zillions-of-games.com/") zillions-of-games.com a(href="https://en.wikipedia.org/wiki/Fairy_chess_piece") | Piezas de ajedrez magicas + a(href="http://abrobecker.free.fr/chess/fairyblitz.htm") fairyblitz.htm diff --git a/client/src/translations/about/fr.pug b/client/src/translations/about/fr.pug index be36f3ea..32da0e02 100644 --- a/client/src/translations/about/fr.pug +++ b/client/src/translations/about/fr.pug @@ -52,3 +52,4 @@ h3 Liens connexes a(href="http://www.zillions-of-games.com/") zillions-of-games.com a(href="https://en.wikipedia.org/wiki/Fairy_chess_piece") | Pièces d'échecs féériques + a(href="http://abrobecker.free.fr/chess/fairyblitz.htm") fairyblitz.htm diff --git a/client/src/translations/rules/Ambiguous/en.pug b/client/src/translations/rules/Ambiguous/en.pug new file mode 100644 index 00000000..1db93674 --- /dev/null +++ b/client/src/translations/rules/Ambiguous/en.pug @@ -0,0 +1,37 @@ +p.boxed + | Your moves are decided by the opponent, to a square that you choose. + +p. + At each turn you must first play a move with opponent's pieces to the + marked square, and then play a move freely with your pieces. + The target in the first part of the move is indicated either by a colored + discus for an empty square, or by a colored piece for a capture. + +p. + Exceptions: the first move of the game (which just mark a square), and a + move which captures a king don't have a second part. + +p. + No check or checkmate, one wins by capturing the opponent's king. + There is no castling. + +figure.diagram-container + .diagram.diag12 + | fen:rnbqkbnr/pppppppp/8/8/8/5g2/PPPPPPPP/RNBQKBNR: + .diagram.diag12 + | fen:rnbqkbnr/pppppppp/8/3G4/8/5P2/PPPPP1PP/RNBQKBNR: + figcaption. + Left: White plays 1.Nf3. + Right: Black executes 1.f3 instead, then plays 1...d5. + +h3 Source + +p + | The + a(href="http://www.pion.ch/echecs/variante.php?jeu=ambigus") + | rules description + |  by the author of this variant, which is also playable + a(href="https://brainking.com/en/GameRules?tp=85") on brainking.com + | . + +p Inventor: Fabrice Liardet (2005) diff --git a/client/src/translations/rules/Ambiguous/es.pug b/client/src/translations/rules/Ambiguous/es.pug new file mode 100644 index 00000000..af1f07d2 --- /dev/null +++ b/client/src/translations/rules/Ambiguous/es.pug @@ -0,0 +1,38 @@ +p.boxed + | Tus jugadas son decididos por el oponente, hacia una casilla que tendrás + | elegido. + +p. + En cada turno, primero debes hacer un movimiento con las piezas opuestas + hacia el espacio designado, luego juega libremente con tus piezas. + El objetivo en la primera parte del movimiento se indica mediante un disco + coloreado para una casilla vacía, o por una pieza coloreada para una captura. + +p. + Excepciones: el primer movimiento del juego (que solo marca un cuadrado), y + una jugada que captura a un rey no tiene una segunda parte. + +p. + Sin jaque o jaque mate, el objetivo es capturar al rey contrario. + No hay enroque. + +figure.diagram-container + .diagram.diag12 + | fen:rnbqkbnr/pppppppp/8/8/8/5g2/PPPPPPPP/RNBQKBNR: + .diagram.diag12 + | fen:rnbqkbnr/pppppppp/8/3G4/8/5P2/PPPPP1PP/RNBQKBNR: + figcaption. + Izquierda: las blancas juegan 1.Nf3. + Derecha: las negras ejecutan 1.f3 en su lugar, luego juegan 1...d5. + +h3 Fuente + +p + | La + a(href="http://www.pion.ch/echecs/variante.php?jeu=ambigus") + | descripción de las reglas + |  por el autor de esta variante, que también es jugable + a(href="https://brainking.com/en/GameRules?tp=85") en brainking.com + | . + +p Inventor: Fabrice Liardet (2005) diff --git a/client/src/translations/rules/Ambiguous/fr.pug b/client/src/translations/rules/Ambiguous/fr.pug new file mode 100644 index 00000000..e71661c2 --- /dev/null +++ b/client/src/translations/rules/Ambiguous/fr.pug @@ -0,0 +1,38 @@ +p.boxed + | Vos coups sont décidés par l'adversaire, vers une case que vous aurez + | choisi. + +p. + À chaque tour vous devez d'abord jouer un coup avec les pièces adverses + vers la case désignée, puis jouer un coup librement avec vos pièces. + La cible dans la première partie du coup est indiquée soit par un disque + coloré pour une case vide, soit par une pièce colorée pour une capture. + +p. + Exceptions : le premier coup de la partie (qui marque juste une case), et + un coup capturant un roi n'ont pas de seconde partie. + +p. + Pas d'échec ou de mat, l'objectif est de capturer le roi adverse. + Il n'y a pas de roque. + +figure.diagram-container + .diagram.diag12 + | fen:rnbqkbnr/pppppppp/8/8/8/5g2/PPPPPPPP/RNBQKBNR: + .diagram.diag12 + | fen:rnbqkbnr/pppppppp/8/3G4/8/5P2/PPPPP1PP/RNBQKBNR: + figcaption. + Gauche : Les blancs jouent 1.Nf3. + Droite : Les noirs exécutent 1.f3 à la place, puis jouent 1...d5. + +h3 Source + +p + | La + a(href="http://www.pion.ch/echecs/variante.php?jeu=ambigus") + | description des règles + |  par l'auteur de cette variante, qui est jouable également + a(href="https://brainking.com/en/GameRules?tp=85") sur brainking.com + | . + +p Inventeur : Fabrice Liardet (2005) diff --git a/client/src/translations/rules/Rifle/en.pug b/client/src/translations/rules/Rifle/en.pug index 04c41724..561346ce 100644 --- a/client/src/translations/rules/Rifle/en.pug +++ b/client/src/translations/rules/Rifle/en.pug @@ -23,4 +23,4 @@ p a(href="https://www.chessvariants.com/difftaking.dir/rifle.html") Rifle Chess |  on chessvariants.com. -p Inventor: W. B. Seabrook (1921) +p Inventor: William Buehler Seabrook (1921) diff --git a/client/src/translations/rules/Rifle/es.pug b/client/src/translations/rules/Rifle/es.pug index 5b76a3e8..d98f3e59 100644 --- a/client/src/translations/rules/Rifle/es.pug +++ b/client/src/translations/rules/Rifle/es.pug @@ -27,4 +27,4 @@ p | variante Rifle |  en chessvariants.com. -p Inventor: W. B. Seabrook (1921) +p Inventor: William Buehler Seabrook (1921) diff --git a/client/src/translations/rules/Rifle/fr.pug b/client/src/translations/rules/Rifle/fr.pug index 08913d10..3aa407a5 100644 --- a/client/src/translations/rules/Rifle/fr.pug +++ b/client/src/translations/rules/Rifle/fr.pug @@ -26,4 +26,4 @@ p | variante Rifle |  sur chessvariants.com. -p Inventeur : W. B. Seabrook (1921) +p Inventeur : William Buehler Seabrook (1921) diff --git a/client/src/variants/Ambiguous.js b/client/src/variants/Ambiguous.js new file mode 100644 index 00000000..dea19d55 --- /dev/null +++ b/client/src/variants/Ambiguous.js @@ -0,0 +1,257 @@ +import { ChessRules } from "@/base_rules"; + +export class AmbiguousRules extends ChessRules { + static get HasFlags() { + return false; + } + + setOtherVariables(fen) { + super.setOtherVariables(fen); + if (this.movesCount == 0) this.subTurn = 2; + else this.subTurn = 1; + } + + // Subturn 1: play a move for the opponent on the designated square. + // Subturn 2: play a move for me (which just indicate a square). + getPotentialMovesFrom([x, y]) { + const color = this.turn; + const oppCol = V.GetOppCol(color); + if (this.subTurn == 2) { + // Just play a normal move (which in fact only indicate a square) + return ( + super.getPotentialMovesFrom([x, y]) + .map(m => { + if (m.vanish.length == 1) m.appear[0].p = V.GOAL; + else m.appear[0].p = V.TARGET_CODE[m.vanish[1].p]; + m.appear[0].c = oppCol; + m.vanish.shift(); + return m; + }) + ); + } + // At subTurn == 1, play a targeted move for opponent + // Search for target (we could also have it in a stack...) + let target = { x: -1, y: -1 }; + outerLoop: for (let i = 0; i < V.size.x; i++) { + for (let j = 0; j < V.size.y; j++) { + if (this.board[i][j] != V.EMPTY) { + const piece = this.board[i][j][1]; + if ( + piece == V.GOAL || + Object.keys(V.TARGET_DECODE).includes(piece) + ) { + target = { x: i, y: j}; + break outerLoop; + } + } + } + } + // TODO: could be more efficient than generating all moves. + this.turn = oppCol; + const emptyTarget = (this.board[target.x][target.y][1] == V.GOAL); + if (emptyTarget) this.board[target.x][target.y] = V.EMPTY; + let moves = super.getPotentialMovesFrom([x, y]); + if (emptyTarget) { + this.board[target.x][target.y] = color + V.GOAL; + moves.forEach(m => { + m.vanish.push({ + x: target.x, + y: target.y, + c: color, + p: V.GOAL + }); + }); + } + this.turn = color; + return moves.filter(m => m.end.x == target.x && m.end.y == target.y); + } + + canIplay(side, [x, y]) { + const color = this.getColor(x, y); + return ( + (this.subTurn == 1 && color != side) || + (this.subTurn == 2 && color == side) + ); + } + + getPpath(b) { + if (b[1] == V.GOAL || Object.keys(V.TARGET_DECODE).includes(b[1])) + return "Ambiguous/" + b; + return b; + } + + // Code for empty square target + static get GOAL() { + return 'g'; + } + + static get TARGET_DECODE() { + return { + 's': 'p', + 't': 'q', + 'u': 'r', + 'o': 'n', + 'c': 'b', + 'l': 'k' + }; + } + + static get TARGET_CODE() { + return { + 'p': 's', + 'q': 't', + 'r': 'u', + 'n': 'o', + 'b': 'c', + 'k': 'l' + }; + } + + static get PIECES() { + return ( + ChessRules.PIECES.concat(Object.keys(V.TARGET_DECODE)).concat([V.GOAL]) + ); + } + + getAllPotentialMoves() { + const color = this.turn; + let potentialMoves = []; + for (let i = 0; i < V.size.x; i++) { + for (let j = 0; j < V.size.y; j++) { + if ( + this.board[i][j] != V.EMPTY && + this.getColor(i, j) == color && + this.board[i][j][1] != V.GOAL && + !(Object.keys(V.TARGET_DECODE).includes(this.board[i][j][1])) + ) { + Array.prototype.push.apply( + potentialMoves, + this.getPotentialMovesFrom([i, j]) + ); + } + } + } + return potentialMoves; + } + + atLeastOneMove() { + // Since there are no checks this seems true (same as for Magnetic...) + return true; + } + + filterValid(moves) { + return moves; + } + + getCheckSquares() { + return []; + } + + getCurrentScore() { + // This function is only called at subTurn 1 + const color = V.GetOppCol(this.turn); + if (this.kingPos[color][0] < 0) return (color == 'w' ? "0-1" : "1-0"); + return "*"; + } + + prePlay(move) { + const c = V.GetOppCol(this.turn); + const piece = move.vanish[0].p; + if (piece == V.KING) { + // (Opp) king moves: + this.kingPos[c][0] = move.appear[0].x; + this.kingPos[c][1] = move.appear[0].y; + } + if (move.vanish.length == 2 && [V.KING, 'l'].includes(move.vanish[1].p)) + // (My) king is captured: + this.kingPos[this.turn] = [-1, -1]; + } + + play(move) { + let kingCaptured = false; + if (this.subTurn == 1) { + this.prePlay(move); + this.epSquares.push(this.getEpSquare(move)); + kingCaptured = this.kingPos[this.turn][0] < 0; + } + if (kingCaptured) move.kingCaptured = true; + V.PlayOnBoard(this.board, move); + if (this.subTurn == 2 || kingCaptured) { + this.turn = V.GetOppCol(this.turn); + this.movesCount++; + } + if (!kingCaptured) this.subTurn = 3 - this.subTurn; + } + + undo(move) { + if (!move.kingCaptured) this.subTurn = 3 - this.subTurn; + if (this.subTurn == 2 || !!move.kingCaptured) { + this.turn = V.GetOppCol(this.turn); + this.movesCount--; + } + V.UndoOnBoard(this.board, move); + if (this.subTurn == 1) { + this.epSquares.pop(); + this.postUndo(move); + } + } + + postUndo(move) { + // (Potentially) Reset king(s) position + const c = V.GetOppCol(this.turn); + const piece = move.vanish[0].p; + if (piece == V.KING) { + // (Opp) king moved: + this.kingPos[c][0] = move.vanish[0].x; + this.kingPos[c][1] = move.vanish[0].y; + } + if (move.vanish.length == 2 && [V.KING, 'l'].includes(move.vanish[1].p)) + // (My) king was captured: + this.kingPos[this.turn] = [move.vanish[1].x, move.vanish[1].y]; + } + + static GenRandInitFen(randomness) { + if (randomness == 0) + return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 -"; + + let pieces = { w: new Array(8), b: new Array(8) }; + for (let c of ["w", "b"]) { + if (c == 'b' && randomness == 1) { + pieces['b'] = pieces['w']; + break; + } + + // Get random squares for every piece, totally freely + let positions = shuffle(ArrayFun.range(8)); + const composition = ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q']; + const rem2 = positions[0] % 2; + if (rem2 == positions[1] % 2) { + // Fix bishops (on different colors) + for (let i=2; i<8; i++) { + if (positions[i] % 2 != rem2) + [positions[1], positions[i]] = [positions[i], positions[1]]; + } + } + for (let i = 0; i < 8; i++) pieces[c][positions[i]] = composition[i]; + } + return ( + pieces["b"].join("") + + "/pppppppp/8/8/8/8/PPPPPPPP/" + + pieces["w"].join("").toUpperCase() + + // En-passant allowed, but no flags + " w 0 -" + ); + } + + getNotation(move) { + if (this.subTurn == 2) return "T:" + V.CoordsToSquare(move.end); + // Remove and re-add target to get a good notation: + const withTarget = move.vanish[1]; + if (move.vanish[1].p == V.GOAL) move.vanish.pop(); + else move.vanish[1].p = V.TARGET_DECODE[move.vanish[1].p]; + const notation = super.getNotation(move); + if (move.vanish.length == 1) move.vanish.push(withTarget); + else move.vanish[1].p = V.TARGET_CODE[move.vanish[1].p]; + return notation; + } +}; diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue index 7dae055e..0d9cba22 100644 --- a/client/src/views/Game.vue +++ b/client/src/views/Game.vue @@ -815,8 +815,7 @@ export default { GameStorage.update(this.gameRef, { drawOffer: "" }); } } - this.$refs["basegame"].play( - movePlus.move, "received", null, true); + this.$refs["basegame"].play(movePlus.move, "received"); this.game.clocks[moveColIdx] = movePlus.clock; this.processMove( movePlus.move, @@ -883,6 +882,8 @@ export default { this.$router.push("/game/" + gameInfo.id); } else { this.rematchId = gameInfo.id; + document.getElementById("modalRules").checked = false; + document.getElementById("modalScore").checked = false; document.getElementById("modalRematch").checked = true; } break; @@ -945,7 +946,7 @@ export default { this.game.clocks[oppIdx] = data.clock; if (data.movesCount > L) { // Just got last move from him - this.$refs["basegame"].play(data.lastMove, "received", null, true); + this.$refs["basegame"].play(data.lastMove, "received"); this.processMove(data.lastMove); } else { if (!!this.clockUpdate) clearInterval(this.clockUpdate); @@ -1566,6 +1567,7 @@ export default { this.game.score = score; if (!scoreMsg) scoreMsg = getScoreMessage(score); this.game.scoreMsg = scoreMsg; + document.getElementById("modalRules").checked = false; // Display result in a un-missable way: document.getElementById("modalScore").checked = true; this.$set(this.game, "scoreMsg", scoreMsg); diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue index 04ac6b84..cb088088 100644 --- a/client/src/views/Hall.vue +++ b/client/src/views/Hall.vue @@ -186,13 +186,13 @@ main GameList( v-show="gdisplay=='live'" :games="filterGames('live')" - :showBoth="true" + :show-both="true" @show-game="showGame" ) div(v-show="gdisplay=='corr'") GameList( :games="filterGames('corr')" - :showBoth="true" + :show-both="true" @show-game="showGame" ) button#loadMoreBtn( diff --git a/client/src/views/MyGames.vue b/client/src/views/MyGames.vue index 10ec0c27..a48c8d47 100644 --- a/client/src/views/MyGames.vue +++ b/client/src/views/MyGames.vue @@ -31,6 +31,7 @@ main v-show="display=='import'" ref="importgames" :games="importGames" + :show-both="true" @show-game="showGame" ) button#loadMoreBtn(