return false;
}
+ // Some variants do not flip board as black
+ get flippedBoard() {
+ return (this.playerColor == 'b');
+ }
+
// Some variants use click infos:
doClick(coords) {
if (typeof coords.x != "number")
// Get SVG board (background, no pieces)
getSvgChessboard() {
- const flipped = (this.playerColor == 'b');
+ const flipped = this.flippedBoard;
let board = `
<svg
viewBox="0 0 ${10*this.size.y} ${10*this.size.x}"
}
else {
const sqSize = r.width / this.size.y;
- const flipped = (this.playerColor == 'b');
+ const flipped = this.flippedBoard;
x = (flipped ? this.size.y - 1 - j : j) * sqSize;
y = (flipped ? this.size.x - 1 - i : i) * sqSize;
}
// TODO: (0, 0) is wrong, would need to place an attacker here...
const steps = this.pieces(this.playerColor, 0, 0)["p"].attack[0].steps;
for (let step of steps) {
- const x = this.epSquare.x - step[0],
+ const x = this.epSquare.x - step[0], //NOTE: epSquare.x not on edge
y = this.getY(this.epSquare.y - step[1]);
if (
this.onBoard(x, y) &&
getPawnShift(color) {
return (color == "w" ? -1 : 1);
}
+ isPawnInitRank(x, color) {
+ return (color == 'w' && x >= 6) || (color == 'b' && x <= 1);
+ }
pieces(color, x, y) {
const pawnShift = this.getPawnShift(color);
- // NOTE: jump 2 squares from first rank (pawns can be here sometimes)
- const initRank = ((color == 'w' && x >= 6) || (color == 'b' && x <= 1));
return {
'p': {
"class": "pawn",
moves: [
{
steps: [[pawnShift, 0]],
- range: (initRank ? 2 : 1)
+ range: (this.isPawnInitRank(x, color) ? 2 : 1)
}
],
attack: [
res += this.size.y;
return res;
}
+ // Circular?
+ getX(x) {
+ return x; //generally, no
+ }
+
+ increment([x, y], step) {
+ return [
+ this.getX(x + step[0]),
+ this.getY(y + step[1])
+ ];
+ }
getSegments(curSeg, segStart, segEnd) {
if (curSeg.length == 0)
const attacks = stepSpec.both.concat(stepSpec.attack);
for (let a of attacks) {
outerLoop: for (let step of a.steps) {
- let [i, j] = [x + step[0], y + step[1]];
- let stepCounter = 1;
+ let [i, j] = this.increment([x, y], step);
+ let stepCounter = 0;
while (this.onBoard(i, j) && this.board[i][j] == "") {
if (a.range <= stepCounter++)
continue outerLoop;
- i += step[0];
- j = this.getY(j + step[1]);
+ [i, j] = this.increment([i, j], step);
}
if (
this.onBoard(i, j) &&
vanish: []
});
for (let step of steps) {
- let x = m.end.x + step[0];
- let y = this.getY(m.end.y + step[1]);
+ let [x, y] = this.increment([m.end.x, m.end.y], step);
if (
this.onBoard(x, y) &&
this.board[x][y] != "" &&
m.appear[0].p = this.pawnPromotions[0];
for (let i=1; i<this.pawnPromotions.length; i++) {
let newMv = JSON.parse(JSON.stringify(m));
- newMv.appear[0].p = this.pawnSpecs.promotions[i];
+ newMv.appear[0].p = this.pawnPromotions[i];
newMoves.push(newMv);
}
}
if (s.range <= stepCounter++)
continue outerLoop;
const oldIJ = [i, j];
- i += step[0];
- j = this.getY(j + step[1]);
+ [i, j] = this.increment([i, j], step);
if (o.segments && Math.abs(j - oldIJ[1]) > 1) {
// Boundary between segments (cylinder mode)
segments.push([[segStart[0], segStart[1]], oldIJ]);
const oppCols = this.getOppCols(color);
if (
this.epSquare &&
- this.epSquare.x == x + shiftX &&
+ this.epSquare.x == x + shiftX && //NOTE: epSquare.x not on edge
Math.abs(this.getY(this.epSquare.y - y)) == 1 &&
// Doublemove (and Progressive?) guards:
this.board[this.epSquare.x][this.epSquare.y] == "" &&
{name: 'Checkered', desc: 'Shared pieces'},
{name: 'Checkless', desc: 'No-check mode'},
{name: 'Chess960', disp: "Chess 960", desc: "Standard rules"},
-// {name: 'Circular', desc: 'Run forward'},
+ {name: 'Circular', desc: 'Run forward'},
// {name: 'Clorange', desc: 'A Clockwork Orange', disp: 'Clockwork Orange'},
// {name: 'Convert', desc: 'Convert enemy pieces'},
// {name: 'Copycat', desc: 'Borrow powers'},
--- /dev/null
+import ChessRules from "/base_rules.js";
+import {FenUtil} from "/utils/setupPieces.js";
+
+export default class CircularRules extends ChessRules {
+
+ get hasCastle() {
+ return false;
+ }
+ get hasEnpassant() {
+ return false;
+ }
+
+ // Everypawn is going up!
+ getPawnShift(color) {
+ return -1;
+ }
+ isPawnInitRank(x, color) {
+ return (color == 'w' && x == 6) || (color == 'b' && x == 2);
+ }
+
+ get flippedBoard() {
+ return false;
+ }
+
+ // Circular board:
+ // TODO: graph with segments, as for cylindrical...
+ getX(x) {
+ let res = x % this.size.x;
+ if (res < 0)
+ res += this.size.x;
+ return res;
+ }
+
+ // TODO: rewrite in more elegant way
+ getFlagsFen() {
+ let flags = "";
+ for (let c of ["w", "b"]) {
+ for (let i = 0; i < 8; i++)
+ flags += this.pawnFlags[c][i] ? "1" : "0";
+ }
+ return flags;
+ }
+
+ setFlags(fenflags) {
+ this.pawnFlags = {
+ w: [...Array(8)], //pawns can move 2 squares?
+ b: [...Array(8)]
+ };
+ for (let c of ["w", "b"]) {
+ for (let i = 0; i < 8; i++)
+ this.pawnFlags[c][i] = fenflags.charAt((c == "w" ? 0 : 8) + i) == "1";
+ }
+ }
+
+ genRandInitBaseFen() {
+ let setupOpts = {
+ randomness: this.options["randomness"],
+ diffCol: ['b']
+ };
+ const s = FenUtil.setupPieces(
+ ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'], setupOpts);
+ return {
+ fen: "8/8/pppppppp/" + s.b.join("") + "/8/8/PPPPPPPP/" +
+ s.w.join("").toUpperCase(),
+ o: {flags: "11111111"}
+ };
+ }
+
+ filterValid(moves) {
+ const filteredMoves = super.filterValid(moves);
+ // If at least one full move made, everything is allowed:
+ if (this.movesCount >= 2)
+ return filteredMoves;
+ // Else, forbid checks:
+ const oppCol = C.GetOppTurn(this.turn);
+ const oppKingPos = this.searchKingPos(oppCol);
+ return filteredMoves.filter(m => {
+ this.playOnBoard(m);
+ const res = !this.underCheck(oppKingPos, [this.turn]);
+ this.undoOnBoard(m);
+ return res;
+ });
+ }
+
+ prePlay(move) {
+ if (move.appear.length > 0 && move.vanish.length > 0) {
+ super.prePlay(move);
+ if (
+ [2, 6].includes(move.start.x) &&
+ move.vanish[0].p == 'p' &&
+ Math.abs(move.end.x - move.start.x) == 2
+ ) {
+ // This move turns off a 2-squares pawn flag
+ this.pawnFlags[move.start.x == 6 ? "w" : "b"][move.start.y] = false;
+ }
+ }
+ }
+
+};