From 0c3fe8a6c3e02af46e0bc646b40c1a0c420f9dcd Mon Sep 17 00:00:00 2001 From: Benjamin Auder <benjamin.auder@somewhere> Date: Sun, 2 Feb 2020 02:29:39 +0100 Subject: [PATCH] Check variants. All OK except Dark (bug), Checkered (missing internal moves stack), Marseille (old bot using checkGameEnd()) --- client/src/base_rules.js | 4 +- client/src/variants/Alice.js | 7 +- client/src/variants/Antiking.js | 17 +++- client/src/variants/Atomic.js | 148 +++++++++++++++++++++++++++++- client/src/variants/Atomic_OLD.js | 143 ----------------------------- client/src/variants/Baroque.js | 10 +- client/src/variants/Berolina.js | 4 +- client/src/variants/Checkered.js | 16 +++- client/src/variants/Crazyhouse.js | 7 +- client/src/variants/Dark.js | 25 +++-- client/src/variants/Extinction.js | 18 ++-- client/src/variants/Grand.js | 10 +- client/src/variants/Losers.js | 21 +++-- client/src/variants/Magnetic.js | 15 ++- client/src/variants/Marseille.js | 5 +- client/src/variants/Switching.js | 135 --------------------------- client/src/variants/Upsidedown.js | 12 ++- client/src/variants/Wildebeest.js | 25 +++-- client/src/variants/Zen.js | 4 +- 19 files changed, 276 insertions(+), 350 deletions(-) delete mode 100644 client/src/variants/Atomic_OLD.js delete mode 100644 client/src/variants/Switching.js 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<moves1.length && moves1[j].eval == moves1[0].eval; j++) candidates.push(j); - let currentBest = moves1[sample(candidates)]; + let currentBest = moves1[candidates[randInt(candidates.length)]]; // Skip depth 3+ if we found a checkmate (or if we are checkmated in 1...) if (V.SEARCH_DEPTH >= 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<moves.length && moves[j].eval == moves[0].eval; j++) candidates.push(j); - return moves[sample(candidates)]; + return moves[candidates[randInt(candidates.length)]]; } } diff --git a/client/src/variants/Extinction.js b/client/src/variants/Extinction.js index aab359f8..748f9e46 100644 --- a/client/src/variants/Extinction.js +++ b/client/src/variants/Extinction.js @@ -1,4 +1,6 @@ -class ExtinctionRules extends ChessRules +import { ChessRules } from "@/base_rules"; + +export const VariantRules = class ExtinctionRules extends ChessRules { setOtherVariables(fen) { @@ -99,28 +101,20 @@ class ExtinctionRules extends ChessRules this.material[move.vanish[1].c][move.vanish[1].p]++; } - checkGameOver() + getCurrentScore() { - if (this.checkRepetition()) - return "1/2"; - if (this.atLeastOneMove()) // game not over? { const color = this.turn; if (Object.keys(this.material[color]).some( p => { 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<x || (i==x && j<y))) - continue; //only switch with superior indices - if (V.OnBoard(i,j) && this.board[i][j]!=V.EMPTY - && this.getColor(i,j)==color && this.getPiece(i,j)!=piece - // No switching under check (theoretically non-king pieces could, but not) - && !this.isAttacked(kp, [oppCol])) - { - let switchMove_s = this.getSwitchMove_s([x,y],[i,j]); - if (switchMove_s.length == 1) - moves.push(switchMove_s[0]); - else //promotion - moves = moves.concat(switchMove_s); - } - } - return moves; - } - - getAllValidMoves(computer) - { - const color = this.turn; - const oppCol = V.GetOppCol(color); - let potentialMoves = []; - for (let i=0; i<V.size.x; i++) - { - for (let j=0; j<V.size.y; j++) - { - if (this.board[i][j] != V.EMPTY && this.getColor(i,j) == color) - { - Array.prototype.push.apply(potentialMoves, - this.getPotentialMovesFrom([i,j], computer)); - } - } - } - return this.filterValid(potentialMoves); - } - - updateVariables(move) - { - super.updateVariables(move); - if (move.appear.length == 2 && move.vanish.length == 2 - && move.appear[1].p == V.KING) - { - // Switch with the king; not castle, and not handled by main class - const color = move.vanish[0].c; - this.kingPos[color] = [move.appear[1].x, move.appear[1].y]; - } - } - - unupdateVariables(move) - { - super.unupdateVariables(move); - if (move.appear.length == 2 && move.vanish.length == 2 - && move.appear[1].p == V.KING) - { - const color = move.vanish[0].c; - this.kingPos[color] = [move.appear[0].x, move.appear[0].y]; - } - } - - static get SEARCH_DEPTH() { return 2; } //high branching factor - - getNotation(move) - { - if (move.appear.length == 1) - return super.getNotation(move); //no switch - // Switch or castle - if (move.appear[0].p == V.KING && move.appear[1].p == V.ROOK) - return (move.end.y < move.start.y ? "0-0-0" : "0-0"); - // Switch: - return "S" + V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end); - } -} diff --git a/client/src/variants/Upsidedown.js b/client/src/variants/Upsidedown.js index c3e8716e..de722168 100644 --- a/client/src/variants/Upsidedown.js +++ b/client/src/variants/Upsidedown.js @@ -1,4 +1,8 @@ -class UpsidedownRules extends ChessRules +import { ChessRules } from "@/base_rules"; +import { randInt } from "@/utils/alea"; +import { ArrayFun } from "@/utils/array"; + +export const VariantRules = class UpsidedownRules extends ChessRules { static get HasFlags() { return false; } @@ -16,7 +20,7 @@ class UpsidedownRules extends ChessRules let pieces = { "w": new Array(8), "b": new Array(8) }; for (let c of ["w","b"]) { - let positions = range(8); + let positions = ArrayFun.range(8); let randIndex = randInt(8); const kingPos = positions[randIndex]; @@ -29,7 +33,7 @@ class UpsidedownRules extends ChessRules else if (kingPos == V.size.y-1) knight1Pos = V.size.y-2; else - knight1Pos = kingPos + (Math.randInt() < 0.5 ? 1 : -1); + knight1Pos = kingPos + (Math.random() < 0.5 ? 1 : -1); // Search for knight1Pos index in positions and remove it const knight1Index = positions.indexOf(knight1Pos); positions.splice(knight1Index, 1); @@ -65,6 +69,6 @@ class UpsidedownRules extends ChessRules return pieces["w"].join("").toUpperCase() + "/PPPPPPPP/8/8/8/8/pppppppp/" + pieces["b"].join("") + - " w"; //no castle, no en-passant + " w 0"; //no castle, no en-passant } } diff --git a/client/src/variants/Wildebeest.js b/client/src/variants/Wildebeest.js index d0baa6f9..20a32ccf 100644 --- a/client/src/variants/Wildebeest.js +++ b/client/src/variants/Wildebeest.js @@ -1,4 +1,8 @@ -class WildebeestRules extends ChessRules +import { ChessRules } from "@/base_rules"; +import { ArrayFun } from "@/utils/array"; +import { sample, randInt } from "@/utils/alea"; + +export const VariantRules = class WildebeestRules extends ChessRules { static getPpath(b) { @@ -211,10 +215,13 @@ class WildebeestRules extends ChessRules V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]), "oneStep"); } - checkGameEnd() - { + getCurrentScore() + { + if (this.atLeastOneMove()) // game not over + return "*"; + // No valid move: game is lost (stalemate is a win) - return this.turn == "w" ? "0-1" : "1-0"; + return (this.turn == "w" ? "0-1" : "1-0"); } static get VALUES() { @@ -231,14 +238,16 @@ class WildebeestRules extends ChessRules let pieces = { "w": new Array(10), "b": new Array(10) }; for (let c of ["w","b"]) { - let positions = range(11); + let positions = ArrayFun.range(11); // Get random squares for bishops + camels (different colors) - let randIndexes = sample(range(6), 2).map(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; } -- 2.44.0