Fix incheck by pawns for checkered
[vchess.git] / public / javascripts / base_rules.js
index 0a5c0e7..11de147 100644 (file)
@@ -46,14 +46,14 @@ class ChessRules
        /////////////////
        // INITIALIZATION
 
-       // fen = "position flags epSquare movesCount"
+       // fen == "position flags"
        constructor(fen, moves)
        {
                this.moves = moves;
                // Use fen string to initialize variables, flags and board
-               this.initVariables(fen);
-               this.flags = VariantRules.GetFlags(fen);
                this.board = VariantRules.GetBoard(fen);
+               this.flags = VariantRules.GetFlags(fen);
+               this.initVariables(fen);
        }
 
        initVariables(fen)
@@ -98,14 +98,8 @@ class ChessRules
                                j++;
                        }
                }
-               let epSq = undefined;
-               if (fenParts[2] != "-")
-               {
-                       const digits = fenParts[2].split(","); //3,2 ...
-                       epSq = { x:Number.parseInt(digits[0]), y:Number.parseInt(digits[1]) };
-               }
+               const epSq = this.moves.length > 0 ? this.getEpSquare(this.lastMove) : undefined;
                this.epSquares = [ epSq ];
-               this.movesCount = Number.parseInt(fenParts[3]);
        }
 
        // Turn diagram fen into double array ["wb","wp","bk",...]
@@ -158,7 +152,7 @@ class ChessRules
                return L>0 ? this.moves[L-1] : null;
        }
        get turn() {
-               return this.movesCount%2==0 ? 'w' : 'b';
+               return this.moves.length%2==0 ? 'w' : 'b';
        }
 
        // Pieces codes
@@ -476,8 +470,8 @@ class ChessRules
 
        canIplay(color, sq)
        {
-               return ((color=='w' && this.movesCount%2==0)
-                               || (color=='b' && this.movesCount%2==1))
+               return ((color=='w' && this.moves.length%2==0)
+                               || (color=='b' && this.moves.length%2==1))
                        && this.getColor(sq[0], sq[1]) == color;
        }
 
@@ -517,6 +511,32 @@ class ChessRules
                // No: if happen on last 1/2 move, could lead to forbidden moves, wrong evals
                return this.filterValid(potentialMoves);
        }
+       
+       // Stop at the first move found
+       atLeastOneMove(color)
+       {
+               const oppCol = this.getOppCol(color);
+               let [sizeX,sizeY] = VariantRules.size;
+               for (var i=0; i<sizeX; i++)
+               {
+                       for (var j=0; j<sizeY; j++)
+                       {
+                               if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) != oppCol)
+                               {
+                                       const moves = this.getPotentialMovesFrom([i,j]);
+                                       if (moves.length > 0)
+                                       {
+                                               for (let i=0; i<moves.length; i++)
+                                               {
+                                                       if (this.filterValid([moves[i]]).length > 0)
+                                                               return true;
+                                               }
+                                       }
+                               }
+                       }
+               }
+               return false;
+       }
 
        // Check if pieces of color 'color' are attacking square x,y
        isAttacked(sq, color)
@@ -603,6 +623,7 @@ class ChessRules
                return false;
        }
 
+       // Is color c under check after move ?
        underCheck(move, c)
        {
                this.play(move);
@@ -611,6 +632,17 @@ class ChessRules
                return res;
        }
 
+       // On which squares is color c under check (after move) ?
+       getCheckSquares(move, c)
+       {
+               this.play(move);
+               let res = this.isAttacked(this.kingPos[c], this.getOppCol(c))
+                       ? [ JSON.parse(JSON.stringify(this.kingPos[c])) ] //need to duplicate!
+                       : [ ];
+               this.undo(move);
+               return res;
+       }
+
        // Apply a move on board
        static PlayOnBoard(board, move)
        {
@@ -628,7 +660,7 @@ class ChessRules
                        board[psq.x][psq.y] = psq.c + psq.p;
        }
 
-       // Before move is played:
+       // Before move is played, update variables + flags
        updateVariables(move)
        {
                const piece = this.getPiece(move.start.x,move.start.y);
@@ -659,34 +691,33 @@ class ChessRules
                }
        }
 
-       play(move, ingame)
+       unupdateVariables(move)
        {
-               // Save flags (for undo)
-               move.flags = JSON.stringify(this.flags); //TODO: less costly
-               this.updateVariables(move);
+               // (Potentially) Reset king position
+               const c = this.getColor(move.start.x,move.start.y);
+               if (this.getPiece(move.start.x,move.start.y) == VariantRules.KING)
+                       this.kingPos[c] = [move.start.x, move.start.y];
+       }
 
+       play(move, ingame)
+       {
                if (!!ingame)
-               {
                        move.notation = this.getNotation(move);
-                       this.moves.push(move);
-               }
 
+               // Save flags (for undo)
+               move.flags = JSON.stringify(this.flags); //TODO: less costly?
+               this.updateVariables(move);
+               this.moves.push(move);
                this.epSquares.push( this.getEpSquare(move) );
                VariantRules.PlayOnBoard(this.board, move);
-               this.movesCount++;
        }
 
        undo(move)
        {
                VariantRules.UndoOnBoard(this.board, move);
                this.epSquares.pop();
-               this.movesCount--;
-
-               // Update king position, and reset stored/computed flags
-               const c = this.getColor(move.start.x,move.start.y);
-               if (this.getPiece(move.start.x,move.start.y) == VariantRules.KING)
-                       this.kingPos[c] = [move.start.x, move.start.y];
-
+               this.moves.pop();
+               this.unupdateVariables(move);
                this.flags = JSON.parse(move.flags);
        }
 
@@ -709,8 +740,7 @@ class ChessRules
                        }
                }
 
-               // TODO: not required to generate ALL: just need one (callback ? hook ? ...)
-               if (this.getAllValidMoves(color).length > 0)
+               if (this.atLeastOneMove(color))
                {
                        // game not over
                        return "*";
@@ -779,14 +809,14 @@ class ChessRules
                moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
 
                // TODO: show current analyzed move for depth 3, allow stopping eval (return moves1[0])
-//             for (let i=0; i<moves1.length; i++)
-//             {
-//                     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);
-//                     this.undo(moves1[i]);
-//             }
-//             moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
+               for (let i=0; i<moves1.length; i++)
+               {
+                       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);
+                       this.undo(moves1[i]);
+               }
+               moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
 
                let candidates = [0]; //indices of candidates moves
                for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++)
@@ -798,8 +828,7 @@ class ChessRules
 
        alphabeta(color, oppCol, depth, alpha, beta)
   {
-               let moves = this.getAllValidMoves(color);
-               if (moves.length == 0)
+               if (!this.atLeastOneMove(color))
                {
                        switch (this.checkGameEnd(color))
                        {
@@ -809,6 +838,7 @@ class ChessRules
                }
                if (depth == 0)
       return this.evalPosition();
+               const moves = this.getAllValidMoves(color);
     let v = color=="w" ? -1000 : 1000;
                if (color == "w")
                {
@@ -909,19 +939,14 @@ class ChessRules
                let fen = pieces[0].join("") +
                        "/pppppppp/8/8/8/8/PPPPPPPP/" +
                        pieces[1].join("").toUpperCase() +
-                       " 1111 - 0"; //flags + enPassant + movesCount
+                       " 1111"; //add flags
                return fen;
        }
 
        // Return current fen according to pieces+colors state
        getFen()
        {
-               const L = this.epSquares.length;
-               const epSq = this.epSquares[L-1]===undefined
-                       ? "-"
-                       : this.epSquares[L-1].x+","+this.epSquares[L-1].y;
-               return this.getBaseFen() + " " + this.getFlagsFen()
-                       + " " + epSq + " " + this.movesCount;
+               return this.getBaseFen() + " " + this.getFlagsFen();
        }
 
        getBaseFen()
@@ -1012,7 +1037,7 @@ class ChessRules
        }
 
        // The score is already computed when calling this function
-       getPGN(mycolor, score)
+       getPGN(mycolor, score, fenStart)
        {
                let pgn = "";
                pgn += '[Site "vchess.club"]<br>';
@@ -1020,7 +1045,8 @@ class ChessRules
                pgn += '[Date "' + d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate() + '"]<br>';
                pgn += '[White "' + (mycolor=='w'?'Myself':'Anonymous') + '"]<br>';
                pgn += '[Black "' + (mycolor=='b'?'Myself':'Anonymous') + '"]<br>';
-               pgn += '[Result "' + score + '"]<br>';
+               pgn += '[Fen "' + fenStart + '"]<br>';
+               pgn += '[Result "' + score + '"]<br><br>';
 
                for (let i=0; i<this.moves.length; i++)
                {