From f3f8470750e91ac7b5bf57a4e01c5791c54b65fb Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Sun, 17 Jan 2021 02:33:21 +0100
Subject: [PATCH] Prevent infinite loops for moves input in Pacosako + Otage

---
 client/src/variants/Otage.js    | 29 ++++++++++++++++++++++++++++-
 client/src/variants/Pacosako.js | 30 +++++++++++++++++++++++++++++-
 2 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/client/src/variants/Otage.js b/client/src/variants/Otage.js
index 938ef0ee..3e35767a 100644
--- a/client/src/variants/Otage.js
+++ b/client/src/variants/Otage.js
@@ -167,6 +167,7 @@ export class OtageRules extends ChessRules {
     super.setOtherVariables(fen);
     // Stack of "last move" only for intermediate chaining
     this.lastMoveEnd = [null];
+    this.repetitions = [];
   }
 
   static IsGoodFlags(flags) {
@@ -638,8 +639,24 @@ export class OtageRules extends ChessRules {
   getCheckSquares() {
     return [];
   }
+
   filterValid(moves) {
-    return moves;
+    if (moves.length == 0) return [];
+    return moves.filter(m => {
+      if (!m.end.released) return true;
+      // Check for repetitions:
+      V.PlayOnBoard(this.board, m);
+      const newState = { piece: m.end.released, position: this.getBaseFen() };
+      const repet =
+        this.repetitions.some(r => {
+          return (
+            r.piece == newState.piece &&
+            r.position == newState.position
+          );
+        });
+      V.UndoOnBoard(this.board, m);
+      return !repet;
+    });
   }
 
   updateCastleFlags(move, piece) {
@@ -702,6 +719,15 @@ export class OtageRules extends ChessRules {
     else
       this.lastMoveEnd.push(Object.assign({ p: move.end.released }, move.end));
     V.PlayOnBoard(this.board, move);
+    if (!move.end.released) this.repetitions = [];
+    else {
+      this.repetitions.push(
+        {
+          piece: move.end.released,
+          position: this.getBaseFen()
+        }
+      );
+    }
   }
 
   undo(move) {
@@ -713,6 +739,7 @@ export class OtageRules extends ChessRules {
       this.turn = V.GetOppCol(this.turn);
       this.movesCount--;
     }
+    if (!!move.end.releasd) this.repetitions.pop();
     this.postUndo(move);
   }
 
diff --git a/client/src/variants/Pacosako.js b/client/src/variants/Pacosako.js
index 218980c4..efc8fd38 100644
--- a/client/src/variants/Pacosako.js
+++ b/client/src/variants/Pacosako.js
@@ -139,6 +139,8 @@ export class PacosakoRules extends ChessRules {
         end: ChessRules.SquareToCoords(umove.substr(2))
       });
     }
+    // Local stack of positions to avoid redundant moves:
+    this.repetitions = [];
   }
 
   static IsGoodFen(fen) {
@@ -705,10 +707,26 @@ export class PacosakoRules extends ChessRules {
   getCheckSquares() {
     return [];
   }
+
   filterValid(moves) {
     if (moves.length == 0) return [];
     const L = this.umoves.length; //at least 1: init from FEN
-    return moves.filter(m => !this.oppositeMoves(this.umoves[L - 1], m));
+    return moves.filter(m => {
+      if (this.oppositeMoves(this.umoves[L - 1], m)) return false;
+      if (!m.end.released) return true;
+      // Check for repetitions:
+      V.PlayOnBoard(this.board, m);
+      const newState = { piece: m.end.released, position: this.getBaseFen() };
+      const repet =
+        this.repetitions.some(r => {
+          return (
+            r.piece == newState.piece &&
+            r.position == newState.position
+          );
+        });
+      V.UndoOnBoard(this.board, m);
+      return !repet;
+    });
   }
 
   updateCastleFlags(move, piece) {
@@ -778,6 +796,15 @@ export class PacosakoRules extends ChessRules {
     }
     V.PlayOnBoard(this.board, move);
     this.umoves.push(this.getUmove(move));
+    if (!move.end.released) this.repetitions = [];
+    else {
+      this.repetitions.push(
+        {
+          piece: move.end.released,
+          position: this.getBaseFen()
+        }
+      );
+    }
   }
 
   undo(move) {
@@ -790,6 +817,7 @@ export class PacosakoRules extends ChessRules {
       this.movesCount--;
     }
     this.umoves.pop();
+    if (!!move.end.releasd) this.repetitions.pop();
     this.postUndo(move);
   }
 
-- 
2.44.0