Generalize code in VariantRules. Considerable speed-up for checkered bot. Prepare...
[vchess.git] / public / javascripts / variants / Checkered.js
index 322d8fc..6ed73a8 100644 (file)
@@ -32,24 +32,6 @@ class CheckeredRules extends ChessRules
                return ChessRules.fen2board(f);
        }
 
-       initVariables(fen)
-       {
-               super.initVariables(fen);
-               // Decode last non-capturing checkered move (if any)
-               const cmove = fen.split(" ")[4];
-               if (cmove != "-")
-               {
-                       const piece = cmove.charAt(0);
-                       const startEnd = cmove.substr(1).split(";");
-                       const start = startEnd[0].split(",");
-                       const end = startEnd[1].split(",");
-                       this.moves.push(new Move({
-                               appear: [ new PiPo({c:"c", p:piece, x:end[0], y:end[1]}) ],
-                               vanish: [ new PiPo({c:"c", p:piece, x:start[0], y:start[1]}) ]
-                       }));
-               }
-       }
-
        static GetFlags(fen)
        {
                let flags = [
@@ -68,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)
-               {
-                       // No capture, standard move construction
-                       return [super.getBasicMove(sx,sy,ex,ey,tr)];
-               }
-               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])))
+               const piece = this.getPiece(sx,sy);
+               if (piece != VariantRules.KING)
                {
-                       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);
+                       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}));
                }
-               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;
@@ -162,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)
@@ -189,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}));
                        });
                }
 
@@ -210,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,
@@ -224,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)
 
@@ -292,11 +234,10 @@ class CheckeredRules extends ChessRules
                return moves;
        }
 
-       canIplay(color, sq)
+       canIplay(side, [x,y])
        {
-               return ((color=='w' && this.movesCount%2==0) || color=='c'
-                               || (color=='b' && this.movesCount%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)
@@ -304,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
@@ -316,39 +255,55 @@ 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)
        {
-               const color = c == 'c' ? this.turn : c;
+               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 = 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)
+       {
+               this.play(move);
+               const color = this.turn;
+               this.moves.push(move); //artifically change turn, for checkered pawns (TODO)
+               const kingAttacked = this.isAttacked(this.kingPos[color], [this.getOppCol(color),'c']);
+               let res = kingAttacked
+                       ? [ JSON.parse(JSON.stringify(this.kingPos[color])) ] //need to duplicate!
+                       : [ ];
+               this.moves.pop();
                this.undo(move);
                return res;
        }
@@ -391,21 +346,9 @@ class CheckeredRules extends ChessRules
                        this.flags[1][move.start.x==6 ? "w" : "b"][move.start.y] = false;
        }
 
-       play(move, ingame)
-       {
-               super.play(move, ingame);
-               if (!ingame)
-                       this.moves.push(move); //needed for turn indication for checkered pieces
-       }
-
-       undo(move)
-       {
-               super.undo(move);
-               this.moves.pop();
-       }
-
-       checkGameEnd(color)
+       checkGameEnd()
        {
+               const color = this.turn;
                if (!this.isAttacked(this.kingPos[color], this.getOppCol(color))
                        && !this.isAttacked(this.kingPos[color], 'c'))
                {
@@ -437,23 +380,7 @@ class CheckeredRules extends ChessRules
 
        static GenRandInitFen()
        {
-               let fen = ChessRules.GenRandInitFen();
-               return fen.replace(/ - 0$/, "1111111111111111 - 0 -");
-       }
-
-       getFen()
-       {
-               let fen = super.getFen() + " ";
-               const L = this.moves.length;
-               if (L > 0 && this.moves[L-1].vanish.length==1 && this.moves[L-1].appear[0].c=="c")
-               {
-                       // Ok, un-cancellable checkered move
-                       fen += this.moves[L-1].appear[0].p
-                               + this.moves[L-1].start.x + "," + this.moves[L-1].start.y + ";"
-                               + this.moves[L-1].end.x + "," + this.moves[L-1].end.y;
-               }
-               else fen += "-";
-               return fen;
+               return ChessRules.GenRandInitFen() + "1111111111111111"; //add 16 pawns flags
        }
 
        getFlagsFen()