Fix. Was an issue in berolina variant
[vchess.git] / client / src / variants / Grasshopper.js
1 import { ChessRules } from "@/base_rules";
2 import { ArrayFun } from "@/utils/array";
3 import { randInt } from "@/utils/alea";
4
5 export const VariantRules = class GrasshopperRules extends ChessRules {
6 static get HasEnpassant() {
7 return false;
8 }
9
10 static get GRASSHOPPER() {
11 return "g";
12 }
13
14 static get PIECES() {
15 return ChessRules.PIECES.concat([V.GRASSHOPPER]);
16 }
17
18 getPpath(b) {
19 return (b[1] == V.GRASSHOPPER ? "Grasshopper/" : "") + b;
20 }
21
22 getPotentialMovesFrom([x, y]) {
23 switch (this.getPiece(x, y)) {
24 case V.GRASSHOPPER:
25 return this.getPotentialGrasshopperMoves([x, y]);
26 default:
27 return super.getPotentialMovesFrom([x, y]);
28 }
29 }
30
31 getPotentialPawnMoves([x, y]) {
32 const color = this.turn;
33 let moves = [];
34 const [sizeX, sizeY] = [V.size.x, V.size.y];
35 const shiftX = color == "w" ? -1 : 1;
36 const lastRank = color == "w" ? 0 : sizeX - 1;
37
38 const finalPieces =
39 x + shiftX == lastRank
40 ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.GRASSHOPPER]
41 : [V.PAWN];
42 if (this.board[x + shiftX][y] == V.EMPTY) {
43 // One square forward
44 for (let piece of finalPieces) {
45 moves.push(
46 this.getBasicMove([x, y], [x + shiftX, y], {
47 c: color,
48 p: piece
49 })
50 );
51 }
52 // No 2-squares jump
53 }
54 // Captures
55 for (let shiftY of [-1, 1]) {
56 if (
57 y + shiftY >= 0 &&
58 y + shiftY < sizeY &&
59 this.board[x + shiftX][y + shiftY] != V.EMPTY &&
60 this.canTake([x, y], [x + shiftX, y + shiftY])
61 ) {
62 for (let piece of finalPieces) {
63 moves.push(
64 this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
65 c: color,
66 p: piece
67 })
68 );
69 }
70 }
71 }
72
73 return moves;
74 }
75
76 getPotentialGrasshopperMoves([x, y]) {
77 let moves = [];
78 // Look in every direction until an obstacle (to jump) is met
79 for (const step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
80 let i = x + step[0];
81 let j = y + step[1];
82 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
83 i += step[0];
84 j += step[1];
85 }
86 // Move is valid if the next square is empty or occupied by enemy
87 const nextSq = [i+step[0], j+step[1]];
88 if (V.OnBoard(nextSq[0], nextSq[1]) && this.canTake([x, y], nextSq))
89 moves.push(this.getBasicMove([x, y], nextSq));
90 }
91 return moves;
92 }
93
94 isAttacked(sq, colors) {
95 return (
96 super.isAttacked(sq, colors) ||
97 this.isAttackedByGrasshopper(sq, colors)
98 );
99 }
100
101 isAttackedByGrasshopper([x, y], colors) {
102 // Reversed process: is there an adjacent obstacle,
103 // and a grasshopper next in the same line?
104 for (const step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
105 const nextSq = [x+step[0], y+step[1]];
106 if (
107 V.OnBoard(nextSq[0], nextSq[1]) &&
108 this.board[nextSq[0]][nextSq[1]] != V.EMPTY
109 ) {
110 let i = nextSq[0] + step[0];
111 let j = nextSq[1] + step[1];
112 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
113 i += step[0];
114 j += step[1];
115 }
116 if (
117 V.OnBoard(i, j) &&
118 this.getPiece(i, j) == V.GRASSHOPPER &&
119 colors.includes(this.getColor(i, j))
120 ) {
121 return true;
122 }
123 }
124 }
125 return false;
126 }
127
128 static get VALUES() {
129 return Object.assign(
130 // TODO: grasshoppers power decline with less pieces on board...
131 { g: 2 },
132 ChessRules.VALUES
133 );
134 }
135
136 static GenRandInitFen(randomness) {
137 return ChessRules.GenRandInitFen(randomness)
138 .replace("w 0 1111 -", "w 0 1111")
139 .replace(
140 "/pppppppp/8/8/8/8/PPPPPPPP/",
141 "/gggggggg/pppppppp/8/8/PPPPPPPP/GGGGGGGG/"
142 );
143 }
144 };