From: Benjamin Auder Date: Fri, 19 Mar 2021 09:30:53 +0000 (+0100) Subject: Add Knightmate2: two kings, as in Spartan Chess X-Git-Url: https://git.auder.net/?p=vchess.git;a=commitdiff_plain;h=3de62e0a608bd2be22c875930bb538b674968a6b Add Knightmate2: two kings, as in Spartan Chess --- diff --git a/client/src/translations/en.js b/client/src/translations/en.js index 5a5461da..e3f361c7 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -259,7 +259,8 @@ export const translations = { "Mandatory captures": "Mandatory captures", "Mate any piece (v1)": "Mate any piece (v1)", "Mate any piece (v2)": "Mate any piece (v2)", - "Mate the knight": "Mate the knight", + "Mate the knight (v1)": "Mate the knight (v1)", + "Mate the knight (v2)": "Mate the knight (v2)", "Meet the Mammoth": "Meet the Mammoth", "Middle battle": "Middle battle", "Mind control (v1)": "Mind control (v1)", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index f868d266..3869b5de 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -259,7 +259,8 @@ export const translations = { "Mandatory captures": "Capturas obligatorias", "Mate any piece (v1)": "Matar cualquier pieza (v1)", "Mate any piece (v2)": "Matar cualquier pieza (v2)", - "Mate the knight": "Matar el caballo", + "Mate the knight (v1)": "Matar el caballo (v1)", + "Mate the knight (v2)": "Matar el caballo (v2)", "Meet the Mammoth": "Conoce al Mamut", "Middle battle": "Batalla media", "Mind control (v1)": "Control telepático(v1)", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index b0a92165..e75273a5 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -259,7 +259,8 @@ export const translations = { "Mandatory captures": "Captures obligatoires", "Mate any piece (v1)": "Matez n'importe quelle pièce (v1)", "Mate any piece (v2)": "Matez n'importe quelle pièce (v2)", - "Mate the knight": "Matez le cavalier", + "Mate the knight (v1)": "Matez le cavalier (v1)", + "Mate the knight (v2)": "Matez le cavalier (v2)", "Meet the Mammoth": "Rencontrez le Mammouth", "Middle battle": "Bataille du milieu", "Mind control (v1)": "Contrôle télépathique (v1)", diff --git a/client/src/translations/rules/Clorange/en.pug b/client/src/translations/rules/Clorange/en.pug index 26679f58..695b676d 100644 --- a/client/src/translations/rules/Clorange/en.pug +++ b/client/src/translations/rules/Clorange/en.pug @@ -23,7 +23,6 @@ figure.diagram-container h3 Source p - | Slightly simplified from a(href="https://www.chessvariants.com/other.dir/clockworkorange.html") | Clockwork Orange Chess |  on chessvariants.com. diff --git a/client/src/translations/rules/Clorange/es.pug b/client/src/translations/rules/Clorange/es.pug index a5a8c832..ab73e356 100644 --- a/client/src/translations/rules/Clorange/es.pug +++ b/client/src/translations/rules/Clorange/es.pug @@ -26,7 +26,6 @@ figure.diagram-container h3 Fuente p - | Ligeramente simplificado desde a(href="https://www.chessvariants.com/other.dir/clockworkorange.html") | Clockwork Orange Chess |  en chessvariants.com. diff --git a/client/src/translations/rules/Clorange/fr.pug b/client/src/translations/rules/Clorange/fr.pug index 1e8a4069..44888fa3 100644 --- a/client/src/translations/rules/Clorange/fr.pug +++ b/client/src/translations/rules/Clorange/fr.pug @@ -26,7 +26,6 @@ figure.diagram-container h3 Source p - | Légèrement simplifié depuis a(href="https://www.chessvariants.com/other.dir/clockworkorange.html") | Clockwork Orange Chess |  sur chessvariants.com. diff --git a/client/src/translations/rules/Knightmate/en.pug b/client/src/translations/rules/Knightmate1/en.pug similarity index 100% rename from client/src/translations/rules/Knightmate/en.pug rename to client/src/translations/rules/Knightmate1/en.pug diff --git a/client/src/translations/rules/Knightmate/es.pug b/client/src/translations/rules/Knightmate1/es.pug similarity index 100% rename from client/src/translations/rules/Knightmate/es.pug rename to client/src/translations/rules/Knightmate1/es.pug diff --git a/client/src/translations/rules/Knightmate/fr.pug b/client/src/translations/rules/Knightmate1/fr.pug similarity index 96% rename from client/src/translations/rules/Knightmate/fr.pug rename to client/src/translations/rules/Knightmate1/fr.pug index 7e0b3867..dcb7c2b7 100644 --- a/client/src/translations/rules/Knightmate/fr.pug +++ b/client/src/translations/rules/Knightmate1/fr.pug @@ -1,6 +1,6 @@ p.boxed | Le roi se déplace comme un cavalier, et les cavaliers comme des rois. - | l'objectif est encore de mater le roi. + | L'objectif est encore de mater le roi. p. Les "cavaliers se déplaçant comme des rois" sont alors assez logiquement diff --git a/client/src/translations/rules/Knightmate2/en.pug b/client/src/translations/rules/Knightmate2/en.pug new file mode 100644 index 00000000..dd759e57 --- /dev/null +++ b/client/src/translations/rules/Knightmate2/en.pug @@ -0,0 +1,19 @@ +p.boxed + | Kings move like knights, and knights move like kings. + +p + a(href="/#/variants/Knightmate1") Knightmate1 + | , with two kings (moving like knights) and without castling. + +p. + As long as a side has two kings, they are considered like non-royal pieces + and one can be captured. Then, the remaining king is royal. + +figure.diagram-container + .diagram + | fen:r6r/1pp1qpp1/p1pcbk1p/2b1p1K1/4P3/2KPBP2/PPPQC1PP/R6R: + figcaption g5 king is mated, but the game is not over. + +p. + Checkmating both kings at the same time also counts as a win: + if both are under attacks, you must remove at least one attack. diff --git a/client/src/translations/rules/Knightmate2/es.pug b/client/src/translations/rules/Knightmate2/es.pug new file mode 100644 index 00000000..bf0157fa --- /dev/null +++ b/client/src/translations/rules/Knightmate2/es.pug @@ -0,0 +1,20 @@ +p.boxed + | Los reyes se mueven como caballos y los caballos como reyes. + +p + a(href="/#/variants/Knightmate1") Knightmate1 + | , con dos reyes (muevense como caballos) y sin enroque. + +p. + Siempre que un lado tenga sus dos reyes, se consideran piezas normales: + se puede capturar uno de los dos. Entonces el rey restante + tiene estatus real. + +figure.diagram-container + .diagram + | fen:r6r/1pp1qpp1/p1pcbk1p/2b1p1K1/4P3/2KPBP2/PPPQC1PP/R6R: + figcaption El rey g5 está atrapado, pero el juego continúa. + +p. + Matar a ambos reyes al mismo tiempo también gana: + si ambos son atacados, debes reprimir al menos un ataque. diff --git a/client/src/translations/rules/Knightmate2/fr.pug b/client/src/translations/rules/Knightmate2/fr.pug new file mode 100644 index 00000000..efff2237 --- /dev/null +++ b/client/src/translations/rules/Knightmate2/fr.pug @@ -0,0 +1,20 @@ +p.boxed + | Les rois se déplacent comme des cavaliers, et les cavaliers comme des rois. + +p + a(href="/#/variants/Knightmate1") Knightmate1 + | , avec deux rois (aux déplacements cavaliers) et sans roque. + +p. + Tant qu'un camp a ses deux rois, ils sont considérés comme des pièces + normales : l'un des deux peut être capturé. Ensuite, le roi restant + a un statut royal. + +figure.diagram-container + .diagram + | fen:r6r/1pp1qpp1/p1pcbk1p/2b1p1K1/4P3/2KPBP2/PPPQC1PP/R6R: + figcaption Le roi g5 est maté, mais la partie continue. + +p. + Mater les deux rois en même temps gagne également : + si les deux sont attaqués, vous devez supprimez au moins une attaque. diff --git a/client/src/translations/variants/en.pug b/client/src/translations/variants/en.pug index e5e92f84..15a8aa5e 100644 --- a/client/src/translations/variants/en.pug +++ b/client/src/translations/variants/en.pug @@ -90,6 +90,7 @@ p Standard pieces versus a team of different pieces. "Empire", "Horde", "Orda", + "Shinobi", "Spartan", "Synochess" ] @@ -249,7 +250,8 @@ p. - var varlist = [ "Balaklava", - "Knightmate", + "Knightmate1", + "Knightmate2", "Knightrelay1", "Knightrelay2" ] @@ -313,7 +315,7 @@ ul h3 Repositioning -p Pieces can be drop on the board, either immediately or later in the game. +p Pieces can be dropped on the board, either immediately or later in the game. - var varlist = [ "Clorange", @@ -321,7 +323,6 @@ p Pieces can be drop on the board, either immediately or later in the game. "Madhouse", "Rampage", "Recycle", - "Shinobi", "Shogun", "Teleport" ] diff --git a/client/src/translations/variants/es.pug b/client/src/translations/variants/es.pug index a09ce9d3..cc855000 100644 --- a/client/src/translations/variants/es.pug +++ b/client/src/translations/variants/es.pug @@ -94,6 +94,7 @@ p Piezas estándar contra un equipo de diferentes piezas. "Empire", "Horde", "Orda", + "Shinobi", "Spartan", "Synochess" ] @@ -256,7 +257,8 @@ p. - var varlist = [ "Balaklava", - "Knightmate", + "Knightmate1", + "Knightmate2", "Knightrelay1", "Knightrelay2" ] @@ -330,7 +332,6 @@ p. "Madhouse", "Rampage", "Recycle", - "Shinobi", "Shogun", "Teleport" ] diff --git a/client/src/translations/variants/fr.pug b/client/src/translations/variants/fr.pug index 2737859f..f23512ea 100644 --- a/client/src/translations/variants/fr.pug +++ b/client/src/translations/variants/fr.pug @@ -93,6 +93,7 @@ p Pièces standard contre une équipe de pièces différentes. "Empire", "Horde", "Orda", + "Shinobi", "Spartan", "Synochess" ] @@ -255,7 +256,8 @@ p. - var varlist = [ "Balaklava", - "Knightmate", + "Knightmate1", + "Knightmate2", "Knightrelay1", "Knightrelay2" ] @@ -329,7 +331,6 @@ p. "Madhouse", "Rampage", "Recycle", - "Shinobi", "Shogun", "Teleport" ] diff --git a/client/src/variants/Knightmate.js b/client/src/variants/Knightmate1.js similarity index 92% rename from client/src/variants/Knightmate.js rename to client/src/variants/Knightmate1.js index bc886c12..80e7005b 100644 --- a/client/src/variants/Knightmate.js +++ b/client/src/variants/Knightmate1.js @@ -1,8 +1,6 @@ import { ChessRules } from "@/base_rules"; -import { ArrayFun } from "@/utils/array"; -import { randInt } from "@/utils/alea"; -export class KnightmateRules extends ChessRules { +export class Knightmate1Rules extends ChessRules { static get COMMONER() { return "c"; diff --git a/client/src/variants/Knightmate2.js b/client/src/variants/Knightmate2.js new file mode 100644 index 00000000..c90fb0cc --- /dev/null +++ b/client/src/variants/Knightmate2.js @@ -0,0 +1,194 @@ +import { ChessRules } from "@/base_rules"; + +export class Knightmate2Rules extends ChessRules { + + static get HasFlags() { + return false; + } + + static get COMMONER() { + return "c"; + } + + static get PIECES() { + return ChessRules.PIECES.concat([V.COMMONER]); + } + + getPpath(b) { + return ([V.KING, V.COMMONER].includes(b[1]) ? "Knightmate/" : "") + b; + } + + static IsGoodPosition(position) { + if (position.length == 0) return false; + const rows = position.split("/"); + if (rows.length != V.size.x) return false; + let kings = { "k": 0, "K": 0 }; + for (let row of rows) { + let sumElts = 0; + for (let i = 0; i < row.length; i++) { + if (['K','k'].includes(row[i])) kings[row[i]]++; + if (V.PIECES.includes(row[i].toLowerCase())) sumElts++; + else { + const num = parseInt(row[i], 10); + if (isNaN(num) || num <= 0) return false; + sumElts += num; + } + } + if (sumElts != V.size.y) return false; + } + // 1 or 2 kings should be on board. + if (Object.values(kings).some(k => ![1, 2].includes(k))) return false; + return true; + } + + scanKings() {} + + static GenRandInitFen(randomness) { + return ( + ChessRules.GenRandInitFen(randomness) + .replace(/k/g, 'c').replace(/K/g, 'C') + .replace(/n/g, 'k').replace(/N/g, 'K') + ); + } + + getPotentialMovesFrom([x, y]) { + switch (this.getPiece(x, y)) { + case V.COMMONER: + return this.getPotentialCommonerMoves([x, y]); + default: + return super.getPotentialMovesFrom([x, y]); + } + } + + getPotentialCommonerMoves(sq) { + return this.getSlideNJumpMoves( + sq, + V.steps[V.ROOK].concat(V.steps[V.BISHOP]), + "oneStep" + ); + } + + getPotentialKingMoves(sq) { + return super.getPotentialKnightMoves(sq); + } + + isAttacked(sq, color) { + return ( + this.isAttackedByCommoner(sq, color) || + this.isAttackedByPawn(sq, color) || + this.isAttackedByRook(sq, color) || + this.isAttackedByBishop(sq, color) || + this.isAttackedByQueen(sq, color) || + this.isAttackedByKing(sq, color) + ); + } + + isAttackedByKing(sq, color) { + return this.isAttackedBySlideNJump( + sq, + color, + V.KING, + V.steps[V.KNIGHT], + "oneStep" + ); + } + + isAttackedByCommoner(sq, color) { + return this.isAttackedBySlideNJump( + sq, + color, + V.COMMONER, + V.steps[V.ROOK].concat(V.steps[V.BISHOP]), + "oneStep" + ); + } + + postPlay() {} + postUndo() {} + + // NOTE: 4 next functions (almost) copy-paste from Spartan Chess + getKingsPos(color) { + let kings = []; + for (let i=0; i<8; i++) { + for (let j=0; j<8; j++) { + if ( + this.board[i][j] != V.EMPTY && + this.getColor(i, j) == color && + this.getPiece(i, j) == V.KING + ) { + kings.push({ x: i, y: j }); + } + } + } + return kings; + } + + getCheckSquares() { + const color = this.turn; + const oppCol = V.GetOppCol(color); + const kings = this.getKingsPos(color); + let res = []; + for (let i of [0, 1]) { + if ( + kings.length >= i+1 && + super.isAttacked([kings[i].x, kings[i].y], oppCol) + ) { + res.push([kings[i].x, kings[i].y]); + } + } + return res; + } + + filterValid(moves) { + if (moves.length == 0) return []; + const color = moves[0].vanish[0].c; + const oppCol = V.GetOppCol(color); + // Check if both kings under attack. + // If yes, moves must remove at least one attack. + const kings = this.getKingsPos(color); + return moves.filter(m => { + this.play(m); + let attacks = 0; + for (let k of kings) { + const curKingPos = + this.board[k.x][k.y] == V.EMPTY + ? [m.appear[0].x, m.appear[0].y] //king moved + : [k.x, k.y] + if (super.isAttacked(curKingPos, oppCol)) attacks++; + else break; //no need to check further + } + this.undo(m); + return ( + (kings.length == 2 && attacks <= 1) || + (kings.length == 1 && attacks == 0) + ); + }); + } + + getCurrentScore() { + if (super.atLeastOneMove()) return "*"; + // Count kings on board + const color = this.turn; + const oppCol = V.GetOppCol(color); + const kings = this.getKingsPos(color); + if ( + super.isAttacked([kings[0].x, kings[0].y], oppCol) || + (kings.length == 2 && super.isAttacked([kings[1].x, kings[1].y], oppCol)) + ) { + return (color == 'w' ? "0-1" : "1-0"); + } + return "1/2"; //stalemate + } + + static get VALUES() { + return { + p: 1, + r: 5, + c: 5, //the commoner is valuable + b: 3, + q: 9, + k: 1000 + }; + } + +}; diff --git a/server/db/populate.sql b/server/db/populate.sql index 58af05d1..f9722f67 100644 --- a/server/db/populate.sql +++ b/server/db/populate.sql @@ -92,7 +92,8 @@ insert or ignore into Variants (name, description) values ('Karouk', 'Thai Chess (v3)'), ('Kinglet', 'Protect your pawns'), ('Kingsmaker', 'Promote into kings'), - ('Knightmate', 'Mate the knight'), + ('Knightmate1', 'Mate the knight (v1)'), + ('Knightmate2', 'Mate the knight (v2)'), ('Knightpawns', 'Knight versus pawns'), ('Knightrelay1', 'Move like a knight (v1)'), ('Knightrelay2', 'Move like a knight (v2)'),