Draft Checkless. Add 'trackKingWrap' method, untested. Use 'canSelfTake'
[xogo.git] / base_rules.js
index aa499f8..0c316eb 100644 (file)
@@ -640,7 +640,7 @@ export default class ChessRules {
           const color = this.getColor(i, j);
           const piece = this.getPiece(i, j);
           addPiece(i, j, "g_pieces", this.pieces(color, i, j)[piece]["class"]);
-          this.g_pieces[i][j].classList.add(C.GetColorClass(color));
+          this.g_pieces[i][j].classList.add(V.GetColorClass(color));
           if (this.enlightened && !this.enlightened[i][j])
             this.g_pieces[i][j].classList.add("hidden");
         }
@@ -707,7 +707,7 @@ export default class ChessRules {
         rcontainer.appendChild(r_cell);
         let piece = document.createElement("piece");
         C.AddClass_es(piece, this.pieces(c, c, p)[p]["class"]);
-        piece.classList.add(C.GetColorClass(c));
+        piece.classList.add(V.GetColorClass(c));
         piece.style.width = "100%";
         piece.style.height = "100%";
         this.r_pieces[c][p] = piece;
@@ -1016,7 +1016,7 @@ export default class ChessRules {
       const cdisp = moves[i].choice || moves[i].appear[0].p;
       C.AddClass_es(piece,
         this.pieces(color, moves[i].end.x, moves[i].end.y)[cdisp]["class"]);
-      piece.classList.add(C.GetColorClass(color));
+      piece.classList.add(V.GetColorClass(color));
       piece.style.width = "100%";
       piece.style.height = "100%";
       choice.appendChild(piece);
@@ -1321,6 +1321,11 @@ export default class ChessRules {
     return this.getColor(x1, y1) !== this.getColor(x2, y2);
   }
 
+  // Teleport & Recycle. Assumption: color(x1,y1) == color(x2,y2)
+  canSelfTake([x1, y1], [x2, y2]) {
+    return !this.isKing(x2, y2);
+  }
+
   canStepOver(i, j, p) {
     // In some variants, objects on boards don't stop movement (Chakart)
     return this.board[i][j] == "";
@@ -1483,6 +1488,7 @@ export default class ChessRules {
   }
 
   // All possible moves from selected square
+  // TODO: generalize usage if arg "color" (e.g. Checkered)
   getPotentialMovesFrom([x, y], color) {
     if (this.subTurnTeleport == 2)
       return [];
@@ -1717,14 +1723,18 @@ export default class ChessRules {
           segments: this.options["cylinder"],
           stepSpec: stepSpec
         },
-        ([i1, j1], [i2, j2]) =>
-          this.getColor(i2, j2) == color && !this.isKing(i2, j2)
+        ([i1, j1], [i2, j2]) => {
+          return (
+            this.getColor(i2, j2) == color &&
+            this.canSelfTake([i1, j1], [i2, j2])
+          );
+        }
       );
       Array.prototype.push.apply(squares, selfCaptures);
     }
     return squares.map(s => {
       let mv = this.getBasicMove([x, y], s.sq);
-      if (this.options["cylinder"] && s.segments.length >= 2)
+      if (this.options["cylinder"] && !!s.segments && s.segments.length >= 2)
         mv.segments = s.segments;
       return mv;
     });
@@ -1742,7 +1752,7 @@ export default class ChessRules {
     const addSquare = ([i, j]) => {
       let elt = {sq: [i, j]};
       if (o.segments)
-        elt.segments = this.getSegments(segments, segStart, end);
+        elt.segments = this.getSegments(segments, segStart, [i, j]);
       res.push(elt);
     };
     const exploreSteps = (stepArray, mode) => {
@@ -1849,9 +1859,7 @@ export default class ChessRules {
                 {
                   captureTarget: [x, y],
                   captureSteps: [{steps: [s], range: a.range}],
-                  segments: o.segments,
-                  attackOnly: true,
-                  one: false //one and captureTarget are mutually exclusive
+                  segments: o.segments
                 },
                 allowed
               );
@@ -1989,7 +1997,7 @@ export default class ChessRules {
       oppCols.includes(this.getColor(x, this.epSquare.y))
     ) {
       const [epx, epy] = [this.epSquare.x, this.epSquare.y];
-      this.board[epx][epy] = this.board[x][this.epSquares.y];
+      this.board[epx][epy] = this.board[x][this.epSquare.y];
       let enpassantMove = this.getBasicMove([x, y], [epx, epy]);
       this.board[epx][epy] = "";
       const lastIdx = enpassantMove.vanish.length - 1; //think Rifle
@@ -2161,42 +2169,44 @@ export default class ChessRules {
     return res;
   }
 
+  // cb: callback returning a boolean (false if king missing)
+  trackKingWrap(move, kingPos, cb) {
+    let newKingPP = null,
+        sqIdx = 0,
+        res = true; //a priori valid
+    const oldKingPP =
+      move.vanish.find(v => this.isKing(0, 0, v.p) && v.c == color);
+    if (oldKingPP) {
+      // Search king in appear array:
+      newKingPP =
+        move.appear.find(a => this.isKing(0, 0, a.p) && a.c == color);
+      if (newKingPP) {
+        sqIdx = kingPos.findIndex(kp =>
+          kp[0] == oldKingPP.x && kp[1] == oldKingPP.y);
+        kingPos[sqIdx] = [newKingPP.x, newKingPP.y];
+      }
+      else
+        res = false; //king vanished
+    }
+    res &&= cb(kingPos);
+    if (oldKingPP && newKingPP)
+      kingPos[sqIdx] = [oldKingPP.x, oldKingPP.y];
+    return res;
+  }
+
   // 'color' arg because some variants (e.g. Refusal) check opponent moves
   filterValid(moves, color) {
     if (!color)
       color = this.turn;
     const oppCols = this.getOppCols(color);
     let kingPos = this.searchKingPos(color);
-    let filtered = {}; //avoid re-checking similar moves (promotions...)
     return moves.filter(m => {
-      const key = m.start.x + m.start.y + '.' + m.end.x + m.end.y;
-      if (!filtered[key]) {
-        this.playOnBoard(m);
-        let newKingPP = null,
-            sqIdx = 0,
-            res = true; //a priori valid
-        const oldKingPP =
-          m.vanish.find(v => this.isKing(0, 0, v.p) && v.c == color);
-        if (oldKingPP) {
-          // Search king in appear array:
-          newKingPP =
-            m.appear.find(a => this.isKing(0, 0, a.p) && a.c == color);
-          if (newKingPP) {
-            sqIdx = kingPos.findIndex(kp =>
-              kp[0] == oldKingPP.x && kp[1] == oldKingPP.y);
-            kingPos[sqIdx] = [newKingPP.x, newKingPP.y];
-          }
-          else
-            res = false; //king vanished
-        }
-        res &&= !this.underCheck(kingPos, oppCols);
-        if (oldKingPP && newKingPP)
-          kingPos[sqIdx] = [oldKingPP.x, oldKingPP.y];
-        this.undoOnBoard(m);
-        filtered[key] = res;
-        return res;
-      }
-      return filtered[key];
+      this.playOnBoard(m);
+      const res = this.trackKingWrap(m, kingPos, (kp) => {
+        return !this.underCheck(kp, oppCols);
+      });
+      this.undoOnBoard(m);
+      return res;
     });
   }
 
@@ -2328,7 +2338,7 @@ export default class ChessRules {
 
   tryChangeTurn(move) {
     if (this.isLastMove(move)) {
-      this.turn = (this.turn == 'w' ? 'b' : 'w');
+      this.turn = C.GetOppTurn(this.turn);
       this.movesCount++;
       this.subTurn = 1;
     }
@@ -2370,7 +2380,7 @@ export default class ChessRules {
         if (this.board[i][j] != "" && this.getColor(i, j) == color) {
           // NOTE: in fact searching for all potential moves from i,j.
           //       I don't believe this is an issue, for now at least.
-          const moves = this.getPotentialMovesFrom([i, j]);
+          const moves = this.getPotentialMovesFrom([i, j], color);
           if (moves.some(m => this.filterValid([m]).length >= 1))
             return true;
         }
@@ -2400,9 +2410,9 @@ export default class ChessRules {
     if (kingPos[this.turn].length == 0 && kingPos[oppTurn].length == 0)
       return "1/2";
     if (kingPos[this.turn].length == 0)
-      return (color == "w" ? "0-1" : "1-0");
+      return (this.turn == "w" ? "0-1" : "1-0");
     if (kingPos[oppTurn].length == 0)
-      return (color == "w" ? "1-0" : "0-1");
+      return (this.turn == "w" ? "1-0" : "0-1");
     if (this.atLeastOneMove(this.turn))
       return "*";
     // No valid move: stalemate or checkmate?
@@ -2414,7 +2424,8 @@ export default class ChessRules {
 
   playVisual(move, r) {
     move.vanish.forEach(v => {
-      this.g_pieces[v.x][v.y].remove();
+      if (this.g_pieces[v.x][v.y]) //can be null (e.g. Apocalypse)
+        this.g_pieces[v.x][v.y].remove();
       this.g_pieces[v.x][v.y] = null;
     });
     let chessboard =
@@ -2426,7 +2437,7 @@ export default class ChessRules {
       this.g_pieces[a.x][a.y] = document.createElement("piece");
       C.AddClass_es(this.g_pieces[a.x][a.y],
                     this.pieces(a.c, a.x, a.y)[a.p]["class"]);
-      this.g_pieces[a.x][a.y].classList.add(C.GetColorClass(a.c));
+      this.g_pieces[a.x][a.y].classList.add(V.GetColorClass(a.c));
       this.g_pieces[a.x][a.y].style.width = pieceWidth + "px";
       this.g_pieces[a.x][a.y].style.height = pieceWidth + "px";
       const [ip, jp] = this.getPixelPosition(a.x, a.y, r);
@@ -2483,6 +2494,10 @@ export default class ChessRules {
 
   animateMoving(start, end, drag, segments, cb) {
     let initPiece = this.getDomPiece(start.x, start.y);
+    if (!initPiece) { //TODO: shouldn't occur!
+      cb();
+      return;
+    }
     // NOTE: cloning often not required, but light enough, and simpler
     let movingPiece = initPiece.cloneNode();
     initPiece.style.opacity = "0";
@@ -2503,8 +2518,8 @@ export default class ChessRules {
       C.RemoveClass_es(movingPiece, pieces[startCode]["class"]);
       C.AddClass_es(movingPiece, pieces[drag.p]["class"]);
       if (apparentColor != drag.c) {
-        movingPiece.classList.remove(C.GetColorClass(apparentColor));
-        movingPiece.classList.add(C.GetColorClass(drag.c));
+        movingPiece.classList.remove(V.GetColorClass(apparentColor));
+        movingPiece.classList.add(V.GetColorClass(drag.c));
       }
     }
     container.appendChild(movingPiece);