},
'r': {
"class": "rook",
- moves: [
+ both: [
{steps: [[0, 1], [0, -1], [1, 0], [-1, 0]]}
]
},
'n': {
"class": "knight",
- moves: [
+ both: [
{
steps: [
[1, 2], [1, -2], [-1, 2], [-1, -2],
},
'b': {
"class": "bishop",
- moves: [
+ both: [
{steps: [[1, 1], [1, -1], [-1, 1], [-1, -1]]}
]
},
'q': {
"class": "queen",
- moves: [
+ both: [
{
steps: [
[0, 1], [0, -1], [1, 0], [-1, 0],
},
'k': {
"class": "king",
- moves: [
+ both: [
{
steps: [
[0, 1], [0, -1], [1, 0], [-1, 0],
getStepSpec(color, x, y, piece) {
let pieceType = piece;
- const allSpecs = this.pieces(color, x, y);
+ let allSpecs = this.pieces(color, x, y);
if (!piece)
pieceType = this.getPieceType(x, y);
else if (allSpecs[piece].moveas)
pieceType = allSpecs[piece].moveas;
- return allSpecs[pieceType];
+ let res = allSpecs[pieceType];
+ if (!res["both"])
+ res.both = [];
+ if (!res["moves"])
+ res.moves = [];
+ if (!res["attack"])
+ res.attack = [];
+ return res;
}
// Can thing on square1 capture thing on square2?
const oppCol = C.GetOppCol(color);
const piece = this.getPieceType(x, y);
const stepSpec = this.getStepSpec(color, x, y, piece);
- const attacks = stepSpec.attack || stepSpec.moves;
+ 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]];
elt.segments = this.getSegments(segments, segStart, end);
res.push(elt);
};
- const exploreSteps = (stepArray) => {
+ const exploreSteps = (stepArray, mode) => {
for (let s of stepArray) {
outerLoop: for (let step of s.steps) {
if (o.segments) {
!o.captureTarget ||
(o.captureTarget[0] == i && o.captureTarget[1] == j)
) {
- if (o.one && !o.attackOnly)
+ if (o.one && mode != "attack")
return true;
- if (!o.attackOnly)
+ if (mode != "attack")
addSquare(!o.captureTarget ? [i, j] : [x, y]);
if (o.captureTarget)
return res[0];
if (!explored[i + "." + j]) {
explored[i + "." + j] = true;
if (allowed([x, y], [i, j])) {
- if (o.one && !o.moveOnly)
+ if (o.one && mode != "moves")
return true;
- if (!o.moveOnly)
+ if (mode != "moves")
addSquare(!o.captureTarget ? [i, j] : [x, y]);
if (
o.captureTarget &&
return undefined; //default, but let's explicit it
};
if (o.captureTarget)
- return exploreSteps(o.captureSteps)
+ return exploreSteps(o.captureSteps, "attack");
else {
const stepSpec =
o.stepSpec || this.getStepSpec(this.getColor(x, y), x, y);
let outOne = false;
- if (!o.attackOnly || !stepSpec.attack)
- outOne = exploreSteps(stepSpec.moves);
- if (!outOne && !o.moveOnly && !!stepSpec.attack) {
- o.attackOnly = true; //ok because o is always a temporary object
- outOne = exploreSteps(stepSpec.attack);
- }
+ if (!o.attackOnly)
+ outOne = exploreSteps(stepSpec.both.concat(stepSpec.moves), "moves");
+ if (!outOne && !o.moveOnly)
+ outOne = exploreSteps(stepSpec.both.concat(stepSpec.attack), "attack");
return (o.one ? outOne : res);
}
}
if (this.canStepOver(x, y, apparentPiece))
continue;
const stepSpec = this.getStepSpec(colIJ, i, j);
- const attacks = stepSpec.attack || stepSpec.moves;
+ const attacks = stepSpec.attack.concat(stepSpec.both);
for (let a of attacks) {
for (let s of a.steps) {
// Quick check: if step isn't compatible, don't even try
min = 0;
}
if (!Random.rand)
- Random.setSeed(Math.floor(Math.random() * 1984));
+ Random.setSeed(Math.floor(Math.random() * 19840));
return Math.floor(Random.rand() * (max - min)) + min;
},
{name: 'Atomic', desc: 'Explosive captures'},
{name: 'Avalam', desc: 'Build towers'},
{name: 'Avalanche', desc: 'Pawnfalls'},
-// {name: 'Balaklava', desc: 'Meet the Mammoth'},
+ {name: 'Balaklava', desc: 'Meet the Mammoth'},
{name: 'Bario', desc: 'A quantum story'},
{name: "Balanced", desc: "balanced chess"},
{name: 'Baroque', desc: 'Exotic captures'},
{name: "Benedict", desc: "Change colors"},
{name: 'Berolina', desc: 'Pawns move diagonally'},
-// {name: 'Bicolour', desc: 'Harassed kings'},
-// {name: 'Brotherhood', desc: 'Friendly pieces'},
+ {name: 'Bicolour', desc: 'Harassed kings'},
+ {name: 'Brotherhood', desc: 'Friendly pieces'},
{name: 'Cannibal', desc: 'Capture powers'},
// {name: 'Capablanca', desc: 'Capablanca Chess', disp: 'Capablanca Chess'},
{name: 'Capture', desc: 'Mandatory captures'},
// amazon
'a': {
"class": "amazon",
- moves: [
+ both: [
{
steps: [
[0, 1], [0, -1], [1, 0], [-1, 0],
// empress
'e': {
"class": "empress",
- moves: [
+ both: [
{
steps: [
[1, 0], [-1, 0], [0, 1], [0, -1]
// princess
's': {
"class": "princess",
- moves: [
+ both: [
{
steps: [
[1, 1], [1, -1], [-1, 1], [-1, -1]
{"class": "bishop" + (this.playerColor != color ? "_inv" : "")}),
's': { //"square"
"class": "babyrook",
- moves: [
+ both: [
{
steps: [[0, 1], [0, -1], [1, 0], [-1, 0]],
range: 1
},
'c': { //"circle"
"class": "babyqueen",
- moves: [
+ both: [
{
steps: [
[0, 1], [0, -1], [1, 0], [-1, 0],
},
't': { //"triangle"
"class": "babybishop" + (this.playerColor != color ? "_inv" : ""),
- moves: [
+ both: [
{
steps: [[1, 1], [1, -1], [-1, 1], [-1, -1]],
range: 1
't': {"class": "target-queen", moves: []},
'l': {"class": "target-king", moves: []}
};
- return Object.assign({ 'g': {"class": "target", moves: []} },
+ return Object.assign({ 'g': {"class": "target"} },
targets, super.pieces(color, x, y));
}
import ChessRules from "/base_rules.js";
import AbstractAntikingRules from "/variants/_Antiking/class.js";
+import BerolinaPawnSpec from "/variants/_Berolina/pawnSpec.js";
export default class Antiking1Rules extends AbstractAntikingRules {
}
pieces(color, x, y) {
- const pawnShift = (color == "w" ? -1 : 1);
let res = super.pieces(color, x, y);
- res['p'].moves = [
- {
- steps: [[pawnShift, 1], [pawnShift, -1]],
- range: 1
- }
- ];
- res['p'].attack = [
- {
- steps: [[pawnShift, 0]],
- range: 1
- }
- ];
+ res['p'] = BerolinaPawnSpec(color);
return res;
}
const pawnShift = (color == "w" ? -1 : 1);
Array.prototype.push.apply(pawnSpec.attack[0].steps,
[[-pawnShift, 1], [-pawnShift, -1]]);
- queenSpec.moves[0].range = 3;
- kingSpec.moves[0].range = 3;
+ queenSpec.both[0].range = 3;
+ kingSpec.both[0].range = 3;
return Object.assign({},
allSpecs,
{
c: c
})
],
- start: { x: x, y: y },
- end: { x: x, y: y }
+ start: {x: x, y: y},
+ end: {x: x, y: y}
})
];
}
return {
'b': {
"class": "stack",
- moves: [{steps: steps, range: 1}]
+ both: [{steps: steps, range: 1}]
},
'c': {
"class": "stack2",
--- /dev/null
+import ChessRules from "/base_rules.js";
+
+export default class BalaklavaRules extends ChessRules {
+
+ get pawnPromotions() {
+ return ['r', 'm', 'b', 'q'];
+ }
+
+ get hasEnpassant() {
+ return false;
+ }
+
+ pieces(color, x, y) {
+ let res = super.pieces(color, x, y);
+ const knightSpec = res['n'];
+ delete res['n'];
+ res['m'] = {
+ "class": "mammoth",
+ both: [
+ {
+ steps: [
+ [-2, -2], [-2, 0], [-2, 2],
+ [0, -2], [0, 2], [2, -2],
+ [2, 0], [2, 2]
+ ],
+ range: 1
+ }
+ ]
+ };
+ ['r', 'b', 'm', 'q'].forEach(p => res[p].moves = knightSpec.moves);
+ return res;
+ }
+
+ genRandInitBaseFen() {
+ const baseFen = super.genRandInitBaseFen();
+ return {
+ fen: baseFen.replace(/n/g, 'm').replace(/N/g, 'M'),
+ o: baseFen.o
+ };
+ }
+
+ pawnPostProcess(moves) {
+ if (moves.length == 0)
+ return [];
+ const color = moves[0].vanish[0].c;
+ const lastRank = (color == 'w' ? 0 : this.size.x - 1);
+ const noKnightPromotions = moves.filter(m => {
+ return (
+ m.end.x != lastRank ||
+ (
+ Math.abs(m.start.x - m.end.x) <= 1 &&
+ Math.abs(m.start.y - m.end.y) <= 1
+ )
+ );
+ });
+ return super.pawnPostProcess(noKnightPromotions);
+ }
+
+};
--- /dev/null
+<p>TODO</p>
--- /dev/null
+@import url("/base_pieces.css");
pieces(color, x, y) {
return Object.assign(
- {
- 'u': {
- "class": "undefined",
- moves: []
- }
- },
+ { 'u': {"class": "undefined"} },
super.pieces(color, x, y)
);
}
if (super.underCheck(square_s, oppCol))
return true;
// Check potential specializations of undefined using reserve:
+ const inReserve = Object.keys(this.reserve[oppCol])
+ .filter(k => this.reserve[oppCol][k] >= 1);
const allAttacks = Array.prototype.concat.apply(
- ['r', 'n', 'b', 'q'].map(p => this.pieces()[p].moves[0]));
+ inReserve.map(p => this.pieces()[p].both[0]));
const [x, y] = square_s[0];
for (let i=0; i<this.size.x; i++) {
for (let j=0; j<this.size.y; j++) {
-import ChessRules from "/base_rules.js";
+import AbstractFlipRules from "/variants/_Flip/class.js";
import PiPo from "/utils/PiPo.js";
-export default class BenedictRules extends ChessRules {
+export default class BenedictRules extends AbstractFlipRules {
static get Options() {
return {
};
}
- get hasEnpassant() {
- return false;
- }
-
- canTake() {
- return false;
- }
-
pieces(color, x, y) {
if (!this.options["cleopatra"])
return super.pieces(color, x, y);
return bMoves;
}
- playOnBoard(move) {
- super.playOnBoard(move);
- this.flipColorOf(move.flips);
- }
- undoOnBoard(move) {
- super.undoOnBoard(move);
- this.flipColorOf(move.flips);
- }
-
- flipColorOf(flips) {
- for (let xy of flips) {
- const newColor = C.GetOppCol(this.getColor(xy.x, xy.y));
- this.board[xy.x][xy.y] = newColor + this.board[xy.x][xy.y][1];
- }
- }
-
- // Moves cannot flip our king's color, so all are valid
- filterValid(moves) {
- return moves;
- }
-
- // A king under (regular) check flips color, and the game is over.
- underCheck() {
- return false;
- }
-
- playVisual(move, r) {
- super.playVisual(move, r);
- move.flips.forEach(f => {
- this.g_pieces[f.x][f.y].classList.toggle("white");
- this.g_pieces[f.x][f.y].classList.toggle("black");
- });
- }
-
};
-import AbstractBerolinaRules from "/variants/_Berolina/class.js";
+import ChessRules from "/base_rules.js";
+import BerolinaPawnSpec from "/variants/_Berolina/pawnSpec.js";
-export default class BerolinaRules extends AbstractBerolinaRules {};
+export default class BerolinaRules extends ChessRules {
+
+ pieces(color, x, y) {
+ let res = super.pieces(color, x, y);
+ res['p'] = BerolinaPawnSpec(color);
+ return res;
+ }
+
+};
--- /dev/null
+import ChessRules from "/base_rules.js";
+import {Random} from "/utils/alea.js";
+import {ArrayFun} from "/utils/array.js";
+
+export class BicolourRules extends ChessRules {
+
+ // TODO: Options
+
+ get hasFlags() {
+ return false;
+ }
+
+ canTake([x1, y1], [x2, y2]) {
+ return (this.getPiece(x2, y2) == 'k' || super.canTake([x1, y1], [x2, y2]));
+ }
+
+ genRandInitBaseFen() {
+ if (this.options["randomness"] == 0)
+ return { fen: "rqbnkbnr/pppppppp/8/8/8/8/PPPPPPPP/RQBNKBNR", o: {} };
+
+ // Place pieces at random but the king cannot be next to a rook or queen.
+ // => One bishop and one knight should surround the king.
+ let pieces = {w: new Array(8), b: new Array(8)};
+ let flags = "";
+ for (let c of ["w", "b"]) {
+ if (c == 'b' && this.options["randomness"] == 1) {
+ pieces['b'] = pieces['w'];
+ break;
+ }
+ let positions = ArrayFun.range(8);
+ const kingPos = randInt(8);
+ let toRemove = [kingPos];
+ let knight1Pos = undefined;
+ let bishop1Pos = undefined;
+ if (kingPos == 0) {
+ if (Random.randBool())
+ knight1Pos = 1;
+ else
+ bishop1Pos = 1;
+ toRemove.push(1);
+ }
+ else if (kingPos == V.size.y - 1) {
+ if (Random.randBool())
+ knight1Pos = V.size.y - 2;
+ else
+ bishop1Pos = V.size.y - 2;
+ toRemove.push(V.size.y - 2);
+ }
+ else {
+ knight1Pos = kingPos + (Random.randBool() ? 1 : -1);
+ bishop1Pos = kingPos + (knight1Pos < kingPos ? 1 : -1);
+ toRemove.push(knight1Pos, bishop1Pos);
+ }
+ const firstPieces = [kingPos, knight1Pos, bishop1Pos]
+ .filter(elt => elt !== undefined);
+ firstPieces
+ .sort((a, b) => b - a)
+ .forEach(elt => positions.splice(elt, 1));
+ let randIndex = undefined;
+ if (bishop1Pos === undefined) {
+ const posWithIdx = positions.map((e,i) => { return {e: e, i: i}; });
+ let availableSquares = posWithIdx.filter(p => p.e % 2 == 0);
+ randIndex = randInt(availableSquares.length);
+ bishop1Pos = availableSquares[randIndex].e;
+ positions.splice(availableSquares[randIndex].i, 1);
+ }
+ const posWithIdx = positions.map((e,i) => { return {e: e, i: i}; });
+ const rem1B = bishop1Pos % 2;
+ let availableSquares = posWithIdx.filter(p => p.e % 2 == 1 - rem1B);
+ randIndex = randInt(availableSquares.length);
+ const bishop2Pos = availableSquares[randIndex].e;
+ positions.splice(availableSquares[randIndex].i, 1);
+ if (knight1Pos === undefined) {
+ randIndex = randInt(5);
+ knight1Pos = positions[randIndex];
+ positions.splice(randIndex, 1);
+ }
+ randIndex = randInt(4);
+ const knight2Pos = positions[randIndex];
+ positions.splice(randIndex, 1);
+ randIndex = randInt(3);
+ const queenPos = positions[randIndex];
+ positions.splice(randIndex, 1);
+ const rook1Pos = positions[0];
+ const rook2Pos = positions[1];
+ pieces[c][rook1Pos] = "r";
+ pieces[c][knight1Pos] = "n";
+ pieces[c][bishop1Pos] = "b";
+ pieces[c][queenPos] = "q";
+ pieces[c][kingPos] = "k";
+ pieces[c][bishop2Pos] = "b";
+ pieces[c][knight2Pos] = "n";
+ pieces[c][rook2Pos] = "r";
+ }
+
+ return {
+ fen: (
+ pieces["b"].join("") +
+ "/pppppppp/8/8/8/8/PPPPPPPP/" +
+ pieces["w"].join("").toUpperCase()
+ ),
+ o: {}
+ };
+ }
+
+ underCheck(color) {
+ const kingPos = this.searchKingPos(color)[0],
+ return (this.underAttack(kingPos, 'w') || this.underAttack(kingPos, 'b'));
+ }
+
+};
--- /dev/null
+<p>TODO</p>
--- /dev/null
+@import url("/base_pieces.css");
--- /dev/null
+import ChessRules from "/base_rules.js";
+
+export default class BrotherhoodRules extends ChessRules {
+
+ canTake([x1, y1], [x2, y2]) {
+ if (!super.canTake([x1, y1], [x2, y2]))
+ return false;
+ const p1 = this.getPiece(x1, y1),
+ p2 = this.getPiece(x2, y2);
+ return (p1 != p2 || ['p', 'k'].some(symb => [p1, p2].includes(symb)));
+ }
+
+ getCurrentScore() {
+ if (this.atLeastOneMove(this.turn))
+ return "*";
+ // Stalemate = loss
+ return (this.turn == 'w' ? "0-1" : "1-0");
+ }
+
+};
--- /dev/null
+<p>
+ Rooks, knights, bishops and queens cannot
+ capture enemy pieces of the same nature.
+</p>
+
+<p class="author">Gianluca Vecchi (1993).</p>
--- /dev/null
+@import url("/base_pieces.css");
{
'y': {
// Virtual piece for "king remote shell captures"
- moves: [],
attack: [
{
steps: [
+++ /dev/null
-import ChessRules from "/base_rules.js";
-
-export default class BerolinaRules extends ChessRules {
-
- pieces(color, x, y) {
- const pawnShift = (color == "w" ? -1 : 1);
- let res = super.pieces(color, x, y);
- res['p'].moves = [
- {
- steps: [[pawnShift, 1], [pawnShift, -1]],
- range: 1
- }
- ];
- res['p'].attack = [
- {
- steps: [[pawnShift, 0]],
- range: 1
- }
- ];
- return res;
- }
-
-};
--- /dev/null
+export default BerolinaPawnSpec = (color) => {
+
+ const pawnShift = (color == "w" ? -1 : 1);
+ return {
+ moves: [
+ {
+ steps: [[pawnShift, 1], [pawnShift, -1]],
+ range: 1
+ }
+ ],
+ attack: [
+ {
+ steps: [[pawnShift, 0]],
+ range: 1
+ }
+ ]
+ };
+
+};
--- /dev/null
+import ChessRules from "/base_rules.js";
+
+export default class AbstractFlipRules extends ChessRules {
+
+ // For games without captures (replaced by flips)
+ get hasEnpassant() {
+ return false;
+ }
+ canTake() {
+ return false;
+ }
+ filterValid(moves) {
+ return moves;
+ }
+ underCheck() {
+ return false;
+ }
+
+ playOnBoard(move) {
+ super.playOnBoard(move);
+ this.flipColorOf(move.flips);
+ }
+ undoOnBoard(move) {
+ super.undoOnBoard(move);
+ this.flipColorOf(move.flips);
+ }
+
+ flipColorOf(flips) {
+ for (let xy of flips) {
+ const newColor = C.GetOppCol(this.getColor(xy.x, xy.y));
+ this.board[xy.x][xy.y] = newColor + this.board[xy.x][xy.y][1];
+ }
+ }
+
+ playVisual(move, r) {
+ super.playVisual(move, r);
+ move.flips.forEach(f => {
+ this.g_pieces[f.x][f.y].classList.toggle("white");
+ this.g_pieces[f.x][f.y].classList.toggle("black");
+ });
+ }
+
+};