import { randInt } from "@/utils/alea";
export class ChakartRules extends ChessRules {
+
static get PawnSpecs() {
return SuicideRules.PawnSpecs;
}
return true;
}
- hoverHighlight(x, y) {
- if (this.subTurn == 1) return false;
- const L = this.firstMove.length;
- const fm = this.firstMove[L-1];
- if (fm.end.effect != 0) return false;
- const deltaX = Math.abs(fm.appear[0].x - x);
- const deltaY = Math.abs(fm.appear[0].y - y);
- return (
- (this.board[x][y] == V.EMPTY || this.getColor(x, y) == 'a') &&
- (
- (fm.vanish[0].p == V.ROOK && deltaX == 1 && deltaY == 1) ||
- (fm.vanish[0].p == V.BISHOP && deltaX + deltaY == 1)
- )
- );
- }
-
static get IMMOBILIZE_CODE() {
return {
'p': 's',
getPPpath(m) {
if (!!m.promoteInto) return m.promoteInto;
+ if (m.appear.length == 0 && m.vanish.length == 1)
+ // King 'remote shell capture', on an adjacent square:
+ return this.getPpath(m.vanish[0].c + m.vanish[0].p);
let piece = m.appear[0].p;
if (Object.keys(V.IMMOBILIZE_DECODE).includes(piece))
+ // Promotion by capture into immobilized piece: do not reveal!
piece = V.IMMOBILIZE_DECODE[piece];
return this.getPpath(m.appear[0].c + piece);
}
if (['K', 'k', 'L', 'l'].includes(row[i])) kings[row[i]]++;
if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
else {
- const num = parseInt(row[i]);
+ const num = parseInt(row[i], 10);
if (isNaN(num)) return false;
sumElts += num;
}
setOtherVariables(fen) {
super.setOtherVariables(fen);
- const fenParsed = V.ParseFen(fen);
// Initialize captured pieces' counts from FEN
+ const captured =
+ V.ParseFen(fen).captured.split("").map(x => parseInt(x, 10));
this.captured = {
w: {
- [V.PAWN]: parseInt(fenParsed.captured[0]),
- [V.ROOK]: parseInt(fenParsed.captured[1]),
- [V.KNIGHT]: parseInt(fenParsed.captured[2]),
- [V.BISHOP]: parseInt(fenParsed.captured[3]),
- [V.QUEEN]: parseInt(fenParsed.captured[4]),
- [V.KING]: parseInt(fenParsed.captured[5])
+ [V.PAWN]: captured[0],
+ [V.ROOK]: captured[1],
+ [V.KNIGHT]: captured[2],
+ [V.BISHOP]: captured[3],
+ [V.QUEEN]: captured[4],
+ [V.KING]: captured[5]
},
b: {
- [V.PAWN]: parseInt(fenParsed.captured[6]),
- [V.ROOK]: parseInt(fenParsed.captured[7]),
- [V.KNIGHT]: parseInt(fenParsed.captured[8]),
- [V.BISHOP]: parseInt(fenParsed.captured[9]),
- [V.QUEEN]: parseInt(fenParsed.captured[10]),
- [V.KING]: parseInt(fenParsed.captured[11])
+ [V.PAWN]: captured[6],
+ [V.ROOK]: captured[7],
+ [V.KNIGHT]: captured[8],
+ [V.BISHOP]: captured[9],
+ [V.QUEEN]: captured[10],
+ [V.KING]: captured[11]
}
};
- this.firstMove = [];
+ this.effects = [];
this.subTurn = 1;
}
}
else {
// Subturn == 2
- const L = this.firstMove.length;
- const fm = this.firstMove[L-1];
- switch (fm.end.effect) {
- // case 0: a click is required (banana or bomb)
+ const L = this.effects.length;
+ switch (this.effects[L-1]) {
case "kingboo":
- // Exchange position with any piece,
+ // Exchange position with any visible piece,
// except pawns if arriving on last rank.
const lastRank = { 'w': 0, 'b': 7 };
const color = this.turn;
for (let i=0; i<8; i++) {
for (let j=0; j<8; j++) {
const colIJ = this.getColor(i, j);
+ const pieceIJ = this.getPiece(i, j);
if (
(i != x || j != y) &&
this.board[i][j] != V.EMPTY &&
+ pieceIJ != V.INVISIBLE_QUEEN &&
colIJ != 'a'
) {
- const pieceIJ = this.getPiece(i, j);
if (
(pieceIJ != V.PAWN || x != lastRank[colIJ]) &&
(allowLastRank || i != lastRank[color])
if (x >= V.size.x) moves = this.getReserveMoves([x, y]);
break;
case "daisy":
- // Play again with the same piece
- if (fm.appear[0].x == x && fm.appear[0].y == y)
- moves = super.getPotentialMovesFrom([x, y]);
+ // Play again with any piece
+ moves = super.getPotentialMovesFrom([x, y]);
break;
}
}
return moves;
}
- // Helper for getBasicMove()
+ // Helper for getBasicMove(): banana/bomb effect
getRandomSquare([x, y], steps) {
- const validSteps = steps.filter(s => {
- const [i, j] = [x + s[0], y + s[1]];
- return (
- V.OnBoard(i, j) &&
- (this.board[i][j] == V.EMPTY || this.getColor(i, j) == 'a')
- );
- });
- if (validSteps.length == 0)
- // Can happen after mushroom jump
- return [x, y];
+ const validSteps = steps.filter(s => V.OnBoard(x + s[0], y + s[1]));
const step = validSteps[randInt(validSteps.length)];
return [x + step[0], y + step[1]];
}
- canMove([x, y], piece) {
- const color = this.getColor(x, y);
- const oppCol = V.GetOppCol(color);
- piece = piece || this.getPiece(x, y);
- if (piece == V.PAWN) {
- const forward = (color == 'w' ? -1 : 1);
- return (
- V.OnBoard(x + forward, y) &&
- (
- this.board[x + forward][y] != oppCol ||
- (
- V.OnBoard(x + forward, y + 1) &&
- this.board[x + forward][y + 1] != V.EMPTY &&
- this.getColor[x + forward, y + 1] == oppCol
- ) ||
- (
- V.OnBoard(x + forward, y - 1) &&
- this.board[x + forward][y - 1] != V.EMPTY &&
- this.getColor[x + forward, y - 1] == oppCol
- )
- )
- );
- }
- // Checking one step is enough:
- const steps =
- [V.KING, V.QUEEN].includes(piece)
- ? V.steps[V.ROOK].concat(V.steps[V.BISHOP])
- : V.steps[piece];
- for (let step of steps) {
- const [i, j] = [x + step[0], y + step[1]];
- if (
- V.OnBoard(i, j) &&
- (this.board[i][j] == V.EMPTY || this.getColor(i, j) != color)
- ) {
- return true;
- }
- }
- return false;
- }
-
// Apply mushroom, bomb or banana effect (hidden to the player).
// Determine egg effect, too, and apply its first part if possible.
getBasicMove_aux(psq1, sq2, tr, initMove) {
// The move starts normally, on board:
let move = super.getBasicMove([x1, y1], [x2, y2], tr);
if (!!tr) move.promoteInto = tr.c + tr.p; //in case of (chomped...)
- const L = this.firstMove.length;
+ const L = this.effects.length;
if (
[V.PAWN, V.KNIGHT].includes(piece1) &&
!!initMove &&
- (this.subTurn == 1 || this.firstMove[L-1].end.effect == "daisy")
+ (this.subTurn == 1 || this.effects[L-1] == "daisy")
) {
switch (piece1) {
case V.PAWN: {
const oppLastRank = (color == 'w' ? 7 : 0);
for (let i=0; i<8; i++) {
for (let j=0; j<8; j++) {
+ const piece = this.getPiece(i, j);
if (
(i != move.vanish[0].x || j != move.vanish[0].y) &&
this.board[i][j] != V.EMPTY &&
+ piece != V.INVISIBLE_QUEEN &&
this.getColor(i, j) == color
) {
- const piece = this.getPiece(i, j);
if (piece != V.KING && (piece != V.PAWN || i != oppLastRank))
pieces.push({ x: i, y: j, p: piece });
}
// No egg effects at subTurn 2
return;
// 1) Determine the effect (some may be impossible)
- let effects = ["kingboo", "koopa", "chomp", "bowser"];
+ let effects = ["kingboo", "koopa", "chomp", "bowser", "daisy"];
if (Object.values(this.captured[color1]).some(c => c >= 1))
effects.push("toadette");
const lastRank = { 'w': 0, 'b': 7 };
- let canPlayAgain = undefined;
- if (
- move.appear[0].p == V.PAWN &&
- move.appear[0].x == lastRank[color1]
- ) {
- // Always possible: promote into a queen, rook or king
- canPlayAgain = true;
- }
- else {
- move.end.effect = "daisy";
- V.PlayOnBoard(this.board, move);
- const square = [move.appear[0].x, move.appear[0].y];
- canPlayAgain = this.canMove(square, piece1);
- V.UndoOnBoard(this.board, move);
- delete move.end["effect"];
- }
- if (canPlayAgain) effects.push("daisy");
if (
this.board.some((b,i) =>
b.some(cell => {
];
if (
V.OnBoard(i, j) &&
- (this.board[i][j] == V.EMPTY || this.getColor(i, j) == 'a')
+ (
+ this.board[i][j] == V.EMPTY ||
+ this.getPiece(i, j) == V.INVISIBLE_QUEEN ||
+ this.getColor(i, j) == 'a'
+ )
) {
move.appear[0].x = i;
move.appear[0].y = j;
if (this.board[i][j] != V.EMPTY) {
const object = this.getPiece(i, j);
+ const color = this.getColor(i, j);
move.vanish.push(
new PiPo({
x: i,
y: j,
- c: 'a',
+ c: color,
p: object
})
);
if (
V.OnBoard(next[0], next[1]) &&
this.board[next[0]][next[1]] != V.EMPTY &&
+ this.getPiece(next[0], next[1]) != V.INVISIBLE_QUEEN &&
this.getColor(next[0], next[1]) != 'a'
) {
const afterNext = [next[0] + step[0], next[1] + step[1]];
if (V.OnBoard(afterNext[0], afterNext[1])) {
- const afterColor = this.getColor(afterNext[0], afterNext[1])
+ const afterColor = this.getColor(afterNext[0], afterNext[1]);
if (
this.board[afterNext[0]][afterNext[1]] == V.EMPTY ||
afterColor != color1
const [i, j] = [finalSquare[0] + s[0], finalSquare[1] + s[1]];
return (
V.OnBoard(i, j) &&
+ // NOTE: do not place a bomb or banana on the invisible queen!
(this.board[i][j] == V.EMPTY || this.getColor(i, j) == 'a')
);
});
if (validSteps.length >= 1) {
+ const randIdx = randInt(validSteps.length);
const [x, y] = [
- finalSquare[0] + validSteps[0][0],
- finalSquare[1] + validSteps[0][1]
+ finalSquare[0] + validSteps[randIdx][0],
+ finalSquare[1] + validSteps[randIdx][1]
];
move.appear.push(
new PiPo({
let moves = [];
if (
this.board[x + shiftX][y] == V.EMPTY ||
- this.getColor(x + shiftX, y) == 'a'
+ this.getColor(x + shiftX, y) == 'a' ||
+ this.getPiece(x + shiftX, y) == V.INVISIBLE_QUEEN
) {
this.addPawnMoves([x, y], [x + shiftX, y], moves);
if (
[firstRank, firstRank + shiftX].includes(x) &&
(
this.board[x + 2 * shiftX][y] == V.EMPTY ||
- this.getColor(x + 2 * shiftX, y) == 'a'
+ this.getColor(x + 2 * shiftX, y) == 'a' ||
+ this.getPiece(x + 2 * shiftX, y) == V.INVISIBLE_QUEEN
)
) {
moves.push(this.getBasicMove({ x: x, y: y }, [x + 2 * shiftX, y]));
y + shiftY >= 0 &&
y + shiftY < sizeY &&
this.board[x + shiftX][y + shiftY] != V.EMPTY &&
+ // Pawns cannot capture invisible queen this way!
+ this.getPiece(x + shiftX, y + shiftY) != V.INVISIBLE_QUEEN &&
['a', oppCol].includes(this.getColor(x + shiftX, y + shiftY))
) {
this.addPawnMoves([x, y], [x + shiftX, y + shiftY], moves);
// If flag allows it, add 'remote shell captures'
if (this.powerFlags[this.turn][V.KING]) {
V.steps[V.ROOK].concat(V.steps[V.BISHOP]).forEach(step => {
- const [nextX, nextY] = [x + step[0], y + step[1]];
- if (
- V.OnBoard(nextX, nextY) &&
+ let [i, j] = [x + step[0], y + step[1]];
+ while (
+ V.OnBoard(i, j) &&
(
- this.board[nextX][nextY] == V.EMPTY ||
+ this.board[i][j] == V.EMPTY ||
+ this.getPiece(i, j) == V.INVISIBLE_QUEEN ||
(
- this.getColor(nextX, nextY) == 'a' &&
- [V.EGG, V.MUSHROOM].includes(this.getPiece(nextX, nextY))
+ this.getColor(i, j) == 'a' &&
+ [V.EGG, V.MUSHROOM].includes(this.getPiece(i, j))
)
)
) {
- let [i, j] = [x + 2 * step[0], y + 2 * step[1]];
- while (
- V.OnBoard(i, j) &&
- (
- this.board[i][j] == V.EMPTY ||
- (
- this.getColor(i, j) == 'a' &&
- [V.EGG, V.MUSHROOM].includes(this.getPiece(i, j))
- )
- )
- ) {
- i += step[0];
- j += step[1];
- }
- if (V.OnBoard(i, j)) {
- const colIJ = this.getColor(i, j);
- if (colIJ != color) {
- // May just destroy a bomb or banana:
- moves.push(
- new Move({
- start: { x: x, y: y},
- end: { x: i, y: j },
- appear: [],
- vanish: [
- new PiPo({
- x: i, y: j, c: colIJ, p: this.getPiece(i, j)
- })
- ]
- })
- );
- }
+ i += step[0];
+ j += step[1];
+ }
+ if (V.OnBoard(i, j)) {
+ const colIJ = this.getColor(i, j);
+ if (colIJ != color) {
+ // May just destroy a bomb or banana:
+ moves.push(
+ new Move({
+ start: { x: x, y: y},
+ end: { x: i, y: j },
+ appear: [],
+ vanish: [
+ new PiPo({
+ x: i, y: j, c: colIJ, p: this.getPiece(i, j)
+ })
+ ]
+ })
+ );
}
}
});
getAllPotentialMoves() {
if (this.subTurn == 1) return super.getAllPotentialMoves();
let moves = [];
- const L = this.firstMove.length;
- const fm = this.firstMove[L-1];
- switch (fm.end.effect) {
- case 0:
- moves.push({
- start: { x: -1, y: -1 },
- end: { x: -1, y: -1 },
- appear: [],
- vanish: []
- });
- for (
- let step of
- (fm.vanish[0].p == V.ROOK ? V.steps[V.BISHOP] : V.steps[V.ROOK])
- ) {
- const [i, j] = [fm.appear[0].x + step[0], fm.appear[0].y + step[1]];
- if (
- V.OnBoard(i, j) &&
- (this.board[i][j] == V.EMPTY || this.getColor(i, j) == 'a')
- ) {
- let m = new Move({
- start: { x: -1, y: -1 },
- end: { x: i, y: j },
- appear: [
- new PiPo({
- x: i,
- y: j,
- c: 'a',
- p: (fm.vanish[0].p == V.ROOK ? V.BANANA : V.BOMB)
- })
- ],
- vanish: []
- });
- if (this.board[i][j] != V.EMPTY) {
- m.vanish.push(
- new PiPo({ x: i, y: j, c: 'a', p: this.getPiece(i, j) }));
- }
- moves.push(m);
- }
- }
- break;
+ const color = this.turn;
+ const L = this.effects.length;
+ switch (this.effects[L-1]) {
case "kingboo": {
- const [x, y] = [fm.appear[0].x, fm.appear[0].y];
+ let allPieces = [];
for (let i=0; i<8; i++) {
for (let j=0; j<8; j++) {
const colIJ = this.getColor(i, j);
+ const pieceIJ = this.getPiece(i, j);
if (
- i != x &&
- j != y &&
+ i != x && j != y &&
this.board[i][j] != V.EMPTY &&
- colIJ != 'a'
+ colIJ != 'a' &&
+ pieceIJ != V.INVISIBLE_QUEEN
) {
- const movedUnit = new PiPo({
- x: x,
- y: y,
- c: colIJ,
- p: this.getPiece(i, j)
+ allPieces.push({ x: i, y: j, c: colIJ, p: pieceIJ });
+ }
+ }
+ }
+ for (let x=0; x<8; x++) {
+ for (let y=0; y<8; y++) {
+ if (this.getColor(i, j) == color) {
+ // Add exchange with something
+ allPieces.forEach(pp => {
+ if (pp.x != i || pp.y != j) {
+ const movedUnit = new PiPo({
+ x: x,
+ y: y,
+ c: pp.c,
+ p: pp.p
+ });
+ let mMove = this.getBasicMove({ x: x, y: y }, [pp.x, pp.y]);
+ mMove.appear.push(movedUnit);
+ moves.push(mMove);
+ }
});
- let mMove = this.getBasicMove({ x: x, y: y }, [i, j]);
- mMove.appear.push(movedUnit);
- moves.push(mMove);
}
}
}
break;
}
case "daisy":
- moves = super.getPotentialMovesFrom([fm.appear[0].x, fm.appear[0].y]);
+ moves = super.getAllPotentialMoves();
break;
}
return moves;
}
- doClick(square) {
- const L = this.firstMove.length;
- const fm = (L > 0 ? this.firstMove[L-1] : null);
- if (
- isNaN(square[0]) ||
- this.subTurn == 1 ||
- !([0, "daisy"].includes(fm.end.effect))
- ) {
- return null;
- }
- const [x, y] = [square[0], square[1]];
- const deltaX = Math.abs(fm.appear[0].x - x);
- const deltaY = Math.abs(fm.appear[0].y - y);
- if (
- fm.end.effect == 0 &&
- (this.board[x][y] == V.EMPTY || this.getColor(x, y) == 'a') &&
- (
- (fm.vanish[0].p == V.ROOK && deltaX == 1 && deltaY == 1) ||
- (fm.vanish[0].p == V.BISHOP && deltaX + deltaY == 1)
- )
- ) {
- let m = new Move({
- start: { x: -1, y: -1 },
- end: { x: x, y: y },
- appear: [
- new PiPo({
- x: x,
- y: y,
- c: 'a',
- p: (fm.vanish[0].p == V.ROOK ? V.BANANA : V.BOMB)
- })
- ],
- vanish: []
- });
- if (this.board[x][y] != V.EMPTY) {
- m.vanish.push(
- new PiPo({ x: x, y: y, c: 'a', p: this.getPiece(x, y) }));
- }
- return m;
- }
- else if (
- fm.end.effect == "daisy" &&
- deltaX == 0 && deltaY == 0 &&
- !this.canMove([x, y])
- ) {
- // No possible move: return empty move
- return {
- start: { x: -1, y: -1 },
- end: { x: -1, y: -1 },
- appear: [],
- vanish: []
- };
- }
- return null;
- }
-
play(move) {
// if (!this.states) this.states = [];
// const stateFen = this.getFen();
move.flags = JSON.stringify(this.aggregateFlags());
V.PlayOnBoard(this.board, move);
move.turn = [this.turn, this.subTurn];
- if ([0, "kingboo", "toadette", "daisy"].includes(move.end.effect)) {
- this.firstMove.push(move);
+ if (["kingboo", "toadette", "daisy"].includes(move.end.effect)) {
+ this.effects.push(move.end.effect);
this.subTurn = 2;
}
else {
if (move.end.effect == "toadette") this.reserve = this.captured;
else this.reserve = undefined;
const color = move.turn[0];
- if (move.vanish.length == 2 && move.vanish[1].c != 'a') {
+ if (
+ move.vanish.length == 2 &&
+ move.vanish[1].c != 'a' &&
+ move.appear.length == 1 //avoid king Boo!
+ ) {
// Capture: update this.captured
let capturedPiece = move.vanish[1].p;
if (capturedPiece == V.INVISIBLE_QUEEN) capturedPiece = V.QUEEN;
undo(move) {
this.disaggregateFlags(JSON.parse(move.flags));
V.UndoOnBoard(this.board, move);
- if ([0, "kingboo", "toadette", "daisy"].includes(move.end.effect))
- this.firstMove.pop();
+ if (["kingboo", "toadette", "daisy"].includes(move.end.effect))
+ this.effects.pop();
else this.movesCount--;
this.turn = move.turn[0];
this.subTurn = move.turn[1];
return "*";
}
- static GenRandInitFen(randomness) {
+ static GenRandInitFen(options) {
return (
- SuicideRules.GenRandInitFen(randomness).slice(0, -1) +
+ SuicideRules.GenRandInitFen(options).slice(0, -1) +
// Add Peach + Mario flags + capture counts
"1111 000000000000"
);
// Play a deterministic one: capture king or material if possible
return super.getComputerMove(deterministicMoves);
// Play a random effect move, at random:
- let move1 = randomMoves[randInt(moves.length)];
+ let move1 = randomMoves[randInt(randomMoves.length)];
this.play(move1);
let move2 = undefined;
if (this.subTurn == 2) {
move.vanish.every(v => v.c != 'a')
) {
// King Boo exchange
- return move.vanish[1].p.toUpperCase() + finalSquare;
+ return V.CoordsToSquare(move.start) + finalSquare;
}
const piece = move.vanish[0].p;
let notation = undefined;
if (piece == V.PAWN) {
// Pawn move
- if (move.vanish.length >= 2) {
+ if (this.board[move.end.x][move.end.y] != V.EMPTY) {
// Capture
const startColumn = V.CoordToColumn(move.start.y);
notation = startColumn + "x" + finalSquare;
else {
notation =
piece.toUpperCase() +
- (move.vanish.length >= 2 ? "x" : "") +
+ (this.board[move.end.x][move.end.y] != V.EMPTY ? "x" : "") +
finalSquare;
}
if (!!move.end.effect) {
}
return notation;
}
+
};