Draft Hiddenqueen, Grasshopper and Knightmate chess (rules unwritten)
[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 GRASSHOPPER() {
7 return "g";
8 }
9
10 static get PIECES() {
11 return ChessRules.PIECES.concat([V.GRASSHOPPER]);
12 }
13
14 getPpath(b) {
15 return (b[1] == V.GRASSHOPPER ? "Grasshopper/" : "") + b;
16 }
17
18 getPotentialMovesFrom([x, y]) {
19 switch (this.getPiece(x, y)) {
20 case V.GRASSHOPPER:
21 return this.getPotentialGrasshopperMoves([x, y]);
22 default:
23 return super.getPotentialMovesFrom([x, y]);
24 }
25 }
26
27 getPotentialGrasshopperMoves([x, y]) {
28 let moves = [];
29 // Look in every direction until an obstacle (to jump) is met
30 for (const step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
31 let i = x + step[0];
32 let j = y + step[1];
33 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
34 i += step[0];
35 j += step[1];
36 }
37 // Move is valid if the next square is empty or occupied by enemy
38 const nextSq = [i+step[0], j+step[1]];
39 if (V.OnBoard(nextSq[0], nextSq[1]) && this.canTake([x, y], nextSq))
40 moves.push(this.getBasicMove([x, y], nextSq));
41 }
42 return moves;
43 }
44
45 isAttacked(sq, colors) {
46 return (
47 super.isAttacked(sq, colors) ||
48 this.isAttackedByGrasshopper(sq, colors)
49 );
50 }
51
52 isAttackedByGrasshopper([x, y], colors) {
53 // Reversed process: is there an adjacent obstacle,
54 // and a grasshopper next in the same line?
55 for (const step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
56 const nextSq = [x+step[0], y+step[1]];
57 if (
58 V.OnBoard(nextSq[0], nextSq[1]) &&
59 this.board[nextSq[0]][nextSq[1]] != V.EMPTY
60 ) {
61 let i = nextSq[0] + step[0];
62 let j = nextSq[1] + step[1];
63 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
64 i += step[0];
65 j += step[1];
66 }
67 if (
68 V.OnBoard(i, j) &&
69 this.getPiece(i, j) == V.GRASSHOPPER &&
70 colors.includes(this.getColor(i, j))
71 ) {
72 return true;
73 }
74 }
75 }
76 return false;
77 }
78
79 static get VALUES() {
80 return Object.assign(
81 // TODO: grasshoppers power decline when less pieces on board...
82 { g: 3 },
83 ChessRules.VALUES
84 );
85 }
86
87 static GenRandInitFen() {
88 let pieces = { w: new Array(10), b: new Array(10) };
89 for (let c of ["w", "b"]) {
90 let positions = ArrayFun.range(8);
91
92 // Get random squares for grasshoppers (unconstrained)
93 let randIndex = randInt(8);
94 const grasshopper1Pos = positions[randIndex];
95 positions.splice(randIndex, 1);
96 randIndex = randInt(7);
97 const grasshopper2Pos = positions[randIndex];
98 positions.splice(randIndex, 1);
99
100 // Knights
101 randIndex = randInt(6);
102 let knight1Pos = positions[randIndex];
103 positions.splice(randIndex, 1);
104 randIndex = randInt(5);
105 let knight2Pos = positions[randIndex];
106 positions.splice(randIndex, 1);
107
108 // Queen
109 randIndex = randInt(4);
110 let queenPos = positions[randIndex];
111 positions.splice(randIndex, 1);
112
113 let rook1Pos = positions[0];
114 let kingPos = positions[1];
115 let rook2Pos = positions[2];
116
117 pieces[c][rook1Pos] = "r";
118 pieces[c][knight1Pos] = "n";
119 pieces[c][grasshopper1Pos] = "g";
120 pieces[c][queenPos] = "q";
121 pieces[c][kingPos] = "k";
122 pieces[c][grasshopper2Pos] = "g";
123 pieces[c][knight2Pos] = "n";
124 pieces[c][rook2Pos] = "r";
125 }
126 return (
127 pieces["b"].join("") +
128 "/pppppppp/8/8/8/8/PPPPPPPP/" +
129 pieces["w"].join("").toUpperCase() +
130 " w 0 1111 -"
131 );
132 }
133 };