--- /dev/null
+<?xml version="1.0" encoding="iso-8859-1"?>\r
+<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\r
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\r
+ viewBox="0 0 198 198" style="enable-background:new 0 0 198 198;" xml:space="preserve">\r
+<path d="M192.384,99.825c-9.361-22.131-28.78-38.577-51.252-46.075c5.413-0.686,10.891-0.927,16.379-0.688\r
+ c9.065,0.395,17.464,2.395,25.745,5.045c5.602,1.793,9.756-5.12,5.588-9.271c-6.584-6.556-14.508-11.75-23.228-15.286\r
+ c-22.268-9.029-47.629-6.927-68.82,3.66c3.342-4.313,7.046-8.356,11.095-12.067c6.689-6.13,14.043-10.656,21.772-14.637\r
+ c5.229-2.694,3.278-10.519-2.604-10.507c-9.292,0.02-18.568,1.95-27.234,5.616C77.695,14.977,61.248,34.396,53.75,56.868\r
+ c-0.686-5.413-0.927-10.891-0.687-16.379c0.395-9.065,2.395-17.464,5.045-25.745c1.793-5.602-5.12-9.756-9.271-5.588\r
+ c-6.556,6.584-11.75,14.508-15.286,23.228c-9.029,22.268-6.927,47.629,3.66,68.82c-4.313-3.342-8.356-7.046-12.067-11.095\r
+ c-6.13-6.689-10.656-14.042-14.637-21.772C7.813,63.107-0.012,65.059,0,70.941c0.02,9.292,1.95,18.568,5.616,27.234\r
+ c9.361,22.131,28.78,38.577,51.251,46.075c-5.413,0.686-10.891,0.927-16.378,0.687c-9.065-0.395-17.464-2.395-25.745-5.045\r
+ c-5.602-1.793-9.756,5.12-5.588,9.271c6.584,6.556,14.508,11.75,23.228,15.286c22.268,9.029,47.629,6.927,68.82-3.66\r
+ c-3.342,4.313-7.046,8.356-11.095,12.067c-6.689,6.13-14.042,10.656-21.772,14.637c-5.229,2.694-3.278,10.519,2.604,10.507\r
+ c9.292-0.02,18.568-1.95,27.234-5.616c22.131-9.361,38.577-28.781,46.075-51.252c0.686,5.413,0.927,10.891,0.687,16.379\r
+ c-0.395,9.065-2.395,17.464-5.045,25.745c-1.793,5.602,5.12,9.756,9.271,5.588c6.556-6.584,11.75-14.509,15.286-23.228\r
+ c9.029-22.268,6.927-47.629-3.66-68.82c4.313,3.342,8.356,7.046,12.067,11.095c6.13,6.689,10.656,14.042,14.637,21.772\r
+ c2.694,5.229,10.519,3.278,10.507-2.604C197.98,117.767,196.05,108.491,192.384,99.825z M99,122.917\r
+ c-13.209,0-23.917-10.708-23.917-23.917S85.791,75.083,99,75.083S122.917,85.791,122.917,99S112.209,122.917,99,122.917z"/>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+<g>\r
+</g>\r
+</svg>\r
let j = y + step[1];
while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
moves.push(this.getBasicMove([x, y], [i, j]));
- if (oneStep !== undefined) continue outerLoop;
+ if (oneStep) continue outerLoop;
i += step[0];
j += step[1];
}
// (for engine and game end)
getAllValidMoves() {
const color = this.turn;
- const oppCol = V.GetOppCol(color);
let potentialMoves = [];
for (let i = 0; i < V.size.x; i++) {
for (let j = 0; j < V.size.y; j++) {
- // Next condition "!= oppCol" to work with checkered variant
- if (this.board[i][j] != V.EMPTY && this.getColor(i, j) != oppCol) {
+ if (this.getColor(i, j) == color) {
Array.prototype.push.apply(
potentialMoves,
this.getPotentialMovesFrom([i, j])
// Stop at the first move found
atLeastOneMove() {
const color = this.turn;
- const oppCol = V.GetOppCol(color);
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) != oppCol) {
+ if (this.getColor(i, j) == color) {
const moves = this.getPotentialMovesFrom([i, j]);
if (moves.length > 0) {
for (let k = 0; k < moves.length; k++) {
);
}
+ // Generic method for non-pawn pieces ("sliding or jumping"):
+ // is x,y attacked by a piece of color in array 'colors' ?
+ isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) {
+ for (let step of steps) {
+ let rx = x + step[0],
+ ry = y + step[1];
+ while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) {
+ rx += step[0];
+ ry += step[1];
+ }
+ if (
+ V.OnBoard(rx, ry) &&
+ this.getPiece(rx, ry) === piece &&
+ colors.includes(this.getColor(rx, ry))
+ ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
// Is square x,y attacked by 'colors' pawns ?
isAttackedByPawn([x, y], colors) {
for (let c of colors) {
- let pawnShift = c == "w" ? 1 : -1;
+ const pawnShift = c == "w" ? 1 : -1;
if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
for (let i of [-1, 1]) {
if (
);
}
- // Generic method for non-pawn pieces ("sliding or jumping"):
- // is x,y attacked by a piece of color in array 'colors' ?
- isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) {
- for (let step of steps) {
- let rx = x + step[0],
- ry = y + step[1];
- while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) {
- rx += step[0];
- ry += step[1];
- }
- if (
- V.OnBoard(rx, ry) &&
- this.getPiece(rx, ry) === piece &&
- colors.includes(this.getColor(rx, ry))
- ) {
- return true;
- }
- }
- return false;
- }
-
// Is color under check after his move ?
underCheck(color) {
return this.isAttacked(this.kingPos[color], [V.GetOppCol(color)]);
-p TODO
+p.boxed
+ | Either enter the arena, or capture something inside.
+ | The arena is the 4x8 rectangle in the middle of the board.
+
+ul
+ li.
+ In addition to its usual movement, a pawn can capture diagonally backward.
+ li.
+ The king and queen are replaced with dukes.
+ A duke may move up to 3 spaces in any direction.
+ Dukes may move into check as they please,
+ but if a player loses both of his dukes, he loses.
+
+p.
+ The 4x8 area in the center of the board (the area in which no pieces start)
+ is known as the Arena. A piece that is not currently in the Arena may only move
+ if it is to enter the Arena. A piece which is currently in the Arena may
+ only move to capture something in the Arena.
+
+p A player wins if:
+ul
+ li He captures both of his opponent's Dukes.
+ li.
+ His opponent has no pieces left in the Arena at the end of any turn
+ other than the first.
+ li His opponent cannot make a move.
+
+figure.diagram-container
+ .diagram
+ | fen:brnkqrnb/pppppppp/8/8/8/8/PPPPPPPP/QNRBBKNR:
+ figcaption After the moves 1.e4 Nd6 2.Bg4 Nxe4.
+
+p.
+ In the diagram situation, the g4 bishop cannot take anything in the Arena,
+ and thus cannot move. Note that 1...d5?? 2.exd5 1-0, because black would have
+ no pieces in the Arena.
+
+h3 Source
+
+p
+ a(href="https://www.chessvariants.com/32turn.dir/arenachess.html") Arena chess
+ | on chessvariants.com.
-p TODO
+p.boxed
+ | Entra en la arena o captura algo.
+ | La "arena" designa el rectángulo de 4x8 en el centro del tablero.
+
+ul
+ li.
+ Además de su moviéndose habitual,
+ el peón puede capturar en diagonal hacia atrás.
+ li.
+ El rey y la reina son reemplazados por duques.
+ Un duque puede mover uno, dos o tres casillas en todas las direcciones.
+ Puede estar en jaque en cualquier momento, pero perder ambos
+ duques significa perder la partida.
+
+p.
+ El área de tamaño 4x8 en el centro del tablero de ajedrez (inicialmente vacía)
+ se llama la "arena". Una pieza fuera de la arena solo puede realizar
+ un movimiento entrando en él.
+ Una pieza que ya está en la arena solo puede moverse capturando algo en esta área.
+
+p Un jugador gana si:
+ul
+ li Captura a los dos duques opuestos.
+ li.
+ Su oponente no tiene más piezas en la arena después de
+ cualquier turno excepto el primero.
+ li Su oponente ya no puede jugar.
+
+figure.diagram-container
+ .diagram
+ | fen:brnkqrnb/pppppppp/8/8/8/8/PPPPPPPP/QNRBBKNR:
+ figcaption Después de los movimientos 1.e4 Nd6 2.Bg4 Nxe4.
+
+p.
+ En la situación del diagrama, el alfil en g4 no puede capturar nada en la arena,
+ y por lo tanto no puede moverse. Tenga en cuenta que 1...d5?? 2.exd5 1-0,
+ porque los negros no tendría piezas en la arena.
+
+h3 Fuente
+
+p
+ | La
+ a(href="https://www.chessvariants.com/32turn.dir/arenachess.html") variante Arena
+ | en chessvariants.com.
-p TODO
+p.boxed
+ | Entrez dans l'arène, ou capturez-y quelque chose.
+ | L'"arène" désigne le rectangle 4x8 au centre de l'échiquier.
+
+ul
+ li.
+ En plus de son déplacement habituel,
+ le pion peut capturer en diagonale vers arrière.
+ li.
+ Le roi et la reine sont remplacés par des ducs.
+ Un duc peut se déplacer d'une, deux ou trois cases dans toutes les directions.
+ Il peut se retrouver en échec à tout moment, mais perdre ses deux
+ ducs signifie perdre la partie.
+
+p.
+ La zone de taille 4x8 au centre de l'échiquier (initialement vide)
+ s'appelle l'"arène". Une pièce hors de l'arène peut seulement effectuer
+ un coup y entrant.
+ Une pièce déjà dans l'arène ne peut se déplacer qu'en capturant
+ quelque chose dans cette zone.
+
+p Un joueur gagne si :
+ul
+ li Il capture les deux ducs adverses.
+ li.
+ Son adversaire n'a plus de pièces dans l'arène à l'issue de
+ n'importe quel tour excepté le premier.
+ li Son adversaire ne peut plus jouer.
+
+figure.diagram-container
+ .diagram
+ | fen:brnkqrnb/pppppppp/8/8/8/8/PPPPPPPP/QNRBBKNR:
+ figcaption Après les coups 1.e4 Nd6 2.Bg4 Nxe4.
+
+p.
+ Dans la situation du diagramme, le fou en g4 ne peut rien capturer dans l'arène,
+ et donc ne peut pas bouger. Notez que 1...d5?? 2.exd5 1-0, car les noirs
+ n'auraient aucune pièce dans l'arène.
+
+h3 Source
+
+p
+ | La
+ a(href="https://www.chessvariants.com/32turn.dir/arenachess.html") variante Arena
+ | sur chessvariants.com.
-p TODO
+p.boxed
+ | Win by giving three checks (or a single checkmate).
+
+p.
+ Orthodox rules apply, but giving three checks win the game.
+ Since it is easier than checkmate, most games would end in this way.
+
+h3 Source
+
+p
+ | Old variant, playable at several locations including
+ a(href="https://lichess.org/variant/threeCheck") lichess
+ | .
-p TODO
+p.boxed
+ | Gana dando tres jaques (o un solo jaque mate).
+
+p.
+ Se aplican las reglas ortodoxas, pero dar tres jaques gana
+ la partida. Es más fácil que de matar, entonces la mayoría de las
+ partidas deberían terminar así.
+
+h3 Fuente
+
+p
+ | Es una variante antigua, jugable en varios lugares, incluyendo
+ a(href="https://lichess.org/variant/threeCheck") lichess
+ | .
-p TODO
+p.boxed
+ | Gagnez en donnant trois échecs (ou un seul échec et mat).
+
+p.
+ Les règles orthodoxes s'appliquent, mais donner trois échecs fait gagner
+ la partie. C'est plus facile que de mater,
+ donc la plupart des parties devraient s'achever ainsi.
+
+h3 Source
+
+p
+ | C'est une vieille variante, jouable à plusieurs endroits dont
+ a(href="https://lichess.org/variant/threeCheck") lichess
+ | .
-p TODO
+p.boxed
+ | Any piece guarded by a friendly knight can also move like a knight.
+
+p.
+ In addition to its normal abilities, a piece guarded by a knight can move like him.
+ On the following diagram, 1.Nf4 would checkmate because it guard the g6 queen.
+ If it was black to play, then 1...Rxe2 is possible due to the c8 knight.
+
+figure.diagram-container
+ .diagram
+ | fen:7k/8/6Q1/1n6/8/2r5/4N3/K7:
+
+h3 Source
+
+p
+ a(href="TODO") TODO
-p TODO
+p.boxed
+ | When a piece moves, the initial square disappears. It creates a
+ a(href="https://en.wikipedia.org/wiki/Wormhole") "wormhole"
+ | .
+
+p.
+ Since all initial squares vanish, the board has exactly 64 - T squares
+ after T turns, so the game cannot last more than 32 moves.
+ Indeed a vanished square can be jumped over, but cannot be used again.
+ Holes are indicated with the letter 'x' on FEN strings.
+
+p.
+ In the diagram situation, the black knight can go to all the marked squares:
+ g5 and f6 are reachable because of the holes on f4 and e5.
+ Indeed the knight first moves one square vertically or horizontally,
+ and only then one square diagonally "in the same direction".
+ This is the only valid description in this variant
+ (others would lead to different knight movements around holes).
+ The black king can go to c6:
+ it moves to the closest non-vanished square (if any).
+
+figure.diagram-container
+ .diagram
+ | fen:rbkxxxbn/ppxppppx/2qxxB2/4x2p/3P1x2/3n1x2/PPPxPPPP/RBxxxNKR b2,f2,b4,c5,g5,f6:
+ figcaption Possible moves for the knight on d3.
+
+p.
+ No castle or en passant captures are possible.
+ Promotion is permitted but only by capturing.
+
+h3 Pieces movements
+
+ul
+ li The rook moves one or two squares vertically or horizontally.
+ li The bishop moves one or two squares diagonally.
+ li The queen moves either like a rook or like a bishop.
+ li.
+ The pawn moves like in orthodox chess, but can jumped over pieces at its
+ initial potential 2-squares move.
+
+h3 End of the game
+
+p Win by checkmate or stalemate: if you can no longer move, you lose.
+
+h3 Source
+
+p
+ a(href="https://www.chessvariants.com/32turn.dir/wormhole.html") Wormhole chess
+ | on chessvariants.com.
+ | I changed the pieces movements because I have a better feeling with the moves
+ | described earlier. It might evolve.
-p TODO
+p.boxed
+ | Cuando una pieza se mueve, la casilla inicial desaparece. Esto crea un
+ a(href="https://es.wikipedia.org/wiki/Agujero_de_gusano") "agujero de gusano"
+ | .
+
+p.
+ Como todas la casillas iniciales desaparecen, el tablero de ajedrez tiene exactamente
+ 64 - T casillas después de T turnos. La partida no puede exceder los 32 movimientos.
+ De hecho, puedes saltar sobre una casilla faltante, pero ya no se puede usar.
+ Los agujeros se indican con la letra 'x' en la cadena FEN.
+
+p.
+ En la situación del diagrama, el caballo negro puede ir a las casillas marcadas:
+ g5 y f6 son accesibles gracias a los agujeros en f4 y e5.
+ De hecho, el caballo primero se mueve de una casilla horizontalmente o
+ verticalmente, luego diagonalmente "en la misma dirección".
+ Esta es la única descripción válida en esta variante (las otras
+ conduciría a diferentes movimientos de caballos alrededor de los agujeros).
+ El rey negro puede ir a c6: se mueve hacia la casilla no desaparecida
+ más cercano (si lo hay).
+
+figure.diagram de contenedores
+ .diagram
+ | fen: rbkxxxbn / ppxppppx / 2qxxB2 / 4x2p / 3P1x2 / 3n1x2 / PPPxPPPP / RBxxxNKR b2, f2, b4, c5, g5, f6:
+ figcaption Posibles movimientos para el caballo en d3.
+
+p.
+ El enroque y la captura en passant no están permitidos.
+ La promoción es posible pero solo mediante la captura.
+
+h3 ¿Como las piezas se mueven?
+
+ul
+ li La torre se mueve de una o dos casillas verticalmente u horizontalmente.
+ li El alfil se mueve de una o dos casillas en diagonal.
+ li La dama puede moverse como una torre o un alfil.
+ li.
+ El peón se mueve como en el ajedrez ortodoxo, y también puede saltar
+ por encima de una pieza durante su potencial doble movimiento inicial.
+
+h3 Fin de la partida
+
+p Gana por mate o empate: si ya no puedes jugar, pierdes.
+
+h3 Fuente
+
+ | La
+ a(href="https://www.chessvariants.com/32turn.dir/wormhole.html") variante Wormhole
+ | en chessvariants.com.
+ | Cambié los movimientos de las piezas porque las descritas aquí
+ | Parece más adecuado. Esto podría evolucionar.
-p TODO
+p.boxed
+ | Quand une pièce se déplace, la case initiale disparaît. Cela crée un
+ a(href="https://fr.wikipedia.org/wiki/Trou_de_ver") "trou de ver"
+ | .
+
+p.
+ Puisque toutes les cases initiales disparaissent, l'échiquier a exactement
+ 64 - T cases après T tours. La partie ne peut donc excéder 32 coups.
+ En effet on peut sauter par dessus une case disparue, mais celle-ci ne
+ peut plus être utilisée.
+ Les trous sont indiqués par la lettre 'x' sur les chaînes FEN.
+
+p.
+ Dans la situation du diagramme, le cavalier noir peut aller sur toutes les
+ cases marquées : g5 et f6 sont accessibles grâce aux trous en f4 et e5.
+ En effet le cavalier se déplace d'abord d'une case horizontalement ou
+ verticalement, puis d'une case en diagonale "dans la même direction".
+ Ceci est la seule description valide dans cette variante (les autres
+ mèneraient à différents coups de cavaliers aux abords des trous).
+ Le roi noir peut aller en c6 : il se déplace vers la case non disparue
+ la plus proche (s'il y en a).
+
+figure.diagram-container
+ .diagram
+ | fen:rbkxxxbn/ppxppppx/2qxxB2/4x2p/3P1x2/3n1x2/PPPxPPPP/RBxxxNKR b2,f2,b4,c5,g5,f6:
+ figcaption Coups possibles pour le cavalier en d3.
+
+p.
+ Le roque ainsi que la prise en passant ne sont pas permis.
+ La promotion est possible mais seulement en capturant.
+
+h3 Déplacement des pièces
+
+ul
+ li La tour se déplace d'une ou deux cases verticalement ou horizontalement.
+ li Le fous se déplace d'une ou deux cases en diagonale.
+ li La dame peut se mouvoir comme une tour ou un fou.
+ li.
+ Le pion se déplace comme aux échecs orthodoxes, et peut aussi sauter par
+ dessus une pièce lors de son potentiel double coup initial.
+
+h3 Fin de la partie
+
+p Gagnez par mat ou pat : si vous ne pouvez plus jouer, vous perdez.
+
+h3 Source
+
+p
+ | La
+ a(href="https://www.chessvariants.com/32turn.dir/wormhole.html") variante Wormhole
+ | sur chessvariants.com.
+ | J'ai changé les déplacements des pièces car ceux décrits ici me
+ | paraissent mieux adaptés. Ceci pourrait évoluer.
moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
}
}
- // Captures
+ // Captures: also possible backward
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])
- ) {
- moves.push(this.getBasicMove([x, y], [x + shiftX, y + shiftY]));
+ if (y + shiftY >= 0 && y + shiftY < sizeY) {
+ for (let direction of [-1,1]) {
+ if (
+ this.board[x + direction][y + shiftY] != V.EMPTY &&
+ this.canTake([x, y], [x + direction, y + shiftY])
+ ) {
+ moves.push(this.getBasicMove([x, y], [x + direction, y + shiftY]));
+ }
+ }
}
}
}
getPpath(b) {
- return b[0] == "c" ? "Checkered/" + b : b;
+ return (b[0] == "c" ? "Checkered/" : "") + b;
}
setOtherVariables(fen) {
});
}
+ getAllValidMoves() {
+ const oppCol = V.GetOppCol(this.turn);
+ let potentialMoves = [];
+ for (let i = 0; i < V.size.x; i++) {
+ for (let j = 0; j < V.size.y; j++) {
+ // NOTE: just testing == color isn't enough because of checkred pieces
+ if (this.board[i][j] != V.EMPTY && this.getColor(i, j) != oppCol) {
+ Array.prototype.push.apply(
+ potentialMoves,
+ this.getPotentialMovesFrom([i, j])
+ );
+ }
+ }
+ }
+ return this.filterValid(potentialMoves);
+ }
+
+ atLeastOneMove() {
+ const oppCol = V.GetOppCol(this.turn);
+ for (let i = 0; i < V.size.x; i++) {
+ for (let j = 0; j < V.size.y; j++) {
+ // NOTE: just testing == color isn't enough because of checkred pieces
+ if (this.board[i][j] != V.EMPTY && this.getColor(i, j) != oppCol) {
+ const moves = this.getPotentialMovesFrom([i, j]);
+ if (moves.length > 0) {
+ for (let k = 0; k < moves.length; k++) {
+ if (this.filterValid([moves[k]]).length > 0) return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
isAttackedByPawn([x, y], colors) {
for (let c of colors) {
const color = c == "c" ? this.turn : c;
evalPosition() {
let evaluation = 0;
- //Just count material for now, considering checkered neutral (...)
+ // Just count material for now, considering checkered neutral (...)
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) {
const sqColor = this.getColor(i, j);
- const sign = sqColor == "w" ? 1 : sqColor == "b" ? -1 : 0;
- evaluation += sign * V.VALUES[this.getPiece(i, j)];
+ if (["w","b"].includes(sqColor)) {
+ const sign = sqColor == "w" ? 1 : -1;
+ evaluation += sign * V.VALUES[this.getPiece(i, j)];
+ }
}
}
}
-import { ChessRules, PiPo, Move } from "@/base_rules";
-import { ArrayFun } from "@/utils/array";
-import { randInt } from "@/utils/alea";
-
-// TODO:
-// Short-range pieces:
-// rook 1 or 2 squares orthogonal
-// bishop 1 or 2 diagonal
-// queen = bishop + rook
-// knight: one square orthogonal + 1 diagonal (only acepted desc)
-// no castle or en passant. Promotion possible only by capture (otherwise hole)
+import { ChessRules } from "@/base_rules";
export const VariantRules = class WormholeRules extends ChessRules {
- // TODO: redefine pieces movements, taking care of holes (auxiliary func: getSquareAfter(shiftX,shiftY))
- // this aux func could return null / undefined
- // revoir getPotentialMoves et isAttacked : tout ce qui touche au board avec calcul,
- // car les "board[x+..][y+..]" deviennent des board[getSquareAfter...]
- // Special FEN sign for holes: 'x'
-
static get HasFlags() {
return false;
}
return false;
}
- getSquareAfter(sq, shift) {
- // TODO
+ static get HOLE() {
+ return "xx";
+ }
+
+ static board2fen(b) {
+ if (b[0] == 'x') return 'x';
+ return ChessRules.board2fen(b);
+ }
+
+ static fen2board(f) {
+ if (f == 'x') return V.HOLE;
+ return ChessRules.fen2board(f);
}
getPpath(b) {
- if (b.indexOf('x') >= 0)
- return "Wormhole/hole.svg";
+ if (b[0] == 'x') return "Wormhole/hole";
return b;
}
- // TODO: postUpdateVars: board[start] = "xx"; --> V.HOLE
+ getSquareAfter(square, movement) {
+ let shift1, shift2;
+ if (Array.isArray(movement[0])) {
+ // A knight
+ shift1 = movement[0];
+ shift2 = movement[1];
+ } else {
+ shift1 = movement;
+ shift2 = null;
+ }
+ const tryMove = (init, shift) => {
+ let step = [
+ shift[0] / Math.abs(shift[0]) || 0,
+ shift[1] / Math.abs(shift[1]) || 0,
+ ];
+ const nbSteps = Math.max(Math.abs(shift[0]), Math.abs(shift[1]));
+ let stepsAchieved = 0;
+ let sq = [init[0] + step[0], init[1] + step[1]];
+ while (V.OnBoard(sq[0],sq[1])) {
+ if (this.board[sq[0]][sq[1]] != V.HOLE)
+ stepsAchieved++;
+ if (stepsAchieved < nbSteps) {
+ sq[0] += step[0];
+ sq[1] += step[1];
+ }
+ else break;
+ }
+ if (stepsAchieved < nbSteps)
+ // The move is impossible
+ return null;
+ return sq;
+ };
+ // First, apply shift1
+ let dest = tryMove(square, shift1);
+ if (dest && shift2)
+ // A knight: apply second shift
+ dest = tryMove(dest, shift2);
+ return dest;
+ }
+
+ // NOTE (TODO?): some extra work done in some function because informations
+ // on one step should ease the computation for a step in the same direction.
+ static get steps() {
+ return {
+ r: [
+ [-1, 0],
+ [1, 0],
+ [0, -1],
+ [0, 1],
+ [-2, 0],
+ [2, 0],
+ [0, -2],
+ [0, 2]
+ ],
+ // Decompose knight movements into one step orthogonal + one diagonal
+ n: [
+ [[0, -1], [-1, -1]],
+ [[0, -1], [1, -1]],
+ [[-1, 0], [-1,-1]],
+ [[-1, 0], [-1, 1]],
+ [[0, 1], [-1, 1]],
+ [[0, 1], [1, 1]],
+ [[1, 0], [1, -1]],
+ [[1, 0], [1, 1]]
+ ],
+ b: [
+ [-1, -1],
+ [-1, 1],
+ [1, -1],
+ [1, 1],
+ [-2, -2],
+ [-2, 2],
+ [2, -2],
+ [2, 2]
+ ],
+ k: [
+ [-1, 0],
+ [1, 0],
+ [0, -1],
+ [0, 1],
+ [-1, -1],
+ [-1, 1],
+ [1, -1],
+ [1, 1]
+ ]
+ };
+ }
+
+ getJumpMoves([x, y], steps) {
+ let moves = [];
+ for (let step of steps) {
+ const sq = this.getSquareAfter([x,y], step);
+ if (sq &&
+ (
+ this.board[sq[0]][sq[1]] == V.EMPTY ||
+ this.canTake([x, y], sq)
+ )
+ ) {
+ moves.push(this.getBasicMove([x, y], sq));
+ }
+ }
+ return moves;
+ }
+
+ // What are the pawn moves from square x,y ?
+ getPotentialPawnMoves([x, y]) {
+ const color = this.turn;
+ 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 lastRank = color == "w" ? 0 : sizeX - 1;
+
+ const sq1 = this.getSquareAfter([x,y], [shiftX,0]);
+ if (sq1 && this.board[sq1[0]][y] == V.EMPTY) {
+ // One square forward (cannot be a promotion)
+ moves.push(this.getBasicMove([x, y], [sq1[0], y]));
+ if (x == startRank) {
+ // If two squares after is available, then move is possible
+ const sq2 = this.getSquareAfter([x,y], [2*shiftX,0]);
+ if (sq2 && this.board[sq2[0]][y] == V.EMPTY)
+ // Two squares jump
+ moves.push(this.getBasicMove([x, y], [sq2[0], y]));
+ }
+ }
+ // Captures
+ const finalPieces = x + shiftX == lastRank
+ ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]
+ : [V.PAWN];
+ for (let shiftY of [-1, 1]) {
+ const sq = this.getSquareAfter([x,y], [shiftX,shiftY]);
+ if (
+ sq &&
+ this.board[sq[0]][sq[1]] != V.EMPTY &&
+ this.canTake([x, y], [sq[0], sq[1]])
+ ) {
+ for (let piece of finalPieces) {
+ moves.push(
+ this.getBasicMove([x, y], [sq[0], sq[1]], {
+ c: color,
+ p: piece
+ })
+ );
+ }
+ }
+ }
+
+ return moves;
+ }
+
+ getPotentialRookMoves(sq) {
+ return this.getJumpMoves(sq, V.steps[V.ROOK]);
+ }
+
+ getPotentialKnightMoves(sq) {
+ return this.getJumpMoves(sq, V.steps[V.KNIGHT]);
+ }
+
+ getPotentialBishopMoves(sq) {
+ return this.getJumpMoves(sq, V.steps[V.BISHOP]);
+ }
+
+ getPotentialQueenMoves(sq) {
+ return this.getJumpMoves(
+ sq,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP])
+ );
+ }
+
+ getPotentialKingMoves(sq) {
+ return this.getJumpMoves(sq, V.steps[V.KING]);
+ }
+
+ isAttackedByJump([x, y], colors, piece, steps) {
+ for (let step of steps) {
+ const sq = this.getSquareAfter([x,y], step);
+ if (
+ sq &&
+ this.getPiece(sq[0], sq[1]) === piece &&
+ colors.includes(this.getColor(sq[0], sq[1]))
+ ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ isAttackedByPawn([x, y], colors) {
+ for (let c of colors) {
+ const pawnShift = c == "w" ? 1 : -1;
+ for (let i of [-1, 1]) {
+ const sq = this.getSquareAfter([x,y], [pawnShift,i]);
+ if (
+ sq &&
+ this.getPiece(sq[0], sq[1]) == V.PAWN &&
+ this.getColor(sq[0], sq[1]) == c
+ ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ isAttackedByRook(sq, colors) {
+ return this.isAttackedByJump(sq, colors, V.ROOK, V.steps[V.ROOK]);
+ }
+
+ isAttackedByKnight(sq, colors) {
+ // NOTE: knight attack is not symmetric in this variant:
+ // steps order need to be reversed.
+ return this.isAttackedByJump(
+ sq,
+ colors,
+ V.KNIGHT,
+ V.steps[V.KNIGHT].map(s => s.reverse())
+ );
+ }
+
+ isAttackedByBishop(sq, colors) {
+ return this.isAttackedByJump(sq, colors, V.BISHOP, V.steps[V.BISHOP]);
+ }
+
+ isAttackedByQueen(sq, colors) {
+ return this.isAttackedByJump(
+ sq,
+ colors,
+ V.QUEEN,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP])
+ );
+ }
+
+ isAttackedByKing(sq, colors) {
+ return this.isAttackedByJump(sq, colors, V.KING, V.steps[V.KING]);
+ }
+
+ static PlayOnBoard(board, move) {
+ board[move.vanish[0].x][move.vanish[0].y] = V.HOLE;
+ for (let psq of move.appear) board[psq.x][psq.y] = psq.c + psq.p;
+ }
- updateVariables(move) {
- super.updateVariables(move);
+ getCurrentScore() {
+ if (this.atLeastOneMove())
+ return "*";
+ // No valid move: I lose
+ return this.turn == "w" ? "0-1" : "1-0";
}
- unupdateVariables(move) {
- super.unupdateVariables(move);
+ evalPosition() {
+ let evaluation = 0;
+ for (let i = 0; i < V.size.x; i++) {
+ for (let j = 0; j < V.size.y; j++) {
+ if (![V.EMPTY,V.HOLE].includes(this.board[i][j])) {
+ const sign = this.getColor(i, j) == "w" ? 1 : -1;
+ evaluation += sign * V.VALUES[this.getPiece(i, j)];
+ }
+ }
+ }
+ return evaluation;
}
getNotation(move) {
break;
case "fullgame":
// Callback "roomInit" to poll clients only after game is loaded
- this.loadGame(data.data, this.roomInit);
+ let game = data.data;
+ // Move format isn't the same in storage and in browser,
+ // because of the 'addTime' field.
+ game.moves = game.moves.map(m => { return m.move || m; });
+ this.loadGame(game, this.roomInit);
break;
case "asklastate":
// Sending last state if I played a move or score != "*"