From: Benjamin Auder Date: Mon, 30 Nov 2020 16:13:46 +0000 (+0100) Subject: Almost added TitanChess + EvolutionChess X-Git-Url: https://git.auder.net/%7B%7B%20asset%28%27mixstore/images/css/mini-custom.min.css?a=commitdiff_plain;h=7e8a7ea1cb66adb4a987badfb0a3c2f99a21bd0a;p=vchess.git Almost added TitanChess + EvolutionChess --- diff --git a/client/download_objects.sh b/client/download_objects.sh index 3cb25775..5ad81f5b 100755 --- a/client/download_objects.sh +++ b/client/download_objects.sh @@ -7,6 +7,12 @@ for color in "w" "b"; do 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 diff --git a/client/public/images/pieces/Coregal/castle.svg b/client/public/images/pieces/Coregal/castle.svg new file mode 100644 index 00000000..f60e4c39 --- /dev/null +++ b/client/public/images/pieces/Coregal/castle.svg @@ -0,0 +1,106 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/SOURCE b/client/public/images/pieces/SOURCE index 8854bad4..c021dfca 100644 --- a/client/public/images/pieces/SOURCE +++ b/client/public/images/pieces/SOURCE @@ -10,3 +10,4 @@ the black Colorbound + Sittuyin pieces set, and he sent me the wizard and 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 diff --git a/client/public/images/pieces/Titan/ba.png b/client/public/images/pieces/Titan/ba.png new file mode 100644 index 00000000..66e816dc --- /dev/null +++ b/client/public/images/pieces/Titan/ba.png @@ -0,0 +1 @@ +#$# git-fat 56f92909d57649cf4e83fee222d4d31ce97db450 6873 diff --git a/client/public/images/pieces/Titan/bb.png b/client/public/images/pieces/Titan/bb.png new file mode 100644 index 00000000..89579a8f --- /dev/null +++ b/client/public/images/pieces/Titan/bb.png @@ -0,0 +1 @@ +#$# git-fat 11885764721183414a33ca0d4f361eef52cf59ea 7864 diff --git a/client/public/images/pieces/Titan/bc.png b/client/public/images/pieces/Titan/bc.png new file mode 100644 index 00000000..eb8089f9 --- /dev/null +++ b/client/public/images/pieces/Titan/bc.png @@ -0,0 +1 @@ +#$# git-fat 58e925edb232bba20e4c6b8bcd0ba030c400522c 6496 diff --git a/client/public/images/pieces/Titan/bj.png b/client/public/images/pieces/Titan/bj.png new file mode 100644 index 00000000..eb916f72 --- /dev/null +++ b/client/public/images/pieces/Titan/bj.png @@ -0,0 +1 @@ +#$# git-fat 377da5b2300e17ca49be08a724d85080141f6677 14023 diff --git a/client/public/images/pieces/Titan/bk.png b/client/public/images/pieces/Titan/bk.png new file mode 100644 index 00000000..899596c9 --- /dev/null +++ b/client/public/images/pieces/Titan/bk.png @@ -0,0 +1 @@ +#$# git-fat 492668b6b23979d939af8640a747cad8c02a57f0 16668 diff --git a/client/public/images/pieces/Titan/bl.png b/client/public/images/pieces/Titan/bl.png new file mode 100644 index 00000000..b26315ee --- /dev/null +++ b/client/public/images/pieces/Titan/bl.png @@ -0,0 +1 @@ +#$# git-fat b25f663c757f85265d1b43057d8ab3c3a74c71a3 13518 diff --git a/client/public/images/pieces/Titan/bm.png b/client/public/images/pieces/Titan/bm.png new file mode 100644 index 00000000..399656b9 --- /dev/null +++ b/client/public/images/pieces/Titan/bm.png @@ -0,0 +1 @@ +#$# git-fat 09ff40b2e6cdae5e3d9c1996b63663991d8e517d 8711 diff --git a/client/public/images/pieces/Titan/bn.png b/client/public/images/pieces/Titan/bn.png new file mode 100644 index 00000000..a38e7722 --- /dev/null +++ b/client/public/images/pieces/Titan/bn.png @@ -0,0 +1 @@ +#$# git-fat 637d993756ed5916271875bfeac445aa41943838 9702 diff --git a/client/public/images/pieces/Titan/bo.png b/client/public/images/pieces/Titan/bo.png new file mode 100644 index 00000000..3782be67 --- /dev/null +++ b/client/public/images/pieces/Titan/bo.png @@ -0,0 +1 @@ +#$# git-fat 4f8983d7172fb5c940b42acc292a6f1afadc4fdc 8166 diff --git a/client/public/images/pieces/Titan/bp.png b/client/public/images/pieces/Titan/bp.png new file mode 120000 index 00000000..9fa31726 --- /dev/null +++ b/client/public/images/pieces/Titan/bp.png @@ -0,0 +1 @@ +../Eightpieces/tmp_png/bp.png \ No newline at end of file diff --git a/client/public/images/pieces/Titan/bq.png b/client/public/images/pieces/Titan/bq.png new file mode 100644 index 00000000..9d6f1699 --- /dev/null +++ b/client/public/images/pieces/Titan/bq.png @@ -0,0 +1 @@ +#$# git-fat 25aa2c85c5fd3aaf2bce47343b39c51edf89f342 11151 diff --git a/client/public/images/pieces/Titan/br.png b/client/public/images/pieces/Titan/br.png new file mode 100644 index 00000000..71433c7e --- /dev/null +++ b/client/public/images/pieces/Titan/br.png @@ -0,0 +1 @@ +#$# git-fat 788248951c09c73e32081deae6efce7c242766ac 7581 diff --git a/client/public/images/pieces/Titan/bs.png b/client/public/images/pieces/Titan/bs.png new file mode 100644 index 00000000..ccfeb31a --- /dev/null +++ b/client/public/images/pieces/Titan/bs.png @@ -0,0 +1 @@ +#$# git-fat bad9ab44a4634519db15e8eaec1f3402d276bb91 9803 diff --git a/client/public/images/pieces/Titan/bt.png b/client/public/images/pieces/Titan/bt.png new file mode 100644 index 00000000..8ade9e26 --- /dev/null +++ b/client/public/images/pieces/Titan/bt.png @@ -0,0 +1 @@ +#$# git-fat 3d5d5dcd30705eb5b3b94f5f0492242429d53bac 9181 diff --git a/client/public/images/pieces/Titan/bu.png b/client/public/images/pieces/Titan/bu.png new file mode 100644 index 00000000..f62467ac --- /dev/null +++ b/client/public/images/pieces/Titan/bu.png @@ -0,0 +1 @@ +#$# git-fat d64664f3b195b0a59412918bba2c16d052913793 7126 diff --git a/client/public/images/pieces/Titan/bv.png b/client/public/images/pieces/Titan/bv.png new file mode 100644 index 00000000..00b59f50 --- /dev/null +++ b/client/public/images/pieces/Titan/bv.png @@ -0,0 +1 @@ +#$# git-fat 58d858d675e11f6fb7d8bc4884b6f425de51689b 6636 diff --git a/client/public/images/pieces/Titan/script.sh b/client/public/images/pieces/Titan/script.sh new file mode 100644 index 00000000..95ffa59d --- /dev/null +++ b/client/public/images/pieces/Titan/script.sh @@ -0,0 +1,13 @@ +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) diff --git a/client/public/images/pieces/Titan/wa.png b/client/public/images/pieces/Titan/wa.png new file mode 100644 index 00000000..74c935c4 --- /dev/null +++ b/client/public/images/pieces/Titan/wa.png @@ -0,0 +1 @@ +#$# git-fat 2cfeae196dcf329318e9aa32b4e8fd83743cf49f 11469 diff --git a/client/public/images/pieces/Titan/wb.png b/client/public/images/pieces/Titan/wb.png new file mode 100644 index 00000000..4153b89f --- /dev/null +++ b/client/public/images/pieces/Titan/wb.png @@ -0,0 +1 @@ +#$# git-fat 9b675b6208624b902fbbd6a433da8d1c8f295032 11911 diff --git a/client/public/images/pieces/Titan/wc.png b/client/public/images/pieces/Titan/wc.png new file mode 100644 index 00000000..1da7092c --- /dev/null +++ b/client/public/images/pieces/Titan/wc.png @@ -0,0 +1 @@ +#$# git-fat e35d4bc6f8d216ae4bd65874699dcd5eb2d4eeac 11735 diff --git a/client/public/images/pieces/Titan/wj.png b/client/public/images/pieces/Titan/wj.png new file mode 100644 index 00000000..6d369e79 --- /dev/null +++ b/client/public/images/pieces/Titan/wj.png @@ -0,0 +1 @@ +#$# git-fat 313f9037f964c36f83e04345e7f5d48d033eef0a 13086 diff --git a/client/public/images/pieces/Titan/wk.png b/client/public/images/pieces/Titan/wk.png new file mode 100644 index 00000000..86fcf430 --- /dev/null +++ b/client/public/images/pieces/Titan/wk.png @@ -0,0 +1 @@ +#$# git-fat dce8ad8bcb0b2b000f51e4b931815069be8cdf4f 14204 diff --git a/client/public/images/pieces/Titan/wl.png b/client/public/images/pieces/Titan/wl.png new file mode 100644 index 00000000..a196e4e1 --- /dev/null +++ b/client/public/images/pieces/Titan/wl.png @@ -0,0 +1 @@ +#$# git-fat 38d8d25e99dd6579f221e1287cae92ea0f390e03 13139 diff --git a/client/public/images/pieces/Titan/wm.png b/client/public/images/pieces/Titan/wm.png new file mode 100644 index 00000000..18dfc06b --- /dev/null +++ b/client/public/images/pieces/Titan/wm.png @@ -0,0 +1 @@ +#$# git-fat 2b3afd85b70abdbd3aea594aed7c997974c75c2c 11727 diff --git a/client/public/images/pieces/Titan/wn.png b/client/public/images/pieces/Titan/wn.png new file mode 100644 index 00000000..f8d66c93 --- /dev/null +++ b/client/public/images/pieces/Titan/wn.png @@ -0,0 +1 @@ +#$# git-fat 9b7b3ada1693eab7198074517a25fff77601dcc5 11975 diff --git a/client/public/images/pieces/Titan/wo.png b/client/public/images/pieces/Titan/wo.png new file mode 100644 index 00000000..aad608f5 --- /dev/null +++ b/client/public/images/pieces/Titan/wo.png @@ -0,0 +1 @@ +#$# git-fat 192a9061313c6dd2c0dad24e3d4260e956452d5e 11849 diff --git a/client/public/images/pieces/Titan/wp.png b/client/public/images/pieces/Titan/wp.png new file mode 120000 index 00000000..b1c56708 --- /dev/null +++ b/client/public/images/pieces/Titan/wp.png @@ -0,0 +1 @@ +../Eightpieces/tmp_png/wp.png \ No newline at end of file diff --git a/client/public/images/pieces/Titan/wq.png b/client/public/images/pieces/Titan/wq.png new file mode 100644 index 00000000..da98cb76 --- /dev/null +++ b/client/public/images/pieces/Titan/wq.png @@ -0,0 +1 @@ +#$# git-fat 8d55cfeab39f3ffefe0249d3123f3492dce591d2 16221 diff --git a/client/public/images/pieces/Titan/wr.png b/client/public/images/pieces/Titan/wr.png new file mode 100644 index 00000000..310782f4 --- /dev/null +++ b/client/public/images/pieces/Titan/wr.png @@ -0,0 +1 @@ +#$# git-fat 58fffbf851cf42da824c472c606af380b097f91f 8644 diff --git a/client/public/images/pieces/Titan/ws.png b/client/public/images/pieces/Titan/ws.png new file mode 100644 index 00000000..b6a224a8 --- /dev/null +++ b/client/public/images/pieces/Titan/ws.png @@ -0,0 +1 @@ +#$# git-fat 67b9dc06c174f688dda8a94132192089455232ae 14810 diff --git a/client/public/images/pieces/Titan/wt.png b/client/public/images/pieces/Titan/wt.png new file mode 100644 index 00000000..b4bb52da --- /dev/null +++ b/client/public/images/pieces/Titan/wt.png @@ -0,0 +1 @@ +#$# git-fat 53bf6c11b93148cf0cdb2135769bac7fbe2df52f 15043 diff --git a/client/public/images/pieces/Titan/wu.png b/client/public/images/pieces/Titan/wu.png new file mode 100644 index 00000000..6917106e --- /dev/null +++ b/client/public/images/pieces/Titan/wu.png @@ -0,0 +1 @@ +#$# git-fat 488f25c90f9f6b1e4c6b07563ab8d860edee678e 10146 diff --git a/client/public/images/pieces/Titan/wv.png b/client/public/images/pieces/Titan/wv.png new file mode 100644 index 00000000..420aee21 --- /dev/null +++ b/client/public/images/pieces/Titan/wv.png @@ -0,0 +1 @@ +#$# git-fat 2a69102b1577f207dcab57a0e3d76826b9c0849a 10249 diff --git a/client/src/base_rules.js b/client/src/base_rules.js index acf6eff0..d17ebe67 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -30,6 +30,7 @@ export const Move = class Move { // NOTE: x coords = top to bottom; y = left to right // (from white player perspective) export const ChessRules = class ChessRules { + ////////////// // MISC UTILS @@ -518,7 +519,6 @@ export const ChessRules = class ChessRules { // 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("/"); @@ -529,11 +529,9 @@ export const ChessRules = class ChessRules { 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); @@ -667,7 +665,7 @@ export const ChessRules = class ChessRules { // 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({ @@ -694,7 +692,7 @@ export const ChessRules = class ChessRules { x: ex, y: ey, c: this.getColor(ex, ey), - p: this.getPiece(ex, ey) + p: this.board[ex][ey].charAt(1) }) ); } @@ -735,8 +733,7 @@ export const ChessRules = class ChessRules { 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) }); } @@ -879,25 +876,22 @@ export const ChessRules = class ChessRules { 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; @@ -908,30 +902,28 @@ export const ChessRules = class ChessRules { // 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; } @@ -950,7 +942,7 @@ export const ChessRules = class ChessRules { 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 ) ) { @@ -965,7 +957,7 @@ export const ChessRules = class ChessRules { new PiPo({ x: x, y: finalSquares[castleSide][0], - p: V.KING, + p: castlingKing, c: c }), new PiPo({ @@ -976,7 +968,8 @@ export const ChessRules = class ChessRules { }) ], 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: @@ -1479,4 +1472,5 @@ export const ChessRules = class ChessRules { ) ); } + }; diff --git a/client/src/translations/rules/Evolution/en.pug b/client/src/translations/rules/Evolution/en.pug new file mode 100644 index 00000000..17241bfd --- /dev/null +++ b/client/src/translations/rules/Evolution/en.pug @@ -0,0 +1,25 @@ +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) diff --git a/client/src/translations/rules/Evolution/es.pug b/client/src/translations/rules/Evolution/es.pug new file mode 100644 index 00000000..3a33838b --- /dev/null +++ b/client/src/translations/rules/Evolution/es.pug @@ -0,0 +1,2 @@ +p.boxed + | TODO diff --git a/client/src/translations/rules/Evolution/fr.pug b/client/src/translations/rules/Evolution/fr.pug new file mode 100644 index 00000000..68accd76 --- /dev/null +++ b/client/src/translations/rules/Evolution/fr.pug @@ -0,0 +1,5 @@ +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 diff --git a/client/src/translations/rules/Titan/en.pug b/client/src/translations/rules/Titan/en.pug new file mode 100644 index 00000000..151118a4 --- /dev/null +++ b/client/src/translations/rules/Titan/en.pug @@ -0,0 +1,40 @@ +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) diff --git a/client/src/translations/rules/Titan/es.pug b/client/src/translations/rules/Titan/es.pug new file mode 100644 index 00000000..deaa2cfe --- /dev/null +++ b/client/src/translations/rules/Titan/es.pug @@ -0,0 +1,42 @@ +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) diff --git a/client/src/translations/rules/Titan/fr.pug b/client/src/translations/rules/Titan/fr.pug new file mode 100644 index 00000000..ecaf09c3 --- /dev/null +++ b/client/src/translations/rules/Titan/fr.pug @@ -0,0 +1,41 @@ +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) diff --git a/client/src/variants/Absorption.js b/client/src/variants/Absorption.js index 64c03e68..c6e1de74 100644 --- a/client/src/variants/Absorption.js +++ b/client/src/variants/Absorption.js @@ -1,6 +1,7 @@ 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; @@ -147,4 +148,5 @@ export class AbsorptionRules extends ChessRules { notation += "=" + move.appear[0].p.toUpperCase(); return notation; } + }; diff --git a/client/src/variants/Alice.js b/client/src/variants/Alice.js index 61fcd015..16ccb84d 100644 --- a/client/src/variants/Alice.js +++ b/client/src/variants/Alice.js @@ -5,6 +5,7 @@ import { ArrayFun } from "@/utils/array"; // TODO? atLeastOneMove() would be more efficient if rewritten here // (less sideBoard computations) export class AliceRules extends ChessRules { + static get ALICE_PIECES() { return { s: "p", @@ -366,4 +367,5 @@ export class AliceRules extends ChessRules { notation += "=" + move.appear[0].p.toUpperCase(); return notation; } + }; diff --git a/client/src/variants/Allmate1.js b/client/src/variants/Allmate1.js index 0e7e8ed5..88599e49 100644 --- a/client/src/variants/Allmate1.js +++ b/client/src/variants/Allmate1.js @@ -1,6 +1,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; export class Allmate1Rules extends ChessRules { + static get HasEnpassant() { return false; } @@ -125,7 +126,7 @@ export class Allmate1Rules extends ChessRules { // 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) @@ -201,10 +202,10 @@ export class Allmate1Rules extends ChessRules { 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; } } @@ -248,4 +249,5 @@ export class Allmate1Rules extends ChessRules { } return notation; } + }; diff --git a/client/src/variants/Allmate2.js b/client/src/variants/Allmate2.js index 259faa74..f4abbe33 100644 --- a/client/src/variants/Allmate2.js +++ b/client/src/variants/Allmate2.js @@ -1,6 +1,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; export class Allmate2Rules extends ChessRules { + static get HasEnpassant() { return false; } @@ -108,7 +109,7 @@ export class Allmate2Rules extends ChessRules { // 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) @@ -184,10 +185,10 @@ export class Allmate2Rules extends ChessRules { 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; } } @@ -231,4 +232,5 @@ export class Allmate2Rules extends ChessRules { } return notation; } + }; diff --git a/client/src/variants/Ambiguous.js b/client/src/variants/Ambiguous.js index 17e5283a..36824db8 100644 --- a/client/src/variants/Ambiguous.js +++ b/client/src/variants/Ambiguous.js @@ -3,6 +3,7 @@ import { randInt, shuffle } from "@/utils/alea"; import { ArrayFun } from "@/utils/array"; export class AmbiguousRules extends ChessRules { + static get HasFlags() { return false; } @@ -292,4 +293,5 @@ export class AmbiguousRules extends ChessRules { else move.vanish[1].p = V.TARGET_CODE[move.vanish[1].p]; return notation; } + }; diff --git a/client/src/variants/Antiking1.js b/client/src/variants/Antiking1.js index 07dae26c..92034a30 100644 --- a/client/src/variants/Antiking1.js +++ b/client/src/variants/Antiking1.js @@ -4,6 +4,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; export class Antiking1Rules extends BerolinaRules { + static get PawnSpecs() { return Object.assign( {}, @@ -221,4 +222,5 @@ export class Antiking1Rules extends BerolinaRules { static get SEARCH_DEPTH() { return 2; } + }; diff --git a/client/src/variants/Antiking2.js b/client/src/variants/Antiking2.js index 762ca5c8..df764583 100644 --- a/client/src/variants/Antiking2.js +++ b/client/src/variants/Antiking2.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; export class Antiking2Rules extends ChessRules { + static get ANTIKING() { return "a"; } @@ -232,4 +233,5 @@ export class Antiking2Rules extends ChessRules { static get SEARCH_DEPTH() { return 2; } + }; diff --git a/client/src/variants/Antimatter.js b/client/src/variants/Antimatter.js index f8f4c73e..6d3d36d4 100644 --- a/client/src/variants/Antimatter.js +++ b/client/src/variants/Antimatter.js @@ -1,9 +1,9 @@ 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 ( @@ -14,7 +14,7 @@ export class AntimatterRules extends ChessRules { m.appear.pop(); } }); - return moves; } + }; diff --git a/client/src/variants/Apocalypse.js b/client/src/variants/Apocalypse.js index 77d17c0e..e817296a 100644 --- a/client/src/variants/Apocalypse.js +++ b/client/src/variants/Apocalypse.js @@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules"; import { randInt } from "@/utils/alea"; export class ApocalypseRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -512,4 +513,5 @@ export class ApocalypseRules extends ChessRules { V.CoordsToSquare(move.end) ); } + }; diff --git a/client/src/variants/Arena.js b/client/src/variants/Arena.js index 62d2802a..285dd4c8 100644 --- a/client/src/variants/Arena.js +++ b/client/src/variants/Arena.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class ArenaRules extends ChessRules { + static get HasFlags() { return false; } @@ -143,4 +144,5 @@ export class ArenaRules extends ChessRules { static get SEARCH_DEPTH() { return 4; } + }; diff --git a/client/src/variants/Atomic.js b/client/src/variants/Atomic.js index 9675c58a..e2f4bdf5 100644 --- a/client/src/variants/Atomic.js +++ b/client/src/variants/Atomic.js @@ -1,6 +1,7 @@ import { ChessRules, PiPo } from "@/base_rules"; export class AtomicRules extends ChessRules { + getPotentialMovesFrom([x, y]) { let moves = super.getPotentialMovesFrom([x, y]); @@ -149,4 +150,5 @@ export class AtomicRules extends ChessRules { if (!this.isAttacked(kp, V.GetOppCol(color))) return "1/2"; return color == "w" ? "0-1" : "1-0"; //checkmate } + }; diff --git a/client/src/variants/Balaklava.js b/client/src/variants/Balaklava.js index 4a720f04..835aa965 100644 --- a/client/src/variants/Balaklava.js +++ b/client/src/variants/Balaklava.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class BalaklavaRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -117,4 +118,5 @@ export class BalaklavaRules extends ChessRules { ChessRules.VALUES ); } + }; diff --git a/client/src/variants/Ball.js b/client/src/variants/Ball.js index 056ec61b..04ad1feb 100644 --- a/client/src/variants/Ball.js +++ b/client/src/variants/Ball.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { shuffle } from "@/utils/alea"; export class BallRules extends ChessRules { + static get Lines() { return [ // White goal: @@ -565,4 +566,5 @@ export class BallRules extends ChessRules { finalSquare ); } + }; diff --git a/client/src/variants/Baroque.js b/client/src/variants/Baroque.js index 4ff23a00..063b1822 100644 --- a/client/src/variants/Baroque.js +++ b/client/src/variants/Baroque.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { shuffle } from "@/utils/alea"; export class BaroqueRules extends ChessRules { + static get HasFlags() { return false; } @@ -586,4 +587,5 @@ export class BaroqueRules extends ChessRules { if (move.vanish.length > 1 && move.appear[0].p != V.KING) notation += "X"; return notation; } + }; diff --git a/client/src/variants/Benedict.js b/client/src/variants/Benedict.js index 039aaeaf..40d7a091 100644 --- a/client/src/variants/Benedict.js +++ b/client/src/variants/Benedict.js @@ -1,6 +1,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; export class BenedictRules extends ChessRules { + static get HasEnpassant() { return false; } @@ -166,4 +167,5 @@ export class BenedictRules extends ChessRules { }; return super.getNotation(basicMove); } + }; diff --git a/client/src/variants/Berolina.js b/client/src/variants/Berolina.js index a7f7afa7..0765d4b9 100644 --- a/client/src/variants/Berolina.js +++ b/client/src/variants/Berolina.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class BerolinaRules extends ChessRules { + // En-passant after 2-sq jump getEpSquare(moveOrSquare) { if (!moveOrSquare) return undefined; @@ -161,4 +162,5 @@ export class BerolinaRules extends ChessRules { } return super.getNotation(move); //all other pieces are orthodox } + }; diff --git a/client/src/variants/Bicolour.js b/client/src/variants/Bicolour.js index eaa66d17..795f4ba4 100644 --- a/client/src/variants/Bicolour.js +++ b/client/src/variants/Bicolour.js @@ -3,6 +3,7 @@ import { randInt } from "@/utils/alea"; import { ArrayFun } from "@/utils/array"; export class BicolourRules extends ChessRules { + static get HasFlags() { return false; } @@ -108,4 +109,5 @@ export class BicolourRules extends ChessRules { this.isAttacked(this.kingPos[color], 'b') ); } + }; diff --git a/client/src/variants/Bishopawns.js b/client/src/variants/Bishopawns.js index 67afe66c..feecd458 100644 --- a/client/src/variants/Bishopawns.js +++ b/client/src/variants/Bishopawns.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class BishopawnsRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -52,4 +53,5 @@ export class BishopawnsRules extends ChessRules { static get SEARCH_DEPTH() { return 4; } + }; diff --git a/client/src/variants/Cannibal.js b/client/src/variants/Cannibal.js index bd5524f7..1d8469e2 100644 --- a/client/src/variants/Cannibal.js +++ b/client/src/variants/Cannibal.js @@ -1,6 +1,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules"; export class CannibalRules extends ChessRules { + static get KING_CODE() { return { 'p': 's', @@ -246,4 +247,5 @@ export class CannibalRules extends ChessRules { } return notation; } + }; diff --git a/client/src/variants/Capture.js b/client/src/variants/Capture.js index c5f86dcd..f919c60e 100644 --- a/client/src/variants/Capture.js +++ b/client/src/variants/Capture.js @@ -1,6 +1,7 @@ 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); @@ -43,4 +44,5 @@ export class CaptureRules extends ChessRules { return V.KeepCaptures(moves); return moves; } + }; diff --git a/client/src/variants/Castle.js b/client/src/variants/Castle.js index e6b0470d..cf44ba56 100644 --- a/client/src/variants/Castle.js +++ b/client/src/variants/Castle.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class CastleRules extends ChessRules { + getCurrentScore() { const baseScore = super.getCurrentScore(); if (baseScore != '*') return baseScore; @@ -11,4 +12,5 @@ export class CastleRules extends ChessRules { } return '*'; } + }; diff --git a/client/src/variants/Chakart.js b/client/src/variants/Chakart.js index 669ade11..1795e30c 100644 --- a/client/src/variants/Chakart.js +++ b/client/src/variants/Chakart.js @@ -4,6 +4,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; export class ChakartRules extends ChessRules { + static get PawnSpecs() { return SuicideRules.PawnSpecs; } @@ -1470,4 +1471,5 @@ export class ChakartRules extends ChessRules { } return notation; } + }; diff --git a/client/src/variants/Checkered1.js b/client/src/variants/Checkered1.js index fd0d68df..71f9b920 100644 --- a/client/src/variants/Checkered1.js +++ b/client/src/variants/Checkered1.js @@ -1,6 +1,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules"; export class Checkered1Rules extends ChessRules { + static board2fen(b) { const checkered_codes = { p: "s", @@ -678,4 +679,5 @@ export class Checkered1Rules extends ChessRules { notation += "=" + move.appear[0].p.toUpperCase(); return notation; } + }; diff --git a/client/src/variants/Checkered2.js b/client/src/variants/Checkered2.js index db288766..9c883f71 100644 --- a/client/src/variants/Checkered2.js +++ b/client/src/variants/Checkered2.js @@ -1,6 +1,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules"; export class Checkered2Rules extends ChessRules { + static board2fen(b) { const checkered_codes = { p: "s", @@ -462,4 +463,5 @@ export class Checkered2Rules extends ChessRules { notation += "=" + move.appear[0].p.toUpperCase(); return notation; } + }; diff --git a/client/src/variants/Checkless.js b/client/src/variants/Checkless.js index b304a483..63c35c66 100644 --- a/client/src/variants/Checkless.js +++ b/client/src/variants/Checkless.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class ChecklessRules extends ChessRules { + // Cannot use super.atLeastOneMove: lead to infinite recursion atLeastOneMove_aux() { const color = this.turn; @@ -45,4 +46,5 @@ export class ChecklessRules extends ChessRules { static get SEARCH_DEPTH() { return 2; } + }; diff --git a/client/src/variants/Circular.js b/client/src/variants/Circular.js index d1f8230b..aa700bb1 100644 --- a/client/src/variants/Circular.js +++ b/client/src/variants/Circular.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { shuffle } from "@/utils/alea"; export class CircularRules extends ChessRules { + static get HasCastle() { return false; } @@ -221,4 +222,5 @@ export class CircularRules extends ChessRules { static get SEARCH_DEPTH() { return 2; } + }; diff --git a/client/src/variants/Clorange.js b/client/src/variants/Clorange.js index e96d8514..d4ec1ef3 100644 --- a/client/src/variants/Clorange.js +++ b/client/src/variants/Clorange.js @@ -2,6 +2,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; import { ArrayFun } from "@/utils/array"; export class ClorangeRules extends ChessRules { + static IsGoodFen(fen) { if (!ChessRules.IsGoodFen(fen)) return false; const fenParsed = V.ParseFen(fen); @@ -314,4 +315,5 @@ export class ClorangeRules extends ChessRules { move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : ""; return piece + "@" + V.CoordsToSquare(move.end); } + }; diff --git a/client/src/variants/Colorbound.js b/client/src/variants/Colorbound.js index 7e0a6222..b806184c 100644 --- a/client/src/variants/Colorbound.js +++ b/client/src/variants/Colorbound.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; export class ColorboundRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -184,93 +185,14 @@ export class ColorboundRules extends ChessRules { ); } - // 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) { @@ -335,4 +257,5 @@ export class ColorboundRules extends ChessRules { static get SEARCH_DEPTH() { return 2; } + }; diff --git a/client/src/variants/Coregal.js b/client/src/variants/Coregal.js index 198cbfb0..cf83f1fc 100644 --- a/client/src/variants/Coregal.js +++ b/client/src/variants/Coregal.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt, sample } from "@/utils/alea"; export class CoregalRules extends ChessRules { + static IsGoodPosition(position) { if (!ChessRules.IsGoodPosition(position)) return false; const rows = position.split("/"); @@ -20,8 +21,7 @@ export class CoregalRules extends ChessRules { 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("/"); @@ -46,6 +46,18 @@ export class CoregalRules extends ChessRules { } } + 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 = []; @@ -164,103 +176,42 @@ export class CoregalRules extends ChessRules { } } - 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; } @@ -341,4 +292,5 @@ export class CoregalRules extends ChessRules { } return super.getNotation(move); } + }; diff --git a/client/src/variants/Coronation.js b/client/src/variants/Coronation.js index 9e12ea10..5077eb50 100644 --- a/client/src/variants/Coronation.js +++ b/client/src/variants/Coronation.js @@ -1,6 +1,7 @@ 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: @@ -47,4 +48,5 @@ export class CoronationRules extends ChessRules { notation += "=Q"; return notation; } + }; diff --git a/client/src/variants/Crazyhouse.js b/client/src/variants/Crazyhouse.js index ee485448..9d5834fb 100644 --- a/client/src/variants/Crazyhouse.js +++ b/client/src/variants/Crazyhouse.js @@ -2,6 +2,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; import { ArrayFun } from "@/utils/array"; export class CrazyhouseRules extends ChessRules { + static IsGoodFen(fen) { if (!ChessRules.IsGoodFen(fen)) return false; const fenParsed = V.ParseFen(fen); @@ -253,4 +254,5 @@ export class CrazyhouseRules extends ChessRules { move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : ""; return piece + "@" + V.CoordsToSquare(move.end); } + }; diff --git a/client/src/variants/Cylinder.js b/client/src/variants/Cylinder.js index 2324bdcf..8b68e9ce 100644 --- a/client/src/variants/Cylinder.js +++ b/client/src/variants/Cylinder.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; 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; @@ -154,4 +155,5 @@ export class CylinderRules extends ChessRules { k: 1000 }; } + }; diff --git a/client/src/variants/Dark.js b/client/src/variants/Dark.js index 65934e69..fd6d5719 100644 --- a/client/src/variants/Dark.js +++ b/client/src/variants/Dark.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; export class DarkRules extends ChessRules { + // Analyse in Dark mode makes no sense static get CanAnalyze() { return false; @@ -269,4 +270,5 @@ export class DarkRules extends ChessRules { candidates.push(j); return moves[candidates[randInt(candidates.length)]]; } + }; diff --git a/client/src/variants/Diamond.js b/client/src/variants/Diamond.js index 2a3869ed..415a6aa3 100644 --- a/client/src/variants/Diamond.js +++ b/client/src/variants/Diamond.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { shuffle } from "@/utils/alea"; export class DiamondRules extends ChessRules { + static get HasFlags() { return false; } @@ -136,4 +137,5 @@ export class DiamondRules extends ChessRules { } return super.getNotation(move); //all other pieces are orthodox } + }; diff --git a/client/src/variants/Dice.js b/client/src/variants/Dice.js index 682b8d50..262f273d 100644 --- a/client/src/variants/Dice.js +++ b/client/src/variants/Dice.js @@ -2,6 +2,7 @@ import { ChessRules, Move } from "@/base_rules"; import { randInt } from "@/utils/alea"; export class DiceRules extends ChessRules { + static get CanAnalyze() { return false; } @@ -177,4 +178,5 @@ export class DiceRules extends ChessRules { getNotation(move) { return super.getNotation(move) + "/" + move.end.p.toUpperCase(); } + }; diff --git a/client/src/variants/Discoduel.js b/client/src/variants/Discoduel.js index ea522df5..f5735700 100644 --- a/client/src/variants/Discoduel.js +++ b/client/src/variants/Discoduel.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class DiscoduelRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -47,4 +48,5 @@ export class DiscoduelRules extends ChessRules { static get SEARCH_DEPTH() { return 4; } + }; diff --git a/client/src/variants/Doublearmy.js b/client/src/variants/Doublearmy.js index 0fafe96f..1846aa3c 100644 --- a/client/src/variants/Doublearmy.js +++ b/client/src/variants/Doublearmy.js @@ -6,6 +6,7 @@ import { ChessRules } from "@/base_rules"; // ...But the middle king will get captured quickly... export class DoublearmyRules extends ChessRules { + static get COMMONER() { return "c"; } @@ -78,4 +79,5 @@ export class DoublearmyRules extends ChessRules { static get SEARCH_DEPTH() { return 2; } + }; diff --git a/client/src/variants/Doublemove1.js b/client/src/variants/Doublemove1.js index 95d94d7a..261ca5dd 100644 --- a/client/src/variants/Doublemove1.js +++ b/client/src/variants/Doublemove1.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class Doublemove1Rules extends ChessRules { + static IsGoodEnpassant(enpassant) { const squares = enpassant.split(","); if (squares.length > 2) return false; @@ -273,4 +274,5 @@ export class Doublemove1Rules extends ChessRules { } return doubleMove; } + }; diff --git a/client/src/variants/Doublemove2.js b/client/src/variants/Doublemove2.js index f9d5e4f1..287f9491 100644 --- a/client/src/variants/Doublemove2.js +++ b/client/src/variants/Doublemove2.js @@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules"; import { randInt } from "@/utils/alea"; export class Doublemove2Rules extends ChessRules { + static IsGoodEnpassant(enpassant) { const squares = enpassant.split(","); if (squares.length > 2) return false; @@ -234,4 +235,5 @@ export class Doublemove2Rules extends ChessRules { // TODO: not always the best move played (why ???) return doubleMove; } + }; diff --git a/client/src/variants/Dynamo.js b/client/src/variants/Dynamo.js index dbc19c21..67065d7c 100644 --- a/client/src/variants/Dynamo.js +++ b/client/src/variants/Dynamo.js @@ -2,6 +2,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules"; 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; @@ -873,4 +874,5 @@ export class DynamoRules extends ChessRules { return initialSquare + "R"; return move.appear[0].p.toUpperCase() + initialSquare + finalSquare; } + }; diff --git a/client/src/variants/Eightpieces.js b/client/src/variants/Eightpieces.js index 583b1c5a..3e155009 100644 --- a/client/src/variants/Eightpieces.js +++ b/client/src/variants/Eightpieces.js @@ -3,6 +3,7 @@ import { randInt } from "@/utils/alea"; import { ChessRules, PiPo, Move } from "@/base_rules"; export class EightpiecesRules extends ChessRules { + static get JAILER() { return "j"; } @@ -1194,4 +1195,5 @@ export class EightpiecesRules extends ChessRules { } return notation; } + }; diff --git a/client/src/variants/Enpassant.js b/client/src/variants/Enpassant.js index 4c6ca67d..cb218306 100644 --- a/client/src/variants/Enpassant.js +++ b/client/src/variants/Enpassant.js @@ -1,6 +1,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; export class EnpassantRules extends ChessRules { + static IsGoodEnpassant(enpassant) { if (enpassant != "-") { const squares = enpassant.split(","); @@ -204,4 +205,5 @@ export class EnpassantRules extends ChessRules { k: 1000 }; } + }; diff --git a/client/src/variants/Evolution.js b/client/src/variants/Evolution.js new file mode 100644 index 00000000..5250089e --- /dev/null +++ b/client/src/variants/Evolution.js @@ -0,0 +1,34 @@ +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; + } + +}; diff --git a/client/src/variants/Extinction.js b/client/src/variants/Extinction.js index db8abc06..e42e2941 100644 --- a/client/src/variants/Extinction.js +++ b/client/src/variants/Extinction.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class ExtinctionRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -113,4 +114,5 @@ export class ExtinctionRules extends ChessRules { } return super.evalPosition(); } + }; diff --git a/client/src/variants/Football.js b/client/src/variants/Football.js index 1ccb2a4d..bde38e53 100644 --- a/client/src/variants/Football.js +++ b/client/src/variants/Football.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { shuffle } from "@/utils/alea"; export class FootballRules extends ChessRules { + static get HasFlags() { return false; } @@ -112,4 +113,5 @@ export class FootballRules extends ChessRules { " w 0 -" ); } + }; diff --git a/client/src/variants/Forward.js b/client/src/variants/Forward.js index 8f2fc895..e0a0f9ba 100644 --- a/client/src/variants/Forward.js +++ b/client/src/variants/Forward.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class ForwardRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -26,7 +27,6 @@ export class ForwardRules extends ChessRules { } 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("/"); @@ -38,12 +38,10 @@ export class ForwardRules extends ChessRules { 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); @@ -140,4 +138,5 @@ export class ForwardRules extends ChessRules { ChessRules.VALUES ); } + }; diff --git a/client/src/variants/Freecapture.js b/client/src/variants/Freecapture.js index 19098b6d..5fe93ea4 100644 --- a/client/src/variants/Freecapture.js +++ b/client/src/variants/Freecapture.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class FreecaptureRules extends ChessRules { + canTake() { // Can capture both colors: return true; @@ -9,4 +10,5 @@ export class FreecaptureRules extends ChessRules { static get SEARCH_DEPTH() { return 2; } + }; diff --git a/client/src/variants/Grand.js b/client/src/variants/Grand.js index 14c0cc96..398a96cc 100644 --- a/client/src/variants/Grand.js +++ b/client/src/variants/Grand.js @@ -5,6 +5,7 @@ import { randInt } from "@/utils/alea"; // 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); @@ -391,4 +392,5 @@ export class GrandRules extends ChessRules { " w 0 " + flags + " - 00000000000000" ); } + }; diff --git a/client/src/variants/Grasshopper.js b/client/src/variants/Grasshopper.js index c47ac2b1..90411d14 100644 --- a/client/src/variants/Grasshopper.js +++ b/client/src/variants/Grasshopper.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; export class GrasshopperRules extends ChessRules { + static get HasEnpassant() { return false; } @@ -108,4 +109,5 @@ export class GrasshopperRules extends ChessRules { "/gggggggg/pppppppp/8/8/PPPPPPPP/GGGGGGGG/" ); } + }; diff --git a/client/src/variants/Gridolina.js b/client/src/variants/Gridolina.js index 797190cd..6efff14e 100644 --- a/client/src/variants/Gridolina.js +++ b/client/src/variants/Gridolina.js @@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules"; import { BerolinaRules } from "@/variants/Berolina"; export class GridolinaRules extends BerolinaRules { + static get Lines() { return [ [[2, 0], [2, 8]], @@ -54,4 +55,5 @@ export class GridolinaRules extends BerolinaRules { } return false; } + }; diff --git a/client/src/variants/Hamilton.js b/client/src/variants/Hamilton.js index 69dd9e5c..88f67139 100644 --- a/client/src/variants/Hamilton.js +++ b/client/src/variants/Hamilton.js @@ -2,6 +2,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules"; import { randInt } from "@/utils/alea"; export class HamiltonRules extends ChessRules { + static get HasFlags() { return false; } @@ -158,4 +159,5 @@ export class HamiltonRules extends ChessRules { // First game move: return "N@" + V.CoordsToSquare(move.end); } + }; diff --git a/client/src/variants/Hidden.js b/client/src/variants/Hidden.js index 2d44df5c..280062c3 100644 --- a/client/src/variants/Hidden.js +++ b/client/src/variants/Hidden.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; export class HiddenRules extends ChessRules { + static get HasFlags() { return false; } @@ -327,4 +328,5 @@ export class HiddenRules extends ChessRules { finalSquare ); } + }; diff --git a/client/src/variants/Hiddenqueen.js b/client/src/variants/Hiddenqueen.js index f1607a6a..0cf09677 100644 --- a/client/src/variants/Hiddenqueen.js +++ b/client/src/variants/Hiddenqueen.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; export class HiddenqueenRules extends ChessRules { + // Analyse in Hiddenqueen mode makes no sense static get CanAnalyze() { return false; @@ -230,4 +231,5 @@ export class HiddenqueenRules extends ChessRules { else notation = finalSquare; return notation; } + }; diff --git a/client/src/variants/Horde.js b/client/src/variants/Horde.js index c436811b..33157c94 100644 --- a/client/src/variants/Horde.js +++ b/client/src/variants/Horde.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class HordeRules extends ChessRules { + static get HasFlags() { return false; } @@ -80,4 +81,5 @@ export class HordeRules extends ChessRules { // From black side, just run usual checks: return super.getCurrentScore(); } + }; diff --git a/client/src/variants/Interweave.js b/client/src/variants/Interweave.js index 973797d6..1a9cd94c 100644 --- a/client/src/variants/Interweave.js +++ b/client/src/variants/Interweave.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt, shuffle } from "@/utils/alea"; export class InterweaveRules extends ChessRules { + static get HasFlags() { return false; } @@ -639,4 +640,5 @@ export class InterweaveRules extends ChessRules { if (move.vanish.length >= 2) notation += "X"; return notation; } + }; diff --git a/client/src/variants/Kinglet.js b/client/src/variants/Kinglet.js index 518069f8..81c81607 100644 --- a/client/src/variants/Kinglet.js +++ b/client/src/variants/Kinglet.js @@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules"; import { SuicideRules } from "@/variants/Suicide"; export class KingletRules extends ChessRules { + static get HasFlags() { return false; } @@ -84,4 +85,5 @@ export class KingletRules extends ChessRules { k: 4 }; } + }; diff --git a/client/src/variants/Knightmate.js b/client/src/variants/Knightmate.js index 593fcc09..bc886c12 100644 --- a/client/src/variants/Knightmate.js +++ b/client/src/variants/Knightmate.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; export class KnightmateRules extends ChessRules { + static get COMMONER() { return "c"; } @@ -82,4 +83,5 @@ export class KnightmateRules extends ChessRules { k: 1000 }; } + }; diff --git a/client/src/variants/Knightpawns.js b/client/src/variants/Knightpawns.js index 96476535..c8c1ceaa 100644 --- a/client/src/variants/Knightpawns.js +++ b/client/src/variants/Knightpawns.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class KnightpawnsRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -48,4 +49,5 @@ export class KnightpawnsRules extends ChessRules { postPlay() {} postUndo() {} + }; diff --git a/client/src/variants/Knightrelay1.js b/client/src/variants/Knightrelay1.js index 626519cd..1e7bfabb 100644 --- a/client/src/variants/Knightrelay1.js +++ b/client/src/variants/Knightrelay1.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class Knightrelay1Rules extends ChessRules { + static get HasEnpassant() { return false; } @@ -139,4 +140,5 @@ export class Knightrelay1Rules extends ChessRules { return notation; } + }; diff --git a/client/src/variants/Knightrelay2.js b/client/src/variants/Knightrelay2.js index 1827accb..a8a796e8 100644 --- a/client/src/variants/Knightrelay2.js +++ b/client/src/variants/Knightrelay2.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class Knightrelay2Rules extends ChessRules { + getPotentialMovesFrom([x, y]) { let moves = super.getPotentialMovesFrom([x, y]); @@ -120,4 +121,5 @@ export class Knightrelay2Rules extends ChessRules { return notation; } + }; diff --git a/client/src/variants/Koopa.js b/client/src/variants/Koopa.js index 57d19b92..a624eebb 100644 --- a/client/src/variants/Koopa.js +++ b/client/src/variants/Koopa.js @@ -1,6 +1,7 @@ import { ChessRules, PiPo } from "@/base_rules"; export class KoopaRules extends ChessRules { + static get HasEnpassant() { return false; } @@ -58,7 +59,6 @@ export class KoopaRules extends ChessRules { // 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("/"); @@ -70,12 +70,10 @@ export class KoopaRules extends ChessRules { 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); @@ -221,7 +219,7 @@ export class KoopaRules extends ChessRules { sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep" - ).concat(super.getCastleMoves(sq, true, ['r'])) + ).concat(super.getCastleMoves(sq, null, true, ['r'])) ); } @@ -369,4 +367,5 @@ export class KoopaRules extends ChessRules { } return notation; } + }; diff --git a/client/src/variants/Koth.js b/client/src/variants/Koth.js index 42ffe7bb..38e10108 100644 --- a/client/src/variants/Koth.js +++ b/client/src/variants/Koth.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class KothRules extends ChessRules { + static get Lines() { return [ [[3, 3], [3, 5]], @@ -39,4 +40,5 @@ export class KothRules extends ChessRules { ) / 2 ); } + }; diff --git a/client/src/variants/Losers.js b/client/src/variants/Losers.js index 3ac46c2f..7bd5e541 100644 --- a/client/src/variants/Losers.js +++ b/client/src/variants/Losers.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; 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); @@ -75,4 +76,5 @@ console.log(this.atLeastOneCapture()); // Less material is better (more subtle in fact but...) return -super.evalPosition(); } + }; diff --git a/client/src/variants/Madhouse.js b/client/src/variants/Madhouse.js index c157437d..5f90904e 100644 --- a/client/src/variants/Madhouse.js +++ b/client/src/variants/Madhouse.js @@ -2,6 +2,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules"; 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. @@ -254,4 +255,5 @@ export class MadhouseRules extends ChessRules { move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : ""; return piece + "@" + V.CoordsToSquare(move.end); } + }; diff --git a/client/src/variants/Madrasi.js b/client/src/variants/Madrasi.js index ed2a8791..f0db5107 100644 --- a/client/src/variants/Madrasi.js +++ b/client/src/variants/Madrasi.js @@ -1,6 +1,7 @@ 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]); @@ -61,4 +62,5 @@ export class MadrasiRules extends ChessRules { // Connected kings paralyze each other return false; } + }; diff --git a/client/src/variants/Magnetic.js b/client/src/variants/Magnetic.js index 9fc4180d..dc228ce1 100644 --- a/client/src/variants/Magnetic.js +++ b/client/src/variants/Magnetic.js @@ -1,6 +1,7 @@ import { ChessRules, PiPo } from "@/base_rules"; export class MagneticRules extends ChessRules { + static get HasEnpassant() { return false; } @@ -210,4 +211,5 @@ export class MagneticRules extends ChessRules { static get SEARCH_DEPTH() { return 2; } + }; diff --git a/client/src/variants/Makruk.js b/client/src/variants/Makruk.js index 6205f230..c7dd3430 100644 --- a/client/src/variants/Makruk.js +++ b/client/src/variants/Makruk.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt, shuffle } from "@/utils/alea"; export class MakrukRules extends ChessRules { + static get HasFlags() { return false; } @@ -136,4 +137,5 @@ export class MakrukRules extends ChessRules { k: 1000 }; } + }; diff --git a/client/src/variants/Maxima.js b/client/src/variants/Maxima.js index e8b15d25..05df0bd9 100644 --- a/client/src/variants/Maxima.js +++ b/client/src/variants/Maxima.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { shuffle } from "@/utils/alea"; export class MaximaRules extends ChessRules { + static get HasFlags() { return false; } @@ -827,4 +828,5 @@ export class MaximaRules extends ChessRules { if (move.vanish.length > 1 && move.appear[0].p != V.KING) notation += "X"; return notation; } + }; diff --git a/client/src/variants/Minishogi.js b/client/src/variants/Minishogi.js index afeaa57e..d7bc0d71 100644 --- a/client/src/variants/Minishogi.js +++ b/client/src/variants/Minishogi.js @@ -2,6 +2,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; import { ShogiRules } from "@/variants/Shogi"; export class MinishogiRules extends ShogiRules { + static IsGoodFen(fen) { if (!ChessRules.IsGoodFen(fen)) return false; const fenParsed = V.ParseFen(fen); @@ -168,4 +169,5 @@ export class MinishogiRules extends ShogiRules { static get SEARCH_DEPTH() { return 3; } + }; diff --git a/client/src/variants/Monochrome.js b/client/src/variants/Monochrome.js index 2d5a912f..22242ed2 100644 --- a/client/src/variants/Monochrome.js +++ b/client/src/variants/Monochrome.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class MonochromeRules extends ChessRules { + static get HasEnpassant() { // Pawns would be on the same side return false; @@ -217,4 +218,5 @@ export class MonochromeRules extends ChessRules { } return notation; } + }; diff --git a/client/src/variants/Monster.js b/client/src/variants/Monster.js index 6e3e10c5..cb29adae 100644 --- a/client/src/variants/Monster.js +++ b/client/src/variants/Monster.js @@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules"; import { randInt } from "@/utils/alea"; export class MonsterRules extends ChessRules { + static IsGoodFlags(flags) { // Only black can castle return !!flags.match(/^[a-z]{2,2}$/); @@ -214,4 +215,5 @@ export class MonsterRules extends ChessRules { const color = this.turn; return (color == 'w' ? getBestWhiteMove() : getBestBlackMove()); } + }; diff --git a/client/src/variants/Omega.js b/client/src/variants/Omega.js index 355960b7..289c05b3 100644 --- a/client/src/variants/Omega.js +++ b/client/src/variants/Omega.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; export class OmegaRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -331,102 +332,12 @@ export class OmegaRules extends ChessRules { 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) { @@ -506,4 +417,5 @@ export class OmegaRules extends ChessRules { } return evaluation; } + }; diff --git a/client/src/variants/Orda.js b/client/src/variants/Orda.js index 1ff4898a..308ae2f4 100644 --- a/client/src/variants/Orda.js +++ b/client/src/variants/Orda.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; export class OrdaRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -321,4 +322,5 @@ export class OrdaRules extends ChessRules { } ); } + }; diff --git a/client/src/variants/Ordamirror.js b/client/src/variants/Ordamirror.js index d79d8c5f..8176b957 100644 --- a/client/src/variants/Ordamirror.js +++ b/client/src/variants/Ordamirror.js @@ -4,6 +4,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; export class OrdamirrorRules extends OrdaRules { + static get PawnSpecs() { return Object.assign( {}, @@ -145,4 +146,5 @@ export class OrdamirrorRules extends OrdaRules { k: 1000 }; } + }; diff --git a/client/src/variants/Pacifist1.js b/client/src/variants/Pacifist1.js index 43c81d71..37d0521f 100644 --- a/client/src/variants/Pacifist1.js +++ b/client/src/variants/Pacifist1.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class Pacifist1Rules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -36,30 +37,8 @@ export class Pacifist1Rules extends ChessRules { 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]) { @@ -260,4 +239,5 @@ export class Pacifist1Rules extends ChessRules { static get SEARCH_DEPTH() { return 1; } + }; diff --git a/client/src/variants/Pacifist2.js b/client/src/variants/Pacifist2.js index e8128024..78d4ec84 100644 --- a/client/src/variants/Pacifist2.js +++ b/client/src/variants/Pacifist2.js @@ -1,6 +1,7 @@ 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]) { @@ -55,4 +56,5 @@ export class Pacifist2Rules extends Pacifist1Rules { }); return res; } + }; diff --git a/client/src/variants/Parachute.js b/client/src/variants/Parachute.js index 8cd0f9c2..631301c9 100644 --- a/client/src/variants/Parachute.js +++ b/client/src/variants/Parachute.js @@ -1,6 +1,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; export class ParachuteRules extends ChessRules { + static get HasFlags() { return false; } @@ -234,4 +235,5 @@ export class ParachuteRules extends ChessRules { move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : ""; return piece + "@" + V.CoordsToSquare(move.end); } + }; diff --git a/client/src/variants/Pawnmassacre.js b/client/src/variants/Pawnmassacre.js index 45756bce..12ce1d04 100644 --- a/client/src/variants/Pawnmassacre.js +++ b/client/src/variants/Pawnmassacre.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class PawnmassacreRules extends ChessRules { + static get HasFlags() { return false; } @@ -20,4 +21,5 @@ export class PawnmassacreRules extends ChessRules { .concat(bFen.substr(splitIdx)) ); } + }; diff --git a/client/src/variants/Pawns.js b/client/src/variants/Pawns.js index 1dca994d..b3e84ea9 100644 --- a/client/src/variants/Pawns.js +++ b/client/src/variants/Pawns.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class PawnsRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -45,4 +46,5 @@ export class PawnsRules extends ChessRules { static get SEARCH_DEPTH() { return 4; } + }; diff --git a/client/src/variants/Pawnsking.js b/client/src/variants/Pawnsking.js index 76d03efa..bce2fe75 100644 --- a/client/src/variants/Pawnsking.js +++ b/client/src/variants/Pawnsking.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class PawnskingRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -53,4 +54,5 @@ export class PawnskingRules extends ChessRules { static get SEARCH_DEPTH() { return 4; } + }; diff --git a/client/src/variants/Perfect.js b/client/src/variants/Perfect.js index 4f65a0ed..4bd2ac92 100644 --- a/client/src/variants/Perfect.js +++ b/client/src/variants/Perfect.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; export class PerfectRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -200,4 +201,5 @@ export class PerfectRules extends ChessRules { " w 0 " + flags + " -" ); } + }; diff --git a/client/src/variants/Pocketknight.js b/client/src/variants/Pocketknight.js index 6b92a1f5..8756ab9f 100644 --- a/client/src/variants/Pocketknight.js +++ b/client/src/variants/Pocketknight.js @@ -2,6 +2,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules"; 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. @@ -282,4 +283,5 @@ export class PocketknightRules extends ChessRules { // Knight landing: return "N@" + V.CoordsToSquare(move.end); } + }; diff --git a/client/src/variants/Progressive1.js b/client/src/variants/Progressive1.js index 3023b729..43c7c4db 100644 --- a/client/src/variants/Progressive1.js +++ b/client/src/variants/Progressive1.js @@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules"; import { randInt } from "@/utils/alea"; export class Progressive1Rules extends ChessRules { + static get HasEnpassant() { return false; } @@ -108,4 +109,5 @@ export class Progressive1Rules extends ChessRules { for (let i=res.length - 1; i>= 0; i--) this.undo(res[i]); return res; } + }; diff --git a/client/src/variants/Progressive2.js b/client/src/variants/Progressive2.js index cb9911d3..d0c15e61 100644 --- a/client/src/variants/Progressive2.js +++ b/client/src/variants/Progressive2.js @@ -4,6 +4,7 @@ import { ChessRules } from "@/base_rules"; import { randInt } from "@/utils/alea"; export class Progressive2Rules extends Progressive1Rules { + static get PawnSpecs() { return Object.assign( {}, @@ -47,4 +48,5 @@ export class Progressive2Rules extends Progressive1Rules { k: 1000 }; } + }; diff --git a/client/src/variants/Queenpawns.js b/client/src/variants/Queenpawns.js index 8ffe111b..0ea2e45f 100644 --- a/client/src/variants/Queenpawns.js +++ b/client/src/variants/Queenpawns.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class QueenpawnsRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -52,4 +53,5 @@ export class QueenpawnsRules extends ChessRules { static get SEARCH_DEPTH() { return 4; } + }; diff --git a/client/src/variants/Racingkings.js b/client/src/variants/Racingkings.js index 11457485..e898a554 100644 --- a/client/src/variants/Racingkings.js +++ b/client/src/variants/Racingkings.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class RacingkingsRules extends ChessRules { + static get HasFlags() { return false; } @@ -61,4 +62,5 @@ export class RacingkingsRules extends ChessRules { // Ponder with king position: return evaluation/5 + this.kingPos["b"][0] - this.kingPos["w"][0]; } + }; diff --git a/client/src/variants/Rampage.js b/client/src/variants/Rampage.js index 79b8b956..12a61a18 100644 --- a/client/src/variants/Rampage.js +++ b/client/src/variants/Rampage.js @@ -1,6 +1,7 @@ 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) => { @@ -84,4 +85,5 @@ export class RampageRules extends ChessRules { static get SEARCH_DEPTH() { return 1; } + }; diff --git a/client/src/variants/Recycle.js b/client/src/variants/Recycle.js index d23a78c2..eb84044e 100644 --- a/client/src/variants/Recycle.js +++ b/client/src/variants/Recycle.js @@ -2,6 +2,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; import { ArrayFun } from "@/utils/array"; export class RecycleRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -227,4 +228,5 @@ export class RecycleRules extends ChessRules { move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : ""; return piece + "@" + V.CoordsToSquare(move.end); } + }; diff --git a/client/src/variants/Rifle.js b/client/src/variants/Rifle.js index 94a6d058..7d71a98a 100644 --- a/client/src/variants/Rifle.js +++ b/client/src/variants/Rifle.js @@ -1,6 +1,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; export class RifleRules extends ChessRules { + getBasicMove([sx, sy], [ex, ey], tr) { let mv = new Move({ appear: [], @@ -71,4 +72,5 @@ export class RifleRules extends ChessRules { static get SEARCH_DEPTH() { return 2; } + }; diff --git a/client/src/variants/Rococo.js b/client/src/variants/Rococo.js index e6480a8c..66d9615a 100644 --- a/client/src/variants/Rococo.js +++ b/client/src/variants/Rococo.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { shuffle } from "@/utils/alea"; export class RococoRules extends ChessRules { + static get HasFlags() { return false; } @@ -721,4 +722,5 @@ export class RococoRules extends ChessRules { if (move.vanish.length > 1 && move.appear[0].p != V.KING) notation += "X"; return notation; } + }; diff --git a/client/src/variants/Rookpawns.js b/client/src/variants/Rookpawns.js index 2d0c3a85..1daae907 100644 --- a/client/src/variants/Rookpawns.js +++ b/client/src/variants/Rookpawns.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class RookpawnsRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -52,4 +53,5 @@ export class RookpawnsRules extends ChessRules { static get SEARCH_DEPTH() { return 4; } + }; diff --git a/client/src/variants/Royalrace.js b/client/src/variants/Royalrace.js index 5232cc94..5d5992c8 100644 --- a/client/src/variants/Royalrace.js +++ b/client/src/variants/Royalrace.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt, shuffle } from "@/utils/alea"; export class RoyalraceRules extends ChessRules { + static get HasFlags() { return false; } @@ -219,4 +220,5 @@ export class RoyalraceRules extends ChessRules { V.CoordsToSquare(move.end) ); } + }; diff --git a/client/src/variants/Rugby.js b/client/src/variants/Rugby.js index 209def87..bbe25cb7 100644 --- a/client/src/variants/Rugby.js +++ b/client/src/variants/Rugby.js @@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules"; import { ArrayFun } from "@/utils/array"; export class RugbyRules extends ChessRules { + static get HasFlags() { return false; } @@ -61,4 +62,5 @@ export class RugbyRules extends ChessRules { // Stalemate (will probably never happen) return "1/2"; } + }; diff --git a/client/src/variants/Schess.js b/client/src/variants/Schess.js index 95118982..bdc93275 100644 --- a/client/src/variants/Schess.js +++ b/client/src/variants/Schess.js @@ -1,6 +1,7 @@ import { ChessRules, PiPo } from "@/base_rules"; export class SchessRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -349,4 +350,5 @@ export class SchessRules extends ChessRules { } return super.getNotation(move); } + }; diff --git a/client/src/variants/Shako.js b/client/src/variants/Shako.js index ee694d01..c3db2794 100644 --- a/client/src/variants/Shako.js +++ b/client/src/variants/Shako.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { randInt, sample } from "@/utils/alea"; export class ShakoRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -96,97 +97,11 @@ export class ShakoRules extends ChessRules { } 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) { @@ -337,4 +252,5 @@ export class ShakoRules extends ChessRules { " w 0 " + flags + " -" ); } + }; diff --git a/client/src/variants/Shatranj.js b/client/src/variants/Shatranj.js index 7aab305e..0569f713 100644 --- a/client/src/variants/Shatranj.js +++ b/client/src/variants/Shatranj.js @@ -1,6 +1,7 @@ import { ChessRules } from "@/base_rules"; export class ShatranjRules extends ChessRules { + static get HasFlags() { return false; } @@ -151,4 +152,5 @@ export class ShatranjRules extends ChessRules { k: 1000 }; } + }; diff --git a/client/src/variants/Shogi.js b/client/src/variants/Shogi.js index 6c522dc6..e8b17918 100644 --- a/client/src/variants/Shogi.js +++ b/client/src/variants/Shogi.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { sample, shuffle } from "@/utils/alea"; export class ShogiRules extends ChessRules { + static get HasFlags() { return false; } @@ -656,4 +657,5 @@ export class ShogiRules extends ChessRules { ) ); } + }; diff --git a/client/src/variants/Sittuyin.js b/client/src/variants/Sittuyin.js index 0ddf74c9..5efd8cda 100644 --- a/client/src/variants/Sittuyin.js +++ b/client/src/variants/Sittuyin.js @@ -2,6 +2,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules"; import { randInt } from "@/utils/alea"; export class SittuyinRules extends ChessRules { + static get HasFlags() { return false; } @@ -402,4 +403,5 @@ export class SittuyinRules extends ChessRules { } return super.getNotation(move); } + }; diff --git a/client/src/variants/Suction.js b/client/src/variants/Suction.js index 7fff939e..d12f83d2 100644 --- a/client/src/variants/Suction.js +++ b/client/src/variants/Suction.js @@ -2,6 +2,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; import { SuicideRules } from "@/variants/Suicide"; export class SuctionRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -235,4 +236,5 @@ export class SuctionRules extends ChessRules { finalSquare ); } + }; diff --git a/client/src/variants/Suicide.js b/client/src/variants/Suicide.js index e3630bb4..05fb19c3 100644 --- a/client/src/variants/Suicide.js +++ b/client/src/variants/Suicide.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { shuffle } from "@/utils/alea"; export class SuicideRules extends ChessRules { + static get HasFlags() { return false; } @@ -165,4 +166,5 @@ export class SuicideRules extends ChessRules { " w 0 -" ); } + }; diff --git a/client/src/variants/Swap.js b/client/src/variants/Swap.js index 4d97ae93..9c3407b7 100644 --- a/client/src/variants/Swap.js +++ b/client/src/variants/Swap.js @@ -2,6 +2,7 @@ import { ChessRules, PiPo } from "@/base_rules"; import { randInt } from "@/utils/alea"; export class SwapRules extends ChessRules { + setOtherVariables(fen) { super.setOtherVariables(fen); // Local stack of swaps @@ -341,4 +342,5 @@ export class SwapRules extends ChessRules { // Swap return "S" + V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end); } + }; diff --git a/client/src/variants/Switching.js b/client/src/variants/Switching.js index b940d0a6..8cb3141d 100644 --- a/client/src/variants/Switching.js +++ b/client/src/variants/Switching.js @@ -1,6 +1,7 @@ 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 @@ -123,4 +124,5 @@ export class SwitchingRules extends ChessRules { // Switch return "S" + V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end); } -} + +}; diff --git a/client/src/variants/Synchrone.js b/client/src/variants/Synchrone.js index fea63378..365dd9aa 100644 --- a/client/src/variants/Synchrone.js +++ b/client/src/variants/Synchrone.js @@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules"; import { randInt } from "@/utils/alea"; export class SynchroneRules extends ChessRules { + static get CanAnalyze() { return false; } @@ -78,7 +79,6 @@ export class SynchroneRules extends ChessRules { : 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++) { @@ -516,4 +516,5 @@ export class SynchroneRules extends ChessRules { V.CoordsToSquare(move.end) ); } + }; diff --git a/client/src/variants/Takenmake.js b/client/src/variants/Takenmake.js index 6cea420c..5c0ac08e 100644 --- a/client/src/variants/Takenmake.js +++ b/client/src/variants/Takenmake.js @@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules"; import { randInt } from "@/utils/alea"; export class TakenmakeRules extends ChessRules { + setOtherVariables(fen) { super.setOtherVariables(fen); // Stack of "last move" only for intermediate captures @@ -197,4 +198,5 @@ export class TakenmakeRules extends ChessRules { delete moves[mIdx]["next"]; return [moves[mIdx], move2]; } + }; diff --git a/client/src/variants/Teleport.js b/client/src/variants/Teleport.js index eadaf13b..52c7fd6e 100644 --- a/client/src/variants/Teleport.js +++ b/client/src/variants/Teleport.js @@ -2,6 +2,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules"; 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. @@ -319,4 +320,5 @@ export class TeleportRules extends ChessRules { move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : ""; return piece + "@" + V.CoordsToSquare(move.end); } + }; diff --git a/client/src/variants/Tencubed.js b/client/src/variants/Tencubed.js index 33166254..af48977e 100644 --- a/client/src/variants/Tencubed.js +++ b/client/src/variants/Tencubed.js @@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array"; import { shuffle } from "@/utils/alea"; export class TencubedRules extends ChessRules { + static get PawnSpecs() { return Object.assign( {}, @@ -242,4 +243,5 @@ export class TencubedRules extends ChessRules { { c: 4, w: 3, a: 6, m: 8 } ); } + }; diff --git a/client/src/variants/Threechecks.js b/client/src/variants/Threechecks.js index 05250596..c297d0a4 100644 --- a/client/src/variants/Threechecks.js +++ b/client/src/variants/Threechecks.js @@ -1,6 +1,7 @@ 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}$/); @@ -64,4 +65,5 @@ export class ThreechecksRules extends ChessRules { // Take number of checks into account return baseEval/5 - this.checkFlags["w"] + this.checkFlags["b"]; } + }; diff --git a/client/src/variants/Titan.js b/client/src/variants/Titan.js index 2ed54992..aadc3e70 100644 --- a/client/src/variants/Titan.js +++ b/client/src/variants/Titan.js @@ -1,13 +1,15 @@ -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) { @@ -29,8 +31,6 @@ export class TitanRules extends ChessRules { } } - // 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() { @@ -48,6 +48,10 @@ export class TitanRules extends ChessRules { ]; } + getPpath(b) { + return "Titan/" + b; + } + // Decode above notation into additional piece getExtraPiece(symbol) { if (['a','j','m','s','u'].includes(symbol)) @@ -55,18 +59,98 @@ export class TitanRules extends ChessRules { 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, @@ -77,9 +161,129 @@ export class TitanRules extends ChessRules { ); }); } + 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; + } + }; diff --git a/client/src/variants/Twokings.js b/client/src/variants/Twokings.js index d56d0951..a5bd76af 100644 --- a/client/src/variants/Twokings.js +++ b/client/src/variants/Twokings.js @@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules"; import { CoregalRules } from "@/variants/Coregal"; export class TwokingsRules extends CoregalRules { + static get PawnSpecs() { return Object.assign( {}, @@ -101,6 +102,11 @@ export class TwokingsRules extends CoregalRules { ); } + 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