From 9bd6786b863c31c3ccd0057b87cf454c90886056 Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Sat, 22 Feb 2020 11:23:15 +0100 Subject: [PATCH] Fix en-passant for Wildebeest, draft Benedict variant --- client/src/base_rules.js | 17 +- client/src/translations/en.js | 1 + client/src/translations/es.js | 1 + client/src/translations/fr.js | 1 + client/src/translations/rules/Benedict/en.pug | 30 +++ client/src/translations/rules/Benedict/es.pug | 27 +++ client/src/translations/rules/Benedict/fr.pug | 27 +++ .../src/translations/rules/Extinction/en.pug | 4 +- client/src/variants/Benedict.js | 186 ++++++++++++++++++ client/src/variants/Zen.js | 9 +- client/src/views/Rules.vue | 4 +- server/db/populate.sql | 1 + 12 files changed, 293 insertions(+), 15 deletions(-) create mode 100644 client/src/translations/rules/Benedict/en.pug create mode 100644 client/src/translations/rules/Benedict/es.pug create mode 100644 client/src/translations/rules/Benedict/fr.pug create mode 100644 client/src/variants/Benedict.js diff --git a/client/src/base_rules.js b/client/src/base_rules.js index 350e0de6..656f7019 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -435,7 +435,7 @@ export const ChessRules = class ChessRules { if (V.HasEnpassant) { const epSq = parsedFen.enpassant != "-" - ? V.SquareToCoords(parsedFen.enpassant) + ? this.getEpSquare(parsedFen.enpassant) : undefined; this.epSquares = [epSq]; } @@ -722,10 +722,11 @@ export const ChessRules = class ChessRules { const oppCol = V.GetOppCol(c); let moves = []; let i = 0; + // King, then rook: const finalSquares = [ [2, 3], [V.size.y - 2, V.size.y - 3] - ]; //king, then rook + ]; castlingCheck: for ( let castleSide = 0; castleSide < 2; @@ -1022,9 +1023,9 @@ export const ChessRules = class ChessRules { play(move) { // DEBUG: - // if (!this.states) this.states = []; - // const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen(); - // this.states.push(stateFen); +// if (!this.states) this.states = []; +// const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen(); +// this.states.push(stateFen); if (V.HasFlags) move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo) if (V.HasEnpassant) this.epSquares.push(this.getEpSquare(move)); @@ -1043,9 +1044,9 @@ export const ChessRules = class ChessRules { this.unupdateVariables(move); // DEBUG: - // const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen(); - // if (stateFen != this.states[this.states.length-1]) debugger; - // this.states.pop(); +// const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen(); +// if (stateFen != this.states[this.states.length-1]) debugger; +// this.states.pop(); } /////////////// diff --git a/client/src/translations/en.js b/client/src/translations/en.js index 2d7ccce5..64ff2b02 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -131,6 +131,7 @@ export const translations = { "Both sides of the mirror": "Both sides of the mirror", "Capture all of a kind": "Capture all of a kind", "Captures reborn": "Captures reborn", + "Change colors": "Change colors", "Exchange pieces positions": "Exchange pieces positions", "Exotic captures": "Exotic captures", "Explosive captures": "Explosive captures", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index f004b123..760e644c 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -131,6 +131,7 @@ export const translations = { "Both sides of the mirror": "Ambos lados del espejo", "Capture all of a kind": "Capturar todo del mismo tipo", "Captures reborn": "Las capturas renacen", + "Change colors": "Cambiar colores", "Exchange pieces positions": "Intercambiar las posiciones de las piezas", "Exotic captures": "Capturas exóticas", "Explosive captures": "Capturas explosivas", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index de8b1dae..b6906745 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -131,6 +131,7 @@ export const translations = { "Both sides of the mirror": "Les deux côté du miroir", "Capture all of a kind": "Capturez tout d'un même type", "Captures reborn": "Les captures renaissent", + "Change colors": "Changer les couleurs", "Exchange pieces positions": "Échangez les positions des pièces", "Exotic captures": "Captures exotiques", "Explosive captures": "Captures explosives", diff --git a/client/src/translations/rules/Benedict/en.pug b/client/src/translations/rules/Benedict/en.pug new file mode 100644 index 00000000..805dbf4c --- /dev/null +++ b/client/src/translations/rules/Benedict/en.pug @@ -0,0 +1,30 @@ +p.boxed + | Attacked pieces change color after each turn. Goal is to change king's color. + +p. + More precisely, only pieces attacked by the moving unit are flipped. + They change side, until the opponent in turn can attack them. + There are no captures: only color changes. + +figure.diagram-container + .diagram + | fen:r1bqkbnr/ppPpPppp/2n5/3N4/8/8/PPPPPPPP/R1BQKBNR: + figcaption After 1.Nc3 Nc6 2.Nd5: pawns on c7 and e7 change color. + +h3 End of the game + +p. + The game ends when a king changes color. + There can be no stalemate since all pieces remain on the board. + +p. + In the diagram position, 2...g6?? for example would allow 3.Nf6 which + attacks the king and therefore ends the game. + However black can defend with 2...Nb4 or 2...Nf6 which flips the d5 knight, + or even 2...f6. + +h3 Source + +p + a(href="https://www.chessvariants.com/difftaking.dir/benedict.html") Benedict chess + |  on chessvariants.com. diff --git a/client/src/translations/rules/Benedict/es.pug b/client/src/translations/rules/Benedict/es.pug new file mode 100644 index 00000000..30a7b743 --- /dev/null +++ b/client/src/translations/rules/Benedict/es.pug @@ -0,0 +1,27 @@ +p.boxed + | Las piezas atacadas cambian de color después de cada turno. + | El objetivo es cambiar el color del rey. + +figure.diagram-container + .diagram + | fen:r1bqkbnr/ppPpPppp/2n5/3N4/8/8/PPPPPPPP/R1BQKBNR: + figcaption Después de 1.Nc3 Nc6 2.Nd5: los peones en c7 y e7 cambian de color. + +h3 Fin de la partida + +p. + El juego termina cuando un rey cambia de color. + No puede hay empate ya que todas las piezas permanecen en el tablero. + +p. + En la posición del diagrama, 2...g6?? por ejemplo permitiría 3.Nf6 que + atacar al rey y ganar (1-0). + Sin embargo, las negras pueden defender con 2...Nb4 o 2...Nf6 que hacen + cambiar de lado el caballo d5, o incluso 2...f6. + +h3 Fuente + +p + | La + a(href="https://www.chessvariants.com/difftaking.dir/benedict.html") variante Benedict + |  en chessvariants.com. diff --git a/client/src/translations/rules/Benedict/fr.pug b/client/src/translations/rules/Benedict/fr.pug new file mode 100644 index 00000000..6625912b --- /dev/null +++ b/client/src/translations/rules/Benedict/fr.pug @@ -0,0 +1,27 @@ +p.boxed + | Les pièces attaquées changent de couleur après chaque tour. + | Le but est de changer la couleur du roi. + +figure.diagram-container + .diagram + | fen:r1bqkbnr/ppPpPppp/2n5/3N4/8/8/PPPPPPPP/R1BQKBNR: + figcaption Après 1.Nc3 Nc6 2.Nd5 : les pions en c7 et e7 changent de couleur. + +h3 Fin de la partie + +p. + La partie s'achève quand un roi change de couleur. + Il ne peut pas y avoir de pat puisque toutes les pièces restent sur l'échiquier. + +p. + Dans la position du diagrame, 2...g6?? par exemple permettrait 3.Nf6 qui + attaque le roi et gagne (1-0). + Cependant les noirs peuvent défendre avec 2...Nb4 ou 2...Nf6 qui font + changer de camp le cavalier d5, ou même 2...f6. + +h3 Source + +p + | La + a(href="https://www.chessvariants.com/difftaking.dir/benedict.html") variante Benedict + |  sur chessvariants.com. diff --git a/client/src/translations/rules/Extinction/en.pug b/client/src/translations/rules/Extinction/en.pug index 55ad4201..5ca6a7cb 100644 --- a/client/src/translations/rules/Extinction/en.pug +++ b/client/src/translations/rules/Extinction/en.pug @@ -24,5 +24,5 @@ figure.diagram-container h3 Source p - a(href="https://www.chessvariants.com/winning.dir/extinction.html") Extinction chess - | on chessvariants.com. + a(href="https://www.chessvariants.com/winning.dir/extinction.html") Extinction chess + |  on chessvariants.com. diff --git a/client/src/variants/Benedict.js b/client/src/variants/Benedict.js new file mode 100644 index 00000000..e3e55eb6 --- /dev/null +++ b/client/src/variants/Benedict.js @@ -0,0 +1,186 @@ +import { ChessRules, PiPo, Move } from "@/base_rules"; + +export const VariantRules = class BenedictRules extends ChessRules { + static get HasEnpassant() { + return false; + } + + // TODO(?): some duplicated code in 2 next functions + getSlideNJumpMoves([x, y], steps, oneStep) { + let moves = []; + outerLoop: for (let loop = 0; loop < steps.length; loop++) { + const step = steps[loop]; + let i = x + step[0]; + let j = y + step[1]; + while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { + moves.push(this.getBasicMove([x, y], [i, j])); + if (oneStep) continue outerLoop; + i += step[0]; + j += step[1]; + } + // No capture check: handled elsewhere (next method) + } + return moves; + } + + // Find possible captures from a square + // follow steps from x,y until something is met. + findCaptures([x, y]) { + const color = this.getColor(x, y); + const piece = this.getPiece(x, y); + let squares = []; + const steps = + piece != V.PAWN + ? [V.QUEEN,V.KING].includes(piece) + ? V.steps[V.ROOK].concat(V.steps[V.BISHOP]) + : V.steps[piece] + : color == "w" + ? [ + [-1, -1], + [-1, 1] + ] + : [ + [1, -1], + [1, 1] + ]; + const oneStep = [V.KNIGHT,V.PAWN,V.KING].includes(piece); + outerLoop: for (let loop = 0; loop < steps.length; loop++) { + const step = steps[loop]; + let i = x + step[0]; + let j = y + step[1]; + while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { + if (oneStep) continue outerLoop; + i += step[0]; + j += step[1]; + } + if ( + V.OnBoard(i, j) && + this.getColor(i, j) == V.GetOppCol(color) + ) { + // eat! + squares.push([i, j]); + } + } + return squares; + } + + getPotentialPawnMoves([x, y]) { + const color = this.getColor(x, y); + let moves = []; + const sizeY = V.size.y; + const shift = color == "w" ? -1 : 1; + const startRank = color == "w" ? sizeY - 2 : 1; + const firstRank = color == "w" ? sizeY - 1 : 0; + const lastRank = color == "w" ? 0 : sizeY - 1; + + if (x + shift != lastRank) { + // Normal moves + if (this.board[x + shift][y] == V.EMPTY) { + moves.push(this.getBasicMove([x, y], [x + shift, y])); + if ( + [startRank, firstRank].includes(x) && + this.board[x + 2 * shift][y] == V.EMPTY + ) { + // Two squares jump + moves.push(this.getBasicMove([x, y], [x + 2 * shift, y])); + } + } + } + else { + // Promotion + let promotionPieces = [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]; + promotionPieces.forEach(p => { + // Normal move + if (this.board[x + shift][y] == V.EMPTY) + moves.push( + this.getBasicMove([x, y], [x + shift, y], { c: color, p: p }) + ); + }); + } + + // No en passant here + + return moves; + } + + getPotentialRookMoves(sq) { + return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]); + } + + getPotentialKnightMoves(sq) { + return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep"); + } + + getPotentialBishopMoves(sq) { + return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]); + } + + getPotentialQueenMoves(sq) { + return this.getSlideNJumpMoves( + sq, + V.steps[V.ROOK].concat(V.steps[V.BISHOP]) + ); + } + + getPotentialKingMoves(sq) { + // Initialize with normal (non-capturing) moves + let noCaptures = this.getSlideNJumpMoves( + sq, + V.steps[V.ROOK].concat(V.steps[V.BISHOP]), + "oneStep" + ); + return noCaptures.concat(this.getCastleMoves(sq)); + } + + // TODO: appear/vanish description of a move is too verbose for Benedict. + // => Would need a new "flipped" array, to be passed in Game.vue... + getPotentialMovesFrom([x, y]) { + const color = this.turn; + const oppCol = V.GetOppCol(color); + // Get all moves from x,y without captures: + let moves = super.getPotentialMovesFrom([x, y]); + // Add flips: + moves.forEach(m => { + V.PlayOnBoard(this.board, m); + const flipped = this.findCaptures([m.end.x, m.end.y]); + V.UndoOnBoard(this.board, m); + flipped.forEach(sq => { + const piece = this.getPiece(sq[0],sq[1]); + const pipoA = new PiPo({ + x:sq[0], + y:sq[1], + c:color, + p:piece + }); + const pipoV = new PiPo({ + x:sq[0], + y:sq[1], + c:oppCol, + p:piece + }); + m.appear.push(pipoA); + m.vanish.push(pipoV); + }); + }); + return moves; + } + + // Moves cannot flip our king's color, so all are valid + filterValid(moves) { + return moves; + } + + // No notion of check here: + getCheckSquares() { + return []; + } + + getCurrentScore() { + const color = this.turn; + // Did a king change color? + const kp = this.kingPos[color]; + if (this.getColor(kp[0], kp[1]) != color) + return color == "w" ? "0-1" : "1-0"; + return "*"; + } +}; diff --git a/client/src/variants/Zen.js b/client/src/variants/Zen.js index f5bff8ff..a6067f9c 100644 --- a/client/src/variants/Zen.js +++ b/client/src/variants/Zen.js @@ -28,7 +28,7 @@ export const VariantRules = class ZenRules extends ChessRules { // if met piece is opponent and same movement (asA): eat it! findCaptures_aux([x, y], asA) { const color = this.getColor(x, y); - var moves = []; + let moves = []; const steps = asA != V.PAWN ? asA == V.QUEEN @@ -43,7 +43,7 @@ export const VariantRules = class ZenRules extends ChessRules { [1, -1], [1, 1] ]; - const oneStep = asA == V.KNIGHT || asA == V.PAWN; //we don't capture king + const oneStep = [V.KNIGHT,V.PAWN].includes(asA); //we don't capture king const lastRank = color == "w" ? 0 : V.size.x - 1; const promotionPieces = [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]; outerLoop: for (let loop = 0; loop < steps.length; loop++) { @@ -105,12 +105,13 @@ export const VariantRules = class ZenRules extends ChessRules { [startRank, firstRank].includes(x) && this.board[x + 2 * shift][y] == V.EMPTY ) { - //two squares jump + // Two squares jump moves.push(this.getBasicMove([x, y], [x + 2 * shift, y])); } } - } //promotion + } else { + // Promotion let promotionPieces = [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]; promotionPieces.forEach(p => { // Normal move diff --git a/client/src/views/Rules.vue b/client/src/views/Rules.vue index 00202e64..015df7b3 100644 --- a/client/src/views/Rules.vue +++ b/client/src/views/Rules.vue @@ -24,6 +24,8 @@ main @click="gotoAnalyze()" ) | {{ st.tr["Analyse"] }} + .row + .col-sm-12.col-md-8.col-md-offset-2.col-lg-6.col-lg-offset-3 div( v-show="display=='rules'" v-html="content" @@ -69,7 +71,7 @@ export default { }, computed: { showAnalyzeBtn: function() { - return (this.display=='rules' && (!window.V || V.CanAnalyse)); + return (this.display=='rules' && (!window.V || V.CanAnalyze)); }, content: function() { if (!this.gameInfo.vname) return ""; //variant not set yet diff --git a/server/db/populate.sql b/server/db/populate.sql index 96f9e07e..34d0f44e 100644 --- a/server/db/populate.sql +++ b/server/db/populate.sql @@ -5,6 +5,7 @@ insert or ignore into Variants (name,description) values ('Antiking', 'Keep antiking in check'), ('Atomic', 'Explosive captures'), ('Baroque', 'Exotic captures'), + ('Benedict', 'Change colors'), ('Berolina', 'Pawns move diagonally'), ('Checkered', 'Shared pieces'), ('Chess960', 'Standard rules'), -- 2.44.0