From: Benjamin Auder Date: Fri, 11 Dec 2020 23:57:51 +0000 (+0100) Subject: Fix Xiangqi + a few cosmetics X-Git-Url: https://git.auder.net/?p=vchess.git;a=commitdiff_plain;h=9a1e3abe33fff07218b17c7c799eb622a730b7c7 Fix Xiangqi + a few cosmetics --- diff --git a/client/src/App.vue b/client/src/App.vue index 82c9bf33..67dc225b 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -157,7 +157,9 @@ table padding: 0 10px 0 0 height: 100% & > span - padding: 0 5px 0 0 + padding-top: 0 + padding-bottom: 0 + padding-right: 5px vertical-align: middle & > img padding: 0 diff --git a/client/src/base_rules.js b/client/src/base_rules.js index 46c09a5c..2c2b705c 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -126,6 +126,11 @@ export const ChessRules = class ChessRules { return null; } + // In some variants, the player who repeat a position loses + static get LoseOnRepetition() { + return false; + } + // Some variants use click infos: doClick() { return null; diff --git a/client/src/translations/en.js b/client/src/translations/en.js index 708fed0a..a523ff82 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -116,6 +116,7 @@ export const translations = { Rematch: "Rematch", "Rematch in progress": "Rematch in progress", "Remove game?": "Remove game?", + Repetition: "Repetition", Resign: "Resign", "Resign the game?": "Resign the game?", "Resize board": "Resize board", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index 9542c286..9fd30769 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -116,6 +116,7 @@ export const translations = { Rematch: "Revancha", "Rematch in progress": "Revancha en progreso", "Remove game?": "¿Eliminar la partida?", + Repetition: "Repetición", Resign: "Abandonar", "Resign the game?": "¿Abandonar la partida?", "Resize board": "Redimensionar el tablero", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index 4adeff38..466137b4 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -116,6 +116,7 @@ export const translations = { Rematch: "Rejouer", "Rematch in progress": "Revanche en cours", "Remove game?": "Supprimer la partie ?", + Repetition: "Répétition", Resign: "Abandonner", "Resign the game?": "Abandonner la partie ?", "Resize board": "Redimensionner l'échiquier", diff --git a/client/src/variants/Minixiangqi.js b/client/src/variants/Minixiangqi.js index c027aedf..34e7d198 100644 --- a/client/src/variants/Minixiangqi.js +++ b/client/src/variants/Minixiangqi.js @@ -31,17 +31,6 @@ export class MinixiangqiRules extends XiangqiRules { return { x: 7, y: 7}; } - getPotentialMovesFrom(sq) { - switch (this.getPiece(sq[0], sq[1])) { - case V.PAWN: return this.getPotentialPawnMoves(sq); - case V.ROOK: return super.getPotentialRookMoves(sq); - case V.KNIGHT: return super.getPotentialKnightMoves(sq); - case V.KING: return super.getPotentialKingMoves(sq); - case V.CANNON: return super.getPotentialCannonMoves(sq); - } - return []; //never reached - } - getPotentialPawnMoves([x, y]) { const c = this.getColor(x, y); const shiftX = (c == 'w' ? -1 : 1); diff --git a/client/src/variants/Xiangqi.js b/client/src/variants/Xiangqi.js index 3889b20a..1b49cedb 100644 --- a/client/src/variants/Xiangqi.js +++ b/client/src/variants/Xiangqi.js @@ -36,6 +36,10 @@ export class XiangqiRules extends ChessRules { return false; } + static get LoseOnRepetition() { + return true; + } + static get ELEPHANT() { return "e"; } @@ -61,16 +65,80 @@ export class XiangqiRules extends ChessRules { } getPotentialMovesFrom(sq) { - switch (this.getPiece(sq[0], sq[1])) { - case V.PAWN: return this.getPotentialPawnMoves(sq); - case V.ROOK: return super.getPotentialRookMoves(sq); - case V.KNIGHT: return this.getPotentialKnightMoves(sq); - case V.ELEPHANT: return this.getPotentialElephantMoves(sq); - case V.ADVISOR: return this.getPotentialAdvisorMoves(sq); - case V.KING: return this.getPotentialKingMoves(sq); - case V.CANNON: return this.getPotentialCannonMoves(sq); + let moves = []; + const piece = this.getPiece(sq[0], sq[1]); + switch (piece) { + case V.PAWN: + moves = this.getPotentialPawnMoves(sq); + break; + case V.ROOK: + moves = super.getPotentialRookMoves(sq); + break; + case V.KNIGHT: + moves = this.getPotentialKnightMoves(sq); + break; + case V.ELEPHANT: + moves = this.getPotentialElephantMoves(sq); + break; + case V.ADVISOR: + moves = this.getPotentialAdvisorMoves(sq); + break; + case V.KING: + moves = this.getPotentialKingMoves(sq); + break; + case V.CANNON: + moves = this.getPotentialCannonMoves(sq); + break; + } + if (piece != V.KING && this.kingPos['w'][1] != this.kingPos['b'][1]) + return moves; + if (this.kingPos['w'][1] == this.kingPos['b'][1]) { + const colKing = this.kingPos['w'][1]; + let intercept = 0; //count intercepting pieces + for (let i = this.kingPos['b'][0] + 1; i < this.kingPos['w'][0]; i++) { + if (this.board[i][colKing] != V.EMPTY) intercept++; + } + if (intercept >= 2) return moves; + // intercept == 1 (0 is impossible): + // Any move not removing intercept is OK + return moves.filter(m => { + return ( + // From another column? + m.start.y != colKing || + // From behind a king? (including kings themselves!) + m.start.x <= this.kingPos['b'][0] || + m.start.x >= this.kingPos['w'][0] || + // Intercept piece moving: must remain in-between + ( + m.end.y == colKing && + m.end.x > this.kingPos['b'][0] && + m.end.x < this.kingPos['w'][0] + ) + ); + }); } - return []; //never reached + // piece == king: check only if move.end.y == enemy king column + const color = this.getColor(sq[0], sq[1]); + const oppCol = V.GetOppCol(color); + // colCheck == -1 if unchecked, 1 if checked and occupied, + // 0 if checked and clear + let colCheck = -1; + return moves.filter(m => { + if (m.end.y != this.kingPos[oppCol][1]) return true; + if (colCheck < 0) { + // Do the check: + colCheck = 0; + for (let i = this.kingPos['b'][0] + 1; i < this.kingPos['w'][0]; i++) { + if (this.board[i][m.end.y] != V.EMPTY) { + colCheck++; + break; + } + } + return colCheck == 1; + } + // Check already done: + return colCheck == 1; + }); } getPotentialPawnMoves([x, y]) { @@ -255,6 +323,14 @@ export class XiangqiRules extends ChessRules { return false; } + getCurrentScore() { + if (this.atLeastOneMove()) return "*"; + // Game over + const color = this.turn; + // No valid move: I lose! + return (color == "w" ? "0-1" : "1-0"); + } + static get VALUES() { return { p: 1, diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue index d66716c2..d88ac1e2 100644 --- a/client/src/views/Game.vue +++ b/client/src/views/Game.vue @@ -1391,7 +1391,11 @@ export default { !!this.repeat[fenObj] ? this.repeat[fenObj] + 1 : 1; - if (this.repeat[fenObj] >= 3) this.drawOffer = "threerep"; + if (this.repeat[fenObj] >= 3) { + if (V.LoseOnRepetition) + this.gameOver(moveCol == "w" ? "0-1" : "1-0", "Repetition"); + else this.drawOffer = "threerep"; + } else if (this.drawOffer == "threerep") this.drawOffer = ""; if (!!this.game.mycolor && !data.receiveMyMove) { // NOTE: 'var' to see that variable outside this block diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue index 0b3bb4ef..03d34119 100644 --- a/client/src/views/Hall.vue +++ b/client/src/views/Hall.vue @@ -1350,7 +1350,7 @@ export default { new Audio("/sounds/newgame.flac").play().catch(() => {}); notify( "New live game", - { body: "vs " + game.players[1-myIdx].name || "@nonymous" } + { body: "vs " + (game.players[1-myIdx].name || "@nonymous") } ); } this.$router.push("/game/" + gameInfo.id);