X-Git-Url: https://git.auder.net/assets/rpsls.css?a=blobdiff_plain;f=base_rules.js;h=3d8e4635b53d6e5a46c7a38cb907bc5330a50633;hb=HEAD;hp=aa499f84f9275a5d1a980ac2bc6c98e9c6f78bc9;hpb=ceeac4e82346ffba2d87d763df3cffcddaec912a;p=xogo.git
diff --git a/base_rules.js b/base_rules.js
index aa499f8..3d8e463 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -97,6 +97,14 @@ export default class ChessRules {
return true;
}
+ // Allow to take (moving: not disappearing) own pieces?
+ get hasSelfCaptures() {
+ return (
+ this.options["recycle"] ||
+ (this.options["teleport"] && this.subTurnTeleport == 1)
+ );
+ }
+
get hasReserve() {
return (
!!this.options["crazyhouse"] ||
@@ -122,6 +130,11 @@ export default class ChessRules {
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")
@@ -162,6 +175,22 @@ export default class ChessRules {
return Object.values(cd).map(c => c.toString(36)).join("");
}
+ // c10 --> 02 (assuming 10 rows)
+ static SquareFromUsual(sq) {
+ return (
+ (this.size.x - parseInt(sq.substring(1), 10)).toString(36) +
+ (sq.charCodeAt(0) - 97).toString(36)
+ );
+ }
+
+ // 02 --> c10
+ static UsualFromSquare(sq) {
+ return (
+ String.fromCharCode(parseInt(sq.charAt(1), 36) + 97) +
+ (this.size.x - parseInt(sq.charAt(0), 36)).toString(10)
+ );
+ }
+
coordsToId(cd) {
if (typeof cd.x == "number") {
return (
@@ -220,7 +249,7 @@ export default class ChessRules {
['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'],
{
randomness: this.options["randomness"],
- between: {p1: 'k', p2: 'r'},
+ between: [{p1: 'k', p2: 'r'}],
diffCol: ['b'],
flags: ['r']
}
@@ -311,7 +340,7 @@ export default class ChessRules {
// Flags part of the FEN string
getFlagsFen() {
- return ["w", "b"].map(c => {
+ return ['w', 'b'].map(c => {
return this.castleFlags[c].map(x => x.toString(36)).join("");
}).join("");
}
@@ -325,9 +354,9 @@ export default class ChessRules {
getReserveFen(o) {
if (o.init)
- return "000000000000";
+ return Array(2 * V.ReserveArray.length).fill('0').join("");
return (
- ["w","b"].map(c => Object.values(this.reserve[c]).join("")).join("")
+ ['w', 'b'].map(c => Object.values(this.reserve[c]).join("")).join("")
);
}
@@ -428,14 +457,16 @@ export default class ChessRules {
}
// ordering as in pieces() p,r,n,b,q,k
- initReserves(reserveStr, pieceArray) {
- if (!pieceArray)
- pieceArray = ['p', 'r', 'n', 'b', 'q', 'k'];
+ static get ReserveArray() {
+ return ['p', 'r', 'n', 'b', 'q', 'k'];
+ }
+
+ initReserves(reserveStr) {
const counts = reserveStr.split("").map(c => parseInt(c, 36));
- const L = pieceArray.length;
+ const L = V.ReserveArray.length;
this.reserve = {
- w: ArrayFun.toObject(pieceArray, counts.slice(0, L)),
- b: ArrayFun.toObject(pieceArray, counts.slice(L, 2 * L))
+ w: ArrayFun.toObject(V.ReserveArray, counts.slice(0, L)),
+ b: ArrayFun.toObject(V.ReserveArray, counts.slice(L, 2 * L))
};
}
@@ -450,7 +481,7 @@ export default class ChessRules {
// VISUAL UTILS
getPieceWidth(rwidth) {
- return (rwidth / this.size.y);
+ return (rwidth / Math.max(this.size.x, this.size.y));
}
getReserveSquareSize(rwidth, nbR) {
@@ -566,11 +597,18 @@ export default class ChessRules {
// Get SVG board (background, no pieces)
getSvgChessboard() {
- const flipped = (this.playerColor == 'b');
let board = `
";
+ return board;
+ }
+
+ getBaseSvgChessboard() {
+ let board = "";
+ const flipped = this.flippedBoard;
for (let i=0; i < this.size.x; i++) {
for (let j=0; j < this.size.y; j++) {
if (!this.onBoard(i, j))
@@ -592,7 +630,6 @@ export default class ChessRules {
/>`;
}
}
- board += "";
return board;
}
@@ -629,7 +666,11 @@ export default class ChessRules {
else
this[arrName] = ArrayFun.init(this.size.x, this.size.y, null);
if (arrName == "d_pieces")
- this.marks.forEach(([i, j]) => addPiece(i, j, arrName, "mark"));
+ this.marks.forEach((m) => {
+ const formattedSquare = C.SquareFromUsual(m);
+ const mCoords = C.SquareToCoords(formattedSquare);
+ addPiece(mCoords.x, mCoords.y, arrName, "mark");
+ });
};
if (this.marks)
conditionalReset("d_pieces");
@@ -640,7 +681,7 @@ export default class ChessRules {
const color = this.getColor(i, j);
const piece = this.getPiece(i, j);
addPiece(i, j, "g_pieces", this.pieces(color, i, j)[piece]["class"]);
- this.g_pieces[i][j].classList.add(C.GetColorClass(color));
+ this.g_pieces[i][j].classList.add(V.GetColorClass(color));
if (this.enlightened && !this.enlightened[i][j])
this.g_pieces[i][j].classList.add("hidden");
}
@@ -707,7 +748,7 @@ export default class ChessRules {
rcontainer.appendChild(r_cell);
let piece = document.createElement("piece");
C.AddClass_es(piece, this.pieces(c, c, p)[p]["class"]);
- piece.classList.add(C.GetColorClass(c));
+ piece.classList.add(V.GetColorClass(c));
piece.style.width = "100%";
piece.style.height = "100%";
this.r_pieces[c][p] = piece;
@@ -829,9 +870,10 @@ export default class ChessRules {
y = (this.playerColor == i ? y = r.height + 5 : - 5 - rsqSize);
}
else {
- const sqSize = r.width / this.size.y;
- const flipped = (this.playerColor == 'b');
- x = (flipped ? this.size.y - 1 - j : j) * sqSize;
+ const sqSize = r.width / Math.max(this.size.x, this.size.y);
+ const flipped = this.flippedBoard;
+ x = (flipped ? this.size.y - 1 - j : j) * sqSize +
+ Math.abs(this.size.x - this.size.y) * sqSize / 2;
y = (flipped ? this.size.x - 1 - i : i) * sqSize;
}
return [r.x + x, r.y + y];
@@ -1016,7 +1058,7 @@ export default class ChessRules {
const cdisp = moves[i].choice || moves[i].appear[0].p;
C.AddClass_es(piece,
this.pieces(color, moves[i].end.x, moves[i].end.y)[cdisp]["class"]);
- piece.classList.add(C.GetColorClass(color));
+ piece.classList.add(V.GetColorClass(color));
piece.style.width = "100%";
piece.style.height = "100%";
choice.appendChild(piece);
@@ -1067,7 +1109,7 @@ export default class ChessRules {
// 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) &&
@@ -1178,18 +1220,19 @@ export default class ChessRules {
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));
+ const pawnShift = this.getPawnShift(color || 'w');
return {
'p': {
"class": "pawn",
moves: [
{
steps: [[pawnShift, 0]],
- range: (initRank ? 2 : 1)
+ range: (this.isPawnInitRank(x, color) ? 2 : 1)
}
],
attack: [
@@ -1290,6 +1333,17 @@ export default class ChessRules {
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)
@@ -1321,6 +1375,11 @@ export default class ChessRules {
return this.getColor(x1, y1) !== this.getColor(x2, y2);
}
+ // Teleport & Recycle. Assumption: color(x1,y1) == color(x2,y2)
+ canSelfTake([x1, y1], [x2, y2]) {
+ return !this.isKing(x2, y2);
+ }
+
canStepOver(i, j, p) {
// In some variants, objects on boards don't stop movement (Chakart)
return this.board[i][j] == "";
@@ -1350,13 +1409,12 @@ export default class ChessRules {
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) &&
@@ -1392,7 +1450,6 @@ export default class ChessRules {
{
attackOnly: true,
one: true,
- segments: this.options["cylinder"]
},
allowed
)
@@ -1403,10 +1460,7 @@ export default class ChessRules {
this.options["zen"] &&
this.findCapturesOn(
[i, j],
- {
- one: true,
- segments: this.options["cylinder"]
- },
+ {one: true},
allowed
)
)
@@ -1483,6 +1537,7 @@ export default class ChessRules {
}
// All possible moves from selected square
+ // TODO: generalize usage if arg "color" (e.g. Checkered)
getPotentialMovesFrom([x, y], color) {
if (this.subTurnTeleport == 2)
return [];
@@ -1494,12 +1549,8 @@ export default class ChessRules {
let moves = this.getPotentialMovesOf(piece, [x, y]);
if (piece == "p" && this.hasEnpassant && this.epSquare)
Array.prototype.push.apply(moves, this.getEnpassantCaptures([x, y]));
- if (
- this.isKing(0, 0, piece) && this.hasCastle &&
- this.castleFlags[color || this.turn].some(v => v < this.size.y)
- ) {
+ if (this.isKing(0, 0, piece) && this.hasCastle)
Array.prototype.push.apply(moves, this.getCastleMoves([x, y]));
- }
return this.postProcessPotentialMoves(moves);
}
@@ -1564,8 +1615,7 @@ export default class ChessRules {
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] != "" &&
@@ -1653,7 +1703,7 @@ export default class ChessRules {
m.appear[0].p = this.pawnPromotions[0];
for (let i=1; i {
@@ -1686,7 +1735,6 @@ export default class ChessRules {
[x, y],
{
moveOnly: !!stepSpec.attack || this.options["zen"],
- segments: this.options["cylinder"],
stepSpec: stepSpec
}
);
@@ -1699,32 +1747,31 @@ export default class ChessRules {
!this.isKing(i1, j1) && this.canTake([i2, j2], [i1, j1])
);
// Technical step: segments (if any) are reversed
- if (this.options["cylinder"]) {
- zenCaptures.forEach(z => {
+ zenCaptures.forEach(z => {
+ if (!!z.segments)
z.segments = z.segments.reverse().map(s => s.reverse())
- });
- }
+ });
Array.prototype.push.apply(squares, zenCaptures);
}
- if (
- this.options["recycle"] ||
- (this.options["teleport"] && this.subTurnTeleport == 1)
- ) {
+ if (this.hasSelfCaptures) {
const selfCaptures = this.findDestSquares(
[x, y],
{
attackOnly: true,
- segments: this.options["cylinder"],
stepSpec: stepSpec
},
- ([i1, j1], [i2, j2]) =>
- this.getColor(i2, j2) == color && !this.isKing(i2, j2)
+ ([i1, j1], [i2, j2]) => {
+ return (
+ this.getColor(i2, j2) == color &&
+ this.canSelfTake([i1, j1], [i2, j2])
+ );
+ }
);
Array.prototype.push.apply(squares, selfCaptures);
}
return squares.map(s => {
let mv = this.getBasicMove([x, y], s.sq);
- if (this.options["cylinder"] && s.segments.length >= 2)
+ if (!!s.segments)
mv.segments = s.segments;
return mv;
});
@@ -1735,23 +1782,21 @@ export default class ChessRules {
allowed = (sq1, sq2) => this.canTake(sq1, sq2);
const apparentPiece = this.getPiece(x, y); //how it looks
let res = [];
- // Next 3 for Cylinder mode: (unused if !o.segments)
+ // Next 3 for Cylinder mode or circular (useless otherwise)
let explored = {};
let segments = [];
let segStart = [];
const addSquare = ([i, j]) => {
let elt = {sq: [i, j]};
- if (o.segments)
- elt.segments = this.getSegments(segments, segStart, end);
+ if (segments.length >= 1)
+ elt.segments = this.getSegments(segments, segStart, [i, j]);
res.push(elt);
};
const exploreSteps = (stepArray, mode) => {
for (let s of stepArray) {
outerLoop: for (let step of s.steps) {
- if (o.segments) {
- segments = [];
- segStart = [x, y];
- }
+ segments = [];
+ segStart = [x, y];
let [i, j] = [x, y];
let stepCounter = 0;
while (
@@ -1775,10 +1820,12 @@ export default class ChessRules {
if (s.range <= stepCounter++)
continue outerLoop;
const oldIJ = [i, j];
- i += step[0];
- j = this.getY(j + step[1]);
- if (o.segments && Math.abs(j - oldIJ[1]) > 1) {
- // Boundary between segments (cylinder mode)
+ [i, j] = this.increment([i, j], step);
+ if (
+ Math.abs(i - oldIJ[0]) != Math.abs(step[0]) ||
+ Math.abs(j - oldIJ[1]) != Math.abs(step[1])
+ ) {
+ // Boundary between segments (cylinder or circular mode)
segments.push([[segStart[0], segStart[1]], oldIJ]);
segStart = [i, j];
}
@@ -1849,9 +1896,6 @@ export default class ChessRules {
{
captureTarget: [x, y],
captureSteps: [{steps: [s], range: a.range}],
- segments: o.segments,
- attackOnly: true,
- one: false //one and captureTarget are mutually exclusive
},
allowed
);
@@ -1982,14 +2026,14 @@ export default class ChessRules {
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] == "" &&
oppCols.includes(this.getColor(x, this.epSquare.y))
) {
const [epx, epy] = [this.epSquare.x, this.epSquare.y];
- this.board[epx][epy] = this.board[x][this.epSquares.y];
+ this.board[epx][epy] = this.board[x][this.epSquare.y];
let enpassantMove = this.getBasicMove([x, y], [epx, epy]);
this.board[epx][epy] = "";
const lastIdx = enpassantMove.vanish.length - 1; //think Rifle
@@ -1999,8 +2043,9 @@ export default class ChessRules {
return [];
}
- getCastleMoves([x, y], finalSquares, castleWith) {
+ getCastleMoves([x, y], finalSquares, castleWith, castleFlags) {
const c = this.getColor(x, y);
+ castleFlags = castleFlags || this.castleFlags[c];
// Castling ?
const oppCols = this.getOppCols(c);
@@ -2014,12 +2059,12 @@ export default class ChessRules {
castleSide < 2;
castleSide++ //large, then small
) {
- if (this.castleFlags[c][castleSide] >= this.size.y)
+ if (castleFlags[castleSide] >= this.size.y)
continue;
// If this code is reached, rook and king are on initial position
// NOTE: in some variants this is not a rook
- const rookPos = this.castleFlags[c][castleSide];
+ const rookPos = castleFlags[castleSide];
const castlingPiece = this.getPiece(x, rookPos);
if (
this.board[x][rookPos] == "" ||
@@ -2121,7 +2166,6 @@ export default class ChessRules {
[x, y],
{
byCol: oppCols,
- segments: this.options["cylinder"],
one: true
}
)
@@ -2133,7 +2177,6 @@ export default class ChessRules {
[x, y],
{
attackOnly: true,
- segments: this.options["cylinder"],
one: true
},
([i1, j1], [i2, j2]) => oppCols.includes(this.getColor(i2, j2))
@@ -2161,42 +2204,48 @@ export default class ChessRules {
return res;
}
+ // cb: callback returning a boolean (false if king missing)
+ trackKingWrap(move, kingPos, cb) {
+ if (move.appear.length == 0 && move.vanish.length == 0)
+ return true;
+ const color =
+ (move.vanish.length > 0 ? move.vanish[0].c : move.appear[0].c);
+ let newKingPP = null,
+ sqIdx = 0,
+ res = true; //a priori valid
+ const oldKingPP =
+ move.vanish.find(v => this.isKing(0, 0, v.p) && v.c == color);
+ if (oldKingPP) {
+ // Search king in appear array:
+ newKingPP =
+ move.appear.find(a => this.isKing(0, 0, a.p) && a.c == color);
+ if (newKingPP) {
+ sqIdx = kingPos.findIndex(kp =>
+ kp[0] == oldKingPP.x && kp[1] == oldKingPP.y);
+ kingPos[sqIdx] = [newKingPP.x, newKingPP.y];
+ }
+ else
+ res = false; //king vanished
+ }
+ res &&= cb(kingPos);
+ if (oldKingPP && newKingPP)
+ kingPos[sqIdx] = [oldKingPP.x, oldKingPP.y];
+ return res;
+ }
+
// 'color' arg because some variants (e.g. Refusal) check opponent moves
filterValid(moves, color) {
if (!color)
color = this.turn;
const oppCols = this.getOppCols(color);
let kingPos = this.searchKingPos(color);
- let filtered = {}; //avoid re-checking similar moves (promotions...)
return moves.filter(m => {
- const key = m.start.x + m.start.y + '.' + m.end.x + m.end.y;
- if (!filtered[key]) {
- this.playOnBoard(m);
- let newKingPP = null,
- sqIdx = 0,
- res = true; //a priori valid
- const oldKingPP =
- m.vanish.find(v => this.isKing(0, 0, v.p) && v.c == color);
- if (oldKingPP) {
- // Search king in appear array:
- newKingPP =
- m.appear.find(a => this.isKing(0, 0, a.p) && a.c == color);
- if (newKingPP) {
- sqIdx = kingPos.findIndex(kp =>
- kp[0] == oldKingPP.x && kp[1] == oldKingPP.y);
- kingPos[sqIdx] = [newKingPP.x, newKingPP.y];
- }
- else
- res = false; //king vanished
- }
- res &&= !this.underCheck(kingPos, oppCols);
- if (oldKingPP && newKingPP)
- kingPos[sqIdx] = [oldKingPP.x, oldKingPP.y];
- this.undoOnBoard(m);
- filtered[key] = res;
- return res;
- }
- return filtered[key];
+ this.playOnBoard(m);
+ const res = this.trackKingWrap(m, kingPos, (kp) => {
+ return !this.underCheck(kp, oppCols);
+ });
+ this.undoOnBoard(m);
+ return res;
});
}
@@ -2218,11 +2267,20 @@ export default class ChessRules {
this.board[psq.x][psq.y] = psq.c + psq.p;
}
- updateCastleFlags(move) {
+ // NOTE: arg "castleFlags" for Coregal or Twokings
+ updateCastleFlags(move, castleFlags, king) {
+ castleFlags = castleFlags || this.castleFlags;
+ // If flags already off, no need to re-check:
+ if (
+ Object.values(castleFlags).every(cvals =>
+ cvals.every(val => val >= this.size.y))
+ ) {
+ return;
+ }
// Update castling flags if start or arrive from/at rook/king locations
move.appear.concat(move.vanish).forEach(psq => {
- if (this.isKing(0, 0, psq.p))
- this.castleFlags[psq.c] = [this.size.y, this.size.y];
+ if ((king && psq.p == king) || (!king && this.isKing(0, 0, psq.p)))
+ castleFlags[psq.c] = [this.size.y, this.size.y];
// NOTE: not "else if" because king can capture enemy rook...
let c = "";
if (psq.x == 0)
@@ -2230,28 +2288,17 @@ export default class ChessRules {
else if (psq.x == this.size.x - 1)
c = "w";
if (c != "") {
- const fidx = this.castleFlags[c].findIndex(f => f == psq.y);
+ const fidx = castleFlags[c].findIndex(f => f == psq.y);
if (fidx >= 0)
- this.castleFlags[c][fidx] = this.size.y;
+ castleFlags[c][fidx] = this.size.y;
}
});
}
prePlay(move) {
- if (
- this.hasCastle &&
- // If flags already off, no need to re-check:
- Object.values(this.castleFlags).some(cvals =>
- cvals.some(val => val < this.size.y))
- ) {
+ if (this.hasCastle)
this.updateCastleFlags(move);
- }
if (this.options["crazyhouse"]) {
- move.vanish.forEach(v => {
- const square = C.CoordsToSquare({x: v.x, y: v.y});
- if (this.ispawn[square])
- delete this.ispawn[square];
- });
if (move.appear.length > 0 && move.vanish.length > 0) {
// Assumption: something is moving
const initSquare = C.CoordsToSquare(move.start);
@@ -2270,6 +2317,11 @@ export default class ChessRules {
delete this.ispawn[destSquare];
}
}
+ move.vanish.forEach(v => {
+ const square = C.CoordsToSquare({x: v.x, y: v.y});
+ if (this.ispawn[square])
+ delete this.ispawn[square];
+ });
}
const minSize = Math.min(move.appear.length, move.vanish.length);
if (
@@ -2312,10 +2364,11 @@ export default class ChessRules {
if (this.options["teleport"]) {
if (
this.subTurnTeleport == 1 &&
- move.vanish.length > move.appear.length &&
+ move.vanish.length == 2 &&
+ move.appear.length == 1 &&
move.vanish[1].c == this.turn
) {
- const v = move.vanish[move.vanish.length - 1];
+ const v = move.vanish[1];
this.captured = {x: v.x, y: v.y, c: v.c, p: v.p};
this.subTurnTeleport = 2;
return;
@@ -2328,7 +2381,7 @@ export default class ChessRules {
tryChangeTurn(move) {
if (this.isLastMove(move)) {
- this.turn = (this.turn == 'w' ? 'b' : 'w');
+ this.turn = C.GetOppTurn(this.turn);
this.movesCount++;
this.subTurn = 1;
}
@@ -2370,7 +2423,7 @@ export default class ChessRules {
if (this.board[i][j] != "" && this.getColor(i, j) == color) {
// NOTE: in fact searching for all potential moves from i,j.
// I don't believe this is an issue, for now at least.
- const moves = this.getPotentialMovesFrom([i, j]);
+ const moves = this.getPotentialMovesFrom([i, j], color);
if (moves.some(m => this.filterValid([m]).length >= 1))
return true;
}
@@ -2400,9 +2453,9 @@ export default class ChessRules {
if (kingPos[this.turn].length == 0 && kingPos[oppTurn].length == 0)
return "1/2";
if (kingPos[this.turn].length == 0)
- return (color == "w" ? "0-1" : "1-0");
+ return (this.turn == "w" ? "0-1" : "1-0");
if (kingPos[oppTurn].length == 0)
- return (color == "w" ? "1-0" : "0-1");
+ return (this.turn == "w" ? "1-0" : "0-1");
if (this.atLeastOneMove(this.turn))
return "*";
// No valid move: stalemate or checkmate?
@@ -2414,7 +2467,8 @@ export default class ChessRules {
playVisual(move, r) {
move.vanish.forEach(v => {
- this.g_pieces[v.x][v.y].remove();
+ if (this.g_pieces[v.x][v.y]) //can be null (e.g. Apocalypse)
+ this.g_pieces[v.x][v.y].remove();
this.g_pieces[v.x][v.y] = null;
});
let chessboard =
@@ -2426,7 +2480,7 @@ export default class ChessRules {
this.g_pieces[a.x][a.y] = document.createElement("piece");
C.AddClass_es(this.g_pieces[a.x][a.y],
this.pieces(a.c, a.x, a.y)[a.p]["class"]);
- this.g_pieces[a.x][a.y].classList.add(C.GetColorClass(a.c));
+ this.g_pieces[a.x][a.y].classList.add(V.GetColorClass(a.c));
this.g_pieces[a.x][a.y].style.width = pieceWidth + "px";
this.g_pieces[a.x][a.y].style.height = pieceWidth + "px";
const [ip, jp] = this.getPixelPosition(a.x, a.y, r);
@@ -2483,6 +2537,10 @@ export default class ChessRules {
animateMoving(start, end, drag, segments, cb) {
let initPiece = this.getDomPiece(start.x, start.y);
+ if (!initPiece) { //TODO: shouldn't occur!
+ cb();
+ return;
+ }
// NOTE: cloning often not required, but light enough, and simpler
let movingPiece = initPiece.cloneNode();
initPiece.style.opacity = "0";
@@ -2503,8 +2561,8 @@ export default class ChessRules {
C.RemoveClass_es(movingPiece, pieces[startCode]["class"]);
C.AddClass_es(movingPiece, pieces[drag.p]["class"]);
if (apparentColor != drag.c) {
- movingPiece.classList.remove(C.GetColorClass(apparentColor));
- movingPiece.classList.add(C.GetColorClass(drag.c));
+ movingPiece.classList.remove(V.GetColorClass(apparentColor));
+ movingPiece.classList.add(V.GetColorClass(drag.c));
}
}
container.appendChild(movingPiece);