Draft Coregal variant - still getCastleMoves() and updateCastleFlags() TODO
authorBenjamin Auder <benjamin.auder@somewhere>
Thu, 19 Mar 2020 23:05:34 +0000 (00:05 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Thu, 19 Mar 2020 23:05:34 +0000 (00:05 +0100)
19 files changed:
client/src/base_rules.js
client/src/translations/rules/Coregal/en.pug [new file with mode: 0644]
client/src/translations/rules/Coregal/es.pug [new file with mode: 0644]
client/src/translations/rules/Coregal/fr.pug [new file with mode: 0644]
client/src/variants/Alice.js
client/src/variants/Allmate1.js
client/src/variants/Allmate2.js
client/src/variants/Antiking1.js
client/src/variants/Antiking2.js
client/src/variants/Atomic.js
client/src/variants/Benedict.js
client/src/variants/Cannibal.js
client/src/variants/Checkered.js
client/src/variants/Coregal.js [new file with mode: 0644]
client/src/variants/Extinction.js
client/src/variants/Magnetic.js
client/src/variants/Suction.js
client/src/variants/Wildebeest.js
client/src/variants/Wormhole.js

index 267b233..71fa13c 100644 (file)
@@ -132,8 +132,7 @@ export const ChessRules = class ChessRules {
     for (let row of rows) {
       let sumElts = 0;
       for (let i = 0; i < row.length; i++) {
-        if (['K','k'].includes(row[i]))
-          kings[row[i]] = true;
+        if (['K','k'].includes(row[i])) kings[row[i]] = true;
         if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
         else {
           const num = parseInt(row[i]);
@@ -144,8 +143,7 @@ export const ChessRules = class ChessRules {
       if (sumElts != V.size.y) return false;
     }
     // Both kings should be on board:
-    if (Object.keys(kings).length != 2)
-      return false;
+    if (Object.keys(kings).length != 2) return false;
     return true;
   }
 
@@ -433,7 +431,7 @@ export const ChessRules = class ChessRules {
   // Extract (relevant) flags from fen
   setFlags(fenflags) {
     // white a-castle, h-castle, black a-castle, h-castle
-    this.castleFlags = { w: [true, true], b: [true, true] };
+    this.castleFlags = { w: [-1, -1], b: [-1, -1] };
     for (let i = 0; i < 4; i++) {
       this.castleFlags[i < 2 ? "w" : "b"][i % 2] =
         V.ColumnToCoord(fenflags.charAt(i));
@@ -1081,13 +1079,15 @@ export const ChessRules = class ChessRules {
     this.postPlay(move);
   }
 
-  updateCastleFlags(move) {
+  updateCastleFlags(move, piece) {
     const c = V.GetOppCol(this.turn);
     const firstRank = (c == "w" ? V.size.x - 1 : 0);
     // Update castling flags if rooks are moved
     const oppCol = V.GetOppCol(c);
     const oppFirstRank = V.size.x - 1 - firstRank;
-    if (
+    if (piece == V.KING && move.appear.length > 0)
+      this.castleFlags[c] = [V.size.y, V.size.y];
+    else if (
       move.start.x == firstRank && //our rook moves?
       this.castleFlags[c].includes(move.start.y)
     ) {
@@ -1117,10 +1117,9 @@ export const ChessRules = class ChessRules {
     if (piece == V.KING && move.appear.length > 0) {
       this.kingPos[c][0] = move.appear[0].x;
       this.kingPos[c][1] = move.appear[0].y;
-      if (V.HasCastle) this.castleFlags[c] = [V.size.y, V.size.y];
       return;
     }
-    if (V.HasCastle) this.updateCastleFlags(move);
+    if (V.HasCastle) this.updateCastleFlags(move, piece);
   }
 
   preUndo() {}
@@ -1154,14 +1153,11 @@ export const ChessRules = class ChessRules {
 
   // What is the score ? (Interesting if game is over)
   getCurrentScore() {
-    if (this.atLeastOneMove())
-      return "*";
-
+    if (this.atLeastOneMove()) return "*";
     // Game over
     const color = this.turn;
     // No valid move: stalemate or checkmate?
-    if (!this.isAttacked(this.kingPos[color], V.GetOppCol(color)))
-      return "1/2";
+    if (!this.underCheck(color)) return "1/2";
     // OK, checkmate
     return (color == "w" ? "0-1" : "1-0");
   }
diff --git a/client/src/translations/rules/Coregal/en.pug b/client/src/translations/rules/Coregal/en.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
diff --git a/client/src/translations/rules/Coregal/es.pug b/client/src/translations/rules/Coregal/es.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
diff --git a/client/src/translations/rules/Coregal/fr.pug b/client/src/translations/rules/Coregal/fr.pug
new file mode 100644 (file)
index 0000000..3a33838
--- /dev/null
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
index d30acf0..9eb0e87 100644 (file)
@@ -263,10 +263,7 @@ export class AliceRules extends ChessRules {
   }
 
   getCurrentScore() {
-    if (this.atLeastOneMove())
-      // game not over
-      return "*";
-
+    if (this.atLeastOneMove()) return "*";
     const pieces = Object.keys(V.ALICE_CODES);
     const color = this.turn;
     const kp = this.kingPos[color];
index 3d84300..d07474b 100644 (file)
@@ -203,8 +203,7 @@ export class Allmate1Rules extends ChessRules {
     if (kp[0] < 0)
       // King disappeared
       return color == "w" ? "0-1" : "1-0";
-    if (this.atLeastOneMove())
-      return "*";
+    if (this.atLeastOneMove()) return "*";
     // Kings still there, no moves:
     return "1/2";
   }
index 424ff61..31d41be 100644 (file)
@@ -207,8 +207,7 @@ export class Allmate2Rules extends ChessRules {
     if (kp[0] < 0)
       // King disappeared
       return color == "w" ? "0-1" : "1-0";
-    if (this.atLeastOneMove())
-      return "*";
+    if (this.atLeastOneMove()) return "*";
     // Kings still there, no moves:
     return "1/2";
   }
index 75cc14c..3aa02d3 100644 (file)
@@ -192,21 +192,6 @@ export class Antiking1Rules extends BerolinaRules {
       this.antikingPos[c] = [move.start.x, move.start.y];
   }
 
-  getCurrentScore() {
-    if (this.atLeastOneMove())
-      return "*";
-
-    const color = this.turn;
-    const oppCol = V.GetOppCol(color);
-    if (
-      !this.isAttacked(this.kingPos[color], oppCol) &&
-      this.isAttacked(this.antikingPos[color], oppCol)
-    ) {
-      return "1/2";
-    }
-    return color == "w" ? "0-1" : "1-0";
-  }
-
   static get VALUES() {
     return Object.assign(
       { a: 1000 },
index 0a42743..087dce3 100644 (file)
@@ -132,21 +132,6 @@ export class Antiking2Rules extends ChessRules {
       this.antikingPos[c] = [move.start.x, move.start.y];
   }
 
-  getCurrentScore() {
-    if (this.atLeastOneMove())
-      return "*";
-
-    const color = this.turn;
-    const oppCol = V.GetOppCol(color);
-    if (
-      !this.isAttacked(this.kingPos[color], oppCol) &&
-      this.isAttacked(this.antikingPos[color], oppCol)
-    ) {
-      return "1/2";
-    }
-    return color == "w" ? "0-1" : "1-0";
-  }
-
   static get VALUES() {
     return Object.assign(
       { a: 1000 },
index a6a5625..889f2df 100644 (file)
@@ -151,8 +151,7 @@ export class AtomicRules extends ChessRules {
     if (kp[0] < 0)
       // King disappeared
       return color == "w" ? "0-1" : "1-0";
-    if (this.atLeastOneMove())
-      return "*";
+    if (this.atLeastOneMove()) return "*";
     if (!this.isAttacked(kp, V.GetOppCol(color))) return "1/2";
     return color == "w" ? "0-1" : "1-0"; //checkmate
   }
index fbc4b48..4ca5292 100644 (file)
@@ -154,8 +154,7 @@ export class BenedictRules extends ChessRules {
     const kp = this.kingPos[color];
     if (this.getColor(kp[0], kp[1]) != color)
       return color == "w" ? "0-1" : "1-0";
-    if (this.atLeastOneMove())
-      return "*";
+    if (this.atLeastOneMove()) return "*";
     // Stalemate:
     return "1/2";
   }
index 6166860..7860ab0 100644 (file)
@@ -181,7 +181,7 @@ export class CannibalRules extends ChessRules {
       this.castleFlags[c] = [V.size.y, V.size.y];
       return;
     }
-    super.updateCastleFlags(move);
+    super.updateCastleFlags(move, piece);
   }
 
   postUndo(move) {
index d601d14..d48d632 100644 (file)
@@ -357,10 +357,7 @@ export class CheckeredRules extends ChessRules {
   }
 
   getCurrentScore() {
-    if (this.atLeastOneMove())
-      // game not over
-      return "*";
-
+    if (this.atLeastOneMove()) return "*";
     const color = this.turn;
     // Artifically change turn, for checkered pawns
     this.turn = V.GetOppCol(this.turn);
diff --git a/client/src/variants/Coregal.js b/client/src/variants/Coregal.js
new file mode 100644 (file)
index 0000000..ff4f1df
--- /dev/null
@@ -0,0 +1,268 @@
+import { ChessRules } from "@/base_rules";
+import { ArrayFun } from "@/utils/array";
+import { randInt, sample } from "@/utils/alea";
+
+export class CoregalRules extends ChessRules {
+  static IsGoodPosition(position) {
+    if (!super.IsGoodPosition(position)) return false;
+    // Check that at least one queen of each color is there:
+    let queens = {};
+    for (let row of rows) {
+      for (let i = 0; i < row.length; i++)
+        if (['Q','q'].includes(row[i])) queens[row[i]] = true;
+    }
+    if (Object.keys(queens).length != 2) return false;
+    return true;
+  }
+
+  static IsGoodFlags(flags) {
+    return !!flags.match(/^[a-z]{8,8}$/);
+  }
+
+  getCheckSquares(color) {
+    let squares = [];
+    const oppCol = V.GetOppCol(color);
+    if (this.isAttacked(this.kingPos[color], oppCol))
+      squares.push(this.kingPos[color]);
+    for (let i=0; i<V.size.x; i++) {
+      for (let j=0; j<V.size.y; j++) {
+        if (
+          this.getColor(i, j) == color &&
+          this.getPiece(i, j) == V.QUEEN &&
+          this.isAttacked([i, j], oppCol)
+        ) {
+          squares.push([i, j]);
+        }
+      }
+    }
+    return squares;
+  }
+
+  static GenRandInitFen(randomness) {
+    if (randomness == 0)
+      // Castle flags here indicate pieces positions (if can castle)
+      return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 adehadeh -";
+
+    let pieces = { w: new Array(8), b: new Array(8) };
+    let flags = "";
+    for (let c of ["w", "b"]) {
+      if (c == 'b' && randomness == 1) {
+        pieces['b'] = pieces['w'];
+        flags += flags;
+        break;
+      }
+
+      // Get random squares for king and queen between b and g files
+      let randIndex = randInt(6);
+      let kingPos = randIndex + 1;
+      randIndex = randInt(5);
+      if (randIndex >= kingPos) randIndex++;
+      let queenPos = randIndex + 1;
+
+      // Get random squares for rooks to the left and right of the queen
+      // and king: not all squares of the same colors (for bishops).
+      const minQR = Math.min(kingPos, queenPos);
+      const maxQR = Math.max(kingPos, queenPos);
+      let rook1Pos = randInt(minQR);
+      let rook2Pos = 7 - randInt(7 - maxQR);
+
+      // Now, if we are unlucky all these 4 pieces may be on the same color.
+      const rem2 = [kingPos, queenPos, rook1Pos, rook2Pos].map(pos => pos % 2);
+      if (rem2.every(r => r == 0) || rem2.every(r => r == 1)) {
+        // Shift a random of these pieces to the left or right
+        switch (randInt(4)) {
+          case 0:
+            if (rook1Pos == 0) rook1Pos++;
+            else rook1Pos--;
+            break;
+          case 1:
+            if (Math.random() < 0.5) kingPos++;
+            else kingPos--;
+            break;
+          case 2:
+            if (Math.random() < 0.5) queenPos++;
+            else queenPos--;
+            break;
+          case 3:
+            if (rook2Pos == 7) rook2Pos--;
+            else rook2Pos++;
+            break;
+        }
+      }
+      let bishop1Options = { 0: true, 2: true, 4: true, 6: true };
+      let bishop2Options = { 1: true, 3: true, 5: true, 7: true };
+      [kingPos, queenPos, rook1Pos, rook2Pos].forEach(pos => {
+        if (!!bishop1Options[pos]) delete bishop1Options[pos];
+        else if (!!bishop2Options[pos]) delete bishop2Options[pos];
+      });
+      const bishop1Pos = parseInt(sample(Object.keys(bishop1Options), 1)[0]);
+      const bishop2Pos = parseInt(sample(Object.keys(bishop2Options), 1)[0]);
+
+      // Knights' positions are now determined
+      const forbidden = [
+        kingPos, queenPos, rook1Pos, rook2Pos, bishop1Pos, bishop2Pos
+      ];
+      const [knight1Pos, knight2Pos] =
+        ArrayFun.range(8).filter(pos => !forbidden.includes(pos));
+
+      pieces[c][rook1Pos] = "r";
+      pieces[c][knight1Pos] = "n";
+      pieces[c][bishop1Pos] = "b";
+      pieces[c][queenPos] = "q";
+      pieces[c][kingPos] = "k";
+      pieces[c][bishop2Pos] = "b";
+      pieces[c][knight2Pos] = "n";
+      pieces[c][rook2Pos] = "r";
+      flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(queenPos) +
+               V.CoordToColumn(kingPos) + V.CoordToColumn(rook2Pos);
+    }
+    // Add turn + flags + enpassant
+    return (
+      pieces["b"].join("") +
+      "/pppppppp/8/8/8/8/PPPPPPPP/" +
+      pieces["w"].join("").toUpperCase() +
+      " w 0 " + flags + " -"
+    );
+  }
+
+  setFlags(fenflags) {
+    // white a-castle, h-castle, black a-castle, h-castle
+    this.castleFlags = { w: [...Array(4)], b: [...Array(4)] };
+    for (let i = 0; i < 8; i++) {
+      this.castleFlags[i < 4 ? "w" : "b"][i % 4] =
+        V.ColumnToCoord(fenflags.charAt(i));
+    }
+  }
+
+  getPotentialQueenMoves(sq) {
+    return super.getPotentialQueenMoves(sq).concat(this.getCastleMoves(sq));
+  }
+
+  getCastleMoves([x, y], castleInCheck) {
+    return [];
+//    const c = this.getColor(x, y);
+//    if (x != (c == "w" ? V.size.x - 1 : 0) || y != this.INIT_COL_KING[c])
+//      return []; //x isn't first rank, or king has moved (shortcut)
+//
+//    // Castling ?
+//    const oppCol = V.GetOppCol(c);
+//    let moves = [];
+//    let i = 0;
+//    // King, then rook:
+//    const finalSquares = [
+//      [2, 3],
+//      [V.size.y - 2, V.size.y - 3]
+//    ];
+//    castlingCheck: for (
+//      let castleSide = 0;
+//      castleSide < 2;
+//      castleSide++ //large, then small
+//    ) {
+//      if (this.castleFlags[c][castleSide] >= V.size.y) continue;
+//      // If this code is reached, rooks and king are on initial position
+//
+//      // NOTE: in some variants this is not a rook, but let's keep variable name
+//      const rookPos = this.castleFlags[c][castleSide];
+//      const castlingPiece = this.getPiece(x, rookPos);
+//      if (this.getColor(x, rookPos) != c)
+//        // Rook is here but changed color (see Benedict)
+//        continue;
+//
+//      // Nothing on the path of the king ? (and no checks)
+//      const finDist = finalSquares[castleSide][0] - y;
+//      let step = finDist / Math.max(1, Math.abs(finDist));
+//      i = y;
+//      do {
+//        if (
+//          (!castleInCheck && this.isAttacked([x, i], oppCol)) ||
+//          (this.board[x][i] != V.EMPTY &&
+//            // NOTE: next check is enough, because of chessboard constraints
+//            (this.getColor(x, i) != c ||
+//              ![V.KING, castlingPiece].includes(this.getPiece(x, i))))
+//        ) {
+//          continue castlingCheck;
+//        }
+//        i += step;
+//      } while (i != finalSquares[castleSide][0]);
+//
+//      // Nothing on the path to the rook?
+//      step = castleSide == 0 ? -1 : 1;
+//      for (i = y + step; i != rookPos; i += step) {
+//        if (this.board[x][i] != V.EMPTY) continue castlingCheck;
+//      }
+//
+//      // Nothing on final squares, except maybe king and castling rook?
+//      for (i = 0; i < 2; i++) {
+//        if (
+//          this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
+//          this.getPiece(x, finalSquares[castleSide][i]) != V.KING &&
+//          finalSquares[castleSide][i] != rookPos
+//        ) {
+//          continue castlingCheck;
+//        }
+//      }
+//
+//      // If this code is reached, castle is valid
+//      moves.push(
+//        new Move({
+//          appear: [
+//            new PiPo({ x: x, y: finalSquares[castleSide][0], p: V.KING, c: c }),
+//            new PiPo({ x: x, y: finalSquares[castleSide][1], p: castlingPiece, c: c })
+//          ],
+//          vanish: [
+//            new PiPo({ x: x, y: y, p: V.KING, c: c }),
+//            new PiPo({ x: x, y: rookPos, p: castlingPiece, c: c })
+//          ],
+//          end:
+//            Math.abs(y - rookPos) <= 2
+//              ? { x: x, y: rookPos }
+//              : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) }
+//        })
+//      );
+//    }
+//
+//    return moves;
+  }
+
+  underCheck(color) {
+    const oppCol = V.GetOppCol(color);
+    if (this.isAttacked(this.kingPos[color], oppCol)) return true;
+    for (let i=0; i<V.size.x; i++) {
+      for (let j=0; j<V.size.y; j++) {
+        if (
+          this.getColor(i, j) == color &&
+          this.getPiece(i, j) == V.QUEEN &&
+          this.isAttacked([i, j], oppCol)
+        ) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  updateCastleFlags(move, piece) {
+//    const c = V.GetOppCol(this.turn);
+//    const firstRank = (c == "w" ? V.size.x - 1 : 0);
+//    // Update castling flags if rooks are moved
+//    const oppCol = V.GetOppCol(c);
+//    const oppFirstRank = V.size.x - 1 - firstRank;
+//    if (piece == V.KING && move.appear.length > 0)
+//      this.castleFlags[c] = [V.size.y, V.size.y];
+//    else if (
+//      move.start.x == firstRank && //our rook moves?
+//      this.castleFlags[c].includes(move.start.y)
+//    ) {
+//      const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1);
+//      this.castleFlags[c][flagIdx] = V.size.y;
+//    } else if (
+//      move.end.x == oppFirstRank && //we took opponent rook?
+//      this.castleFlags[oppCol].includes(move.end.y)
+//    ) {
+//      const flagIdx = (move.end.y == this.castleFlags[oppCol][0] ? 0 : 1);
+//      this.castleFlags[oppCol][flagIdx] = V.size.y;
+//    }
+  }
+
+  // NOTE: do not set queen value to 1000 or so, because there may be several.
+};
index e257137..746809f 100644 (file)
@@ -99,7 +99,6 @@ export class ExtinctionRules extends ChessRules {
       }
       return "*";
     }
-
     return this.turn == "w" ? "0-1" : "1-0"; //NOTE: currently unreachable...
   }
 
index a6150cb..ee9fe28 100644 (file)
@@ -196,7 +196,7 @@ export class MagneticRules extends ChessRules {
       // King disappeared
       return color == "w" ? "0-1" : "1-0";
     if (this.atLeastOneMove())
-      // game not over
+      // Game not over
       return "*";
     return "1/2"; //no moves but kings still there
   }
index d15a7c8..ac3aeb0 100644 (file)
@@ -182,10 +182,8 @@ export class SuctionRules extends ChessRules {
   getCurrentScore() {
     const color = this.turn;
     const kp = this.kingPos[color];
-    if (color == "w" && kp[0] == 0)
-      return "0-1";
-    if (color == "b" && kp[0] == V.size.x - 1)
-      return "1-0";
+    if (color == "w" && kp[0] == 0) return "0-1";
+    if (color == "b" && kp[0] == V.size.x - 1) return "1-0";
     // King is not on the opposite edge: game not over
     return "*";
   }
index 8192fa0..5bb50e8 100644 (file)
@@ -225,10 +225,7 @@ export class WildebeestRules extends ChessRules {
   }
 
   getCurrentScore() {
-    if (this.atLeastOneMove())
-      // game not over
-      return "*";
-
+    if (this.atLeastOneMove()) return "*";
     // No valid move: game is lost (stalemate is a win)
     return this.turn == "w" ? "0-1" : "1-0";
   }
index f47fea3..20c9d11 100644 (file)
@@ -270,8 +270,7 @@ export class WormholeRules extends ChessRules {
   }
 
   getCurrentScore() {
-    if (this.atLeastOneMove())
-      return "*";
+    if (this.atLeastOneMove()) return "*";
     // No valid move: I lose
     return this.turn == "w" ? "0-1" : "1-0";
   }