Started code review + some fixes (unfinished)
[vchess.git] / client / src / variants / Zen.js
1 import { ChessRules } from "@/base_rules";
2
3 export const VariantRules = class ZenRules extends ChessRules {
4 // NOTE: enPassant, if enabled, would need to redefine carefully getEpSquare
5 static get HasEnpassant() {
6 return false;
7 }
8
9 // TODO(?): some duplicated code in 2 next functions
10 getSlideNJumpMoves([x, y], steps, oneStep) {
11 let moves = [];
12 outerLoop: for (let loop = 0; loop < steps.length; loop++) {
13 const step = steps[loop];
14 let i = x + step[0];
15 let j = y + step[1];
16 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
17 moves.push(this.getBasicMove([x, y], [i, j]));
18 if (oneStep) continue outerLoop;
19 i += step[0];
20 j += step[1];
21 }
22 // No capture check: handled elsewhere (next method)
23 }
24 return moves;
25 }
26
27 // follow steps from x,y until something is met.
28 // if met piece is opponent and same movement (asA): eat it!
29 findCaptures_aux([x, y], asA) {
30 const color = this.getColor(x, y);
31 var moves = [];
32 const steps =
33 asA != V.PAWN
34 ? asA == V.QUEEN
35 ? V.steps[V.ROOK].concat(V.steps[V.BISHOP])
36 : V.steps[asA]
37 : color == "w"
38 ? [
39 [-1, -1],
40 [-1, 1]
41 ]
42 : [
43 [1, -1],
44 [1, 1]
45 ];
46 const oneStep = asA == V.KNIGHT || asA == V.PAWN; //we don't capture king
47 const lastRank = color == "w" ? 0 : V.size.x - 1;
48 const promotionPieces = [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN];
49 outerLoop: for (let loop = 0; loop < steps.length; loop++) {
50 const step = steps[loop];
51 let i = x + step[0];
52 let j = y + step[1];
53 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
54 if (oneStep) continue outerLoop;
55 i += step[0];
56 j += step[1];
57 }
58 if (
59 V.OnBoard(i, j) &&
60 this.getColor(i, j) == V.GetOppCol(color) &&
61 this.getPiece(i, j) == asA
62 ) {
63 // eat!
64 if (this.getPiece(x, y) == V.PAWN && i == lastRank) {
65 // Special case of promotion:
66 promotionPieces.forEach(p => {
67 moves.push(this.getBasicMove([x, y], [i, j], { c: color, p: p }));
68 });
69 } else {
70 // All other cases
71 moves.push(this.getBasicMove([x, y], [i, j]));
72 }
73 }
74 }
75 return moves;
76 }
77
78 // Find possible captures from a square: look in every direction!
79 findCaptures(sq) {
80 let moves = [];
81
82 Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.PAWN));
83 Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.ROOK));
84 Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.KNIGHT));
85 Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.BISHOP));
86 Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.QUEEN));
87
88 return moves;
89 }
90
91 getPotentialPawnMoves([x, y]) {
92 const color = this.getColor(x, y);
93 let moves = [];
94 const sizeY = V.size.y;
95 const shift = color == "w" ? -1 : 1;
96 const startRank = color == "w" ? sizeY - 2 : 1;
97 const firstRank = color == "w" ? sizeY - 1 : 0;
98 const lastRank = color == "w" ? 0 : sizeY - 1;
99
100 if (x + shift != lastRank) {
101 // Normal moves
102 if (this.board[x + shift][y] == V.EMPTY) {
103 moves.push(this.getBasicMove([x, y], [x + shift, y]));
104 if (
105 [startRank, firstRank].includes(x) &&
106 this.board[x + 2 * shift][y] == V.EMPTY
107 ) {
108 //two squares jump
109 moves.push(this.getBasicMove([x, y], [x + 2 * shift, y]));
110 }
111 }
112 } //promotion
113 else {
114 let promotionPieces = [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN];
115 promotionPieces.forEach(p => {
116 // Normal move
117 if (this.board[x + shift][y] == V.EMPTY)
118 moves.push(
119 this.getBasicMove([x, y], [x + shift, y], { c: color, p: p })
120 );
121 });
122 }
123
124 // No en passant here
125
126 // Add "zen" captures
127 Array.prototype.push.apply(moves, this.findCaptures([x, y]));
128
129 return moves;
130 }
131
132 getPotentialRookMoves(sq) {
133 let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.ROOK]);
134 let captures = this.findCaptures(sq);
135 return noCaptures.concat(captures);
136 }
137
138 getPotentialKnightMoves(sq) {
139 let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep");
140 let captures = this.findCaptures(sq);
141 return noCaptures.concat(captures);
142 }
143
144 getPotentialBishopMoves(sq) {
145 let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]);
146 let captures = this.findCaptures(sq);
147 return noCaptures.concat(captures);
148 }
149
150 getPotentialQueenMoves(sq) {
151 let noCaptures = this.getSlideNJumpMoves(
152 sq,
153 V.steps[V.ROOK].concat(V.steps[V.BISHOP])
154 );
155 let captures = this.findCaptures(sq);
156 return noCaptures.concat(captures);
157 }
158
159 getPotentialKingMoves(sq) {
160 // Initialize with normal moves
161 let noCaptures = this.getSlideNJumpMoves(
162 sq,
163 V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
164 "oneStep"
165 );
166 let captures = this.findCaptures(sq);
167 return noCaptures.concat(captures).concat(this.getCastleMoves(sq));
168 }
169
170 getNotation(move) {
171 // Recognize special moves first
172 if (move.appear.length == 2) {
173 // castle
174 if (move.end.y < move.start.y) return "0-0-0";
175 return "0-0";
176 }
177
178 // Translate initial square (because pieces may fly unusually in this variant!)
179 const initialSquare = V.CoordsToSquare(move.start);
180
181 // Translate final square
182 const finalSquare = V.CoordsToSquare(move.end);
183
184 let notation = "";
185 const piece = this.getPiece(move.start.x, move.start.y);
186 if (piece == V.PAWN) {
187 // pawn move (TODO: enPassant indication)
188 if (move.vanish.length > 1) {
189 // capture
190 notation = initialSquare + "x" + finalSquare;
191 } //no capture
192 else notation = finalSquare;
193 if (piece != move.appear[0].p)
194 //promotion
195 notation += "=" + move.appear[0].p.toUpperCase();
196 } else {
197 // Piece movement
198 notation = piece.toUpperCase();
199 if (move.vanish.length > 1) notation += initialSquare + "x";
200 notation += finalSquare;
201 }
202 return notation;
203 }
204
205 static get VALUES() {
206 return {
207 p: 1,
208 r: 3,
209 n: 2,
210 b: 2,
211 q: 5,
212 k: 1000
213 };
214 }
215 };