Fix capture-in-check bug for Capture and Losers variants
[vchess.git] / client / src / variants / Cylinder.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 class CylinderRules extends ChessRules {
6 // Output basically x % 8 (circular board)
7 static ComputeY(y) {
8 let res = y % V.size.y;
9 if (res < 0)
10 res += V.size.y;
11 return res;
12 }
13
14 getSlideNJumpMoves([x, y], steps, oneStep) {
15 let moves = [];
16 // Don't add move twice when running on an infinite rank:
17 let infiniteSteps = {};
18 outerLoop: for (let step of steps) {
19 if (!!infiniteSteps[(-step[0]) + "." + (-step[1])]) continue;
20 let i = x + step[0];
21 let j = V.ComputeY(y + step[1]);
22 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
23 moves.push(this.getBasicMove([x, y], [i, j]));
24 if (oneStep !== undefined) continue outerLoop;
25 i += step[0];
26 j = V.ComputeY(j + step[1]);
27 }
28 if (V.OnBoard(i, j)) {
29 if (i == x && j == y)
30 // Looped back onto initial square
31 infiniteSteps[step[0] + "." + step[1]] = true;
32 else if (this.canTake([x, y], [i, j]))
33 moves.push(this.getBasicMove([x, y], [i, j]));
34 }
35 }
36 return moves;
37 }
38
39 getPotentialPawnMoves([x, y]) {
40 const color = this.turn;
41 let moves = [];
42 const [sizeX, sizeY] = [V.size.x, V.size.y];
43 const shiftX = color == "w" ? -1 : 1;
44 const startRank = color == "w" ? sizeX - 2 : 1;
45 const lastRank = color == "w" ? 0 : sizeX - 1;
46
47 const finalPieces =
48 x + shiftX == lastRank
49 ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]
50 : [V.PAWN];
51 if (this.board[x + shiftX][y] == V.EMPTY) {
52 // One square forward
53 for (let piece of finalPieces) {
54 moves.push(
55 this.getBasicMove([x, y], [x + shiftX, y], {
56 c: color,
57 p: piece
58 })
59 );
60 }
61 if (
62 x == startRank &&
63 this.board[x + 2 * shiftX][y] == V.EMPTY
64 ) {
65 // Two squares jump
66 moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
67 }
68 }
69 // Captures
70 for (let shiftY of [-1, 1]) {
71 const nextFile = V.ComputeY(y + shiftY);
72 if (
73 this.board[x + shiftX][nextFile] != V.EMPTY &&
74 this.canTake([x, y], [x + shiftX, nextFile])
75 ) {
76 for (let piece of finalPieces) {
77 moves.push(
78 this.getBasicMove([x, y], [x + shiftX, nextFile], {
79 c: color,
80 p: piece
81 })
82 );
83 }
84 }
85 }
86
87 // En passant
88 const Lep = this.epSquares.length;
89 const epSquare = this.epSquares[Lep - 1]; //always at least one element
90 if (
91 !!epSquare &&
92 epSquare.x == x + shiftX &&
93 Math.abs( (epSquare.y - y) % V.size.y ) == 1
94 ) {
95 let enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]);
96 enpassantMove.vanish.push({
97 x: x,
98 y: epSquare.y,
99 p: "p",
100 c: this.getColor(x, epSquare.y)
101 });
102 moves.push(enpassantMove);
103 }
104
105 return moves;
106 }
107
108 isAttackedByPawn([x, y], color) {
109 let pawnShift = (color == "w" ? 1 : -1);
110 if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
111 for (let i of [-1, 1]) {
112 const nextFile = V.ComputeY(y + i);
113 if (
114 this.getPiece(x + pawnShift, nextFile) == V.PAWN &&
115 this.getColor(x + pawnShift, nextFile) == color
116 ) {
117 return true;
118 }
119 }
120 }
121 return false;
122 }
123
124 isAttackedBySlideNJump([x, y], color, piece, steps, oneStep) {
125 for (let step of steps) {
126 let rx = x + step[0],
127 ry = V.ComputeY(y + step[1]);
128 while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) {
129 rx += step[0];
130 ry = V.ComputeY(ry + step[1]);
131 }
132 if (
133 V.OnBoard(rx, ry) &&
134 this.getPiece(rx, ry) == piece &&
135 this.getColor(rx, ry) == color
136 ) {
137 return true;
138 }
139 }
140 return false;
141 }
142
143 static get SEARCH_DEPTH() {
144 return 2;
145 }
146
147 static get VALUES() {
148 return {
149 p: 1,
150 r: 5,
151 n: 3,
152 b: 4,
153 q: 10,
154 k: 1000
155 };
156 }
157 };