From: Benjamin Auder <benjamin.auder@somewhere> Date: Wed, 26 Dec 2018 21:45:23 +0000 (+0100) Subject: Bugs fixing, finalization of rules in french+english X-Git-Url: https://git.auder.net/doc/html/css/pieces/vendor/common.css?a=commitdiff_plain;h=69f3d8014e594ef949792d04d97b8286e9c2c268;p=vchess.git Bugs fixing, finalization of rules in french+english --- diff --git a/TODO b/TODO new file mode 100644 index 00000000..3f79e69f --- /dev/null +++ b/TODO @@ -0,0 +1,19 @@ +Finish rules translation in Spanish + +Design: final touch (gain extra space on top, using space on the right) +Crazyhouse: my reserve vertically on the right, opponent just below board + +Bug MarseilleRules turn issue in computer game (last move is wrong) + +[Site "vchess.club"] +[Variant "Marseille"] +[Date "2018-12-26"] +[White "Myself"] +[Black "Computer"] +[FenStart "nrqkbrnb/pppppppp/8/8/8/8/PPPPPPPP/RKQBNRBN"] +[Fen "1rq3nb/1pk1p1p1/1Np1p2p/p7/8/2P5/PP2P1PP/RK1B2B1 w1 1000 -"] +[Result "0-1"] + +1.f4 a5,h6 2.d3,c3 c6,Kc7 3.Qe3,Qe5 d6,dxe5 4.fxe5,e6 fxe6,Rxf1 5.Ng3,Nxf1 Nb6,Bg6 6.Nd2,Ne4 Bxe4,Bxd3 7.Nxd3,Nc5 Na4,Nxb6 + +1.f2f4 a7a5,h7h6 2.d2d3,c2c3 c7c6,d8c7 3.c1e3,e3e5 d7d6,d6e5 4.f4e5,e5e6 f7e6,f8f1 5.h1g3,g3f1 a8b6,e8g6 6.f1d2,d2e4 g6e4,e4d3 7.e1d3,d3c5 c5a4,a4b6 diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js index 891948b9..9599fdbf 100644 --- a/public/javascripts/base_rules.js +++ b/public/javascripts/base_rules.js @@ -628,7 +628,8 @@ class ChessRules const lastRank = (color == "w" ? 0 : sizeX-1); const pawnColor = this.getColor(x,y); //can be different for checkered - if (x+shiftX >= 0 && x+shiftX < sizeX) //TODO: always true + // NOTE: next condition is generally true (no pawn on last rank) + if (x+shiftX >= 0 && x+shiftX < sizeX) { const finalPieces = x + shiftX == lastRank ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN] @@ -1001,22 +1002,27 @@ class ChessRules { this.kingPos[c][0] = move.appear[0].x; this.kingPos[c][1] = move.appear[0].y; - this.castleFlags[c] = [false,false]; + if (V.HasFlags) + this.castleFlags[c] = [false,false]; return; } - const oppCol = this.getOppCol(c); - const oppFirstRank = (V.size.x-1) - firstRank; - if (move.start.x == firstRank //our rook moves? - && this.INIT_COL_ROOK[c].includes(move.start.y)) - { - const flagIdx = (move.start.y == this.INIT_COL_ROOK[c][0] ? 0 : 1); - this.castleFlags[c][flagIdx] = false; - } - else if (move.end.x == oppFirstRank //we took opponent rook? - && this.INIT_COL_ROOK[oppCol].includes(move.end.y)) + if (V.HasFlags) { - const flagIdx = (move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1); - this.castleFlags[oppCol][flagIdx] = false; + // Update castling flags if rooks are moved + const oppCol = this.getOppCol(c); + const oppFirstRank = (V.size.x-1) - firstRank; + if (move.start.x == firstRank //our rook moves? + && this.INIT_COL_ROOK[c].includes(move.start.y)) + { + const flagIdx = (move.start.y == this.INIT_COL_ROOK[c][0] ? 0 : 1); + this.castleFlags[c][flagIdx] = false; + } + else if (move.end.x == oppFirstRank //we took opponent rook? + && this.INIT_COL_ROOK[oppCol].includes(move.end.y)) + { + const flagIdx = (move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1); + this.castleFlags[oppCol][flagIdx] = false; + } } } @@ -1244,7 +1250,7 @@ class ChessRules } else return currentBest; - //console.log(moves1.map(m => { return [this.getNotation(m), m.eval]; })); +// console.log(moves1.map(m => { return [this.getNotation(m), m.eval]; })); candidates = [0]; for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++) diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js index 6627252f..ac03c8fc 100644 --- a/public/javascripts/components/game.js +++ b/public/javascripts/components/game.js @@ -1287,6 +1287,12 @@ Vue.component('my-game', { }, clickComputerGame: function(e) { this.getRidOfTooltip(e.currentTarget); + if (this.mode == "computer" && this.score == "*" + && this.vr.turn != this.mycolor) + { + // Wait for computer reply first (avoid potential "ghost move" bug) + return; + } this.newGame("computer"); }, clickFriendGame: function(e) { @@ -1344,8 +1350,6 @@ Vue.component('my-game', { return; } } - else if (score == "*") - return this.continueGame("computer"); } } else if (mode == "friend") @@ -1416,7 +1420,7 @@ Vue.component('my-game', { else if (mode == "computer") { this.compWorker.postMessage(["init",fen]); - if (this.mycolor != this.vr.turn) + if (score == "*" && this.mycolor != this.vr.turn) this.playComputerMove(); } //else: nothing special to do in friend mode diff --git a/public/javascripts/utils/printDiagram.js b/public/javascripts/utils/printDiagram.js index b7282fee..47274303 100644 --- a/public/javascripts/utils/printDiagram.js +++ b/public/javascripts/utils/printDiagram.js @@ -42,6 +42,32 @@ function getDiagram(args) shadowArray[i][colnum] = true; continue; } + if (squares[i].indexOf("-") >= 0) + { + // Shadow a range of squares, horizontally or vertically + const firstLastSq = squares[i].split("-"); + const range = + [ + V.SquareToCoords(firstLastSq[0]), + V.SquareToCoords(firstLastSq[1]) + ]; + const step = + [ + range[1].x == range[0].x + ? 0 + : (range[1].x - range[0].x) / Math.abs(range[1].x - range[0].x), + range[1].y == range[0].y + ? 0 + : (range[1].y - range[0].y) / Math.abs(range[1].y - range[0].y) + ]; + // Convention: range always from smaller to larger number + for (let x=range[0].x, y=range[0].y; x <= range[1].x && y <= range[1].y; + x += step[0], y += step[1]) + { + shadowArray[x][y] = true; + } + continue; + } // Shadow just one square: const coords = V.SquareToCoords(squares[i]); shadowArray[coords.x][coords.y] = true; diff --git a/public/javascripts/variants/Baroque.js b/public/javascripts/variants/Baroque.js index 0b00c262..fe0e8466 100644 --- a/public/javascripts/variants/Baroque.js +++ b/public/javascripts/variants/Baroque.js @@ -1,4 +1,4 @@ -class UltimaRules extends ChessRules +class BaroqueRules extends ChessRules { static get HasFlags() { return false; } @@ -7,7 +7,7 @@ class UltimaRules extends ChessRules static getPpath(b) { if (b[1] == "m") //'m' for Immobilizer (I is too similar to 1) - return "Ultima/" + b; + return "Baroque/" + b; return b; //usual piece } @@ -169,7 +169,7 @@ class UltimaRules extends ChessRules }); } - // "Pincher" + // "Pincer" getPotentialPawnMoves([x,y]) { let moves = super.getPotentialRookMoves([x,y]); @@ -526,18 +526,6 @@ class UltimaRules extends ChessRules return false; } - updateVariables(move) - { - // Just update king(s) position(s) - const piece = move.vanish[0].p; - const c = move.vanish[0].c; - if (piece == V.KING && move.appear.length > 0) - { - this.kingPos[c][0] = move.appear[0].x; - this.kingPos[c][1] = move.appear[0].y; - } - } - static get VALUES() { // TODO: totally experimental! @@ -627,4 +615,4 @@ class UltimaRules extends ChessRules } } -const VariantRules = UltimaRules; +const VariantRules = BaroqueRules; diff --git a/public/javascripts/variants/Berolina.js b/public/javascripts/variants/Berolina.js index 8ea3c9cc..31630ab5 100644 --- a/public/javascripts/variants/Berolina.js +++ b/public/javascripts/variants/Berolina.js @@ -44,37 +44,34 @@ class BerolinaRules extends ChessRules const firstRank = (color == 'w' ? sizeX-1 : 0); const startRank = (color == "w" ? sizeX-2 : 1); const lastRank = (color == "w" ? 0 : sizeX-1); + const finalPieces = x + shiftX == lastRank + ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN] + : [V.PAWN]; - if (x+shiftX >= 0 && x+shiftX < sizeX) //TODO: always true + // One square diagonally + for (let shiftY of [-1,1]) { - const finalPieces = x + shiftX == lastRank - ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN] - : [V.PAWN] - // One square diagonally - for (let shiftY of [-1,1]) + if (this.board[x+shiftX][y+shiftY] == V.EMPTY) { - if (this.board[x+shiftX][y+shiftY] == V.EMPTY) + for (let piece of finalPieces) { - for (let piece of finalPieces) - { - moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY], - {c:color,p:piece})); - } - if (x == startRank && y+2*shiftY>=0 && y+2*shiftY<sizeY - && this.board[x+2*shiftX][y+2*shiftY] == V.EMPTY) - { - // Two squares jump - moves.push(this.getBasicMove([x,y], [x+2*shiftX,y+2*shiftY])); - } + moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY], + {c:color,p:piece})); + } + if (x == startRank && y+2*shiftY>=0 && y+2*shiftY<sizeY + && this.board[x+2*shiftX][y+2*shiftY] == V.EMPTY) + { + // Two squares jump + moves.push(this.getBasicMove([x,y], [x+2*shiftX,y+2*shiftY])); } } - // Capture - if (this.board[x+shiftX][y] != V.EMPTY - && this.canTake([x,y], [x+shiftX,y])) - { - for (let piece of finalPieces) - moves.push(this.getBasicMove([x,y], [x+shiftX,y], {c:color,p:piece})); - } + } + // Capture + if (this.board[x+shiftX][y] != V.EMPTY + && this.canTake([x,y], [x+shiftX,y])) + { + for (let piece of finalPieces) + moves.push(this.getBasicMove([x,y], [x+shiftX,y], {c:color,p:piece})); } // En passant diff --git a/public/javascripts/variants/Dark.js b/public/javascripts/variants/Dark.js index 96f50de7..f1bd6c0d 100644 --- a/public/javascripts/variants/Dark.js +++ b/public/javascripts/variants/Dark.js @@ -16,6 +16,7 @@ class DarkRules extends ChessRules updateEnlightened() { + const pawnShift = {"w":-1, "b":1}; // Initialize with pieces positions (which are seen) for (let i=0; i<V.size.x; i++) { @@ -24,7 +25,22 @@ class DarkRules extends ChessRules this.enlightened["w"][i][j] = false; this.enlightened["b"][i][j] = false; if (this.board[i][j] != V.EMPTY) - this.enlightened[this.getColor(i,j)][i][j] = true; + { + const color = this.getColor(i,j); + this.enlightened[color][i][j] = true; + // Add potential squares visible by "impossible pawn capture" + if (this.getPiece(i,j) == V.PAWN) + { + for (let shiftY of [-1,1]) + { + if (V.OnBoard(i+pawnShift[color],j+shiftY) + && this.board[i+pawnShift[color]][j+shiftY] == V.EMPTY) + { + this.enlightened[color][i+pawnShift[color]][j+shiftY] = true; + } + } + } + } } } const currentTurn = this.turn; @@ -75,19 +91,11 @@ class DarkRules extends ChessRules updateVariables(move) { - // Update kings positions - const piece = move.vanish[0].p; - const c = move.vanish[0].c; - if (piece == V.KING && move.appear.length > 0) - { - this.kingPos[c][0] = move.appear[0].x; - this.kingPos[c][1] = move.appear[0].y; - } + super.updateVariables(move); if (move.vanish.length >= 2 && move.vanish[1].p == V.KING) { // We took opponent king ! - const oppCol = this.getOppCol(c); - this.kingPos[oppCol] = [-1,-1]; + this.kingPos[this.turn] = [-1,-1]; } // Update moves for both colors: diff --git a/public/javascripts/variants/Grand.js b/public/javascripts/variants/Grand.js index 7f5fe422..16963f82 100644 --- a/public/javascripts/variants/Grand.js +++ b/public/javascripts/variants/Grand.js @@ -13,7 +13,7 @@ class GrandRules extends ChessRules return false; const fenParsed = V.ParseFen(fen); // 5) Check captures - if (!fenParsed.captured || !fenParsed.captured.match(/^[0-9]{10,10}$/)) + if (!fenParsed.captured || !fenParsed.captured.match(/^[0-9]{14,14}$/)) return false; return true; } @@ -51,11 +51,15 @@ class GrandRules extends ChessRules getCapturedFen() { - let counts = _.map(_.range(10), 0); - for (let i=0; i<V.PIECES.length-1; i++) //-1: no king captured + let counts = _.map(_.range(14), 0); + let i = 0; + for (let j=0; j<V.PIECES.length; j++) { + if (V.PIECES[j] == V.KING) //no king captured + continue; counts[i] = this.captured["w"][V.PIECES[i]]; - counts[5+i] = this.captured["b"][V.PIECES[i]]; + counts[7+i] = this.captured["b"][V.PIECES[i]]; + i++; } return counts.join(""); } @@ -74,14 +78,18 @@ class GrandRules extends ChessRules [V.KNIGHT]: parseInt(fenParsed.captured[2]), [V.BISHOP]: parseInt(fenParsed.captured[3]), [V.QUEEN]: parseInt(fenParsed.captured[4]), + [V.MARSHALL]: parseInt(fenParsed.captured[5]), + [V.CARDINAL]: parseInt(fenParsed.captured[6]), }, "b": { - [V.PAWN]: parseInt(fenParsed.captured[5]), - [V.ROOK]: parseInt(fenParsed.captured[6]), - [V.KNIGHT]: parseInt(fenParsed.captured[7]), - [V.BISHOP]: parseInt(fenParsed.captured[8]), - [V.QUEEN]: parseInt(fenParsed.captured[9]), + [V.PAWN]: parseInt(fenParsed.captured[7]), + [V.ROOK]: parseInt(fenParsed.captured[8]), + [V.KNIGHT]: parseInt(fenParsed.captured[9]), + [V.BISHOP]: parseInt(fenParsed.captured[10]), + [V.QUEEN]: parseInt(fenParsed.captured[11]), + [V.MARSHALL]: parseInt(fenParsed.captured[12]), + [V.CARDINAL]: parseInt(fenParsed.captured[13]), } }; } @@ -167,80 +175,75 @@ class GrandRules extends ChessRules const color = this.turn; let moves = []; const [sizeX,sizeY] = [V.size.x,V.size.y]; - const shift = (color == "w" ? -1 : 1); + const shiftX = (color == "w" ? -1 : 1); const startRanks = (color == "w" ? [sizeX-2,sizeX-3] : [1,2]); const lastRanks = (color == "w" ? [0,1,2] : [sizeX-1,sizeX-2,sizeX-3]); + const promotionPieces = + [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN,V.MARSHALL,V.CARDINAL]; - if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRanks[0]) + // Always x+shiftX >= 0 && x+shiftX < sizeX, because no pawns on last rank + let finalPieces = undefined; + if (lastRanks.includes(x + shiftX)) { - // Normal moves - if (this.board[x+shift][y] == V.EMPTY) + finalPieces = promotionPieces.filter(p => this.captured[color][p] > 0); + if (x + shiftX != lastRanks[0]) + finalPieces.push(V.PAWN); + } + else + finalPieces = [V.PAWN]; + if (this.board[x+shiftX][y] == V.EMPTY) + { + // One square forward + for (let piece of finalPieces) + moves.push(this.getBasicMove([x,y], [x+shiftX,y], {c:color,p:piece})); + if (startRanks.includes(x)) { - moves.push(this.getBasicMove([x,y], [x+shift,y])); - if (startRanks.includes(x) && this.board[x+2*shift][y] == V.EMPTY) + if (this.board[x+2*shiftX][y] == V.EMPTY) { // Two squares jump - moves.push(this.getBasicMove([x,y], [x+2*shift,y])); - if (x == startRanks[0] && this.board[x+3*shift][y] == V.EMPTY) + moves.push(this.getBasicMove([x,y], [x+2*shiftX,y])); + if (x==startRanks[0] && this.board[x+3*shiftX][y] == V.EMPTY) { - // 3-squares jump - moves.push(this.getBasicMove([x,y], [x+3*shift,y])); + // Three squares jump + moves.push(this.getBasicMove([x,y], [x+3*shiftX,y])); } } } - // Captures - if (y>0 && this.canTake([x,y], [x+shift,y-1]) - && this.board[x+shift][y-1] != V.EMPTY) - { - moves.push(this.getBasicMove([x,y], [x+shift,y-1])); - } - if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) - && this.board[x+shift][y+1] != V.EMPTY) - { - moves.push(this.getBasicMove([x,y], [x+shift,y+1])); - } } - - if (lastRanks.includes(x+shift)) + // Captures + for (let shiftY of [-1,1]) { - // Promotion - let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN,V.MARSHALL,V.CARDINAL]; - promotionPieces.forEach(p => { - if (this.captured[color][p]==0) - return; - // Normal move - if (this.board[x+shift][y] == V.EMPTY) - moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p})); - // Captures - if (y>0 && this.canTake([x,y], [x+shift,y-1]) - && this.board[x+shift][y-1] != V.EMPTY) - { - moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p})); - } - if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) - && this.board[x+shift][y+1] != V.EMPTY) + if (y + shiftY >= 0 && y + shiftY < sizeY + && this.board[x+shiftX][y+shiftY] != V.EMPTY + && this.canTake([x,y], [x+shiftX,y+shiftY])) + { + for (let piece of finalPieces) { - moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:p})); + moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY], + {c:color,p:piece})); } - }); + } } // En passant const Lep = this.epSquares.length; - const epSquare = Lep>0 ? this.epSquares[Lep-1] : undefined; + const epSquare = this.epSquares[Lep-1]; if (!!epSquare) { for (let epsq of epSquare) { // TODO: some redundant checks - if (epsq.x == x+shift && Math.abs(epsq.y - y) == 1) + if (epsq.x == x+shiftX && Math.abs(epsq.y - y) == 1) { - var enpassantMove = this.getBasicMove([x,y], [x+shift,epsq.y]); + var enpassantMove = this.getBasicMove([x,y], [epsq.x,epsq.y]); + // WARNING: the captured pawn may be diagonally behind us, + // if it's a 3-squares jump and we take on 1st passing square + const px = (this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX); enpassantMove.vanish.push({ - x: x, + x: px, y: epsq.y, p: 'p', - c: this.getColor(x,epsq.y) + c: this.getColor(px,epsq.y) }); moves.push(enpassantMove); } @@ -288,18 +291,25 @@ class GrandRules extends ChessRules updateVariables(move) { super.updateVariables(move); - if (move.vanish.length==2 && move.appear.length==1 && move.vanish[1].p != V.PAWN) + if (move.vanish.length == 2 && move.appear.length == 1) { // Capture: update this.captured this.captured[move.vanish[1].c][move.vanish[1].p]++; } + if (move.vanish[0].p != move.appear[0].p) + { + // Promotion: update this.captured + this.captured[move.vanish[0].c][move.appear[0].p]--; + } } unupdateVariables(move) { super.unupdateVariables(move); - if (move.vanish.length==2 && move.appear.length==1 && move.vanish[1].p != V.PAWN) + if (move.vanish.length == 2 && move.appear.length == 1) this.captured[move.vanish[1].c][move.vanish[1].p]--; + if (move.vanish[0].p != move.appear[0].p) + this.captured[move.vanish[0].c][move.appear[0].p]++; } static get VALUES() @@ -374,7 +384,7 @@ class GrandRules extends ChessRules return pieces["b"].join("") + "/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/" + pieces["w"].join("").toUpperCase() + - " w 1111 - 0000000000"; + " w 1111 - 00000000000000"; } } diff --git a/public/javascripts/variants/Marseille.js b/public/javascripts/variants/Marseille.js index 6e72a236..618e8330 100644 --- a/public/javascripts/variants/Marseille.js +++ b/public/javascripts/variants/Marseille.js @@ -73,40 +73,37 @@ class MarseilleRules extends ChessRules const startRank = (color == "w" ? sizeX-2 : 1); const lastRank = (color == "w" ? 0 : sizeX-1); const pawnColor = this.getColor(x,y); //can be different for checkered + const finalPieces = x + shiftX == lastRank + ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN] + : [V.PAWN]; - if (x+shiftX >= 0 && x+shiftX < sizeX) //TODO: always true + // One square forward + if (this.board[x+shiftX][y] == V.EMPTY) { - const finalPieces = x + shiftX == lastRank - ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN] - : [V.PAWN] - // One square forward - if (this.board[x+shiftX][y] == V.EMPTY) + for (let piece of finalPieces) { - for (let piece of finalPieces) - { - moves.push(this.getBasicMove([x,y], [x+shiftX,y], - {c:pawnColor,p:piece})); - } - // Next condition because pawns on 1st rank can generally jump - if ([startRank,firstRank].includes(x) - && this.board[x+2*shiftX][y] == V.EMPTY) - { - // Two squares jump - moves.push(this.getBasicMove([x,y], [x+2*shiftX,y])); - } + moves.push(this.getBasicMove([x,y], [x+shiftX,y], + {c:pawnColor,p:piece})); } - // Captures - for (let shiftY of [-1,1]) + // Next condition because pawns on 1st rank can generally jump + if ([startRank,firstRank].includes(x) + && this.board[x+2*shiftX][y] == V.EMPTY) { - if (y + shiftY >= 0 && y + shiftY < sizeY - && this.board[x+shiftX][y+shiftY] != V.EMPTY - && this.canTake([x,y], [x+shiftX,y+shiftY])) + // Two squares jump + moves.push(this.getBasicMove([x,y], [x+2*shiftX,y])); + } + } + // Captures + for (let shiftY of [-1,1]) + { + if (y + shiftY >= 0 && y + shiftY < sizeY + && this.board[x+shiftX][y+shiftY] != V.EMPTY + && this.canTake([x,y], [x+shiftX,y+shiftY])) + { + for (let piece of finalPieces) { - for (let piece of finalPieces) - { - moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY], - {c:pawnColor,p:piece})); - } + moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY], + {c:pawnColor,p:piece})); } } } @@ -127,6 +124,7 @@ class MarseilleRules extends ChessRules { if (this.subTurn == 1 || (epSqs.length == 2 && // Was this en-passant capture already played at subturn 1 ? + // (Or maybe the opponent filled the en-passant square with a piece) this.board[epSqs[0].x][epSqs[0].y] != V.EMPTY)) { if (sq.x == x+shiftX && Math.abs(sq.y - y) == 1) @@ -362,7 +360,6 @@ class MarseilleRules extends ChessRules return pgn; } - } const VariantRules = MarseilleRules; diff --git a/public/javascripts/variants/Upsidedown.js b/public/javascripts/variants/Upsidedown.js index 1a812887..3e389d0d 100644 --- a/public/javascripts/variants/Upsidedown.js +++ b/public/javascripts/variants/Upsidedown.js @@ -1,8 +1,8 @@ class UpsidedownRules extends ChessRules { - static HasFlags() { return false; } + static get HasFlags() { return false; } - static HasEnpassant() { return false; } + static get HasEnpassant() { return false; } getPotentialKingMoves(sq) { @@ -65,7 +65,7 @@ class UpsidedownRules extends ChessRules return pieces["w"].join("").toUpperCase() + "/PPPPPPPP/8/8/8/8/pppppppp/" + pieces["b"].join("") + - " w 1111 -"; //add turn + flags + enpassant + " w"; //no castle, no en-passant } } diff --git a/public/javascripts/variants/Wildebeest.js b/public/javascripts/variants/Wildebeest.js index 357a5eb8..293b3b15 100644 --- a/public/javascripts/variants/Wildebeest.js +++ b/public/javascripts/variants/Wildebeest.js @@ -110,78 +110,66 @@ class WildebeestRules extends ChessRules const color = this.turn; let moves = []; const [sizeX,sizeY] = [V.size.x,V.size.y]; - const shift = (color == "w" ? -1 : 1); + const shiftX = (color == "w" ? -1 : 1); const startRanks = (color == "w" ? [sizeX-2,sizeX-3] : [1,2]); const lastRank = (color == "w" ? 0 : sizeX-1); + const finalPieces = x + shiftX == lastRank + ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN] + : [V.PAWN]; - if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank) + if (this.board[x+shiftX][y] == V.EMPTY) { - // Normal moves - if (this.board[x+shift][y] == V.EMPTY) + // One square forward + for (let piece of finalPieces) + moves.push(this.getBasicMove([x,y], [x+shiftX,y], {c:color,p:piece})); + if (startRanks.includes(x)) { - moves.push(this.getBasicMove([x,y], [x+shift,y])); - if (startRanks.includes(x) && this.board[x+2*shift][y] == V.EMPTY) + if (this.board[x+2*shiftX][y] == V.EMPTY) { // Two squares jump - moves.push(this.getBasicMove([x,y], [x+2*shift,y])); - if (x == startRanks[0] && this.board[x+3*shift][y] == V.EMPTY) + moves.push(this.getBasicMove([x,y], [x+2*shiftX,y])); + if (x==startRanks[0] && this.board[x+3*shiftX][y] == V.EMPTY) { - // 3-squares jump - moves.push(this.getBasicMove([x,y], [x+3*shift,y])); + // Three squares jump + moves.push(this.getBasicMove([x,y], [x+3*shiftX,y])); } } } - // Captures - if (y>0 && this.canTake([x,y], [x+shift,y-1]) - && this.board[x+shift][y-1] != V.EMPTY) - { - moves.push(this.getBasicMove([x,y], [x+shift,y-1])); - } - if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) - && this.board[x+shift][y+1] != V.EMPTY) - { - moves.push(this.getBasicMove([x,y], [x+shift,y+1])); - } } - - if (x+shift == lastRank) + // Captures + for (let shiftY of [-1,1]) { - // Promotion - let promotionPieces = [V.QUEEN,V.WILDEBEEST]; - 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})); - // Captures - if (y>0 && this.canTake([x,y], [x+shift,y-1]) - && this.board[x+shift][y-1] != V.EMPTY) - { - moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p})); - } - if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) - && this.board[x+shift][y+1] != V.EMPTY) + if (y + shiftY >= 0 && y + shiftY < sizeY + && this.board[x+shiftX][y+shiftY] != V.EMPTY + && this.canTake([x,y], [x+shiftX,y+shiftY])) + { + for (let piece of finalPieces) { - moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:p})); + moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY], + {c:color,p:piece})); } - }); + } } // En passant const Lep = this.epSquares.length; - const epSquare = Lep>0 ? this.epSquares[Lep-1] : undefined; + const epSquare = this.epSquares[Lep-1]; if (!!epSquare) { for (let epsq of epSquare) { // TODO: some redundant checks - if (epsq.x == x+shift && Math.abs(epsq.y - y) == 1) + if (epsq.x == x+shiftX && Math.abs(epsq.y - y) == 1) { - var enpassantMove = this.getBasicMove([x,y], [x+shift,epsq.y]); + var enpassantMove = this.getBasicMove([x,y], [epsq.x,epsq.y]); + // WARNING: the captured pawn may be diagonally behind us, + // if it's a 3-squares jump and we take on 1st passing square + const px = (this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX); enpassantMove.vanish.push({ - x: x, + x: px, y: epsq.y, p: 'p', - c: this.getColor(x,epsq.y) + c: this.getColor(px,epsq.y) }); moves.push(enpassantMove); } diff --git a/public/javascripts/variants/Zen.js b/public/javascripts/variants/Zen.js index 66cd61f4..0675fbc9 100644 --- a/public/javascripts/variants/Zen.js +++ b/public/javascripts/variants/Zen.js @@ -97,7 +97,7 @@ class ZenRules extends ChessRules const firstRank = (color == 'w' ? sizeY-1 : 0); const lastRank = (color == "w" ? 0 : sizeY-1); - if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank) + if (x+shift != lastRank) { // Normal moves if (this.board[x+shift][y] == V.EMPTY) @@ -111,9 +111,8 @@ class ZenRules extends ChessRules } } - if (x+shift == lastRank) + else //promotion { - // Promotion let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]; promotionPieces.forEach(p => { // Normal move diff --git a/public/stylesheets/variant.sass b/public/stylesheets/variant.sass index c2e28a18..c5e38a89 100644 --- a/public/stylesheets/variant.sass +++ b/public/stylesheets/variant.sass @@ -195,9 +195,8 @@ div.board11 background-color: #e6ee9c &:hover background-color: skyblue - .choice-piece - width: 90% - max-width: 100% + &.choice-piece + width: 100% height: auto display: block @@ -313,6 +312,7 @@ figure.diagram-container display: block clear: both padding-top: 5px + font-size: 0.8em p.boxed background-color: #FFCC66 diff --git a/views/rules/Berolina/en.pug b/views/rules/Berolina/en.pug index 5cdfff95..d723bc4e 100644 --- a/views/rules/Berolina/en.pug +++ b/views/rules/Berolina/en.pug @@ -15,21 +15,35 @@ h3 Basics p. Only the pawn movements change, but since there are many on the board it's a - consequent change. They move diagonally instead of moving forward, and capture by - advancing to the next square vertically. The initial 2-squares jump is allowed, - as well as en-passant captures: after 1.d2b4 on the diagram, - 1...Pxc3 e.p. is possible. + consequent change. They move forward diagonally instead of moving straight, + and capture by advancing to the next square vertically. + +figure.diagram-container + .diagram + | fen:8/8/5p2/5P2/P7/8/5P2/8 b5,e3,d4,g3,h4,e6,f6,g6: + figcaption Possible pawn moves p. - Note about notation: pawns + The initial 2-squares jump is allowed, as well as en-passant captures: + after 1.d2b4 on the diagram, 1...Pxc3 e.p. is possible. + +p. + About notation: since pawn captures are non-ambigous they writes e.g. + "Pxe6" ('P' is redundant but looks nicer); simple pawn moves are often + ambiguous, so they write for example "f2g3" (again, the starting row is + redundant but this also looks better). figure.diagram-container .diagram - | fen:r3kbnr/pp3ppp/3p4/4p3/8/8/PPPPPPPP/R1BQKBNR: - figcaption After the moves 1.Nc3 d6?? 2.Nd5 e5 3.Nxc7 + | fen:rnbqkbnr/p1pppppp/8/8/1Pp2P2/5N2/PPP1PPP1/R1BQKBNR c3: + figcaption. + After 1.Nf3 b7d5 2.h2f4 d5c4 3.d2b4, en-passant capture on marked square + is possible -h3 More information +h3 Other source -p. - Possible starting point Wikipedia page ? +p + | See for example the + a(href="https://brainking.com/en/GameRules?tp=59") Berolina chess + | page on brainking.com. diff --git a/views/rules/Berolina/fr.pug b/views/rules/Berolina/fr.pug new file mode 100644 index 00000000..4ee1c25a --- /dev/null +++ b/views/rules/Berolina/fr.pug @@ -0,0 +1,51 @@ +p.boxed + | Les pions avancent en diagonale, et capturent en montant + | d'une case verticalement. + +h3 Caractéristiques + +ul + li Ãchiquier: standard. + li Matériel: standard. + li Coups non capturants: mouvements de pions différents. + li Coups spéciaux: standards (prise en passant adaptée. + li Captures: standards (excepté les pions). + li Fin de partie: standard. + +h3 Bases + +p. + Seuls les déplacements de pions changent, mais puisqu'il y en a beaucoup sur + l'échiquier c'est un changement important. Ils se déplacent en diagonale + (toujours vers l'avant) au lieu d'avancer tout droit, et capturent en + montant d'une case verticalement. + +figure.diagram-container + .diagram + | fen:8/8/5p2/5P2/P7/8/5P2/8 b5,e3,d4,g3,h4,e6,f6,g6: + figcaption Possibles coups de pion + +p. + Le saut initial de deux cases est permis, ainsi que la prise en passant : + après 3.d2b4 sur le diagrame, 3...Pxc3 e.p. est possible. + +p. + Au sujet de la notation : puisque les captures effectuées par les pions ne + sont pas ambigues on les note par exemple "Pxe6" ('P' est redondant mais + l'écriture est plus jolie ainsi) ; en revanche les simples déplacements sont + en général ambigus, et donc notés par exemple "f2g3" (ici encore, + la rangée de départ est inutile mais l'écriture paraît mieux ainsi). + +figure.diagram-container + .diagram + | fen:rnbqkbnr/p1pppppp/8/8/1Pp2P2/5N2/PPP1PPP1/R1BQKBNR c3: + figcaption. + Après 1.Nf3 b7d5 2.h2f4 d5c4 3.d2b4, il est possible de capturer en passant + sur la case marquée + +h3 Autre source + +p + | Visitez par exemple la page + a(href="https://brainking.com/fr/GameRules?tp=59") Ãchecs Berolina + | page sur brainking.com. diff --git a/views/rules/Dark/en.pug b/views/rules/Dark/en.pug index 7dc9aa9e..f4585e35 100644 --- a/views/rules/Dark/en.pug +++ b/views/rules/Dark/en.pug @@ -1,17 +1,14 @@ p.boxed | You only see what your pieces can reach. Incomplete information game. -h3 Specifications +h3 Divergences ul - li Chessboard: standard. - li Material: standard. - li Non-capturing moves: standard. - li Special moves: standard. - li Captures: standard. li End of game: capture the king. -p Incomplete information version of the orthodox game (also shuffled). +p. + Incomplete information version of the orthodox game + (with randomized initial position). h3 Basics @@ -20,11 +17,13 @@ p. That is to say: empty squares where your pieces can go, but also occupied squares where captures can be made. For example on the illustration next, after 1.e4 d5 white sees a black - pawn on d5, the squares e5, h5, b5, a6 and all four first ranks. + pawn on d5, the squares e5, f5, h5, b5, a6 and all four first ranks. + The f5 square is visible (and empty) because of the + special case of pawn capture. figure.diagram-container .diagram - | fen:8/8/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR - w 8,7,b6,c6,d6,e6,f6,g6,h6,a5,c5,f5,g5: + | fen:8/8/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR - w 8,7,b6-h6,a5,c5,g5: figcaption Standard initial position after 1.e4 d5 p. @@ -33,13 +32,13 @@ p. Good luck! p. - Note: the bot is not cheating - it really uses only the information described earlier. - Moreover, it is very basic and clearly less challenging that a human. - But it may be a fun start :) + Note: the bot is not cheating - it really uses only the information + described earlier. Moreover, it is very basic and clearly less + challenging that a human. But it may be a fun start :) h3 End of the game -p Win by capturing the king (no checks, no stalemate). +p Win by capturing the enemy king (no checks, no stalemate). h3 More information diff --git a/views/rules/Dark/fr.pug b/views/rules/Dark/fr.pug new file mode 100644 index 00000000..b64224e3 --- /dev/null +++ b/views/rules/Dark/fr.pug @@ -0,0 +1,53 @@ +p.boxed + | Vous ne voyez que ce que vos pièces peuvent atteindre. + | Jeu à information incomplète. + +h3 Divergences + +ul + li Fin de partie: capturer le roi. + +p. + Version à information incomplète du jeu d'échecs orthodoxe + (avec position initiale aléatoire). + +p Incomplete information version of the orthodox game (also shuffled). + +h3 Bases + +p. + Avant chaque coup, les joueurs ne voient que ce que leurs pièces peuvent + atteindre. C'est-à -dire : les cases vides où peuvent se déplacer les pièces, + mais aussi les cases occupées où des captures sont possibles. + Par exemple sur l'illustration suivante, après 1.e4 d5 les blancs voient + un pion noir en d5, les cases e5, f5, h5, b5, a6 et les quatre premières + rangées. La case f5 est visible (et vide) grâce au mode de capture + particulier des pions. + +figure.diagram-container + .diagram + | fen:8/8/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR - w 8,7,b6-h6,a5,c5,g5: + figcaption Position de départ habituelle après 1.e4 d5 + +p. + Choisissez votre coup prudemment, en vous basant sur ce qui est devinable des + derniers coups adverses. En particulier, compter le matériel est + indispensable. Bonne chance ! + +p. + Note : le robot joueur ne triche pas - il n'utilise que l'information décrite + plus haut. De plus il est très basique et clairement moins performant qu'un + humain. Ceci dit il peut être amusant de commencer contre lui :) + +h3 Fin de partie + +p Gagnez en capturant le roi adverse (pas d'échecs, pas de pat). + +h3 Plus d'information + +p + | J'ai découvert cette variante sur + a(href="https://www.buho21.com/") Buho21 + | , qui dispose d'une belle interface mais dont l'esprit est très différent + | d'ici (système de classement, abonnement VIP ou publicités, etc.). + | Il semblait être le seul endroit où jouer en direct à cette variante. diff --git a/views/rules/Marseille/en.pug b/views/rules/Marseille/en.pug index 5c66e781..a390e9d9 100644 --- a/views/rules/Marseille/en.pug +++ b/views/rules/Marseille/en.pug @@ -18,24 +18,43 @@ p. h3 Basics p. - TODO: explain, every turn twice except if check on 1st turn, or - very first move in game. - En-passant: possible in any order if 2 ep squares, - otherwise has to be the first move. - OK even if opponent moved his pawn at subturn 1. + At the very first move of the game, white make only one move - as usual. + However, after that and for all the game each side must play twice at + every turn. There are two exceptions: + +ul + li. + If the first move gives check (maybe checkmate), + then a second move isn't played. + li. + If no move is available after the first move, then it's stalemate + and again, there is no second move. p. - PGN game notation: since there are two moves at each turn except on move 1, - a double move in the game is indicated as two comma-separated (ordered) moves, - as in 3.Na5,Bd3 e6,f4 (the two first are white moves, + About the PGN game notation: when a side plays two moves in a row, + they are separated (in order) by a comma in the PGN. + Example: 3.Na5,Bd3 e6,f4 (the two first are white moves, the two others are black moves). - Sometimes a move gives check, or stalemate occurs after subTurn 1 :: just one move figure.diagram-container .diagram | fen:r1bqkbnr/pppp1p1p/2n5/4p2p/4P3/5N2/PPPP1PPP/RNB1KB1R: figcaption After the moves 1.e4 e5,Nc6 2.Qh5,Nf3 g6,gxh5 +h3 En-passant capture + +p. + Capturing en-passant is allowed under certain conditions. + If the opponent moved a pawn allowing such a capture (once or twice), + then (to take it) you must capture en-passant at the first move of your turn. + After that, if (and only if) there is another en-passant capture available + you can play it on the second move. + +p. + Note: if a pawn 2-squares jump was made and then a piece landed at the + en-passant square at the second move, a pawn capture on this square + takes only the piece. + h3 More information p diff --git a/views/rules/Marseille/fr.pug b/views/rules/Marseille/fr.pug new file mode 100644 index 00000000..51c0fcd0 --- /dev/null +++ b/views/rules/Marseille/fr.pug @@ -0,0 +1,56 @@ +p.boxed + | Jouez deux coups à chaque tour. + +h3 Divergences + +p. + La seule différence avec le jeu orthodoxe est la règle du double-coup, mais cela + affecte beaucoup le jeu. + +h3 Bases + +p. + Au tout début de la partie les blancs ne jouent qu'un seul coup, comme + d'habitude. Cependant, après cela et ce pour tout le reste de la partie + chaque camp doit jouer deux coups à chaque tour. Avec deux exceptions : + +ul + li. + Si le premier coup donne échec (peut-être mat), + alors un second coup n'est pas joué. + li. + Si aucun coup n'est autorisé après le premier, alors c'est pat et + une fois encore il n'y a pas de deuxième coup. + +p. + Au sujet du format PGN de la partie : quand un camp joue deux coups d'affilée, + ils sont séparés (dans l'ordre) par une virgule. Exemple : 3.Na5,Bd3 e6,f4 + (les deux premiers sont des coups blancs, les deux suivants + sont des coups noirs). + +figure.diagram-container + .diagram + | fen:r1bqkbnr/pppp1p1p/2n5/4p2p/4P3/5N2/PPPP1PPP/RNB1KB1R: + figcaption Après les coups 1.e4 e5,Nc6 2.Qh5,Nf3 g6,gxh5 + +h3 Prise en passant + +p. + Capturer en passant est autorisé sous certaines conditions. + Si l'adversaire a déplacé un pion permettant une telle capture + (une fois ou deux fois), alors pour en profiter il faut prendre en passant + dès le premier coup de votre tour. Ensuite, si (et seulement si) il y a + une autre prise en passant disponible, vous pouvez l'exécuter au second coup. + +p. + Note : si un pion se déplace de deux cases puis qu'une pièce occupe la case de + prise en passant au second coup d'un tour, une capture sur cette case ne + prendra que la pièce. + +h3 Plus d'information + +p + | Voir par exemple la page + a(href="https://www.chessvariants.com/multimove.dir/marseill.html") + | Ãchecs Marseillais + | sur chessvariants.com. diff --git a/views/rules/Upsidedown/en.pug b/views/rules/Upsidedown/en.pug index ff70cd30..8d4fa73b 100644 --- a/views/rules/Upsidedown/en.pug +++ b/views/rules/Upsidedown/en.pug @@ -1,37 +1,32 @@ p.boxed | Pawns start on the 7th rank. Move a knight to promote them. -h3 Specifications +h3 Divergences -ul - li Chessboard: standard. - li Material: standard. - li Non-capturing moves: standard. - li Special moves: no castling (and no en-passant). - li Captures: standard. - li End of game: standard. +p No castle, no en-passant capture. p. - ...(Almost) Only the initial position changes, but this is a very big change. + ...Only the initial position changes, but this makes a huge difference. In particular, castling would be rather pointless so it's disabled here. En-passant captures are impossible because all pawns already reached 7th rank. -h3 Note on initial position +h3 About the initial position p. - Since truly random start can allow un-defendable mate in 3 with a knight, - the kings touch at least one knight in the initial position. + Since truly random start can allow a mate in 3 with a knight, + the kings have at least one knight neighbor in the initial position. This allows to move free out of potential check from the very beginning. p. - To illustrate this phenomenon, although it's defendable the standard initial - position allows the attack as 1.Na6 (threatening Nc5-Nd3#), - forcing the defense Nf3-Ne5. With a knight next to the king you have more options. + A less constraining condition would be to require the two knights to stand on + two squares of different colors, but it's not enough as proved by the + following diagram. + White can mate in 3: 1.Nc6 followed by Nb4 threatening both a2 and d3. figure.diagram-container .diagram - | fen:R1BQKBNR/PPPPPPP/N7/8/8/8/pppppppp/rnbqkbnr: - figcaption Standard initial position after 1.Na6 + | fen:RBN1BRRQ/PPPPPPP/8/4n3/8/8/Nppppppp/brkbqr1n: + figcaption After 1.Nc6 Nf3 2.Nb4 Ne5 (covers d3 but not a2) 3.Nxa2# h3 Source diff --git a/views/rules/Upsidedown/fr.pug b/views/rules/Upsidedown/fr.pug new file mode 100644 index 00000000..bc0a7087 --- /dev/null +++ b/views/rules/Upsidedown/fr.pug @@ -0,0 +1,38 @@ +p.boxed + | Pawns start on the 7th rank. Move a knight to promote them. + +h3 Specifications + +p Pas de roque ni prise en passant. + +p. + ...Seule la position de départ change, mais c'est une énorme différence. + En particulier, le roque serait sans intérêt et est donc désactivé ici. + Les captures en passant sont également impossible car tous les pions + sont déjà sur la 7eme rangée. + +h3 Au sujet de la position initiale + +p. + Un placement complètement aléatoire des pièces peut permettre un mat en 3 + à l'aide d'un cavalier : c'est pourquoi le roi est toujours à côté d'au moins + un cavalier en début de partie. Cela permet de se dégager des échecs dès le + premier coup. + +p. + Pour illustrer ce phénomène, les blancs peuvent mater en 3 dans la position + du diagramme suivant : 1.Na6 suivi de Nc5 puis Nd3#. + Si le cavalier noir était en g1 une défense serait possible par Nf3-Ne5 ; + mais avec un cavalier voisin du roi plus d'options sont offertes. + +figure.diagram-container + .diagram + | fen:R1BQKBNR/PPPPPPP/N7/8/8/8/pppppppp/rnbqkbrn c5,d3: + figcaption Standard initial position after 1.Na6 + +h3 Source + +p + | Voir par exemple la page + a(href="https://www.chessvariants.com/diffsetup.dir/upside.html") Ãchecs Upside down + | sur chessvariants.com.