Various fixes, additions...
[vchess.git] / public / javascripts / variants / Checkered.js
CommitLineData
1d184b4c
BA
1class CheckeredRules extends ChessRules
2{
1d184b4c
BA
3 static getPpath(b)
4 {
5 return b[0]=='c' ? "Checkered/"+b : b;
6 }
2d7194bd 7
1d184b4c
BA
8 static board2fen(b)
9 {
10 const checkered_codes = {
11 'p': 's',
12 'q': 't',
13 'r': 'u',
14 'b': 'c',
15 'n': 'o',
16 };
17 if (b[0]=="c")
18 return checkered_codes[b[1]];
19 return ChessRules.board2fen(b);
20 }
2d7194bd 21
1d184b4c
BA
22 static fen2board(f)
23 {
7931e479 24 // Tolerate upper-case versions of checkered pieces (why not?)
1d184b4c
BA
25 const checkered_pieces = {
26 's': 'p',
7931e479 27 'S': 'p',
1d184b4c 28 't': 'q',
7931e479 29 'T': 'q',
1d184b4c 30 'u': 'r',
7931e479 31 'U': 'r',
1d184b4c 32 'c': 'b',
7931e479 33 'C': 'b',
1d184b4c 34 'o': 'n',
7931e479 35 'O': 'n',
1d184b4c
BA
36 };
37 if (Object.keys(checkered_pieces).includes(f))
38 return 'c'+checkered_pieces[f];
39 return ChessRules.fen2board(f);
40 }
41
2d7194bd
BA
42 static get PIECES()
43 {
7931e479
BA
44 return ChessRules.PIECES.concat(['s','t','u','c','o']);
45 }
46
47 static IsGoodFlags(flags)
48 {
49 // 4 for castle + 16 for pawns
50 return !!flags.match(/^[01]{20,20}$/);
51 }
52
c794dbb8 53 setFlags(fenflags)
1d184b4c 54 {
c794dbb8 55 super.setFlags(fenflags); //castleFlags
2526c041
BA
56 this.pawnFlags =
57 {
c794dbb8
BA
58 "w": _.map(_.range(8), i => true), //pawns can move 2 squares?
59 "b": _.map(_.range(8), i => true)
2526c041 60 };
c794dbb8
BA
61 if (!fenflags)
62 return;
63 const flags = fenflags.substr(4); //skip first 4 digits, for castle
1d184b4c
BA
64 for (let c of ['w','b'])
65 {
66 for (let i=0; i<8; i++)
2526c041 67 this.pawnFlags[c][i] = (flags.charAt((c=='w'?0:8)+i) == '1');
1d184b4c 68 }
1d184b4c
BA
69 }
70
2d7194bd
BA
71 aggregateFlags()
72 {
2526c041 73 return [this.castleFlags, this.pawnFlags];
1d184b4c
BA
74 }
75
2d7194bd 76 disaggregateFlags(flags)
1d184b4c 77 {
2526c041
BA
78 this.castleFlags = flags[0];
79 this.pawnFlags = flags[1];
1d184b4c
BA
80 }
81
2526c041 82 canTake([x1,y1], [x2,y2])
1d184b4c 83 {
2526c041
BA
84 const color1 = this.getColor(x1,y1);
85 const color2 = this.getColor(x2,y2);
86 // Checkered aren't captured
87 return color1 != color2 && color2 != 'c' && (color1 != 'c' || color2 != this.turn);
1d184b4c
BA
88 }
89
2526c041
BA
90 // Post-processing: apply "checkerization" of standard moves
91 getPotentialMovesFrom([x,y])
1d184b4c 92 {
2526c041 93 let standardMoves = super.getPotentialMovesFrom([x,y]);
aea1443e 94 const lastRank = this.turn == "w" ? 0 : 7;
0b7d99ec 95 if (this.getPiece(x,y) == V.KING)
2526c041 96 return standardMoves; //king has to be treated differently (for castles)
1d184b4c 97 let moves = [];
2526c041 98 standardMoves.forEach(m => {
0b7d99ec 99 if (m.vanish[0].p == V.PAWN)
1d184b4c 100 {
2316f8b8
BA
101 if (Math.abs(m.end.x-m.start.x)==2 && !this.pawnFlags[this.turn][m.start.y])
102 return; //skip forbidden 2-squares jumps
0b7d99ec
BA
103 if (this.board[m.end.x][m.end.y] == V.EMPTY && m.vanish.length==2
104 && this.getColor(m.start.x,m.start.y) == 'c')
2316f8b8
BA
105 {
106 return; //checkered pawns cannot take en-passant
107 }
1d184b4c 108 }
2526c041
BA
109 if (m.vanish.length == 1)
110 moves.push(m); //no capture
111 else
1d184b4c 112 {
2526c041
BA
113 // A capture occured (m.vanish.length == 2)
114 m.appear[0].c = "c";
aea1443e 115 moves.push(m);
68f5ccc8 116 if (m.appear[0].p != m.vanish[1].p //avoid promotions (already treated):
0b7d99ec 117 && (m.vanish[0].p != V.PAWN || m.end.x != lastRank))
1d184b4c 118 {
2526c041
BA
119 // Add transformation into captured piece
120 let m2 = JSON.parse(JSON.stringify(m));
aea1443e 121 m2.appear[0].p = m.vanish[1].p;
2526c041 122 moves.push(m2);
1d184b4c
BA
123 }
124 }
2526c041 125 });
1d184b4c
BA
126 return moves;
127 }
128
46302e64 129 canIplay(side, [x,y])
1d184b4c 130 {
7931e479 131 return (side == this.turn && [side,'c'].includes(this.getColor(x,y)));
1d184b4c
BA
132 }
133
134 // Does m2 un-do m1 ? (to disallow undoing checkered moves)
135 oppositeMoves(m1, m2)
136 {
137 return m1.appear.length == 1 && m2.appear.length == 1
138 && m1.vanish.length == 1 && m2.vanish.length == 1
1d184b4c
BA
139 && m1.start.x == m2.end.x && m1.end.x == m2.start.x
140 && m1.start.y == m2.end.y && m1.end.y == m2.start.y
141 && m1.appear[0].c == m2.vanish[0].c && m1.appear[0].p == m2.vanish[0].p
142 && m1.vanish[0].c == m2.appear[0].c && m1.vanish[0].p == m2.appear[0].p;
143 }
144
145 filterValid(moves)
146 {
147 if (moves.length == 0)
148 return [];
46302e64 149 const color = this.turn;
1d184b4c
BA
150 return moves.filter(m => {
151 const L = this.moves.length;
152 if (L > 0 && this.oppositeMoves(this.moves[L-1], m))
153 return false;
f6dbe8e3
BA
154 this.play(m);
155 const res = !this.underCheck(color);
156 this.undo(m);
157 return res;
1d184b4c
BA
158 });
159 }
160
46302e64
BA
161 isAttackedByPawn([x,y], colors)
162 {
163 for (let c of colors)
164 {
165 const color = (c=="c" ? this.turn : c);
166 let pawnShift = (color=="w" ? 1 : -1);
167 if (x+pawnShift>=0 && x+pawnShift<8)
168 {
169 for (let i of [-1,1])
170 {
0b7d99ec 171 if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==V.PAWN
46302e64
BA
172 && this.getColor(x+pawnShift,y+i)==c)
173 {
174 return true;
175 }
176 }
177 }
178 }
179 return false;
180 }
1d184b4c 181
f6dbe8e3 182 underCheck(color)
1d184b4c 183 {
f6dbe8e3 184 return this.isAttacked(this.kingPos[color], [this.getOppCol(color),'c']);
1d184b4c
BA
185 }
186
f6dbe8e3 187 getCheckSquares(color)
bd6ff57c 188 {
2d7194bd
BA
189 // Artifically change turn, for checkered pawns
190 this.turn = this.getOppCol(color);
92342261
BA
191 const kingAttacked = this.isAttacked(
192 this.kingPos[color], [this.getOppCol(color),'c']);
bd6ff57c 193 let res = kingAttacked
2d7194bd
BA
194 ? [JSON.parse(JSON.stringify(this.kingPos[color]))] //need to duplicate!
195 : [];
196 this.turn = color;
bd6ff57c
BA
197 return res;
198 }
199
1d184b4c
BA
200 updateVariables(move)
201 {
0b7d99ec
BA
202 super.updateVariables(move);
203 // Does this move turn off a 2-squares pawn flag?
1d184b4c 204 const secondRank = [1,6];
0b7d99ec 205 if (secondRank.includes(move.start.x) && move.vanish[0].p == V.PAWN)
2526c041 206 this.pawnFlags[move.start.x==6 ? "w" : "b"][move.start.y] = false;
1d184b4c
BA
207 }
208
46302e64 209 checkGameEnd()
1d184b4c 210 {
46302e64 211 const color = this.turn;
7931e479
BA
212 // Artifically change turn, for checkered pawns
213 this.turn = this.getOppCol(this.turn);
e2b216fe
BA
214 const res = this.isAttacked(this.kingPos[color], [this.getOppCol(color),'c'])
215 ? (color == "w" ? "0-1" : "1-0")
216 : "1/2";
7931e479 217 this.turn = this.getOppCol(this.turn);
e2b216fe 218 return res;
1d184b4c
BA
219 }
220
221 evalPosition()
222 {
1d184b4c
BA
223 let evaluation = 0;
224 //Just count material for now, considering checkered neutral (...)
0b7d99ec 225 for (let i=0; i<V.size.x; i++)
1d184b4c 226 {
0b7d99ec 227 for (let j=0; j<V.size.y; j++)
1d184b4c 228 {
0b7d99ec 229 if (this.board[i][j] != V.EMPTY)
1d184b4c
BA
230 {
231 const sqColor = this.getColor(i,j);
232 const sign = sqColor == "w" ? 1 : (sqColor=="b" ? -1 : 0);
0b7d99ec 233 evaluation += sign * V.VALUES[this.getPiece(i,j)];
1d184b4c
BA
234 }
235 }
236 }
237 return evaluation;
238 }
239
240 static GenRandInitFen()
241 {
c794dbb8
BA
242 const randFen = ChessRules.GenRandInitFen();
243 // Add 16 pawns flags:
2d7194bd 244 return randFen.replace(" w 1111", " w 11111111111111111111");
1d184b4c
BA
245 }
246
247 getFlagsFen()
248 {
2526c041 249 let fen = super.getFlagsFen();
1d184b4c
BA
250 // Add pawns flags
251 for (let c of ['w','b'])
252 {
253 for (let i=0; i<8; i++)
2526c041 254 fen += this.pawnFlags[c][i] ? '1' : '0';
1d184b4c
BA
255 }
256 return fen;
257 }
258
259 getNotation(move)
260 {
261 if (move.appear.length == 2)
262 {
263 // Castle
264 if (move.end.y < move.start.y)
265 return "0-0-0";
266 else
267 return "0-0";
268 }
269
270 // Translate final square
2d7194bd 271 const finalSquare = V.CoordsToSquare(move.end);
1d184b4c 272
2d7194bd 273 const piece = this.getPiece(move.start.x, move.start.y);
0b7d99ec 274 if (piece == V.PAWN)
1d184b4c
BA
275 {
276 // Pawn move
277 let notation = "";
278 if (move.vanish.length > 1)
279 {
280 // Capture
26c1e3bd 281 const startColumn = V.CoordToColumn(move.start.y);
92342261
BA
282 notation = startColumn + "x" + finalSquare +
283 "=" + move.appear[0].p.toUpperCase();
1d184b4c
BA
284 }
285 else //no capture
098e8468 286 {
1d184b4c 287 notation = finalSquare;
098e8468
BA
288 if (move.appear.length > 0 && piece != move.appear[0].p) //promotion
289 notation += "=" + move.appear[0].p.toUpperCase();
290 }
1d184b4c
BA
291 return notation;
292 }
293
294 else
295 {
296 // Piece movement
297 return piece.toUpperCase() + (move.vanish.length > 1 ? "x" : "") + finalSquare
298 + (move.vanish.length > 1 ? "=" + move.appear[0].p.toUpperCase() : "");
299 }
300 }
301}
643479f8
BA
302
303const VariantRules = CheckeredRules;