Some fixes, draw lines on board, add 7 variants
[vchess.git] / client / src / variants / Ball.js
index cd504a9..ceddb61 100644 (file)
@@ -3,23 +3,35 @@ import { ArrayFun } from "@/utils/array";
 import { shuffle } from "@/utils/alea";
 
 export class BallRules extends ChessRules {
+  static get Lines() {
+    return [
+      // White goal:
+      [[0, 3], [0, 6]],
+      [[0, 6], [1, 6]],
+      [[1, 6], [1, 3]],
+      [[1, 3], [0, 3]],
+      // Black goal:
+      [[9, 3], [9, 6]],
+      [[9, 6], [8, 6]],
+      [[8, 6], [8, 3]],
+      [[8, 3], [9, 3]]
+    ];
+  }
+
   static get PawnSpecs() {
     return Object.assign(
       {},
       ChessRules.PawnSpecs,
-      { promotions: ChessRules.PawnSpecs.promotions.concat([V.CHAMPION]) }
+      { promotions: ChessRules.PawnSpecs.promotions.concat([V.PHOENIX]) }
     );
   }
 
   static get HasFlags() {
     return false;
   }
-  static get HasCastle() {
-    return false;
-  }
 
-  static get CHAMPION() {
-    return 'c';
+  static get PHOENIX() {
+    return 'h';
   }
 
   static get BALL() {
@@ -41,7 +53,7 @@ export class BallRules extends ChessRules {
       'b': 'd',
       'q': 't',
       'k': 'l',
-      'c': 'h'
+      'h': 'i'
     };
   }
 
@@ -53,13 +65,13 @@ export class BallRules extends ChessRules {
       'd': 'b',
       't': 'q',
       'l': 'k',
-      'h': 'c'
+      'i': 'h'
     };
   }
 
   static get PIECES() {
     return ChessRules.PIECES
-      .concat([V.CHAMPION])
+      .concat([V.PHOENIX])
       .concat(Object.keys(V.HAS_BALL_DECODE))
       .concat(['a']);
   }
@@ -108,27 +120,34 @@ export class BallRules extends ChessRules {
     let prefix = "";
     const withPrefix =
       Object.keys(V.HAS_BALL_DECODE)
-      .concat([V.CHAMPION])
+      .concat([V.PHOENIX])
       .concat(['a']);
     if (withPrefix.includes(b[1])) prefix = "Ball/";
     return prefix + b;
   }
 
   canTake([x1, y1], [x2, y2]) {
-    // Capture enemy or pass ball to friendly pieces
+    if (this.getColor(x1, y1) !== this.getColor(x2, y2)) {
+      // The piece holding the ball cannot capture:
+      return (
+        !(Object.keys(V.HAS_BALL_DECODE)
+          .includes(this.board[x1][y1].charAt(1)))
+      );
+    }
+    // Pass: possible only if one of the friendly pieces has the ball
     return (
-      this.getColor(x1, y1) !== this.getColor(x2, y2) ||
-      Object.keys(V.HAS_BALL_DECODE).includes(this.board[x1][y1].charAt(1))
+      Object.keys(V.HAS_BALL_DECODE).includes(this.board[x1][y1].charAt(1)) ||
+      Object.keys(V.HAS_BALL_DECODE).includes(this.board[x2][y2].charAt(1))
     );
   }
 
-  getCheckSquares(color) {
+  getCheckSquares() {
     return [];
   }
 
   static GenRandInitFen(randomness) {
     if (randomness == 0)
-      return "rnbcqcnbr/ppppppppp/9/9/4a4/9/9/PPPPPPPPP/RNBCQCNBR w 0 -";
+      return "hbnrqrnhb/ppppppppp/9/9/4a4/9/9/PPPPPPPPP/HBNRQRNHB w 0 -";
 
     let pieces = { w: new Array(9), b: new Array(9) };
     for (let c of ["w", "b"]) {
@@ -137,17 +156,26 @@ export class BallRules extends ChessRules {
         break;
       }
 
-      // Get random squares for every piece, totally freely
+      // Get random squares for every piece, with bishops and phoenixes
+      // on different colors:
       let positions = shuffle(ArrayFun.range(9));
-      const composition = ['b', 'b', 'r', 'r', 'n', 'n', 'c', 'c', 'q'];
-      const rem2 = positions[0] % 2;
+      const composition = ['b', 'b', 'h', 'h', 'n', 'n', 'r', 'r', 'q'];
+      let rem2 = positions[0] % 2;
       if (rem2 == positions[1] % 2) {
         // Fix bishops (on different colors)
-        for (let i=2; i<9; i++) {
+        for (let i=4; i<9; i++) {
           if (positions[i] % 2 != rem2)
             [positions[1], positions[i]] = [positions[i], positions[1]];
         }
       }
+      rem2 = positions[2] % 2;
+      if (rem2 == positions[3] % 2) {
+        // Fix phoenixes too:
+        for (let i=4; i<9; i++) {
+          if (positions[i] % 2 != rem2)
+            [positions[3], positions[i]] = [positions[i], positions[3]];
+        }
+      }
       for (let i = 0; i < 9; i++) pieces[c][positions[i]] = composition[i];
     }
     return (
@@ -176,16 +204,12 @@ export class BallRules extends ChessRules {
     return Object.assign(
       {},
       ChessRules.steps,
-      // Add champion moves
+      // Add phoenix moves
       {
-        c: [
+        h: [
           [-2, -2],
-          [-2, 0],
           [-2, 2],
-          [0, -2],
-          [0, 2],
           [2, -2],
-          [2, 0],
           [2, 2],
           [-1, 0],
           [1, 0],
@@ -236,15 +260,29 @@ export class BallRules extends ChessRules {
       );
     }
 
-    // Post-processing: maybe the ball was taken, or a piece + ball
+    // Post-processing: maybe the ball was taken, or a piece + ball,
+    // or maybe a pass (ball <--> piece)
     if (mv.vanish.length == 2) {
       if (
         // Take the ball?
         mv.vanish[1].c == 'a' ||
-        // Capture a ball-holding piece?
+        // Capture a ball-holding piece? If friendly one, then adjust
         Object.keys(V.HAS_BALL_DECODE).includes(mv.vanish[1].p)
       ) {
         mv.appear[0].p = V.HAS_BALL_CODE[mv.appear[0].p];
+        if (mv.vanish[1].c == mv.vanish[0].c) {
+          // "Capturing" self => pass
+          mv.appear[0].x = mv.start.x;
+          mv.appear[0].y = mv.start.y;
+          mv.appear.push(
+            new PiPo({
+              x: mv.end.x,
+              y: mv.end.y,
+              p: V.HAS_BALL_DECODE[mv.vanish[1].p],
+              c: mv.vanish[0].c
+            })
+          );
+        }
       } else if (mv.vanish[1].c == mv.vanish[0].c) {
         // Pass the ball: the passing unit does not disappear
         mv.appear.push(JSON.parse(JSON.stringify(mv.vanish[0])));
@@ -261,12 +299,12 @@ export class BallRules extends ChessRules {
   // So base implementation is fine.
 
   getPotentialMovesFrom([x, y]) {
-    if (this.getPiece(x, y) == V.CHAMPION)
-      return this.getPotentialChampionMoves([x, y]);
+    if (this.getPiece(x, y) == V.PHOENIX)
+      return this.getPotentialPhoenixMoves([x, y]);
     return super.getPotentialMovesFrom([x, y]);
   }
 
-  // "Sliders": at most 2 steps
+  // "Sliders": at most 3 steps
   getSlideNJumpMoves([x, y], steps, oneStep) {
     let moves = [];
     outerLoop: for (let step of steps) {
@@ -275,7 +313,7 @@ export class BallRules extends ChessRules {
       let stepCount = 1;
       while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
         moves.push(this.getBasicMove([x, y], [i, j]));
-        if (oneStep || stepCount == 2) continue outerLoop;
+        if (oneStep || stepCount == 3) continue outerLoop;
         i += step[0];
         j += step[1];
         stepCount++;
@@ -286,8 +324,8 @@ export class BallRules extends ChessRules {
     return moves;
   }
 
-  getPotentialChampionMoves(sq) {
-    return this.getSlideNJumpMoves(sq, V.steps[V.CHAMPION], "oneStep");
+  getPotentialPhoenixMoves(sq) {
+    return this.getSlideNJumpMoves(sq, V.steps[V.PHOENIX], "oneStep");
   }
 
   filterValid(moves) {
@@ -324,10 +362,10 @@ export class BallRules extends ChessRules {
     return {
       p: 1,
       r: 3,
-      n: 4,
+      n: 3,
       b: 2,
       q: 5,
-      c: 4,
+      h: 3,
       a: 0 //ball: neutral
     };
   }