Save current state (unfinished, untested)
[vchess.git] / public / javascripts / variants / Wildebeest.js
CommitLineData
8a196305 1class WildebeestRules extends ChessRules
a37076f1
BA
2{
3 static getPpath(b)
4 {
a37076f1
BA
5 return ([V.CAMEL,V.WILDEBEEST].includes(b[1]) ? "Wildebeest/" : "") + b;
6 }
7
0b7d99ec 8 static get size() { return {x:10,y:11}; }
8a196305 9
a37076f1
BA
10 static get CAMEL() { return 'c'; }
11 static get WILDEBEEST() { return 'w'; }
12
7931e479
BA
13 static get PIECES() {
14 return ChessRules.PIECES.concat([V.CAMEL,V.WILDEBEEST]);
15 }
16
a37076f1
BA
17 static get steps() {
18 return Object.assign(
19 ChessRules.steps, //add camel moves:
20 {'c': [ [-3,-1],[-3,1],[-1,-3],[-1,3],[1,-3],[1,3],[3,-1],[3,1] ]}
21 );
22 }
23
8a196305 24 // En-passant after 2-sq or 3-sq jumps
a37076f1
BA
25 getEpSquare(move)
26 {
27 const [sx,sy,ex] = [move.start.x,move.start.y,move.end.x];
0b7d99ec 28 if (this.getPiece(sx,sy) == V.PAWN && Math.abs(sx - ex) >= 2)
a37076f1 29 {
8a196305
BA
30 const step = (ex-sx) / Math.abs(ex-sx);
31 let res = [{
32 x: sx + step,
a37076f1 33 y: sy
8a196305
BA
34 }];
35 if (sx + 2*step != ex) //3-squares move
36 {
37 res.push({
38 x: sx + 2*step,
39 y: sy
40 });
41 }
42 return res;
a37076f1
BA
43 }
44 return undefined; //default
45 }
46
47 getPotentialMovesFrom([x,y])
48 {
49 switch (this.getPiece(x,y))
50 {
0b7d99ec 51 case V.CAMEL:
a37076f1 52 return this.getPotentialCamelMoves([x,y]);
0b7d99ec 53 case V.WILDEBEEST:
a37076f1
BA
54 return this.getPotentialWildebeestMoves([x,y]);
55 default:
56 return super.getPotentialMovesFrom([x,y])
57 }
58 }
59
8a196305 60 // Pawns jump 2 or 3 squares, and promote to queen or wildebeest
a37076f1
BA
61 getPotentialPawnMoves([x,y])
62 {
63 const color = this.turn;
64 let moves = [];
0b7d99ec 65 const [sizeX,sizeY] = [V.size.x,V.size.y];
a37076f1 66 const shift = (color == "w" ? -1 : 1);
cf130369
BA
67 const startRanks = (color == "w" ? [sizeX-2,sizeX-3] : [1,2]);
68 const lastRank = (color == "w" ? 0 : sizeX-1);
a37076f1
BA
69
70 if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank)
71 {
72 // Normal moves
73 if (this.board[x+shift][y] == V.EMPTY)
74 {
75 moves.push(this.getBasicMove([x,y], [x+shift,y]));
8a196305 76 if (startRanks.includes(x) && this.board[x+2*shift][y] == V.EMPTY)
a37076f1
BA
77 {
78 // Two squares jump
79 moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
8a196305
BA
80 if (x == startRanks[0] && this.board[x+3*shift][y] == V.EMPTY)
81 {
82 // 3-squares jump
83 moves.push(this.getBasicMove([x,y], [x+3*shift,y]));
84 }
a37076f1
BA
85 }
86 }
87 // Captures
92342261
BA
88 if (y>0 && this.canTake([x,y], [x+shift,y-1])
89 && this.board[x+shift][y-1] != V.EMPTY)
90 {
a37076f1 91 moves.push(this.getBasicMove([x,y], [x+shift,y-1]));
92342261
BA
92 }
93 if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1])
94 && this.board[x+shift][y+1] != V.EMPTY)
95 {
a37076f1 96 moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
92342261 97 }
a37076f1
BA
98 }
99
100 if (x+shift == lastRank)
101 {
102 // Promotion
8a196305 103 let promotionPieces = [V.QUEEN,V.WILDEBEEST];
a37076f1
BA
104 promotionPieces.forEach(p => {
105 // Normal move
106 if (this.board[x+shift][y] == V.EMPTY)
107 moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
108 // Captures
92342261
BA
109 if (y>0 && this.canTake([x,y], [x+shift,y-1])
110 && this.board[x+shift][y-1] != V.EMPTY)
111 {
a37076f1 112 moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p}));
92342261
BA
113 }
114 if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1])
115 && this.board[x+shift][y+1] != V.EMPTY)
116 {
a37076f1 117 moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:p}));
92342261 118 }
a37076f1
BA
119 });
120 }
121
122 // En passant
123 const Lep = this.epSquares.length;
124 const epSquare = Lep>0 ? this.epSquares[Lep-1] : undefined;
8a196305 125 if (!!epSquare)
a37076f1 126 {
8a196305
BA
127 for (let epsq of epSquare)
128 {
129 // TODO: some redundant checks
130 if (epsq.x == x+shift && Math.abs(epsq.y - y) == 1)
131 {
132 let epStep = epsq.y - y;
133 var enpassantMove = this.getBasicMove([x,y], [x+shift,y+epStep]);
134 enpassantMove.vanish.push({
135 x: x,
136 y: y+epStep,
137 p: 'p',
138 c: this.getColor(x,y+epStep)
139 });
140 moves.push(enpassantMove);
141 }
142 }
a37076f1
BA
143 }
144
145 return moves;
146 }
147
8a196305
BA
148 // TODO: wildebeest castle
149
a37076f1
BA
150 getPotentialCamelMoves(sq)
151 {
0b7d99ec 152 return this.getSlideNJumpMoves(sq, V.steps[V.CAMEL], "oneStep");
a37076f1
BA
153 }
154
155 getPotentialWildebeestMoves(sq)
156 {
92342261
BA
157 return this.getSlideNJumpMoves(
158 sq, V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]), "oneStep");
a37076f1
BA
159 }
160
a37076f1
BA
161 isAttacked(sq, colors)
162 {
8a196305 163 return super.isAttacked(sq, colors)
a37076f1
BA
164 || this.isAttackedByCamel(sq, colors)
165 || this.isAttackedByWildebeest(sq, colors);
166 }
167
168 isAttackedByCamel(sq, colors)
169 {
170 return this.isAttackedBySlideNJump(sq, colors,
0b7d99ec 171 V.CAMEL, V.steps[V.CAMEL], "oneStep");
a37076f1
BA
172 }
173
174 isAttackedByWildebeest(sq, colors)
175 {
a37076f1 176 return this.isAttackedBySlideNJump(sq, colors, V.WILDEBEEST,
cf130369 177 V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]), "oneStep");
a37076f1
BA
178 }
179
cf130369
BA
180 checkGameEnd()
181 {
182 // No valid move: game is lost (stalemate is a win)
183 return this.turn == "w" ? "0-1" : "1-0";
184 }
efb20746 185
a37076f1
BA
186 static get VALUES() {
187 return Object.assign(
188 ChessRules.VALUES,
189 {'c': 3, 'w': 7} //experimental
190 );
191 }
192
3c09dc49
BA
193 static get SEARCH_DEPTH() { return 2; }
194
a37076f1
BA
195 static GenRandInitFen()
196 {
92342261
BA
197 let pieces = { "w": new Array(10), "b": new Array(10) };
198 for (let c of ["w","b"])
a37076f1 199 {
8a196305 200 let positions = _.range(11);
a37076f1 201
dca02599
BA
202 // Get random squares for bishops + camels (different colors)
203 let randIndexes = _.sample(_.range(6), 2).map(i => { return 2*i; });
204 let bishop1Pos = positions[randIndexes[0]];
205 let camel1Pos = positions[randIndexes[1]];
206 // The second bishop (camel) must be on a square of different color
207 let randIndexes_tmp = _.sample(_.range(5), 2).map(i => { return 2*i+1; });
208 let bishop2Pos = positions[randIndexes_tmp[0]];
209 let camel2Pos = positions[randIndexes_tmp[1]];
92342261
BA
210 for (let idx of randIndexes.concat(randIndexes_tmp)
211 .sort((a,b) => { return b-a; })) //largest indices first
212 {
dca02599 213 positions.splice(idx, 1);
92342261 214 }
a37076f1 215
dca02599 216 let randIndex = _.random(6);
a37076f1
BA
217 let knight1Pos = positions[randIndex];
218 positions.splice(randIndex, 1);
dca02599 219 randIndex = _.random(5);
a37076f1
BA
220 let knight2Pos = positions[randIndex];
221 positions.splice(randIndex, 1);
222
8a196305 223 randIndex = _.random(4);
dca02599 224 let queenPos = positions[randIndex];
8a196305
BA
225 positions.splice(randIndex, 1);
226
92342261 227 // Random square for wildebeest
8a196305
BA
228 randIndex = _.random(3);
229 let wildebeestPos = positions[randIndex];
230 positions.splice(randIndex, 1);
231
a37076f1
BA
232 let rook1Pos = positions[0];
233 let kingPos = positions[1];
234 let rook2Pos = positions[2];
235
a37076f1
BA
236 pieces[c][rook1Pos] = 'r';
237 pieces[c][knight1Pos] = 'n';
238 pieces[c][bishop1Pos] = 'b';
239 pieces[c][queenPos] = 'q';
8a196305
BA
240 pieces[c][camel1Pos] = 'c';
241 pieces[c][camel2Pos] = 'c';
242 pieces[c][wildebeestPos] = 'w';
a37076f1
BA
243 pieces[c][kingPos] = 'k';
244 pieces[c][bishop2Pos] = 'b';
245 pieces[c][knight2Pos] = 'n';
246 pieces[c][rook2Pos] = 'r';
247 }
c794dbb8 248 return pieces["b"].join("") +
8a196305 249 "/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/" +
92342261 250 pieces["w"].join("").toUpperCase() +
c794dbb8 251 " 1111 w";
a37076f1
BA
252 }
253}