Attempt to fix Eightpieces
[vchess.git] / client / src / variants / Eightpieces.js
index 583b1c5..dc7580b 100644 (file)
@@ -1,8 +1,8 @@
-import { ArrayFun } from "@/utils/array";
-import { randInt } from "@/utils/alea";
+import { randInt, sample } from "@/utils/alea";
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
 export class EightpiecesRules extends ChessRules {
+
   static get JAILER() {
     return "j";
   }
@@ -167,81 +167,54 @@ export class EightpiecesRules extends ChessRules {
     }
   }
 
-  static GenRandInitFen(randomness) {
-    if (randomness == 0)
-      // Deterministic:
+  static GenRandInitFen(options) {
+    if (options.randomness == 0)
       return "jfsqkbnr/pppppppp/8/8/8/8/PPPPPPPP/JDSQKBNR w 0 ahah - -";
 
-    let pieces = { w: new Array(8), b: new Array(8) };
-    let flags = "";
-    // Shuffle pieces on first (and last rank if randomness == 2)
-    for (let c of ["w", "b"]) {
-      if (c == 'b' && randomness == 1) {
-        const lancerIdx = pieces['w'].findIndex(p => {
-          return Object.keys(V.LANCER_DIRS).includes(p);
-        });
-        pieces['b'] =
-          pieces['w'].slice(0, lancerIdx)
-          .concat(['g'])
-          .concat(pieces['w'].slice(lancerIdx + 1));
-        flags += flags;
-        break;
-      }
+    const baseFen = ChessRules.GenRandInitFen(options);
+    const fenParts = baseFen.split(' ');
+    const posParts = fenParts[0].split('/');
 
-      let positions = ArrayFun.range(8);
-
-      // Get random squares for bishop and sentry
-      let randIndex = 2 * randInt(4);
-      let bishopPos = positions[randIndex];
-      // The sentry must be on a square of different color
-      let randIndex_tmp = 2 * randInt(4) + 1;
-      let sentryPos = positions[randIndex_tmp];
-      if (c == 'b') {
-        // Check if white sentry is on the same color as ours.
-        // If yes: swap bishop and sentry positions.
-        // NOTE: test % 2 == 1 because there are 7 slashes.
-        if ((pieces['w'].indexOf('s') - sentryPos) % 2 == 1)
-          [bishopPos, sentryPos] = [sentryPos, bishopPos];
+    // Replace one bishop by sentry, so that sentries on different colors
+    // Also replace one random rook by jailer,
+    // and one random knight by lancer (facing north/south)
+    let pieceLine = { b: posParts[0], w: posParts[7].toLowerCase() };
+    let posBlack = { r: -1, n: -1, b: -1 };
+    const mapP = { r: 'j', n: 'l', b: 's' };
+    ['w', 'b'].forEach(c => {
+      ['r', 'n', 'b'].forEach(p => {
+        let pl = pieceLine[c];
+        let pos = -1;
+        if (options.randomness == 2 || c == 'b')
+          pos = (randInt(2) == 0 ? pl.indexOf(p) : pl.lastIndexOf(p));
+        else pos = posBlack[p];
+        pieceLine[c] =
+          pieceLine[c].substr(0, pos) + mapP[p] + pieceLine[c].substr(pos+1);
+        if (options.randomness == 1 && c == 'b') posBlack[p] = pos;
+      });
+    });
+    // Rename 'l' into 'g' (black) or 'c' (white)
+    pieceLine['w'] = pieceLine['w'].replace('l', 'c');
+    pieceLine['b'] = pieceLine['b'].replace('l', 'g');
+    if (options.randomness == 2) {
+      const ws = pieceLine['w'].indexOf('s');
+      const bs = pieceLine['b'].indexOf('s');
+      if (ws % 2 != bs % 2) {
+        // Fix sentry: should be on different colors.
+        // => move sentry on other bishop for random color
+        const c = sample(['w', 'b'], 1);
+        pieceLine[c] = pieceLine[c]
+                       .replace('b', 't'); //tmp
+                       .replace('s', 'b');
+                       .replace('t', 's');
       }
-      positions.splice(Math.max(randIndex, randIndex_tmp), 1);
-      positions.splice(Math.min(randIndex, randIndex_tmp), 1);
-
-      // Get random squares for knight and lancer
-      randIndex = randInt(6);
-      const knightPos = positions[randIndex];
-      positions.splice(randIndex, 1);
-      randIndex = randInt(5);
-      const lancerPos = positions[randIndex];
-      positions.splice(randIndex, 1);
-
-      // Get random square for queen
-      randIndex = randInt(4);
-      const queenPos = positions[randIndex];
-      positions.splice(randIndex, 1);
-
-      // Rook, jailer and king positions are now almost fixed,
-      // only the ordering rook->jailer or jailer->rook must be decided.
-      let rookPos = positions[0];
-      let jailerPos = positions[2];
-      const kingPos = positions[1];
-      flags += V.CoordToColumn(rookPos) + V.CoordToColumn(jailerPos);
-      if (Math.random() < 0.5) [rookPos, jailerPos] = [jailerPos, rookPos];
-
-      pieces[c][rookPos] = "r";
-      pieces[c][knightPos] = "n";
-      pieces[c][bishopPos] = "b";
-      pieces[c][queenPos] = "q";
-      pieces[c][kingPos] = "k";
-      pieces[c][sentryPos] = "s";
-      // Lancer faces north for white, and south for black:
-      pieces[c][lancerPos] = c == 'w' ? 'c' : 'g';
-      pieces[c][jailerPos] = "j";
     }
+
     return (
-      pieces["b"].join("") +
-      "/pppppppp/8/8/8/8/PPPPPPPP/" +
-      pieces["w"].join("").toUpperCase() +
-      " w 0 " + flags + " - -"
+      pieceLine['b'] + "/" +
+      posParts.slice(1, 7).join('/') + "/" +
+      pieceLine['w'].toUpperCase() + " " +
+      fenParts.slice(1, 5).join(' ') + " -"
     );
   }
 
@@ -269,45 +242,6 @@ export class EightpiecesRules extends ChessRules {
     return null;
   }
 
-  // Because of the lancers, getPiece() could be wrong:
-  // use board[x][y][1] instead (always valid).
-  getBasicMove([sx, sy], [ex, ey], tr) {
-    const initColor = this.getColor(sx, sy);
-    const initPiece = this.board[sx][sy].charAt(1);
-    let mv = new Move({
-      appear: [
-        new PiPo({
-          x: ex,
-          y: ey,
-          c: tr ? tr.c : initColor,
-          p: tr ? tr.p : initPiece
-        })
-      ],
-      vanish: [
-        new PiPo({
-          x: sx,
-          y: sy,
-          c: initColor,
-          p: initPiece
-        })
-      ]
-    });
-
-    // The opponent piece disappears if we take it
-    if (this.board[ex][ey] != V.EMPTY) {
-      mv.vanish.push(
-        new PiPo({
-          x: ex,
-          y: ey,
-          c: this.getColor(ex, ey),
-          p: this.board[ex][ey].charAt(1)
-        })
-      );
-    }
-
-    return mv;
-  }
-
   canIplay(side, [x, y]) {
     return (
       (this.subTurn == 1 && this.turn == side && this.getColor(x, y) == side)
@@ -383,7 +317,8 @@ export class EightpiecesRules extends ChessRules {
         }
         return true;
       });
-    } else if (this.subTurn == 2) {
+    }
+    else if (this.subTurn == 2) {
       // Put back the sentinel on board:
       const color = this.turn;
       moves.forEach(m => {
@@ -732,10 +667,7 @@ export class EightpiecesRules extends ChessRules {
 
   getPotentialKingMoves(sq) {
     const moves = this.getSlideNJumpMoves(
-      sq,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
-      "oneStep"
-    );
+      sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]), 1);
     return (
       this.subTurn == 1
         ? moves.concat(this.getCastleMoves(sq))
@@ -885,8 +817,8 @@ export class EightpiecesRules extends ChessRules {
     const oppCol = V.GetOppCol(color);
     const sliderAttack = (allowedSteps, lancer) => {
       const deltaX = x2 - x1,
-            absDeltaX = Math.abs(deltaX);
-      const deltaY = y2 - y1,
+            deltaY = y2 - y1;
+      const absDeltaX = Math.abs(deltaX),
             absDeltaY = Math.abs(deltaY);
       const step = [ deltaX / absDeltaX || 0, deltaY / absDeltaY || 0 ];
       if (
@@ -898,12 +830,18 @@ export class EightpiecesRules extends ChessRules {
       }
       let sq = [ x1 + step[0], y1 + step[1] ];
       while (sq[0] != x2 || sq[1] != y2) {
-        if (
-          // NOTE: no need to check OnBoard in this special case
-          (!lancer && this.board[sq[0]][sq[1]] != V.EMPTY) ||
-          (!!lancer && this.getColor(sq[0], sq[1]) == oppCol)
-        ) {
-          return false;
+        // NOTE: no need to check OnBoard in this special case
+        if (this.board[sq[0]][sq[1]] != V.EMPTY) {
+          const p = this.getPiece(sq[0], sq[1]);
+          const pc = this.getColor(sq[0], sq[1]);
+          if (
+            // Enemy sentry on the way will be gone:
+            (p != V.SENTRY || pc != oppCol) &&
+            // Lancer temporarily "changed color":
+            (!lancer || pc == color)
+          ) {
+            return false;
+          }
         }
         sq[0] += step[0];
         sq[1] += step[1];
@@ -1194,4 +1132,5 @@ export class EightpiecesRules extends ChessRules {
     }
     return notation;
   }
+
 };