Check variants. All OK except Dark (bug), Checkered (missing internal moves stack...
authorBenjamin Auder <benjamin.auder@somewhere>
Sun, 2 Feb 2020 01:29:39 +0000 (02:29 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Sun, 2 Feb 2020 01:29:39 +0000 (02:29 +0100)
19 files changed:
client/src/base_rules.js
client/src/variants/Alice.js
client/src/variants/Antiking.js
client/src/variants/Atomic.js
client/src/variants/Atomic_OLD.js [deleted file]
client/src/variants/Baroque.js
client/src/variants/Berolina.js
client/src/variants/Checkered.js
client/src/variants/Crazyhouse.js
client/src/variants/Dark.js
client/src/variants/Extinction.js
client/src/variants/Grand.js
client/src/variants/Losers.js
client/src/variants/Magnetic.js
client/src/variants/Marseille.js
client/src/variants/Switching.js [deleted file]
client/src/variants/Upsidedown.js
client/src/variants/Wildebeest.js
client/src/variants/Zen.js

index 169fb2d..8cf86a8 100644 (file)
@@ -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)
index e80e13b..e81d3fc 100644 (file)
@@ -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);
index 22fd2b4..7b57e74 100644 (file)
@@ -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 1111 -";
        }
 }
index 622e9d0..e1b1c16 100644 (file)
@@ -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 (file)
index 1ee5b98..0000000
+++ /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
-       }
-}
index ce9e667..9b5a3cd 100644 (file)
@@ -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)
index 517a93e..05152b4 100644 (file)
@@ -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)
index 1f6750b..f8176cd 100644 (file)
@@ -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()
index 179fffa..ec903c2 100644 (file)
@@ -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(","))
index 76e9462..777aacc 100644 (file)
@@ -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)]];
        }
 }
index aab359f..748f9e4 100644 (file)
@@ -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()
index 2ce8542..6ddfbd7 100644 (file)
@@ -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 1111 - 00000000000000";
        }
 }
index 7c6b422..86b00f9 100644 (file)
@@ -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
        }
 }
index 3227990..ea745dd 100644 (file)
@@ -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()
index 2d4ecfa..0439ab4 100644 (file)
@@ -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 (file)
index 04bb110..0000000
+++ /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);
-       }
-}
index c3e8716..de72216 100644 (file)
@@ -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
        }
 }
index d0baa6f..20a32cc 100644 (file)
@@ -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 1111 -";
        }
 }
index 6a568db..db2146a 100644 (file)
@@ -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; }