From: Benjamin Auder Date: Sun, 2 Feb 2020 01:29:39 +0000 (+0100) Subject: Check variants. All OK except Dark (bug), Checkered (missing internal moves stack... X-Git-Url: https://git.auder.net/variants/Chakart/%7B%7B%20asset%28%27mixstore/images/%7B%7B%20targetUrl%20%7D%7D?a=commitdiff_plain;h=0c3fe8a6c3e02af46e0bc646b40c1a0c420f9dcd;p=vchess.git Check variants. All OK except Dark (bug), Checkered (missing internal moves stack), Marseille (old bot using checkGameEnd()) --- diff --git a/client/src/base_rules.js b/client/src/base_rules.js index 169fb2d9..8cf86a86 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -2,7 +2,7 @@ // Variants generally inherit from it, and modify some parts. import { ArrayFun } from "@/utils/array"; -import { randInt, sample, shuffle } from "@/utils/alea"; +import { randInt, shuffle } from "@/utils/alea"; export const PiPo = class PiPo //Piece+Position { @@ -1198,7 +1198,7 @@ export const ChessRules = class ChessRules let candidates = [0]; //indices of candidates moves for (let j=1; j= 3 && Math.abs(moves1[0].eval) < V.THRESHOLD_MATE) diff --git a/client/src/variants/Alice.js b/client/src/variants/Alice.js index e80e13b6..e81d3fc8 100644 --- a/client/src/variants/Alice.js +++ b/client/src/variants/Alice.js @@ -293,9 +293,12 @@ export const VariantRules = class AliceRules extends ChessRules this.kingPos[c] = [move.start.x, move.start.y]; } - checkGameEnd() + getCurrentScore() { - const pieces = Object.keys(V.ALICE_CODES); + if (this.atLeastOneMove()) // game not over + return "*"; + + const pieces = Object.keys(V.ALICE_CODES); const color = this.turn; const kp = this.kingPos[color]; const mirrorSide = (pieces.includes(this.getPiece(kp[0],kp[1])) ? 1 : 2); diff --git a/client/src/variants/Antiking.js b/client/src/variants/Antiking.js index 22fd2b40..7b57e74a 100644 --- a/client/src/variants/Antiking.js +++ b/client/src/variants/Antiking.js @@ -1,4 +1,8 @@ -class AntikingRules extends ChessRules +import { ChessRules } from "@/base_rules"; +import { ArrayFun} from "@/utils/array"; +import { randInt } from "@/utils/alea"; + +export const VariantRules = class AntikingRules extends ChessRules { static getPpath(b) { @@ -125,9 +129,12 @@ class AntikingRules extends ChessRules this.antikingPos[c] = [move.start.x, move.start.y]; } - checkGameEnd() + getCurrentScore() { - const color = this.turn; + if (this.atLeastOneMove()) // game not over + return "*"; + + const color = this.turn; const oppCol = V.GetOppCol(color); if (!this.isAttacked(this.kingPos[color], [oppCol]) && this.isAttacked(this.antikingPos[color], [oppCol])) @@ -150,7 +157,7 @@ class AntikingRules extends ChessRules let antikingPos = { "w": -1, "b": -1 }; for (let c of ["w","b"]) { - let positions = range(8); + let positions = ArrayFun.range(8); // Get random squares for bishops, but avoid corners; because, // if an antiking blocks a cornered bishop, it can never be checkmated @@ -195,6 +202,6 @@ class AntikingRules extends ChessRules return pieces["b"].join("") + "/" + ranks23_black + "/8/8/" + ranks23_white + "/" + pieces["w"].join("").toUpperCase() + - " w 1111 -"; + " w 0 1111 -"; } } diff --git a/client/src/variants/Atomic.js b/client/src/variants/Atomic.js index 622e9d00..e1b1c161 100644 --- a/client/src/variants/Atomic.js +++ b/client/src/variants/Atomic.js @@ -1,6 +1,146 @@ -export const V = class AtomicRules { - show() { - console.log("AtomicRules"); +import { ChessRules, PiPo } from "@/base_rules"; + +export const VariantRules = class AtomicRules extends ChessRules +{ + getPotentialMovesFrom([x,y]) + { + let moves = super.getPotentialMovesFrom([x,y]); + + // Handle explosions + moves.forEach(m => { + if (m.vanish.length > 1 && m.appear.length <= 1) //avoid castles + { + // Explosion! TODO(?): drop moves which explode our king here + let steps = [ [-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1] ]; + for (let step of steps) + { + let x = m.end.x + step[0]; + let y = m.end.y + step[1]; + if (V.OnBoard(x,y) && this.board[x][y] != V.EMPTY + && this.getPiece(x,y) != V.PAWN) + { + m.vanish.push( + new PiPo({p:this.getPiece(x,y),c:this.getColor(x,y),x:x,y:y})); + } + } + m.end = {x:m.appear[0].x, y:m.appear[0].y}; + m.appear.pop(); //Nothin appears in this case + } + }); + + return moves; + } + + getPotentialKingMoves([x,y]) + { + // King cannot capture: + let moves = []; + const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]); + for (let step of steps) + { + const i = x + step[0]; + const j = y + step[1]; + if (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY) + moves.push(this.getBasicMove([x,y], [i,j])); + } + return moves.concat(this.getCastleMoves([x,y])); + } + + isAttacked(sq, colors) + { + if (this.getPiece(sq[0],sq[1]) == V.KING && this.isAttackedByKing(sq, colors)) + return false; //king cannot take... + return (this.isAttackedByPawn(sq, colors) + || this.isAttackedByRook(sq, colors) + || this.isAttackedByKnight(sq, colors) + || this.isAttackedByBishop(sq, colors) + || this.isAttackedByQueen(sq, colors)); + } + + updateVariables(move) + { + super.updateVariables(move); + const color = move.vanish[0].c; + if (move.appear.length == 0) //capture + { + const firstRank = {"w": 7, "b": 0}; + for (let c of ["w","b"]) + { + // Did we explode king of color c ? (TODO: remove move earlier) + if (Math.abs(this.kingPos[c][0]-move.end.x) <= 1 + && Math.abs(this.kingPos[c][1]-move.end.y) <= 1) + { + this.kingPos[c] = [-1,-1]; + this.castleFlags[c] = [false,false]; + } + else + { + // Now check if init rook(s) exploded + if (Math.abs(move.end.x-firstRank[c]) <= 1) + { + if (Math.abs(move.end.y-this.INIT_COL_ROOK[c][0]) <= 1) + this.castleFlags[c][0] = false; + if (Math.abs(move.end.y-this.INIT_COL_ROOK[c][1]) <= 1) + this.castleFlags[c][1] = false; + } + } + } + } + } + + unupdateVariables(move) + { + super.unupdateVariables(move); + const c = move.vanish[0].c; + const oppCol = V.GetOppCol(c); + if ([this.kingPos[c][0],this.kingPos[oppCol][0]].some(e => { return e < 0; })) + { + // There is a chance that last move blowed some king away.. + for (let psq of move.vanish) + { + if (psq.p == 'k') + this.kingPos[psq.c==c ? c : oppCol] = [psq.x, psq.y]; + } + } + } + + underCheck(color) + { + const oppCol = V.GetOppCol(color); + let res = undefined; + // If our king disappeared, move is not valid + if (this.kingPos[color][0] < 0) + res = true; + // If opponent king disappeared, move is valid + else if (this.kingPos[oppCol][0] < 0) + res = false; + // Otherwise, if we remain under check, move is not valid + else + res = this.isAttacked(this.kingPos[color], [oppCol]); + return res; + } + + getCheckSquares(color) + { + let res = [ ]; + if (this.kingPos[color][0] >= 0 //king might have exploded + && this.isAttacked(this.kingPos[color], [V.GetOppCol(color)])) + { + res = [ JSON.parse(JSON.stringify(this.kingPos[color])) ] + } + return res; + } + + getCurrentScore() + { + const color = this.turn; + const kp = this.kingPos[color]; + if (kp[0] < 0) //king disappeared + return color == "w" ? "0-1" : "1-0"; + if (this.atLeastOneMove()) // game not over + return "*"; + if (!this.isAttacked(kp, [V.GetOppCol(color)])) + return "1/2"; + return color == "w" ? "0-1" : "1-0"; //checkmate } } -//export default V = AtomicRules; diff --git a/client/src/variants/Atomic_OLD.js b/client/src/variants/Atomic_OLD.js deleted file mode 100644 index 1ee5b98f..00000000 --- a/client/src/variants/Atomic_OLD.js +++ /dev/null @@ -1,143 +0,0 @@ -import { ChessRules } from "@/base_rules"; -export const VariantRules = class AtomicRules extends ChessRules -{ - getPotentialMovesFrom([x,y]) - { - let moves = super.getPotentialMovesFrom([x,y]); - - // Handle explosions - moves.forEach(m => { - if (m.vanish.length > 1 && m.appear.length <= 1) //avoid castles - { - // Explosion! TODO(?): drop moves which explode our king here - let steps = [ [-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1] ]; - for (let step of steps) - { - let x = m.end.x + step[0]; - let y = m.end.y + step[1]; - if (V.OnBoard(x,y) && this.board[x][y] != V.EMPTY - && this.getPiece(x,y) != V.PAWN) - { - m.vanish.push( - new PiPo({p:this.getPiece(x,y),c:this.getColor(x,y),x:x,y:y})); - } - } - m.end = {x:m.appear[0].x, y:m.appear[0].y}; - m.appear.pop(); //Nothin appears in this case - } - }); - - return moves; - } - - getPotentialKingMoves([x,y]) - { - // King cannot capture: - let moves = []; - const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]); - for (let step of steps) - { - const i = x + step[0]; - const j = y + step[1]; - if (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY) - moves.push(this.getBasicMove([x,y], [i,j])); - } - return moves.concat(this.getCastleMoves([x,y])); - } - - isAttacked(sq, colors) - { - if (this.getPiece(sq[0],sq[1]) == V.KING && this.isAttackedByKing(sq, colors)) - return false; //king cannot take... - return (this.isAttackedByPawn(sq, colors) - || this.isAttackedByRook(sq, colors) - || this.isAttackedByKnight(sq, colors) - || this.isAttackedByBishop(sq, colors) - || this.isAttackedByQueen(sq, colors)); - } - - updateVariables(move) - { - super.updateVariables(move); - const color = move.vanish[0].c; - if (move.appear.length == 0) //capture - { - const firstRank = {"w": 7, "b": 0}; - for (let c of ["w","b"]) - { - // Did we explode king of color c ? (TODO: remove move earlier) - if (Math.abs(this.kingPos[c][0]-move.end.x) <= 1 - && Math.abs(this.kingPos[c][1]-move.end.y) <= 1) - { - this.kingPos[c] = [-1,-1]; - this.castleFlags[c] = [false,false]; - } - else - { - // Now check if init rook(s) exploded - if (Math.abs(move.end.x-firstRank[c]) <= 1) - { - if (Math.abs(move.end.y-this.INIT_COL_ROOK[c][0]) <= 1) - this.castleFlags[c][0] = false; - if (Math.abs(move.end.y-this.INIT_COL_ROOK[c][1]) <= 1) - this.castleFlags[c][1] = false; - } - } - } - } - } - - unupdateVariables(move) - { - super.unupdateVariables(move); - const c = move.vanish[0].c; - const oppCol = V.GetOppCol(c); - if ([this.kingPos[c][0],this.kingPos[oppCol][0]].some(e => { return e < 0; })) - { - // There is a chance that last move blowed some king away.. - for (let psq of move.vanish) - { - if (psq.p == 'k') - this.kingPos[psq.c==c ? c : oppCol] = [psq.x, psq.y]; - } - } - } - - underCheck(color) - { - const oppCol = V.GetOppCol(color); - let res = undefined; - // If our king disappeared, move is not valid - if (this.kingPos[color][0] < 0) - res = true; - // If opponent king disappeared, move is valid - else if (this.kingPos[oppCol][0] < 0) - res = false; - // Otherwise, if we remain under check, move is not valid - else - res = this.isAttacked(this.kingPos[color], [oppCol]); - return res; - } - - getCheckSquares(color) - { - let res = [ ]; - if (this.kingPos[color][0] >= 0 //king might have exploded - && this.isAttacked(this.kingPos[color], [V.GetOppCol(color)])) - { - res = [ JSON.parse(JSON.stringify(this.kingPos[color])) ] - } - return res; - } - - checkGameEnd() - { - const color = this.turn; - const kp = this.kingPos[color]; - if (kp[0] < 0) //king disappeared - return color == "w" ? "0-1" : "1-0"; - if (!this.isAttacked(kp, [V.GetOppCol(color)])) - return "1/2"; - return color == "w" ? "0-1" : "1-0"; //checkmate - } -} diff --git a/client/src/variants/Baroque.js b/client/src/variants/Baroque.js index ce9e6672..9b5a3cdb 100644 --- a/client/src/variants/Baroque.js +++ b/client/src/variants/Baroque.js @@ -1,4 +1,8 @@ -class BaroqueRules extends ChessRules +import { ChessRules, PiPo, Move } from "@/base_rules"; +import { ArrayFun } from "@/utils/array"; +import { randInt } from "@/utils/alea"; + +export const VariantRules = class BaroqueRules extends ChessRules { static get HasFlags() { return false; } @@ -548,7 +552,7 @@ class BaroqueRules extends ChessRules // Shuffle pieces on first and last rank for (let c of ["w","b"]) { - let positions = range(8); + let positions = ArrayFun.range(8); // Get random squares for every piece, totally freely let randIndex = randInt(8); @@ -592,7 +596,7 @@ class BaroqueRules extends ChessRules return pieces["b"].join("") + "/pppppppp/8/8/8/8/PPPPPPPP/" + pieces["w"].join("").toUpperCase() + - " w"; + " w 0"; } getNotation(move) diff --git a/client/src/variants/Berolina.js b/client/src/variants/Berolina.js index 517a93e5..05152b43 100644 --- a/client/src/variants/Berolina.js +++ b/client/src/variants/Berolina.js @@ -1,4 +1,6 @@ -class BerolinaRules extends ChessRules +import { ChessRules } from "@/base_rules"; + +export const VariantRules = class BerolinaRules extends ChessRules { // En-passant after 2-sq jump getEpSquare(moveOrSquare) diff --git a/client/src/variants/Checkered.js b/client/src/variants/Checkered.js index 1f6750b9..f8176cd3 100644 --- a/client/src/variants/Checkered.js +++ b/client/src/variants/Checkered.js @@ -1,4 +1,9 @@ -class CheckeredRules extends ChessRules +// TODO: to detect oppositeMoves, we need last move --> encoded in FEN +// + local moves stack (for AlphaBeta) + lastMove (in FEN) + +import { ChessRules } from "@/base_rules"; + +export const VariantRules = class CheckeredRules extends ChessRules { static getPpath(b) { @@ -206,9 +211,12 @@ class CheckeredRules extends ChessRules this.pawnFlags[move.start.x==6 ? "w" : "b"][move.start.y] = false; } - checkGameEnd() + getCurrentScore() { - const color = this.turn; + if (this.atLeastOneMove()) // game not over + return "*"; + + const color = this.turn; // Artifically change turn, for checkered pawns this.turn = V.GetOppCol(this.turn); const res = this.isAttacked(this.kingPos[color], [V.GetOppCol(color),'c']) @@ -241,7 +249,7 @@ class CheckeredRules extends ChessRules { const randFen = ChessRules.GenRandInitFen(); // Add 16 pawns flags: - return randFen.replace(" w 1111", " w 11111111111111111111"); + return randFen.replace(" w 0 1111", " w 0 11111111111111111111"); } getFlagsFen() diff --git a/client/src/variants/Crazyhouse.js b/client/src/variants/Crazyhouse.js index 179fffa5..ec903c2f 100644 --- a/client/src/variants/Crazyhouse.js +++ b/client/src/variants/Crazyhouse.js @@ -1,4 +1,7 @@ -class CrazyhouseRules extends ChessRules +import { ChessRules, PiPo, Move } from "@/base_rules"; +import { ArrayFun} from "@/utils/array"; + +export const VariantRules = class CrazyhouseRules extends ChessRules { static IsGoodFen(fen) { @@ -98,7 +101,7 @@ class CrazyhouseRules extends ChessRules [V.QUEEN]: parseInt(fenParsed.reserve[9]), } }; - this.promoted = doubleArray(V.size.x, V.size.y, false); + this.promoted = ArrayFun.init(V.size.x, V.size.y, false); if (fenParsed.promoted != "-") { for (let square of fenParsed.promoted.split(",")) diff --git a/client/src/variants/Dark.js b/client/src/variants/Dark.js index 76e9462e..777aacc8 100644 --- a/client/src/variants/Dark.js +++ b/client/src/variants/Dark.js @@ -1,4 +1,8 @@ -class DarkRules extends ChessRules +import { ChessRules } from "@/base_rules"; +import { ArrayFun} from "@/utils/array"; +import { randInt } from "@/utils/alea"; + +export const VariantRules = class DarkRules extends ChessRules { // Standard rules, in the shadow setOtherVariables(fen) @@ -6,8 +10,8 @@ class DarkRules extends ChessRules super.setOtherVariables(fen); const [sizeX,sizeY] = [V.size.x,V.size.y]; this.enlightened = { - "w": doubleArray(sizeX,sizeY), - "b": doubleArray(sizeX,sizeY) + "w": ArrayFun.init(sizeX,sizeY), + "b": ArrayFun.init(sizeX,sizeY) }; // Setup enlightened: squares reachable by each side // (TODO: one side would be enough ?) @@ -130,10 +134,15 @@ class DarkRules extends ChessRules this.updateEnlightened(); } - checkGameEnd() - { - // No valid move: our king disappeared - return this.turn == "w" ? "0-1" : "1-0"; + getCurrentScore() + { + const color = this.turn; + const kp = this.kingPos[color]; + if (kp[0] < 0) //king disappeared + return (color == "w" ? "0-1" : "1-0"); + if (this.atLeastOneMove()) // game not over + return "*"; + return "1/2"; //no moves but kings still there (seems impossible) } static get THRESHOLD_MATE() @@ -282,6 +291,6 @@ class DarkRules extends ChessRules let candidates = [0]; for (let j=1; j { return this.material[color][p] == 0; })) { - return this.checkGameEnd(); + return (this.turn == "w" ? "0-1" : "1-0"); } return "*"; } - return this.checkGameEnd(); //NOTE: currently unreachable... - } - - checkGameEnd() - { - return (this.turn == "w" ? "0-1" : "1-0"); + return (this.turn == "w" ? "0-1" : "1-0"); //NOTE: currently unreachable... } evalPosition() diff --git a/client/src/variants/Grand.js b/client/src/variants/Grand.js index 2ce85422..6ddfbd7c 100644 --- a/client/src/variants/Grand.js +++ b/client/src/variants/Grand.js @@ -1,6 +1,10 @@ +import { ChessRules } from "@/base_rules"; +import { ArrayFun } from "@/utils/array"; +import { randInt } from "@/utils/alea"; + // NOTE: initial setup differs from the original; see // https://www.chessvariants.com/large.dir/freeling.html -class GrandRules extends ChessRules +export const VariantRules = class GrandRules extends ChessRules { static getPpath(b) { @@ -329,7 +333,7 @@ class GrandRules extends ChessRules // Shuffle pieces on first and last rank for (let c of ["w","b"]) { - let positions = range(10); + let positions = ArrayFun.range(10); // Get random squares for bishops let randIndex = 2 * randInt(5); @@ -384,6 +388,6 @@ class GrandRules extends ChessRules return pieces["b"].join("") + "/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/" + pieces["w"].join("").toUpperCase() + - " w 1111 - 00000000000000"; + " w 0 1111 - 00000000000000"; } } diff --git a/client/src/variants/Losers.js b/client/src/variants/Losers.js index 7c6b4220..86b00f90 100644 --- a/client/src/variants/Losers.js +++ b/client/src/variants/Losers.js @@ -1,4 +1,8 @@ -class LosersRules extends ChessRules +import { ChessRules } from "@/base_rules"; +import { ArrayFun } from "@/utils/array"; +import { randInt } from "@/utils/alea"; + +export const VariantRules = class LosersRules extends ChessRules { static get HasFlags() { return false; } @@ -101,10 +105,13 @@ class LosersRules extends ChessRules updateVariables(move) { } unupdateVariables(move) { } - checkGameEnd() - { - // No valid move: you win! - return this.turn == "w" ? "1-0" : "0-1"; + getCurrentScore() + { + if (this.atLeastOneMove()) // game not over + return "*"; + + // No valid move: the side who cannot move wins + return (this.turn == "w" ? "1-0" : "0-1"); } static get VALUES() @@ -133,7 +140,7 @@ class LosersRules extends ChessRules // Shuffle pieces on first and last rank for (let c of ["w","b"]) { - let positions = range(8); + let positions = ArrayFun.range(8); // Get random squares for bishops let randIndex = 2 * randInt(4); @@ -180,6 +187,6 @@ class LosersRules extends ChessRules return pieces["b"].join("") + "/pppppppp/8/8/8/8/PPPPPPPP/" + pieces["w"].join("").toUpperCase() + - " w -"; //no en-passant + " w 0 -"; //en-passant allowed, but no flags } } diff --git a/client/src/variants/Magnetic.js b/client/src/variants/Magnetic.js index 32279905..ea745dd6 100644 --- a/client/src/variants/Magnetic.js +++ b/client/src/variants/Magnetic.js @@ -1,4 +1,6 @@ -class MagneticRules extends ChessRules +import { ChessRules, PiPo } from "@/base_rules"; + +export const VariantRules = class MagneticRules extends ChessRules { static get HasEnpassant() { return false; } @@ -191,10 +193,15 @@ class MagneticRules extends ChessRules } } - checkGameEnd() + getCurrentScore() { - // No valid move: our king disappeared - return this.turn == "w" ? "0-1" : "1-0"; + const color = this.turn; + const kp = this.kingPos[color]; + if (kp[0] < 0) //king disappeared + return (color == "w" ? "0-1" : "1-0"); + if (this.atLeastOneMove()) // game not over + return "*"; + return "1/2"; //no moves but kings still there } static get THRESHOLD_MATE() diff --git a/client/src/variants/Marseille.js b/client/src/variants/Marseille.js index 2d4ecfa4..0439ab4d 100644 --- a/client/src/variants/Marseille.js +++ b/client/src/variants/Marseille.js @@ -205,6 +205,7 @@ class MarseilleRules extends ChessRules }; } + // TODO: this is wrong: revise following base_rules.getComputerMove() // No alpha-beta here, just adapted min-max at depth 2(+1) getComputerMove() { @@ -221,7 +222,7 @@ class MarseilleRules extends ChessRules let moves = this.getAllValidMoves(); if (moves.length == 0) { - const score = this.checkGameEnd(); + const score = this.getCurrentScore(); if (score == "1/2") return 0; return maxeval * (score == "1-0" ? 1 : -1); @@ -234,7 +235,7 @@ class MarseilleRules extends ChessRules // Otherwise it's color,1. In both cases the next test makes sense if (!this.atLeastOneMove()) { - const score = this.checkGameEnd(); + const score = this.getCurrentScore(); if (score == "1/2") res = (oppCol == "w" ? Math.max(res, 0) : Math.min(res, 0)); else diff --git a/client/src/variants/Switching.js b/client/src/variants/Switching.js deleted file mode 100644 index 04bb110c..00000000 --- a/client/src/variants/Switching.js +++ /dev/null @@ -1,135 +0,0 @@ -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 - const p1 = this.getPiece(x1,y1); - const p2 = this.getPiece(x2,y2); - if (p1 == V.KING && p2 == V.ROOK) - return []; //avoid duplicate moves (potential conflict with castle) - let move = new Move({ - appear: [ - new PiPo({x:x2,y:y2,c:c,p:p1}), - new PiPo({x:x1,y:y1,c:c,p:p2}) - ], - vanish: [ - new PiPo({x:x1,y:y1,c:c,p:p1}), - new PiPo({x:x2,y:y2,c:c,p:p2}) - ], - start: {x:x1,y:y1}, - end: {x:x2,y:y2} - }); - // Move completion: promote switched pawns (as in Magnetic) - const lastRank = (c == "w" ? 0 : V.size.x-1); - let moves = []; - if ((p1==V.PAWN && x2==lastRank) || (p2==V.PAWN && x1==lastRank)) - { - const idx = (p1==V.PAWN ? 0 : 1); - move.appear[idx].p = V.ROOK; - moves.push(move); - for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN]) - { - let cmove = JSON.parse(JSON.stringify(move)); - cmove.appear[idx].p = piece; - moves.push(cmove); - } - if (idx == 1) - { - // Swap moves[i].appear[0] and [1] for moves presentation [TODO...] - moves.forEach(m => { - let tmp = m.appear[0]; - m.appear[0] = m.appear[1]; - m.appear[1] = tmp; - }); - } - } - else //other cases - moves.push(move); - return moves; - } - - getPotentialMovesFrom([x,y], computer) - { - let moves = super.getPotentialMovesFrom([x,y]); - // Add switches: respecting chessboard ordering if "computer" is on - const color = this.turn; - const piece = this.getPiece(x,y); - const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]); - const kp = this.kingPos[color]; - const oppCol = V.GetOppCol(color); - for (let step of steps) - { - let [i,j] = [x+step[0],y+step[1]]; - if (!!computer && (i { return 2*i; }); + let randIndexes = sample(ArrayFun.range(6), 2) + .map(i => { return 2*i; }); let bishop1Pos = positions[randIndexes[0]]; let camel1Pos = positions[randIndexes[1]]; // The second bishop (camel) must be on a square of different color - let randIndexes_tmp = sample(range(5), 2).map(i => { return 2*i+1; }); + let randIndexes_tmp = sample(ArrayFun.range(5), 2) + .map(i => { return 2*i+1; }); let bishop2Pos = positions[randIndexes_tmp[0]]; let camel2Pos = positions[randIndexes_tmp[1]]; for (let idx of randIndexes.concat(randIndexes_tmp) @@ -282,6 +291,6 @@ class WildebeestRules extends ChessRules return pieces["b"].join("") + "/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/" + pieces["w"].join("").toUpperCase() + - " w 1111 -"; + " w 0 1111 -"; } } diff --git a/client/src/variants/Zen.js b/client/src/variants/Zen.js index 6a568db9..db2146a7 100644 --- a/client/src/variants/Zen.js +++ b/client/src/variants/Zen.js @@ -1,4 +1,6 @@ -class ZenRules extends ChessRules +import { ChessRules } from "@/base_rules"; + +export const VariantRules = class ZenRules extends ChessRules { // NOTE: enPassant, if enabled, would need to redefine carefully getEpSquare static get HasEnpassant() { return false; }