Refactor Games structure on server: no longer use an extra 'Players' table
[vchess.git] / client / src / variants / Eightpieces.js
index 1033a5a..76f72b0 100644 (file)
@@ -13,6 +13,11 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
     return "l";
   }
 
+  static get IMAGE_EXTENSION() {
+    // Temporarily, for the time SVG pieces are being designed:
+    return ".png";
+  }
+
   // Lancer directions *from white perspective*
   static get LANCER_DIRS() {
     return {
@@ -41,9 +46,9 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
   }
 
   getPpath(b, color, score, orientation) {
-    if ([V.JAILER, V.SENTRY].includes(b[1])) return "Eightpieces/" + b;
+    if ([V.JAILER, V.SENTRY].includes(b[1])) return "Eightpieces/tmp_png/" + b;
     if (Object.keys(V.LANCER_DIRS).includes(b[1])) {
-      if (orientation == 'w') return "Eightpieces/" + b;
+      if (orientation == 'w') return "Eightpieces/tmp_png/" + b;
       // Find opposite direction for adequate display:
       let oppDir = '';
       switch (b[1]) {
@@ -72,9 +77,10 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
           oppDir = 'f';
           break;
       }
-      return "Eightpieces/" + b[0] + oppDir;
+      return "Eightpieces/tmp_png/" + b[0] + oppDir;
     }
-    return b;
+    // TODO: after we have SVG pieces, remove the folder and next prefix:
+    return "Eightpieces/tmp_png/" + b;
   }
 
   getPPpath(b, orientation) {
@@ -284,12 +290,14 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
 
   getPotentialMovesFrom([x, y]) {
     // At subTurn == 2, jailers aren't effective (Jeff K)
+    const piece = this.getPiece(x, y);
+    const L = this.sentryPush.length;
     if (this.subTurn == 1) {
       const jsq = this.isImmobilized([x, y]);
       if (!!jsq) {
         let moves = [];
         // Special pass move if king:
-        if (this.getPiece(x, y) == V.KING) {
+        if (piece == V.KING) {
           moves.push(
             new Move({
               appear: [],
@@ -299,11 +307,26 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
             })
           );
         }
+        else if (piece == V.LANCER && !!this.sentryPush[L-1]) {
+          // A pushed lancer next to the jailer: reorient
+          const color = this.getColor(x, y);
+          const curDir = this.board[x][y].charAt(1);
+          Object.keys(V.LANCER_DIRS).forEach(k => {
+            moves.push(
+              new Move({
+                appear: [{ x: x, y: y, c: color, p: k }],
+                vanish: [{ x: x, y: y, c: color, p: curDir }],
+                start: { x: x, y: y },
+                end: { x: jsq[0], y: jsq[1] }
+              })
+            );
+          });
+        }
         return moves;
       }
     }
     let moves = [];
-    switch (this.getPiece(x, y)) {
+    switch (piece) {
       case V.JAILER:
         moves = this.getPotentialJailerMoves([x, y]);
         break;
@@ -317,12 +340,15 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
         moves = super.getPotentialMovesFrom([x, y]);
         break;
     }
-    const L = this.sentryPush.length;
     if (!!this.sentryPush[L-1]) {
-      // Delete moves walking back on sentry push path
+      // Delete moves walking back on sentry push path,
+      // only if not a pawn, and the piece is the pushed one.
+      const pl = this.sentryPush[L-1].length;
+      const finalPushedSq = this.sentryPush[L-1][pl-1];
       moves = moves.filter(m => {
         if (
           m.vanish[0].p != V.PAWN &&
+          m.start.x == finalPushedSq.x && m.start.y == finalPushedSq.y &&
           this.sentryPush[L-1].some(sq => sq.x == m.end.x && sq.y == m.end.y)
         ) {
           return false;
@@ -343,8 +369,9 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
     const color = this.getColor(x, y);
     let moves = [];
     const [sizeX, sizeY] = [V.size.x, V.size.y];
-    let shiftX = color == "w" ? -1 : 1;
+    let shiftX = (color == "w" ? -1 : 1);
     if (this.subTurn == 2) shiftX *= -1;
+    const firstRank = color == "w" ? sizeX - 1 : 0;
     const startRank = color == "w" ? sizeX - 2 : 1;
     const lastRank = color == "w" ? 0 : sizeX - 1;
 
@@ -352,10 +379,9 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
     if (!V.OnBoard(x + shiftX, y)) return [];
 
     const finalPieces =
-      // No promotions after pushes!
-      x + shiftX == lastRank && this.subTurn == 1
-        ?
-          Object.keys(V.LANCER_DIRS).concat(
+      // A push cannot put a pawn on last rank (it goes backward)
+      x + shiftX == lastRank
+        ? Object.keys(V.LANCER_DIRS).concat(
           [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.SENTRY, V.JAILER])
         : [V.PAWN];
     if (this.board[x + shiftX][y] == V.EMPTY) {
@@ -369,7 +395,9 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
         );
       }
       if (
-        x == startRank &&
+        // 2-squares jumps forbidden if pawn push
+        this.subTurn == 1 &&
+        [startRank, firstRank].includes(x) &&
         this.board[x + 2 * shiftX][y] == V.EMPTY
       ) {
         // Two squares jump
@@ -395,10 +423,11 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
       }
     }
 
-    // En passant:
+    // En passant: only on subTurn == 1
     const Lep = this.epSquares.length;
     const epSquare = this.epSquares[Lep - 1];
     if (
+      this.subTurn == 1 &&
       !!epSquare &&
       epSquare.x == x + shiftX &&
       Math.abs(epSquare.y - y) == 1
@@ -444,6 +473,7 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
     // Add all lancer possible orientations, similar to pawn promotions.
     // Except if just after a push: allow all movements from init square then
     const L = this.sentryPush.length;
+    const color = this.getColor(x, y);
     if (!!this.sentryPush[L-1]) {
       // Maybe I was pushed
       const pl = this.sentryPush[L-1].length;
@@ -454,7 +484,6 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
         // I was pushed: allow all directions (for this move only), but
         // do not change direction after moving, *except* if I keep the
         // same orientation in which I was pushed.
-        const color = this.getColor(x, y);
         const curDir = V.LANCER_DIRS[this.board[x][y].charAt(1)];
         Object.values(V.LANCER_DIRS).forEach(step => {
           const dirCode = Object.keys(V.LANCER_DIRS).find(k => {
@@ -507,10 +536,15 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
           V.OnBoard(x + step[0], y + step[1]) &&
           this.board[x + step[0]][y + step[1]] == V.EMPTY
         ) {
+          const newDirCode = Object.keys(V.LANCER_DIRS).find(k => {
+            const codeStep = V.LANCER_DIRS[k];
+            return (codeStep[0] == step[0] && codeStep[1] == step[1]);
+          });
           potentialNudges.push(
             this.getBasicMove(
               [x, y],
-              [x + step[0], y + step[1]]
+              [x + step[0], y + step[1]],
+              { c: color, p: newDirCode }
             )
           );
         }
@@ -728,9 +762,9 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
   }
 
   play(move) {
-    if (!this.states) this.states = [];
-    const stateFen = this.getFen();
-    this.states.push(stateFen);
+//    if (!this.states) this.states = [];
+//    const stateFen = this.getFen();
+//    this.states.push(stateFen);
 
     this.prePlay(move);
     move.flags = JSON.stringify(this.aggregateFlags());
@@ -793,9 +827,9 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
     }
     this.postUndo(move);
 
-    const stateFen = this.getFen();
-    if (stateFen != this.states[this.states.length-1]) debugger;
-    this.states.pop();
+//    const stateFen = this.getFen();
+//    if (stateFen != this.states[this.states.length-1]) debugger;
+//    this.states.pop();
   }
 
   postUndo(move) {
@@ -886,13 +920,20 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
   selfAttack([x1, y1], [x2, y2]) {
     const color = this.getColor(x1, y1);
     const sliderAttack = (allowedSteps, lancer) => {
-      const deltaX = x2 - x1;
-      const deltaY = y2 - y1;
-      const step = [ deltaX / Math.abs(deltaX), deltaY / Math.abs(deltaY) ];
-      if (allowedSteps.every(st => st[0] != step[0] || st[1] != step[1]))
+      const deltaX = x2 - x1,
+            absDeltaX = Math.abs(deltaX);
+      const deltaY = y2 - y1,
+            absDeltaY = Math.abs(deltaY);
+      const step = [ deltaX / absDeltaX || 0, deltaY / absDeltaY || 0 ];
+      if (
+        // Check that the step is a priori valid:
+        (absDeltaX != absDeltaY && deltaX != 0 && deltaY != 0) ||
+        allowedSteps.every(st => st[0] != step[0] || st[1] != step[1])
+      ) {
         return false;
+      }
       let sq = [ x1 + step[0], y1 + step[1] ];
-      while (sq[0] != x2 && sq[1] != y2) {
+      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) ||
@@ -1039,9 +1080,24 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
     return (!choice.second ? choice : [choice, choice.second]);
   }
 
+  // For moves notation:
+  static get LANCER_DIRNAMES() {
+    return {
+      'c': "N",
+      'd': "NE",
+      'e': "E",
+      'f': "SE",
+      'g': "S",
+      'h': "SW",
+      'm': "W",
+      'o': "NW"
+    };
+  }
+
   getNotation(move) {
     // Special case "king takes jailer" is a pass move
     if (move.appear.length == 0 && move.vanish.length == 0) return "pass";
+    let notation = undefined;
     if (this.subTurn == 2) {
       // Do not consider appear[1] (sentry) for sentry pushes
       const simpleMove = {
@@ -1050,8 +1106,11 @@ export const VariantRules = class EightpiecesRules extends ChessRules {
         start: move.start,
         end: move.end
       };
-      return super.getNotation(simpleMove);
-    }
-    return super.getNotation(move);
+      notation = super.getNotation(simpleMove);
+    } else notation = super.getNotation(move);
+    if (Object.keys(V.LANCER_DIRNAMES).includes(move.vanish[0].p))
+      // Lancer: add direction info
+      notation += "=" + V.LANCER_DIRNAMES[move.appear[0].p];
+    return notation;
   }
 };