import ChessRules from "/base_rules";
-import { SuicideRules } from "/variants/Suicide"; ////////:TODO generalize genRandInitFen ?!
-// constraints satisfaction ? + Chakart display bonus messages
-// + animation + multi-moves for bananas/bombs/mushrooms
-
-
-
-import { ArrayFun } from "/utils/array";
-import { randInt } from "/utils/alea";
+import GiveawayRules from "/variants/Giveaway";
+import { ArrayFun } from "/utils/array.js";
+import { Random } from "/utils/alea.js";
import PiPo from "/utils/PiPo.js";
import Move from "/utils/Move.js";
};
}
- static get PawnSpecs() {
- return SuicideRules.PawnSpecs;
+ get pawnPromotions() {
+ return ['q', 'r', 'n', 'b', 'k'];
}
+
get hasCastle() {
return false;
}
return 'm';
}
- static fen2board(f) {
+ genRandInitFen(seed) {
+ const gr = new GiveawayRules({mode: "suicide"}, true);
return (
- f.charCodeAt() <= 90
- ? "w" + f.toLowerCase()
- : (['w', 'd', 'e', 'm'].includes(f) ? "a" : "b") + f
+ gr.genRandInitFen(seed).slice(0, -1) +
+ // Add Peach + Mario flags + capture counts
+ '{"flags": "1111", "ccount": "000000000000"}'
);
}
- static get PIECES() {
+ fen2board(f) {
return (
- ChessRules.PIECES.concat(
- Object.keys(V.IMMOBILIZE_DECODE)).concat(
- [V.BANANA, V.BOMB, V.EGG, V.MUSHROOM, V.INVISIBLE_QUEEN])
- );
- }
-
- getPpath(b) {
- let prefix = "";
- if (
- b[0] == 'a' ||
- b[1] == V.INVISIBLE_QUEEN ||
- Object.keys(V.IMMOBILIZE_DECODE).includes(b[1])
- ) {
- prefix = "Chakart/";
- }
- return prefix + b;
- }
-
- 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);
- }
-
- static ParseFen(fen) {
- const fenParts = fen.split(" ");
- return Object.assign(
- ChessRules.ParseFen(fen),
- { captured: fenParts[4] }
+ f.charCodeAt() <= 90
+ ? "w" + f.toLowerCase()
+ : (['w', 'd', 'e', 'm'].includes(f) ? "a" : "b") + f
);
}
- static IsGoodFen(fen) {
- if (!ChessRules.IsGoodFen(fen)) return false;
- const captured = V.ParseFen(fen).captured;
- if (!captured || !captured.match(/^[0-9]{12,12}$/)) return false;
- return true;
- }
-
- // King can be l or L (immobilized) --> similar to Alice variant
- static IsGoodPosition(position) {
- if (position.length == 0) return false;
- const rows = position.split("/");
- if (rows.length != V.size.x) return false;
- let kings = { "k": 0, "K": 0, 'l': 0, 'L': 0 };
- for (let row of rows) {
- let sumElts = 0;
- for (let i = 0; i < row.length; i++) {
- 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], 10);
- if (isNaN(num)) return false;
- sumElts += num;
- }
- }
- if (sumElts != V.size.y) return false;
- }
- if (kings['k'] + kings['l'] == 0 || kings['K'] + kings['L'] == 0)
- return false;
- return true;
- }
-
- static IsGoodFlags(flags) {
- // 4 for Peach + Mario w, b
- return !!flags.match(/^[01]{4,4}$/);
- }
-
setFlags(fenflags) {
// King can send shell? Queen can be invisible?
this.powerFlags = {
- w: { 'k': false, 'q': false },
- b: { 'k': false, 'q': false }
+ w: {k: false, q: false},
+ b: {k: false, q: false}
};
- for (let c of ["w", "b"]) {
+ for (let c of ['w', 'b']) {
for (let p of ['k', 'q']) {
this.powerFlags[c][p] =
fenflags.charAt((c == "w" ? 0 : 2) + (p == 'k' ? 0 : 1)) == "1";
return super.getFen() + " " + this.getCapturedFen();
}
- getFenForRepeat() {
- return super.getFenForRepeat() + "_" + this.getCapturedFen();
+ getFlagsFen() {
+ return ['w', 'b'].map(c => {
+ return ['k', 'q'].map(p => this.powerFlags[c][p] ? "1" : "0").join("");
+ }).join("");
}
getCapturedFen() {
- let counts = [...Array(12).fill(0)];
- let i = 0;
- for (let p of V.RESERVE_PIECES) {
- counts[i] = this.captured["w"][p];
- counts[6 + i] = this.captured["b"][p];
- i++;
- }
- return counts.join("");
+ const res = ['w', 'b'].map(c => {
+ Object.values(this.captured[c])
+ });
+ return res[0].concat(res[1]).join("");
}
- scanKings() {}
-
- setOtherVariables(fen) {
- super.setOtherVariables(fen);
+ setOtherVariables(fenParsed) {
+ super.setOtherVariables(fenParsed);
// Initialize captured pieces' counts from FEN
- const captured =
- V.ParseFen(fen).captured.split("").map(x => parseInt(x, 10));
+ const allCapts = fenParsed.captured.split("").map(x => parseInt(x, 10));
+ const pieces = ['p', 'r', 'n', 'b', 'q', 'k'];
this.captured = {
- w: {
- [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]: captured[6],
- [V.ROOK]: captured[7],
- [V.KNIGHT]: captured[8],
- [V.BISHOP]: captured[9],
- [V.QUEEN]: captured[10],
- [V.KING]: captured[11]
- }
+ w: Array.toObject(pieces, allCapts.slice(0, 6)),
+ b: Array.toObject(pieces, allCapts.slice(6, 12))
};
- this.effects = [];
- this.subTurn = 1;
- }
-
- getFlagsFen() {
- let fen = "";
- // Add power flags
- for (let c of ["w", "b"])
- for (let p of ['k', 'q']) fen += (this.powerFlags[c][p] ? "1" : "0");
- return fen;
+ this.reserve = { w: {}, b: {} }; //to be replaced by this.captured
+ this.effect = "";
}
- getColor(i, j) {
- if (i >= V.size.x) return i == V.size.x ? "w" : "b";
- return this.board[i][j].charAt(0);
- }
-
- getPiece(i, j) {
- if (i >= V.size.x) return V.RESERVE_PIECES[j];
- return this.board[i][j].charAt(1);
- }
-
- getReservePpath(index, color) {
- return color + V.RESERVE_PIECES[index];
- }
-
- static get RESERVE_PIECES() {
- return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.KING];
- }
-
- getReserveMoves([x, y]) {
- const color = this.turn;
- const p = V.RESERVE_PIECES[y];
- if (this.reserve[color][p] == 0) return [];
+ getDropMovesFrom([c, p]) {
+ if (this.reserve[c][p] == 0) return [];
let moves = [];
- const start = (color == 'w' && p == V.PAWN ? 1 : 0);
- const end = (color == 'b' && p == V.PAWN ? 7 : 8);
+ const start = (c == 'w' && p == 'p' ? 1 : 0);
+ const end = (color == 'b' && p == 'p' ? 7 : 8);
for (let i = start; i < end; i++) {
- for (let j = 0; j < V.size.y; j++) {
+ for (let j = 0; j < this.size.y; j++) {
+ const pieceIJ = this.getPiece(i, j);
if (
- this.board[i][j] == V.EMPTY ||
+ this.board[i][j] == "" ||
this.getColor(i, j) == 'a' ||
- this.getPiece(i, j) == V.INVISIBLE_QUEEN
+ pieceIJ == V.INVISIBLE_QUEEN
) {
- let m = this.getBasicMove({ p: p, x: i, y: j});
- m.start = { x: x, y: y };
+ let m = new Move({
+ start: {x: c, y: p},
+ end: {x: i, y: j},
+ appear: [new PiPo({x: i, y: j, c: c, p: p})],
+ vanish: []
+ });
+ // A drop move may remove a bonus (or hidden queen!)
+ if (this.board[i][j] != "")
+ m.vanish.push(new PiPo({x: i, y: j, c: 'a', p: pieceIJ}));
moves.push(m);
}
}
return moves;
}
+
+
+
+
+
+
+
+
+
+// TODO: rethink from here:
+
getPotentialMovesFrom([x, y]) {
let moves = [];
if (this.subTurn == 1) {
return moves;
}
- play(move) {
-// if (!this.states) this.states = [];
-// const stateFen = this.getFen();
-// this.states.push(stateFen);
- move.flags = JSON.stringify(this.aggregateFlags());
- V.PlayOnBoard(this.board, move);
- move.turn = [this.turn, this.subTurn];
- if (["kingboo", "toadette", "daisy"].includes(move.end.effect)) {
- this.effects.push(move.end.effect);
- this.subTurn = 2;
- }
- else {
- this.turn = V.GetOppCol(this.turn);
- this.movesCount++;
- this.subTurn = 1;
- }
- this.postPlay(move);
- }
- postPlay(move) {
- if (move.end.effect == "toadette") this.reserve = this.captured;
- else this.reserve = undefined;
- const color = move.turn[0];
+
+
+
+
+
+
+
+
+ prePlay(move) {
+ if (move.effect == "toadette")
+ this.reserve = this.captured;
+ else
+ this.reserve = { w: {}, b: {} };;
+ const color = this.turn;
if (
move.vanish.length == 2 &&
move.vanish[1].c != 'a' &&
) {
// Capture: update this.captured
let capturedPiece = move.vanish[1].p;
- if (capturedPiece == V.INVISIBLE_QUEEN) capturedPiece = V.QUEEN;
+ if (capturedPiece == V.INVISIBLE_QUEEN)
+ capturedPiece = V.QUEEN;
else if (Object.keys(V.IMMOBILIZE_DECODE).includes(capturedPiece))
capturedPiece = V.IMMOBILIZE_DECODE[capturedPiece];
this.captured[move.vanish[1].c][capturedPiece]++;
}
}
- undo(move) {
- this.disaggregateFlags(JSON.parse(move.flags));
- V.UndoOnBoard(this.board, move);
- if (["kingboo", "toadette", "daisy"].includes(move.end.effect))
- this.effects.pop();
- else this.movesCount--;
- this.turn = move.turn[0];
- this.subTurn = move.turn[1];
- this.postUndo(move);
-
-// const stateFen = this.getFen();
-// if (stateFen != this.states[this.states.length-1]) debugger;
-// this.states.pop();
- }
-
- postUndo(move) {
- if (!!move.wasImmobilized) {
- const [i, j] = move.wasImmobilized;
- this.board[i][j] =
- this.getColor(i, j) + V.IMMOBILIZE_CODE[this.getPiece(i, j)];
- }
- if (!!move.wasInvisible) {
- const [i, j] = move.wasInvisible;
- this.board[i][j] = this.getColor(i, j) + V.INVISIBLE_QUEEN;
- }
- if (move.vanish.length == 2 && move.vanish[1].c != 'a') {
- let capturedPiece = move.vanish[1].p;
- if (capturedPiece == V.INVISIBLE_QUEEN) capturedPiece = V.QUEEN;
- else if (Object.keys(V.IMMOBILIZE_DECODE).includes(capturedPiece))
- capturedPiece = V.IMMOBILIZE_DECODE[capturedPiece];
- this.captured[move.vanish[1].c][capturedPiece]--;
- }
- else if (move.vanish.length == 0) {
- if (move.appear.length == 0 || move.appear[0].c == 'a') return;
- // A piece was back on board
- this.captured[move.appear[0].c][move.appear[0].p]++;
+ play(move) {
+ this.prePlay(move);
+ this.playOnBoard(move);
+ if (["kingboo", "toadette", "daisy"].includes(move.effect)) {
+ this.effect = move.effect;
+ this.subTurn = 2;
}
- else if (move.appear.length == 0 && move.end.effect == "chomp")
- this.captured[move.vanish[0].c][move.vanish[0].p]--;
- if (move.vanish.length == 0) this.reserve = this.captured;
- else this.reserve = undefined;
- }
-
- getCheckSquares() {
- return [];
- }
-
- getCurrentScore() {
- // Find kings (not tracked in this variant)
- let kingThere = { w: false, b: false };
- for (let i=0; i<8; i++) {
- for (let j=0; j<8; j++) {
- if (
- this.board[i][j] != V.EMPTY &&
- ['k', 'l'].includes(this.getPiece(i, j))
- ) {
- kingThere[this.getColor(i, j)] = true;
- }
- }
+ else {
+ this.turn = C.GetOppCol(this.turn);
+ this.movesCount++;
+ this.subTurn = 1;
}
- if (!kingThere['w']) return "0-1";
- if (!kingThere['b']) return "1-0";
- if (!this.atLeastOneMove()) return (this.turn == 'w' ? "0-1" : "1-0");
- return "*";
- }
-
- static GenRandInitFen(options) {
- return (
- SuicideRules.GenRandInitFen(options).slice(0, -1) +
- // Add Peach + Mario flags + capture counts
- "1111 000000000000"
- );
}
filterValid(moves) {
return moves;
}
- static get VALUES() {
- return Object.assign(
- {},
- ChessRules.VALUES,
- {
- s: 1,
- u: 5,
- o: 3,
- c: 3,
- t: 9,
- l: 1000,
- e: 0,
- d: 0,
- w: 0,
- m: 0
- }
- );
- }
-
- static get SEARCH_DEPTH() {
- return 1;
- }
-
- getComputerMove() {
- const moves = this.getAllValidMoves();
- // Split into "normal" and "random" moves:
- // (Next splitting condition is OK because cannot take self object
- // without a banana or bomb on the way).
- const deterministicMoves = moves.filter(m => {
- return m.vanish.every(a => a.c != 'a' || a.p == V.MUSHROOM);
- });
- const randomMoves = moves.filter(m => {
- return m.vanish.some(a => a.c == 'a' && a.p != V.MUSHROOM);
- });
- if (Math.random() < deterministicMoves.length / randomMoves.length)
- // 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(randomMoves.length)];
- this.play(move1);
- let move2 = undefined;
- if (this.subTurn == 2) {
- const moves2 = this.getAllValidMoves();
- move2 = moves2[randInt(moves2.length)];
- }
- this.undo(move1);
- if (!move2) return move1;
- return [move1, move2];
- }
-
- getNotation(move) {
- if (move.vanish.length == 0 && move.appear.length == 0) return "-";
- if (
- !move.end.effect &&
- move.appear.length > 0 &&
- move.appear[0].p == V.INVISIBLE_QUEEN
- ) {
- return "Q??";
- }
- const finalSquare = V.CoordsToSquare(move.end);
- // Next condition also includes Toadette placements:
- if (move.appear.length > 0 && move.vanish.every(a => a.c == 'a')) {
- const piece =
- move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
- return piece + "@" + finalSquare;
- }
- else if (move.appear.length == 0) {
- const piece = this.getPiece(move.start.x, move.start.y);
- if (piece == V.KING && !move.end.effect)
- // King remote capture
- return "Kx" + finalSquare;
- // Koopa or Chomp, or loopback after bananas, bombs & mushrooms:
- return (
- piece.toUpperCase() + "x" + finalSquare +
- (
- !!move.end.effect
- ? "*" + (move.end.effect == "koopa" ? "K" : "C")
- : ""
- )
- );
- }
- if (move.appear.length == 1 && move.vanish.length == 1) {
- const moveStart = move.appear[0].p.toUpperCase() + "@";
- if (move.appear[0].c == 'a' && move.vanish[0].c == 'a')
- // Bonus replacement:
- return moveStart + finalSquare;
- if (
- move.vanish[0].p == V.INVISIBLE_QUEEN &&
- move.appear[0].x == move.vanish[0].x &&
- move.appear[0].y == move.vanish[0].y
- ) {
- // Toadette takes invisible queen
- return moveStart + "Q" + finalSquare;
- }
- }
- if (
- move.appear.length == 2 &&
- move.vanish.length == 2 &&
- move.appear.every(a => a.c != 'a') &&
- move.vanish.every(v => v.c != 'a')
- ) {
- // King Boo exchange
- return V.CoordsToSquare(move.start) + finalSquare;
- }
- const piece = move.vanish[0].p;
- let notation = undefined;
- if (piece == V.PAWN) {
- // Pawn move
- 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 = finalSquare;
- if (move.appear[0].p != V.PAWN)
- // Promotion
- notation += "=" + move.appear[0].p.toUpperCase();
- }
- else {
- notation =
- piece.toUpperCase() +
- (this.board[move.end.x][move.end.y] != V.EMPTY ? "x" : "") +
- finalSquare;
- }
- if (!!move.end.effect) {
- switch (move.end.effect) {
- case "kingboo":
- notation += "*B";
- break;
- case "toadette":
- notation += "*T";
- break;
- case "daisy":
- notation += "*D";
- break;
- case "bowser":
- notation += "*M";
- break;
- case "luigi":
- case "waluigi":
- const lastAppear = move.appear[move.appear.length - 1];
- const effectOn =
- V.CoordsToSquare({ x: lastAppear.x, y : lastAppear.y });
- notation += "*" + move.end.effect[0].toUpperCase() + effectOn;
- break;
- }
- }
- return notation;
- }
+ // TODO + display bonus messages
+ // + animation + multi-moves for bananas/bombs/mushrooms
};