Finish Ultima rules + a few technical fixes
[vchess.git] / public / javascripts / base_rules.js
index 8de0176..63b535d 100644 (file)
@@ -727,8 +727,18 @@ class ChessRules
                        this.kingPos[c] = [move.start.x, move.start.y];
        }
 
+       // Hash of position+flags+turn after a move is played (to detect repetitions)
+       getHashState()
+       {
+               return hex_md5(this.getFen() + " " + this.turn);
+       }
+
        play(move, ingame)
        {
+               // DEBUG:
+//             if (!this.states) this.states = [];
+//             if (!ingame) this.states.push(JSON.stringify(this.board));
+
                if (!!ingame)
                        move.notation = [this.getNotation(move), this.getLongNotation(move)];
 
@@ -737,6 +747,9 @@ class ChessRules
                this.moves.push(move);
                this.epSquares.push( this.getEpSquare(move) );
                VariantRules.PlayOnBoard(this.board, move);
+
+               if (!!ingame)
+                       move.hash = this.getHashState();
        }
 
        undo(move)
@@ -746,27 +759,34 @@ class ChessRules
                this.moves.pop();
                this.unupdateVariables(move);
                this.parseFlags(JSON.parse(move.flags));
+
+               // DEBUG:
+//             if (JSON.stringify(this.board) != this.states[this.states.length-1])
+//                     debugger;
+//             this.states.pop();
        }
 
        //////////////
        // END OF GAME
 
-       // Basic check for 3 repetitions (in the last moves only)
-       // TODO: extend to usual 3-repetition recognition (storing FEN with move?)
+       // Check for 3 repetitions (position + flags + turn)
        checkRepetition()
        {
-               if (this.moves.length >= 8)
+               if (!this.hashStates)
+                       this.hashStates = {};
+               const startIndex =
+                       Object.values(this.hashStates).reduce((a,b) => { return a+b; }, 0)
+               // Update this.hashStates with last move (or all moves if continuation)
+               // NOTE: redundant storage, but faster and moderate size
+               for (let i=startIndex; i<this.moves.length; i++)
                {
-                       const L = this.moves.length;
-                       if (_.isEqual(this.moves[L-1], this.moves[L-5]) &&
-                               _.isEqual(this.moves[L-2], this.moves[L-6]) &&
-                               _.isEqual(this.moves[L-3], this.moves[L-7]) &&
-                               _.isEqual(this.moves[L-4], this.moves[L-8]))
-                       {
-                               return true;
-                       }
+                       const move = this.moves[i];
+                       if (!this.hashStates[move.hash])
+                               this.hashStates[move.hash] = 1;
+                       else
+                               this.hashStates[move.hash]++;
                }
-               return false;
+               return Object.values(this.hashStates).some(elt => { return (elt >= 3); });
        }
 
        // Is game over ? And if yes, what is the score ?