Fix en-passant for Wildebeest, draft Benedict variant
[vchess.git] / client / src / variants / Benedict.js
1 import { ChessRules, PiPo, Move } from "@/base_rules";
2
3 export const VariantRules = class BenedictRules extends ChessRules {
4 static get HasEnpassant() {
5 return false;
6 }
7
8 // TODO(?): some duplicated code in 2 next functions
9 getSlideNJumpMoves([x, y], steps, oneStep) {
10 let moves = [];
11 outerLoop: for (let loop = 0; loop < steps.length; loop++) {
12 const step = steps[loop];
13 let i = x + step[0];
14 let j = y + step[1];
15 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
16 moves.push(this.getBasicMove([x, y], [i, j]));
17 if (oneStep) continue outerLoop;
18 i += step[0];
19 j += step[1];
20 }
21 // No capture check: handled elsewhere (next method)
22 }
23 return moves;
24 }
25
26 // Find possible captures from a square
27 // follow steps from x,y until something is met.
28 findCaptures([x, y]) {
29 const color = this.getColor(x, y);
30 const piece = this.getPiece(x, y);
31 let squares = [];
32 const steps =
33 piece != V.PAWN
34 ? [V.QUEEN,V.KING].includes(piece)
35 ? V.steps[V.ROOK].concat(V.steps[V.BISHOP])
36 : V.steps[piece]
37 : color == "w"
38 ? [
39 [-1, -1],
40 [-1, 1]
41 ]
42 : [
43 [1, -1],
44 [1, 1]
45 ];
46 const oneStep = [V.KNIGHT,V.PAWN,V.KING].includes(piece);
47 outerLoop: for (let loop = 0; loop < steps.length; loop++) {
48 const step = steps[loop];
49 let i = x + step[0];
50 let j = y + step[1];
51 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
52 if (oneStep) continue outerLoop;
53 i += step[0];
54 j += step[1];
55 }
56 if (
57 V.OnBoard(i, j) &&
58 this.getColor(i, j) == V.GetOppCol(color)
59 ) {
60 // eat!
61 squares.push([i, j]);
62 }
63 }
64 return squares;
65 }
66
67 getPotentialPawnMoves([x, y]) {
68 const color = this.getColor(x, y);
69 let moves = [];
70 const sizeY = V.size.y;
71 const shift = color == "w" ? -1 : 1;
72 const startRank = color == "w" ? sizeY - 2 : 1;
73 const firstRank = color == "w" ? sizeY - 1 : 0;
74 const lastRank = color == "w" ? 0 : sizeY - 1;
75
76 if (x + shift != lastRank) {
77 // Normal moves
78 if (this.board[x + shift][y] == V.EMPTY) {
79 moves.push(this.getBasicMove([x, y], [x + shift, y]));
80 if (
81 [startRank, firstRank].includes(x) &&
82 this.board[x + 2 * shift][y] == V.EMPTY
83 ) {
84 // Two squares jump
85 moves.push(this.getBasicMove([x, y], [x + 2 * shift, y]));
86 }
87 }
88 }
89 else {
90 // Promotion
91 let promotionPieces = [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN];
92 promotionPieces.forEach(p => {
93 // Normal move
94 if (this.board[x + shift][y] == V.EMPTY)
95 moves.push(
96 this.getBasicMove([x, y], [x + shift, y], { c: color, p: p })
97 );
98 });
99 }
100
101 // No en passant here
102
103 return moves;
104 }
105
106 getPotentialRookMoves(sq) {
107 return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]);
108 }
109
110 getPotentialKnightMoves(sq) {
111 return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep");
112 }
113
114 getPotentialBishopMoves(sq) {
115 return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]);
116 }
117
118 getPotentialQueenMoves(sq) {
119 return this.getSlideNJumpMoves(
120 sq,
121 V.steps[V.ROOK].concat(V.steps[V.BISHOP])
122 );
123 }
124
125 getPotentialKingMoves(sq) {
126 // Initialize with normal (non-capturing) moves
127 let noCaptures = this.getSlideNJumpMoves(
128 sq,
129 V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
130 "oneStep"
131 );
132 return noCaptures.concat(this.getCastleMoves(sq));
133 }
134
135 // TODO: appear/vanish description of a move is too verbose for Benedict.
136 // => Would need a new "flipped" array, to be passed in Game.vue...
137 getPotentialMovesFrom([x, y]) {
138 const color = this.turn;
139 const oppCol = V.GetOppCol(color);
140 // Get all moves from x,y without captures:
141 let moves = super.getPotentialMovesFrom([x, y]);
142 // Add flips:
143 moves.forEach(m => {
144 V.PlayOnBoard(this.board, m);
145 const flipped = this.findCaptures([m.end.x, m.end.y]);
146 V.UndoOnBoard(this.board, m);
147 flipped.forEach(sq => {
148 const piece = this.getPiece(sq[0],sq[1]);
149 const pipoA = new PiPo({
150 x:sq[0],
151 y:sq[1],
152 c:color,
153 p:piece
154 });
155 const pipoV = new PiPo({
156 x:sq[0],
157 y:sq[1],
158 c:oppCol,
159 p:piece
160 });
161 m.appear.push(pipoA);
162 m.vanish.push(pipoV);
163 });
164 });
165 return moves;
166 }
167
168 // Moves cannot flip our king's color, so all are valid
169 filterValid(moves) {
170 return moves;
171 }
172
173 // No notion of check here:
174 getCheckSquares() {
175 return [];
176 }
177
178 getCurrentScore() {
179 const color = this.turn;
180 // Did a king change color?
181 const kp = this.kingPos[color];
182 if (this.getColor(kp[0], kp[1]) != color)
183 return color == "w" ? "0-1" : "1-0";
184 return "*";
185 }
186 };