-import { ChessRules } from "@/base_rules";
+import { ChessRules, Move, PiPo } from "@/base_rules";
import { ArrayFun } from "@/utils/array";
import { sample, randInt } from "@/utils/alea";
-export const VariantRules = class WildebeestRules extends ChessRules {
+export class WildebeestRules extends ChessRules {
+
static get size() {
return { x: 10, y: 11 };
}
static get steps() {
return Object.assign(
- ChessRules.steps, //add camel moves:
+ {},
+ ChessRules.steps,
+ // Add camel moves:
{
c: [
[-3, -1],
}
static IsGoodEnpassant(enpassant) {
- if (enpassant != "-") {
- const squares = enpassant.split(",");
- if (squares.length > 2) return false;
- for (let sq of squares) {
- const ep = V.SquareToCoords(sq);
- if (isNaN(ep.x) || !V.OnBoard(ep)) return false;
- }
- }
+ if (enpassant != "-") return !!enpassant.match(/^([a-j][0-9]{1,2},?)+$/);
return true;
}
}
];
if (sx + 2 * step != ex) {
- //3-squares move
+ // 3-squares move
res.push({
x: sx + 2 * step,
y: sy
const [sizeX, sizeY] = [V.size.x, V.size.y];
const shiftX = color == "w" ? -1 : 1;
const startRanks = color == "w" ? [sizeX - 2, sizeX - 3] : [1, 2];
- const lastRank = color == "w" ? 0 : sizeX - 1;
- const finalPieces = x + shiftX == lastRank
- ? [V.WILDEBEEST, V.QUEEN]
- : [V.PAWN];
+ const lastRanks = color == "w" ? [0, 1] : [sizeX - 1, sizeX -2];
+ let finalPieces = [V.PAWN];
+ if (x + shiftX == lastRanks[1])
+ Array.prototype.push.apply(finalPieces, [V.WILDEBEEST, V.QUEEN]);
+ else if (x + shiftX == lastRanks[0])
+ finalPieces = [V.WILDEBEEST, V.QUEEN];
if (this.board[x + shiftX][y] == V.EMPTY) {
// One square forward
// En passant
const Lep = this.epSquares.length;
const epSquare = this.epSquares[Lep - 1];
- if (epSquare) {
+ if (!!epSquare) {
for (let epsq of epSquare) {
// TODO: some redundant checks
if (epsq.x == x + shiftX && Math.abs(epsq.y - y) == 1) {
- var enpassantMove = this.getBasicMove([x, y], [epsq.x, epsq.y]);
+ let enpassantMove = this.getBasicMove([x, y], [epsq.x, epsq.y]);
// WARNING: the captured pawn may be diagonally behind us,
// if it's a 3-squares jump and we take on 1st passing square
const px = this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX;
return moves;
}
- // TODO: wildebeest castle
-
getPotentialCamelMoves(sq) {
- return this.getSlideNJumpMoves(sq, V.steps[V.CAMEL], "oneStep");
+ return this.getSlideNJumpMoves(sq, V.steps[V.CAMEL], 1);
}
getPotentialWildebeestMoves(sq) {
return this.getSlideNJumpMoves(
- sq,
- V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]),
- "oneStep"
- );
+ sq, V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]), 1);
+ }
+
+ getPPpath(m) {
+ if (
+ m.appear.length == 2 && m.vanish.length == 2 &&
+ Math.abs(m.end.y - m.start.y) == 1 &&
+ this.board[m.end.x][m.end.y] == V.EMPTY
+ ) {
+ // Castle, king moved by one square only, not directly onto rook
+ return "Wildebeest/castle";
+ }
+ return super.getPPpath(m);
+ }
+
+ // Special Wildebeest castling rules:
+ getCastleMoves([x, y]) {
+ const c = this.getColor(x, y);
+ const oppCol = V.GetOppCol(c);
+ let moves = [];
+ let i = 0;
+ const castlingKing = this.board[x][y].charAt(1);
+ castlingCheck: for (
+ let castleSide = 0;
+ castleSide < 2;
+ castleSide++ //"large", then "small"
+ ) {
+ if (this.castleFlags[c][castleSide] >= V.size.y) continue;
+ // Rook and king are on initial position
+ const rookPos = this.castleFlags[c][castleSide];
+ const range = (castleSide == 0 ? [rookPos, y] : [y, rookPos]);
+
+ // King and rook must be connected:
+ for (let i = range[0] + 1; i <= range[1] - 1; i++) {
+ if (this.board[x][i] != V.EMPTY) continue castlingCheck;
+ }
+ const step = 2 * castleSide - 1;
+ // No attacks on the path of the king ?
+ for (let i = range[0]; i <= range[1]; i++) {
+ if (i != rookPos && this.isAttacked([x, i], oppCol))
+ continue castlingCheck;
+ if (
+ i != y &&
+ // Do not end in the corner, except if starting square is too near
+ (i > 0 || y == 1) &&
+ (i < V.size.y - 1 || y == V.size.y - 2)
+ ) {
+ // Found a possible castle move:
+ moves.push(
+ new Move({
+ appear: [
+ new PiPo({
+ x: x,
+ y: i,
+ p: V.KING,
+ c: c
+ }),
+ new PiPo({
+ x: x,
+ y: i - step,
+ p: V.ROOK,
+ c: c
+ })
+ ],
+ vanish: [
+ new PiPo({ x: x, y: y, p: V.KING, c: c }),
+ new PiPo({ x: x, y: rookPos, p: V.ROOK, c: c })
+ ]
+ })
+ );
+ }
+ }
+ }
+
+ return moves;
}
isAttacked(sq, color) {
isAttackedByCamel(sq, color) {
return this.isAttackedBySlideNJump(
- sq,
- color,
- V.CAMEL,
- V.steps[V.CAMEL],
- "oneStep"
- );
+ sq, color, V.CAMEL, V.steps[V.CAMEL], 1);
}
isAttackedByWildebeest(sq, color) {
return this.isAttackedBySlideNJump(
- sq,
- color,
- V.WILDEBEEST,
- V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]),
- "oneStep"
- );
+ sq, color, V.WILDEBEEST, V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]), 1);
}
getCurrentScore() {
- if (this.atLeastOneMove())
- // game not over
- return "*";
-
+ if (this.atLeastOneMove()) return "*";
// No valid move: game is lost (stalemate is a win)
return this.turn == "w" ? "0-1" : "1-0";
}
return 2;
}
- static GenRandInitFen(randomness) {
- if (!randomness) randomness = 2;
- if (randomness == 0)
- return "rnccwkqbbnr/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/RNBBQKWCCNR w 0 akak -";
+ static GenRandInitFen(options) {
+ if (options.randomness == 0) {
+ return (
+ "rnccwkqbbnr/ppppppppppp/92/92/92/92/92/92/PPPPPPPPPPP/RNBBQKWCCNR " +
+ "w 0 akak -"
+ );
+ }
- let pieces = { w: new Array(10), b: new Array(10) };
+ let pieces = { w: new Array(11), b: new Array(11) };
let flags = "";
for (let c of ["w", "b"]) {
- if (c == 'b' && randomness == 1) {
+ if (c == 'b' && options.randomness == 1) {
pieces['b'] = pieces['w'];
flags += flags;
break;
}
return (
pieces["b"].join("") +
- "/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/" +
+ "/ppppppppppp/92/92/92/92/92/92/PPPPPPPPPPP/" +
pieces["w"].join("").toUpperCase() +
" w 0 " + flags + " -"
);
}
+
};