a5a5d6448148086a7f15d9471174e2918ed2050b
[vchess.git] / client / src / variants / Circular.js
1 import { ChessRules, PiPo, Move } from "@/base_rules";
2 import { ArrayFun } from "@/utils/array";
3 import { randInt, shuffle } from "@/utils/alea";
4
5 export const VariantRules = class CircularRules extends ChessRules {
6 static get HasFlags() {
7 return false;
8 }
9
10 static get HasEnpassant() {
11 return false;
12 }
13
14 // TODO: CanFlip --> also for racing kings (answer is false)
15
16 // TODO: shuffle on 1st and 5th ranks
17 static GenRandInitFen() {
18 let pieces = { w: new Array(8), b: new Array(8) };
19 // Shuffle pieces on first and last rank
20 for (let c of ["w", "b"]) {
21 let positions = ArrayFun.range(8);
22
23 // Get random squares for bishops
24 let randIndex = 2 * randInt(4);
25 const bishop1Pos = positions[randIndex];
26 // The second bishop must be on a square of different color
27 let randIndex_tmp = 2 * randInt(4) + 1;
28 const bishop2Pos = positions[randIndex_tmp];
29 // Remove chosen squares
30 positions.splice(Math.max(randIndex, randIndex_tmp), 1);
31 positions.splice(Math.min(randIndex, randIndex_tmp), 1);
32
33 // Get random squares for knights
34 randIndex = randInt(6);
35 const knight1Pos = positions[randIndex];
36 positions.splice(randIndex, 1);
37 randIndex = randInt(5);
38 const knight2Pos = positions[randIndex];
39 positions.splice(randIndex, 1);
40
41 // Get random square for queen
42 randIndex = randInt(4);
43 const queenPos = positions[randIndex];
44 positions.splice(randIndex, 1);
45
46 // Rooks and king positions are now fixed,
47 // because of the ordering rook-king-rook
48 const rook1Pos = positions[0];
49 const kingPos = positions[1];
50 const rook2Pos = positions[2];
51
52 // Finally put the shuffled pieces in the board array
53 pieces[c][rook1Pos] = "r";
54 pieces[c][knight1Pos] = "n";
55 pieces[c][bishop1Pos] = "b";
56 pieces[c][queenPos] = "q";
57 pieces[c][kingPos] = "k";
58 pieces[c][bishop2Pos] = "b";
59 pieces[c][knight2Pos] = "n";
60 pieces[c][rook2Pos] = "r";
61 }
62 return (
63 pieces["b"].join("") +
64 "/pppppppp/8/8/8/8/PPPPPPPP/" +
65 pieces["w"].join("").toUpperCase() +
66 " w 0"
67 );
68 }
69
70 // TODO: adapt this for a circular board
71 getSlideNJumpMoves([x, y], steps, oneStep) {
72 let moves = [];
73 outerLoop: for (let step of steps) {
74 let i = x + step[0];
75 let j = y + step[1];
76 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
77 moves.push(this.getBasicMove([x, y], [i, j]));
78 if (oneStep !== undefined) continue outerLoop;
79 i += step[0];
80 j += step[1];
81 }
82 if (V.OnBoard(i, j) && this.canTake([x, y], [i, j]))
83 moves.push(this.getBasicMove([x, y], [i, j]));
84 }
85 return moves;
86 }
87
88 // TODO: adapt: all pawns go in thz same direction!
89 getPotentialPawnMoves([x, y]) {
90 const color = this.turn;
91 let moves = [];
92 const [sizeX, sizeY] = [V.size.x, V.size.y];
93 const shiftX = color == "w" ? -1 : 1;
94 const firstRank = color == "w" ? sizeX - 1 : 0;
95 const startRank = color == "w" ? sizeX - 2 : 1;
96 const lastRank = color == "w" ? 0 : sizeX - 1;
97 const pawnColor = this.getColor(x, y); //can be different for checkered
98
99 // NOTE: next condition is generally true (no pawn on last rank)
100 if (x + shiftX >= 0 && x + shiftX < sizeX) {
101 const finalPieces =
102 x + shiftX == lastRank
103 ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]
104 : [V.PAWN];
105 // One square forward
106 if (this.board[x + shiftX][y] == V.EMPTY) {
107 for (let piece of finalPieces) {
108 moves.push(
109 this.getBasicMove([x, y], [x + shiftX, y], {
110 c: pawnColor,
111 p: piece
112 })
113 );
114 }
115 // Next condition because pawns on 1st rank can generally jump
116 if (
117 [startRank, firstRank].includes(x) &&
118 this.board[x + 2 * shiftX][y] == V.EMPTY
119 ) {
120 // Two squares jump
121 moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
122 }
123 }
124 // Captures
125 for (let shiftY of [-1, 1]) {
126 if (
127 y + shiftY >= 0 &&
128 y + shiftY < sizeY &&
129 this.board[x + shiftX][y + shiftY] != V.EMPTY &&
130 this.canTake([x, y], [x + shiftX, y + shiftY])
131 ) {
132 for (let piece of finalPieces) {
133 moves.push(
134 this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
135 c: pawnColor,
136 p: piece
137 })
138 );
139 }
140 }
141 }
142 }
143
144 return moves;
145 }
146
147 // What are the king moves from square x,y ?
148 getPotentialKingMoves(sq) {
149 return this.getSlideNJumpMoves(
150 sq,
151 V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
152 "oneStep"
153 );
154 }
155
156 // TODO: check boundaries here as well
157 isAttackedByPawn([x, y], colors) {
158 for (let c of colors) {
159 let pawnShift = c == "w" ? 1 : -1;
160 if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
161 for (let i of [-1, 1]) {
162 if (
163 y + i >= 0 &&
164 y + i < V.size.y &&
165 this.getPiece(x + pawnShift, y + i) == V.PAWN &&
166 this.getColor(x + pawnShift, y + i) == c
167 ) {
168 return true;
169 }
170 }
171 }
172 }
173 return false;
174 }
175
176 // TODO: adapt this function
177 isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) {
178 for (let step of steps) {
179 let rx = x + step[0],
180 ry = y + step[1];
181 while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) {
182 rx += step[0];
183 ry += step[1];
184 }
185 if (
186 V.OnBoard(rx, ry) &&
187 this.getPiece(rx, ry) === piece &&
188 colors.includes(this.getColor(rx, ry))
189 ) {
190 return true;
191 }
192 }
193 return false;
194 }
195 };