From: Benjamin Auder Date: Tue, 25 Dec 2018 08:14:11 +0000 (+0100) Subject: Various fixes, additions... X-Git-Url: https://git.auder.net/%7B%7B%20asset%28%27mixstore/css/store/pieces/cb.svg?a=commitdiff_plain;h=26c1e3bd4d3e9fb7c86e25c0f423bea57b977111;p=vchess.git Various fixes, additions... --- diff --git a/public/images/pieces/Ultima/bm.svg b/public/images/pieces/Baroque/bm.svg similarity index 100% rename from public/images/pieces/Ultima/bm.svg rename to public/images/pieces/Baroque/bm.svg diff --git a/public/images/pieces/Ultima/wm.svg b/public/images/pieces/Baroque/wm.svg similarity index 100% rename from public/images/pieces/Ultima/wm.svg rename to public/images/pieces/Baroque/wm.svg diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js index 0bf5114a..8b8165a0 100644 --- a/public/javascripts/base_rules.js +++ b/public/javascripts/base_rules.js @@ -77,7 +77,7 @@ class ChessRules if (fenParsed.enpassant != "-") { const ep = V.SquareToCoords(fenParsed.enpassant); - if (ep.y < 0 || ep.y > V.size.y || isNaN(ep.x) || ep.x < 0 || ep.x > V.size.x) + if (isNaN(ep.x) || !V.OnBoard(ep)) return false; } } @@ -119,8 +119,14 @@ class ChessRules return !!flags.match(/^[01]{4,4}$/); } - // 3 --> d (column letter from number) - static GetColumn(colnum) + // 3 --> d (column number to letter) + static CoordToColumn(colnum) + { + return String.fromCharCode(97 + colnum); + } + + // d --> 3 (column letter to number) + static ColumnToCoord(colnum) { return String.fromCharCode(97 + colnum); } @@ -129,6 +135,8 @@ class ChessRules static SquareToCoords(sq) { return { + // NOTE: column is always one char => max 26 columns + // row is counted from black side => subtraction x: V.size.x - parseInt(sq.substr(1)), y: sq[0].charCodeAt() - 97 }; @@ -137,7 +145,7 @@ class ChessRules // {x:0,y:4} --> e8 static CoordsToSquare(coords) { - return V.GetColumn(coords.y) + (V.size.x - coords.x); + return V.CoordToColumn(coords.y) + (V.size.x - coords.x); } // Aggregates flags into one object @@ -217,31 +225,32 @@ class ChessRules // Get random squares for bishops let randIndex = 2 * _.random(3); - let bishop1Pos = positions[randIndex]; + const bishop1Pos = positions[randIndex]; // The second bishop must be on a square of different color let randIndex_tmp = 2 * _.random(3) + 1; - let bishop2Pos = positions[randIndex_tmp]; + const bishop2Pos = positions[randIndex_tmp]; // Remove chosen squares positions.splice(Math.max(randIndex,randIndex_tmp), 1); positions.splice(Math.min(randIndex,randIndex_tmp), 1); // Get random squares for knights randIndex = _.random(5); - let knight1Pos = positions[randIndex]; + const knight1Pos = positions[randIndex]; positions.splice(randIndex, 1); randIndex = _.random(4); - let knight2Pos = positions[randIndex]; + const knight2Pos = positions[randIndex]; positions.splice(randIndex, 1); // Get random square for queen randIndex = _.random(3); - let queenPos = positions[randIndex]; + const queenPos = positions[randIndex]; positions.splice(randIndex, 1); - // Rooks and king positions are now fixed, because of the ordering rook-king-rook - let rook1Pos = positions[0]; - let kingPos = positions[1]; - let rook2Pos = positions[2]; + // Rooks and king positions are now fixed, + // because of the ordering rook-king-rook + const rook1Pos = positions[0]; + const kingPos = positions[1]; + const rook2Pos = positions[2]; // Finally put the shuffled pieces in the board array pieces[c][rook1Pos] = 'r'; @@ -528,7 +537,8 @@ class ChessRules } } - // Build a regular move from its initial and destination squares; tr: transformation + // Build a regular move from its initial and destination squares. + // tr: transformation getBasicMove([sx,sy], [ex,ey], tr) { let mv = new Move({ @@ -565,7 +575,8 @@ class ChessRules return mv; } - // Generic method to find possible moves of non-pawn pieces ("sliding or jumping") + // Generic method to find possible moves of non-pawn pieces: + // "sliding or jumping" getSlideNJumpMoves([x,y], steps, oneStep) { const color = this.getColor(x,y); @@ -680,7 +691,8 @@ class ChessRules // What are the queen moves from square x,y ? getPotentialQueenMoves(sq) { - return this.getSlideNJumpMoves(sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP])); + return this.getSlideNJumpMoves(sq, + V.steps[V.ROOK].concat(V.steps[V.BISHOP])); } // What are the king moves from square x,y ? @@ -710,13 +722,15 @@ class ChessRules continue; // If this code is reached, rooks and king are on initial position - // Nothing on the path of the king (and no checks; OK also if y==finalSquare)? + // Nothing on the path of the king ? + // (And no checks; OK also if y==finalSquare) let step = finalSquares[castleSide][0] < y ? -1 : 1; for (i=y; i!=finalSquares[castleSide][0]; i+=step) { if (this.isAttacked([x,i], [oppCol]) || (this.board[x][i] != V.EMPTY && // NOTE: next check is enough, because of chessboard constraints - (this.getColor(x,i) != c || ![V.KING,V.ROOK].includes(this.getPiece(x,i))))) + (this.getColor(x,i) != c + || ![V.KING,V.ROOK].includes(this.getPiece(x,i))))) { continue castlingCheck; } @@ -782,7 +796,8 @@ class ChessRules }); } - // Search for all valid moves considering current turn (for engine and game end) + // Search for all valid moves considering current turn + // (for engine and game end) getAllValidMoves() { const color = this.turn; @@ -792,13 +807,14 @@ class ChessRules { for (let j=0; j eval2)) + if ((color == "w" && evalPos < eval2) + || (color=="b" && evalPos > eval2)) + { eval2 = evalPos; + } this.undo(moves2[j]); } } @@ -1187,7 +1206,8 @@ class ChessRules this.alphabeta(V.SEARCH_DEPTH-1, -maxeval, maxeval); this.undo(moves1[i]); } - moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); }); + moves1.sort( (a,b) => { + return (color=="w" ? 1 : -1) * (b.eval - a.eval); }); } else return currentBest; @@ -1284,12 +1304,12 @@ class ChessRules if (move.vanish.length > move.appear.length) { // Capture - const startColumn = String.fromCharCode(97 + move.start.y); + const startColumn = V.CoordToColumn(move.start.y); notation = startColumn + "x" + finalSquare; } else //no capture notation = finalSquare; - if (move.appear.length > 0 && piece != move.appear[0].p) //promotion + if (move.appear.length > 0 && move.appear[0].p != V.PAWN) //promotion notation += "=" + move.appear[0].p.toUpperCase(); return notation; } diff --git a/public/javascripts/utils/printDiagram.js b/public/javascripts/utils/printDiagram.js index ef750490..61c726eb 100644 --- a/public/javascripts/utils/printDiagram.js +++ b/public/javascripts/utils/printDiagram.js @@ -15,9 +15,8 @@ function getDiagram(args) for (let i=0; i move.appear.length ? "x" : ""); let pawnMark = ""; if (["p","s"].includes(piece) && captureMark.length == 1) - pawnMark = V.GetColumn(move.start.y); //start column + pawnMark = V.CoordToColumn(move.start.y); //start column // Piece or pawn movement let notation = piece.toUpperCase() + pawnMark + captureMark + finalSquare; diff --git a/public/javascripts/variants/Ultima.js b/public/javascripts/variants/Baroque.js similarity index 100% rename from public/javascripts/variants/Ultima.js rename to public/javascripts/variants/Baroque.js diff --git a/public/javascripts/variants/Berolina.js b/public/javascripts/variants/Berolina.js index dc9e2574..be72e2d5 100644 --- a/public/javascripts/variants/Berolina.js +++ b/public/javascripts/variants/Berolina.js @@ -10,17 +10,26 @@ class BerolinaRules extends ChessRules const square = moveOrSquare; if (square == "-") return undefined; - return V.SquareToCoords(square); + // Enemy pawn initial column must be given too: + let res = []; + const epParts = square.split(","); + res.push(V.SquareToCoords(epParts[0])); + res.push(V.ColumnToCoord(epParts[1])); + return res; } // Argument is a move: const move = moveOrSquare; const [sx,ex,sy] = [move.start.x,move.end.x,move.start.y]; if (this.getPiece(sx,sy) == V.PAWN && Math.abs(sx - ex) == 2) { - return { - x: (ex + sx)/2, - y: (move.end.y + sy)/2 - }; + return + [ + { + x: (ex + sx)/2, + y: (move.end.y + sy)/2 + }, + move.end.y + ]; } return undefined; //default } @@ -47,7 +56,10 @@ class BerolinaRules extends ChessRules if (this.board[x+shiftX][y+shiftY] == V.EMPTY) { for (let piece of finalPieces) - moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY], {c:color,p:piece})); + { + moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY], + {c:color,p:piece})); + } if (x == startRank && y+2*shiftY>=0 && y+2*shiftY=0 && x+pawnShift 1) { // Capture - const startColumn = V.GetColumn(move.start.y); + const startColumn = V.CoordToColumn(move.start.y); notation = startColumn + "x" + finalSquare + "=" + move.appear[0].p.toUpperCase(); } diff --git a/public/javascripts/variants/Loser.js b/public/javascripts/variants/Losers.js similarity index 98% rename from public/javascripts/variants/Loser.js rename to public/javascripts/variants/Losers.js index dcc5c112..c9509c6f 100644 --- a/public/javascripts/variants/Loser.js +++ b/public/javascripts/variants/Losers.js @@ -1,4 +1,4 @@ -class LoserRules extends ChessRules +class LosersRules extends ChessRules { static get HasFlags() { return false; } @@ -184,4 +184,4 @@ class LoserRules extends ChessRules } } -const VariantRules = LoserRules; +const VariantRules = LosersRules; diff --git a/public/javascripts/variants/Switching.js b/public/javascripts/variants/Switching.js index 53b14d58..67078f53 100644 --- a/public/javascripts/variants/Switching.js +++ b/public/javascripts/variants/Switching.js @@ -130,11 +130,7 @@ class SwitchingRules extends ChessRules if (move.appear[0].p == V.KING && move.appear[1].p == V.ROOK) return (move.end.y < move.start.y ? "0-0-0" : "0-0"); // Switch: - const startSquare = - String.fromCharCode(97 + move.start.y) + (V.size.x-move.start.x); - const finalSquare = - String.fromCharCode(97 + move.end.y) + (V.size.x-move.end.x); - return "S" + startSquare + finalSquare; + return "S" + V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end); } } diff --git a/public/javascripts/variants/Upsidedown.js b/public/javascripts/variants/Upsidedown.js index f9668997..1a812887 100644 --- a/public/javascripts/variants/Upsidedown.js +++ b/public/javascripts/variants/Upsidedown.js @@ -2,13 +2,7 @@ class UpsidedownRules extends ChessRules { static HasFlags() { return false; } - // Forbid two knights moves in a row at moves 1 and 2 - getPotentialKnightMoves(sq) - { - // But this will also affect FEN for problems, and... - // does it really solve the problem ? - //if (this.moves. ...) - } + static HasEnpassant() { return false; } getPotentialKingMoves(sq) { @@ -24,27 +18,40 @@ class UpsidedownRules extends ChessRules { let positions = _.range(8); - let randIndex = 2 * _.random(3); - let bishop1Pos = positions[randIndex]; - let randIndex_tmp = 2 * _.random(3) + 1; - let bishop2Pos = positions[randIndex_tmp]; + let randIndex = _.random(7); + const kingPos = positions[randIndex]; + positions.splice(randIndex, 1); + + // At least a knight must be next to the king: + let knight1Pos = undefined; + if (kingPos == 0) + knight1Pos = 1; + else if (kingPos == V.size.y-1) + knight1Pos = V.size.y-2; + else + knight1Pos = kingPos + (Math.random() < 0.5 ? 1 : -1); + // Search for knight1Pos index in positions and remove it + const knight1Index = positions.indexOf(knight1Pos); + positions.splice(knight1Index, 1); + + // King+knight1 are on two consecutive squares: one light, one dark + randIndex = 2 * _.random(2); + const bishop1Pos = positions[randIndex]; + let randIndex_tmp = 2 * _.random(2) + 1; + const bishop2Pos = positions[randIndex_tmp]; positions.splice(Math.max(randIndex,randIndex_tmp), 1); positions.splice(Math.min(randIndex,randIndex_tmp), 1); - randIndex = _.random(5); - let knight1Pos = positions[randIndex]; - positions.splice(randIndex, 1); - randIndex = _.random(4); - let knight2Pos = positions[randIndex]; + randIndex = _.random(3); + const knight2Pos = positions[randIndex]; positions.splice(randIndex, 1); - randIndex = _.random(3); - let queenPos = positions[randIndex]; + randIndex = _.random(2); + const queenPos = positions[randIndex]; positions.splice(randIndex, 1); - let rook1Pos = positions[0]; - let kingPos = positions[1]; - let rook2Pos = positions[2]; + const rook1Pos = positions[0]; + const rook2Pos = positions[1]; pieces[c][rook1Pos] = 'r'; pieces[c][knight1Pos] = 'n'; diff --git a/public/stylesheets/layout.sass b/public/stylesheets/layout.sass index cb8f8018..e70d3fe4 100644 --- a/public/stylesheets/layout.sass +++ b/public/stylesheets/layout.sass @@ -59,6 +59,9 @@ a .bigfont font-size: 1.2em +.bold + font-weight: bold + [type="checkbox"].modal+div .card max-width: 767px max-height: 100vh diff --git a/views/rules/Ultima/en.pug b/views/rules/Baroque/en.pug similarity index 96% rename from views/rules/Ultima/en.pug rename to views/rules/Baroque/en.pug index e2876fdf..fb781641 100644 --- a/views/rules/Ultima/en.pug +++ b/views/rules/Baroque/en.pug @@ -3,6 +3,13 @@ p.boxed | They generally move like an orthodox queen, | but capturing rules are complex. +p + | Note: 'Baroque' is the initial name thought by the author, + | but 'Ultima' is also largely adopted. + a(href="https://www.chessvariants.com/people.dir/abbott.html") + | He prefers 'Baroque' + | , and I think me too. + h3 Specifications ul diff --git a/views/rules/Ultima/fr.pug b/views/rules/Baroque/fr.pug similarity index 96% rename from views/rules/Ultima/fr.pug rename to views/rules/Baroque/fr.pug index 006df693..b49a66b0 100644 --- a/views/rules/Ultima/fr.pug +++ b/views/rules/Baroque/fr.pug @@ -2,6 +2,13 @@ p.boxed | La plupart des pièces sont connues mais se déplacent différemment ; | en général comme une dame orthodoxe, mais les captures sont complexes. +p + | Note : le nom initialement choisit par l'auteur est 'Baroque', + | mais 'Ultima' est également largement utilisé. + a(href="https://www.chessvariants.com/people.dir/abbott.html") + | Il préfère 'Baroque' + | , et moi aussi je crois. + h3 Caractéristiques ul diff --git a/views/rules/Checkered/en.pug b/views/rules/Checkered/en.pug index ac78d65e..9ce4f10c 100644 --- a/views/rules/Checkered/en.pug +++ b/views/rules/Checkered/en.pug @@ -11,6 +11,8 @@ figure.showPieces.center-align img(src="/images/tmp_checkered/no_ck.png") figcaption Checkered pieces, born after captures. +p Note: the initial French name for this variant is "l'Échiqueté". + h3 Specifications ul @@ -65,6 +67,28 @@ ul Checkered pawns cannot capture en passant, because while the pawn was "passing" they were of the same color. +p.bold.bigfont If you wanna play, you can stop reading here. + +h3 Stalemate or checkmate? + +p. + The following diagram seems to show a mating pattern, but the king if + "attacked" by a checkered pawn – which still belongs to white. + Therefore, it can be considered that it's not really a check because + white is unable to "give back the turn". + Without the black bishop on a7 it would be mate (without debate), because + the king could virtually go to g1 before being captured by the pawn-chamaleon. + +figure.diagram-container + .diagram + | fen:7k/b5pp/8/8/8/8/6ss/7K: + figcaption After 1...g2+(#?) + +p. + The interface considers that it's mate in both cases, following the idea + "it's checkmate if we cannot move, and the opponent on its turn could + take the king" (maybe after an impossible move). + h2.stageDelimiter Stage 2 p.warn This stage is not (and probably will never be) implemented. diff --git a/views/rules/Checkered/fr.pug b/views/rules/Checkered/fr.pug index 65282489..ddc9a6f4 100644 --- a/views/rules/Checkered/fr.pug +++ b/views/rules/Checkered/fr.pug @@ -11,6 +11,11 @@ figure.showPieces.center-align img(src="/images/tmp_checkered/no_ck.png") figcaption Pièces échiquetées, nées suite aux captures. +p. + Note : le (vrai) nom initial de cette variante est "l'Échiqueté". + "Checkered" en est la traduction anglaise, et ce dernier terme me paraît + plus lisible pour des non francophones. + h3 Caractéristiques ul @@ -67,6 +72,28 @@ ul Les pions échiquetés ne peuvent capturer en passant, puisque pendant que le pion adverse "passait" ils étaient dans le même camp. +p.bold.bigfont Pour jouer, vous pouvez arrêter de lire ici. + +h3 Mat ou pat ? + +p. + La situation du diagramme suivant ressemble à un mat, mais le roi est + "attaqué" par un pion échiqueté : celui-ci appartient pour l'instant aux blancs. + On peut donc considérer qu'ils ne sont pas vraiment en échec + puisqu'incapables de "rendre le trait". + Sans le fou noir en a7 ce serait mat (indiscutable), car le roi pourrait + virtuellement aller en g1 avant de se faire capturer par le pion caméléon. + +figure.diagram-container + .diagram + | fen:7k/b5pp/8/8/8/8/6ss/7K: + figcaption Après 1...g2+(#?) + +p. + L'interface considère que c'est mat dans les deux cas, partant + du principe que "c'est mat si on ne peut pas bouger et que l'adversaire + au trait peut capturer le roi" (éventuellement après un coup interdit). + h2.stageDelimiter Phase 2 p.warn Cette étape n'est pas (et ne sera probablement jamais) implémentée ici. @@ -102,5 +129,5 @@ ul Les règles de l'Échiqueté ont été déterminées par Patrick Bernier, puis développées avec l'aide de Benjamin Auder. li. - Merci également à Olive Martin, Christian Poisson, Bevis Martin, Laurent Nouhaud - et Frédéric Fradet. + Merci également à Olive Martin, Christian Poisson, Bevis Martin, + Laurent Nouhaud et Frédéric Fradet. diff --git a/views/rules/Loser/en.pug b/views/rules/Losers/en.pug similarity index 100% rename from views/rules/Loser/en.pug rename to views/rules/Losers/en.pug diff --git a/views/rules/Loser/fr.pug b/views/rules/Losers/fr.pug similarity index 100% rename from views/rules/Loser/fr.pug rename to views/rules/Losers/fr.pug