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