-# New variants
Finish https://www.chessvariants.com/mvopponent.dir/dynamo.html
https://echekk.fr/spip.php?page=article&id_article=599
-
-Colorbound
-https://www.chessvariants.com/d.betza/chessvar/dan/colclob.html
-https://en.wikipedia.org/wiki/Grotesque_(chess) (fun)
-
-Maxima, Interweave, Roccoco
-https://www.chessvariants.com/other.dir/rococo.html
-https://www.chessvariants.com/dpieces.dir/maxima/maxima.html
-https://www.chessvariants.com/other.dir/interweave.html
-
-Take(a)n(d)make : if capture a piece, take its power for the last of the turn and make a move like it.
-If a pawn taken: direction of the capturer, can capture enemy.
-http://www.strategems.net/sections/fairy_defs.html
-Having captured, a unit must immediately, as part of its move, play a non-capturing move, using ONLY the powers of movement of the captured unit from the capture-square. If no such move is available, the capture is illegal. Promotion by capture occurs only when a pawn arrives on the promotion rank as the result of a Take & Make move. Checks are as in normal chess: after the notional capture of the checked King, the checking unit does not move away from the King’s square.
-
-Chakart :)
-
-https://en.wikipedia.org/wiki/Fairy_chess_piece
+And Chakart :)
background-color: #6f8f57
div.board
+ user-select: none
float: left
height: 0
display: inline-block
const L = this.moves.length;
if (!Array.isArray(this.moves[L-1]))
this.$set(this.moves, L-1, [this.moves[L-1], smove]);
- else
- this.$set(this.moves, L-1, this.moves.concat([smove]));
+ else this.moves[L-1].push(smove);
}
};
const playMove = () => {
this.compWorker.postMessage(["init", game.fen]);
this.vr = new V(game.fen);
game.players = [{ name: "Myself" }, { name: "Computer" }];
- if (game.myColor == "b") game.players = game.players.reverse();
+ if (game.mycolor == "b") game.players = game.players.reverse();
game.score = "*"; //finished games are removed
this.currentUrl = document.location.href; //to avoid playing outside page
this.game = game;
a(href="https://brainking.com/") brainking.com
a(href="https://www.facebook.com/groups/592562551198628") A Facebook group
a(href="http://www.zillions-of-games.com/") zillions-of-games.com
+ a(href="https://en.wikipedia.org/wiki/Fairy_chess_piece") Fairy chess pieces
a(href="https://brainking.com/") brainking.com
a(href="https://www.facebook.com/groups/592562551198628") Un grupo Facebook
a(href="http://www.zillions-of-games.com/") zillions-of-games.com
+ a(href="https://en.wikipedia.org/wiki/Fairy_chess_piece")
+ | Piezas de ajedrez magicas
a(href="https://brainking.com/") brainking.com
a(href="https://www.facebook.com/groups/592562551198628") Un groupe Facebook
a(href="http://www.zillions-of-games.com/") zillions-of-games.com
+ a(href="https://en.wikipedia.org/wiki/Fairy_chess_piece")
+ | Pièces d'échecs féériques
--- /dev/null
+p.boxed
+ Pieces are colorbound and capture on the other color.
+ The goal is to capture a king. Captures are mandatory.
+
+figure.diagram-container
+ .diagram
+ | fen:rbnkknbr/pppppppp/8/8/8/8/PPPPPPPP/RBNKKNBR:
+ figcaption Initial deterministic position
+
+p.
+ Each side has two small teams of four pieces (and four pawns), one on light
+ squares and the other, identical, on dark squares.
+ Kings have no royal status, and the goal is to capture one of them.
+ Captures are mandatory, but if there are several available you can choose.
+
+p.
+ All pieces end their move on a square of the same color as the initial one.
+ They all capture pieces only on the other color.
+
+p.
+ If after a capture the capturer can take another piece, then the move
+ continue with this new capture. As in international Draughts.
+
+h4 King
+
+p.
+ The King moves without capturing one square diagonally.
+ He captures by a short orthogonal leap. There is no castling.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:8/8/4b3/3nK3/8/8/8/8 d6,f6,f4,d4:
+ .diagram.diag22
+ | fen:8/8/4b3/3nK3/8/8/8/8 c5,e7:
+ figcaption.
+ King moves without captures on the left, and with captures on the right.
+
+h4 Rook ("Smasher")
+
+p.
+ The Smasher may slide any distance diagonally without capturing.
+ It may slide orthogonally as well, potentially capturing.
+ Captures are achieved by approach, by stopping just before the enemy piece.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:8/8/5p2/8/8/8/1R3p2/8 a1,c1,d2,a3,b4,c3,d4,e5,b6,b8:
+ .diagram.diag22
+ | fen:8/1p6/8/8/8/8/1R4p1/8 b6,f2:
+ figcaption.
+ Rook moves without captures on the left, and with captures on the right.
+
+h4 Knight ("Leaper")
+
+p.
+ The Leaper may slide any distance diagonally without capturing.
+ It may slide orthogonally as well, potentially capturing.
+ It may also make a double knight's move (a 4x2 'L'), as long as there
+ isn't a friendly piece at the midpoint of the move.
+ Captures are achieved by jumping over a piece, either with an orthogonal
+ or double-knight move.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:8/p7/5p2/8/8/1P/8/N5p1 e3,c1,e1,a3,a5,b2,c3,d4,e5:
+ .diagram.diag22
+ | fen:8/8/8/8/p7/8/2p5/N6b e3,a5,a7:
+ figcaption.
+ Knight moves without captures on the left, and with captures on the right.
+
+h4 Bishop ("Remover")
+
+p.
+ The Remover may slide any distance diagonally without capturing.
+ It may capture any orthogonally adjacent opposing piece without moving.
+
+figure.diagram-container
+ .diagram
+ | fen:8/8/8/4p3/4Bp2/8/8/8 e5,f4:
+ figcaption The bishop must capture both pawns.
+
+h4 Pawns
+
+p.
+ A Pawn may move one square diagonally forward to an empty square, or
+ optionally two squares if it hasn't moved yet (in this case it may be
+ captured en-passant by short leap).
+ Pawns capture by a short leap forward, and by orthogonal custodian capture
+ where the Pawn completes the sequence FEP (Friend, Enemy, Pawn, or
+ Pawn, Enemy, Friend).
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:8/8/3P4/8/8/8/1P6/8 a3,c3,d4,e7,c7:
+ .diagram.diag22
+ | fen:8/Nr5R/2rP3n/2N5/7b/4b2P/4P3/8 c7,e4,h5:
+ figcaption.
+ Pawn moves without captures on the left, and with captures on the right.
+
+p.
+ A Pawn that reaches the second-to-last rank of the board may promote to any
+ previously captured friendly piece (not yet returned to play).
+ Upon reaching the last rank, the Pawn is required to promote; if there are
+ no pieces available for promotion, then the move is not possible.
+
+h3 Source
+
+p
+ | The
+ a(href="https://www.chessvariants.com/other.dir/interweave.html")
+ | Interweave variant
+ | on chessvariants.com.
+
+p Inventor: Peter Aronson (2002)
--- /dev/null
+p.boxed.
+ Las piezas evolucionan en un solo color y se capturan en el otro.
+ El objetivo es capturar a un rey. Las capturas son obligatorias.
+
+figure.diagram-container
+ .diagram
+ | fen:rbnkknbr/pppppppp/8/8/8/8/PPPPPPPP/RBNKKNBR:
+ figcaption Posición determinista inicial
+
+p.
+ Cada campamento tiene dos pequeños equipos de cuatro piezas (y cuatro
+ peones), uno en casillas blancas y el otro, idéntico, en casillas negras.
+ Los reyes no tienen estatus real, y el objetivo es capturar uno.
+ Las capturas son obligatorias, pero si es posible realizar varias
+ se puede elegir.
+
+p.
+ Todas las piezas finalizan su movimiento en un cuadrado del mismo color que
+ el casilla inicial Todas capturan piezas de otro color.
+
+p.
+ Si después de una captura la pieza de captura puede hacer otra,
+ entonces ella debe hacerlo y la jugada continúa.
+ Como las Damas internacionales.
+
+h4 King
+
+p.
+ El rey se mueve sin capturar una casilla en diagonal.
+ Captura por un corto salto ortogonal. No hay enroque.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:8/8/4b3/3nK3/8/8/8/8 d6,f6,f4,d4:
+ .diagram.diag22
+ | fen:8/8/4b3/3nK3/8/8/8/8 c5,e7:
+ figcaption.
+ Movimientos del rey sin capturas a la izquierda y con capturas
+ a la derecha.
+
+h4 Torre ("Bateador")
+
+p.
+ El Bateador se mueve como un alfil ortodoxo sin capturar.
+ También puede moverse como una torre, posiblemente con captura.
+ Las capturas se realizan por acercamiento, deteniéndose justo antes de la
+ pieza opuesta.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:8/8/5p2/8/8/8/1R3p2/8 a1,c1,d2,a3,b4,c3,d4,e5,b6,b8:
+ .diagram.diag22
+ | fen: 8/1p6/8/8/8/8/1R4p1/8 b6,f2:
+ figcaption.
+ Movimientos de torre sin capturas a la izquierda y con capturas
+ a la derecha.
+
+h4 Caballo ("Saltador")
+
+p.
+ El Saltador se mueve como un alfil ortodoxo sin capturar.
+ También puede moverse como una torre, posiblemente con captura,
+ o hacer un doble puente (una 'L' 4x2), si no hay una pieza amiga en el medio.
+ Las capturas se realizan saltando sobre una pieza, a través de un movimiento
+ ortogonal o un doble puente.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:8/p7/5p2/8/8/1P/8/N5p1 e3,c1,e1,a3,a5,b2,c3,d4,e5:
+ .diagram.diag22
+ | fen:8/8/8/8/p7/8/2p5/N6b e3,a5,a7:
+ figcaption.
+ Movimiento de alfil sin capturas a la izquierda y con capturas
+ a la derecha.
+
+h4 Alfil ("Supresor")
+
+p.
+ El Supresor se mueve como un alfil ortodoxo sin capturar.
+ Puede capturar cualquier pieza opuesta ortogonalmente adyacente.
+
+figure.diagram-container
+ .diagrama
+ | fen:8/8/8/4p3/4Bp2/8/8/8 e5,f4:
+ figcaption El alfil debe capturar los dos peones.
+
+h4 Peones
+
+p.
+ Un peón se mueve una casilla diagonalmente hacia adelante, o posiblemente
+ dos espacios si aún no se ha movido (podría ser capturado
+ en passant por salto corto).
+ Los peones capturan por un salto corto hacia adelante y por capturas
+ "encuadre" ortogonal donde el peón completa la secuencia AEP (Amigo,
+ Enemigo, Peón o Peón, Enemigo, Amigo).
+
+figure.diagram-container
+ .diagram.diag12
+ | fen: 8/8/3P4/8/8/8/1P6/8 a3,c3,d4,e7,c7:
+ .diagram.diag22
+ | fen: 8/Nr5R/2rP3n/2N5/7b/4b2P/4P3/8 c7,e4,h5:
+ figcaption.
+ Movimientos de peón sin capturas a la izquierda y con capturas
+ a la derecha.
+
+p.
+ Un peón que alcanza la penúltima fila puede ser promovido en cualquier
+ pieza amiga capturó (y aún no ha puesto en juego).
+ En la última fila, la promoción se vuelve obligatoria y, por lo tanto,
+ el movimiento es prohibido si no hay piezas disponibles.
+
+h3 Fuente
+
+p
+ | La
+ a(href="https://www.chessvariants.com/other.dir/interweave.html")
+ variante Interweave
+ | en chessvariants.com.
+
+p Inventor: Peter Aronson (2002)
--- /dev/null
+p.boxed.
+ Les pièces n'évoluent que sur une seule couleur, et capturent sur l'autre.
+ L'objectif est de capturer un roi. Les captures sont obligatoires.
+
+figure.diagram-container
+ .diagram
+ | fen:rbnkknbr/pppppppp/8/8/8/8/PPPPPPPP/RBNKKNBR:
+ figcaption Position initiale déterministe
+
+p.
+ Chaque camp dispose de deux petites équipes de quatre pièces (et quatre
+ pions), une sur cases blanches et l'autre, identique, sur cases noires.
+ Les rois n'ont pas de statut royal, et l'objectif est d'en capturer un.
+ Les captures sont obligatoires, mais plusieurs sont possibles vous pouvez
+ choisir.
+
+p.
+ Toutes les pièces terminent leur coup sur une case de la même couleur que la
+ case initiale. Elles capturent toutes des pièces sur une autre couleur.
+
+p.
+ Si à l'issue d'une capture la pièce capturante peut en effectuer une autre,
+ alors elle doit le faire et le coup continue.
+ Comme aux Dames internationales.
+
+h4 Roi
+
+p.
+ Le roi se déplace sans capturer d'une case en diagonale.
+ Il capture par un court saut orthogonal. Il n'y a pas de roque.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:8/8/4b3/3nK3/8/8/8/8 d6,f6,f4,d4:
+ .diagram.diag22
+ | fen:8/8/4b3/3nK3/8/8/8/8 c5,e7:
+ figcaption.
+ Coups de roi sans captures à gauche, et avec captures à droite.
+
+h4 Tour ("Frappeur")
+
+p.
+ Le Frappeur se déplace comme un fou orthodoxe sans capturer.
+ Il peut aussi se déplacer comme une tour, éventuellement avec capture.
+ Les captures sont réalisées par approche, en s'arrêtant juste avant la pièce
+ adverse.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:8/8/5p2/8/8/8/1R3p2/8 a1,c1,d2,a3,b4,c3,d4,e5,b6,b8:
+ .diagram.diag22
+ | fen:8/1p6/8/8/8/8/1R4p1/8 b6,f2:
+ figcaption.
+ Coups de tour sans captures à gauche, et avec captures à droite.
+
+h4 Cavalier ("Sauteur")
+
+p.
+ Le Sauteur se déplace comme un fou orthodoxe sans capturer.
+ Il peut aussi se déplacer comme une tour, éventuellement avec capture,
+ ou effectuer un double saut de cavalier (un 'L' 4x2), du moment qu'il n'y
+ ait pas de pièce amie au milieu.
+ Les captures s'effectuent en sautant par dessus une pièce, via un coup
+ orthogonal ou un double coup de cavalier.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:8/p7/5p2/8/8/1P/8/N5p1 e3,c1,e1,a3,a5,b2,c3,d4,e5:
+ .diagram.diag22
+ | fen:8/8/8/8/p7/8/2p5/N6b e3,a5,a7:
+ figcaption.
+ Coups de cavalier sans captures à gauche, et avec captures à droite.
+
+h4 Fou ("Supprimeur")
+
+p.
+ Le Supprimeur se déplace comme un fou orthodoxe sans capturer.
+ Il peut capturer toute pièce adverse orthogonalement adjacente.
+
+figure.diagram-container
+ .diagram
+ | fen:8/8/8/4p3/4Bp2/8/8/8 e5,f4:
+ figcaption Le fou doit capturer les deux pions.
+
+h4 Pions
+
+p.
+ Un pion se déplace d'une case en diagonale vers l'avant, ou éventuellement
+ de deux cases s'il n'a pas encore bougé (il pourrait alors être capturé
+ en passant par saut court).
+ Les pions capturent par un saut court vers l'avant, et par captures
+ d'"encadrement" orthogonales où le pion complète la séquence AEP (Ami,
+ Ennemi, Pion ou Pion, Enemmi, Ami).
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:8/8/3P4/8/8/8/1P6/8 a3,c3,d4,e7,c7:
+ .diagram.diag22
+ | fen:8/Nr5R/2rP3n/2N5/7b/4b2P/4P3/8 c7,e4,h5:
+ figcaption.
+ Coups de pion sans captures à gauche, et avec captures à droite.
+
+p.
+ Un pion qui atteint l'avant-dernière rangée peut se promouvoir en n'importe
+ quelle pièce amie capturée (et pas encore remise en jeu).
+ Sur la dernière rangée la promotion devient obligatoire, et donc le coup est
+ interdit si aucune pièce n'est disponible.
+
+h3 Source
+
+p
+ | La
+ a(href="https://www.chessvariants.com/other.dir/interweave.html")
+ | variante Interweave
+ | sur chessvariants.com.
+
+p Inventeur : Peter Aronson (2002)
--- /dev/null
+p.boxed.
+ After each capture, you must play again with the capturing piece,
+ moving as the captured unit.
+
+p.
+ Having captured, a unit must immediately play a non-capturing move,
+ using only the movements of the captured unit from the
+ capture-square. If no such move is available, the capture is illegal.
+ For example, on the following diagram after 4.Bxc6 the bishop will have a
+ knight move to execute, arriving either on a5, b4, d4, e7 or b8.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:r1bqkb1r/pppp1ppp/2n2n2/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R:
+ .diagram.diag22
+ | fen:r1bqkb1r/pppp1ppp/5n2/B3p3/4P3/5N2/PPPP1PPP/RNBQK2R:
+ figcaption Left: before Bxc6. Right: after Bxc6,Ba5
+
+p.
+ Promotion can occur when a pawn arrives on the last rank as the result
+ of a Take & Make move. Pawns can also take on the final rank:
+ they promote, and immediatly make a move as the captured unit.
+
+p.
+ Note: the king can be under check after a capture, and cover the check with
+ the subsequent "as-captured" move, as the following diagram illustrates.
+
+figure.diagram-container
+ .diagram
+ | fen:rnbq2nr/ppp2ppp/4p3/3p4/1k1P4/P1N2N2/1PP1PPPP/R2QKB1R c3:
+ figcaption The king can take the knight and then move to a4, b5 or e4.
+
+p.
+ Checks are as in normal chess: after the
+ notional capture of the checked King, the checking unit does not move away
+ from the King's square.
+
+figure.diagram-container
+ .diagram
+ | fen:r1bqkbnr/1ppppppp/2n2N2/p7/8/8/PPPPPPPP/R1BQKBNR:
+ figcaption The black king is in check.
+
+h3 Source
+
+p
+ | These rules are slightly modified from the definition given
+ a(href="http://www.strategems.net/sections/fairy_defs.html") on this page
+ | . (Type 'take' in the search box).
--- /dev/null
+p.boxed.
+ Después de cada captura, aún debes jugar con la pieza de captura,
+ luego se mueve como la pieza capturada.
+
+p.
+ Una vez capturada, una pieza debe hacer inmediatamente un movimiento
+ non capturando desde la casilla de captura, moviéndose como la pieza
+ capturado. Si tal movimiento no es posible, entonces la captura es ilegal.
+ Por ejemplo, en el siguiente diagrama después de 4.Bxc6 el alfil tendrá un
+ movimiento de caballo a ejecutar, llegando a uno de las casillas
+ a5, b4, d4, e7 o b8.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:r1bqkb1r/pppp1ppp/2n2n2/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R:
+ .diagram.diag22
+ | fen:r1bqkb1r/pppp1ppp/5n2/B3p3/4P3/5N2/PPPP1PPP/RNBQK2R:
+ figcaption Izquierda: antes de Bxc6. Derecha: después de Bxc6,Ba5
+
+p.
+ Una promoción puede ocurrir cuando un peón llega a la última fila
+ siguiendo un movimiento Take & Make ("Tomar & hacer"). Un peón también
+ puede capturar en la última fila: es promovido e inmediatamente hace un
+ movimiento como la unidad capturada.
+
+p.
+ Nota: El rey puede estar en jaque después de una captura y cubrirlo para
+ usando el movimiento "como-capturado" para seguir, como se ilustra en el
+ diagrama.
+
+figure.diagram-container
+ .diagram
+ | fen:rnbq2nr/ppp2ppp/4p3/3p4/1k1P4/P1N2N2/1PP1PPPP/R2QKB1R c3:
+ figcaption El rey puede tomar el caballo y luego ir a a4, b5 o e4.
+
+p.
+ Los jaques se entienden como en el juego ortodoxo: después de la captura
+ imaginario del rey contrario, la pieza que da jaque no tendría que
+ ejecutar un movimiento de rey.
+
+figure.diagram-container
+ .diagram
+ | fen:r1bqkbnr/1ppppppp/2n2N2/p7/8/8/PPPPPPPPP/R1BQKBNR:
+ figcaption El rey negro está en jaque.
+
+h3 Fuente
+
+p
+ | Estas reglas están ligeramente modificadas ya que la definición dada
+ a(href="http://www.strategems.net/sections/fairy_defs.html") en esta página
+ | . (Escriba 'take' en el campo de búsqueda).
--- /dev/null
+p.boxed.
+ Après chaque capture, vous devez jouer encore avec la pièce capturante,
+ se déplaçant alors comme la pièce capturée.
+
+p.
+ Une fois qu'elle a capturé, une pièce doit immédiatement jour un coup
+ non-capturant depuis la case de capture, en se déplaçant comme la pièce
+ capturée. Si aucun tel coup n'est possible, alors la capture est illégale.
+ Par exemple, sur le diagramme suivant après 4.Bxc6 le fou aura un coup
+ de cavalier à exécuter, arrivant sur l'une des cases a5, b4, d4, e7 ou b8.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:r1bqkb1r/pppp1ppp/2n2n2/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R:
+ .diagram.diag22
+ | fen:r1bqkb1r/pppp1ppp/5n2/B3p3/4P3/5N2/PPPP1PPP/RNBQK2R:
+ figcaption Gauche: avant Bxc6. Droite: après Bxc6,Ba5
+
+p.
+ Une promotion peut survenir quand un pion arrive sur la dernière rangée
+ suite à un coup Take & Make ("Prend & Fait"). Un pion peut aussi capturer
+ sur la dernière rangée : il est promu, et effectue immédiatement un coup
+ comme l'unité capturée.
+
+p.
+ Note : Le roi peut être en échec après une capture, et couvrir celui-ci à
+ l'aide du coup "comme-capturé" à suivre, comme illustré sur le diagramme.
+
+figure.diagram-container
+ .diagram
+ | fen:rnbq2nr/ppp2ppp/4p3/3p4/1k1P4/P1N2N2/1PP1PPPP/R2QKB1R c3:
+ figcaption Le roi peut prendre le cavalier et ensuite aller en a4, b5 ou e4.
+
+p.
+ Les échecs s'entendent comme dans le jeu orthodoxe : après la capture
+ imaginaire du roi adverse, la pièce donnant échec n'aurait pas à exécuter un
+ coup de roi.
+
+figure.diagram-container
+ .diagram
+ | fen:r1bqkbnr/1ppppppp/2n2N2/p7/8/8/PPPPPPPP/R1BQKBNR:
+ figcaption Le roi noir est en échec.
+
+h3 Source
+
+p
+ | Ces règles sont légèrement modifiées depuis la définition donnée
+ a(href="http://www.strategems.net/sections/fairy_defs.html") sur cette page
+ | . (Tapez 'take' dans le champ de recherche).
this.board[x + shiftX][y] != V.EMPTY &&
this.canTake([x, y], [x + shiftX, y])
) {
- for (let piece of finalPieces)
+ for (let piece of finalPieces) {
moves.push(
this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece })
);
+ }
}
// Next condition so that other variants could inherit from this class
const finalSquare = V.CoordsToSquare(move.end);
let notation = "";
if (move.vanish.length == 2)
- //capture
+ // Capture
notation = "Px" + finalSquare;
else {
// No capture: indicate the initial square for potential ambiguity
}
getCapturedFen() {
- let counts = [...Array(14).fill(0)];
+ let counts = [...Array(12).fill(0)];
let i = 0;
for (let j = 0; j < V.PIECES.length; j++) {
- if (V.PIECES[j] == V.KING)
- //no king captured
+ if ([V.KING, V.PAWN].includes(V.PIECES[j]))
+ // No king captured, and pawns don't promote in pawns
continue;
- counts[i] = this.captured["w"][V.PIECES[i]];
- counts[7 + i] = this.captured["b"][V.PIECES[i]];
+ counts[i] = this.captured["w"][V.PIECES[j]];
+ counts[6 + i] = this.captured["b"][V.PIECES[j]];
i++;
}
return counts.join("");
// Initialize captured pieces' counts from FEN
this.captured = {
w: {
- [V.PAWN]: parseInt(fenParsed.captured[0]),
- [V.ROOK]: parseInt(fenParsed.captured[1]),
- [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])
+ [V.ROOK]: parseInt(fenParsed.captured[0]),
+ [V.KNIGHT]: parseInt(fenParsed.captured[1]),
+ [V.BISHOP]: parseInt(fenParsed.captured[2]),
+ [V.QUEEN]: parseInt(fenParsed.captured[3]),
+ [V.MARSHALL]: parseInt(fenParsed.captured[4]),
+ [V.CARDINAL]: parseInt(fenParsed.captured[5])
},
b: {
- [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])
+ [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.MARSHALL]: parseInt(fenParsed.captured[10]),
+ [V.CARDINAL]: parseInt(fenParsed.captured[11])
}
};
}
postPlay(move) {
super.postPlay(move);
- if (move.vanish.length == 2 && move.appear.length == 1) {
+ 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]--;
- }
}
postUndo(move) {
super.postUndo(move);
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() {
--- /dev/null
+import { ChessRules, PiPo, Move } from "@/base_rules";
+import { ArrayFun } from "@/utils/array";
+import { randInt, shuffle } from "@/utils/alea";
+
+export class InterweaveRules extends ChessRules {
+ static get HasFlags() {
+ return false;
+ }
+
+ static GenRandInitFen(randomness) {
+ if (randomness == 0)
+ return "rbnkknbr/pppppppp/8/8/8/8/PPPPPPPP/RBNKKNBR w 0 - 000000";
+
+ let pieces = { w: new Array(8), b: new Array(8) };
+ for (let c of ["w", "b"]) {
+ if (c == 'b' && randomness == 1) {
+ pieces['b'] = pieces['w'];
+ break;
+ }
+
+ // Each pair of pieces on 2 colors:
+ const composition = ['r', 'n', 'b', 'k', 'r', 'n', 'b', 'k'];
+ let positions = shuffle(ArrayFun.range(4));
+ for (let i = 0; i < 4; i++)
+ pieces[c][2 * positions[i]] = composition[i];
+ positions = shuffle(ArrayFun.range(4));
+ for (let i = 0; i < 4; i++)
+ pieces[c][2 * positions[i] + 1] = composition[i];
+ }
+ return (
+ pieces["b"].join("") +
+ "/pppppppp/8/8/8/8/PPPPPPPP/" +
+ pieces["w"].join("").toUpperCase() +
+ // En-passant allowed, but no flags
+ " w 0 - 000000"
+ );
+ }
+
+ static IsGoodFen(fen) {
+ if (!ChessRules.IsGoodFen(fen)) return false;
+ const fenParsed = V.ParseFen(fen);
+ // 4) Check captures
+ if (!fenParsed.captured || !fenParsed.captured.match(/^[0-9]{6,6}$/))
+ return false;
+ return true;
+ }
+
+ 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]);
+ if (isNaN(num)) return false;
+ sumElts += num;
+ }
+ }
+ if (sumElts != V.size.y) return false;
+ }
+ // Both kings should be on board. Exactly two per color.
+ if (Object.values(kings).some(v => v != 2)) return false;
+ return true;
+ }
+
+ static ParseFen(fen) {
+ const fenParts = fen.split(" ");
+ return Object.assign(
+ ChessRules.ParseFen(fen),
+ { captured: fenParts[4] }
+ );
+ }
+
+ getFen() {
+ return super.getFen() + " " + this.getCapturedFen();
+ }
+
+ getFenForRepeat() {
+ return super.getFenForRepeat() + "_" + this.getCapturedFen();
+ }
+
+ getCapturedFen() {
+ let counts = [...Array(6).fill(0)];
+ [V.ROOK, V.KNIGHT, V.BISHOP].forEach((p,idx) => {
+ counts[idx] = this.captured["w"][p];
+ counts[3 + idx] = this.captured["b"][p];
+ });
+ return counts.join("");
+ }
+
+ scanKings() {}
+
+ setOtherVariables(fen) {
+ super.setOtherVariables(fen);
+ const fenParsed = V.ParseFen(fen);
+ // Initialize captured pieces' counts from FEN
+ this.captured = {
+ w: {
+ [V.ROOK]: parseInt(fenParsed.captured[0]),
+ [V.KNIGHT]: parseInt(fenParsed.captured[1]),
+ [V.BISHOP]: parseInt(fenParsed.captured[2]),
+ },
+ b: {
+ [V.ROOK]: parseInt(fenParsed.captured[3]),
+ [V.KNIGHT]: parseInt(fenParsed.captured[4]),
+ [V.BISHOP]: parseInt(fenParsed.captured[5]),
+ }
+ };
+ // Stack of "last move" only for intermediate captures
+ this.lastMoveEnd = [null];
+ }
+
+ // Trim all non-capturing moves
+ static KeepCaptures(moves) {
+ return moves.filter(m => m.vanish.length >= 2 || m.appear.length == 0);
+ }
+
+ // Stop at the first capture found (if any)
+ atLeastOneCapture() {
+ const color = this.turn;
+ for (let i = 0; i < V.size.x; i++) {
+ for (let j = 0; j < V.size.y; j++) {
+ if (
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) == color &&
+ V.KeepCaptures(this.getPotentialMovesFrom([i, j])).length > 0
+ ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ // En-passant after 2-sq jump
+ getEpSquare(moveOrSquare) {
+ if (!moveOrSquare) return undefined;
+ if (typeof moveOrSquare === "string") {
+ const square = moveOrSquare;
+ if (square == "-") return undefined;
+ // 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, ey] =
+ [move.start.x, move.end.x, move.start.y, move.end.y];
+ if (
+ move.vanish.length == 1 &&
+ this.getPiece(sx, sy) == V.PAWN &&
+ Math.abs(sx - ex) == 2 &&
+ Math.abs(sy - ey) == 2
+ ) {
+ return [
+ {
+ x: (ex + sx) / 2,
+ y: (ey + sy) / 2
+ },
+ // The arrival column must be remembered, because
+ // potentially two pawns could be candidates to be captured:
+ // one on our left, and one on our right.
+ move.end.y
+ ];
+ }
+ return undefined; //default
+ }
+
+ static IsGoodEnpassant(enpassant) {
+ if (enpassant != "-") {
+ const epParts = enpassant.split(",");
+ const epSq = V.SquareToCoords(epParts[0]);
+ if (isNaN(epSq.x) || isNaN(epSq.y) || !V.OnBoard(epSq)) return false;
+ const arrCol = V.ColumnToCoord(epParts[1]);
+ if (isNaN(arrCol) || arrCol < 0 || arrCol >= V.size.y) return false;
+ }
+ return true;
+ }
+
+ getEnpassantFen() {
+ const L = this.epSquares.length;
+ if (!this.epSquares[L - 1]) return "-"; //no en-passant
+ return (
+ V.CoordsToSquare(this.epSquares[L - 1][0]) +
+ "," +
+ V.CoordToColumn(this.epSquares[L - 1][1])
+ );
+ }
+
+ getPotentialMovesFrom([x, y], noPostprocess) {
+ const L = this.lastMoveEnd.length;
+ if (
+ !!this.lastMoveEnd[L-1] &&
+ (
+ x != this.lastMoveEnd[L-1].x ||
+ y != this.lastMoveEnd[L-1].y
+ )
+ ) {
+ // A capture must continue: wrong square
+ return [];
+ }
+ let moves = [];
+ switch (this.getPiece(x, y)) {
+ case V.PAWN:
+ moves = this.getPotentialPawnMoves([x, y]);
+ break;
+ case V.ROOK:
+ moves = this.getPotentialRookMoves([x, y]);
+ break;
+ case V.KNIGHT:
+ moves = this.getPotentialKnightMoves([x, y]);
+ break;
+ case V.BISHOP:
+ moves = this.getPotentialBishopMoves([x, y]);
+ break;
+ case V.KING:
+ moves = this.getPotentialKingMoves([x, y]);
+ break;
+ // No queens
+ }
+ if (!noPostprocess) {
+ // Post-process: if capture,
+ // can another capture be achieved with the same piece?
+ moves.forEach(m => {
+ if (m.vanish.length >= 2 || m.appear.length == 0) {
+ this.play(m);
+ const moreCaptures = (
+ V.KeepCaptures(
+ this.getPotentialMovesFrom([m.end.x, m.end.y], "noPostprocess")
+ )
+ .length > 0
+ );
+ this.undo(m);
+ if (!moreCaptures) m.last = true;
+ }
+ else m.last = true;
+ });
+ }
+ return moves;
+ }
+
+ // Special pawns movements
+ getPotentialPawnMoves([x, y]) {
+ const color = this.turn;
+ const oppCol = V.GetOppCol(color);
+ let moves = [];
+ const [sizeX, sizeY] = [V.size.x, V.size.y];
+ const shiftX = color == "w" ? -1 : 1;
+ const startRank = color == "w" ? sizeX - 2 : 1;
+ const potentialFinalPieces =
+ [V.ROOK, V.KNIGHT, V.BISHOP].filter(p => this.captured[color][p] > 0);
+ const lastRanks = (color == "w" ? [0, 1] : [sizeX - 1, sizeX - 2]);
+ if (x + shiftX == lastRanks[0] && potentialFinalPieces.length == 0)
+ // If no captured piece is available, the pawn cannot promote
+ return [];
+
+ const finalPieces1 =
+ x + shiftX == lastRanks[0]
+ ? potentialFinalPieces
+ :
+ x + shiftX == lastRanks[1]
+ ? potentialFinalPieces.concat([V.PAWN])
+ : [V.PAWN];
+ // One square diagonally
+ for (let shiftY of [-1, 1]) {
+ if (this.board[x + shiftX][y + shiftY] == V.EMPTY) {
+ for (let piece of finalPieces1) {
+ moves.push(
+ this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
+ c: color,
+ p: piece
+ })
+ );
+ }
+ if (
+ V.PawnSpecs.twoSquares &&
+ 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
+ const finalPieces2 =
+ x + 2 * shiftX == lastRanks[0]
+ ? potentialFinalPieces
+ :
+ x + 2 * shiftX == lastRanks[1]
+ ? potentialFinalPieces.concat([V.PAWN])
+ : [V.PAWN];
+ if (
+ this.board[x + shiftX][y] != V.EMPTY &&
+ this.canTake([x, y], [x + shiftX, y]) &&
+ V.OnBoard(x + 2 * shiftX, y) &&
+ this.board[x + 2 * shiftX][y] == V.EMPTY
+ ) {
+ const oppPiece = this.getPiece(x + shiftX, y);
+ for (let piece of finalPieces2) {
+ let mv = this.getBasicMove(
+ [x, y], [x + 2 * shiftX, y], { c: color, p: piece });
+ mv.vanish.push({
+ x: x + shiftX,
+ y: y,
+ p: oppPiece,
+ c: oppCol
+ });
+ moves.push(mv);
+ }
+ }
+
+ // En passant
+ const Lep = this.epSquares.length;
+ const epSquare = this.epSquares[Lep - 1]; //always at least one element
+ if (
+ !!epSquare &&
+ epSquare[0].x == x + shiftX &&
+ epSquare[0].y == y &&
+ this.board[x + 2 * shiftX][y] == V.EMPTY
+ ) {
+ for (let piece of finalPieces2) {
+ let enpassantMove =
+ this.getBasicMove(
+ [x, y], [x + 2 * shiftX, y], { c: color, p: piece});
+ enpassantMove.vanish.push({
+ x: x,
+ y: epSquare[1],
+ p: "p",
+ c: this.getColor(x, epSquare[1])
+ });
+ moves.push(enpassantMove);
+ }
+ }
+
+ // Add custodian captures:
+ const steps = V.steps[V.ROOK];
+ moves.forEach(m => {
+ // Try capturing in every direction
+ for (let step of steps) {
+ const sq2 = [m.end.x + 2 * step[0], m.end.y + 2 * step[1]];
+ if (
+ V.OnBoard(sq2[0], sq2[1]) &&
+ this.board[sq2[0]][sq2[1]] != V.EMPTY &&
+ this.getColor(sq2[0], sq2[1]) == color
+ ) {
+ // Potential capture
+ const sq1 = [m.end.x + step[0], m.end.y + step[1]];
+ if (
+ this.board[sq1[0]][sq1[1]] != V.EMPTY &&
+ this.getColor(sq1[0], sq1[1]) == oppCol
+ ) {
+ m.vanish.push(
+ new PiPo({
+ x: sq1[0],
+ y: sq1[1],
+ c: oppCol,
+ p: this.getPiece(sq1[0], sq1[1])
+ })
+ );
+ }
+ }
+ }
+ });
+
+ return moves;
+ }
+
+ getSlides([x, y], steps, options) {
+ options = options || {};
+ // No captures:
+ let moves = [];
+ outerLoop: for (let step of steps) {
+ let i = x + step[0];
+ let j = y + step[1];
+ let counter = 1;
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ if (!options["doubleStep"] || counter % 2 == 0)
+ moves.push(this.getBasicMove([x, y], [i, j]));
+ if (!!options["oneStep"]) continue outerLoop;
+ i += step[0];
+ j += step[1];
+ counter++;
+ }
+ }
+ return moves;
+ }
+
+ // Smasher
+ getPotentialRookMoves([x, y]) {
+ let moves =
+ this.getSlides([x, y], V.steps[V.ROOK], { doubleStep: true })
+ .concat(this.getSlides([x, y], V.steps[V.BISHOP]));
+ // Add captures
+ const oppCol = V.GetOppCol(this.turn);
+ moves.forEach(m => {
+ const delta = [m.end.x - m.start.x, m.end.y - m.start.y];
+ const step = [
+ delta[0] / Math.abs(delta[0]) || 0,
+ delta[1] / Math.abs(delta[1]) || 0
+ ];
+ if (step[0] == 0 || step[1] == 0) {
+ // Rook-like move, candidate for capturing
+ const [i, j] = [m.end.x + step[0], m.end.y + step[1]];
+ if (
+ V.OnBoard(i, j) &&
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) == oppCol
+ ) {
+ m.vanish.push({
+ x: i,
+ y: j,
+ p: this.getPiece(i, j),
+ c: oppCol
+ });
+ }
+ }
+ });
+ return moves;
+ }
+
+ // Leaper
+ getPotentialKnightMoves([x, y]) {
+ let moves =
+ this.getSlides([x, y], V.steps[V.ROOK], { doubleStep: true })
+ .concat(this.getSlides([x, y], V.steps[V.BISHOP]));
+ const oppCol = V.GetOppCol(this.turn);
+ // Look for double-knight moves (could capture):
+ for (let step of V.steps[V.KNIGHT]) {
+ const [i, j] = [x + 2 * step[0], y + 2 * step[1]];
+ if (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ const [ii, jj] = [x + step[0], y + step[1]];
+ if (this.board[ii][jj] == V.EMPTY || this.getColor(ii, jj) == oppCol) {
+ let mv = this.getBasicMove([x, y], [i, j]);
+ if (this.board[ii][jj] != V.EMPTY) {
+ mv.vanish.push({
+ x: ii,
+ y: jj,
+ c: oppCol,
+ p: this.getPiece(ii, jj)
+ });
+ }
+ moves.push(mv);
+ }
+ }
+ }
+ // Look for an enemy in every orthogonal direction
+ for (let step of V.steps[V.ROOK]) {
+ let [i, j] = [x + step[0], y+ step[1]];
+ let counter = 1;
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ i += step[0];
+ j += step[1];
+ counter++;
+ }
+ if (
+ V.OnBoard(i, j) &&
+ counter % 2 == 1 &&
+ this.getColor(i, j) == oppCol
+ ) {
+ const oppPiece = this.getPiece(i, j);
+ // Candidate for capture: can I land after?
+ let [ii, jj] = [i + step[0], j + step[1]];
+ counter++;
+ while (V.OnBoard(ii, jj) && this.board[ii][jj] == V.EMPTY) {
+ if (counter % 2 == 0) {
+ // Same color: add capture
+ let mv = this.getBasicMove([x, y], [ii, jj]);
+ mv.vanish.push({
+ x: i,
+ y: j,
+ c: oppCol,
+ p: oppPiece
+ });
+ moves.push(mv);
+ }
+ ii += step[0];
+ jj += step[1];
+ counter++;
+ }
+ }
+ }
+ return moves;
+ }
+
+ // Remover
+ getPotentialBishopMoves([x, y]) {
+ let moves = this.getSlides([x, y], V.steps[V.BISHOP]);
+ // Add captures
+ const oppCol = V.GetOppCol(this.turn);
+ let captures = [];
+ for (let step of V.steps[V.ROOK]) {
+ const [i, j] = [x + step[0], y + step[1]];
+ if (
+ V.OnBoard(i, j) &&
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) == oppCol
+ ) {
+ captures.push([i, j]);
+ }
+ }
+ captures.forEach(c => {
+ moves.push({
+ start: { x: x, y: y },
+ end: { x: c[0], y: c[1] },
+ appear: [],
+ vanish: captures.map(ct => {
+ return {
+ x: ct[0],
+ y: ct[1],
+ c: oppCol,
+ p: this.getPiece(ct[0], ct[1])
+ };
+ })
+ });
+ });
+ return moves;
+ }
+
+ getPotentialKingMoves([x, y]) {
+ let moves = this.getSlides([x, y], V.steps[V.BISHOP], { oneStep: true });
+ // Add captures
+ const oppCol = V.GetOppCol(this.turn);
+ for (let step of V.steps[V.ROOK]) {
+ const [i, j] = [x + 2 * step[0], y + 2 * step[1]];
+ if (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ const [ii, jj] = [x + step[0], y + step[1]];
+ if (this.board[ii][jj] != V.EMPTY && this.getColor(ii, jj) == oppCol) {
+ let mv = this.getBasicMove([x, y], [i, j]);
+ mv.vanish.push({
+ x: ii,
+ y: jj,
+ c: oppCol,
+ p: this.getPiece(ii, jj)
+ });
+ moves.push(mv);
+ }
+ }
+ }
+ return moves;
+ }
+
+ getPossibleMovesFrom(sq) {
+ const L = this.lastMoveEnd.length;
+ if (
+ !!this.lastMoveEnd[L-1] &&
+ (
+ sq[0] != this.lastMoveEnd[L-1].x ||
+ sq[1] != this.lastMoveEnd[L-1].y
+ )
+ ) {
+ return [];
+ }
+ let moves = this.getPotentialMovesFrom(sq);
+ const captureMoves = V.KeepCaptures(moves);
+ if (captureMoves.length > 0) return captureMoves;
+ if (this.atLeastOneCapture()) return [];
+ return moves;
+ }
+
+ getAllValidMoves() {
+ const moves = this.getAllPotentialMoves();
+ const captures = V.KeepCaptures(moves);
+ if (captures.length > 0) return captures;
+ return moves;
+ }
+
+ filterValid(moves) {
+ // No checks
+ return moves;
+ }
+
+ play(move) {
+ this.epSquares.push(this.getEpSquare(move));
+ V.PlayOnBoard(this.board, move);
+ if (move.vanish.length >= 2) {
+ // Capture: update this.captured
+ for (let i=1; i<move.vanish.length; i++)
+ this.captured[move.vanish[i].c][move.vanish[i].p]++;
+ }
+ if (!!move.last) {
+ // No capture, or no more capture available
+ this.turn = V.GetOppCol(this.turn);
+ this.movesCount++;
+ this.lastMoveEnd.push(null);
+ }
+ else this.lastMoveEnd.push(move.end);
+ }
+
+ undo(move) {
+ this.epSquares.pop();
+ this.lastMoveEnd.pop();
+ V.UndoOnBoard(this.board, move);
+ if (move.vanish.length >= 2) {
+ for (let i=1; i<move.vanish.length; i++)
+ this.captured[move.vanish[i].c][move.vanish[i].p]--;
+ }
+ if (!!move.last) {
+ this.turn = V.GetOppCol(this.turn);
+ this.movesCount--;
+ }
+ }
+
+ getCheckSquares() {
+ return [];
+ }
+
+ getCurrentScore() {
+ // Count kings: if one is missing, the side lost
+ let kingsCount = { 'w': 0, 'b': 0 };
+ for (let i=0; i<8; i++) {
+ for (let j=0; j<8; j++) {
+ if (this.board[i][j] != V.EMPTY && this.getPiece(i, j) == V.KING)
+ kingsCount[this.getColor(i, j)]++;
+ }
+ }
+ if (kingsCount['w'] < 2) return "0-1";
+ if (kingsCount['b'] < 2) return "1-0";
+ return "*";
+ }
+
+ getComputerMove() {
+ let moves = this.getAllValidMoves();
+ if (moves.length == 0) return null;
+ // Just play random moves (for now at least. TODO?)
+ let mvArray = [];
+ while (moves.length > 0) {
+ const mv = moves[randInt(moves.length)];
+ mvArray.push(mv);
+ if (!mv.last) {
+ this.play(mv);
+ moves = V.KeepCaptures(
+ this.getPotentialMovesFrom([mv.end.x, mv.end.y]));
+ }
+ else break;
+ }
+ for (let i = mvArray.length - 2; i >= 0; i--) this.undo(mvArray[i]);
+ return (mvArray.length > 1 ? mvArray : mvArray[0]);
+ }
+
+ getNotation(move) {
+ const initialSquare = V.CoordsToSquare(move.start);
+ const finalSquare = V.CoordsToSquare(move.end);
+ if (move.appear.length == 0)
+ // Remover captures 'R'
+ return initialSquare + "R";
+ let notation = move.appear[0].p.toUpperCase() + finalSquare;
+ // Add a capture mark (not describing what is captured...):
+ if (move.vanish.length >= 2) notation += "X";
+ return notation;
+ }
+};
--- /dev/null
+import { ChessRules } from "@/base_rules";
+
+export class TakenmakeRules extends ChessRules {
+ setOtherVariables(fen) {
+ super.setOtherVariables(fen);
+ // Stack of "last move" only for intermediate captures
+ this.lastMoveEnd = [null];
+ }
+
+ getPotentialMovesFrom([x, y], asA) {
+ const L = this.lastMoveEnd.length;
+ if (!asA && !!this.lastMoveEnd[L-1]) {
+ asA = this.lastMoveEnd[L-1].p;
+ if (x != this.lastMoveEnd[L-1].x || y != this.lastMoveEnd[L-1].y) {
+ // A capture was played: wrong square
+ return [];
+ }
+ }
+ let moves = [];
+ const piece = this.getPiece(x, y);
+ switch (asA || piece) {
+ case V.PAWN:
+ if (!asA || piece == V.PAWN)
+ moves = this.getPotentialPawnMoves([x, y]);
+ else {
+ // Special case: we don't want promotion, since just moving like
+ // a pawn, but I'm in fact not a pawn :)
+ const shiftX = (this.turn == 'w' ? -1 : 1);
+ if (this.board[x + shiftX][y] == V.EMPTY)
+ moves = [this.getBasicMove([x, y], [x + shiftX, y])];
+ }
+ break;
+ case V.ROOK:
+ moves = this.getPotentialRookMoves([x, y]);
+ break;
+ case V.KNIGHT:
+ moves = this.getPotentialKnightMoves([x, y]);
+ break;
+ case V.BISHOP:
+ moves = this.getPotentialBishopMoves([x, y]);
+ break;
+ case V.KING:
+ moves = this.getPotentialKingMoves([x, y]);
+ break;
+ case V.QUEEN:
+ moves = this.getPotentialQueenMoves([x, y]);
+ break;
+ }
+ // Post-process: if capture,
+ // can a move "as-capturer" be achieved with the same piece?
+ if (!asA) {
+ const color = this.turn;
+ return moves.filter(m => {
+ if (m.vanish.length == 2 && m.appear.length == 1) {
+ this.play(m);
+ let moveOk = true;
+ const makeMoves =
+ this.getPotentialMovesFrom([m.end.x, m.end.y], m.vanish[1].p);
+ if (
+ makeMoves.every(mm => {
+ // Cannot castle after a capturing move
+ // (with the capturing piece):
+ if (mm.vanish.length == 2) return true;
+ this.play(mm);
+ const res = this.underCheck(color);
+ this.undo(mm);
+ return res;
+ })
+ ) {
+ moveOk = false;
+ }
+ this.undo(m);
+ return moveOk;
+ }
+ return true;
+ });
+ }
+ // Moving "as a": filter out captures (no castles here)
+ return moves.filter(m => m.vanish.length == 1);
+ }
+
+ getPossibleMovesFrom(sq) {
+ const L = this.lastMoveEnd.length;
+ let asA = undefined;
+ if (!!this.lastMoveEnd[L-1]) {
+ if (
+ sq[0] != this.lastMoveEnd[L-1].x ||
+ sq[1] != this.lastMoveEnd[L-1].y
+ ) {
+ return [];
+ }
+ asA = this.lastMoveEnd[L-1].p;
+ }
+ return this.filterValid(this.getPotentialMovesFrom(sq, asA));
+ }
+
+ filterValid(moves) {
+ let noCaptureMoves = [];
+ let captureMoves = [];
+ moves.forEach(m => {
+ if (m.vanish.length == 1 || m.appear.length == 2) noCaptureMoves.push(m);
+ else captureMoves.push(m);
+ });
+ // Capturing moves were already checked in getPotentialMovesFrom()
+ return super.filterValid(noCaptureMoves).concat(captureMoves);
+ }
+
+ play(move) {
+ move.flags = JSON.stringify(this.aggregateFlags());
+ this.epSquares.push(this.getEpSquare(move));
+ V.PlayOnBoard(this.board, move);
+ if (move.vanish.length == 1 || move.appear.length == 2) {
+ // Not a capture: change turn
+ this.turn = V.GetOppCol(this.turn);
+ this.movesCount++;
+ this.lastMoveEnd.push(null);
+ }
+ else {
+ this.lastMoveEnd.push(
+ Object.assign({}, move.end, { p: move.vanish[1].p })
+ );
+ }
+ this.postPlay(move);
+ }
+
+ postPlay(move) {
+ const c = move.vanish[0].c;
+ const piece = move.vanish[0].p;
+ 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.updateCastleFlags(move, piece);
+ }
+
+ undo(move) {
+ this.disaggregateFlags(JSON.parse(move.flags));
+ this.epSquares.pop();
+ this.lastMoveEnd.pop();
+ V.UndoOnBoard(this.board, move);
+ if (move.vanish.length == 1 || move.appear.length == 2) {
+ this.turn = V.GetOppCol(this.turn);
+ this.movesCount--;
+ }
+ super.postUndo(move);
+ }
+};