return "l";
}
+ static get IMAGE_EXTENSION() {
+ // Temporarily, for the time SVG pieces are being designed:
+ return ".png";
+ }
+
// Lancer directions *from white perspective*
static get LANCER_DIRS() {
return {
}
getPpath(b, color, score, orientation) {
- if ([V.JAILER, V.SENTRY].includes(b[1])) return "Eightpieces/" + b;
+ if ([V.JAILER, V.SENTRY].includes(b[1])) return "Eightpieces/tmp_png/" + b;
if (Object.keys(V.LANCER_DIRS).includes(b[1])) {
- if (orientation == 'w') return "Eightpieces/" + b;
+ if (orientation == 'w') return "Eightpieces/tmp_png/" + b;
// Find opposite direction for adequate display:
let oppDir = '';
switch (b[1]) {
oppDir = 'f';
break;
}
- return "Eightpieces/" + b[0] + oppDir;
+ return "Eightpieces/tmp_png/" + b[0] + oppDir;
}
- return b;
+ // TODO: after we have SVG pieces, remove the folder and next prefix:
+ return "Eightpieces/tmp_png/" + b;
}
getPPpath(b, orientation) {
getPotentialMovesFrom([x, y]) {
// At subTurn == 2, jailers aren't effective (Jeff K)
+ const piece = this.getPiece(x, y);
+ const L = this.sentryPush.length;
if (this.subTurn == 1) {
const jsq = this.isImmobilized([x, y]);
if (!!jsq) {
let moves = [];
// Special pass move if king:
- if (this.getPiece(x, y) == V.KING) {
+ if (piece == V.KING) {
moves.push(
new Move({
appear: [],
})
);
}
+ else if (piece == V.LANCER && !!this.sentryPush[L-1]) {
+ // A pushed lancer next to the jailer: reorient
+ const color = this.getColor(x, y);
+ const curDir = this.board[x][y].charAt(1);
+ Object.keys(V.LANCER_DIRS).forEach(k => {
+ moves.push(
+ new Move({
+ appear: [{ x: x, y: y, c: color, p: k }],
+ vanish: [{ x: x, y: y, c: color, p: curDir }],
+ start: { x: x, y: y },
+ end: { x: jsq[0], y: jsq[1] }
+ })
+ );
+ });
+ }
return moves;
}
}
let moves = [];
- switch (this.getPiece(x, y)) {
+ switch (piece) {
case V.JAILER:
moves = this.getPotentialJailerMoves([x, y]);
break;
moves = super.getPotentialMovesFrom([x, y]);
break;
}
- const L = this.sentryPush.length;
if (!!this.sentryPush[L-1]) {
- // Delete moves walking back on sentry push path
+ // Delete moves walking back on sentry push path,
+ // only if not a pawn, and the piece is the pushed one.
+ const pl = this.sentryPush[L-1].length;
+ const finalPushedSq = this.sentryPush[L-1][pl-1];
moves = moves.filter(m => {
if (
m.vanish[0].p != V.PAWN &&
+ m.start.x == finalPushedSq.x && m.start.y == finalPushedSq.y &&
this.sentryPush[L-1].some(sq => sq.x == m.end.x && sq.y == m.end.y)
) {
return false;
const color = this.getColor(x, y);
let moves = [];
const [sizeX, sizeY] = [V.size.x, V.size.y];
- let shiftX = color == "w" ? -1 : 1;
+ let shiftX = (color == "w" ? -1 : 1);
if (this.subTurn == 2) shiftX *= -1;
+ const firstRank = color == "w" ? sizeX - 1 : 0;
const startRank = color == "w" ? sizeX - 2 : 1;
const lastRank = color == "w" ? 0 : sizeX - 1;
if (!V.OnBoard(x + shiftX, y)) return [];
const finalPieces =
- // No promotions after pushes!
- x + shiftX == lastRank && this.subTurn == 1
- ?
- Object.keys(V.LANCER_DIRS).concat(
+ // 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];
if (this.board[x + shiftX][y] == V.EMPTY) {
);
}
if (
- x == startRank &&
+ // 2-squares jumps forbidden if pawn push
+ this.subTurn == 1 &&
+ [startRank, firstRank].includes(x) &&
this.board[x + 2 * shiftX][y] == V.EMPTY
) {
// Two squares jump
}
}
- // En passant:
+ // En passant: only on subTurn == 1
const Lep = this.epSquares.length;
const epSquare = this.epSquares[Lep - 1];
if (
+ this.subTurn == 1 &&
!!epSquare &&
epSquare.x == x + shiftX &&
Math.abs(epSquare.y - y) == 1
// Add all lancer possible orientations, similar to pawn promotions.
// Except if just after a push: allow all movements from init square then
const L = this.sentryPush.length;
+ const color = this.getColor(x, y);
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 color = this.getColor(x, y);
const curDir = V.LANCER_DIRS[this.board[x][y].charAt(1)];
Object.values(V.LANCER_DIRS).forEach(step => {
const dirCode = Object.keys(V.LANCER_DIRS).find(k => {
V.OnBoard(x + step[0], y + step[1]) &&
this.board[x + step[0]][y + step[1]] == V.EMPTY
) {
+ const newDirCode = Object.keys(V.LANCER_DIRS).find(k => {
+ const codeStep = V.LANCER_DIRS[k];
+ return (codeStep[0] == step[0] && codeStep[1] == step[1]);
+ });
potentialNudges.push(
this.getBasicMove(
[x, y],
- [x + step[0], y + step[1]]
+ [x + step[0], y + step[1]],
+ { c: color, p: newDirCode }
)
);
}
i = y;
do {
if (
- this.isAttacked([x, i], [oppCol]) ||
+ this.isAttacked([x, i], oppCol) ||
(this.board[x][i] != V.EMPTY &&
(this.getColor(x, i) != c ||
![V.KING, V.ROOK, V.JAILER].includes(this.getPiece(x, i))))
}
play(move) {
- if (!this.states) this.states = [];
- const stateFen = this.getFen();
- this.states.push(stateFen);
+// if (!this.states) this.states = [];
+// const stateFen = this.getFen();
+// this.states.push(stateFen);
this.prePlay(move);
move.flags = JSON.stringify(this.aggregateFlags());
}
this.postUndo(move);
- const stateFen = this.getFen();
- if (stateFen != this.states[this.states.length-1]) debugger;
- this.states.pop();
+// const stateFen = this.getFen();
+// if (stateFen != this.states[this.states.length-1]) debugger;
+// this.states.pop();
}
postUndo(move) {
this.sentryPush.pop();
}
- isAttacked(sq, colors) {
+ isAttacked(sq, color) {
return (
- super.isAttacked(sq, colors) ||
- this.isAttackedByLancer(sq, colors) ||
- this.isAttackedBySentry(sq, colors)
+ super.isAttacked(sq, color) ||
+ this.isAttackedByLancer(sq, color) ||
+ this.isAttackedBySentry(sq, color)
);
}
- isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) {
+ isAttackedBySlideNJump([x, y], color, piece, steps, oneStep) {
for (let step of steps) {
let rx = x + step[0],
ry = y + step[1];
}
if (
V.OnBoard(rx, ry) &&
- this.getPiece(rx, ry) === piece &&
- colors.includes(this.getColor(rx, ry)) &&
+ this.getPiece(rx, ry) == piece &&
+ this.getColor(rx, ry) == color &&
!this.isImmobilized([rx, ry])
) {
return true;
return false;
}
- isAttackedByPawn([x, y], colors) {
- for (let c of colors) {
- const pawnShift = c == "w" ? 1 : -1;
- if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
- for (let i of [-1, 1]) {
- if (
- y + i >= 0 &&
- y + i < V.size.y &&
- this.getPiece(x + pawnShift, y + i) == V.PAWN &&
- this.getColor(x + pawnShift, y + i) == c &&
- !this.isImmobilized([x + pawnShift, y + i])
- ) {
- return true;
- }
+ isAttackedByPawn([x, y], color) {
+ const pawnShift = (color == "w" ? 1 : -1);
+ if (x + pawnShift >= 0 && x + pawnShift < V.size.x) {
+ for (let i of [-1, 1]) {
+ if (
+ y + i >= 0 &&
+ y + i < V.size.y &&
+ this.getPiece(x + pawnShift, y + i) == V.PAWN &&
+ this.getColor(x + pawnShift, y + i) == color &&
+ !this.isImmobilized([x + pawnShift, y + i])
+ ) {
+ return true;
}
}
}
return false;
}
- isAttackedByLancer([x, y], colors) {
+ isAttackedByLancer([x, y], color) {
for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
// If in this direction there are only enemy pieces and empty squares,
// and we meet a lancer: can he reach us?
V.OnBoard(coord.x, coord.y) &&
(
this.board[coord.x][coord.y] == V.EMPTY ||
- colors.includes(this.getColor(coord.x, coord.y))
+ this.getColor(coord.x, coord.y) == color
)
) {
if (
selfAttack([x1, y1], [x2, y2]) {
const color = this.getColor(x1, y1);
const sliderAttack = (allowedSteps, lancer) => {
- const deltaX = x2 - x1;
- const deltaY = y2 - y1;
- const step = [ deltaX / Math.abs(deltaX), deltaY / Math.abs(deltaY) ];
- if (allowedSteps.every(st => st[0] != step[0] || st[1] != step[1]))
+ const deltaX = x2 - x1,
+ absDeltaX = Math.abs(deltaX);
+ const deltaY = y2 - y1,
+ absDeltaY = Math.abs(deltaY);
+ const step = [ deltaX / absDeltaX || 0, deltaY / absDeltaY || 0 ];
+ if (
+ // Check that the step is a priori valid:
+ (absDeltaX != absDeltaY && deltaX != 0 && deltaY != 0) ||
+ allowedSteps.every(st => st[0] != step[0] || st[1] != step[1])
+ ) {
return false;
+ }
let sq = [ x1 + step[0], y1 + step[1] ];
- while (sq[0] != x2 && sq[1] != y2) {
+ while (sq[0] != x2 || sq[1] != y2) {
if (
// NOTE: no need to check OnBoard in this special case
(!lancer && this.board[sq[0]][sq[1]] != V.EMPTY) ||
return false;
}
- isAttackedBySentry([x, y], colors) {
+ isAttackedBySentry([x, y], color) {
// Attacked by sentry means it can self-take our king.
// Just check diagonals of enemy sentry(ies), and if it reaches
// one of our pieces: can I self-take?
- const color = V.GetOppCol(colors[0]);
+ const myColor = V.GetOppCol(color);
let candidates = [];
for (let i=0; i<V.size.x; i++) {
for (let j=0; j<V.size.y; j++) {
if (
this.getPiece(i,j) == V.SENTRY &&
- colors.includes(this.getColor(i,j)) &&
+ this.getColor(i,j) == color &&
!this.isImmobilized([i, j])
) {
for (let step of V.steps[V.BISHOP]) {
}
if (
V.OnBoard(sq[0], sq[1]) &&
- this.getColor(sq[0], sq[1]) == color
+ this.getColor(sq[0], sq[1]) == myColor
) {
candidates.push([ sq[0], sq[1] ]);
}
return (!choice.second ? choice : [choice, choice.second]);
}
+ // For moves notation:
+ static get LANCER_DIRNAMES() {
+ return {
+ 'c': "N",
+ 'd': "NE",
+ 'e': "E",
+ 'f': "SE",
+ 'g': "S",
+ 'h': "SW",
+ 'm': "W",
+ 'o': "NW"
+ };
+ }
+
getNotation(move) {
// Special case "king takes jailer" is a pass move
if (move.appear.length == 0 && move.vanish.length == 0) return "pass";
+ let notation = undefined;
if (this.subTurn == 2) {
// Do not consider appear[1] (sentry) for sentry pushes
const simpleMove = {
start: move.start,
end: move.end
};
- return super.getNotation(simpleMove);
- }
- return super.getNotation(move);
+ notation = super.getNotation(simpleMove);
+ } 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];
+ return notation;
}
};