3626822bfbfed1ef2cc538a0931c90d8a57a65e1
1 class CheckeredRules
extends ChessRules
6 return b
[0]=='c' ? "Checkered/"+b : b
;
10 const checkered_codes
= {
18 return checkered_codes
[b
[1]];
19 return ChessRules
.board2fen(b
);
23 const checkered_pieces
= {
30 if (Object
.keys(checkered_pieces
).includes(f
))
31 return 'c'+checkered_pieces
[f
];
32 return ChessRules
.fen2board(f
);
38 ChessRules
.GetFlags(fen
), //castle
40 "w": new Array(8), //pawns can move 2 squares
44 const fenFlags
= fen
.split(" ")[1].substr(4); //skip first 4 digits, for castle
45 for (let c
of ['w','b'])
47 for (let i
=0; i
<8; i
++)
48 flags
[1][c
][i
] = (fenFlags
.charAt((c
=='w'?0:8)+i
) == '1');
53 // can color1 take color2?
54 canTake(color1
, color2
)
56 // Checkered aren't captured
57 return color1
!= color2
&& color2
!= 'c' && (color1
!= 'c' || color2
!= this.turn
);
60 // Build regular move(s) from its initial and destination squares; tr: transformation
61 getBasicMove(sx
, sy
, ex
, ey
, tr
)
63 if (this.board
[ex
][ey
] == VariantRules
.EMPTY
)
65 // No capture, standard move construction
66 return [super.getBasicMove(sx
,sy
,ex
,ey
,tr
)];
68 let moves
= []; //captures: generally 2 choices, unless 'tr' is specified or piece==king
69 const startPiece
= this.getPiece(sx
,sy
);
70 const endPiece
= this.getPiece(ex
,ey
);
71 const startColor
= this.getColor(sx
,sy
);
72 const endColor
= this.getColor(ex
,ey
);
73 for (let piece
of !!tr
? [tr
] :
74 (startPiece
==VariantRules
.KING
? VariantRules
.KING : _
.uniq([startPiece
,endPiece
])))
81 c: startPiece
==VariantRules
.KING
? startColor : 'c',
105 // Generic method to find possible moves of non-pawn pieces ("sliding or jumping")
106 getSlideNJumpMoves(x
, y
, color
, steps
, oneStep
)
109 let [sizeX
,sizeY
] = VariantRules
.size
;
111 for (var loop
=0; loop
<steps
.length
; loop
++)
113 var step
= steps
[loop
];
116 while (i
>=0 && i
<sizeX
&& j
>=0 && j
<sizeY
117 && this.board
[i
][j
] == VariantRules
.EMPTY
)
119 moves
.push(this.getBasicMove(x
, y
, i
, j
)[0]); //no capture
120 if (oneStep
!== undefined)
125 if (i
>=0 && i
<8 && j
>=0 && j
<8 && this.canTake(color
, this.getColor(i
,j
)))
126 moves
= moves
.concat(this.getBasicMove(x
, y
, i
, j
));
131 // What are the pawn moves from square x,y considering color "color" ?
132 getPotentialPawnMoves(x
, y
, color
)
135 var V
= VariantRules
;
136 let [sizeX
,sizeY
] = VariantRules
.size
;
137 const c
= (color
== 'c' ? this.turn : color
);
138 const shift
= (c
== "w" ? -1 : 1);
139 let startRank
= (c
== "w" ? sizeY
-2 : 1);
140 let lastRank
= (c
== "w" ? 0 : sizeY
-1);
142 if (x
+shift
>= 0 && x
+shift
< sizeX
&& x
+shift
!= lastRank
)
145 if (this.board
[x
+shift
][y
] == V
.EMPTY
)
147 moves
.push(this.getBasicMove(x
, y
, x
+shift
, y
)[0]);
148 if (x
==startRank
&& this.board
[x
+2*shift
][y
] == V
.EMPTY
&& this.flags
[1][c
][y
])
151 moves
.push(this.getBasicMove(x
, y
, x
+2*shift
, y
)[0]);
155 if (y
>0 && this.canTake(this.getColor(x
,y
), this.getColor(x
+shift
,y
-1))
156 && this.board
[x
+shift
][y
-1] != V
.EMPTY
)
158 moves
= moves
.concat(this.getBasicMove(x
, y
, x
+shift
, y
-1));
160 if (y
<sizeY
-1 && this.canTake(this.getColor(x
,y
), this.getColor(x
+shift
,y
+1))
161 && this.board
[x
+shift
][y
+1] != V
.EMPTY
)
163 moves
= moves
.concat(this.getBasicMove(x
, y
, x
+shift
, y
+1));
167 if (x
+shift
== lastRank
)
170 let promotionPieces
= [V
.ROOK
,V
.KNIGHT
,V
.BISHOP
,V
.QUEEN
];
171 promotionPieces
.forEach(p
=> {
173 if (this.board
[x
+shift
][y
] == V
.EMPTY
)
174 moves
.push(this.getBasicMove(x
, y
, x
+shift
, y
, p
)[0]);
176 if (y
>0 && this.canTake(this.getColor(x
,y
), this.getColor(x
+shift
,y
-1))
177 && this.board
[x
+shift
][y
-1] != V
.EMPTY
)
179 moves
= moves
.concat(this.getBasicMove(x
, y
, x
+shift
, y
-1, p
));
181 if (y
<sizeY
-1 && this.canTake(this.getColor(x
,y
), this.getColor(x
+shift
,y
+1))
182 && this.board
[x
+shift
][y
+1] != V
.EMPTY
)
184 moves
= moves
.concat(this.getBasicMove(x
, y
, x
+shift
, y
+1, p
));
190 const Lep
= this.epSquares
.length
;
191 const epSquare
= Lep
>0 ? this.epSquares
[Lep
-1] : undefined;
192 if (!!epSquare
&& epSquare
.x
== x
+shift
&& Math
.abs(epSquare
.y
- y
) == 1)
194 let epStep
= epSquare
.y
- y
;
195 var enpassantMove
= this.getBasicMove(x
, y
, x
+shift
, y
+epStep
)[0];
196 enpassantMove
.vanish
.push({
200 c: this.getColor(x
,y
+epStep
)
202 enpassantMove
.appear
[0].c
= 'c';
203 moves
.push(enpassantMove
);
209 getCastleMoves(x
,y
,c
)
211 if (x
!= (c
=="w" ? 7 : 0) || y
!= this.INIT_COL_KING
[c
])
212 return []; //x isn't first rank, or king has moved (shortcut)
214 const V
= VariantRules
;
217 const oppCol
= this.getOppCol(c
);
220 const finalSquares
= [ [2,3], [6,5] ]; //king, then rook
222 for (let castleSide
=0; castleSide
< 2; castleSide
++) //large, then small
224 if (!this.flags
[0][c
][castleSide
])
226 // If this code is reached, rooks and king are on initial position
228 // Nothing on the path of the king (and no checks; OK also if y==finalSquare)?
229 let step
= finalSquares
[castleSide
][0] < y
? -1 : 1;
230 for (i
=y
; i
!=finalSquares
[castleSide
][0]; i
+=step
)
232 if (this.isAttacked([x
,i
], oppCol
) || (this.board
[x
][i
] != V
.EMPTY
&&
233 // NOTE: next check is enough, because of chessboard constraints
234 (this.getColor(x
,i
) != c
|| ![V
.KING
,V
.ROOK
].includes(this.getPiece(x
,i
)))))
236 continue castlingCheck
;
240 // Nothing on the path to the rook?
241 step
= castleSide
== 0 ? -1 : 1;
242 for (i
= y
+ step
; i
!= this.INIT_COL_ROOK
[c
][castleSide
]; i
+= step
)
244 if (this.board
[x
][i
] != V
.EMPTY
)
245 continue castlingCheck
;
247 const rookPos
= this.INIT_COL_ROOK
[c
][castleSide
];
249 // Nothing on final squares, except maybe king and castling rook?
252 if (this.board
[x
][finalSquares
[castleSide
][i
]] != V
.EMPTY
&&
253 this.getPiece(x
,finalSquares
[castleSide
][i
]) != V
.KING
&&
254 finalSquares
[castleSide
][i
] != rookPos
)
256 continue castlingCheck
;
260 // If this code is reached, castle is valid
261 moves
.push( new Move({
263 new PiPo({x:x
,y:finalSquares
[castleSide
][0],p:V
.KING
,c:c
}),
264 new PiPo({x:x
,y:finalSquares
[castleSide
][1],p:V
.ROOK
,c:c
})],
266 new PiPo({x:x
,y:y
,p:V
.KING
,c:c
}),
267 new PiPo({x:x
,y:rookPos
,p:V
.ROOK
,c:c
})],
268 end: Math
.abs(y
- rookPos
) <= 2
270 : {x:x
, y:y
+ 2 * (castleSide
==0 ? -1 : 1)}
279 return ((color
=='w' && this.moves
.length
%2==0) || color
=='c'
280 || (color
=='b' && this.moves
.length
%2==1))
281 && [color
,'c'].includes(this.getColor(sq
[0], sq
[1]));
284 // Does m2 un-do m1 ? (to disallow undoing checkered moves)
285 oppositeMoves(m1
, m2
)
287 return m1
.appear
.length
== 1 && m2
.appear
.length
== 1
288 && m1
.vanish
.length
== 1 && m2
.vanish
.length
== 1
289 // && _.isEqual(m1.appear[0], m2.vanish[0]) //fails in HH case
290 // && _.isEqual(m1.vanish[0], m2.appear[0]);
291 && m1
.start
.x
== m2
.end
.x
&& m1
.end
.x
== m2
.start
.x
292 && m1
.start
.y
== m2
.end
.y
&& m1
.end
.y
== m2
.start
.y
293 && m1
.appear
[0].c
== m2
.vanish
[0].c
&& m1
.appear
[0].p
== m2
.vanish
[0].p
294 && m1
.vanish
[0].c
== m2
.appear
[0].c
&& m1
.vanish
[0].p
== m2
.appear
[0].p
;
299 if (moves
.length
== 0)
301 let color
= this.getColor( moves
[0].start
.x
, moves
[0].start
.y
);
302 return moves
.filter(m
=> {
303 const L
= this.moves
.length
;
304 if (L
> 0 && this.oppositeMoves(this.moves
[L
-1], m
))
306 return !this.underCheck(m
, color
);
310 isAttackedByPawn([x
,y
], c
)
312 const color
= (c
=="c" ? this.turn : c
);
313 let pawnShift
= (color
=="w" ? 1 : -1);
314 if (x
+pawnShift
>=0 && x
+pawnShift
<8)
316 for (let i
of [-1,1])
318 if (y
+i
>=0 && y
+i
<8 && this.getPiece(x
+pawnShift
,y
+i
)==VariantRules
.PAWN
319 && this.getColor(x
+pawnShift
,y
+i
)==c
)
330 const color
= c
== 'c' ? this.turn : c
;
332 let res
= this.isAttacked(this.kingPos
[color
], this.getOppCol(color
))
333 || this.isAttacked(this.kingPos
[color
], 'c'); //TODO: quite inefficient...
338 getCheckSquares(move, c
)
341 const kingAttacked
= this.isAttacked(this.kingPos
[c
], this.getOppCol(c
))
342 || this.isAttacked(this.kingPos
[c
], 'c');
343 let res
= kingAttacked
344 ? [ JSON
.parse(JSON
.stringify(this.kingPos
[c
])) ] //need to duplicate!
350 updateVariables(move)
352 const piece
= this.getPiece(move.start
.x
,move.start
.y
);
353 const c
= this.getColor(move.start
.x
,move.start
.y
);
355 if (c
!= 'c') //checkered not concerned by castle flags
357 const firstRank
= (c
== "w" ? 7 : 0);
358 // Update king position + flags
359 if (piece
== VariantRules
.KING
&& move.appear
.length
> 0)
361 this.kingPos
[c
][0] = move.appear
[0].x
;
362 this.kingPos
[c
][1] = move.appear
[0].y
;
363 this.flags
[0][c
] = [false,false];
366 const oppCol
= this.getOppCol(c
);
367 const oppFirstRank
= 7 - firstRank
;
368 if (move.start
.x
== firstRank
//our rook moves?
369 && this.INIT_COL_ROOK
[c
].includes(move.start
.y
))
371 const flagIdx
= move.start
.y
== this.INIT_COL_ROOK
[c
][0] ? 0 : 1;
372 this.flags
[0][c
][flagIdx
] = false;
374 else if (move.end
.x
== oppFirstRank
//we took opponent rook?
375 && this.INIT_COL_ROOK
[c
].includes(move.end
.y
))
377 const flagIdx
= move.end
.y
== this.INIT_COL_ROOK
[oppCol
][0] ? 0 : 1;
378 this.flags
[0][oppCol
][flagIdx
] = false;
382 // Does it turn off a 2-squares pawn flag?
383 const secondRank
= [1,6];
384 if (secondRank
.includes(move.start
.x
) && move.vanish
[0].p
== VariantRules
.PAWN
)
385 this.flags
[1][move.start
.x
==6 ? "w" : "b"][move.start
.y
] = false;
390 if (!this.isAttacked(this.kingPos
[color
], this.getOppCol(color
))
391 && !this.isAttacked(this.kingPos
[color
], 'c'))
396 return color
== "w" ? "0-1" : "1-0";
401 const [sizeX
,sizeY
] = VariantRules
.size
;
403 //Just count material for now, considering checkered neutral (...)
404 for (let i
=0; i
<sizeX
; i
++)
406 for (let j
=0; j
<sizeY
; j
++)
408 if (this.board
[i
][j
] != VariantRules
.EMPTY
)
410 const sqColor
= this.getColor(i
,j
);
411 const sign
= sqColor
== "w" ? 1 : (sqColor
=="b" ? -1 : 0);
412 evaluation
+= sign
* VariantRules
.VALUES
[this.getPiece(i
,j
)];
419 static GenRandInitFen()
421 return ChessRules
.GenRandInitFen() + "1111111111111111"; //add 16 pawns flags
427 // Add castling flags
428 for (let c
of ['w','b'])
430 for (let i
=0; i
<2; i
++)
431 fen
+= this.flags
[0][c
][i
] ? '1' : '0';
434 for (let c
of ['w','b'])
436 for (let i
=0; i
<8; i
++)
437 fen
+= this.flags
[1][c
][i
] ? '1' : '0';
444 if (move.appear
.length
== 2)
447 if (move.end
.y
< move.start
.y
)
453 // Translate final square
455 String
.fromCharCode(97 + move.end
.y
) + (VariantRules
.size
[0]-move.end
.x
);
457 let piece
= this.getPiece(move.start
.x
, move.start
.y
);
458 if (piece
== VariantRules
.PAWN
)
462 if (move.vanish
.length
> 1)
465 let startColumn
= String
.fromCharCode(97 + move.start
.y
);
466 notation
= startColumn
+ "x" + finalSquare
+ "=" + move.appear
[0].p
.toUpperCase();
469 notation
= finalSquare
;
470 if (move.appear
.length
> 0 && piece
!= move.appear
[0].p
) //promotion
471 notation
+= "=" + move.appear
[0].p
.toUpperCase();
478 return piece
.toUpperCase() + (move.vanish
.length
> 1 ? "x" : "") + finalSquare
479 + (move.vanish
.length
> 1 ? "=" + move.appear
[0].p
.toUpperCase() : "");