Reorganize folders (untested Baroque). Draft Go
[xogo.git] / variants / Baroque / class.js
index 98ef1c7..edf1dd5 100644 (file)
@@ -1,10 +1,11 @@
 import ChessRules from "/base_rules.js";
 import GiveawayRules from "/variants/Giveaway/class.js";
+import AbstractSpecialCaptureRules from "/variants/_SpecialCaptures.js";
 import {Random} from "/utils/alea.js";
 import PiPo from "/utils/PiPo.js";
 import Move from "/utils/Move.js";
 
-export default class BaroqueRules extends ChessRules {
+export default class BaroqueRules extends AbstractSpecialCaptureRules {
 
   static get Options() {
     return {
@@ -33,9 +34,6 @@ export default class BaroqueRules extends ChessRules {
   get hasFlags() {
     return false;
   }
-  get hasEnpassant() {
-    return false;
-  }
 
   genRandInitBaseFen() {
     if (this.options["randomness"] == 0)
@@ -61,25 +59,18 @@ export default class BaroqueRules extends ChessRules {
     return res;
   }
 
-  // Although other pieces keep their names here for coding simplicity,
-  // keep in mind that:
-  //  - a "rook" is a coordinator, capturing by coordinating with the king
-  //  - a "knight" is a long-leaper, capturing as in draughts
-  //  - a "bishop" is a chameleon, capturing as its prey
-  //  - a "queen" is a withdrawer, capturing by moving away from pieces
-
   pieces() {
     return Object.assign({},
       super.pieces(),
       {
         'p': {
-          "class": "pawn",
+          "class": "pawn", //pincer
           moves: [
             {steps: [[0, 1], [0, -1], [1, 0], [-1, 0]]}
           ]
         },
         'r': {
-          "class": "rook",
+          "class": "rook", //coordinator
           moves: [
             {
               steps: [
@@ -90,20 +81,20 @@ export default class BaroqueRules extends ChessRules {
           ]
         },
         'n': {
-          "class": "knight",
+          "class": "knight", //long-leaper
           moveas: 'r'
         },
         'b': {
-          "class": "bishop",
+          "class": "bishop", //chameleon
           moveas: 'r'
         },
         'q': {
-          "class": "queen",
+          "class": "queen", //withdrawer
           moveas: 'r'
         },
         'i': {
           "class": "immobilizer",
-          moveas: 'q'
+          moveas: 'r'
         }
       }
     );
@@ -162,236 +153,23 @@ export default class BaroqueRules extends ChessRules {
       return [];
     switch (moves[0].vanish[0].p) {
       case 'p':
-        this.addPawnCaptures(moves);
+        this.addPincerCaptures(moves);
         break;
       case 'r':
-        this.addRookCaptures(moves);
+        this.addCoordinatorCaptures(moves);
         break;
       case 'n':
         const [x, y] = [moves[0].start.x, moves[0].start.y];
-        moves = moves.concat(this.getKnightCaptures([x, y]));
+        moves = moves.concat(this.getLeaperCaptures([x, y]));
         break;
       case 'b':
-        moves = this.getBishopCaptures(moves);
+        moves = this.getChameleonCaptures(moves, "pull");
         break;
       case 'q':
-        this.addQueenCaptures(moves);
+        this.addPushmePullyouCaptures(moves, false, "pull");
         break;
     }
     return moves;
   }
 
-  // Modify capturing moves among listed pawn moves
-  addPawnCaptures(moves, byChameleon) {
-    const steps = this.pieces()['p'].moves[0].steps;
-    const color = this.turn;
-    const oppCol = C.GetOppCol(color);
-    moves.forEach(m => {
-      if (byChameleon && m.start.x != m.end.x && m.start.y != m.end.y)
-        // Chameleon not moving as pawn
-        return;
-      // Try capturing in every direction
-      for (let step of steps) {
-        const sq2 = [m.end.x + 2 * step[0], this.getY(m.end.y + 2 * step[1])];
-        if (
-          this.onBoard(sq2[0], sq2[1]) &&
-          this.board[sq2[0]][sq2[1]] != "" &&
-          this.getColor(sq2[0], sq2[1]) == color
-        ) {
-          // Potential capture
-          const sq1 = [m.end.x + step[0], this.getY(m.end.y + step[1])];
-          if (
-            this.board[sq1[0]][sq1[1]] != "" &&
-            this.getColor(sq1[0], sq1[1]) == oppCol
-          ) {
-            const piece1 = this.getPiece(sq1[0], sq1[1]);
-            if (!byChameleon || piece1 == 'p') {
-              m.vanish.push(
-                new PiPo({
-                  x: sq1[0],
-                  y: sq1[1],
-                  c: oppCol,
-                  p: piece1
-                })
-              );
-            }
-          }
-        }
-      }
-    });
-  }
-
-  addRookCaptures(moves, byChameleon) {
-    const color = this.turn;
-    const oppCol = V.GetOppCol(color);
-    const kp = this.searchKingPos(color)[0];
-    moves.forEach(m => {
-      // Check piece-king rectangle (if any) corners for enemy pieces
-      if (m.end.x == kp[0] || m.end.y == kp[1])
-        return; //"flat rectangle"
-      const corner1 = [m.end.x, kp[1]];
-      const corner2 = [kp[0], m.end.y];
-      for (let [i, j] of [corner1, corner2]) {
-        if (this.board[i][j] != "" && this.getColor(i, j) == oppCol) {
-          const piece = this.getPiece(i, j);
-          if (!byChameleon || piece == 'r') {
-            m.vanish.push(
-              new PiPo({
-                x: i,
-                y: j,
-                p: piece,
-                c: oppCol
-              })
-            );
-          }
-        }
-      }
-    });
-  }
-
-  getKnightCaptures(startSquare, byChameleon) {
-    // Look in every direction for captures
-    const steps = this.pieces()['r'].moves[0].steps;
-    const color = this.turn;
-    const oppCol = C.GetOppCol(color);
-    let moves = [];
-    const [x, y] = [startSquare[0], startSquare[1]];
-    const piece = this.getPiece(x, y); //might be a chameleon!
-    outerLoop: for (let step of steps) {
-      let [i, j] = [x + step[0], this.getY(y + step[1])];
-      while (this.onBoard(i, j) && this.board[i][j] == "")
-        [i, j] = [i + step[0], this.getY(j + step[1])];
-      if (
-        !this.onBoard(i, j) ||
-        this.getColor(i, j) == color ||
-        (byChameleon && this.getPiece(i, j) != 'n')
-      ) {
-        continue;
-      }
-      // last(thing), cur(thing) : stop if "cur" is our color,
-      // or beyond board limits, or if "last" isn't empty and cur neither.
-      // Otherwise, if cur is empty then add move until cur square;
-      // if cur is occupied then stop if !!byChameleon and the square not
-      // occupied by a leaper.
-      let last = [i, j];
-      let cur = [i + step[0], this.getY(j + step[1])];
-      let vanished = [new PiPo({x: x, y: y, c: color, p: piece})];
-      while (this.onBoard(cur[0], cur[1])) {
-        if (this.board[last[0]][last[1]] != "") {
-          const oppPiece = this.getPiece(last[0], last[1]);
-          if (!!byChameleon && oppPiece != 'n')
-            continue outerLoop;
-          // Something to eat:
-          vanished.push(
-            new PiPo({x: last[0], y: last[1], c: oppCol, p: oppPiece})
-          );
-        }
-        if (this.board[cur[0]][cur[1]] != "") {
-          if (
-            this.getColor(cur[0], cur[1]) == color ||
-            this.board[last[0]][last[1]] != ""
-          ) {
-            //TODO: redundant test
-            continue outerLoop;
-          }
-        }
-        else {
-          moves.push(
-            new Move({
-              appear: [new PiPo({x: cur[0], y: cur[1], c: color, p: piece})],
-              vanish: JSON.parse(JSON.stringify(vanished)), //TODO: required?
-              start: {x: x, y: y},
-              end: {x: cur[0], y: cur[1]}
-            })
-          );
-        }
-        last = [last[0] + step[0], this.getY(last[1] + step[1])];
-        cur = [cur[0] + step[0], this.getY(cur[1] + step[1])];
-      }
-    }
-    return moves;
-  }
-
-  // Chameleon
-  getBishopCaptures(moves) {
-    const [x, y] = [moves[0].start.x, moves[0].start.y];
-    moves = moves.concat(this.getKnightCaptures([x, y], "asChameleon"));
-    // No "king capture" because king cannot remain under check
-    this.addPawnCaptures(moves, "asChameleon");
-    this.addRookCaptures(moves, "asChameleon");
-    this.addQueenCaptures(moves, "asChameleon");
-    // Post-processing: merge similar moves, concatenating vanish arrays
-    let mergedMoves = {};
-    moves.forEach(m => {
-      const key = m.end.x + this.size.x * m.end.y;
-      if (!mergedMoves[key])
-        mergedMoves[key] = m;
-      else {
-        for (let i = 1; i < m.vanish.length; i++)
-          mergedMoves[key].vanish.push(m.vanish[i]);
-      }
-    });
-    return Object.values(mergedMoves);
-  }
-
-  addQueenCaptures(moves, byChameleon) {
-    if (moves.length == 0)
-      return;
-    const [x, y] = [moves[0].start.x, moves[0].start.y];
-    const adjacentSteps = this.pieces()['r'].moves[0].steps;
-    let capturingDirections = {};
-    const color = this.turn;
-    const oppCol = C.GetOppCol(color);
-    adjacentSteps.forEach(step => {
-      const [i, j] = [x - step[0], this.getY(y - step[1])];
-      if (
-        this.onBoard(i, j) &&
-        this.board[i][j] != "" &&
-        this.getColor(i, j) == oppCol &&
-        (!byChameleon || this.getPiece(i, j) == 'q')
-      ) {
-        capturingDirections[step[0] + "." + step[1]] = true;
-      }
-    });
-    moves.forEach(m => {
-      const step = [
-        m.end.x != x ? (m.end.x - x) / Math.abs(m.end.x - x) : 0,
-        m.end.y != y ? (m.end.y - y) / Math.abs(m.end.y - y) : 0
-      ];
-      if (capturingDirections[step[0] + "." + step[1]]) {
-        const [i, j] = [x - step[0], this.getY(y - step[1])];
-        m.vanish.push(
-          new PiPo({
-            x: i,
-            y: j,
-            p: this.getPiece(i, j),
-            c: oppCol
-          })
-        );
-      }
-    });
-  }
-
-  underAttack([x, y], oppCol) {
-    // Generate all potential opponent moves, check if king captured.
-    // TODO: do it more efficiently.
-    const color = this.getColor(x, y);
-    for (let i = 0; i < this.size.x; i++) {
-      for (let j = 0; j < this.size.y; j++) {
-        if (
-          this.board[i][j] != "" && this.getColor(i, j) == oppCol &&
-          this.getPotentialMovesFrom([i, j]).some(m => {
-            return (
-              m.vanish.length >= 2 &&
-              [1, m.vanish.length - 1].some(k => m.vanish[k].p == 'k')
-            );
-          })
-        ) {
-          return true;
-        }
-      }
-    }
-    return false;
-  }
-
 };