-import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
import { ChessRules, PiPo, Move } from "@/base_rules";
export class EightpiecesRules extends ChessRules {
+
static get JAILER() {
return "j";
}
return "Eightpieces/tmp_png/" + b;
}
- getPPpath(b, orientation) {
- return this.getPpath(b, null, null, orientation);
+ getPPpath(m, orientation) {
+ return (
+ this.getPpath(
+ m.appear[0].c + m.appear[0].p,
+ null,
+ null,
+ orientation
+ )
+ );
}
static ParseFen(fen) {
// 5) Check sentry push (if any)
if (
fenParsed.sentrypush != "-" &&
- !fenParsed.sentrypush.match(/^([a-h][1-8],?)+$/)
+ !fenParsed.sentrypush.match(/^([a-h][1-8]){2,2}$/)
) {
return false;
}
const L = this.sentryPush.length;
if (!this.sentryPush[L-1]) return "-";
let res = "";
- this.sentryPush[L-1].forEach(coords =>
- res += V.CoordsToSquare(coords) + ",");
- return res.slice(0, -1);
+ const spL = this.sentryPush[L-1].length;
+ // Condensate path: just need initial and final squares:
+ return [0, spL - 1]
+ .map(i => V.CoordsToSquare(this.sentryPush[L-1][i]))
+ .join("");
}
setOtherVariables(fen) {
const parsedFen = V.ParseFen(fen);
if (parsedFen.sentrypush == "-") this.sentryPush = [null];
else {
- this.sentryPush = [
- parsedFen.sentrypush.split(",").map(sq => {
- return V.SquareToCoords(sq);
- })
- ];
+ // Expand init + dest squares into a full path:
+ const init = V.SquareToCoords(parsedFen.sentrypush.substr(0, 2)),
+ dest = V.SquareToCoords(parsedFen.sentrypush.substr(2));
+ let newPath = [init];
+ const delta = ['x', 'y'].map(i => Math.abs(dest[i] - init[i]));
+ // Check that it's not a knight movement:
+ if (delta[0] == 0 || delta[1] == 0 || delta[0] == delta[1]) {
+ const step = ['x', 'y'].map((i, idx) => {
+ return (dest[i] - init[i]) / delta[idx] || 0
+ });
+ let x = init.x + step[0],
+ y = init.y + step[1];
+ while (x != dest.x || y != dest.y) {
+ newPath.push({ x: x, y: y });
+ x += step[0];
+ y += step[1];
+ }
+ }
+ newPath.push(dest);
+ this.sentryPush = [newPath];
}
}
- static GenRandInitFen(randomness) {
- if (randomness == 0)
- // Deterministic:
- return "jsfqkbnr/pppppppp/8/8/8/8/PPPPPPPP/JSDQKBNR w 0 ahah - -";
+ static GenRandInitFen(options) {
+ if (options.randomness == 0)
+ return "jfsqkbnr/pppppppp/8/8/8/8/PPPPPPPP/JDSQKBNR w 0 ahah - -";
- let pieces = { w: new Array(8), b: new Array(8) };
- let flags = "";
- // Shuffle pieces on first (and last rank if randomness == 2)
- for (let c of ["w", "b"]) {
- if (c == 'b' && randomness == 1) {
- const lancerIdx = pieces['w'].findIndex(p => {
- return Object.keys(V.LANCER_DIRS).includes(p);
- });
- pieces['b'] =
- pieces['w'].slice(0, lancerIdx)
- .concat(['g'])
- .concat(pieces['w'].slice(lancerIdx + 1));
- flags += flags;
- break;
- }
+ const baseFen = ChessRules.GenRandInitFen(options);
+ const fenParts = baseFen.split(' ');
+ const posParts = fenParts[0].split('/');
- let positions = ArrayFun.range(8);
+ // Replace one bishop by sentry, so that sentries on different colors
+ // Also replace one random rook by jailer,
+ // and one random knight by lancer (facing north/south)
- // Get random squares for bishop and sentry
- let randIndex = 2 * randInt(4);
- let bishopPos = positions[randIndex];
- // The sentry must be on a square of different color
- let randIndex_tmp = 2 * randInt(4) + 1;
- let sentryPos = positions[randIndex_tmp];
- if (c == 'b') {
- // Check if white sentry is on the same color as ours.
- // If yes: swap bishop and sentry positions.
- // NOTE: test % 2 == 1 because there are 7 slashes.
- if ((pieces['w'].indexOf('s') - sentryPos) % 2 == 1)
- [bishopPos, sentryPos] = [sentryPos, bishopPos];
+ // "replaced" array contains -2 initially, then either -1 if skipped,
+ // or (eventually) the index of replacement:
+ let newPos = { 0: "", 7: "" };
+ let sentryOddity = -1;
+ for (let rank of [0, 7]) {
+ let replaced = { 'b': -2, 'n': -2, 'r': -2 };
+ for (let i = 0; i < 8; i++) {
+ const curChar = posParts[rank].charAt(i).toLowerCase();
+ if (['b', 'n', 'r'].includes(curChar)) {
+ if (
+ replaced[curChar] == -1 ||
+ (curChar == 'b' && rank == 7 && i % 2 == sentryOddity) ||
+ (
+ (curChar != 'b' || rank == 0) &&
+ replaced[curChar] == -2 &&
+ randInt(2) == 0
+ )
+ ) {
+ replaced[curChar] = i;
+ if (curChar == 'b') {
+ if (sentryOddity < 0) sentryOddity = i % 2;
+ newPos[rank] += 's';
+ }
+ else if (curChar == 'r') newPos[rank] += 'j';
+ else
+ // Lancer: orientation depends on side
+ newPos[rank] += (rank == 0 ? 'g' : 'c');
+ }
+ else {
+ if (replaced[curChar] == -2) replaced[curChar]++;
+ newPos[rank] += curChar;
+ }
+ }
+ else newPos[rank] += curChar;
}
- positions.splice(Math.max(randIndex, randIndex_tmp), 1);
- positions.splice(Math.min(randIndex, randIndex_tmp), 1);
-
- // Get random squares for knight and lancer
- randIndex = randInt(6);
- const knightPos = positions[randIndex];
- positions.splice(randIndex, 1);
- randIndex = randInt(5);
- const lancerPos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- // Get random square for queen
- randIndex = randInt(4);
- const queenPos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- // Rook, jailer and king positions are now almost fixed,
- // only the ordering rook->jailer or jailer->rook must be decided.
- let rookPos = positions[0];
- let jailerPos = positions[2];
- const kingPos = positions[1];
- flags += V.CoordToColumn(rookPos) + V.CoordToColumn(jailerPos);
- if (Math.random() < 0.5) [rookPos, jailerPos] = [jailerPos, rookPos];
-
- pieces[c][rookPos] = "r";
- pieces[c][knightPos] = "n";
- pieces[c][bishopPos] = "b";
- pieces[c][queenPos] = "q";
- pieces[c][kingPos] = "k";
- pieces[c][sentryPos] = "s";
- // Lancer faces north for white, and south for black:
- pieces[c][lancerPos] = c == 'w' ? 'c' : 'g';
- pieces[c][jailerPos] = "j";
}
+
return (
- pieces["b"].join("") +
- "/pppppppp/8/8/8/8/PPPPPPPP/" +
- pieces["w"].join("").toUpperCase() +
- " w 0 " + flags + " - -"
+ newPos[0] + "/" + posParts.slice(1, 7).join('/') + "/" +
+ newPos[7].toUpperCase() + " " + fenParts.slice(1, 5).join(' ') + " -"
);
}
return null;
}
- // Because of the lancers, getPiece() could be wrong:
- // use board[x][y][1] instead (always valid).
- getBasicMove([sx, sy], [ex, ey], tr) {
- const initColor = this.getColor(sx, sy);
- const initPiece = this.board[sx][sy].charAt(1);
- let mv = new Move({
- appear: [
- new PiPo({
- x: ex,
- y: ey,
- c: tr ? tr.c : initColor,
- p: tr ? tr.p : initPiece
- })
- ],
- vanish: [
- new PiPo({
- x: sx,
- y: sy,
- c: initColor,
- p: initPiece
- })
- ]
- });
-
- // The opponent piece disappears if we take it
- if (this.board[ex][ey] != V.EMPTY) {
- mv.vanish.push(
- new PiPo({
- x: ex,
- y: ey,
- c: this.getColor(ex, ey),
- p: this.board[ex][ey].charAt(1)
- })
- );
- }
-
- return mv;
- }
-
canIplay(side, [x, y]) {
return (
- (this.subTurn == 1 && this.turn == side && this.getColor(x, y) == side) ||
+ (this.subTurn == 1 && this.turn == side && this.getColor(x, y) == side)
+ ||
(this.subTurn == 2 && x == this.sentryPos.x && y == this.sentryPos.y)
);
}
getPotentialMovesFrom([x, y]) {
- // At subTurn == 2, jailers aren't effective (Jeff K)
const piece = this.getPiece(x, y);
const L = this.sentryPush.length;
+ // At subTurn == 2, jailers aren't effective (Jeff K)
if (this.subTurn == 1) {
const jsq = this.isImmobilized([x, y]);
if (!!jsq) {
}
return true;
});
- } else if (this.subTurn == 2) {
+ }
+ else if (this.subTurn == 2) {
// Put back the sentinel on board:
const color = this.turn;
moves.forEach(m => {
// Pawns might be pushed on 1st rank and attempt to move again:
if (!V.OnBoard(x + shiftX, y)) return [];
- const finalPieces =
- // A push cannot put a pawn on last rank (it goes backward)
- x + shiftX == lastRank
- ? Object.keys(V.LANCER_DIRS).concat(
- [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.SENTRY, V.JAILER])
- : [V.PAWN];
+ // A push cannot put a pawn on last rank (it goes backward)
+ let finalPieces = [V.PAWN];
+ if (x + shiftX == lastRank) {
+ // Only allow direction facing inside board:
+ const allowedLancerDirs =
+ lastRank == 0
+ ? ['e', 'f', 'g', 'h', 'm']
+ : ['c', 'd', 'e', 'm', 'o'];
+ finalPieces =
+ allowedLancerDirs
+ .concat([V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.SENTRY, V.JAILER]);
+ }
if (this.board[x + shiftX][y] == V.EMPTY) {
// One square forward
for (let piece of finalPieces) {
return moves;
}
+ doClick(square) {
+ if (isNaN(square[0])) return null;
+ const L = this.sentryPush.length;
+ const [x, y] = [square[0], square[1]];
+ const color = this.turn;
+ if (
+ this.subTurn == 2 ||
+ this.board[x][y] == V.EMPTY ||
+ this.getPiece(x, y) != V.LANCER ||
+ this.getColor(x, y) != color ||
+ !!this.sentryPush[L-1]
+ ) {
+ return null;
+ }
+ // Stuck lancer?
+ const orientation = this.board[x][y][1];
+ const step = V.LANCER_DIRS[orientation];
+ if (!V.OnBoard(x + step[0], y + step[1])) {
+ let choices = [];
+ Object.keys(V.LANCER_DIRS).forEach(k => {
+ const dir = V.LANCER_DIRS[k];
+ if (
+ (dir[0] != step[0] || dir[1] != step[1]) &&
+ V.OnBoard(x + dir[0], y + dir[1])
+ ) {
+ choices.push(
+ new Move({
+ vanish: [
+ new PiPo({
+ x: x,
+ y: y,
+ c: color,
+ p: orientation
+ })
+ ],
+ appear: [
+ new PiPo({
+ x: x,
+ y: y,
+ c: color,
+ p: k
+ })
+ ],
+ start: { x: x, y : y },
+ end: { x: -1, y: -1 }
+ })
+ );
+ }
+ });
+ return choices;
+ }
+ return null;
+ }
+
// Obtain all lancer moves in "step" direction
getPotentialLancerMoves_aux([x, y], step, tr) {
let moves = [];
// Except if just after a push: allow all movements from init square then
const L = this.sentryPush.length;
const color = this.getColor(x, y);
+ const dirCode = this.board[x][y][1];
+ const curDir = V.LANCER_DIRS[dirCode];
if (!!this.sentryPush[L-1]) {
// Maybe I was pushed
const pl = this.sentryPush[L-1].length;
// I was pushed: allow all directions (for this move only), but
// do not change direction after moving, *except* if I keep the
// same orientation in which I was pushed.
- const curDir = V.LANCER_DIRS[this.board[x][y].charAt(1)];
+ // Also allow simple reorientation ("capturing king"):
+ if (!V.OnBoard(x + curDir[0], y + curDir[1])) {
+ const kp = this.kingPos[color];
+ let reorientMoves = [];
+ Object.keys(V.LANCER_DIRS).forEach(k => {
+ const dir = V.LANCER_DIRS[k];
+ if (
+ (dir[0] != curDir[0] || dir[1] != curDir[1]) &&
+ V.OnBoard(x + dir[0], y + dir[1])
+ ) {
+ reorientMoves.push(
+ new Move({
+ vanish: [
+ new PiPo({
+ x: x,
+ y: y,
+ c: color,
+ p: dirCode
+ })
+ ],
+ appear: [
+ new PiPo({
+ x: x,
+ y: y,
+ c: color,
+ p: k
+ })
+ ],
+ start: { x: x, y : y },
+ end: { x: kp[0], y: kp[1] }
+ })
+ );
+ }
+ });
+ Array.prototype.push.apply(moves, reorientMoves);
+ }
Object.values(V.LANCER_DIRS).forEach(step => {
const dirCode = Object.keys(V.LANCER_DIRS).find(k => {
return (
let chooseMoves = [];
dirMoves.forEach(m => {
Object.keys(V.LANCER_DIRS).forEach(k => {
- let mk = JSON.parse(JSON.stringify(m));
- mk.appear[0].p = k;
- moves.push(mk);
+ const newDir = V.LANCER_DIRS[k];
+ // Prevent orientations toward outer board:
+ if (V.OnBoard(m.end.x + newDir[0], m.end.y + newDir[1])) {
+ let mk = JSON.parse(JSON.stringify(m));
+ mk.appear[0].p = k;
+ chooseMoves.push(mk);
+ }
});
});
Array.prototype.push.apply(moves, chooseMoves);
- } else Array.prototype.push.apply(moves, dirMoves);
+ }
+ else Array.prototype.push.apply(moves, dirMoves);
});
return moves;
}
}
// I wasn't pushed: standard lancer move
- const dirCode = this.board[x][y][1];
const monodirMoves =
this.getPotentialLancerMoves_aux([x, y], V.LANCER_DIRS[dirCode]);
// Add all possible orientations aftermove except if I'm being pushed
if (this.subTurn == 1) {
monodirMoves.forEach(m => {
Object.keys(V.LANCER_DIRS).forEach(k => {
- let mk = JSON.parse(JSON.stringify(m));
- mk.appear[0].p = k;
- moves.push(mk);
+ const newDir = V.LANCER_DIRS[k];
+ // Prevent orientations toward outer board:
+ if (V.OnBoard(m.end.x + newDir[0], m.end.y + newDir[1])) {
+ let mk = JSON.parse(JSON.stringify(m));
+ mk.appear[0].p = k;
+ moves.push(mk);
+ }
});
});
return moves;
- } else {
- // I'm pushed: add potential nudges
+ }
+ else {
+ // I'm pushed: add potential nudges, except for current orientation
let potentialNudges = [];
for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
if (
+ (step[0] != curDir[0] || step[1] != curDir[1]) &&
V.OnBoard(x + step[0], y + step[1]) &&
this.board[x + step[0]][y + step[1]] == V.EMPTY
) {
getPotentialKingMoves(sq) {
const moves = this.getSlideNJumpMoves(
- sq,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
- "oneStep"
- );
+ sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]), 1);
return (
this.subTurn == 1
? moves.concat(this.getCastleMoves(sq))
return this.filterValid(this.getPotentialMovesFrom(sentrySq));
}
- prePlay(move) {
- if (move.appear.length == 0 && move.vanish.length == 1)
- // The sentry is about to push a piece: subTurn goes from 1 to 2
- this.sentryPos = { x: move.end.x, y: move.end.y };
- if (this.subTurn == 2 && move.vanish[0].p != V.PAWN) {
- // A piece is pushed: forbid array of squares between start and end
- // of move, included (except if it's a pawn)
- let squares = [];
- if ([V.KNIGHT,V.KING].includes(move.vanish[0].p))
- // short-range pieces: just forbid initial square
- squares.push({ x: move.start.x, y: move.start.y });
- else {
- const deltaX = move.end.x - move.start.x;
- const deltaY = move.end.y - move.start.y;
- const step = [
- deltaX / Math.abs(deltaX) || 0,
- deltaY / Math.abs(deltaY) || 0
- ];
- for (
- let sq = {x: move.start.x, y: move.start.y};
- sq.x != move.end.x || sq.y != move.end.y;
- sq.x += step[0], sq.y += step[1]
- ) {
- squares.push({ x: sq.x, y: sq.y });
- }
- }
- // Add end square as well, to know if I was pushed (useful for lancers)
- squares.push({ x: move.end.x, y: move.end.y });
- this.sentryPush.push(squares);
- } else this.sentryPush.push(null);
- }
-
- play(move) {
- this.prePlay(move);
- move.flags = JSON.stringify(this.aggregateFlags());
- this.epSquares.push(this.getEpSquare(move));
- V.PlayOnBoard(this.board, move);
- // Is it a sentry push? (useful for undo)
- move.sentryPush = (this.subTurn == 2);
- if (this.subTurn == 1) this.movesCount++;
- if (move.appear.length == 0 && move.vanish.length == 1) this.subTurn = 2;
- else {
- // Turn changes only if not a sentry "pre-push"
- this.turn = V.GetOppCol(this.turn);
- this.subTurn = 1;
- }
- this.postPlay(move);
- }
-
- postPlay(move) {
- if (move.vanish.length == 0 || this.subTurn == 2)
- // Special pass move of the king, or sentry pre-push: nothing to update
- return;
- const c = move.vanish[0].c;
- const piece = move.vanish[0].p;
- const firstRank = c == "w" ? V.size.x - 1 : 0;
-
- if (piece == V.KING) {
- this.kingPos[c][0] = move.appear[0].x;
- this.kingPos[c][1] = move.appear[0].y;
- this.castleFlags[c] = [V.size.y, V.size.y];
- return;
- }
- // Update castling flags if rooks are moved
- const oppCol = V.GetOppCol(c);
- const oppFirstRank = V.size.x - 1 - firstRank;
- if (
- move.start.x == firstRank && //our rook moves?
- this.castleFlags[c].includes(move.start.y)
- ) {
- const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1);
- this.castleFlags[c][flagIdx] = V.size.y;
- } else if (
- move.end.x == oppFirstRank && //we took opponent rook?
- this.castleFlags[oppCol].includes(move.end.y)
- ) {
- const flagIdx = (move.end.y == this.castleFlags[oppCol][0] ? 0 : 1);
- this.castleFlags[oppCol][flagIdx] = V.size.y;
- }
- }
-
- undo(move) {
- this.epSquares.pop();
- this.disaggregateFlags(JSON.parse(move.flags));
- V.UndoOnBoard(this.board, move);
- // Decrement movesCount except if the move is a sentry push
- if (!move.sentryPush) this.movesCount--;
- if (this.subTurn == 2) this.subTurn = 1;
- else {
- this.turn = V.GetOppCol(this.turn);
- if (move.sentryPush) this.subTurn = 2;
- }
- this.postUndo(move);
- }
-
- postUndo(move) {
- super.postUndo(move);
- this.sentryPush.pop();
- }
-
isAttacked(sq, color) {
return (
super.isAttacked(sq, color) ||
coord.x += step[0];
coord.y += step[1];
}
+ const L = this.sentryPush.length;
+ const pl = (!!this.sentryPush[L-1] ? this.sentryPush[L-1].length : 0);
for (let xy of lancerPos) {
const dir = V.LANCER_DIRS[this.board[xy.x][xy.y].charAt(1)];
- if (dir[0] == -step[0] && dir[1] == -step[1]) return true;
+ if (
+ (dir[0] == -step[0] && dir[1] == -step[1]) ||
+ // If the lancer was just pushed, this is an attack too:
+ (
+ !!this.sentryPush[L-1] &&
+ this.sentryPush[L-1][pl-1].x == xy.x &&
+ this.sentryPush[L-1][pl-1].y == xy.y
+ )
+ ) {
+ return true;
+ }
}
}
return false;
case V.QUEEN:
return sliderAttack(V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
case V.LANCER: {
- // Special case: as long as no enemy units stands in-between, it attacks
- // (if it points toward the king).
+ // Special case: as long as no enemy units stands in-between,
+ // it attacks (if it points toward the king).
const allowedStep = V.LANCER_DIRS[this.board[x1][y1].charAt(1)];
return sliderAttack([allowedStep], "lancer");
}
// Jailer doesn't capture or give check
+ prePlay(move) {
+ if (move.appear.length == 0 && move.vanish.length == 1)
+ // The sentry is about to push a piece: subTurn goes from 1 to 2
+ this.sentryPos = { x: move.end.x, y: move.end.y };
+ if (this.subTurn == 2 && move.vanish[0].p != V.PAWN) {
+ // A piece is pushed: forbid array of squares between start and end
+ // of move, included (except if it's a pawn)
+ let squares = [];
+ if ([V.KNIGHT,V.KING].includes(move.vanish[0].p))
+ // short-range pieces: just forbid initial square
+ squares.push({ x: move.start.x, y: move.start.y });
+ else {
+ const deltaX = move.end.x - move.start.x;
+ const deltaY = move.end.y - move.start.y;
+ const step = [
+ deltaX / Math.abs(deltaX) || 0,
+ deltaY / Math.abs(deltaY) || 0
+ ];
+ for (
+ let sq = {x: move.start.x, y: move.start.y};
+ sq.x != move.end.x || sq.y != move.end.y;
+ sq.x += step[0], sq.y += step[1]
+ ) {
+ squares.push({ x: sq.x, y: sq.y });
+ }
+ }
+ // Add end square as well, to know if I was pushed (useful for lancers)
+ squares.push({ x: move.end.x, y: move.end.y });
+ this.sentryPush.push(squares);
+ } else this.sentryPush.push(null);
+ }
+
+ play(move) {
+ this.prePlay(move);
+ move.flags = JSON.stringify(this.aggregateFlags());
+ this.epSquares.push(this.getEpSquare(move));
+ V.PlayOnBoard(this.board, move);
+ // Is it a sentry push? (useful for undo)
+ move.sentryPush = (this.subTurn == 2);
+ if (this.subTurn == 1) this.movesCount++;
+ if (move.appear.length == 0 && move.vanish.length == 1) this.subTurn = 2;
+ else {
+ // Turn changes only if not a sentry "pre-push"
+ this.turn = V.GetOppCol(this.turn);
+ this.subTurn = 1;
+ }
+ this.postPlay(move);
+ }
+
+ postPlay(move) {
+ if (move.vanish.length == 0 || this.subTurn == 2)
+ // Special pass move of the king, or sentry pre-push: nothing to update
+ return;
+ const c = move.vanish[0].c;
+ const piece = move.vanish[0].p;
+ const firstRank = c == "w" ? V.size.x - 1 : 0;
+
+ if (piece == V.KING) {
+ this.kingPos[c][0] = move.appear[0].x;
+ this.kingPos[c][1] = move.appear[0].y;
+ this.castleFlags[c] = [V.size.y, V.size.y];
+ return;
+ }
+ // Update castling flags if rooks are moved
+ const oppCol = V.GetOppCol(c);
+ const oppFirstRank = V.size.x - 1 - firstRank;
+ if (
+ move.start.x == firstRank && //our rook moves?
+ this.castleFlags[c].includes(move.start.y)
+ ) {
+ const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1);
+ this.castleFlags[c][flagIdx] = V.size.y;
+ } else if (
+ move.end.x == oppFirstRank && //we took opponent rook?
+ this.castleFlags[oppCol].includes(move.end.y)
+ ) {
+ const flagIdx = (move.end.y == this.castleFlags[oppCol][0] ? 0 : 1);
+ this.castleFlags[oppCol][flagIdx] = V.size.y;
+ }
+ }
+
+ undo(move) {
+ this.epSquares.pop();
+ this.disaggregateFlags(JSON.parse(move.flags));
+ V.UndoOnBoard(this.board, move);
+ // Decrement movesCount except if the move is a sentry push
+ if (!move.sentryPush) this.movesCount--;
+ if (this.subTurn == 2) this.subTurn = 1;
+ else {
+ this.turn = V.GetOppCol(this.turn);
+ if (move.sentryPush) this.subTurn = 2;
+ }
+ this.postUndo(move);
+ }
+
+ postUndo(move) {
+ super.postUndo(move);
+ this.sentryPush.pop();
+ }
+
static get VALUES() {
return Object.assign(
{ l: 4.8, s: 2.8, j: 3.8 }, //Jeff K. estimations
end: move.end
};
notation = super.getNotation(simpleMove);
- } else notation = super.getNotation(move);
+ }
+ else if (
+ move.appear.length > 0 &&
+ move.vanish[0].x == move.appear[0].x &&
+ move.vanish[0].y == move.appear[0].y
+ ) {
+ // Lancer in-place reorientation:
+ notation = "L" + V.CoordsToSquare(move.start) + ":R";
+ }
+ else notation = super.getNotation(move);
if (Object.keys(V.LANCER_DIRNAMES).includes(move.vanish[0].p))
// Lancer: add direction info
notation += "=" + V.LANCER_DIRNAMES[move.appear[0].p];
Object.keys(V.LANCER_DIRNAMES).includes(move.appear[0].p)
) {
// Fix promotions in lancer:
- notation = notation.slice(0, -1) + "L:" + V.LANCER_DIRNAMES[move.appear[0].p];
+ notation = notation.slice(0, -1) +
+ "L:" + V.LANCER_DIRNAMES[move.appear[0].p];
}
return notation;
}
+
};