wget -q -O public/images/pieces/Eightpieces/tmp_png/"$color$piece".png https://vchess.club/images/pieces/Eightpieces/tmp_png/"$color$piece".png
done
done
+for color in "w" "b"; do
+ for piece in "a" "c" "s" "t" "u" "v" "j" "l" "m" "o" "r" "n" "b" "q" "k"; do
+ rm -f public/images/pieces/Titan/"$color$piece".png
+ wget -q -O public/images/pieces/Titan/"$color$piece".png https://vchess.club/images/pieces/Titan/"$color$piece".png
+ done
+done
for image in "Orda" "Archer" "Lancer" "Kheshig" "Yurt"; do
rm -f /public/images/variants/Orda/"$image".png
wget -q -O public/images/variants/Orda/"$image".png https://vchess.club/images/variants/Orda/"$image".png
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Capa_1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 380 380"
+ style="enable-background:new 0 0 380 380;"
+ xml:space="preserve"
+ sodipodi:docname="castle.svg"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"><metadata
+ id="metadata39"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs37" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1060"
+ id="namedview35"
+ showgrid="false"
+ inkscape:zoom="2.3236842"
+ inkscape:cx="190"
+ inkscape:cy="155.57191"
+ inkscape:window-x="0"
+ inkscape:window-y="20"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="Capa_1" />
+<path
+ d="m 358.93345,163.94827 v -15.2498 h -12.77326 v 15.2498 h -14.6918 v -15.2498 h -12.77325 v 15.2498 h -16.47997 v -15.2498 h -12.77884 v 15.2498 h -14.689 v -15.2498 h -12.77885 v 15.2498 h -2.97001 v 12.07211 h 10.63209 v 32.54831 h -41.16218 v -52.12205 h -3.54374 V 116.43308 L 193.23412,64.845953 V 40.25232 c 7.80739,-7.369721 14.39007,-7.266921 24.80611,-4.452218 10.88171,2.937517 17.56403,2.945705 25.85756,-5.475663 -19.46302,11.54538 -31.47073,-21.4787188 -50.66272,-11.522637 v -1.904974 h -4.83176 v 49.221837 l -30.91008,50.313515 v 40.01355 h -3.54557 v 52.12206 h -41.21901 v -32.54831 h 10.63211 v -12.07212 h -2.97002 v -15.2498 h -12.77046 v 15.2498 H 92.922885 v -15.2498 H 80.149634 v 15.2498 H 63.671532 v -15.2498 H 50.892691 v 15.2498 H 36.204615 v -15.2498 H 23.425774 v 15.2498 h -2.970957 v 12.07212 h 10.632114 v 186.57464 h 68.59838 13.042399 156.90285 13.04429 68.5965 V 176.02038 h 10.63212 V 163.94827 Z M 81.745009,341.22367 H 62.070568 v -30.14663 c 0,-5.30464 4.405212,-9.60765 9.837687,-9.60765 5.432473,0 9.837686,4.30301 9.837686,9.60765 v 30.14663 z M 99.900446,184.39718 h -55.9909 v -8.3768 h 55.9909 z m 91.278234,-72.36172 c 12.22935,4.89161 11.67986,12.79444 11.67986,12.79444 v 26.21204 H 191.17868 179.50254 V 124.8299 c 0,0 -0.55136,-7.90283 11.67614,-12.79444 z m 33.76274,154.32655 v 75.77957 h -33.76274 -33.76087 v -75.77957 c 0,0 -1.5842,-22.84604 33.76087,-36.98871 35.35066,14.14175 33.76274,36.98871 33.76274,36.98871 z m 95.34723,74.86166 H 300.6142 v -30.14663 c 0,-5.30464 4.40522,-9.60765 9.83582,-9.60765 5.43341,0 9.83863,4.30301 9.83863,9.60765 z m 18.16102,-156.82649 h -55.99089 v -8.3768 h 55.99089 z"
+ id="path2"
+ style="stroke-width:0.920469" />
+<g
+ id="g4"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+ id="g6"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+ id="g8"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+ id="g10"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+ id="g12"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+ id="g14"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+ id="g16"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+ id="g18"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+ id="g20"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+ id="g22"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+ id="g24"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+ id="g26"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+ id="g28"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+ id="g30"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+ id="g32"
+ transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+</svg>
champion SVG files as well (modified from Wikipedia)
Letter D: https://svgsilh.com/image/2051714.html
Mammoth: https://www.flaticon.com/free-icon/mammoth_925138
+Castle icon: https://www.flaticon.com/free-icon/castle_89009
--- /dev/null
+#$# git-fat 56f92909d57649cf4e83fee222d4d31ce97db450 6873
--- /dev/null
+#$# git-fat 11885764721183414a33ca0d4f361eef52cf59ea 7864
--- /dev/null
+#$# git-fat 58e925edb232bba20e4c6b8bcd0ba030c400522c 6496
--- /dev/null
+#$# git-fat 377da5b2300e17ca49be08a724d85080141f6677 14023
--- /dev/null
+#$# git-fat 492668b6b23979d939af8640a747cad8c02a57f0 16668
--- /dev/null
+#$# git-fat b25f663c757f85265d1b43057d8ab3c3a74c71a3 13518
--- /dev/null
+#$# git-fat 09ff40b2e6cdae5e3d9c1996b63663991d8e517d 8711
--- /dev/null
+#$# git-fat 637d993756ed5916271875bfeac445aa41943838 9702
--- /dev/null
+#$# git-fat 4f8983d7172fb5c940b42acc292a6f1afadc4fdc 8166
--- /dev/null
+../Eightpieces/tmp_png/bp.png
\ No newline at end of file
--- /dev/null
+#$# git-fat 25aa2c85c5fd3aaf2bce47343b39c51edf89f342 11151
--- /dev/null
+#$# git-fat 788248951c09c73e32081deae6efce7c242766ac 7581
--- /dev/null
+#$# git-fat bad9ab44a4634519db15e8eaec1f3402d276bb91 9803
--- /dev/null
+#$# git-fat 3d5d5dcd30705eb5b3b94f5f0492242429d53bac 9181
--- /dev/null
+#$# git-fat d64664f3b195b0a59412918bba2c16d052913793 7126
--- /dev/null
+#$# git-fat 58d858d675e11f6fb7d8bc4884b6f425de51689b 6636
--- /dev/null
+taille=64
+convert wn.png -resize "$taille"x"$taille" wn_small.png
+convert wb.png -resize "$taille"x"$taille" wb_small.png
+convert bn.png -resize "$taille"x"$taille" bn_small.png
+convert bb.png -resize "$taille"x"$taille" bb_small.png
+# GIMP: manual fill by color (yellow/red). Then:
+for color in w b; do
+ for piece in r n b q k; do
+ convert -composite -gravity center $color$piece.png "$color"n_small.png $color"$piece"_1.png
+ convert -composite -gravity center $color$piece.png "$color"b_small.png $color"$piece"_2.png
+ done
+done
+# Finally: manual renaming (TODO)
--- /dev/null
+#$# git-fat 2cfeae196dcf329318e9aa32b4e8fd83743cf49f 11469
--- /dev/null
+#$# git-fat 9b675b6208624b902fbbd6a433da8d1c8f295032 11911
--- /dev/null
+#$# git-fat e35d4bc6f8d216ae4bd65874699dcd5eb2d4eeac 11735
--- /dev/null
+#$# git-fat 313f9037f964c36f83e04345e7f5d48d033eef0a 13086
--- /dev/null
+#$# git-fat dce8ad8bcb0b2b000f51e4b931815069be8cdf4f 14204
--- /dev/null
+#$# git-fat 38d8d25e99dd6579f221e1287cae92ea0f390e03 13139
--- /dev/null
+#$# git-fat 2b3afd85b70abdbd3aea594aed7c997974c75c2c 11727
--- /dev/null
+#$# git-fat 9b7b3ada1693eab7198074517a25fff77601dcc5 11975
--- /dev/null
+#$# git-fat 192a9061313c6dd2c0dad24e3d4260e956452d5e 11849
--- /dev/null
+../Eightpieces/tmp_png/wp.png
\ No newline at end of file
--- /dev/null
+#$# git-fat 8d55cfeab39f3ffefe0249d3123f3492dce591d2 16221
--- /dev/null
+#$# git-fat 58fffbf851cf42da824c472c606af380b097f91f 8644
--- /dev/null
+#$# git-fat 67b9dc06c174f688dda8a94132192089455232ae 14810
--- /dev/null
+#$# git-fat 53bf6c11b93148cf0cdb2135769bac7fbe2df52f 15043
--- /dev/null
+#$# git-fat 488f25c90f9f6b1e4c6b07563ab8d860edee678e 10146
--- /dev/null
+#$# git-fat 2a69102b1577f207dcab57a0e3d76826b9c0849a 10249
// NOTE: x coords = top to bottom; y = left to right
// (from white player perspective)
export const ChessRules = class ChessRules {
+
//////////////
// MISC UTILS
// Scan board for kings positions
scanKings(fen) {
- this.INIT_COL_KING = { w: -1, b: -1 };
// Squares of white and black king:
this.kingPos = { w: [-1, -1], b: [-1, -1] };
const fenRows = V.ParseFen(fen).position.split("/");
switch (fenRows[i].charAt(j)) {
case "k":
this.kingPos["b"] = [i, k];
- this.INIT_COL_KING["b"] = k;
break;
case "K":
this.kingPos["w"] = [i, k];
- this.INIT_COL_KING["w"] = k;
break;
default: {
const num = parseInt(fenRows[i].charAt(j), 10);
// tr: transformation
getBasicMove([sx, sy], [ex, ey], tr) {
const initColor = this.getColor(sx, sy);
- const initPiece = this.getPiece(sx, sy);
+ const initPiece = this.board[sx][sy].charAt(1);
let mv = new Move({
appear: [
new PiPo({
x: ex,
y: ey,
c: this.getColor(ex, ey),
- p: this.getPiece(ex, ey)
+ p: this.board[ex][ey].charAt(1)
})
);
}
enpassantMove.vanish.push({
x: x,
y: epSquare.y,
- // Captured piece is usually a pawn, but next line seems harmless
- p: this.getPiece(x, epSquare.y),
+ p: this.board[x][epSquare.y].charAt(1),
c: this.getColor(x, epSquare.y)
});
}
V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
"oneStep"
);
- if (V.HasCastle) moves = moves.concat(this.getCastleMoves(sq));
+ if (V.HasCastle && this.castleFlags[this.turn].some(v => v < V.size.y))
+ moves = moves.concat(this.getCastleMoves(sq));
return moves;
}
// "castleInCheck" arg to let some variants castle under check
- getCastleMoves([x, y], castleInCheck, castleWith) {
+ getCastleMoves([x, y], finalSquares, castleInCheck, castleWith) {
const c = this.getColor(x, y);
- if (x != (c == "w" ? V.size.x - 1 : 0) || y != this.INIT_COL_KING[c])
- return []; //x isn't first rank, or king has moved (shortcut)
// Castling ?
const oppCol = V.GetOppCol(c);
let moves = [];
let i = 0;
// King, then rook:
- const finalSquares = [
- [2, 3],
- [V.size.y - 2, V.size.y - 3]
- ];
+ finalSquares = finalSquares || [ [2, 3], [V.size.y - 2, V.size.y - 3] ];
+ const castlingKing = this.board[x][y].charAt(1);
castlingCheck: for (
let castleSide = 0;
castleSide < 2;
// NOTE: in some variants this is not a rook
const rookPos = this.castleFlags[c][castleSide];
+ const castlingPiece = this.board[x][rookPos].charAt(1);
if (
this.board[x][rookPos] == V.EMPTY ||
this.getColor(x, rookPos) != c ||
- (!!castleWith && !castleWith.includes(this.getPiece(x, rookPos)))
+ (!!castleWith && !castleWith.includes(castlingPiece))
) {
// Rook is not here, or changed color (see Benedict)
continue;
}
// Nothing on the path of the king ? (and no checks)
- const castlingPiece = this.getPiece(x, rookPos);
const finDist = finalSquares[castleSide][0] - y;
let step = finDist / Math.max(1, Math.abs(finDist));
i = y;
do {
if (
- // NOTE: "castling" arg is used by some variants (Monster),
- // where "isAttacked" is overloaded in an infinite-recursive way.
- // TODO: not used anymore (Monster + Doublemove2 are simplified).
- (!castleInCheck && this.isAttacked([x, i], oppCol, "castling")) ||
- (this.board[x][i] != V.EMPTY &&
+ (!castleInCheck && 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, castlingPiece].includes(this.getPiece(x, i))))
+ (this.getColor(x, i) != c || ![y, rookPos].includes(i))
+ )
) {
continue castlingCheck;
}
finalSquares[castleSide][i] != rookPos &&
this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
(
- this.getPiece(x, finalSquares[castleSide][i]) != V.KING ||
+ finalSquares[castleSide][i] != y ||
this.getColor(x, finalSquares[castleSide][i]) != c
)
) {
new PiPo({
x: x,
y: finalSquares[castleSide][0],
- p: V.KING,
+ p: castlingKing,
c: c
}),
new PiPo({
})
],
vanish: [
- new PiPo({ x: x, y: y, p: V.KING, c: c }),
+ // 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: rookPos, p: castlingPiece, c: c })
],
end:
)
);
}
+
};
--- /dev/null
+p.boxed
+ | TODO
+
+p.
+ Besides the usual game end conditions, White can win by preventing black
+ long castle. And, Black can win by castling long.
+
+p For example after 1.e4 e5 2.Bc4, Nc6?? loses immediatly: 3.Bxf7+
+
+figure.diagram-container
+ .diagram
+ | fen:r1bqkbnr/pppp1Bpp/2n5/4p3/4P3/8/PPPP1PPP/RNBQK1NR:
+ figcaption After 1.e4 e5 2.Bc4 Nc6 3.Bxf7+ 1-0
+
+h3 Source
+
+p
+ a(href="https://www.chessvariants.com/winning.dir/castle.html")
+ | Castle chess
+ | on chessvariants.com. See also
+ a(href="http://cinquantesignes.blogspot.com/2020/09/castlechess.html")
+ | this post
+ | giving some clarifications and advices.
+
+p Inventor: Éric Angelini (1996)
--- /dev/null
+p.boxed
+ | TODO
--- /dev/null
+p.boxed
+ | Les pièces à longue portée peuvent sauter par dessus un obstacle
+ | lorsqu'elle se trouvent sur la première rangée.
+
+p TODO
--- /dev/null
+p.boxed
+ | Choose squares for new pieces to appear when the initial ones are moved.
+ | One extra knight (first move), and one extra bishop (second move).
+
+p.
+ Everything is the same as in orthodox rules, except that new pieces appear
+ at initially selected locations: a knight and a bishop.
+
+p.
+ The first move of the game selects the square for the knight
+ (just click on a square on the first rank),
+ and the second move picks the square for the bishop.
+ When an "augmented piece" moves, the extra piece enter into play on the
+ initial square.
+
+figure.diagram-container
+ .diagram
+ | fen:rnaqkbor/pppppppp/8/8/8/8/PPPPPPPP/RMBQKCNR:
+ figcaption Augmented pieces on b1, f1, c8 and g8.
+
+p.
+ Castling is always possible. If both pieces involved are augmented,
+ then two extra pieces appear at the end of the move.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:rm1qkbor/ppp2ppp/4b3/3pp3/8/5NP1/PPPPPPBP/RNBQJ2V:
+ .diagram.diag22
+ | fen:rm1qkbor/ppp2ppp/4b3/3pp3/8/5NP1/PPPPPPBP/RNBQNRKB:
+ figcaption Before and after 0-0.
+
+h3 More information
+
+p
+ | The variant idea was suggested recently, and corresponds to
+ a(href="https://musketeerchess.net/home/index.html")
+ | Musketeer Chess
+ | , using non-fairy pieces.
+
+p Inventor: Zied Haddad (2020)
--- /dev/null
+p.boxed
+ | Elige espacios donde aparezcan nuevas piezas,
+ | durante un primer desplazamiento desde la primera fila.
+ | Un caballo adicional (primer movimiento) y un alfil adicional
+ | (segundo movimiento).
+
+p.
+ Todo va como el ajedrez ortodoxo, excepto las nuevas
+ piezas que aparecen en lugares designados: un caballo y un alfil.
+
+p.
+ El primer movimiento del juego selecciona una casilla para el caballo
+ (simplemente haga clic en un cuadro en la primera fila),
+ y el segundo movimiento designa una casilla para el alfil.
+ Cuando se mueve una "pieza aumentada", entra la pieza adicional
+ en el juego en la casilla de salida.
+
+figure.diagram-container
+ .diagram
+ | fen:rnaqkbor/pppppppp/8/8/8/8/PPPPPPPP/RMBQKCNR:
+ figcaption Piezas aumentadas en b1, f1, c8 y g8.
+
+p.
+ El enroque siempre es posible. Si las dos piezas involucradas son
+ aumentado, luego aparecen dos nuevas piezas después del movimiento.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:rm1qkbor/ppp2ppp/4b3/3pp3/8/5NP1/PPPPPPBP/RNBQJ2V:
+ .diagram.diag22
+ | fen:rm1qkbor/ppp2ppp/4b3/3pp3/8/5NP1/PPPPPPBP/RNBQNRKB:
+ figcaption Antes y después de 0-0.
+
+h3 Más información
+
+p
+ | Esta idea ha sido sugerida recientemente y corresponde a
+ a(href = "https://musketeerchess.net/home/index.html")
+ | Ajedrez Mosquetero
+ | , con piezas estándar.
+
+p Inventor: Zied Haddad (2020)
--- /dev/null
+p.boxed
+ | Choisissez des cases d'appartition de nouvelles pièces,
+ | lors d'un premier déplacement depuis la première rangée.
+ | Un cavalier (premier coup) et un fou supplémentaires (second coup).
+
+p.
+ Tout se déroule comme aux échecs orthodoxes, à l'exception de nouvelles
+ pièces apparaissant à des endroits désignés : un cavalier et un fou.
+
+p.
+ Le premier coup de la partie sélectionne une case pour le cavalier
+ (cliquez simplement sur une case de la première rangée),
+ et le second coup désigne une case pour le fou.
+ Quand une "pièce augmentée" se déplace, la pièce supplémentaire entre
+ dans le jeu sur la case de départ.
+
+figure.diagram-container
+ .diagram
+ | fen:rnaqkbor/pppppppp/8/8/8/8/PPPPPPPP/RMBQKCNR:
+ figcaption Pièces augmentées sur b1, f1, c8 et g8.
+
+p.
+ Le roque est toujours possible. Si les deux pièces impliquées sont
+ augmentées, alors deux nouvelles pièces apparaissent après le coup.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:rm1qkbor/ppp2ppp/4b3/3pp3/8/5NP1/PPPPPPBP/RNBQJ2V:
+ .diagram.diag22
+ | fen:rm1qkbor/ppp2ppp/4b3/3pp3/8/5NP1/PPPPPPBP/RNBQNRKB:
+ figcaption Avant et après 0-0.
+
+h3 Plus d'information
+
+p
+ | Cette idée a été suggérée récemment, et correspond aux
+ a(href="https://musketeerchess.net/home/index.html")
+ | Échecs Mousquetaires
+ | , avec des pièces standard.
+
+p Inventeur : Zied Haddad (2020)
import { ChessRules } from "@/base_rules";
export class AbsorptionRules extends ChessRules {
+
getPpath(b) {
if ([V.BN, V.RN, V.QN].includes(b[1])) return "Absorption/" + b;
return b;
notation += "=" + move.appear[0].p.toUpperCase();
return notation;
}
+
};
// TODO? atLeastOneMove() would be more efficient if rewritten here
// (less sideBoard computations)
export class AliceRules extends ChessRules {
+
static get ALICE_PIECES() {
return {
s: "p",
notation += "=" + move.appear[0].p.toUpperCase();
return notation;
}
+
};
import { ChessRules, PiPo, Move } from "@/base_rules";
export class Allmate1Rules extends ChessRules {
+
static get HasEnpassant() {
return false;
}
// No "under check" conditions in castling
getCastleMoves(sq) {
- return super.getCastleMoves(sq, "castleInCheck");
+ return super.getCastleMoves(sq, null, "castleInCheck");
}
// TODO: allow pieces to "commit suicide"? (Currently yes except king)
this.kingPos[this.turn] = [-1, -1];
// Or maybe a rook?
else if (v.p == V.ROOK) {
- if (v.y < this.INIT_COL_KING[v.c])
+ if (v.y < this.kingPos[v.c][1])
this.castleFlags[v.c][0] = 8;
else
- // v.y > this.INIT_COL_KING[v.c]
+ // v.y > this.kingPos[v.c][1]
this.castleFlags[v.c][1] = 8;
}
}
}
return notation;
}
+
};
import { ChessRules, PiPo, Move } from "@/base_rules";
export class Allmate2Rules extends ChessRules {
+
static get HasEnpassant() {
return false;
}
// No "under check" conditions in castling
getCastleMoves(sq) {
- return super.getCastleMoves(sq, "castleInCheck");
+ return super.getCastleMoves(sq, null, "castleInCheck");
}
// TODO: allow pieces to "commit suicide"? (Currently yes except king)
this.kingPos[this.turn] = [-1, -1];
// Or maybe a rook?
else if (v.p == V.ROOK) {
- if (v.y < this.INIT_COL_KING[v.c])
+ if (v.y < this.kingPos[v.c][1])
this.castleFlags[v.c][0] = 8;
else
- // v.y > this.INIT_COL_KING[v.c]
+ // v.y > this.kingPos[v.c][1]
this.castleFlags[v.c][1] = 8;
}
}
}
return notation;
}
+
};
import { ArrayFun } from "@/utils/array";
export class AmbiguousRules extends ChessRules {
+
static get HasFlags() {
return false;
}
else move.vanish[1].p = V.TARGET_CODE[move.vanish[1].p];
return notation;
}
+
};
import { randInt } from "@/utils/alea";
export class Antiking1Rules extends BerolinaRules {
+
static get PawnSpecs() {
return Object.assign(
{},
static get SEARCH_DEPTH() {
return 2;
}
+
};
import { randInt } from "@/utils/alea";
export class Antiking2Rules extends ChessRules {
+
static get ANTIKING() {
return "a";
}
static get SEARCH_DEPTH() {
return 2;
}
+
};
import { ChessRules } from "@/base_rules";
export class AntimatterRules extends ChessRules {
+
getPotentialMovesFrom([x, y]) {
let moves = super.getPotentialMovesFrom([x, y]);
-
// Handle "matter collisions"
moves.forEach(m => {
if (
m.appear.pop();
}
});
-
return moves;
}
+
};
import { randInt } from "@/utils/alea";
export class ApocalypseRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
V.CoordsToSquare(move.end)
);
}
+
};
import { ChessRules } from "@/base_rules";
export class ArenaRules extends ChessRules {
+
static get HasFlags() {
return false;
}
static get SEARCH_DEPTH() {
return 4;
}
+
};
import { ChessRules, PiPo } from "@/base_rules";
export class AtomicRules extends ChessRules {
+
getPotentialMovesFrom([x, y]) {
let moves = super.getPotentialMovesFrom([x, y]);
if (!this.isAttacked(kp, V.GetOppCol(color))) return "1/2";
return color == "w" ? "0-1" : "1-0"; //checkmate
}
+
};
import { ChessRules } from "@/base_rules";
export class BalaklavaRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
ChessRules.VALUES
);
}
+
};
import { shuffle } from "@/utils/alea";
export class BallRules extends ChessRules {
+
static get Lines() {
return [
// White goal:
finalSquare
);
}
+
};
import { shuffle } from "@/utils/alea";
export class BaroqueRules extends ChessRules {
+
static get HasFlags() {
return false;
}
if (move.vanish.length > 1 && move.appear[0].p != V.KING) notation += "X";
return notation;
}
+
};
import { ChessRules, PiPo, Move } from "@/base_rules";
export class BenedictRules extends ChessRules {
+
static get HasEnpassant() {
return false;
}
};
return super.getNotation(basicMove);
}
+
};
import { ChessRules } from "@/base_rules";
export class BerolinaRules extends ChessRules {
+
// En-passant after 2-sq jump
getEpSquare(moveOrSquare) {
if (!moveOrSquare) return undefined;
}
return super.getNotation(move); //all other pieces are orthodox
}
+
};
import { ArrayFun } from "@/utils/array";
export class BicolourRules extends ChessRules {
+
static get HasFlags() {
return false;
}
this.isAttacked(this.kingPos[color], 'b')
);
}
+
};
import { ChessRules } from "@/base_rules";
export class BishopawnsRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
static get SEARCH_DEPTH() {
return 4;
}
+
};
import { ChessRules, Move, PiPo } from "@/base_rules";
export class CannibalRules extends ChessRules {
+
static get KING_CODE() {
return {
'p': 's',
}
return notation;
}
+
};
import { ChessRules } from "@/base_rules";
export class CaptureRules extends ChessRules {
+
// Trim all non-capturing moves
static KeepCaptures(moves) {
return moves.filter(m => m.vanish.length == 2 && m.appear.length == 1);
return V.KeepCaptures(moves);
return moves;
}
+
};
import { ChessRules } from "@/base_rules";
export class CastleRules extends ChessRules {
+
getCurrentScore() {
const baseScore = super.getCurrentScore();
if (baseScore != '*') return baseScore;
}
return '*';
}
+
};
import { randInt } from "@/utils/alea";
export class ChakartRules extends ChessRules {
+
static get PawnSpecs() {
return SuicideRules.PawnSpecs;
}
}
return notation;
}
+
};
import { ChessRules, Move, PiPo } from "@/base_rules";
export class Checkered1Rules extends ChessRules {
+
static board2fen(b) {
const checkered_codes = {
p: "s",
notation += "=" + move.appear[0].p.toUpperCase();
return notation;
}
+
};
import { ChessRules, Move, PiPo } from "@/base_rules";
export class Checkered2Rules extends ChessRules {
+
static board2fen(b) {
const checkered_codes = {
p: "s",
notation += "=" + move.appear[0].p.toUpperCase();
return notation;
}
+
};
import { ChessRules } from "@/base_rules";
export class ChecklessRules extends ChessRules {
+
// Cannot use super.atLeastOneMove: lead to infinite recursion
atLeastOneMove_aux() {
const color = this.turn;
static get SEARCH_DEPTH() {
return 2;
}
+
};
import { shuffle } from "@/utils/alea";
export class CircularRules extends ChessRules {
+
static get HasCastle() {
return false;
}
static get SEARCH_DEPTH() {
return 2;
}
+
};
import { ArrayFun } from "@/utils/array";
export class ClorangeRules extends ChessRules {
+
static IsGoodFen(fen) {
if (!ChessRules.IsGoodFen(fen)) return false;
const fenParsed = V.ParseFen(fen);
move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
return piece + "@" + V.CoordsToSquare(move.end);
}
+
};
import { randInt } from "@/utils/alea";
export class ColorboundRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
);
}
- // TODO: really find a way to avoid duolicating most of the castling code
- // each time: here just the queenside castling squares change for black.
getCastleMoves([x, y]) {
- const c = this.getColor(x, y);
- if (x != (c == "w" ? V.size.x - 1 : 0) || y != this.INIT_COL_KING[c])
- return [];
-
- const oppCol = V.GetOppCol(c);
- let moves = [];
- let i = 0;
- // King, then rook:
+ const color = this.getColor(x, y);
const finalSquares = [
// Black castle long in an unusual way:
- (c == 'w' ? [2, 3] : [1, 2]),
+ (color == 'w' ? [2, 3] : [1, 2]),
[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;
-
- const rookPos = this.castleFlags[c][castleSide];
- const castlingPiece = this.getPiece(x, rookPos);
- 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 &&
- (this.getColor(x, i) != c ||
- ![V.KING, castlingPiece].includes(this.getPiece(x, i))))
- ) {
- continue castlingCheck;
- }
- i += step;
- } while (i != finalSquares[castleSide][0]);
-
- step = castleSide == 0 ? -1 : 1;
- for (i = y + step; i != rookPos; i += step) {
- if (this.board[x][i] != V.EMPTY) continue castlingCheck;
- }
-
- for (i = 0; i < 2; i++) {
- if (
- finalSquares[castleSide][i] != rookPos &&
- this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
- (
- this.getPiece(x, finalSquares[castleSide][i]) != V.KING ||
- this.getColor(x, finalSquares[castleSide][i]) != c
- )
- ) {
- continue castlingCheck;
- }
- }
-
- 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: castlingPiece,
- c: c
- })
- ],
- vanish: [
- new PiPo({ x: x, y: y, p: V.KING, c: c }),
- new PiPo({ x: x, y: rookPos, p: castlingPiece, c: c })
- ],
- end:
- Math.abs(y - rookPos) <= 2
- ? { x: x, y: rookPos }
- : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) }
- })
- );
- }
-
- return moves;
+ return super.getCastleMoves([x, y], finalSquares);
}
isAttacked(sq, color) {
static get SEARCH_DEPTH() {
return 2;
}
+
};
import { randInt, sample } from "@/utils/alea";
export class CoregalRules extends ChessRules {
+
static IsGoodPosition(position) {
if (!ChessRules.IsGoodPosition(position)) return false;
const rows = position.split("/");
return !!flags.match(/^[a-z]{8,8}$/);
}
- // Scanning king position for faster updates is still interesting,
- // but no need for INIT_COL_KING because it's given in castle flags.
+ // Scanning king position for faster updates is still interesting.
scanKings(fen) {
this.kingPos = { w: [-1, -1], b: [-1, -1] };
const fenRows = V.ParseFen(fen).position.split("/");
}
}
+ getPPpath(m) {
+ if (
+ m.vanish.length == 2 &&
+ m.appear.length == 2 &&
+ m.vanish[0].p == V.QUEEN
+ ) {
+ // Large castle: show castle symbol
+ return "Coregal/castle";
+ }
+ return super.getPPpath(m);
+ }
+
getCheckSquares() {
const color = this.turn;
let squares = [];
}
}
- getPotentialQueenMoves(sq) {
- return super.getPotentialQueenMoves(sq).concat(this.getCastleMoves(sq));
+ getPotentialQueenMoves([x, y]) {
+ let moves = super.getPotentialQueenMoves([x, y]);
+ const c = this.getColor(x, y);
+ if (this.castleFlags[c].slice(1, 3).includes(y))
+ moves = moves.concat(this.getCastleMoves([x, y]));
+ return moves;
}
- getCastleMoves([x, y]) {
+ getPotentialKingMoves([x, y]) {
+ let moves = this.getSlideNJumpMoves(
+ [x, y],
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+ "oneStep"
+ );
const c = this.getColor(x, y);
- if (
- x != (c == "w" ? V.size.x - 1 : 0) ||
- !this.castleFlags[c].slice(1, 3).includes(y)
- ) {
- // x isn't first rank, or piece moved
- return [];
- }
- const castlingPiece = this.getPiece(x, y);
+ if (this.castleFlags[c].slice(1, 3).includes(y))
+ moves = moves.concat(this.getCastleMoves([x, y]));
+ return moves;
+ }
+ getCastleMoves([x, y]) {
// Relative position of the selected piece: left or right ?
// If left: small castle left, large castle right.
// If right: usual situation.
+ const c = this.getColor(x, y);
const relPos = (this.castleFlags[c][1] == y ? "left" : "right");
- // Castling ?
- const oppCol = V.GetOppCol(c);
- let moves = [];
- let i = 0;
- // Castling piece, then rook:
- const finalSquares = {
- 0: (relPos == "left" ? [1, 2] : [2, 3]),
- 3: (relPos == "right" ? [6, 5] : [5, 4])
- };
-
- // Left, then right castle:
- castlingCheck: for (let castleSide of [0, 3]) {
- if (this.castleFlags[c][castleSide] >= 8) continue;
-
- // Rook and castling piece are on initial position
- const rookPos = this.castleFlags[c][castleSide];
-
- // 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 ||
- ![castlingPiece, V.ROOK].includes(this.getPiece(x, i))))
- ) {
- continue castlingCheck;
- }
- i += step;
- } while (i != finalSquares[castleSide][0]);
-
- // Nothing on the path to the rook?
- step = castleSide == 0 ? -1 : 1;
- for (i = y + step; i != rookPos; i += step) {
- if (this.board[x][i] != V.EMPTY) continue castlingCheck;
- }
-
- // Nothing on final squares, except maybe castling piece and rook?
- for (i = 0; i < 2; i++) {
- if (
- this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
- ![y, rookPos].includes(finalSquares[castleSide][i])
- ) {
- continue castlingCheck;
- }
- }
-
- // If this code is reached, castle is valid
- moves.push(
- new Move({
- appear: [
- new PiPo({
- x: x,
- y: finalSquares[castleSide][0],
- p: castlingPiece,
- c: c
- }),
- new PiPo({
- x: x,
- y: finalSquares[castleSide][1],
- p: V.ROOK,
- c: c
- })
- ],
- vanish: [
- new PiPo({ x: x, y: y, p: castlingPiece, c: c }),
- new PiPo({ x: x, y: rookPos, p: V.ROOK, c: c })
- ],
- // In this variant, always castle by playing onto the rook
- end: { x: x, y: rookPos }
- })
- );
- }
-
+ const finalSquares = [
+ relPos == "left" ? [1, 2] : [2, 3],
+ relPos == "right" ? [6, 5] : [5, 4]
+ ];
+ const saveFlags = JSON.stringify(this.castleFlags[c]);
+ // Alter flags to follow base_rules semantic
+ this.castleFlags[c] = [0, 3].map(i => this.castleFlags[c][i]);
+ const moves = super.getCastleMoves([x, y], finalSquares);
+ this.castleFlags[c] = JSON.parse(saveFlags);
return moves;
}
}
return super.getNotation(move);
}
+
};
import { ChessRules, Move, PiPo } from "@/base_rules";
export class CoronationRules extends ChessRules {
+
getPotentialMovesFrom([x, y]) {
let moves = super.getPotentialMovesFrom([x, y]);
// If no queen on board, allow rook+bishop fusions:
notation += "=Q";
return notation;
}
+
};
import { ArrayFun } from "@/utils/array";
export class CrazyhouseRules extends ChessRules {
+
static IsGoodFen(fen) {
if (!ChessRules.IsGoodFen(fen)) return false;
const fenParsed = V.ParseFen(fen);
move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
return piece + "@" + V.CoordsToSquare(move.end);
}
+
};
import { randInt, shuffle } from "@/utils/alea";
export class CylinderRules extends ChessRules {
+
// Output basically x % 8 (circular board)
static ComputeY(y) {
let res = y % V.size.y;
k: 1000
};
}
+
};
import { randInt } from "@/utils/alea";
export class DarkRules extends ChessRules {
+
// Analyse in Dark mode makes no sense
static get CanAnalyze() {
return false;
candidates.push(j);
return moves[candidates[randInt(candidates.length)]];
}
+
};
import { shuffle } from "@/utils/alea";
export class DiamondRules extends ChessRules {
+
static get HasFlags() {
return false;
}
}
return super.getNotation(move); //all other pieces are orthodox
}
+
};
import { randInt } from "@/utils/alea";
export class DiceRules extends ChessRules {
+
static get CanAnalyze() {
return false;
}
getNotation(move) {
return super.getNotation(move) + "/" + move.end.p.toUpperCase();
}
+
};
import { ChessRules } from "@/base_rules";
export class DiscoduelRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
static get SEARCH_DEPTH() {
return 4;
}
+
};
// ...But the middle king will get captured quickly...
export class DoublearmyRules extends ChessRules {
+
static get COMMONER() {
return "c";
}
static get SEARCH_DEPTH() {
return 2;
}
+
};
import { ChessRules } from "@/base_rules";
export class Doublemove1Rules extends ChessRules {
+
static IsGoodEnpassant(enpassant) {
const squares = enpassant.split(",");
if (squares.length > 2) return false;
}
return doubleMove;
}
+
};
import { randInt } from "@/utils/alea";
export class Doublemove2Rules extends ChessRules {
+
static IsGoodEnpassant(enpassant) {
const squares = enpassant.split(",");
if (squares.length > 2) return false;
// TODO: not always the best move played (why ???)
return doubleMove;
}
+
};
import { randInt } from "@/utils/alea";
export class DynamoRules extends ChessRules {
+
// TODO? later, allow to push out pawns on a and h files
static get HasEnpassant() {
return false;
return initialSquare + "R";
return move.appear[0].p.toUpperCase() + initialSquare + finalSquare;
}
+
};
import { ChessRules, PiPo, Move } from "@/base_rules";
export class EightpiecesRules extends ChessRules {
+
static get JAILER() {
return "j";
}
}
return notation;
}
+
};
import { ChessRules, PiPo, Move } from "@/base_rules";
export class EnpassantRules extends ChessRules {
+
static IsGoodEnpassant(enpassant) {
if (enpassant != "-") {
const squares = enpassant.split(",");
k: 1000
};
}
+
};
--- /dev/null
+import { ChessRules } from "@/base_rules";
+
+export class EvolutionRules extends ChessRules {
+
+ getPotentialMovesFrom([x, y]) {
+ let moves = super.getPotentialMovesFrom([x, y]);
+ const c = this.getColor(x, y);
+ const piece = this.getPiece(x, y);
+ if (
+ [V.BISHOP, V.ROOK, V.QUEEN].includes(piece) &&
+ (c == 'w' && x == 7) || (c == 'b' && x == 0)
+ ) {
+ // Move from first rank
+ const forward = (c == 'w' ? -1 : 1);
+ for (let shift of [-2, 0, 2]) {
+ if (
+ (piece == V.ROOK && shift != 0) ||
+ (piece == V.BISHOP && shift == 0)
+ ) {
+ continue;
+ }
+ if (
+ V.OnBoard(x+2*forward, y+shift) &&
+ this.board[x+forward][y+shift/2] != V.EMPTY &&
+ this.getColor(x+2*forward, y+shift) != c
+ ) {
+ moves.push(this.getBasicMove([x,y], [x+2*forward,y+shift]));
+ }
+ }
+ }
+ return moves;
+ }
+
+};
import { ChessRules } from "@/base_rules";
export class ExtinctionRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
}
return super.evalPosition();
}
+
};
import { shuffle } from "@/utils/alea";
export class FootballRules extends ChessRules {
+
static get HasFlags() {
return false;
}
" w 0 -"
);
}
+
};
import { ChessRules } from "@/base_rules";
export class ForwardRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
}
scanKings(fen) {
- this.INIT_COL_KING = { w: -1, b: -1 };
// Squares of white and black king:
this.kingPos = { w: [-1, -1], b: [-1, -1] };
const fenRows = V.ParseFen(fen).position.split("/");
case "k":
case "l":
this.kingPos["b"] = [i, k];
- this.INIT_COL_KING["b"] = k;
break;
case "K":
case "L":
this.kingPos["w"] = [i, k];
- this.INIT_COL_KING["w"] = k;
break;
default: {
const num = parseInt(fenRows[i].charAt(j), 10);
ChessRules.VALUES
);
}
+
};
import { ChessRules } from "@/base_rules";
export class FreecaptureRules extends ChessRules {
+
canTake() {
// Can capture both colors:
return true;
static get SEARCH_DEPTH() {
return 2;
}
+
};
// NOTE: initial setup differs from the original; see
// https://www.chessvariants.com/large.dir/freeling.html
export class GrandRules extends ChessRules {
+
static IsGoodFen(fen) {
if (!ChessRules.IsGoodFen(fen)) return false;
const fenParsed = V.ParseFen(fen);
" w 0 " + flags + " - 00000000000000"
);
}
+
};
import { randInt } from "@/utils/alea";
export class GrasshopperRules extends ChessRules {
+
static get HasEnpassant() {
return false;
}
"/gggggggg/pppppppp/8/8/PPPPPPPP/GGGGGGGG/"
);
}
+
};
import { BerolinaRules } from "@/variants/Berolina";
export class GridolinaRules extends BerolinaRules {
+
static get Lines() {
return [
[[2, 0], [2, 8]],
}
return false;
}
+
};
import { randInt } from "@/utils/alea";
export class HamiltonRules extends ChessRules {
+
static get HasFlags() {
return false;
}
// First game move:
return "N@" + V.CoordsToSquare(move.end);
}
+
};
import { randInt } from "@/utils/alea";
export class HiddenRules extends ChessRules {
+
static get HasFlags() {
return false;
}
finalSquare
);
}
+
};
import { randInt } from "@/utils/alea";
export class HiddenqueenRules extends ChessRules {
+
// Analyse in Hiddenqueen mode makes no sense
static get CanAnalyze() {
return false;
else notation = finalSquare;
return notation;
}
+
};
import { ChessRules } from "@/base_rules";
export class HordeRules extends ChessRules {
+
static get HasFlags() {
return false;
}
// From black side, just run usual checks:
return super.getCurrentScore();
}
+
};
import { randInt, shuffle } from "@/utils/alea";
export class InterweaveRules extends ChessRules {
+
static get HasFlags() {
return false;
}
if (move.vanish.length >= 2) notation += "X";
return notation;
}
+
};
import { SuicideRules } from "@/variants/Suicide";
export class KingletRules extends ChessRules {
+
static get HasFlags() {
return false;
}
k: 4
};
}
+
};
import { randInt } from "@/utils/alea";
export class KnightmateRules extends ChessRules {
+
static get COMMONER() {
return "c";
}
k: 1000
};
}
+
};
import { ChessRules } from "@/base_rules";
export class KnightpawnsRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
postPlay() {}
postUndo() {}
+
};
import { ChessRules } from "@/base_rules";
export class Knightrelay1Rules extends ChessRules {
+
static get HasEnpassant() {
return false;
}
return notation;
}
+
};
import { ChessRules } from "@/base_rules";
export class Knightrelay2Rules extends ChessRules {
+
getPotentialMovesFrom([x, y]) {
let moves = super.getPotentialMovesFrom([x, y]);
return notation;
}
+
};
import { ChessRules, PiPo } from "@/base_rules";
export class KoopaRules extends ChessRules {
+
static get HasEnpassant() {
return false;
}
// stand for stunned indicator.
scanKings(fen) {
- this.INIT_COL_KING = { w: -1, b: -1 };
// Squares of white and black king:
this.kingPos = { w: [-1, -1], b: [-1, -1] };
const fenRows = V.ParseFen(fen).position.split("/");
case "k":
case "l":
this.kingPos["b"] = [i, k];
- this.INIT_COL_KING["b"] = k;
break;
case "K":
case "L":
this.kingPos["w"] = [i, k];
- this.INIT_COL_KING["w"] = k;
break;
default: {
const num = parseInt(fenRows[i].charAt(j), 10);
sq,
V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
"oneStep"
- ).concat(super.getCastleMoves(sq, true, ['r']))
+ ).concat(super.getCastleMoves(sq, null, true, ['r']))
);
}
}
return notation;
}
+
};
import { ChessRules } from "@/base_rules";
export class KothRules extends ChessRules {
+
static get Lines() {
return [
[[3, 3], [3, 5]],
) / 2
);
}
+
};
import { randInt } from "@/utils/alea";
export class LosersRules extends ChessRules {
+
// Trim all non-capturing moves
static KeepCaptures(moves) {
return moves.filter(m => m.vanish.length == 2 && m.appear.length == 1);
// Less material is better (more subtle in fact but...)
return -super.evalPosition();
}
+
};
import { randInt } from "@/utils/alea";
export class MadhouseRules extends ChessRules {
+
hoverHighlight(x, y) {
// Testing move validity results in an infinite update loop.
// TODO: find a way to test validity anyway.
move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
return piece + "@" + V.CoordsToSquare(move.end);
}
+
};
import { ChessRules } from "@/base_rules";
export class MadrasiRules extends ChessRules {
+
isImmobilized(sq) {
const oppCol = V.GetOppCol(this.getColor(sq[0], sq[1]));
const piece = this.getPiece(sq[0], sq[1]);
// Connected kings paralyze each other
return false;
}
+
};
import { ChessRules, PiPo } from "@/base_rules";
export class MagneticRules extends ChessRules {
+
static get HasEnpassant() {
return false;
}
static get SEARCH_DEPTH() {
return 2;
}
+
};
import { randInt, shuffle } from "@/utils/alea";
export class MakrukRules extends ChessRules {
+
static get HasFlags() {
return false;
}
k: 1000
};
}
+
};
import { shuffle } from "@/utils/alea";
export class MaximaRules extends ChessRules {
+
static get HasFlags() {
return false;
}
if (move.vanish.length > 1 && move.appear[0].p != V.KING) notation += "X";
return notation;
}
+
};
import { ShogiRules } from "@/variants/Shogi";
export class MinishogiRules extends ShogiRules {
+
static IsGoodFen(fen) {
if (!ChessRules.IsGoodFen(fen)) return false;
const fenParsed = V.ParseFen(fen);
static get SEARCH_DEPTH() {
return 3;
}
+
};
import { ChessRules } from "@/base_rules";
export class MonochromeRules extends ChessRules {
+
static get HasEnpassant() {
// Pawns would be on the same side
return false;
}
return notation;
}
+
};
import { randInt } from "@/utils/alea";
export class MonsterRules extends ChessRules {
+
static IsGoodFlags(flags) {
// Only black can castle
return !!flags.match(/^[a-z]{2,2}$/);
const color = this.turn;
return (color == 'w' ? getBestWhiteMove() : getBestBlackMove());
}
+
};
import { randInt } from "@/utils/alea";
export class OmegaRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
return this.getSlideNJumpMoves(sq, V.steps[V.WIZARD], "oneStep");
}
- getCastleMoves([x, y], castleInCheck) {
- const c = this.getColor(x, y);
- if (x != (c == "w" ? V.size.x - 2 : 1) || y != this.INIT_COL_KING[c])
- return []; //x isn't first rank, or king has moved (shortcut)
-
- // Castling ?
- const oppCol = V.GetOppCol(c);
- let moves = [];
- let i = 0;
- // King, then rook:
+ getCastleMoves([x, y]) {
const finalSquares = [
[4, 5],
[8, 7]
];
- 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, rook and king are on initial position
-
- // NOTE: in some variants this is not a rook
- const rookPos = this.castleFlags[c][castleSide];
- if (this.board[x][rookPos] == V.EMPTY || this.getColor(x, rookPos) != c)
- // Rook is not here, or changed color (see Benedict)
- continue;
-
- // Nothing on the path of the king ? (and no checks)
- const castlingPiece = this.getPiece(x, rookPos);
- const finDist = finalSquares[castleSide][0] - y;
- let step = finDist / Math.max(1, Math.abs(finDist));
- i = y;
- do {
- if (
- (!castleInCheck && 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, castlingPiece].includes(this.getPiece(x, i))))
- ) {
- continue castlingCheck;
- }
- i += step;
- } while (i != finalSquares[castleSide][0]);
-
- // Nothing on the path to the rook?
- step = castleSide == 0 ? -1 : 1;
- for (i = y + step; i != rookPos; i += step) {
- if (this.board[x][i] != V.EMPTY) continue castlingCheck;
- }
-
- // Nothing on final squares, except maybe king and castling rook?
- for (i = 0; i < 2; i++) {
- if (
- finalSquares[castleSide][i] != rookPos &&
- this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
- (
- this.getPiece(x, finalSquares[castleSide][i]) != V.KING ||
- this.getColor(x, finalSquares[castleSide][i]) != c
- )
- ) {
- continue castlingCheck;
- }
- }
-
- // If this code is reached, castle is valid
- 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: castlingPiece,
- c: c
- })
- ],
- vanish: [
- new PiPo({ x: x, y: y, p: V.KING, c: c }),
- new PiPo({ x: x, y: rookPos, p: castlingPiece, c: c })
- ],
- end:
- Math.abs(y - rookPos) <= 2
- ? { x: x, y: rookPos }
- : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) }
- })
- );
- }
-
- return moves;
+ return super.getCastleMoves([x, y], finalSquares);
}
isAttacked(sq, color) {
}
return evaluation;
}
+
};
import { randInt } from "@/utils/alea";
export class OrdaRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
}
);
}
+
};
import { randInt } from "@/utils/alea";
export class OrdamirrorRules extends OrdaRules {
+
static get PawnSpecs() {
return Object.assign(
{},
k: 1000
};
}
+
};
import { ChessRules } from "@/base_rules";
export class Pacifist1Rules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
return true;
}
- scanKings(fen) {
- // Kings may be swapped, so they are not tracked (no kingPos)
- this.INIT_COL_KING = { w: -1, b: -1 };
- const fenRows = V.ParseFen(fen).position.split("/");
- const startRow = { 'w': V.size.x - 1, 'b': 0 };
- for (let i = 0; i < fenRows.length; i++) {
- let k = 0; //column index on board
- for (let j = 0; j < fenRows[i].length; j++) {
- switch (fenRows[i].charAt(j)) {
- case "k":
- this.INIT_COL_KING["b"] = k;
- break;
- case "K":
- this.INIT_COL_KING["w"] = k;
- break;
- default: {
- const num = parseInt(fenRows[i].charAt(j), 10);
- if (!isNaN(num)) k += num - 1;
- }
- }
- k++;
- }
- }
- }
+ // Kings may be swapped, so they are not tracked (no kingPos)
+ scanKings(fen) { }
// Sum white pieces attacking a square, and remove black pieces count.
sumAttacks([x, y]) {
static get SEARCH_DEPTH() {
return 1;
}
+
};
import { Pacifist1Rules } from "@/variants/Pacifist1";
export class Pacifist2Rules extends Pacifist1Rules {
+
// Sum values of white pieces attacking a square,
// and remove (sum of) black pieces values.
sumAttacks([x, y]) {
});
return res;
}
+
};
import { ChessRules, PiPo, Move } from "@/base_rules";
export class ParachuteRules extends ChessRules {
+
static get HasFlags() {
return false;
}
move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
return piece + "@" + V.CoordsToSquare(move.end);
}
+
};
import { ChessRules } from "@/base_rules";
export class PawnmassacreRules extends ChessRules {
+
static get HasFlags() {
return false;
}
.concat(bFen.substr(splitIdx))
);
}
+
};
import { ChessRules } from "@/base_rules";
export class PawnsRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
static get SEARCH_DEPTH() {
return 4;
}
+
};
import { ChessRules } from "@/base_rules";
export class PawnskingRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
static get SEARCH_DEPTH() {
return 4;
}
+
};
import { randInt } from "@/utils/alea";
export class PerfectRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
" w 0 " + flags + " -"
);
}
+
};
import { randInt } from "@/utils/alea";
export class PocketknightRules extends ChessRules {
+
hoverHighlight(x, y) {
// Testing move validity results in an infinite update loop.
// TODO: find a way to test validity anyway.
// Knight landing:
return "N@" + V.CoordsToSquare(move.end);
}
+
};
import { randInt } from "@/utils/alea";
export class Progressive1Rules extends ChessRules {
+
static get HasEnpassant() {
return false;
}
for (let i=res.length - 1; i>= 0; i--) this.undo(res[i]);
return res;
}
+
};
import { randInt } from "@/utils/alea";
export class Progressive2Rules extends Progressive1Rules {
+
static get PawnSpecs() {
return Object.assign(
{},
k: 1000
};
}
+
};
import { ChessRules } from "@/base_rules";
export class QueenpawnsRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
static get SEARCH_DEPTH() {
return 4;
}
+
};
import { ChessRules } from "@/base_rules";
export class RacingkingsRules extends ChessRules {
+
static get HasFlags() {
return false;
}
// Ponder with king position:
return evaluation/5 + this.kingPos["b"][0] - this.kingPos["w"][0];
}
+
};
import { ChessRules } from "@/base_rules";
export class RampageRules extends ChessRules {
+
// Sum white pieces attacking a square, and remove black pieces count.
sumAttacks([x, y]) {
const getSign = (color) => {
static get SEARCH_DEPTH() {
return 1;
}
+
};
import { ArrayFun } from "@/utils/array";
export class RecycleRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
return piece + "@" + V.CoordsToSquare(move.end);
}
+
};
import { ChessRules, PiPo, Move } from "@/base_rules";
export class RifleRules extends ChessRules {
+
getBasicMove([sx, sy], [ex, ey], tr) {
let mv = new Move({
appear: [],
static get SEARCH_DEPTH() {
return 2;
}
+
};
import { shuffle } from "@/utils/alea";
export class RococoRules extends ChessRules {
+
static get HasFlags() {
return false;
}
if (move.vanish.length > 1 && move.appear[0].p != V.KING) notation += "X";
return notation;
}
+
};
import { ChessRules } from "@/base_rules";
export class RookpawnsRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
static get SEARCH_DEPTH() {
return 4;
}
+
};
import { randInt, shuffle } from "@/utils/alea";
export class RoyalraceRules extends ChessRules {
+
static get HasFlags() {
return false;
}
V.CoordsToSquare(move.end)
);
}
+
};
import { ArrayFun } from "@/utils/array";
export class RugbyRules extends ChessRules {
+
static get HasFlags() {
return false;
}
// Stalemate (will probably never happen)
return "1/2";
}
+
};
import { ChessRules, PiPo } from "@/base_rules";
export class SchessRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
}
return super.getNotation(move);
}
+
};
import { randInt, sample } from "@/utils/alea";
export class ShakoRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
}
getCastleMoves([x, y]) {
- const c = this.getColor(x, y);
- if (x != (c == "w" ? V.size.x - 2 : 1) || y != this.INIT_COL_KING[c])
- return []; //x isn't second rank, or king has moved (shortcut)
-
- // Castling ?
- const oppCol = V.GetOppCol(c);
- let moves = [];
- let i = 0;
- // King, then rook:
const finalSquares = [
[3, 4],
[7, 6]
];
- 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, rook and king are on initial position
-
- const rookPos = this.castleFlags[c][castleSide];
-
- // Nothing on the path of the king ? (and no checks)
- const castlingPiece = this.getPiece(x, rookPos);
- 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 ||
- ![V.KING, castlingPiece].includes(this.getPiece(x, i))))
- ) {
- continue castlingCheck;
- }
- i += step;
- } while (i != finalSquares[castleSide][0]);
-
- // Nothing on the path to the rook?
- step = castleSide == 0 ? -1 : 1;
- for (i = y + step; i != rookPos; i += step) {
- if (this.board[x][i] != V.EMPTY) continue castlingCheck;
- }
-
- // Nothing on final squares, except maybe king and castling rook?
- for (i = 0; i < 2; i++) {
- if (
- finalSquares[castleSide][i] != rookPos &&
- this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
- (
- this.getPiece(x, finalSquares[castleSide][i]) != V.KING ||
- this.getColor(x, finalSquares[castleSide][i]) != c
- )
- ) {
- continue castlingCheck;
- }
- }
-
- // If this code is reached, castle is valid
- 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: castlingPiece,
- c: c
- })
- ],
- vanish: [
- new PiPo({ x: x, y: y, p: V.KING, c: c }),
- new PiPo({ x: x, y: rookPos, p: castlingPiece, c: c })
- ],
- end:
- Math.abs(y - rookPos) <= 2
- ? { x: x, y: rookPos }
- : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) }
- })
- );
- }
-
- return moves;
+ return super.getCastleMoves([x, y], finalSquares);
}
isAttacked(sq, color) {
" w 0 " + flags + " -"
);
}
+
};
import { ChessRules } from "@/base_rules";
export class ShatranjRules extends ChessRules {
+
static get HasFlags() {
return false;
}
k: 1000
};
}
+
};
import { sample, shuffle } from "@/utils/alea";
export class ShogiRules extends ChessRules {
+
static get HasFlags() {
return false;
}
)
);
}
+
};
import { randInt } from "@/utils/alea";
export class SittuyinRules extends ChessRules {
+
static get HasFlags() {
return false;
}
}
return super.getNotation(move);
}
+
};
import { SuicideRules } from "@/variants/Suicide";
export class SuctionRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
finalSquare
);
}
+
};
import { shuffle } from "@/utils/alea";
export class SuicideRules extends ChessRules {
+
static get HasFlags() {
return false;
}
" w 0 -"
);
}
+
};
import { randInt } from "@/utils/alea";
export class SwapRules extends ChessRules {
+
setOtherVariables(fen) {
super.setOtherVariables(fen);
// Local stack of swaps
// Swap
return "S" + V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end);
}
+
};
import { ChessRules, Move, PiPo } from "@/base_rules";
export class SwitchingRules extends ChessRules {
+
// Build switch move between squares x1,y1 and x2,y2
getSwitchMove_s([x1, y1], [x2, y2]) {
const c = this.getColor(x1, y1); //same as color at square 2
// Switch
return "S" + V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end);
}
-}
+
+};
import { randInt } from "@/utils/alea";
export class SynchroneRules extends ChessRules {
+
static get CanAnalyze() {
return false;
}
: null;
}
- // After undo(): no need to re-set INIT_COL_KING
scanKings() {
this.kingPos = { w: [-1, -1], b: [-1, -1] };
for (let i = 0; i < V.size.x; i++) {
V.CoordsToSquare(move.end)
);
}
+
};
import { randInt } from "@/utils/alea";
export class TakenmakeRules extends ChessRules {
+
setOtherVariables(fen) {
super.setOtherVariables(fen);
// Stack of "last move" only for intermediate captures
delete moves[mIdx]["next"];
return [moves[mIdx], move2];
}
+
};
import { randInt } from "@/utils/alea";
export class TeleportRules extends ChessRules {
+
hoverHighlight(x, y) {
// Testing move validity results in an infinite update loop.
// TODO: find a way to test validity anyway.
move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
return piece + "@" + V.CoordsToSquare(move.end);
}
+
};
import { shuffle } from "@/utils/alea";
export class TencubedRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
{ c: 4, w: 3, a: 6, m: 8 }
);
}
+
};
import { ChessRules } from "@/base_rules";
export class ThreechecksRules extends ChessRules {
+
static IsGoodFlags(flags) {
// 4 for castle + 2 for checks (0,1 or 2)
return !!flags.match(/^[01]{4,4}[012]{2,2}$/);
// Take number of checks into account
return baseEval/5 - this.checkFlags["w"] + this.checkFlags["b"];
}
+
};
-import { ChessRules } from "@/base_rules";
+import { ChessRules, Move, PiPo } from "@/base_rules";
export class TitanRules extends ChessRules {
- // Idea: yellow = bishop, orange = knight (for white)
- // and, red = bishop + purple = knight (black side)
- // (avoid using a bigger board, or complicated drawings)
+
+ static get IMAGE_EXTENSION() {
+ // Temporarily, for the time SVG pieces are being designed:
+ return ".png";
+ }
// Decode if normal piece, or + bishop or knight
- getPiece(i, j) {
- const piece = this.board[i][j].charAt(1);
+ getPiece(x, y) {
+ const piece = this.board[x][y].charAt(1);
if (ChessRules.PIECES.includes(piece)) return piece;
// Augmented piece:
switch (piece) {
}
}
- // TODO: subtelty, castle forbidden if
-
// Code: a/c = bishop + knight/bishop j/l for king,
// m/o for knight, s/t for queen, u/v for rook
static get AUGMENTED_PIECES() {
];
}
+ getPpath(b) {
+ return "Titan/" + b;
+ }
+
// Decode above notation into additional piece
getExtraPiece(symbol) {
if (['a','j','m','s','u'].includes(symbol))
return 'b';
}
+ // Inverse operation: augment piece
+ getAugmented(piece) {
+ const knight = this.movesCount <= 1;
+ switch (piece) {
+ case V.ROOK: return (knight ? 'u' : 'v');
+ case V.KNIGHT: return (knight ? 'm' : 'o');
+ case V.BISHOP: return (knight ? 'a' : 'c');
+ case V.QUEEN: return (knight ? 's' : 't');
+ case V.KING: return (knight ? 'j' : 'l');
+ }
+ return '_'; //never reached
+ }
+
+ static IsGoodPosition(position) {
+ if (position.length == 0) return false;
+ const rows = position.split("/");
+ if (rows.length != V.size.x) return false;
+ let kings = { "w": 0, "b": 0 };
+ const allPiecesCodes = V.PIECES.concat(V.AUGMENTED_PIECES);
+ const kingBlackCodes = ['j','k','l'];
+ const kingWhiteCodes = ['J','K','L'];
+ for (let row of rows) {
+ let sumElts = 0;
+ for (let i = 0; i < row.length; i++) {
+ if (kingBlackCodes.includes(row[i])) kings['b']++;
+ else if (kingWhiteCodes.includes(row[i])) kings['w']++;
+ if (allPiecesCodes.includes(row[i].toLowerCase())) sumElts++;
+ else {
+ const num = parseInt(row[i], 10);
+ if (isNaN(num)) return false;
+ sumElts += num;
+ }
+ }
+ if (sumElts != V.size.y) return false;
+ }
+ // Both kings should be on board, only one of each color:
+ if (Object.values(kings).some(v => v != 1)) return false;
+ return true;
+ }
+
+ // Kings may be augmented:
+ scanKings(fen) {
+ this.kingPos = { w: [-1, -1], b: [-1, -1] };
+ const rows = V.ParseFen(fen).position.split("/");
+ for (let i = 0; i < rows.length; i++) {
+ let k = 0; //column index on board
+ for (let j = 0; j < rows[i].length; j++) {
+ const piece = rows[i].charAt(j);
+ if (['j','k','l'].includes(piece.toLowerCase())) {
+ const color = (piece.charCodeAt(0) <= 90 ? 'w' : 'b');
+ this.kingPos[color] = [i, k];
+ }
+ else {
+ const num = parseInt(rows[i].charAt(j), 10);
+ if (!isNaN(num)) k += num - 1;
+ }
+ k++;
+ }
+ }
+ }
+
// If piece not in usual list, bishop or knight appears.
getPotentialMovesFrom([x, y]) {
- let moves = super.getPotentialMovesFrom(sq);
+ if (this.movesCount <= 3) {
+ // Setup stage
+ const color = this.getColor(x, y);
+ const firstRank = (color == 'w' ? 7 : 0);
+ if (x != firstRank || V.AUGMENTED_PIECES.includes(this.board[x][y][1]))
+ return [];
+ const piece = this.getPiece(x, y);
+ const move = new Move({
+ appear: [
+ new PiPo({ x: x, y: y, c: color, p: this.getAugmented(piece) })
+ ],
+ vanish: [
+ new PiPo({ x: x, y: y, c: color, p: piece })
+ ],
+ start: { x: x, y: y },
+ end: { x: x, y: y }
+ });
+ return [move];
+ }
+ let moves = super.getPotentialMovesFrom([x, y]);
+ const initialPiece = this.getPiece(x, y);
const color = this.turn;
-
-// treat castle case here (both pieces appear!)
if (
V.AUGMENTED_PIECES.includes(this.board[x][y][1]) &&
((color == 'w' && x == 7) || (color == "b" && x == 0))
) {
const newPiece = this.getExtraPiece(this.board[x][y][1]);
moves.forEach(m => {
+ m.appear[0].p = initialPiece;
m.appear.push(
new PiPo({
p: newPiece,
);
});
}
+ moves.forEach(m => {
+ if (m.vanish.length <= 1) return;
+ const [vx, vy] = [m.vanish[1].x, m.vanish[1].y];
+ if (
+ m.appear.length >= 2 && //3 if the king was also augmented
+ m.vanish.length == 2 &&
+ m.vanish[1].c == color &&
+ V.AUGMENTED_PIECES.includes(this.board[vx][vy][1])
+ ) {
+ // Castle, rook is an "augmented piece"
+ m.appear[1].p = V.ROOK;
+ m.appear.push(
+ new PiPo({
+ p: this.getExtraPiece(this.board[vx][vy][1]),
+ c: color,
+ x: vx,
+ y: vy
+ })
+ );
+ }
+ });
return moves;
}
- // TODO: special case of move 1 = choose squares, knight first, then bishop
- // (just click ?)
+ // Special case of move 1 = choose squares, knight first, then bishop
+ doClick(square) {
+ if (this.movesCount >= 4) return null;
+ const color = this.turn;
+ const [x, y] = [square[0], square[1]];
+ if ((color == 'w' && x != 7) || (color == 'b' && x != 0)) return null;
+ const selectedPiece = this.board[x][y][1];
+ return new Move({
+ appear: [
+ new PiPo({
+ x: x,
+ y: y,
+ c: color,
+ p: this.getAugmented(selectedPiece)
+ })
+ ],
+ vanish: [
+ new PiPo({
+ x: x,
+ y: y,
+ c: color,
+ p: selectedPiece
+ })
+ ],
+ start: { x: x, y: y },
+ end: { x: x, y: y }
+ });
+ }
+
+ postPlay(move) {
+ if (this.movesCount > 4) super.postPlay(move);
+ }
+
+ postUndo(move) {
+ if (this.movesCount >= 4) super.postUndo(move);
+ }
+
+ evalPosition() {
+ let evaluation = 0;
+ 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 sign = this.getColor(i, j) == "w" ? 1 : -1;
+ const piece = this.getPiece(i, j);
+ evaluation += sign * V.VALUES[piece];
+ const symbol = this.board[i][j][1];
+ if (V.AUGMENTED_PIECES.includes(symbol)) {
+ const extraPiece = this.getExtraPiece(symbol);
+ evaluation += sign * V.VALUES[extraPiece]
+ }
+ }
+ }
+ }
+ return evaluation;
+ }
+
+ getNotation(move) {
+ if (
+ move.appear[0].x != move.vanish[0].x ||
+ move.appear[0].y != move.vanish[0].y
+ ) {
+ if (
+ V.AUGMENTED_PIECES.includes(move.vanish[0].p) ||
+ (
+ move.vanish.length >= 2 &&
+ V.AUGMENTED_PIECES.includes(move.vanish[1].p)
+ )
+ ) {
+ // Simplify move before calling super.getNotation()
+ let smove = JSON.parse(JSON.stringify(move));
+ if (ChessRules.PIECES.includes(move.vanish[0].p)) {
+ // Castle with an augmented rook
+ smove.appear.pop();
+ smove.vanish[1].p = smove.appear[1].p;
+ }
+ else {
+ // Moving an augmented piece
+ smove.appear.pop();
+ smove.vanish[0].p = smove.appear[0].p;
+ if (
+ smove.vanish.length == 2 &&
+ smove.vanish[0].c == smove.vanish[1].c &&
+ V.AUGMENTED_PIECES.includes(move.vanish[1].p)
+ ) {
+ // Castle with an augmented rook
+ smove.appear.pop();
+ smove.vanish[1].p = smove.appear[1].p;
+ }
+ }
+ return super.getNotation(smove);
+ }
+ // Else, more common case:
+ return super.getNotation(move);
+ }
+ // First moves in game, placements:
+ const square = V.CoordsToSquare(move.appear[0]);
+ const reserve =
+ (['a','j','m','s','u'].includes(move.appear[0].p) ? 'N' : 'B');
+ return '+' + reserve + '@' + square;
+ }
+
};
import { CoregalRules } from "@/variants/Coregal";
export class TwokingsRules extends CoregalRules {
+
static get PawnSpecs() {
return Object.assign(
{},
);
}
+ getPotentialQueenMoves(sq) {
+ return this.getSlideNJumpMoves(sq,
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
+ }
+
underCheck(color) {
const oppCol = V.GetOppCol(color);
for (let i=0; i<V.size.x; i++) {
}
postUndo() {}
+
};
import { ArrayFun } from "@/utils/array";
export class UpsidedownRules extends ChessRules {
+
static get HasFlags() {
return false;
}
static get SEARCH_DEPTH() {
return 2;
}
+
};
import { ChessRules } from "@/base_rules";
export class VchessRules extends ChessRules {
+
static get PawnSpecs() {
return Object.assign(
{},
}
return notation;
}
+
};
import { sample, randInt } from "@/utils/alea";
export class WildebeestRules extends ChessRules {
+
static get size() {
return { x: 10, y: 11 };
}
" w 0 " + flags + " -"
);
}
+
};
import { ChessRules } from "@/base_rules";
export class WormholeRules extends ChessRules {
+
static get HasFlags() {
return false;
}
notation += "=" + move.appear[0].p.toUpperCase();
return notation;
}
+
};
import { ChessRules } from "@/base_rules";
export class ZenRules extends ChessRules {
+
getEpSquare(moveOrSquare) {
if (!moveOrSquare) return undefined;
if (typeof moveOrSquare === "string") {
k: 1000
};
}
+
};