return V.HasFlags;
}
- // Some variants don't have en-passant
+ // Pawns specifications
+ static get PawnSpecs() {
+ return {
+ directions: { 'w': -1, 'b': 1 },
+ twoSquares: true,
+ promotions: [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN],
+ canCapture: true,
+ captureBackward: false,
+ bidirectional: false
+ };
+ }
+
+ // En-passant captures need a stack of squares:
static get HasEnpassant() {
return true;
}
return moves;
}
+ // Special case of en-passant captures: treated separately
+ getEnpassantCaptures([x, y], shiftX) {
+ const Lep = this.epSquares.length;
+ const epSquare = this.epSquares[Lep - 1]; //always at least one element
+ let enpassantMove = null;
+ if (
+ !!epSquare &&
+ epSquare.x == x + shiftX &&
+ Math.abs(epSquare.y - y) == 1
+ ) {
+ enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]);
+ enpassantMove.vanish.push({
+ x: x,
+ y: epSquare.y,
+ p: "p",
+ c: this.getColor(x, epSquare.y)
+ });
+ }
+ return !!enpassantMove ? [enpassantMove] : [];
+ }
+
// What are the pawn moves from square x,y ?
- getPotentialPawnMoves([x, y]) {
+ getPotentialPawnMoves([x, y], promotions) {
const color = this.turn;
- let moves = [];
const [sizeX, sizeY] = [V.size.x, V.size.y];
- const shiftX = color == "w" ? -1 : 1;
+ const pawnShiftX = V.PawnSpecs.directions[color];
const firstRank = color == "w" ? sizeX - 1 : 0;
const startRank = color == "w" ? sizeX - 2 : 1;
const lastRank = color == "w" ? 0 : sizeX - 1;
- // NOTE: next condition is generally true (no pawn on last rank)
- if (x + shiftX >= 0 && x + shiftX < sizeX) {
- const finalPieces =
- x + shiftX == lastRank
- ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]
- : [V.PAWN];
- if (this.board[x + shiftX][y] == V.EMPTY) {
- // One square forward
- for (let piece of finalPieces) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y], {
- c: color,
- p: piece
- })
- );
- }
- // Next condition because pawns on 1st rank can generally jump
- if (
- [startRank, firstRank].includes(x) &&
- this.board[x + 2 * shiftX][y] == V.EMPTY
- ) {
- // Two squares jump
- moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
- }
+ // Consider all potential promotions:
+ const addMoves = ([x1, y1], [x2, y2], moves) => {
+ let finalPieces = [V.PAWN];
+ if (x2 == lastRank) {
+ // promotions arg: special override for Hiddenqueen variant
+ if (!!promotions) finalPieces = promotions;
+ else if (!!V.PawnSpecs.promotions)
+ finalPieces = V.PawnSpecs.promotions;
}
- // Captures
- for (let shiftY of [-1, 1]) {
- if (
- y + shiftY >= 0 &&
- y + shiftY < sizeY &&
- this.board[x + shiftX][y + shiftY] != V.EMPTY &&
- this.canTake([x, y], [x + shiftX, y + shiftY])
- ) {
- for (let piece of finalPieces) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
- c: color,
- p: piece
- })
- );
+ for (let piece of finalPieces) {
+ moves.push(
+ this.getBasicMove([x1, y1], [x2, y2], {
+ c: color,
+ p: piece
+ })
+ );
+ }
+ }
+
+ // Pawn movements in shiftX direction:
+ const getPawnMoves = (shiftX) => {
+ let moves = [];
+ // NOTE: next condition is generally true (no pawn on last rank)
+ if (x + shiftX >= 0 && x + shiftX < sizeX) {
+ if (this.board[x + shiftX][y] == V.EMPTY) {
+ // One square forward
+ addMoves([x, y], [x + shiftX, y], moves);
+ // Next condition because pawns on 1st rank can generally jump
+ if (
+ V.PawnSpecs.twoSquares &&
+ [startRank, firstRank].includes(x) &&
+ this.board[x + 2 * shiftX][y] == V.EMPTY
+ ) {
+ // Two squares jump
+ moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
+ }
+ }
+ // Captures
+ if (V.PawnSpecs.canCapture) {
+ for (let shiftY of [-1, 1]) {
+ if (
+ y + shiftY >= 0 &&
+ y + shiftY < sizeY
+ ) {
+ if (
+ this.board[x + shiftX][y + shiftY] != V.EMPTY &&
+ this.canTake([x, y], [x + shiftX, y + shiftY])
+ ) {
+ addMoves([x, y], [x + shiftX, y + shiftY], moves);
+ }
+ if (
+ V.PawnSpecs.captureBackward &&
+ x - shiftX >= 0 && x - shiftX < V.size.x &&
+ this.board[x - shiftX][y + shiftY] != V.EMPTY &&
+ this.canTake([x, y], [x - shiftX, y + shiftY])
+ ) {
+ addMoves([x, y], [x + shiftX, y + shiftY], moves);
+ }
+ }
}
}
}
+ return moves;
}
+ let pMoves = getPawnMoves(pawnShiftX);
+ if (V.PawnSpecs.bidirectional)
+ pMoves = pMoves.concat(getPawnMoves(-pawnShiftX));
+
if (V.HasEnpassant) {
- // En passant
- const Lep = this.epSquares.length;
- const epSquare = this.epSquares[Lep - 1]; //always at least one element
- if (
- !!epSquare &&
- epSquare.x == x + shiftX &&
- Math.abs(epSquare.y - y) == 1
- ) {
- let enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]);
- enpassantMove.vanish.push({
- x: x,
- y: epSquare.y,
- p: "p",
- c: this.getColor(x, epSquare.y)
- });
- moves.push(enpassantMove);
- }
+ // NOTE: backward en-passant captures are not considered
+ // because no rules define them (for now).
+ Array.prototype.push.apply(
+ pMoves,
+ this.getEnpassantCaptures([x, y], pawnShiftX)
+ );
}
- return moves;
+ return pMoves;
}
// What are the rook moves from square x,y ?
if (this.castleFlags[c][castleSide] >= V.size.y) continue;
// If this code is reached, rooks and king are on initial position
+ const rookPos = this.castleFlags[c][castleSide];
+ if (this.getColor(x, rookPos) != c)
+ // Rook is here but changed color (see Benedict)
+ continue;
+
// Nothing on the path of the king ? (and no checks)
const finDist = finalSquares[castleSide][0] - y;
let step = finDist / Math.max(1, Math.abs(finDist));
// Nothing on the path to the rook?
step = castleSide == 0 ? -1 : 1;
- const rookPos = this.castleFlags[c][castleSide];
for (i = y + step; i != rookPos; i += step) {
if (this.board[x][i] != V.EMPTY) continue castlingCheck;
}
onmessage = async function(e) {
switch (e.data[0]) {
case "scripts": {
- const vModule = await import("@/variants/" + e.data[1] + ".js");
- self.V = vModule.VariantRules;
+ await import("@/variants/" + e.data[1] + ".js")
+ .then((vModule) => { self.V = vModule[e.data[1] + "Rules"]; });
break;
}
case "init": {
p.boxed
- Las capturas son obligatorias. Gana por jaque mate.
+ | Las capturas son obligatorias. Gana por jaque mate.
p.
Todo va como el ajedrez ortodoxo, pero cuando es posible
como en el siguiente diagrama: Qxd7 será forzado, perdiendo a la dama.
figure.diagram-container
- .diagrama
+ .diagram
| fen:rnbqkbnr/pppp1pp1/7p/4P3/8/8/PPP1PPPP/RNBQKBNR:
figcaption Después de 1.d4?? e5 2.dxe5 h6.
// NOTE: alternative implementation, probably cleaner = use only 1 board
// TODO? atLeastOneMove() would be more efficient if rewritten here (less sideBoard computations)
-export const VariantRules = class AliceRules extends ChessRules {
+export class AliceRules extends ChessRules {
static get ALICE_PIECES() {
return {
s: "p",
import { ChessRules, PiPo, Move } from "@/base_rules";
-export const VariantRules = class Allmate1Rules extends ChessRules {
+export class Allmate1Rules extends ChessRules {
static get HasEnpassant() {
return false;
}
let attacked = {};
for (let i=0; i<V.size.x; i++) {
for (let j=0; j<V.size.y; j++) {
- if (this.getColor(i,j) == oppCol && this.isAttacked([i,j], [color]))
+ if (this.getColor(i,j) == oppCol && this.isAttacked([i,j], color))
attacked[i+"_"+j] = [i,j];
}
}
if (om.start.x == sq[0] && om.start.y == sq[1])
// Piece moved:
sq = [om.appear[0].x, om.appear[0].y];
- if (!this.isAttacked(sq, [color]))
+ if (!this.isAttacked(sq, color))
delete attacked[origSq[0]+"_"+origSq[1]];
});
V.UndoOnBoard(this.board, om);
if (em.start.x == attacked[0] && em.start.y == attacked[1])
// King moved:
sq = [em.appear[0].x, em.appear[0].y];
- if (!this.isAttacked(sq, [oppCol]))
+ if (!this.isAttacked(sq, oppCol))
res = true;
V.UndoOnBoard(this.board, em);
if (res)
import { ChessRules, PiPo, Move } from "@/base_rules";
-export const VariantRules = class Allmate2Rules extends ChessRules {
+export class Allmate2Rules extends ChessRules {
static get HasEnpassant() {
return false;
}
let attacked = {};
for (let i=0; i<V.size.x; i++) {
for (let j=0; j<V.size.y; j++) {
- if (this.getColor(i,j) == oppCol && this.isAttacked([i,j], [color]))
+ if (this.getColor(i,j) == oppCol && this.isAttacked([i,j], color))
attacked[i+"_"+j] = [i,j];
}
}
if (om.start.x == sq[0] && om.start.y == sq[1])
// Piece moved:
sq = [om.appear[0].x, om.appear[0].y];
- if (!this.isAttacked(sq, [color]))
+ if (!this.isAttacked(sq, color))
delete attacked[origSq[0]+"_"+origSq[1]];
});
V.UndoOnBoard(this.board, om);
if (em.start.x == attacked[0] && em.start.y == attacked[1])
// King moved:
sq = [em.appear[0].x, em.appear[0].y];
- if (!this.isAttacked(sq, [oppCol]))
+ if (!this.isAttacked(sq, oppCol))
res = true;
V.UndoOnBoard(this.board, em);
if (res)
import { ChessRules } from "@/base_rules";
+import { BerolinaRules } from "@/variants/Berolina";
import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-export const VariantRules = class Antiking1Rules extends ChessRules {
- static get HasEnpassant() {
- return false;
+export class Antiking1Rules extends BerolinaRules {
+ static get PawnSpecs() {
+ return Object.assign(
+ {},
+ ChessRules.PawnSpecs,
+ { twoSquares: false }
+ );
}
static get HasCastle() {
return moves;
}
- getPotentialPawnMoves([x, y]) {
- const color = this.turn;
- let moves = [];
- const [sizeX, sizeY] = [V.size.x, V.size.y];
- const shiftX = color == "w" ? -1 : 1;
- const startRank = color == "w" ? sizeX - 2 : 1;
- const lastRank = color == "w" ? 0 : sizeX - 1;
- const finalPieces =
- x + shiftX == lastRank ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN] : [V.PAWN];
-
- // One square diagonally
- for (let shiftY of [-1, 1]) {
- if (this.board[x + shiftX][y + shiftY] == V.EMPTY) {
- for (let piece of finalPieces) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
- c: color,
- p: piece
- })
- );
- }
- }
- }
- // Capture
- if (
- this.board[x + shiftX][y] != V.EMPTY &&
- this.canTake([x, y], [x + shiftX, y])
- ) {
- for (let piece of finalPieces)
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece })
- );
- }
-
- return moves;
- }
-
getPotentialAntikingMoves(sq) {
// The antiking moves like a king (only captured colors differ)
return this.getSlideNJumpMoves(
);
}
- isAttackedByPawn([x, y], color) {
- let pawnShift = (color == "w" ? 1 : -1);
- if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
- if (
- this.getPiece(x + pawnShift, y) == V.PAWN &&
- this.getColor(x + pawnShift, y) == color
- ) {
- return true;
- }
- }
- return false;
- }
-
isAttackedByKing([x, y], color) {
// Antiking is not attacked by king:
if (this.getPiece(x, y) == V.ANTIKING) return false;
static get SEARCH_DEPTH() {
return 2;
}
-
- // TODO: notation copied from Berolina
- getNotation(move) {
- const piece = this.getPiece(move.start.x, move.start.y);
- if (piece == V.PAWN) {
- // Pawn move
- const finalSquare = V.CoordsToSquare(move.end);
- let notation = "";
- if (move.vanish.length == 2)
- //capture
- notation = "Px" + finalSquare;
- else {
- // No capture: indicate the initial square for potential ambiguity
- const startSquare = V.CoordsToSquare(move.start);
- notation = startSquare + finalSquare;
- }
- if (move.appear[0].p != V.PAWN)
- // Promotion
- notation += "=" + move.appear[0].p.toUpperCase();
- return notation;
- }
- return super.getNotation(move); //all other pieces are orthodox
- }
};
import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-export const VariantRules = class Antiking2Rules extends ChessRules {
+export class Antiking2Rules extends ChessRules {
static get ANTIKING() {
return "a";
}
import { ChessRules } from "@/base_rules";
-export const VariantRules = class AntimatterRules extends ChessRules {
+export class AntimatterRules extends ChessRules {
getPotentialMovesFrom([x, y]) {
let moves = super.getPotentialMovesFrom([x, y]);
import { ChessRules } from "@/base_rules";
-export const VariantRules = class ArenaRules extends ChessRules {
+export class ArenaRules extends ChessRules {
static get HasFlags() {
return false;
}
+ static get PawnSpecs() {
+ return Object.assign(
+ {},
+ ChessRules.PawnSpecs,
+ { captureBackward: true }
+ );
+ }
+
static GenRandInitFen(randomness) {
return ChessRules.GenRandInitFen(randomness).slice(0, -6) + "-";
}
return moves;
}
- getPotentialPawnMoves([x, y]) {
- const color = this.turn;
- let moves = [];
- const [sizeX, sizeY] = [V.size.x, V.size.y];
- const shiftX = color == "w" ? -1 : 1;
- const startRank = color == "w" ? sizeX - 2 : 1;
-
- if (this.board[x + shiftX][y] == V.EMPTY) {
- // One square forward
- moves.push(this.getBasicMove([x, y], [x + shiftX, y]));
- // Next condition because pawns on 1st rank can generally jump
- if (
- x == startRank &&
- this.board[x + 2 * shiftX][y] == V.EMPTY
- ) {
- // Two squares jump
- moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
- }
- }
- // Captures: also possible backward
- for (let shiftY of [-1, 1]) {
- if (y + shiftY >= 0 && y + shiftY < sizeY) {
- for (let direction of [-1,1]) {
- if (
- this.board[x + direction][y + shiftY] != V.EMPTY &&
- this.canTake([x, y], [x + direction, y + shiftY])
- ) {
- moves.push(this.getBasicMove([x, y], [x + direction, y + shiftY]));
- }
- }
- }
- }
-
- // En passant
- const Lep = this.epSquares.length;
- const epSquare = this.epSquares[Lep - 1]; //always at least one element
- if (
- !!epSquare &&
- epSquare.x == x + shiftX &&
- Math.abs(epSquare.y - y) == 1
- ) {
- let enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]);
- enpassantMove.vanish.push({
- x: x,
- y: epSquare.y,
- p: "p",
- c: this.getColor(x, epSquare.y)
- });
- moves.push(enpassantMove);
- }
-
- return moves;
- }
-
getPotentialQueenMoves(sq) {
return this.getSlideNJumpMoves(
sq,
import { ChessRules, PiPo } from "@/base_rules";
-export const VariantRules = class AtomicRules extends ChessRules {
+export class AtomicRules extends ChessRules {
getEpSquare(moveOrSquare) {
if (typeof moveOrSquare !== "object" || moveOrSquare.appear.length > 0)
return super.getEpSquare(moveOrSquare);
import { ChessRules, PiPo, Move } from "@/base_rules";
import { ArrayFun } from "@/utils/array";
-import { randInt } from "@/utils/alea";
+import { shuffle } from "@/utils/alea";
-export const VariantRules = class BaroqueRules extends ChessRules {
+export class BaroqueRules extends ChessRules {
static get HasFlags() {
return false;
}
break;
}
- let positions = ArrayFun.range(8);
// Get random squares for every piece, totally freely
-
- let randIndex = randInt(8);
- const bishop1Pos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- randIndex = randInt(7);
- const bishop2Pos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- randIndex = randInt(6);
- const knight1Pos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- randIndex = randInt(5);
- const knight2Pos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- randIndex = randInt(4);
- const queenPos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- randIndex = randInt(3);
- const kingPos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- randIndex = randInt(2);
- const rookPos = positions[randIndex];
- positions.splice(randIndex, 1);
- const immobilizerPos = positions[0];
-
- pieces[c][bishop1Pos] = "b";
- pieces[c][bishop2Pos] = "b";
- pieces[c][knight1Pos] = "n";
- pieces[c][knight2Pos] = "n";
- pieces[c][queenPos] = "q";
- pieces[c][kingPos] = "k";
- pieces[c][rookPos] = "r";
- pieces[c][immobilizerPos] = "m";
+ let positions = shuffle(ArrayFun.range(8));
+ const composition = ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'm'];
+ for (let i = 0; i < 8; i++) pieces[c][positions[i]] = composition[i];
}
return (
pieces["b"].join("") +
import { ChessRules, PiPo, Move } from "@/base_rules";
-export const VariantRules = class BenedictRules extends ChessRules {
+export class BenedictRules extends ChessRules {
static get HasEnpassant() {
return false;
}
+ static get PawnSpecs() {
+ return Object.assign(
+ {},
+ ChessRules.PawnSpecs,
+ { canCapture: false }
+ );
+ }
+
// TODO(?): some duplicated code in 2 next functions
getSlideNJumpMoves([x, y], steps, oneStep) {
let moves = [];
return squares;
}
- getPotentialPawnMoves([x, y]) {
- const color = this.getColor(x, y);
- let moves = [];
- const sizeY = V.size.y;
- const shift = color == "w" ? -1 : 1;
- const startRank = color == "w" ? sizeY - 2 : 1;
- const firstRank = color == "w" ? sizeY - 1 : 0;
- const lastRank = color == "w" ? 0 : sizeY - 1;
-
- if (x + shift != lastRank) {
- // Normal moves
- if (this.board[x + shift][y] == V.EMPTY) {
- moves.push(this.getBasicMove([x, y], [x + shift, y]));
- if (
- [startRank, firstRank].includes(x) &&
- this.board[x + 2 * shift][y] == V.EMPTY
- ) {
- // Two squares jump
- moves.push(this.getBasicMove([x, y], [x + 2 * shift, y]));
- }
- }
- }
- else {
- // Promotion
- let promotionPieces = [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN];
- promotionPieces.forEach(p => {
- // Normal move
- if (this.board[x + shift][y] == V.EMPTY)
- moves.push(
- this.getBasicMove([x, y], [x + shift, y], { c: color, p: p })
- );
- });
- }
-
- // No en passant here
-
- return moves;
- }
-
- // No "under check" verifications:
- getCastleMoves([x, y]) {
- const c = this.getColor(x, y);
- if (x != (c == "w" ? V.size.x - 1 : 0) || y != this.INIT_COL_KING[c])
- return []; //x isn't first rank, or king has moved (shortcut)
-
- // Castling ?
- const oppCol = V.GetOppCol(c);
- let moves = [];
- let i = 0;
- // King, then rook:
- const finalSquares = [
- [2, 3],
- [V.size.y - 2, V.size.y - 3]
- ];
- castlingCheck: for (
- let castleSide = 0;
- castleSide < 2;
- castleSide++ //large, then small
- ) {
- if (this.castleFlags[c][castleSide] >= 8) continue;
- // If this code is reached, rooks and king are on initial position
-
- const rookPos = this.castleFlags[c][castleSide];
- if (this.getColor(x, rookPos) != c)
- // Rook is here but changed color
- continue;
-
- // Nothing on the path of the king ?
- const finDist = finalSquares[castleSide][0] - y;
- let step = finDist / Math.max(1, Math.abs(finDist));
- for (let i = y; i != finalSquares[castleSide][0]; i += step) {
- if (
- this.board[x][i] != V.EMPTY &&
- // NOTE: next check is enough, because of chessboard constraints
- (this.getColor(x, i) != c ||
- ![V.KING, V.ROOK].includes(this.getPiece(x, i)))
- ) {
- continue castlingCheck;
- }
- }
-
- // Nothing on the path to the rook?
- step = castleSide == 0 ? -1 : 1;
- for (i = y + step; i != rookPos; i += step) {
- if (this.board[x][i] != V.EMPTY) continue castlingCheck;
- }
-
- // Nothing on final squares, except maybe king and castling rook?
- for (i = 0; i < 2; i++) {
- if (
- this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
- this.getPiece(x, finalSquares[castleSide][i]) != V.KING &&
- finalSquares[castleSide][i] != rookPos
- ) {
- continue castlingCheck;
- }
- }
-
- // If this code is reached, castle is valid
- moves.push(
- new Move({
- appear: [
- new PiPo({ x: x, y: finalSquares[castleSide][0], p: V.KING, c: c }),
- new PiPo({ x: x, y: finalSquares[castleSide][1], p: V.ROOK, c: c })
- ],
- vanish: [
- new PiPo({ x: x, y: y, p: V.KING, c: c }),
- new PiPo({ x: x, y: rookPos, p: V.ROOK, c: c })
- ],
- end:
- Math.abs(y - rookPos) <= 2
- ? { x: x, y: rookPos }
- : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) }
- })
- );
- }
-
- return moves;
- }
-
// TODO: appear/vanish description of a move is too verbose for Benedict.
// => Would need a new "flipped" array, to be passed in Game.vue...
getPotentialMovesFrom([x, y]) {
return moves;
}
+ // Since it's used just for the king, and there are no captures:
+ isAttacked(sq, color) {
+ return false;
+ }
+
// No notion of check here:
getCheckSquares() {
return [];
import { ChessRules } from "@/base_rules";
-export const VariantRules = class BerolinaRules extends ChessRules {
+export class BerolinaRules extends ChessRules {
// En-passant after 2-sq jump
getEpSquare(moveOrSquare) {
if (!moveOrSquare) return undefined;
);
}
if (
+ V.PawnSpecs.twoSquares &&
x == startRank &&
y + 2 * shiftY >= 0 &&
y + 2 * shiftY < sizeY &&
);
}
- // En passant
- const Lep = this.epSquares.length;
- const epSquare = this.epSquares[Lep - 1]; //always at least one element
- if (
- !!epSquare &&
- epSquare[0].x == x + shiftX &&
- epSquare[0].y == y
- ) {
- let enpassantMove = this.getBasicMove([x, y], [x + shiftX, y]);
- enpassantMove.vanish.push({
- x: x,
- y: epSquare[1],
- p: "p",
- c: this.getColor(x, epSquare[1])
- });
- moves.push(enpassantMove);
+ // Next condition so that other variants could inherit from this class
+ if (V.PawnSpecs.enPassant) {
+ // En passant
+ const Lep = this.epSquares.length;
+ const epSquare = this.epSquares[Lep - 1]; //always at least one element
+ if (
+ !!epSquare &&
+ epSquare[0].x == x + shiftX &&
+ epSquare[0].y == y
+ ) {
+ let enpassantMove = this.getBasicMove([x, y], [x + shiftX, y]);
+ enpassantMove.vanish.push({
+ x: x,
+ y: epSquare[1],
+ p: "p",
+ c: this.getColor(x, epSquare[1])
+ });
+ moves.push(enpassantMove);
+ }
}
return moves;
return false;
}
+ static get SEARCH_DEPTH() {
+ return 2;
+ }
+
getNotation(move) {
const piece = this.getPiece(move.start.x, move.start.y);
if (piece == V.PAWN) {
import { ChessRules } from "@/base_rules";
-import { ArrayFun } from "@/utils/array";
-import { randInt } from "@/utils/alea";
-export const VariantRules = class LosersRules extends ChessRules {
+export class CaptureRules extends ChessRules {
// Trim all non-capturing moves
static KeepCaptures(moves) {
return moves.filter(m => m.vanish.length == 2 && m.appear.length == 1);
if (
this.board[i][j] != V.EMPTY &&
this.getColor(i, j) != oppCol &&
- this.getPotentialMovesFrom([i, j]).some(m =>
- // Warning: duscard castle moves
+ this.filterValid(this.getPotentialMovesFrom([i, j])).some(m =>
+ // Warning: discard castle moves
m.vanish.length == 2 && m.appear.length == 1)
) {
return true;
import { ChessRules, Move, PiPo } from "@/base_rules";
-export const VariantRules = class CheckeredRules extends ChessRules {
+export class CheckeredRules extends ChessRules {
static board2fen(b) {
const checkered_codes = {
p: "s",
getPotentialMovesFrom([x, y]) {
let standardMoves = super.getPotentialMovesFrom([x, y]);
const lastRank = this.turn == "w" ? 0 : 7;
- // King has to be treated differently (for castles)
+ // King is treated differently: it never turn checkered
if (this.getPiece(x, y) == V.KING) return standardMoves;
let moves = [];
standardMoves.forEach(m => {
if (
Math.abs(m.end.x - m.start.x) == 2 &&
!this.pawnFlags[this.turn][m.start.y]
- )
+ ) {
return; //skip forbidden 2-squares jumps
+ }
if (
this.board[m.end.x][m.end.y] == V.EMPTY &&
m.vanish.length == 2 &&
return; //checkered pawns cannot take en-passant
}
}
- if (m.vanish.length == 1) moves.push(m);
- // No capture
+ if (m.vanish.length == 1)
+ // No capture
+ moves.push(m);
else {
// A capture occured (m.vanish.length == 2)
m.appear[0].c = "c";
moves.push(m);
if (
- m.appear[0].p != m.vanish[1].p && //avoid promotions (already treated):
+ // Avoid promotions (already treated):
+ m.appear[0].p != m.vanish[1].p &&
(m.vanish[0].p != V.PAWN || m.end.x != lastRank)
) {
// Add transformation into captured piece
}
getPotentialPawnMoves([x, y]) {
- const color = this.turn;
- let moves = [];
- const [sizeX, sizeY] = [V.size.x, V.size.y];
- const shiftX = color == "w" ? -1 : 1;
- const startRank = color == "w" ? sizeX - 2 : 1;
- const lastRank = color == "w" ? 0 : sizeX - 1;
- const pawnColor = this.getColor(x, y); //can be checkered
-
- const finalPieces =
- x + shiftX == lastRank
- ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]
- : [V.PAWN];
- if (this.board[x + shiftX][y] == V.EMPTY) {
- // One square forward
- for (let piece of finalPieces) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y], {
- c: pawnColor,
- p: piece
- })
- );
- }
- if (
- x == startRank &&
- this.board[x + 2 * shiftX][y] == V.EMPTY
- ) {
- // Two squares jump
- moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
- }
- }
- // Captures
- for (let shiftY of [-1, 1]) {
- if (
- y + shiftY >= 0 &&
- y + shiftY < sizeY &&
- this.board[x + shiftX][y + shiftY] != V.EMPTY &&
- this.canTake([x, y], [x + shiftX, y + shiftY])
- ) {
- for (let piece of finalPieces) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
- c: pawnColor,
- p: piece
- })
- );
- }
- }
- }
-
- // En passant
- const Lep = this.epSquares.length;
- const epSquare = this.epSquares[Lep - 1]; //always at least one element
- if (
- !!epSquare &&
- epSquare.x == x + shiftX &&
- Math.abs(epSquare.y - y) == 1
- ) {
- let enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]);
- enpassantMove.vanish.push({
- x: x,
- y: epSquare.y,
- p: "p",
- c: this.getColor(x, epSquare.y)
+ let moves = super.getPotentialPawnMoves([x, y]);
+ // Post-process: set right color for checkered moves
+ if (this.getColor(x, y) == 'c')
+ moves.forEach(m => {
+ m.appear[0].c = 'c'; //may be done twice if capture
+ m.vanish[0].c = 'c';
});
- moves.push(enpassantMove);
- }
-
- return moves;
- }
-
- // Same as in base_rules but with an array given to isAttacked:
- getCastleMoves([x, y]) {
- const c = this.getColor(x, y);
- if (x != (c == "w" ? V.size.x - 1 : 0) || y != this.INIT_COL_KING[c])
- return []; //x isn't first rank, or king has moved (shortcut)
-
- // Castling ?
- const oppCol = V.GetOppCol(c);
- let moves = [];
- let i = 0;
- // King, then rook:
- const finalSquares = [
- [2, 3],
- [V.size.y - 2, V.size.y - 3]
- ];
- castlingCheck: for (
- let castleSide = 0;
- castleSide < 2;
- castleSide++ //large, then small
- ) {
- if (this.castleFlags[c][castleSide] >= V.size.y) continue;
- // If this code is reached, rooks and king are on initial position
-
- // Nothing on the path of the king ? (and no checks)
- const finDist = finalSquares[castleSide][0] - y;
- let step = finDist / Math.max(1, Math.abs(finDist));
- i = y;
- do {
- if (
- this.isAttacked([x, i], [oppCol]) ||
- (this.board[x][i] != V.EMPTY &&
- // NOTE: next check is enough, because of chessboard constraints
- (this.getColor(x, i) != c ||
- ![V.KING, V.ROOK].includes(this.getPiece(x, i))))
- ) {
- continue castlingCheck;
- }
- i += step;
- } while (i != finalSquares[castleSide][0]);
-
- // Nothing on the path to the rook?
- step = castleSide == 0 ? -1 : 1;
- const rookPos = this.castleFlags[c][castleSide];
- for (i = y + step; i != rookPos; i += step) {
- if (this.board[x][i] != V.EMPTY) continue castlingCheck;
- }
-
- // Nothing on final squares, except maybe king and castling rook?
- for (i = 0; i < 2; i++) {
- if (
- this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
- this.getPiece(x, finalSquares[castleSide][i]) != V.KING &&
- finalSquares[castleSide][i] != rookPos
- ) {
- continue castlingCheck;
- }
- }
-
- // If this code is reached, castle is valid
- moves.push(
- new Move({
- appear: [
- new PiPo({ x: x, y: finalSquares[castleSide][0], p: V.KING, c: c }),
- new PiPo({ x: x, y: finalSquares[castleSide][1], p: V.ROOK, c: c })
- ],
- vanish: [
- new PiPo({ x: x, y: y, p: V.KING, c: c }),
- new PiPo({ x: x, y: rookPos, p: V.ROOK, c: c })
- ],
- end:
- Math.abs(y - rookPos) <= 2
- ? { x: x, y: rookPos }
- : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) }
- })
- );
- }
-
return moves;
}
// colors: array, generally 'w' and 'c' or 'b' and 'c'
isAttacked(sq, colors) {
+ if (!Array.isArray(colors)) colors = [colors];
return (
this.isAttackedByPawn(sq, colors) ||
this.isAttackedByRook(sq, colors) ||
import { ChessRules } from "@/base_rules";
-export const VariantRules = class Chess960Rules extends ChessRules {
+export class Chess960Rules extends ChessRules {
// Standard rules
};
import { ChessRules, PiPo, Move } from "@/base_rules";
import { ArrayFun } from "@/utils/array";
-import { randInt, shuffle } from "@/utils/alea";
+import { shuffle } from "@/utils/alea";
-export const VariantRules = class CircularRules extends ChessRules {
+export class CircularRules extends ChessRules {
static get HasCastle() {
return false;
}
return "8/8/pppppppp/rnbqkbnr/8/8/PPPPPPPP/RNBQKBNR w 0 1111111111111111";
let pieces = { w: new Array(8), b: new Array(8) };
- // Shuffle pieces on first and fifth rank
+ // Shuffle pieces on first and last rank
for (let c of ["w", "b"]) {
if (c == 'b' && randomness == 1) {
pieces['b'] = pieces['w'];
break;
}
- let positions = ArrayFun.range(8);
-
- // Get random squares for bishops
- let randIndex = 2 * randInt(4);
- const bishop1Pos = positions[randIndex];
- // The second bishop must be on a square of different color
- let randIndex_tmp = 2 * randInt(4) + 1;
- const bishop2Pos = positions[randIndex_tmp];
- // Remove chosen squares
- positions.splice(Math.max(randIndex, randIndex_tmp), 1);
- positions.splice(Math.min(randIndex, randIndex_tmp), 1);
-
- // Get random squares for knights
- randIndex = randInt(6);
- const knight1Pos = positions[randIndex];
- positions.splice(randIndex, 1);
- randIndex = randInt(5);
- const knight2Pos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- // Get random square for queen
- randIndex = randInt(4);
- const queenPos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- // Rooks and king positions are now fixed,
- // because of the ordering rook-king-rook
- const rook1Pos = positions[0];
- const kingPos = positions[1];
- const rook2Pos = positions[2];
-
- // Finally put the shuffled pieces in the board array
- 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";
+ // Get random squares for every piece, totally freely
+ let positions = shuffle(ArrayFun.range(8));
+ const composition = ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q'];
+ const rem2 = positions[0] % 2;
+ if (rem2 == positions[1] % 2) {
+ // Fix bishops (on different colors)
+ for (let i=2; i<8; i++) {
+ if (positions[i] % 2 != rem2)
+ [positions[1], positions[i]] = [positions[i], positions[1]];
+ }
+ }
+ for (let i = 0; i < 8; i++) pieces[c][positions[i]] = composition[i];
}
return (
"8/8/pppppppp/" +
import { ChessRules, PiPo, Move } from "@/base_rules";
import { ArrayFun } from "@/utils/array";
-export const VariantRules = class CrazyhouseRules extends ChessRules {
+export class CrazyhouseRules extends ChessRules {
static IsGoodFen(fen) {
if (!ChessRules.IsGoodFen(fen)) return false;
const fenParsed = V.ParseFen(fen);
import { ArrayFun } from "@/utils/array";
import { randInt, shuffle } from "@/utils/alea";
-export const VariantRules = class CylinderRules extends ChessRules {
+export class CylinderRules extends ChessRules {
// Output basically x % 8 (circular board)
static ComputeY(y) {
let res = y % V.size.y;
import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-export const VariantRules = class DarkRules extends ChessRules {
+export class DarkRules extends ChessRules {
// Analyse in Dark mode makes no sense
static get CanAnalyze() {
return false;
import { randInt } from "@/utils/alea";
import { ChessRules, PiPo, Move } from "@/base_rules";
-export const VariantRules = class EightpiecesRules extends ChessRules {
+export class EightpiecesRules extends ChessRules {
static get JAILER() {
return "j";
}
import { ChessRules, PiPo, Move } from "@/base_rules";
-export const VariantRules = class EnpassantRules extends ChessRules {
-
+export class EnpassantRules extends ChessRules {
static IsGoodEnpassant(enpassant) {
if (enpassant != "-") {
const squares = enpassant.split(",");
return moves;
}
- // TODO: this getPotentialPawnMovesFrom() is mostly duplicated:
- // it could be split in "capture", "promotion", "enpassant"...
- getPotentialPawnMoves([x, y]) {
- const color = this.turn;
- let moves = [];
- const [sizeX, sizeY] = [V.size.x, V.size.y];
- const shiftX = color == "w" ? -1 : 1;
- const startRank = color == "w" ? sizeX - 2 : 1;
- const lastRank = color == "w" ? 0 : sizeX - 1;
-
- const finalPieces =
- x + shiftX == lastRank
- ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]
- : [V.PAWN];
- // One square forward
- if (this.board[x + shiftX][y] == V.EMPTY) {
- for (let piece of finalPieces) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y], {
- c: color,
- p: piece
- })
- );
- }
- if (
- x == startRank &&
- this.board[x + 2 * shiftX][y] == V.EMPTY
- ) {
- // Two squares jump
- moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
- }
- }
- // Captures
- for (let shiftY of [-1, 1]) {
- if (
- y + shiftY >= 0 &&
- y + shiftY < sizeY &&
- this.board[x + shiftX][y + shiftY] != V.EMPTY &&
- this.canTake([x, y], [x + shiftX, y + shiftY])
- ) {
- for (let piece of finalPieces) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
- c: color,
- p: piece
- })
- );
- }
- }
- }
-
- // En passant
+ getEnpassantCaptures([x, y], shiftX) {
const Lep = this.epSquares.length;
const squares = this.epSquares[Lep - 1];
+ let moves = [];
if (!!squares) {
const S = squares.length;
const taken = squares[S-1];
}
});
}
-
return moves;
}
import { ChessRules } from "@/base_rules";
-export const VariantRules = class ExtinctionRules extends ChessRules {
+export class ExtinctionRules extends ChessRules {
+ static get PawnSpecs() {
+ return Object.assign(
+ {},
+ ChessRules.PawnSpecs,
+ { promotions: ChessRules.PawnSpecs.promotions.concat([V.KING]) }
+ );
+ }
+
static IsGoodPosition(position) {
if (!ChessRules.IsGoodPosition(position))
return false;
};
}
- getPotentialPawnMoves([x, y]) {
- let moves = super.getPotentialPawnMoves([x, y]);
- // Add potential promotions into king
- const color = this.turn;
- const shift = color == "w" ? -1 : 1;
- const lastRank = color == "w" ? 0 : V.size.x - 1;
-
- if (x + shift == lastRank) {
- // Normal move
- if (this.board[x + shift][y] == V.EMPTY)
- moves.push(
- this.getBasicMove([x, y], [x + shift, y], { c: color, p: V.KING })
- );
- // Captures
- if (
- y > 0 &&
- this.board[x + shift][y - 1] != V.EMPTY &&
- this.canTake([x, y], [x + shift, y - 1])
- ) {
- moves.push(
- this.getBasicMove([x, y], [x + shift, y - 1], { c: color, p: V.KING })
- );
- }
- if (
- y < V.size.y - 1 &&
- this.board[x + shift][y + 1] != V.EMPTY &&
- this.canTake([x, y], [x + shift, y + 1])
- ) {
- moves.push(
- this.getBasicMove([x, y], [x + shift, y + 1], { c: color, p: V.KING })
- );
- }
- }
-
- return moves;
- }
-
// TODO: verify this assertion
atLeastOneMove() {
return true; //always at least one possible move
// NOTE: initial setup differs from the original; see
// https://www.chessvariants.com/large.dir/freeling.html
-export const VariantRules = class GrandRules extends ChessRules {
+export class GrandRules extends ChessRules {
static IsGoodFen(fen) {
if (!ChessRules.IsGoodFen(fen)) return false;
const fenParsed = V.ParseFen(fen);
for (let epsq of epSquare) {
// TODO: some redundant checks
if (epsq.x == x + shiftX && Math.abs(epsq.y - y) == 1) {
- var enpassantMove = this.getBasicMove([x, y], [epsq.x, epsq.y]);
+ let enpassantMove = this.getBasicMove([x, y], [epsq.x, epsq.y]);
// WARNING: the captured pawn may be diagonally behind us,
// if it's a 3-squares jump and we take on 1st passing square
const px = this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX;
import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-export const VariantRules = class GrasshopperRules extends ChessRules {
+export class GrasshopperRules extends ChessRules {
static get HasEnpassant() {
return false;
}
+ static get PawnSpecs() {
+ return Object.assign(
+ {},
+ ChessRules.PawnSpecs,
+ {
+ twoSquares: false,
+ promotions: ChessRules.PawnSpecs.promotions.concat([V.GRASSHOPPER])
+ }
+ );
+ }
+
static get GRASSHOPPER() {
return "g";
}
import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-export const VariantRules = class HiddenRules extends ChessRules {
+export class HiddenRules extends ChessRules {
static get HasFlags() {
return false;
}
import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-export const VariantRules = class HiddenqueenRules extends ChessRules {
+export class HiddenqueenRules extends ChessRules {
// Analyse in Hiddenqueen mode makes no sense
static get CanAnalyze() {
return false;
return super.getPotentialMovesFrom([x, y]);
}
- // TODO: find a more general way to describe pawn movements to avoid
- // re-writing almost the same function for several variants.
getPotentialPawnMoves([x, y]) {
- const color = this.turn;
const piece = this.getPiece(x, y);
- let moves = [];
- const [sizeX, sizeY] = [V.size.x, V.size.y];
- const shiftX = color == "w" ? -1 : 1;
- const startRank = color == "w" ? sizeX - 2 : 1;
- const lastRank = color == "w" ? 0 : sizeX - 1;
-
- const finalPieces =
- x + shiftX == lastRank
- ? piece == V.PAWN
- ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]
- : [V.QUEEN] //hidden queen revealed
- : [piece]; //V.PAWN
- if (this.board[x + shiftX][y] == V.EMPTY) {
- // One square forward
- for (let p of finalPieces) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y], {
- c: color,
- p: p
- })
- );
- }
- if (
- x == startRank &&
- this.board[x + 2 * shiftX][y] == V.EMPTY
- ) {
- // Two squares jump
- moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
- }
- }
- // Captures
- for (let shiftY of [-1, 1]) {
- if (
- y + shiftY >= 0 &&
- y + shiftY < sizeY &&
- this.board[x + shiftX][y + shiftY] != V.EMPTY &&
- this.canTake([x, y], [x + shiftX, y + shiftY])
- ) {
- for (let p of finalPieces) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
- c: color,
- p: p
- })
- );
- }
- }
- }
-
- // En passant
- const Lep = this.epSquares.length;
- const epSquare = this.epSquares[Lep - 1]; //always at least one element
- if (
- !!epSquare &&
- epSquare.x == x + shiftX &&
- Math.abs(epSquare.y - y) == 1
- ) {
- let enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]);
- enpassantMove.vanish.push({
- x: x,
- y: epSquare.y,
- p: "p",
- c: this.getColor(x, epSquare.y)
- });
- moves.push(enpassantMove);
- }
-
- return moves;
+ const promotions =
+ piece == V.PAWN
+ ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]
+ : [V.QUEEN]; //hidden queen revealed
+ return super.getPotentialPawnMoves([x, y], promotions);
}
getPossibleMovesFrom(sq) {
import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-export const VariantRules = class KnightmateRules extends ChessRules {
+export class KnightmateRules extends ChessRules {
static get COMMONER() {
return "c";
}
import { ChessRules } from "@/base_rules";
-export const VariantRules = class Knightrelay1Rules extends ChessRules {
+export class Knightrelay1Rules extends ChessRules {
static get HasEnpassant() {
return false;
}
import { ChessRules } from "@/base_rules";
-export const VariantRules = class Knightrelay2Rules extends ChessRules {
+export class Knightrelay2Rules extends ChessRules {
getPotentialMovesFrom([x, y]) {
let moves = super.getPotentialMovesFrom([x, y]);
import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-export const VariantRules = class LosersRules extends ChessRules {
+export class LosersRules extends ChessRules {
// Trim all non-capturing moves
static KeepCaptures(moves) {
return moves.filter(m => m.vanish.length == 2);
import { ChessRules, PiPo } from "@/base_rules";
-export const VariantRules = class MagneticRules extends ChessRules {
+export class MagneticRules extends ChessRules {
static get HasEnpassant() {
return false;
}
import { ChessRules } from "@/base_rules";
import { randInt } from "@/utils/alea";
-export const VariantRules = class MarseilleRules extends ChessRules {
+export class MarseilleRules extends ChessRules {
static IsGoodEnpassant(enpassant) {
const squares = enpassant.split(",");
if (squares.length > 2) return false;
this.subTurn = fullTurn[1] || 1;
}
- getPotentialPawnMoves([x, y]) {
- const color = this.turn;
+ getEnpassantCaptures([x, y], shiftX) {
let moves = [];
- const [sizeX, sizeY] = [V.size.x, V.size.y];
- const shiftX = color == "w" ? -1 : 1;
- const firstRank = color == "w" ? sizeX - 1 : 0;
- const startRank = color == "w" ? sizeX - 2 : 1;
- const lastRank = color == "w" ? 0 : sizeX - 1;
- const finalPieces =
- x + shiftX == lastRank ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN] : [V.PAWN];
-
- // One square forward
- if (this.board[x + shiftX][y] == V.EMPTY) {
- for (let piece of finalPieces) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece })
- );
- }
- // Next condition because pawns on 1st rank can generally jump
- if (
- [startRank, firstRank].includes(x) &&
- this.board[x + 2 * shiftX][y] == V.EMPTY
- ) {
- // Two squares jump
- moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
- }
- }
- // Captures
- for (let shiftY of [-1, 1]) {
- if (
- y + shiftY >= 0 &&
- y + shiftY < sizeY &&
- this.board[x + shiftX][y + shiftY] != V.EMPTY &&
- this.canTake([x, y], [x + shiftX, y + shiftY])
- ) {
- for (let piece of finalPieces) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
- c: color,
- p: piece
- })
- );
- }
- }
- }
-
// En passant: always OK if subturn 1,
// OK on subturn 2 only if enPassant was played at subturn 1
// (and if there are two e.p. squares available).
if (sq) epSqs.push(sq);
});
if (epSqs.length == 0) return moves;
- const oppCol = V.GetOppCol(color);
+ const oppCol = V.GetOppCol(this.getColor(x, y));
for (let sq of epSqs) {
if (
this.subTurn == 1 ||
}
}
}
-
return moves;
}
import { ChessRules } from "@/base_rules";
-export const VariantRules = class RacingkingsRules extends ChessRules {
+export class RacingkingsRules extends ChessRules {
static get HasFlags() {
return false;
}
import { ChessRules, PiPo, Move } from "@/base_rules";
import { ArrayFun } from "@/utils/array";
-export const VariantRules = class RecycleRules extends ChessRules {
+export class RecycleRules extends ChessRules {
+ static get PawnSpecs() {
+ return Object.assign(
+ {},
+ ChessRules.PawnSpecs,
+ { promotions: [V.PAWN] } //in fact, none
+ );
+ }
+
static IsGoodFen(fen) {
if (!ChessRules.IsGoodFen(fen)) return false;
const fenParsed = V.ParseFen(fen);
}
getPotentialPawnMoves([x, y]) {
+ let moves = super.getPotentialPawnMoves([x, y]);
+ // Remove pawns on 8th rank ("fallen"):
const color = this.turn;
- let moves = [];
- const [sizeX, sizeY] = [V.size.x, V.size.y];
- const shiftX = color == "w" ? -1 : 1;
- const startRank = color == "w" ? sizeX - 2 : 1;
- const lastRank = color == "w" ? 0 : sizeX - 1;
-
- // One square forward
- if (this.board[x + shiftX][y] == V.EMPTY) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y])
- );
- // Next condition because pawns on 1st rank can generally jump
- if (
- x == startRank &&
- this.board[x + 2 * shiftX][y] == V.EMPTY
- ) {
- // Two squares jump
- moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
- }
- }
- // Captures
- for (let shiftY of [-1, 1]) {
- if (
- y + shiftY >= 0 &&
- y + shiftY < sizeY &&
- this.board[x + shiftX][y + shiftY] != V.EMPTY &&
- this.canTake([x, y], [x + shiftX, y + shiftY])
- ) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y + shiftY])
- );
- }
- }
-
- // En passant
- const Lep = this.epSquares.length;
- const epSquare = this.epSquares[Lep - 1]; //always at least one element
- if (
- !!epSquare &&
- epSquare.x == x + shiftX &&
- Math.abs(epSquare.y - y) == 1
- ) {
- let enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]);
- enpassantMove.vanish.push({
- x: x,
- y: epSquare.y,
- p: "p",
- c: this.getColor(x, epSquare.y)
- });
- moves.push(enpassantMove);
- }
-
- // Post-processing: remove falling pawns
- if (x + shiftX == lastRank) {
- moves.forEach(m => {
- m.appear.pop();
- });
- }
-
+ const lastRank = (color == "w" ? 0 : V.size.x - 1);
+ moves.forEach(m => {
+ if (m.appear[0].x == lastRank) m.appear.pop();
+ });
return moves;
}
return this.getPiece(x2, y2) != V.KING;
}
- postPlay(move) {
- super.postPlay(move);
+ prePlay(move) {
+ super.prePlay(move);
if (move.vanish.length == 2 && move.appear.length == 2) return; //skip castle
- const color = move.appear[0].c;
+ const color = this.turn;
if (move.vanish.length == 0) this.reserve[color][move.appear[0].p]--;
else if (move.vanish.length == 2 && move.vanish[1].c == color)
// Self-capture
import { ChessRules, PiPo, Move } from "@/base_rules";
-export const VariantRules = class RifleRules extends ChessRules {
+export class RifleRules extends ChessRules {
getEpSquare(moveOrSquare) {
if (typeof moveOrSquare !== "object" || moveOrSquare.appear.length > 0)
return super.getEpSquare(moveOrSquare);
return mv;
}
- getPotentialPawnMoves([x, y]) {
- const color = this.turn;
+ getEnpassantCaptures([x, y], shiftX) {
let moves = [];
- const [sizeX, sizeY] = [V.size.x, V.size.y];
- const shiftX = color == "w" ? -1 : 1;
- const startRank = color == "w" ? sizeX - 2 : 1;
- const lastRank = color == "w" ? 0 : sizeX - 1;
-
- const finalPieces =
- x + shiftX == lastRank
- ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]
- : [V.PAWN];
- if (this.board[x + shiftX][y] == V.EMPTY) {
- for (let piece of finalPieces) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y], {
- c: color,
- p: piece
- })
- );
- }
- if (
- x == startRank &&
- this.board[x + 2 * shiftX][y] == V.EMPTY
- ) {
- moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
- }
- }
- // Captures
- for (let shiftY of [-1, 1]) {
- if (
- y + shiftY >= 0 &&
- y + shiftY < sizeY &&
- this.board[x + shiftX][y + shiftY] != V.EMPTY &&
- this.canTake([x, y], [x + shiftX, y + shiftY])
- ) {
- for (let piece of finalPieces) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
- c: color,
- p: piece
- })
- );
- }
- }
- }
-
- // En passant
const Lep = this.epSquares.length;
const epSquare = this.epSquares[Lep - 1]; //always at least one element
if (
});
moves.push(enpassantMove);
}
-
return moves;
}
+
+ static get SEARCH_DEPTH() {
+ return 2;
+ }
};
import { ArrayFun } from "@/utils/array";
import { randInt, shuffle } from "@/utils/alea";
-export const VariantRules = class RoyalraceRules extends ChessRules {
+export class RoyalraceRules extends ChessRules {
static get HasFlags() {
return false;
}
import { ChessRules } from "@/base_rules";
-export const VariantRules = class ShatranjRules extends ChessRules {
+export class ShatranjRules extends ChessRules {
static get HasFlags() {
return false;
}
return false;
}
+ static get PawnSpecs() {
+ return Object.assign(
+ {},
+ ChessRules.PawnSpecs,
+ {
+ twoSquares: false,
+ promotions: [V.QUEEN]
+ }
+ );
+ }
+
static get ElephantSteps() {
return [
[-2, -2],
return ChessRules.GenRandInitFen(randomness).slice(0, -7);
}
- getPotentialPawnMoves([x, y]) {
- const color = this.turn;
- let moves = [];
- const [sizeX, sizeY] = [V.size.x, V.size.y];
- const shiftX = color == "w" ? -1 : 1;
- const startRank = color == "w" ? sizeX - 2 : 1;
- const lastRank = color == "w" ? 0 : sizeX - 1;
- // Promotion in minister (queen) only:
- const finalPiece = x + shiftX == lastRank ? V.QUEEN : V.PAWN;
-
- if (this.board[x + shiftX][y] == V.EMPTY) {
- // One square forward
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y], {
- c: color,
- p: finalPiece
- })
- );
- }
- // Captures
- for (let shiftY of [-1, 1]) {
- if (
- y + shiftY >= 0 &&
- y + shiftY < sizeY &&
- this.board[x + shiftX][y + shiftY] != V.EMPTY &&
- this.canTake([x, y], [x + shiftX, y + shiftY])
- ) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
- c: color,
- p: finalPiece
- })
- );
- }
- }
-
- return moves;
- }
-
getPotentialBishopMoves(sq) {
let moves = this.getSlideNJumpMoves(sq, V.ElephantSteps, "oneStep");
// Complete with "repositioning moves": like a queen, without capture
import { ChessRules, PiPo, Move } from "@/base_rules";
-export const VariantRules = class SuctionRules extends ChessRules {
+export class SuctionRules extends ChessRules {
static get HasFlags() {
return false;
}
return mv;
}
- getPotentialPawnMoves([x, y]) {
- const color = this.turn;
+ getEnpassantCaptures([x, y], shiftX) {
let moves = [];
- const [sizeX, sizeY] = [V.size.x, V.size.y];
- const shiftX = color == "w" ? -1 : 1;
- const startRank = color == "w" ? sizeX - 2 : 1;
- const firstRank = color == "w" ? sizeX - 1 : 0;
-
- if (x + shiftX >= 0 && x + shiftX < sizeX) {
- // One square forward
- if (this.board[x + shiftX][y] == V.EMPTY) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y], {
- c: color,
- p: "p"
- })
- );
- if (
- [startRank,firstRank].includes(x) &&
- this.board[x + 2 * shiftX][y] == V.EMPTY
- ) {
- // Two squares jump
- moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
- }
- }
- // Swaps
- for (let shiftY of [-1, 1]) {
- if (
- y + shiftY >= 0 &&
- y + shiftY < sizeY &&
- this.board[x + shiftX][y + shiftY] != V.EMPTY &&
- this.canTake([x, y], [x + shiftX, y + shiftY])
- ) {
- moves.push(
- this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
- c: color,
- p: "p"
- })
- );
- }
- }
- }
-
- // En passant
const Lep = this.epSquares.length;
const epSquare = this.epSquares[Lep - 1]; //always at least one element
if (
});
moves.push(enpassantMove);
}
-
return moves;
}
import { ChessRules } from "@/base_rules";
import { ArrayFun } from "@/utils/array";
-import { randInt } from "@/utils/alea";
+import { shuffle } from "@/utils/alea";
-export const VariantRules = class SuicideRules extends ChessRules {
+export class SuicideRules extends ChessRules {
static get HasFlags() {
return false;
}
return false;
}
- getPotentialPawnMoves([x, y]) {
- let moves = super.getPotentialPawnMoves([x, y]);
-
- // Complete with promotion(s) into king, if possible
- const color = this.turn;
- const shift = color == "w" ? -1 : 1;
- const lastRank = color == "w" ? 0 : V.size.x - 1;
- if (x + shift == lastRank) {
- // Normal move
- if (this.board[x + shift][y] == V.EMPTY)
- moves.push(
- this.getBasicMove([x, y], [x + shift, y], { c: color, p: V.KING })
- );
- // Captures
- if (
- y > 0 &&
- this.canTake([x, y], [x + shift, y - 1]) &&
- this.board[x + shift][y - 1] != V.EMPTY
- ) {
- moves.push(
- this.getBasicMove([x, y], [x + shift, y - 1], { c: color, p: V.KING })
- );
- }
- if (
- y < V.size.y - 1 &&
- this.canTake([x, y], [x + shift, y + 1]) &&
- this.board[x + shift][y + 1] != V.EMPTY
- ) {
- moves.push(
- this.getBasicMove([x, y], [x + shift, y + 1], { c: color, p: V.KING })
- );
- }
- }
-
- return moves;
+ static get PawnSpecs() {
+ return Object.assign(
+ {},
+ ChessRules.PawnSpecs,
+ { promotions: ChessRules.PawnSpecs.promotions.concat([V.KING]) }
+ );
}
// Trim all non-capturing moves (not the most efficient, but easy)
break;
}
- let positions = ArrayFun.range(8);
-
- // Get random squares for bishops
- let randIndex = 2 * randInt(4);
- let bishop1Pos = positions[randIndex];
- // The second bishop must be on a square of different color
- let randIndex_tmp = 2 * randInt(4) + 1;
- let bishop2Pos = positions[randIndex_tmp];
- // Remove chosen squares
- positions.splice(Math.max(randIndex, randIndex_tmp), 1);
- positions.splice(Math.min(randIndex, randIndex_tmp), 1);
-
- // Get random squares for knights
- randIndex = randInt(6);
- let knight1Pos = positions[randIndex];
- positions.splice(randIndex, 1);
- randIndex = randInt(5);
- let knight2Pos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- // Get random square for queen
- randIndex = randInt(4);
- let queenPos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- // Random square for king (no castle)
- randIndex = randInt(3);
- let kingPos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- // Rooks positions are now fixed
- let rook1Pos = positions[0];
- let rook2Pos = positions[1];
-
- // Finally put the shuffled pieces in the board array
- 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";
+ // Get random squares for every piece, totally freely
+ let positions = shuffle(ArrayFun.range(8));
+ const composition = ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q'];
+ const rem2 = positions[0] % 2;
+ if (rem2 == positions[1] % 2) {
+ // Fix bishops (on different colors)
+ for (let i=2; i<8; i++) {
+ if (positions[i] % 2 != rem2)
+ [positions[1], positions[i]] = [positions[i], positions[1]];
+ }
+ }
+ for (let i = 0; i < 8; i++) pieces[c][positions[i]] = composition[i];
}
return (
pieces["b"].join("") +
import { ChessRules } from "@/base_rules";
-export const VariantRules = class ThreechecksRules extends ChessRules {
+export class ThreechecksRules extends ChessRules {
static IsGoodFlags(flags) {
// 4 for castle + 2 for checks (0,1 or 2)
return !!flags.match(/^[01]{4,4}[012]{2,2}$/);
import { randInt } from "@/utils/alea";
import { ArrayFun } from "@/utils/array";
-export const VariantRules = class UpsidedownRules extends ChessRules {
+export class UpsidedownRules extends ChessRules {
static get HasFlags() {
return false;
}
import { ArrayFun } from "@/utils/array";
import { sample, randInt } from "@/utils/alea";
-export const VariantRules = class WildebeestRules extends ChessRules {
+export class WildebeestRules extends ChessRules {
static get size() {
return { x: 10, y: 11 };
}
import { ChessRules } from "@/base_rules";
-export const VariantRules = class WormholeRules extends ChessRules {
+export class WormholeRules extends ChessRules {
static get HasFlags() {
return false;
}
import { ChessRules } from "@/base_rules";
-export const VariantRules = class ZenRules extends ChessRules {
+export class ZenRules extends ChessRules {
// NOTE: enPassant, if enabled, would need to redefine carefully getEpSquare
static get HasEnpassant() {
return false;
// Obtain VariantRules object
await import("@/variants/" + this.gameRef.vname + ".js")
.then((vModule) => {
- window.V = vModule.VariantRules;
+ window.V = vModule[this.gameRef.vname + "Rules"];
if (!V.CanAnalyze)
// Late check, in case the user tried to enter URL by hand
this.alertAndQuit("Analysis disabled for this variant");
// - from server (one correspondance game I play[ed] or not)
// - from remote peer (one live game I don't play, finished or not)
loadGame: function(game, callback) {
- const afterRetrieval = async (game) => {
- const vModule = await import("@/variants/" + game.vname + ".js");
- window.V = vModule.VariantRules;
- this.vr = new V(game.fen);
- const gtype = this.getGameType(game);
- const tc = extractTime(game.cadence);
- const myIdx = game.players.findIndex(p => {
- return p.sid == this.st.user.sid || p.id == this.st.user.id;
- });
- const mycolor = [undefined, "w", "b"][myIdx + 1]; //undefined for observers
- if (!game.chats) game.chats = []; //live games don't have chat history
- if (gtype == "corr") {
- // NOTE: clocks in seconds, initime in milliseconds
- game.moves.sort((m1, m2) => m1.idx - m2.idx); //in case of
- game.clocks = [tc.mainTime, tc.mainTime];
- const L = game.moves.length;
- if (game.score == "*") {
- // Set clocks + initime
- game.initime = [Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER];
- if (L >= 1) game.initime[L % 2] = game.moves[L-1].played;
- // NOTE: game.clocks shouldn't be computed right now:
- // job will be done in re_setClocks() called soon below.
- }
- // Sort chat messages from newest to oldest
- game.chats.sort((c1, c2) => {
- return c2.added - c1.added;
- });
- if (myIdx >= 0 && game.score == "*" && game.chats.length > 0) {
- // Did a chat message arrive after my last move?
- let dtLastMove = 0;
- if (L == 1 && myIdx == 0)
- dtLastMove = game.moves[0].played;
- else if (L >= 2) {
- if (L % 2 == 0) {
- // It's now white turn
- dtLastMove = game.moves[L-1-(1-myIdx)].played;
- } else {
- // Black turn:
- dtLastMove = game.moves[L-1-myIdx].played;
- }
- }
- if (dtLastMove < game.chats[0].added)
- document.getElementById("chatBtn").classList.add("somethingnew");
- }
- // Now that we used idx and played, re-format moves as for live games
- game.moves = game.moves.map(m => m.squares);
- }
- if (gtype == "live" && game.clocks[0] < 0) {
- // Game is unstarted. clocks and initime are ignored until move 2
- game.clocks = [tc.mainTime, tc.mainTime];
+ this.vr = new V(game.fen);
+ const gtype = this.getGameType(game);
+ const tc = extractTime(game.cadence);
+ const myIdx = game.players.findIndex(p => {
+ return p.sid == this.st.user.sid || p.id == this.st.user.id;
+ });
+ const mycolor = [undefined, "w", "b"][myIdx + 1]; //undefined for observers
+ if (!game.chats) game.chats = []; //live games don't have chat history
+ if (gtype == "corr") {
+ // NOTE: clocks in seconds, initime in milliseconds
+ game.moves.sort((m1, m2) => m1.idx - m2.idx); //in case of
+ game.clocks = [tc.mainTime, tc.mainTime];
+ const L = game.moves.length;
+ if (game.score == "*") {
+ // Set clocks + initime
game.initime = [Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER];
- if (myIdx >= 0) {
- // I play in this live game
- GameStorage.update(game.id, {
- clocks: game.clocks,
- initime: game.initime
- });
- }
+ if (L >= 1) game.initime[L % 2] = game.moves[L-1].played;
+ // NOTE: game.clocks shouldn't be computed right now:
+ // job will be done in re_setClocks() called soon below.
}
- // TODO: merge next 2 "if" conditions
- if (!!game.drawOffer) {
- if (game.drawOffer == "t")
- // Three repetitions
- this.drawOffer = "threerep";
- else {
- // Draw offered by any of the players:
- if (myIdx < 0) this.drawOffer = "received";
- else {
- // I play in this game:
- if (
- (game.drawOffer == "w" && myIdx == 0) ||
- (game.drawOffer == "b" && myIdx == 1)
- )
- this.drawOffer = "sent";
- else this.drawOffer = "received";
+ // Sort chat messages from newest to oldest
+ game.chats.sort((c1, c2) => {
+ return c2.added - c1.added;
+ });
+ if (myIdx >= 0 && game.score == "*" && game.chats.length > 0) {
+ // Did a chat message arrive after my last move?
+ let dtLastMove = 0;
+ if (L == 1 && myIdx == 0)
+ dtLastMove = game.moves[0].played;
+ else if (L >= 2) {
+ if (L % 2 == 0) {
+ // It's now white turn
+ dtLastMove = game.moves[L-1-(1-myIdx)].played;
+ } else {
+ // Black turn:
+ dtLastMove = game.moves[L-1-myIdx].played;
}
}
+ if (dtLastMove < game.chats[0].added)
+ document.getElementById("chatBtn").classList.add("somethingnew");
+ }
+ // Now that we used idx and played, re-format moves as for live games
+ game.moves = game.moves.map(m => m.squares);
+ }
+ if (gtype == "live" && game.clocks[0] < 0) {
+ // Game is unstarted. clocks and initime are ignored until move 2
+ game.clocks = [tc.mainTime, tc.mainTime];
+ game.initime = [Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER];
+ if (myIdx >= 0) {
+ // I play in this live game
+ GameStorage.update(game.id, {
+ clocks: game.clocks,
+ initime: game.initime
+ });
}
- if (!!game.rematchOffer) {
- if (myIdx < 0) this.rematchOffer = "received";
+ }
+ // TODO: merge next 2 "if" conditions
+ if (!!game.drawOffer) {
+ if (game.drawOffer == "t")
+ // Three repetitions
+ this.drawOffer = "threerep";
+ else {
+ // Draw offered by any of the players:
+ if (myIdx < 0) this.drawOffer = "received";
else {
// I play in this game:
if (
- (game.rematchOffer == "w" && myIdx == 0) ||
- (game.rematchOffer == "b" && myIdx == 1)
+ (game.drawOffer == "w" && myIdx == 0) ||
+ (game.drawOffer == "b" && myIdx == 1)
)
- this.rematchOffer = "sent";
- else this.rematchOffer = "received";
+ this.drawOffer = "sent";
+ else this.drawOffer = "received";
}
}
- this.repeat = {}; //reset: scan past moves' FEN:
- let repIdx = 0;
- let vr_tmp = new V(game.fenStart);
- let curTurn = "n";
- game.moves.forEach(m => {
- playMove(m, vr_tmp);
- const fenIdx = vr_tmp.getFen().replace(/ /g, "_");
- this.repeat[fenIdx] = this.repeat[fenIdx]
- ? this.repeat[fenIdx] + 1
- : 1;
- });
- if (this.repeat[repIdx] >= 3) this.drawOffer = "threerep";
- this.game = Object.assign(
- // NOTE: assign mycolor here, since BaseGame could also be VS computer
- {
- type: gtype,
- increment: tc.increment,
- mycolor: mycolor,
- // opponent sid not strictly required (or available), but easier
- // at least oppsid or oppid is available anyway:
- oppsid: myIdx < 0 ? undefined : game.players[1 - myIdx].sid,
- oppid: myIdx < 0 ? undefined : game.players[1 - myIdx].id
- },
- game
- );
- this.$refs["basegame"].re_setVariables(this.game);
- if (!this.gameIsLoading) {
- // Initial loading:
- this.gotMoveIdx = game.moves.length - 1;
- // If we arrive here after 'nextGame' action, the board might be hidden
- let boardDiv = document.querySelector(".game");
- if (!!boardDiv && boardDiv.style.visibility == "hidden")
- boardDiv.style.visibility = "visible";
+ }
+ if (!!game.rematchOffer) {
+ if (myIdx < 0) this.rematchOffer = "received";
+ else {
+ // I play in this game:
+ if (
+ (game.rematchOffer == "w" && myIdx == 0) ||
+ (game.rematchOffer == "b" && myIdx == 1)
+ )
+ this.rematchOffer = "sent";
+ else this.rematchOffer = "received";
}
- this.re_setClocks();
- this.$nextTick(() => {
- this.game.rendered = true;
- // Did lastate arrive before game was rendered?
- if (this.lastate) this.processLastate();
+ }
+ this.repeat = {}; //reset: scan past moves' FEN:
+ let repIdx = 0;
+ let vr_tmp = new V(game.fenStart);
+ let curTurn = "n";
+ game.moves.forEach(m => {
+ playMove(m, vr_tmp);
+ const fenIdx = vr_tmp.getFen().replace(/ /g, "_");
+ this.repeat[fenIdx] = this.repeat[fenIdx]
+ ? this.repeat[fenIdx] + 1
+ : 1;
+ });
+ if (this.repeat[repIdx] >= 3) this.drawOffer = "threerep";
+ this.game = Object.assign(
+ // NOTE: assign mycolor here, since BaseGame could also be VS computer
+ {
+ type: gtype,
+ increment: tc.increment,
+ mycolor: mycolor,
+ // opponent sid not strictly required (or available), but easier
+ // at least oppsid or oppid is available anyway:
+ oppsid: myIdx < 0 ? undefined : game.players[1 - myIdx].sid,
+ oppid: myIdx < 0 ? undefined : game.players[1 - myIdx].id
+ },
+ game
+ );
+ this.$refs["basegame"].re_setVariables(this.game);
+ if (!this.gameIsLoading) {
+ // Initial loading:
+ this.gotMoveIdx = game.moves.length - 1;
+ // If we arrive here after 'nextGame' action, the board might be hidden
+ let boardDiv = document.querySelector(".game");
+ if (!!boardDiv && boardDiv.style.visibility == "hidden")
+ boardDiv.style.visibility = "visible";
+ }
+ this.re_setClocks();
+ this.$nextTick(() => {
+ this.game.rendered = true;
+ // Did lastate arrive before game was rendered?
+ if (this.lastate) this.processLastate();
+ });
+ if (this.lastateAsked) {
+ this.lastateAsked = false;
+ this.sendLastate(game.oppsid);
+ }
+ if (this.gameIsLoading) {
+ this.gameIsLoading = false;
+ if (this.gotMoveIdx >= game.moves.length)
+ // Some moves arrived meanwhile...
+ this.askGameAgain();
+ }
+ if (!!callback) callback();
+ },
+ fetchGame: function(game, callback) {
+ const afterRetrieval = async (game) => {
+ await import("@/variants/" + game.vname + ".js")
+ .then((vModule) => {
+ window.V = vModule[game.vname + "Rules"];
+ this.loadGame(game, callback);
});
- if (this.lastateAsked) {
- this.lastateAsked = false;
- this.sendLastate(game.oppsid);
- }
- if (this.gameIsLoading) {
- this.gameIsLoading = false;
- if (this.gotMoveIdx >= game.moves.length)
- // Some moves arrived meanwhile...
- this.askGameAgain();
- }
- if (!!callback) callback();
};
if (!!game) {
afterRetrieval(game);
return;
}
- if (this.gameRef.rid) {
+ if (this.gameRef.rid)
// Remote live game: forgetting about callback func... (TODO: design)
this.send("askfullgame", { target: this.gameRef.rid });
- } else {
+ else {
// Local or corr game on server.
// NOTE: afterRetrieval() is never called if game not found
const gid = this.gameRef.id;
{
data: { gid: gid },
success: (res) => {
- let g = res.game;
- g.moves.forEach(m => {
+ res.game.moves.forEach(m => {
m.squares = JSON.parse(m.squares);
});
- afterRetrieval(g);
+ afterRetrieval(res.game);
}
}
);
},
loadNewchallVariant: async function(cb) {
const vname = this.getVname(this.newchallenge.vid);
- const vModule = await import("@/variants/" + vname + ".js");
- this.newchallenge.V = vModule.VariantRules;
- this.newchallenge.vname = vname;
- if (!!cb) cb();
+ await import("@/variants/" + vname + ".js")
+ .then((vModule) => {
+ window.V = vModule[vname + "Rules"];
+ this.newchallenge.V = window.V;
+ this.newchallenge.vname = vname;
+ if (!!cb) cb();
+ });
},
trySetNewchallDiag: function() {
if (!this.newchallenge.fen) {
return;
}
c.accepted = true;
- const vModule = await import("@/variants/" + c.vname + ".js");
- window.V = vModule.VariantRules;
- if (!!c.to) {
- // c.to == this.st.user.name (connected)
- if (!!c.fen) {
- const parsedFen = V.ParseFen(c.fen);
- c.mycolor = V.GetOppCol(parsedFen.turn);
- this.tchallDiag = getDiagram({
- position: parsedFen.position,
- orientation: c.mycolor
- });
+ await import("@/variants/" + c.vname + ".js")
+ .then((vModule) => {
+ window.V = vModule[c.vname + "Rules"];
+ if (!!c.to) {
+ // c.to == this.st.user.name (connected)
+ if (!!c.fen) {
+ const parsedFen = V.ParseFen(c.fen);
+ c.mycolor = V.GetOppCol(parsedFen.turn);
+ this.tchallDiag = getDiagram({
+ position: parsedFen.position,
+ orientation: c.mycolor
+ });
+ }
+ this.curChallToAccept = c;
+ document.getElementById("modalAccept").checked = true;
}
- this.curChallToAccept = c;
- document.getElementById("modalAccept").checked = true;
- }
- else this.finishProcessingChallenge(c);
+ else this.finishProcessingChallenge(c);
+ });
}
else {
// My challenge
// Condition: vid is a valid variant ID
this.loadedVar = 0;
const variant = this.st.variants.find(v => v.id == vid);
- const vModule = await import("@/variants/" + variant.name + ".js");
- window.V = vModule.VariantRules;
- this.loadedVar = vid;
- cb();
+ await import("@/variants/" + variant.name + ".js")
+ .then((vModule) => {
+ window.V = vModule[variant.name + "Rules"];
+ this.loadedVar = vid;
+ cb();
+ });
},
trySetDiagram: function(prob) {
// Problem edit: FEN could be wrong or incomplete,
re_setVariant: async function(vname) {
await import("@/variants/" + vname + ".js")
.then((vModule) => {
- this.V = window.V = vModule.VariantRules;
+ this.V = window.V = vModule[vname + "Rules"];
this.gameInfo.vname = vname;
})
.catch((err) => {
display: block
.diagram
display: block
- width: 40%
+ width: 50%
min-width: 240px
margin-left: auto
margin-right: auto
.diag12
float: left
+ width: 40%
margin-left: calc(10% - 20px)
margin-right: 40px
@media screen and (max-width: 630px)
margin: 0 auto 10px auto
.diag22
float: left
+ width: 40%
margin-right: calc(10% - 20px)
@media screen and (max-width: 630px)
float: none