From: Benjamin Auder Date: Sat, 5 Dec 2020 15:07:37 +0000 (+0100) Subject: Fix Fullcavalry + complete rules. Also complete Atomic2 rules X-Git-Url: https://git.auder.net/%7B%7B%20asset%28%27mixstore/css/static/img/doc/%7B%7B?a=commitdiff_plain;h=a19caec0e72dd3b37af961daabc35eb789476ab1;p=vchess.git Fix Fullcavalry + complete rules. Also complete Atomic2 rules --- diff --git a/client/src/base_rules.js b/client/src/base_rules.js index d17ebe67..46c09a5c 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -969,7 +969,7 @@ export const ChessRules = class ChessRules { ], vanish: [ // King might be initially disguised (Titan...) - new PiPo({ x: x, y: y, p: this.board[x][y][1], c: c }), + new PiPo({ x: x, y: y, p: castlingKing, c: c }), new PiPo({ x: x, y: rookPos, p: castlingPiece, c: c }) ], end: diff --git a/client/src/translations/rules/Atomic2/en.pug b/client/src/translations/rules/Atomic2/en.pug index dc0e87a0..9311018e 100644 --- a/client/src/translations/rules/Atomic2/en.pug +++ b/client/src/translations/rules/Atomic2/en.pug @@ -9,15 +9,14 @@ p. figure.diagram-container .diagram | fen:rnbqkbnr/pppppppp/8/8/8/8/PPPP1PPP/RNBQKBNR: - figcaption. - After 1.e2X, pawn removal at e2. + figcaption After 1.e2X, pawn removal at e2. h3 More information p | This variant aims at balancing the big white's advantage in Atomic1. | It was suggested (relayed?) and analyzed recently (2020) by a strong - | Atomic player (Gannet on Discord). See + | Atomic player (Gannet on Discord). See a(href="https://discord.com/channels/686736099959504907/687076744095858762/762398439043498046") | the messages |  on Discord vchess server. diff --git a/client/src/translations/rules/Atomic2/es.pug b/client/src/translations/rules/Atomic2/es.pug index 21203baa..56cdaff2 100644 --- a/client/src/translations/rules/Atomic2/es.pug +++ b/client/src/translations/rules/Atomic2/es.pug @@ -1 +1,23 @@ -p.boxed TODO +p.boxed. + Atomic1, pero el primer movimiento de las blancas es quitar un peón. + +p. + En el primer movimiento del juego, las blancas deben elegir un peón para + borrar, blanco o negro. + Entonces las negras responden, luego el juego continúa normalmente. + +figure.diagram-container + .diagram + | fen:rnbqkbnr/pppppppp/8/8/8/8/PPPP1PPP/RNBQKBNR: + figcaption Después de 1.e2X, remoción de peones en e2. + +h3 Más información + +p + | Esta variante tiene como objetivo reequilibrar la gran ventaja de las + | blancas en Atomic1. Ha sido sugerido (¿retransmitido?) y analizado + | recientemente (2020) por un fuerte jugador de ajedrez atómico + | (Gannet en Discord). Ver + a(href="https://discord.com/channels/686736099959504907/687076744095858762/762398439043498046") + | los mensajes + |  en el servidor Discord de vchess. diff --git a/client/src/translations/rules/Atomic2/fr.pug b/client/src/translations/rules/Atomic2/fr.pug index 21203baa..d072c7b1 100644 --- a/client/src/translations/rules/Atomic2/fr.pug +++ b/client/src/translations/rules/Atomic2/fr.pug @@ -1 +1,23 @@ -p.boxed TODO +p.boxed. + Atomic1, mais le premier coup des blancs consiste à supprimer un pion. + +p. + Au tout premier coup de la partie, les blancs doivent choisir un pion à + supprimer, blanc ou noir. + Les noirs répondent alors, puis la partie continue normalement. + +figure.diagram-container + .diagram + | fen:rnbqkbnr/pppppppp/8/8/8/8/PPPP1PPP/RNBQKBNR: + figcaption. + Après 1.e2X, suppression de pion en e2. + +h3 Plus d'information + +p + | Cette variante vise à rééquilibrer le gros avantage blanc en Atomic1. + | Elle a été suggérée (relayée ?) et analysée récemment (2020) par un fort + | joueur d'échecs atomiques (Gannet sur Discord). Voir + a(href="https://discord.com/channels/686736099959504907/687076744095858762/762398439043498046") + | les messages + |  sur le serveur Discord de vchess. diff --git a/client/src/translations/rules/Fullcavalry/en.pug b/client/src/translations/rules/Fullcavalry/en.pug index 6bb88da3..98110364 100644 --- a/client/src/translations/rules/Fullcavalry/en.pug +++ b/client/src/translations/rules/Fullcavalry/en.pug @@ -8,16 +8,25 @@ p | . p. - White lancers are facing diagonally upward, while black lancers point - diagonally down in the deterministic initial setup. - In the randomized version, to avoid complex setup rules, - all lancers are facing up. + Lancers are initially facing each other. + They can be reoriented after castling, if they effectively moved. + While castling, some pieces can stand on the lancer's path, + since it can jump over them. figure.diagram-container - .diagram - | fen:efbqkbnm/pppppppp/8/8/8/8/PPPPPPPP/EDBQKBNM: - figcaption Initial deterministic position. + .diagram.diag12 + | fen:nn1ekm2/p1ppppqp/bp3bp1/8/8/BPPN2N1/P1QPPPPP/EB4KM: + .diagram.diag22 + | fen:nn1e1mk1/p1ppppqp/bp3bp1/8/8/BPPN2N1/P1QPPPPP/1BKC3M: + figcaption Before and after 0-0-0+L:N 0-0 (no reorientation) -h3 More information +p. + Pawn cannot promote in rooks, and, since they are absent of this game, + neither do they promote in jailer or sentry. + +h3 Source -p TODO +p + | This variant was imagined by the creator of + a(href="/variants/Eightpieces") Eightpieces + | . diff --git a/client/src/translations/rules/Fullcavalry/es.pug b/client/src/translations/rules/Fullcavalry/es.pug index 21203baa..2067a1d1 100644 --- a/client/src/translations/rules/Fullcavalry/es.pug +++ b/client/src/translations/rules/Fullcavalry/es.pug @@ -1 +1,32 @@ -p.boxed TODO +p.boxed + | Las torres son reemplazadas por lanceros de la variante Eightpieces. + +p + | Todo procede como en el ajedrez ortodoxo, excepto por + | torres que son reemplazadas por lanceros como se define en la + a(href="variants/Eightpieces") variante Eightpieces + | . + +p. + Los Lancers inicialmente se enfrentan entre sí. + Se pueden reorientar después del enroque, si realmente se movieron. + Durante el enroque, se pueden encontrar piezas en el camino de la lanza, + ya que puede saltarlo. + +figure.diagram-container + .diagram.diag12 + | fen:nn1ekm2/p1ppppqp/bp3bp1/8/8/BPPN2N1/P1QPPPPP/EB4KM: + .diagram.diag22 + | fen:nn1e1mk1/p1ppppqp/bp3bp1/8/8/BPPN2N1/P1QPPPPP/1BKC3M: + figcaption Antes y después 0-0-0+L:N 0-0 (sin reorientación) + +p. + Los peones no pueden promoverse en torres y, dado que están ausentes de + este juego, no se promocionan a sí mismos como carceleros o centinelas. + +h3 Fuente + +p + | Esta variante fue imaginada por el creador de + a(href="/variants/Eightpieces") Eightpieces + | . diff --git a/client/src/translations/rules/Fullcavalry/fr.pug b/client/src/translations/rules/Fullcavalry/fr.pug index 2605db40..e92a1afd 100644 --- a/client/src/translations/rules/Fullcavalry/fr.pug +++ b/client/src/translations/rules/Fullcavalry/fr.pug @@ -8,17 +8,25 @@ p | . p. - Les lanciers blancs regarde vers l'avant en diagonale, tandis que les - lanciers noirs pointent vers le sud (toujours en diagonale) dans - l'arrangement initial déterministe. - Dans la version aléatoire, tous les lanciers regardent droit devant afin - d'éviter des règles d'initialisation complexes. + Les lanciers se font initialement face. + Ils peuvent être réorientés après le roque, s'ils ont effectivement bougé. + Pendant le roque, des pièces peuvent se trouver sur le chemin du lancier, + puisqu'il peut sauter par dessus. figure.diagram-container - .diagram - | fen:efbqkbnm/pppppppp/8/8/8/8/PPPPPPPP/EDBQKBNM: - figcaption Position initiale déterministe. + .diagram.diag12 + | fen:nn1ekm2/p1ppppqp/bp3bp1/8/8/BPPN2N1/P1QPPPPP/EB4KM: + .diagram.diag22 + | fen:nn1e1mk1/p1ppppqp/bp3bp1/8/8/BPPN2N1/P1QPPPPP/1BKC3M: + figcaption Avant et après 0-0-0+L:N 0-0 (pas de réorientation) -h3 Plus d'information +p. + Les pions ne peuvent être promus en tours, et, puisqu'ils sont absents de + ce jeu, ils ne se promeuvent pas non plus en geôlier ou sentinelle. + +h3 Source -p TODO +p + | Cette variante a été imaginée par le créateur de + a(href="/variants/Eightpieces") Eightpieces + | . diff --git a/client/src/variants/Fullcavalry.js b/client/src/variants/Fullcavalry.js index c25f3400..672ebc8a 100644 --- a/client/src/variants/Fullcavalry.js +++ b/client/src/variants/Fullcavalry.js @@ -76,9 +76,11 @@ export class FullcavalryRules extends ChessRules { } getPPpath(m, orientation) { + // If castle, show choices on m.appear[1]: + const index = (m.appear.length == 2 ? 1 : 0); return ( this.getPpath( - m.appear[0].c + m.appear[0].p, + m.appear[index].c + m.appear[index].p, null, null, orientation @@ -92,9 +94,18 @@ export class FullcavalryRules extends ChessRules { return "efbqkbnm/pppppppp/8/8/8/8/PPPPPPPP/EDBQKBNM w 0 ahah -"; const baseFen = ChessRules.GenRandInitFen(randomness); - // Replace black rooks by lancers oriented south, - // and white rooks by lancers oriented north: - return baseFen.replace(/r/g, 'g').replace(/R/g, 'C'); + // Replace rooks by lancers with expected orientation: + const firstBlackRook = baseFen.indexOf('r'), + lastBlackRook = baseFen.lastIndexOf('r'), + firstWhiteRook = baseFen.indexOf('R'), + lastWhiteRook = baseFen.lastIndexOf('R'); + return ( + baseFen.substring(0, firstBlackRook) + 'e' + + baseFen.substring(firstBlackRook + 1, lastBlackRook) + 'm' + + baseFen.substring(lastBlackRook + 1, firstWhiteRook) + 'E' + + baseFen.substring(firstWhiteRook + 1, lastWhiteRook) + 'M' + + baseFen.substring(lastWhiteRook + 1) + ); } // Because of the lancers, getPiece() could be wrong: @@ -143,6 +154,65 @@ export class FullcavalryRules extends ChessRules { return super.getPotentialMovesFrom([x, y]); } + getPotentialPawnMoves([x, y]) { + const color = this.getColor(x, y); + let moves = []; + const [sizeX, sizeY] = [V.size.x, V.size.y]; + let shiftX = (color == "w" ? -1 : 1); + const startRank = color == "w" ? sizeX - 2 : 1; + const lastRank = color == "w" ? 0 : sizeX - 1; + + let finalPieces = [V.PAWN]; + if (x + shiftX == lastRank) { + // Only allow direction facing inside board: + const allowedLancerDirs = + lastRank == 0 + ? ['e', 'f', 'g', 'h', 'm'] + : ['c', 'd', 'e', 'm', 'o']; + finalPieces = allowedLancerDirs.concat([V.KNIGHT, V.BISHOP, V.QUEEN]); + } + 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 (x == startRank && this.board[x + 2 * shiftX][y] == V.EMPTY) + // 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) { + moves.push( + this.getBasicMove([x, y], [x + shiftX, y + shiftY], { + c: color, + p: piece + }) + ); + } + } + } + + // Add en-passant captures + Array.prototype.push.apply( + moves, + this.getEnpassantCaptures([x, y], shiftX) + ); + + return moves; + } + // Obtain all lancer moves in "step" direction getPotentialLancerMoves_aux([x, y], step, tr) { let moves = []; @@ -184,6 +254,100 @@ export class FullcavalryRules extends ChessRules { return moves; } + getCastleMoves([x, y]) { + const c = this.getColor(x, y); + + // Castling ? + const oppCol = V.GetOppCol(c); + let moves = []; + let i = 0; + // King, then lancer: + const finalSquares = [ [2, 3], [V.size.y - 2, V.size.y - 3] ]; + castlingCheck: for ( + let castleSide = 0; + castleSide < 2; + castleSide++ //large, then small + ) { + if (this.castleFlags[c][castleSide] >= V.size.y) continue; + // If this code is reached, lancer and king are on initial position + + const lancerPos = this.castleFlags[c][castleSide]; + const castlingPiece = this.board[x][lancerPos].charAt(1); + + // Nothing on the path of the king ? (and no checks) + const finDist = finalSquares[castleSide][0] - y; + let step = finDist / Math.max(1, Math.abs(finDist)); + i = y; + do { + 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 || ![y, lancerPos].includes(i)) + ) + ) { + continue castlingCheck; + } + i += step; + } while (i != finalSquares[castleSide][0]); + + // Nothing on final squares, except maybe king and castling lancer? + for (i = 0; i < 2; i++) { + if ( + finalSquares[castleSide][i] != lancerPos && + this.board[x][finalSquares[castleSide][i]] != V.EMPTY && + ( + finalSquares[castleSide][i] != y || + this.getColor(x, finalSquares[castleSide][i]) != c + ) + ) { + continue castlingCheck; + } + } + + // If this code is reached, castle is valid + let allowedLancerDirs = [castlingPiece]; + if (finalSquares[castleSide][1] != lancerPos) { + // It moved: allow reorientation + allowedLancerDirs = + x == 0 + ? ['e', 'f', 'g', 'h', 'm'] + : ['c', 'd', 'e', 'm', 'o']; + } + allowedLancerDirs.forEach(dir => { + moves.push( + new Move({ + appear: [ + new PiPo({ + x: x, + y: finalSquares[castleSide][0], + p: V.KING, + c: c + }), + new PiPo({ + x: x, + y: finalSquares[castleSide][1], + p: dir, + c: c + }) + ], + vanish: [ + new PiPo({ x: x, y: y, p: V.KING, c: c }), + new PiPo({ x: x, y: lancerPos, p: castlingPiece, c: c }) + ], + end: + Math.abs(y - lancerPos) <= 2 + ? { x: x, y: lancerPos } + : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) } + }) + ); + }); + } + + return moves; + } + isAttacked(sq, color) { return ( super.isAttacked(sq, color) || @@ -254,6 +418,9 @@ export class FullcavalryRules extends ChessRules { if (Object.keys(V.LANCER_DIRNAMES).includes(move.vanish[0].p)) // Lancer: add direction info notation += "=" + V.LANCER_DIRNAMES[move.appear[0].p]; + else if (move.appear.length == 2 && move.vanish[1].p != move.appear[1].p) + // Same after castle: + notation += "+L:" + V.LANCER_DIRNAMES[move.appear[1].p]; else if ( move.vanish[0].p == V.PAWN && Object.keys(V.LANCER_DIRNAMES).includes(move.appear[0].p)