Generalize code in VariantRules. Considerable speed-up for checkered bot. Prepare...
authorBenjamin Auder <benjamin.auder@somewhere>
Tue, 20 Nov 2018 00:26:02 +0000 (01:26 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Tue, 20 Nov 2018 00:26:02 +0000 (01:26 +0100)
public/javascripts/base_rules.js
public/javascripts/components/game.js
public/javascripts/variants/Antiking.js
public/javascripts/variants/Atomic.js
public/javascripts/variants/Checkered.js
public/javascripts/variants/Zen.js

index 1630495..0868a36 100644 (file)
@@ -106,7 +106,7 @@ class ChessRules
        static GetBoard(fen)
        {
                let rows = fen.split(" ")[0].split("/");
-               let [sizeX,sizeY] = VariantRules.size;
+               const [sizeX,sizeY] = VariantRules.size;
                let board = doubleArray(sizeX, sizeY, "");
                for (let i=0; i<rows.length; i++)
                {
@@ -190,10 +190,10 @@ class ChessRules
                return undefined; //default
        }
 
-       // can color1 take color2?
-       canTake(color1, color2)
+       // can thing on square1 take thing on square2
+       canTake([x1,y1], [x2,y2])
        {
-               return color1 != color2;
+               return this.getColor(x1,y1) != this.getColor(x2,y2);
        }
 
        ///////////////////
@@ -202,35 +202,33 @@ class ChessRules
        // All possible moves from selected square (assumption: color is OK)
        getPotentialMovesFrom([x,y])
        {
-               let c = this.getColor(x,y);
-               // Fill possible moves according to piece type
                switch (this.getPiece(x,y))
                {
                        case VariantRules.PAWN:
-                               return this.getPotentialPawnMoves(x,y,c);
+                               return this.getPotentialPawnMoves([x,y]);
                        case VariantRules.ROOK:
-                               return this.getPotentialRookMoves(x,y,c);
+                               return this.getPotentialRookMoves([x,y]);
                        case VariantRules.KNIGHT:
-                               return this.getPotentialKnightMoves(x,y,c);
+                               return this.getPotentialKnightMoves([x,y]);
                        case VariantRules.BISHOP:
-                               return this.getPotentialBishopMoves(x,y,c);
+                               return this.getPotentialBishopMoves([x,y]);
                        case VariantRules.QUEEN:
-                               return this.getPotentialQueenMoves(x,y,c);
+                               return this.getPotentialQueenMoves([x,y]);
                        case VariantRules.KING:
-                               return this.getPotentialKingMoves(x,y,c);
+                               return this.getPotentialKingMoves([x,y]);
                }
        }
 
        // Build a regular move from its initial and destination squares; tr: transformation
-       getBasicMove(sx, sy, ex, ey, tr)
+       getBasicMove([sx,sy], [ex,ey], tr)
        {
                var mv = new Move({
                        appear: [
                                new PiPo({
                                        x: ex,
                                        y: ey,
-                                       c: this.getColor(sx,sy),
-                                       p: !!tr ? tr : this.getPiece(sx,sy)
+                                       c: !!tr ? tr.c : this.getColor(sx,sy),
+                                       p: !!tr ? tr.p : this.getPiece(sx,sy)
                                })
                        ],
                        vanish: [
@@ -259,35 +257,37 @@ class ChessRules
        }
 
        // Generic method to find possible moves of non-pawn pieces ("sliding or jumping")
-       getSlideNJumpMoves(x, y, color, steps, oneStep)
+       getSlideNJumpMoves([x,y], steps, oneStep)
        {
+               const color = this.getColor(x,y);
                var moves = [];
-               let [sizeX,sizeY] = VariantRules.size;
+               const [sizeX,sizeY] = VariantRules.size;
                outerLoop:
                for (let step of steps)
                {
-                       var i = x + step[0];
-                       var j = y + step[1];
+                       let i = x + step[0];
+                       let j = y + step[1];
                        while (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j] == VariantRules.EMPTY)
                        {
-                               moves.push(this.getBasicMove(x, y, i, j));
+                               moves.push(this.getBasicMove([x,y], [i,j]));
                                if (oneStep !== undefined)
                                        continue outerLoop;
                                i += step[0];
                                j += step[1];
                        }
-                       if (i>=0 && i<8 && j>=0 && j<8 && this.canTake(color, this.getColor(i,j)))
-                               moves.push(this.getBasicMove(x, y, i, j));
+                       if (i>=0 && i<8 && j>=0 && j<8 && this.canTake([x,y], [i,j]))
+                               moves.push(this.getBasicMove([x,y], [i,j]));
                }
                return moves;
        }
 
        // What are the pawn moves from square x,y considering color "color" ?
-       getPotentialPawnMoves(x, y, color)
+       getPotentialPawnMoves([x,y])
        {
+               const color = this.getColor(x,y);
                var moves = [];
                var V = VariantRules;
-               let [sizeX,sizeY] = VariantRules.size;
+               const [sizeX,sizeY] = VariantRules.size;
                let shift = (color == "w" ? -1 : 1);
                let startRank = (color == "w" ? sizeY-2 : 1);
                let lastRank = (color == "w" ? 0 : sizeY-1);
@@ -297,24 +297,18 @@ class ChessRules
                        // Normal moves
                        if (this.board[x+shift][y] == V.EMPTY)
                        {
-                               moves.push(this.getBasicMove(x, y, x+shift, y));
+                               moves.push(this.getBasicMove([x,y], [x+shift,y]));
                                if (x==startRank && this.board[x+2*shift][y] == V.EMPTY)
                                {
                                        // Two squares jump
-                                       moves.push(this.getBasicMove(x, y, x+2*shift, y));
+                                       moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
                                }
                        }
                        // Captures
-                       if (y>0 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y-1))
-                               && this.board[x+shift][y-1] != V.EMPTY)
-                       {
-                               moves.push(this.getBasicMove(x, y, x+shift, y-1));
-                       }
-                       if (y<sizeY-1 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y+1))
-                               && this.board[x+shift][y+1] != V.EMPTY)
-                       {
-                               moves.push(this.getBasicMove(x, y, x+shift, y+1));
-                       }
+                       if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+                               moves.push(this.getBasicMove([x,y], [x+shift,y-1]));
+                       if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+                               moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
                }
 
                if (x+shift == lastRank)
@@ -324,18 +318,12 @@ class ChessRules
                        promotionPieces.forEach(p => {
                                // Normal move
                                if (this.board[x+shift][y] == V.EMPTY)
-                                       moves.push(this.getBasicMove(x, y, x+shift, y, p));
+                                       moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
                                // Captures
-                               if (y>0 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y-1))
-                                       && this.board[x+shift][y-1] != V.EMPTY)
-                               {
-                                       moves.push(this.getBasicMove(x, y, x+shift, y-1, p));
-                               }
-                               if (y<sizeY-1 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y+1))
-                                       && this.board[x+shift][y+1] != V.EMPTY)
-                               {
-                                       moves.push(this.getBasicMove(x, y, x+shift, y+1, p));
-                               }
+                               if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+                                       moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p}));
+                               if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+                                       moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:p}));
                        });
                }
 
@@ -345,7 +333,7 @@ class ChessRules
                if (!!epSquare && epSquare.x == x+shift && Math.abs(epSquare.y - y) == 1)
                {
                        let epStep = epSquare.y - y;
-                       var enpassantMove = this.getBasicMove(x, y, x+shift, y+epStep);
+                       var enpassantMove = this.getBasicMove([x,y], [x+shift,y+epStep]);
                        enpassantMove.vanish.push({
                                x: x,
                                y: y+epStep,
@@ -359,45 +347,40 @@ class ChessRules
        }
 
        // What are the rook moves from square x,y ?
-       getPotentialRookMoves(x, y, color)
+       getPotentialRookMoves(sq)
        {
-               return this.getSlideNJumpMoves(
-                       x, y, color, VariantRules.steps[VariantRules.ROOK]);
+               return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.ROOK]);
        }
 
        // What are the knight moves from square x,y ?
-       getPotentialKnightMoves(x, y, color)
+       getPotentialKnightMoves(sq)
        {
-               return this.getSlideNJumpMoves(
-                       x, y, color, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
+               return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
        }
 
        // What are the bishop moves from square x,y ?
-       getPotentialBishopMoves(x, y, color)
+       getPotentialBishopMoves(sq)
        {
-               return this.getSlideNJumpMoves(
-                       x, y, color, VariantRules.steps[VariantRules.BISHOP]);
+               return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.BISHOP]);
        }
 
        // What are the queen moves from square x,y ?
-       getPotentialQueenMoves(x, y, color)
+       getPotentialQueenMoves(sq)
        {
-               return this.getSlideNJumpMoves(
-                       x, y, color, VariantRules.steps[VariantRules.QUEEN]);
+               return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.QUEEN]);
        }
 
        // What are the king moves from square x,y ?
-       getPotentialKingMoves(x, y, c)
+       getPotentialKingMoves(sq)
        {
                // Initialize with normal moves
-               var moves = this.getSlideNJumpMoves(x, y, c,
-                       VariantRules.steps[VariantRules.QUEEN], "oneStep");
-
-               return moves.concat(this.getCastleMoves(x,y,c));
+               let moves = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.QUEEN], "oneStep");
+               return moves.concat(this.getCastleMoves(sq));
        }
 
-       getCastleMoves(x,y,c)
+       getCastleMoves([x,y])
        {
+               const c = this.getColor(x,y);
                if (x != (c=="w" ? 7 : 0) || y != this.INIT_COL_KING[c])
                        return []; //x isn't first rank, or king has moved (shortcut)
 
@@ -467,11 +450,10 @@ class ChessRules
        ///////////////////
        // MOVES VALIDATION
 
-       canIplay(color, sq)
+       canIplay(side, [x,y])
        {
-               return ((color=='w' && this.moves.length%2==0)
-                               || (color=='b' && this.moves.length%2==1))
-                       && this.getColor(sq[0], sq[1]) == color;
+               return ((side=='w' && this.moves.length%2==0) || (side=='b' && this.moves.length%2==1))
+                       && this.getColor(x,y) == side;
        }
 
        getPossibleMovesFrom(sq)
@@ -485,15 +467,14 @@ class ChessRules
        {
                if (moves.length == 0)
                        return [];
-               let color = this.getColor( moves[0].start.x, moves[0].start.y );
-               return moves.filter(m => {
-                       return !this.underCheck(m, color);
-               });
+               let color = this.turn;
+               return moves.filter(m => { return !this.underCheck(m, color); });
        }
 
        // Search for all valid moves considering current turn (for engine and game end)
-       getAllValidMoves(color)
+       getAllValidMoves()
        {
+               const color = this.turn;
                const oppCol = this.getOppCol(color);
                var potentialMoves = [];
                let [sizeX,sizeY] = VariantRules.size;
@@ -512,8 +493,9 @@ class ChessRules
        }
        
        // Stop at the first move found
-       atLeastOneMove(color)
+       atLeastOneMove()
        {
+               const color = this.turn;
                const oppCol = this.getOppCol(color);
                let [sizeX,sizeY] = VariantRules.size;
                for (var i=0; i<sizeX; i++)
@@ -537,29 +519,32 @@ class ChessRules
                return false;
        }
 
-       // Check if pieces of color 'color' are attacking square x,y
-       isAttacked(sq, color)
+       // Check if pieces of color 'colors' are attacking square x,y
+       isAttacked(sq, colors)
        {
-               return (this.isAttackedByPawn(sq, color)
-                       || this.isAttackedByRook(sq, color)
-                       || this.isAttackedByKnight(sq, color)
-                       || this.isAttackedByBishop(sq, color)
-                       || this.isAttackedByQueen(sq, color)
-                       || this.isAttackedByKing(sq, color));
+               return (this.isAttackedByPawn(sq, colors)
+                       || this.isAttackedByRook(sq, colors)
+                       || this.isAttackedByKnight(sq, colors)
+                       || this.isAttackedByBishop(sq, colors)
+                       || this.isAttackedByQueen(sq, colors)
+                       || this.isAttackedByKing(sq, colors));
        }
 
        // Is square x,y attacked by pawns of color c ?
-       isAttackedByPawn([x,y], c)
+       isAttackedByPawn([x,y], colors)
        {
-               let pawnShift = (c=="w" ? 1 : -1);
-               if (x+pawnShift>=0 && x+pawnShift<8)
+               for (let c of colors)
                {
-                       for (let i of [-1,1])
+                       let pawnShift = (c=="w" ? 1 : -1);
+                       if (x+pawnShift>=0 && x+pawnShift<8)
                        {
-                               if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==VariantRules.PAWN
-                                       && this.getColor(x+pawnShift,y+i)==c)
+                               for (let i of [-1,1])
                                {
-                                       return true;
+                                       if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==VariantRules.PAWN
+                                               && this.getColor(x+pawnShift,y+i)==c)
+                                       {
+                                               return true;
+                                       }
                                }
                        }
                }
@@ -567,42 +552,42 @@ class ChessRules
        }
 
        // Is square x,y attacked by rooks of color c ?
-       isAttackedByRook(sq, color)
+       isAttackedByRook(sq, colors)
        {
-               return this.isAttackedBySlideNJump(sq, color,
+               return this.isAttackedBySlideNJump(sq, colors,
                        VariantRules.ROOK, VariantRules.steps[VariantRules.ROOK]);
        }
 
        // Is square x,y attacked by knights of color c ?
-       isAttackedByKnight(sq, color)
+       isAttackedByKnight(sq, colors)
        {
-               return this.isAttackedBySlideNJump(sq, color,
+               return this.isAttackedBySlideNJump(sq, colors,
                        VariantRules.KNIGHT, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
        }
 
        // Is square x,y attacked by bishops of color c ?
-       isAttackedByBishop(sq, color)
+       isAttackedByBishop(sq, colors)
        {
-               return this.isAttackedBySlideNJump(sq, color,
+               return this.isAttackedBySlideNJump(sq, colors,
                        VariantRules.BISHOP, VariantRules.steps[VariantRules.BISHOP]);
        }
 
        // Is square x,y attacked by queens of color c ?
-       isAttackedByQueen(sq, color)
+       isAttackedByQueen(sq, colors)
        {
-               return this.isAttackedBySlideNJump(sq, color,
+               return this.isAttackedBySlideNJump(sq, colors,
                        VariantRules.QUEEN, VariantRules.steps[VariantRules.QUEEN]);
        }
 
        // Is square x,y attacked by king of color c ?
-       isAttackedByKing(sq, color)
+       isAttackedByKing(sq, colors)
        {
-               return this.isAttackedBySlideNJump(sq, color,
+               return this.isAttackedBySlideNJump(sq, colors,
                        VariantRules.KING, VariantRules.steps[VariantRules.QUEEN], "oneStep");
        }
 
        // Generic method for non-pawn pieces ("sliding or jumping"): is x,y attacked by piece != color ?
-       isAttackedBySlideNJump([x,y], c,piece,steps,oneStep)
+       isAttackedBySlideNJump([x,y], colors, piece, steps, oneStep)
        {
                for (let step of steps)
                {
@@ -614,7 +599,7 @@ class ChessRules
                                ry += step[1];
                        }
                        if (rx>=0 && rx<8 && ry>=0 && ry<8 && this.board[rx][ry] != VariantRules.EMPTY
-                               && this.getPiece(rx,ry) == piece && this.getColor(rx,ry) == c)
+                               && this.getPiece(rx,ry) == piece && colors.includes(this.getColor(rx,ry)))
                        {
                                return true;
                        }
@@ -623,20 +608,22 @@ class ChessRules
        }
 
        // Is color c under check after move ?
-       underCheck(move, c)
+       underCheck(move)
        {
+               const color = this.turn;
                this.play(move);
-               let res = this.isAttacked(this.kingPos[c], this.getOppCol(c));
+               let res = this.isAttacked(this.kingPos[color], this.getOppCol(color));
                this.undo(move);
                return res;
        }
 
        // On which squares is color c under check (after move) ?
-       getCheckSquares(move, c)
+       getCheckSquares(move)
        {
                this.play(move);
-               let res = this.isAttacked(this.kingPos[c], this.getOppCol(c))
-                       ? [ JSON.parse(JSON.stringify(this.kingPos[c])) ] //need to duplicate!
+               const color = this.turn;
+               let res = this.isAttacked(this.kingPos[color], this.getOppCol(color))
+                       ? [ JSON.parse(JSON.stringify(this.kingPos[color])) ] //need to duplicate!
                        : [ ];
                this.undo(move);
                return res;
@@ -723,7 +710,7 @@ class ChessRules
        //////////////
        // END OF GAME
 
-       checkGameOver(color)
+       checkGameOver()
        {
                // Check for 3 repetitions
                if (this.moves.length >= 8)
@@ -739,19 +726,20 @@ class ChessRules
                        }
                }
 
-               if (this.atLeastOneMove(color))
+               if (this.atLeastOneMove())
                {
                        // game not over
                        return "*";
                }
 
                // Game over
-               return this.checkGameEnd(color);
+               return this.checkGameEnd();
        }
 
-       // Useful stand-alone for engine
-       checkGameEnd(color)
+       // No moves are possible: compute score
+       checkGameEnd()
        {
+               const color = this.turn;
                // No valid move: stalemate or checkmate?
                if (!this.isAttacked(this.kingPos[color], this.getOppCol(color)))
                        return "1/2";
@@ -775,12 +763,12 @@ class ChessRules
        }
 
        // Assumption: at least one legal move
-       getComputerMove(color)
+       getComputerMove()
        {
-               const oppCol = this.getOppCol(color);
+               const color = this.turn;
 
                // Rank moves using a min-max at depth 2
-               let moves1 = this.getAllValidMoves(color);
+               let moves1 = this.getAllValidMoves();
 
                for (let i=0; i<moves1.length; i++)
                {
@@ -788,10 +776,10 @@ class ChessRules
                        let eval2 = (color=="w" ? 1 : -1) * 1000; //initialized with very high (checkmate) value
                        this.play(moves1[i]);
                        // Second half-move:
-                       let moves2 = this.getAllValidMoves(oppCol);
+                       let moves2 = this.getAllValidMoves();
                        // If no possible moves AND underCheck, eval2 is correct.
                        // If !underCheck, eval2 is 0 (stalemate).
-                       if (moves2.length == 0 && this.checkGameEnd(oppCol) == "1/2")
+                       if (moves2.length == 0 && this.checkGameEnd() == "1/2")
                                eval2 = 0;
                        for (let j=0; j<moves2.length; j++)
                        {
@@ -812,7 +800,7 @@ class ChessRules
                {
                        this.play(moves1[i]);
                        // 0.1 * oldEval : heuristic to avoid some bad moves (not all...)
-                       moves1[i].eval = 0.1*moves1[i].eval + this.alphabeta(oppCol, color, 2, -1000, 1000);
+                       moves1[i].eval = 0.1*moves1[i].eval + this.alphabeta(2, -1000, 1000);
                        this.undo(moves1[i]);
                }
                moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
@@ -825,11 +813,12 @@ class ChessRules
                return moves1[_.sample(candidates, 1)];
        }
 
-       alphabeta(color, oppCol, depth, alpha, beta)
+       alphabeta(depth, alpha, beta)
   {
-               if (!this.atLeastOneMove(color))
+               const color = this.turn;
+               if (!this.atLeastOneMove())
                {
-                       switch (this.checkGameEnd(color))
+                       switch (this.checkGameEnd())
                        {
                                case "1/2": return 0;
                                default: return color=="w" ? -1000 : 1000;
@@ -837,14 +826,14 @@ class ChessRules
                }
                if (depth == 0)
       return this.evalPosition();
-               const moves = this.getAllValidMoves(color);
+               const moves = this.getAllValidMoves();
     let v = color=="w" ? -1000 : 1000;
                if (color == "w")
                {
                        for (let i=0; i<moves.length; i++)
       {
                                this.play(moves[i]);
-                               v = Math.max(v, this.alphabeta(oppCol, color, depth-1, alpha, beta));
+                               v = Math.max(v, this.alphabeta(depth-1, alpha, beta));
                                this.undo(moves[i]);
                                alpha = Math.max(alpha, v);
                                if (alpha >= beta)
@@ -856,7 +845,7 @@ class ChessRules
                        for (let i=0; i<moves.length; i++)
                        {
                                this.play(moves[i]);
-                               v = Math.min(v, this.alphabeta(oppCol, color, depth-1, alpha, beta));
+                               v = Math.min(v, this.alphabeta(depth-1, alpha, beta));
                                this.undo(moves[i]);
                                beta = Math.min(beta, v);
                                if (alpha >= beta)
index 147aeaf..8e8e277 100644 (file)
@@ -618,10 +618,9 @@ Vue.component('my-game', {
                                this.seek = false;
                                if (!!moves && moves.length > 0) //imply continuation
                                {
-                                       const oppCol = this.vr.turn;
                                        const lastMove = moves[moves.length-1];
                                        this.vr.undo(lastMove);
-                                       this.incheck = this.vr.getCheckSquares(lastMove, oppCol);
+                                       this.incheck = this.vr.getCheckSquares(lastMove);
                                        this.vr.play(lastMove, "ingame");
                                }
                                delete localStorage["newgame"];
@@ -635,8 +634,7 @@ Vue.component('my-game', {
                        }
                },
                playComputerMove: function() {
-                       const compColor = this.mycolor=='w' ? 'b' : 'w';
-                       const compMove = this.vr.getComputerMove(compColor);
+                       const compMove = this.vr.getComputerMove();
                        // HACK: avoid selecting elements before they appear on page:
                        setTimeout(() => this.play(compMove, "animate"), 500);
                },
@@ -751,8 +749,7 @@ Vue.component('my-game', {
                                this.animateMove(move);
                                return;
                        }
-                       const oppCol = this.vr.getOppCol(this.vr.turn);
-                       this.incheck = this.vr.getCheckSquares(move, oppCol); //is opponent in check?
+                       this.incheck = this.vr.getCheckSquares(move); //is opponent in check?
                        // Not programmatic, or animation is over
                        if (this.mode == "human" && this.vr.turn == this.mycolor)
                                this.conn.send(JSON.stringify({code:"newmove", move:move, oppid:this.oppid}));
@@ -760,7 +757,7 @@ Vue.component('my-game', {
                        this.vr.play(move, "ingame");
                        if (this.mode == "human")
                                this.updateStorage(); //after our moves and opponent moves
-                       const eog = this.vr.checkGameOver(this.vr.turn);
+                       const eog = this.vr.checkGameOver();
                        if (eog != "*")
                                this.endGame(eog);
                        else if (this.mode == "computer" && this.vr.turn != this.mycolor)
index ac25c73..360deaf 100644 (file)
@@ -7,6 +7,12 @@ class AntikingRules
        }
 
        static get ANTIKING() { return 'a'; }
+       
+       initVariables(fen)
+       {
+               super.initVariables(fen);
+               // TODO: initialize this.antikingPos[...]
+       }
 
        canTake(color1, color2, [x,y])
        {
@@ -26,23 +32,14 @@ class AntikingRules
                }
        }
 
-// TODO: generaliser (à moindre coût) base_rules ? Ou spécialiser variantes ?
-
-       getPotentialAntikingMoves(x, y, c)
+       getPotentialAntikingMoves([x,y])
        {
                // TODO
        }
 
-// TODO: need to re-think some logic, since antikings capture same color
-
-       isAttacked(sq, color)
+       isAttacked(sq, colors)
        {
-               return (this.isAttackedByPawn(sq, color)
-                       || this.isAttackedByRook(sq, color)
-                       || this.isAttackedByKnight(sq, color)
-                       || this.isAttackedByBishop(sq, color)
-                       || this.isAttackedByQueen(sq, color)
-                       || this.isAttackedByKing(sq, color)); //...
+               return (super.isAttacked(sq, colors) || this.isAttackedByAntiking(sq, colors));
        }
 
        isAttackedByAntiking(sq, color)
@@ -50,8 +47,9 @@ class AntikingRules
                // TODO
        }
 
-       underCheck(move, c)
+       underCheck(move)
        {
+               const c = this.turn;
                this.play(move);
                let res = this.isAttacked(this.kingPos[c], this.getOppCol(c));
                // TODO: also check that antiking is still in check
@@ -59,34 +57,18 @@ class AntikingRules
                return res;
        }
 
-       getCheckSquares(move, c)
+       getCheckSquares(move)
        {
                this.play(move);
+               const c = this.turn;
                // TODO
                let res = this.isAttacked(this.kingPos[c], this.getOppCol(c))
-                       ? [ JSON.parse(JSON.stringify(this.kingPos[c])) ] //need to duplicate!
+                       ? [ JSON.parse(JSON.stringify(this.kingPos[c])) ]
                        : [ ];
                this.undo(move);
                return res;
        }
 
-       // Apply a move on board
-       static PlayOnBoard(board, move)
-       {
-               for (let psq of move.vanish)
-                       board[psq.x][psq.y] = VariantRules.EMPTY;
-               for (let psq of move.appear)
-                       board[psq.x][psq.y] = psq.c + psq.p;
-       }
-       // Un-apply the played move
-       static UndoOnBoard(board, move)
-       {
-               for (let psq of move.appear)
-                       board[psq.x][psq.y] = VariantRules.EMPTY;
-               for (let psq of move.vanish)
-                       board[psq.x][psq.y] = psq.c + psq.p;
-       }
-
        // TODO: need antikingPos as well
        updateVariables(move)
        {
@@ -106,7 +88,7 @@ class AntikingRules
                return color == "w" ? "0-1" : "1-0";
        }
 
-       // Pieces values
+       // Pieces values (TODO: use Object.assign() + ChessRules.VALUES ?)
        static get VALUES() {
                return {
                        'p': 1,
@@ -121,55 +103,8 @@ class AntikingRules
 
        static GenRandInitFen()
        {
-               // TODO: no need all code, just add an antiking at rondom on 3rd ranks
-               let pieces = [new Array(8), new Array(8)];
-               // Shuffle pieces on first and last rank
-               for (let c = 0; c <= 1; c++)
-               {
-                       let positions = _.range(8);
-
-                       // Get random squares for bishops
-                       let randIndex = 2 * _.random(3);
-                       let bishop1Pos = positions[randIndex];
-                       // The second bishop must be on a square of different color
-                       let randIndex_tmp = 2 * _.random(3) + 1;
-                       let bishop2Pos = positions[randIndex_tmp];
-                       // Remove chosen squares
-                       positions.splice(Math.max(randIndex,randIndex_tmp), 1);
-                       positions.splice(Math.min(randIndex,randIndex_tmp), 1);
-
-                       // Get random squares for knights
-                       randIndex = _.random(5);
-                       let knight1Pos = positions[randIndex];
-                       positions.splice(randIndex, 1);
-                       randIndex = _.random(4);
-                       let knight2Pos = positions[randIndex];
-                       positions.splice(randIndex, 1);
-
-                       // Get random square for queen
-                       randIndex = _.random(3);
-                       let queenPos = positions[randIndex];
-                       positions.splice(randIndex, 1);
-
-                       // Rooks and king positions are now fixed, because of the ordering rook-king-rook
-                       let rook1Pos = positions[0];
-                       let kingPos = positions[1];
-                       let rook2Pos = positions[2];
-
-                       // Finally put the shuffled pieces in the board array
-                       pieces[c][rook1Pos] = 'r';
-                       pieces[c][knight1Pos] = 'n';
-                       pieces[c][bishop1Pos] = 'b';
-                       pieces[c][queenPos] = 'q';
-                       pieces[c][kingPos] = 'k';
-                       pieces[c][bishop2Pos] = 'b';
-                       pieces[c][knight2Pos] = 'n';
-                       pieces[c][rook2Pos] = 'r';
-               }
-               let fen = pieces[0].join("") +
-                       "/pppppppp/8/8/8/8/PPPPPPPP/" +
-                       pieces[1].join("").toUpperCase() +
-                       " 1111"; //add flags
-               return fen;
+               let randFen = ChessRules.GenRandInitFen();
+               // TODO: just add an antiking at random on 3rd ranks
+               return randFen;
        }
 }
index 0fd7647..d86ed0e 100644 (file)
@@ -1,8 +1,8 @@
 class AtomicRules extends ChessRules
 {
-       getPotentialMovesFrom([x,y], c, lastMove)
+       getPotentialMovesFrom([x,y])
        {
-               let moves = super.getPotentialMovesFrom([x,y], c, lastMove);
+               let moves = super.getPotentialMovesFrom([x,y]);
 
                // Handle explosions
                moves.forEach(m => {
@@ -28,7 +28,7 @@ class AtomicRules extends ChessRules
                return moves;
        }
 
-       getPotentialKingMoves(x, y, c)
+       getPotentialKingMoves([x,y])
        {
                // King cannot capture:
                let moves = [];
@@ -39,20 +39,20 @@ class AtomicRules extends ChessRules
                        var i = x + step[0];
                        var j = y + step[1];
                        if (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j] == VariantRules.EMPTY)
-                               moves.push(this.getBasicMove(x, y, i, j));
+                               moves.push(this.getBasicMove([x,y], [i,j]));
                }
-               return moves.concat(this.getCastleMoves(x,y,c));
+               return moves.concat(this.getCastleMoves([x,y]));
        }
 
-       isAttacked(sq, color)
+       isAttacked(sq, colors)
        {
-               if (this.getPiece(sq[0],sq[1]) == VariantRules.KING && this.isAttackedByKing(sq, color))
+               if (this.getPiece(sq[0],sq[1]) == VariantRules.KING && this.isAttackedByKing(sq, colors))
                        return false; //king cannot take...
-               return (this.isAttackedByPawn(sq, color)
-                       || this.isAttackedByRook(sq, color)
-                       || this.isAttackedByKnight(sq, color)
-                       || this.isAttackedByBishop(sq, color)
-                       || this.isAttackedByQueen(sq, color));
+               return (this.isAttackedByPawn(sq, colors)
+                       || this.isAttackedByRook(sq, colors)
+                       || this.isAttackedByKnight(sq, colors)
+                       || this.isAttackedByBishop(sq, colors)
+                       || this.isAttackedByQueen(sq, colors));
        }
 
        updateVariables(move)
@@ -112,8 +112,9 @@ class AtomicRules extends ChessRules
                }
        }
 
-       underCheck(move, c)
+       underCheck(move)
        {
+               const c = this.turn;
                const oppCol = this.getOppCol(c);
                this.play(move);
                let res = undefined;
@@ -130,8 +131,9 @@ class AtomicRules extends ChessRules
                return res;
        }
 
-       getCheckSquares(move, c)
+       getCheckSquares(move)
        {
+               const c = this.getOppCol(this.turn);
                const saveKingPos = this.kingPos[c]; //king might explode
                this.play(move);
                let res = [ ];
@@ -143,8 +145,9 @@ class AtomicRules extends ChessRules
                return res;
        }
 
-       checkGameEnd(color)
+       checkGameEnd()
        {
+               const color = this.turn;
                const kp = this.kingPos[color];
                if (kp[0] < 0) //king disappeared
                        return color == "w" ? "0-1" : "1-0";
index a74143b..6ed73a8 100644 (file)
@@ -50,87 +50,58 @@ class CheckeredRules extends ChessRules
                return flags;
        }
 
-       // can color1 take color2?
-       canTake(color1, color2)
+       canTake([x1,y1], [x2,y2])
        {
+               const color1 = this.getColor(x1,y1);
+               const color2 = this.getColor(x2,y2);
                // Checkered aren't captured
                return color1 != color2 && color2 != 'c' && (color1 != 'c' || color2 != this.turn);
        }
 
-       // Build regular move(s) from its initial and destination squares; tr: transformation
-       getBasicMove(sx, sy, ex, ey, tr)
+       addCaptures([sx,sy], [ex,ey], moves)
        {
-               if (this.board[ex][ey] == VariantRules.EMPTY)
+               const piece = this.getPiece(sx,sy);
+               if (piece != VariantRules.KING)
                {
-                       // No capture, standard move construction
-                       return [super.getBasicMove(sx,sy,ex,ey,tr)];
+                       moves.push(this.getBasicMove([sx,sy], [ex,ey], {c:'c',p:piece}));
+                       const takePiece = this.getPiece(ex,ey);
+                       if (takePiece != piece)
+                               moves.push(this.getBasicMove([sx,sy], [ex,ey], {c:'c',p:takePiece}));
                }
-               let moves = []; //captures: generally 2 choices, unless 'tr' is specified or piece==king
-               const startPiece = this.getPiece(sx,sy);
-               const endPiece = this.getPiece(ex,ey);
-               const startColor = this.getColor(sx,sy);
-               const endColor = this.getColor(ex,ey);
-               for (let piece of !!tr ? [tr] :
-                       (startPiece==VariantRules.KING ? VariantRules.KING : _.uniq([startPiece,endPiece])))
-               {
-                       var mv = new Move({
-                               appear: [
-                                       new PiPo({
-                                               x: ex,
-                                               y: ey,
-                                               c: startPiece==VariantRules.KING ? startColor : 'c',
-                                               p: piece
-                                       })
-                               ],
-                               vanish: [
-                                       new PiPo({
-                                               x: sx,
-                                               y: sy,
-                                               c: startColor,
-                                               p: startPiece
-                                       }),
-                                       new PiPo({
-                                               x: ex,
-                                               y: ey,
-                                               c: endColor,
-                                               p: endPiece
-                                       })
-                               ]
-                       });
-                       moves.push(mv);
-               }
-               return moves;
+               else
+                       moves.push(this.getBasicMove([sx,sy], [ex,ey]));
        }
 
        // Generic method to find possible moves of non-pawn pieces ("sliding or jumping")
-       getSlideNJumpMoves(x, y, color, steps, oneStep)
+       getSlideNJumpMoves([x,y], steps, oneStep)
        {
-               var moves = [];
-               let [sizeX,sizeY] = VariantRules.size;
+               const color = this.getColor(x,y);
+               let moves = [];
+               const [sizeX,sizeY] = VariantRules.size;
                outerLoop:
                for (var loop=0; loop<steps.length; loop++)
                {
-                       var step = steps[loop];
-                       var i = x + step[0];
-                       var j = y + step[1];
-                       while (i>=0 && i<sizeX && j>=0 && j<sizeY
-                               && this.board[i][j] == VariantRules.EMPTY)
+                       let step = steps[loop];
+                       let i = x + step[0];
+                       let j = y + step[1];
+                       while (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j] == VariantRules.EMPTY)
                        {
-                               moves.push(this.getBasicMove(x, y, i, j)[0]); //no capture
+                               moves.push(this.getBasicMove([x,y], [i,j])); //no capture
                                if (oneStep !== undefined)
                                        continue outerLoop;
                                i += step[0];
                                j += step[1];
                        }
-                       if (i>=0 && i<8 && j>=0 && j<8 && this.canTake(color, this.getColor(i,j)))
-                               moves = moves.concat(this.getBasicMove(x, y, i, j));
+                       if (i>=0 && i<8 && j>=0 && j<8 && this.canTake([x,y], [i,j]))
+                               this.addCaptures([x,y], [i,j], moves);
                }
                return moves;
        }
 
        // What are the pawn moves from square x,y considering color "color" ?
-       getPotentialPawnMoves(x, y, color)
+       getPotentialPawnMoves([x,y])
        {
+               const color = this.getColor(x,y);
                var moves = [];
                var V = VariantRules;
                let [sizeX,sizeY] = VariantRules.size;
@@ -144,24 +115,18 @@ class CheckeredRules extends ChessRules
                        // Normal moves
                        if (this.board[x+shift][y] == V.EMPTY)
                        {
-                               moves.push(this.getBasicMove(x, y, x+shift, y)[0]);
+                               moves.push(this.getBasicMove([x,y], [x+shift,y]));
                                if (x==startRank && this.board[x+2*shift][y] == V.EMPTY && this.flags[1][c][y])
                                {
                                        // Two squares jump
-                                       moves.push(this.getBasicMove(x, y, x+2*shift, y)[0]);
+                                       moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
                                }
                        }
                        // Captures
-                       if (y>0 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y-1))
-                               && this.board[x+shift][y-1] != V.EMPTY)
-                       {
-                               moves = moves.concat(this.getBasicMove(x, y, x+shift, y-1));
-                       }
-                       if (y<sizeY-1 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y+1))
-                               && this.board[x+shift][y+1] != V.EMPTY)
-                       {
-                               moves = moves.concat(this.getBasicMove(x, y, x+shift, y+1));
-                       }
+                       if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+                               this.addCaptures([x,y], [x+shift,y-1], moves);
+                       if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+                               this.addCaptures([x,y], [x+shift,y+1], moves);
                }
 
                if (x+shift == lastRank)
@@ -171,18 +136,12 @@ class CheckeredRules extends ChessRules
                        promotionPieces.forEach(p => {
                                // Normal move
                                if (this.board[x+shift][y] == V.EMPTY)
-                                       moves.push(this.getBasicMove(x, y, x+shift, y, p)[0]);
+                                       moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
                                // Captures
-                               if (y>0 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y-1))
-                                       && this.board[x+shift][y-1] != V.EMPTY)
-                               {
-                                       moves = moves.concat(this.getBasicMove(x, y, x+shift, y-1, p));
-                               }
-                               if (y<sizeY-1 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y+1))
-                                       && this.board[x+shift][y+1] != V.EMPTY)
-                               {
-                                       moves = moves.concat(this.getBasicMove(x, y, x+shift, y+1, p));
-                               }
+                               if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+                                       moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:'c',p:p}));
+                               if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+                                       moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:'c',p:p}));
                        });
                }
 
@@ -192,7 +151,7 @@ class CheckeredRules extends ChessRules
                if (!!epSquare && epSquare.x == x+shift && Math.abs(epSquare.y - y) == 1)
                {
                        let epStep = epSquare.y - y;
-                       var enpassantMove = this.getBasicMove(x, y, x+shift, y+epStep)[0];
+                       var enpassantMove = this.getBasicMove([x,y], [x+shift,y+epStep]);
                        enpassantMove.vanish.push({
                                x: x,
                                y: y+epStep,
@@ -206,8 +165,9 @@ class CheckeredRules extends ChessRules
                return moves;
        }
 
-       getCastleMoves(x,y,c)
+       getCastleMoves([x,y])
        {
+               const c = this.getColor(x,y);
                if (x != (c=="w" ? 7 : 0) || y != this.INIT_COL_KING[c])
                        return []; //x isn't first rank, or king has moved (shortcut)
 
@@ -274,11 +234,10 @@ class CheckeredRules extends ChessRules
                return moves;
        }
 
-       canIplay(color, sq)
+       canIplay(side, [x,y])
        {
-               return ((color=='w' && this.moves.length%2==0) || color=='c'
-                               || (color=='b' && this.moves.length%2==1))
-                       && [color,'c'].includes(this.getColor(sq[0], sq[1]));
+               return ((side=='w' && this.moves.length%2==0) || (side=='b' && this.moves.length%2==1))
+                       && [side,'c'].includes(this.getColor(x,y));
        }
 
        // Does m2 un-do m1 ? (to disallow undoing checkered moves)
@@ -286,8 +245,6 @@ class CheckeredRules extends ChessRules
        {
                return m1.appear.length == 1 && m2.appear.length == 1
                        && m1.vanish.length == 1 && m2.vanish.length == 1
-//                     && _.isEqual(m1.appear[0], m2.vanish[0]) //fails in HH case
-//                     && _.isEqual(m1.vanish[0], m2.appear[0]);
                        && m1.start.x == m2.end.x && m1.end.x == m2.start.x
                        && m1.start.y == m2.end.y && m1.end.y == m2.start.y
                        && m1.appear[0].c == m2.vanish[0].c && m1.appear[0].p == m2.vanish[0].p
@@ -298,51 +255,53 @@ class CheckeredRules extends ChessRules
        {
                if (moves.length == 0)
                        return [];
-               let color = this.getColor( moves[0].start.x, moves[0].start.y );
+               const color = this.turn;
                return moves.filter(m => {
                        const L = this.moves.length;
                        if (L > 0 && this.oppositeMoves(this.moves[L-1], m))
                                return false;
-                       return !this.underCheck(m, color);
+                       return !this.underCheck(m);
                });
        }
 
-  isAttackedByPawn([x,y], c)
-  {
-               const color = (c=="c" ? this.turn : c);
-    let pawnShift = (color=="w" ? 1 : -1);
-    if (x+pawnShift>=0 && x+pawnShift<8)
-    {
-      for (let i of [-1,1])
-      {
-        if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==VariantRules.PAWN
-          && this.getColor(x+pawnShift,y+i)==c)
-        {
-          return true;
-        }
-      }
-    }
-    return false;
-  }
-
-       underCheck(move, c)
+       isAttackedByPawn([x,y], colors)
+       {
+               for (let c of colors)
+               {
+                       const color = (c=="c" ? this.turn : c);
+                       let pawnShift = (color=="w" ? 1 : -1);
+                       if (x+pawnShift>=0 && x+pawnShift<8)
+                       {
+                               for (let i of [-1,1])
+                               {
+                                       if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==VariantRules.PAWN
+                                               && this.getColor(x+pawnShift,y+i)==c)
+                                       {
+                                               return true;
+                                       }
+                               }
+                       }
+               }
+               return false;
+       }
+
+       underCheck(move)
        {
-               const color = c == 'c' ? this.turn : c;
+               const color = this.turn;
                this.play(move);
-               let res = this.isAttacked(this.kingPos[color], this.getOppCol(color))
-                       || this.isAttacked(this.kingPos[color], 'c'); //TODO: quite inefficient...
+               let res = this.isAttacked(this.kingPos[color], [this.getOppCol(color),'c']);
                this.undo(move);
                return res;
        }
 
-       getCheckSquares(move, c)
+       getCheckSquares(move)
        {
                this.play(move);
+               const color = this.turn;
                this.moves.push(move); //artifically change turn, for checkered pawns (TODO)
-               const kingAttacked = this.isAttacked(this.kingPos[c], this.getOppCol(c))
-                       || this.isAttacked(this.kingPos[c], 'c');
+               const kingAttacked = this.isAttacked(this.kingPos[color], [this.getOppCol(color),'c']);
                let res = kingAttacked
-                       ? [ JSON.parse(JSON.stringify(this.kingPos[c])) ] //need to duplicate!
+                       ? [ JSON.parse(JSON.stringify(this.kingPos[color])) ] //need to duplicate!
                        : [ ];
                this.moves.pop();
                this.undo(move);
@@ -387,8 +346,9 @@ class CheckeredRules extends ChessRules
                        this.flags[1][move.start.x==6 ? "w" : "b"][move.start.y] = false;
        }
 
-       checkGameEnd(color)
+       checkGameEnd()
        {
+               const color = this.turn;
                if (!this.isAttacked(this.kingPos[color], this.getOppCol(color))
                        && !this.isAttacked(this.kingPos[color], 'c'))
                {
index bfc0e0a..b7f229c 100644 (file)
@@ -7,8 +7,9 @@ class ZenRules extends ChessRules
        }
 
        // TODO: some duplicated code in 2 next functions
-       getSlideNJumpMoves(x, y, color, steps, oneStep)
+       getSlideNJumpMoves([x,y], steps, oneStep)
        {
+               const color = this.getColor(x,y);
                var moves = [];
                let [sizeX,sizeY] = VariantRules.size;
                outerLoop:
@@ -20,8 +21,8 @@ class ZenRules extends ChessRules
                        while (i>=0 && i<sizeX && j>=0 && j<sizeY
                                && this.board[i][j] == VariantRules.EMPTY)
                        {
-                               moves.push(this.getBasicMove(x, y, i, j));
-                               if (oneStep !== undefined)
+                               moves.push(this.getBasicMove([x,y], [i,j]));
+                               if (!!oneStep)
                                        continue outerLoop;
                                i += step[0];
                                j += step[1];
@@ -33,8 +34,9 @@ class ZenRules extends ChessRules
 
        // follow steps from x,y until something is met.
        // if met piece is opponent and same movement (asA): eat it!
-       findCaptures_aux(x, y, color, asA)
+       findCaptures_aux([x,y], asA)
        {
+               const color = this.getColor(x,y);
                var moves = [];
                var V = VariantRules;
                var steps = asA != V.PAWN
@@ -66,13 +68,13 @@ class ZenRules extends ChessRules
                                {
                                        // Special case of promotion:
                                        promotionPieces.forEach(p => {
-                                               moves.push(this.getBasicMove(x, y, i, j, p));
+                                               moves.push(this.getBasicMove([x,y], [i,j], {c:color,p:p}));
                                        });
                                }
                                else
                                {
                                        // All other cases
-                                       moves.push(this.getBasicMove(x, y, i, j));
+                                       moves.push(this.getBasicMove([x,y], [i,j]));
                                }
                        }
                }
@@ -80,35 +82,31 @@ class ZenRules extends ChessRules
        }
 
        // Find possible captures from a square: look in every direction!
-       findCaptures(x, y, color)
+       findCaptures(sq)
        {
                var moves = [];
 
                // PAWN
-               Array.prototype.push.apply(moves,
-                       this.findCaptures_aux(x, y, color, VariantRules.PAWN));
+               Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.PAWN));
 
                // ROOK
-               Array.prototype.push.apply(moves,
-                       this.findCaptures_aux(x, y, color, VariantRules.ROOK));
+               Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.ROOK));
 
                // KNIGHT
-               Array.prototype.push.apply(moves,
-                       this.findCaptures_aux(x, y, color, VariantRules.KNIGHT));
+               Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.KNIGHT));
 
                // BISHOP
-               Array.prototype.push.apply(moves,
-                       this.findCaptures_aux(x, y, color, VariantRules.BISHOP));
+               Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.BISHOP));
 
                // QUEEN
-               Array.prototype.push.apply(moves,
-                       this.findCaptures_aux(x, y, color, VariantRules.QUEEN));
+               Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.QUEEN));
 
                return moves;
        }
 
-       getPotentialPawnMoves(x, y, color)
+       getPotentialPawnMoves([x,y])
        {
+               const color = this.getColor(x,y);
                var moves = [];
                var V = VariantRules;
                let [sizeX,sizeY] = VariantRules.size;
@@ -122,11 +120,11 @@ class ZenRules extends ChessRules
                        // Normal moves
                        if (this.board[x+shift][y] == V.EMPTY)
                        {
-                               moves.push(this.getBasicMove(x, y, x+shift, y));
+                               moves.push(this.getBasicMove([x,y], [x+shift,y]));
                                if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY)
                                {
                                        //two squares jump
-                                       moves.push(this.getBasicMove(x, y, x+2*shift, y));
+                                       moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
                                }
                        }
                }
@@ -138,62 +136,52 @@ class ZenRules extends ChessRules
                        promotionPieces.forEach(p => {
                                // Normal move
                                if (this.board[x+shift][y] == V.EMPTY)
-                                       moves.push(this.getBasicMove(x, y, x+shift, y, p));
+                                       moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
                        });
                }
 
                // No en passant here
 
                // Add "zen" captures
-               Array.prototype.push.apply(moves, this.findCaptures(x, y, color));
+               Array.prototype.push.apply(moves, this.findCaptures([x,y]));
 
                return moves;
        }
 
-       getPotentialRookMoves(x, y, color)
+       getPotentialRookMoves(sq)
        {
-               let noCaptures = this.getSlideNJumpMoves(
-                       x, y, color, VariantRules.steps[VariantRules.ROOK]);
-               let captures = this.findCaptures(x, y, color);
+               let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.ROOK]);
+               let captures = this.findCaptures(sq);
                return noCaptures.concat(captures);
        }
 
-       getPotentialKnightMoves(x, y, color)
+       getPotentialKnightMoves(sq)
        {
-               let noCaptures = this.getSlideNJumpMoves(
-                       x, y, color, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
-               let captures = this.findCaptures(x, y, color);
+               let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
+               let captures = this.findCaptures(sq);
                return noCaptures.concat(captures);
        }
 
-       getPotentialBishopMoves(x, y, color)
+       getPotentialBishopMoves(sq)
        {
-               let noCaptures = this.getSlideNJumpMoves(
-                       x, y, color, VariantRules.steps[VariantRules.BISHOP]);
-               let captures = this.findCaptures(x, y, color);
+               let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.BISHOP]);
+               let captures = this.findCaptures(sq);
                return noCaptures.concat(captures);
        }
 
-       getPotentialQueenMoves(x, y, color)
+       getPotentialQueenMoves(sq)
        {
-               let noCaptures = this.getSlideNJumpMoves(
-                       x, y, color, VariantRules.steps[VariantRules.QUEEN]);
-               let captures = this.findCaptures(x, y, color);
+               let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.QUEEN]);
+               let captures = this.findCaptures(sq);
                return noCaptures.concat(captures);
        }
 
-       getPotentialKingMoves(x, y, c)
+       getPotentialKingMoves(sq)
        {
                // Initialize with normal moves
-               let noCaptures = this.getSlideNJumpMoves(
-                       x, y, c, VariantRules.steps[VariantRules.QUEEN], "oneStep");
-               let captures = this.findCaptures(x, y, c);
-
-               let moves = noCaptures
-                       .concat(captures)
-                       .concat(this.getCastleMoves(x, y, c));
-
-               return moves;
+               let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.QUEEN], "oneStep");
+               let captures = this.findCaptures(sq);
+               return noCaptures.concat(captures).concat(this.getCastleMoves(sq));
        }
 
        getNotation(move)