import { ChessRules, Move, PiPo } from "@/base_rules";
import { randInt } from "@/utils/alea";
+import { ArrayFun } from "@/utils/array";
export class PandemoniumRules extends ChessRules {
- static get PawnSpecs() {
- return Object.assign(
- {},
- ChessRules.PawnSpecs,
- {
- threeSquares: true,
- promotions: [V.GILDING]
- }
- );
+ loseOnRepetition() {
+ // If current side is under check: lost
+ return this.underCheck(this.turn);
}
static get GILDING() {
return counts.join("");
}
- setFlags(fenflags) {
- // white a-castle, h-castle, king pos, then same for black.
- this.castleFlags = { w: [-1, -1, -1], b: [-1, -1, -1] };
- for (let i = 0; i < 6; i++) {
- this.castleFlags[i < 3 ? "w" : "b"][i % 3] =
- V.ColumnToCoord(fenflags.charAt(i));
+ static GenRandInitFen(randomness) {
+ if (randomness == 0) {
+ return (
+ "rnbqkmcbnr/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/RNBQKMCBNR " +
+ "w 0 ajaj - 00000000000000"
+ );
}
- }
- static GenRandInitFen(randomness) {
- // No randomization here for now (but initial setup choice)
+ let pieces = { w: new Array(10), b: new Array(10) };
+ let flags = "";
+ for (let c of ["w", "b"]) {
+ if (c == 'b' && randomness == 1) {
+ pieces['b'] = pieces['w'];
+ flags += flags;
+ break;
+ }
+
+ let positions = ArrayFun.range(10);
+
+ // Get random squares for bishops (different colors)
+ let randIndex = 2 * randInt(5);
+ let bishop1Pos = positions[randIndex];
+ let randIndex_tmp = 2 * randInt(5) + 1;
+ let bishop2Pos = positions[randIndex_tmp];
+ positions.splice(Math.max(randIndex, randIndex_tmp), 1);
+ positions.splice(Math.min(randIndex, randIndex_tmp), 1);
+
+ randIndex = randInt(8);
+ let knight1Pos = positions[randIndex];
+ positions.splice(randIndex, 1);
+ randIndex = randInt(7);
+ let knight2Pos = positions[randIndex];
+ positions.splice(randIndex, 1);
+
+ randIndex = randInt(6);
+ let queenPos = positions[randIndex];
+ positions.splice(randIndex, 1);
+
+ // Random squares for cardinal + marshal
+ randIndex = randInt(5);
+ let cardinalPos = positions[randIndex];
+ positions.splice(randIndex, 1);
+ randIndex = randInt(4);
+ let marshalPos = positions[randIndex];
+ positions.splice(randIndex, 1);
+
+ let rook1Pos = positions[0];
+ let kingPos = positions[1];
+ let rook2Pos = positions[2];
+
+ pieces[c][rook1Pos] = "r";
+ pieces[c][knight1Pos] = "n";
+ pieces[c][bishop1Pos] = "b";
+ pieces[c][queenPos] = "q";
+ pieces[c][kingPos] = "k";
+ pieces[c][marshalPos] = "m";
+ pieces[c][cardinalPos] = "c";
+ pieces[c][bishop2Pos] = "b";
+ pieces[c][knight2Pos] = "n";
+ pieces[c][rook2Pos] = "r";
+ flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(rook2Pos);
+ }
return (
- "rnbqkmcbnr/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/RNBQKMCBNR " +
- "w 0 ajeaje - 00000000000000"
+ pieces["b"].join("") +
+ "/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/" +
+ pieces["w"].join("").toUpperCase() +
+ " w 0 " + flags + " - 00000000000000"
);
- // TODO later: randomization too --> 2 bishops, not next to each other.
- // then knights next to bishops. Then other pieces (...).
}
getEnpassantFen() {
];
}
const firstRank = (this.movesCount == 0 ? 9 : 0);
- // TODO: initDestFile currently hardcoded for deterministic setup
- const initDestFile = new Map([[1, 2], [8, 7]]);
- // Only option is knight --> bishop swap:
- if (
- x == firstRank &&
- !!initDestFile.get(y) &&
- this.getPiece(x, y) == V.KNIGHT
- ) {
- const destFile = initDestFile.get(y);
- return [
- new Move({
- appear: [
- new PiPo({
- x: x,
- y: destFile,
- c: c,
- p: V.KNIGHT
- }),
- new PiPo({
- x: x,
- y: y,
- c: c,
- p: V.BISHOP
- })
- ],
- vanish: [
- new PiPo({
- x: x,
- y: y,
- c: c,
- p: V.KNIGHT
- }),
- new PiPo({
- x: x,
- y: destFile,
- c: c,
- p: V.BISHOP
- })
- ],
- start: { x: x, y: y },
- end: { x: x, y: destFile }
- })
- ];
+ if (x != firstRank || this.getPiece(x, y) != V.KNIGHT) return [];
+ // Swap with who? search for matching bishop:
+ let knights = [],
+ bishops = [];
+ for (let i = 0; i < 10; i++) {
+ const elt = this.board[x][i][1];
+ if (elt == 'n') knights.push(i);
+ else if (elt == 'b') bishops.push(i);
}
- return [];
+ const destFile = (knights[0] == y ? bishops[0] : bishops[1]);
+ return [
+ new Move({
+ appear: [
+ new PiPo({
+ x: x,
+ y: destFile,
+ c: c,
+ p: V.KNIGHT
+ }),
+ new PiPo({
+ x: x,
+ y: y,
+ c: c,
+ p: V.BISHOP
+ })
+ ],
+ vanish: [
+ new PiPo({
+ x: x,
+ y: y,
+ c: c,
+ p: V.KNIGHT
+ }),
+ new PiPo({
+ x: x,
+ y: destFile,
+ c: c,
+ p: V.BISHOP
+ })
+ ],
+ start: { x: x, y: y },
+ end: { x: x, y: destFile }
+ })
+ ];
}
// Normal move (after initial setup)
- if (x >= V.size.x) return this.getReserveMoves(x, y);
+ if (x >= V.size.x) return this.getReserveMoves([x, y]);
const p = this.getPiece(x, y);
const sq = [x, y];
let moves = [];
// Maybe apply promotions:
if (Object.keys(V.PromoteMap).includes(p)) {
const promoted = V.PromoteMap[p];
- const lastRank = (c == 'w' ? 0 : 9);
+ const lastRanks = (c == 'w' ? [0, 1] : [9, 8]);
let promotions = [];
moves.forEach(m => {
- if (m.start.x == lastRank || m.end.x == lastRank) {
+ if (lastRanks.includes(m.start.x) || lastRanks.includes(m.end.x)) {
let pMove = JSON.parse(JSON.stringify(m));
pMove.appear[0].p = promoted;
promotions.push(pMove);
return moves;
}
+ addPawnMoves([x1, y1], [x2, y2], moves) {
+ const color = this.turn;
+ const lastRanks = (color == "w" ? [0, 1] : [9, 8]);
+ if (!lastRanks.includes(x2)) {
+ moves.push(this.getBasicMove([x1, y1], [x2, y2]));
+ return;
+ }
+ let finalPieces = [V.GILDING];
+ if (x2 == lastRanks[1]) finalPieces.push(V.PAWN);
+ for (let piece of finalPieces) {
+ const tr = (piece != V.PAWN ? { c: color, p: piece } : null);
+ moves.push(this.getBasicMove([x1, y1], [x2, y2], tr));
+ }
+ }
+
getPotentialPawnMoves([x, y]) {
const color = this.turn;
- const shiftX = V.PawnSpecs.directions[color];
+ const shiftX = (color == 'w' ? -1 : 1);
let moves = [];
if (this.board[x + shiftX][y] == V.EMPTY) {
this.addPawnMoves([x, y], [x + shiftX, y], moves);
- if ((color == 'w' && x >= V.size.x - 3) || (color == 'b' && x <= 3)) {
+ if ((color == 'w' && x >= V.size.x - 3) || (color == 'b' && x <= 2)) {
if (this.board[x + 2 * shiftX][y] == V.EMPTY) {
moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
if (
(
- (color == 'w' && x >= V.size.x - 2) ||
- (color == 'b' && x <= 2)
+ (color == 'w' && x == V.size.x - 2) ||
+ (color == 'b' && x == 1)
)
&&
this.board[x + 3 * shiftX][y] == V.EMPTY
if (
this.castleFlags[c][0] < V.size.y ||
this.castleFlags[c][1] < V.size.y
- ) {
- moves = moves.concat(this.getCastleMoves(sq));
- }
- return moves;
- }
-
- getCastleMoves([x, y]) {
- const c = this.getColor(x, y);
- if (
- ((c == 'w' && x == 9) || (c == 'b' && x == 0)) &&
- y == this.castleFlags[c][2]
) {
const finalSquares = [
[1, 2],
[7, 6]
];
- return super.getCastleMoves([x, y], finalSquares, false, [V.ROOK]);
+ moves = moves.concat(super.getCastleMoves(sq, finalSquares));
}
- return [];
+ return moves;
}
isAttacked(sq, color) {
const steps =
V.steps[V.KNIGHT].concat(V.steps[V.ROOK]).concat(V.steps[V.BISHOP]);
return (
- super.isAttackedBySlideNJump(sq, color, steps, V.SCEPTER, "oneStep")
+ super.isAttackedBySlideNJump(sq, color, V.SCEPTER, steps, "oneStep")
);
}
isAttackedByHorse(sq, color) {
return (
- super.isAttackedBySlideNJump(sq, color, V.steps[V.BISHOP], V.HORSE) ||
+ super.isAttackedBySlideNJump(sq, color, V.HORSE, V.steps[V.BISHOP]) ||
super.isAttackedBySlideNJump(
- sq, color, V.steps[V.ROOK], V.HORSE, "oneStep")
+ sq, color, V.HORSE, V.steps[V.ROOK], "oneStep")
);
}
isAttackedByDragon(sq, color) {
return (
- super.isAttackedBySlideNJump(sq, color, V.steps[V.ROOK], V.DRAGON) ||
+ super.isAttackedBySlideNJump(sq, color, V.DRAGON, V.steps[V.ROOK]) ||
super.isAttackedBySlideNJump(
- sq, color, V.steps[V.BISHOP], V.DRAGON, "oneStep")
+ sq, color, V.DRAGON, V.steps[V.BISHOP], "oneStep")
);
}
s: 'n',
h: 'b',
w: 'c',
- a: 'm'
+ a: 'm',
+ g: 'p'
};
}
this.postPlay(move);
}
- updateCastleFlags(move, piece) {
- if (piece == V.KING && move.appear.length == 2) {
- // Castling (only move which disable flags)
- this.castleFlags[move.appear[0].c][0] = 10;
- this.castleFlags[move.appear[0].c][1] = 10;
- }
- }
-
postPlay(move) {
if (move.vanish.length == 0 && move.appear.length == 0) return;
super.postPlay(move);
this.reserve[color][V.MayDecode(move.vanish[1].p)]--;
}
- getCurrentScore() {
- const c = this.turn,
- oppCol = V.GetOppCol(this.turn);
- let facingKings = false;
- if (
- this.kingPos[c][0] == this.kingPos[oppCol][0] ||
- this.kingPos[c][1] == this.kingPos[oppCol][1]
- ) {
- facingKings = true;
- let step = [
- this.kingPos[oppCol][0] - this.kingPos[c][0],
- this.kingPos[oppCol][1] - this.kingPos[c][1]
- ];
- if (step[0] != 0) step[0] /= Math.abs(step[0]);
- else step[1] /= Math.abs(step[1]);
- let [x, y] =
- [ this.kingPos[c][0] + step[0], this.kingPos[c][1] + step[1] ];
- while (x != this.kingPos[oppCol][0] || y != this.kingPos[oppCol][1]) {
- if (this.board[x][y] != V.EMPTY) {
- facingKings = false;
- break;
- }
- x += step[0];
- y += step[1];
- }
- }
- if (facingKings) return (c == "w" ? "1-0" : "0-1");
- if (!this.atLeastOneMove()) return (c == "w" ? "0-1" : "1-0");
- return "*";
- }
-
static get VALUES() {
return Object.assign(
{},