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