}
getPPpath(m, orientation) {
+ // If castle, show choices on m.appear[1]:
+ const index = (m.appear.length == 2 ? 1 : 0);
return (
this.getPpath(
- m.appear[0].c + m.appear[0].p,
+ m.appear[index].c + m.appear[index].p,
null,
null,
orientation
static GenRandInitFen(randomness) {
if (randomness == 0)
// Deterministic:
- return "efbqkbnm/pppppppp/8/8/8/8/PPPPPPPP/EDBQKBNM w 0 ahah -";
+ return "enbqkbnm/pppppppp/8/8/8/8/PPPPPPPP/ENBQKBNM w 0 ahah -";
const baseFen = ChessRules.GenRandInitFen(randomness);
- // Replace black rooks by lancers oriented south,
- // and white rooks by lancers oriented north:
- return baseFen.replace(/r/g, 'g').replace(/R/g, 'C');
- }
-
- // Because of the lancers, getPiece() could be wrong:
- // use board[x][y][1] instead (always valid).
- // TODO: base implementation now uses this too (no?)
- getBasicMove([sx, sy], [ex, ey], tr) {
- const initColor = this.getColor(sx, sy);
- const initPiece = this.board[sx][sy].charAt(1);
- let mv = new Move({
- appear: [
- new PiPo({
- x: ex,
- y: ey,
- c: tr ? tr.c : initColor,
- p: tr ? tr.p : initPiece
- })
- ],
- vanish: [
- new PiPo({
- x: sx,
- y: sy,
- c: initColor,
- p: initPiece
- })
- ]
- });
-
- // The opponent piece disappears if we take it
- if (this.board[ex][ey] != V.EMPTY) {
- mv.vanish.push(
- new PiPo({
- x: ex,
- y: ey,
- c: this.getColor(ex, ey),
- p: this.board[ex][ey].charAt(1)
- })
- );
- }
-
- return mv;
+ // Replace rooks by lancers with expected orientation:
+ const firstBlackRook = baseFen.indexOf('r'),
+ lastBlackRook = baseFen.lastIndexOf('r'),
+ firstWhiteRook = baseFen.indexOf('R'),
+ lastWhiteRook = baseFen.lastIndexOf('R');
+ return (
+ baseFen.substring(0, firstBlackRook) +
+ (firstBlackRook <= 3 ? 'e' : 'm') +
+ baseFen.substring(firstBlackRook + 1, lastBlackRook) +
+ (lastBlackRook >= 5 ? 'm' : 'e') +
+ // Subtract 35 = total number of characters before last FEN row:
+ // 8x3 (full rows) + 4 (empty rows) + 7 (separators)
+ baseFen.substring(lastBlackRook + 1, firstWhiteRook) +
+ (firstWhiteRook - 35 <= 3 ? 'E' : 'M') +
+ baseFen.substring(firstWhiteRook + 1, lastWhiteRook) +
+ (lastWhiteRook - 35 >= 5 ? 'M' : 'E') +
+ baseFen.substring(lastWhiteRook + 1)
+ );
}
getPotentialMovesFrom([x, y]) {
return super.getPotentialMovesFrom([x, y]);
}
+ getPotentialPawnMoves([x, y]) {
+ const color = this.getColor(x, y);
+ let shiftX = (color == "w" ? -1 : 1);
+ const lastRank = (color == "w" ? 0 : 7);
+ let finalPieces = [V.PAWN];
+ if (x + shiftX == lastRank) {
+ // Only allow direction facing inside board:
+ const allowedLancerDirs =
+ lastRank == 0
+ ? ['e', 'f', 'g', 'h', 'm']
+ : ['c', 'd', 'e', 'm', 'o'];
+ finalPieces = allowedLancerDirs.concat([V.KNIGHT, V.BISHOP, V.QUEEN]);
+ }
+ return super.getPotentialPawnMoves([x, y], finalPieces);
+ }
+
// Obtain all lancer moves in "step" direction
getPotentialLancerMoves_aux([x, y], step, tr) {
let moves = [];
return moves;
}
+ getCastleMoves([x, y]) {
+ const c = this.getColor(x, y);
+
+ // Castling ?
+ const oppCol = V.GetOppCol(c);
+ let moves = [];
+ let i = 0;
+ // King, then lancer:
+ const finalSquares = [ [2, 3], [V.size.y - 2, V.size.y - 3] ];
+ castlingCheck: for (
+ let castleSide = 0;
+ castleSide < 2;
+ castleSide++ //large, then small
+ ) {
+ if (this.castleFlags[c][castleSide] >= V.size.y) continue;
+ // If this code is reached, lancer and king are on initial position
+
+ const lancerPos = this.castleFlags[c][castleSide];
+ const castlingPiece = this.board[x][lancerPos].charAt(1);
+
+ // Nothing on the path of the king ? (and no checks)
+ const finDist = finalSquares[castleSide][0] - y;
+ let step = finDist / Math.max(1, Math.abs(finDist));
+ i = y;
+ do {
+ if (
+ (this.isAttacked([x, i], oppCol)) ||
+ (
+ this.board[x][i] != V.EMPTY &&
+ // NOTE: next check is enough, because of chessboard constraints
+ (this.getColor(x, i) != c || ![y, lancerPos].includes(i))
+ )
+ ) {
+ continue castlingCheck;
+ }
+ i += step;
+ } while (i != finalSquares[castleSide][0]);
+
+ // Nothing on final squares, except maybe king and castling lancer?
+ for (i = 0; i < 2; i++) {
+ if (
+ finalSquares[castleSide][i] != lancerPos &&
+ this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
+ (
+ finalSquares[castleSide][i] != y ||
+ this.getColor(x, finalSquares[castleSide][i]) != c
+ )
+ ) {
+ continue castlingCheck;
+ }
+ }
+
+ // If this code is reached, castle is valid
+ let allowedLancerDirs = [castlingPiece];
+ if (finalSquares[castleSide][1] != lancerPos) {
+ // It moved: allow reorientation
+ allowedLancerDirs =
+ x == 0
+ ? ['e', 'f', 'g', 'h', 'm']
+ : ['c', 'd', 'e', 'm', 'o'];
+ }
+ allowedLancerDirs.forEach(dir => {
+ moves.push(
+ new Move({
+ appear: [
+ new PiPo({
+ x: x,
+ y: finalSquares[castleSide][0],
+ p: V.KING,
+ c: c
+ }),
+ new PiPo({
+ x: x,
+ y: finalSquares[castleSide][1],
+ p: dir,
+ c: c
+ })
+ ],
+ vanish: [
+ new PiPo({ x: x, y: y, p: V.KING, c: c }),
+ new PiPo({ x: x, y: lancerPos, p: castlingPiece, c: c })
+ ],
+ end:
+ Math.abs(y - lancerPos) <= 2
+ ? { x: x, y: lancerPos }
+ : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) }
+ })
+ );
+ });
+ }
+
+ return moves;
+ }
+
isAttacked(sq, color) {
return (
super.isAttacked(sq, color) ||
this.getColor(coord.x, coord.y) == color
)
) {
- if (
- this.getPiece(coord.x, coord.y) == V.LANCER &&
- !this.isImmobilized([coord.x, coord.y])
- ) {
+ if (this.getPiece(coord.x, coord.y) == V.LANCER)
lancerPos.push({x: coord.x, y: coord.y});
- }
coord.x += step[0];
coord.y += step[1];
}
filterValid(moves) {
// At move 1, forbid captures (in case of...):
- if (this.movesCount >= 2) return moves;
+ if (this.movesCount >= 2) return super.filterValid(moves);
return moves.filter(m => m.vanish.length == 1);
}
+ static get SEARCH_DEPTH() {
+ return 2;
+ }
+
getNotation(move) {
let notation = super.getNotation(move);
if (Object.keys(V.LANCER_DIRNAMES).includes(move.vanish[0].p))
// Lancer: add direction info
notation += "=" + V.LANCER_DIRNAMES[move.appear[0].p];
+ else if (move.appear.length == 2 && move.vanish[1].p != move.appear[1].p)
+ // Same after castle:
+ notation += "+L:" + V.LANCER_DIRNAMES[move.appear[1].p];
else if (
move.vanish[0].p == V.PAWN &&
Object.keys(V.LANCER_DIRNAMES).includes(move.appear[0].p)