);
}
+ static get SomeHiddenMoves() {
+ return true;
+ }
+
static get HasCastle() {
return false;
}
return "byrow";
}
+ getPPpath(m) {
+ // Show the piece taken, if any, and not multiple pawns:
+ if (m.vanish.length == 1) return "Apocalypse/empty";
+ return m.vanish[1].c + m.vanish[1].p;
+ }
+
static get PIECES() {
return [V.PAWN, V.KNIGHT];
}
if (['P','p'].includes(row[i])) pawns[row[i]]++;
if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
else {
- const num = parseInt(row[i]);
+ const num = parseInt(row[i], 10);
if (isNaN(num)) return false;
sumElts += num;
}
// 4) Check whiteMove
if (
(
- fenParsed.turn == "w" &&
+ fenParsed.turn == "b" &&
// NOTE: do not check really JSON stringified move...
(!fenParsed.whiteMove || fenParsed.whiteMove == "-")
)
||
- (fenParsed.turn == "b" && fenParsed.whiteMove != "-")
+ (fenParsed.turn == "w" && fenParsed.whiteMove != "-")
) {
return false;
}
}
getFlagsFen() {
- return this.penaltyFlags.join("");
+ return (
+ this.penaltyFlags['w'].toString() + this.penaltyFlags['b'].toString()
+ );
}
setOtherVariables(fen) {
}
setFlags(fenflags) {
- this.penaltyFlags = [0, 1].map(i => parseInt(fenflags[i]));
+ this.penaltyFlags = {
+ 'w': parseInt(fenflags[0], 10),
+ 'b': parseInt(fenflags[1], 10)
+ };
}
getWhitemoveFen() {
this.turn = V.GetOppCol(color);
const oppMoves = super.getAllValidMoves();
this.turn = color;
- // For each opponent's move, generate valid moves [from sq]
+ // For each opponent's move, generate valid moves [from sq if same color]
let speculations = [];
oppMoves.forEach(m => {
V.PlayOnBoard(this.board, m);
const newValidMoves =
!!sq
- ? super.getPotentialMovesFrom(sq)
+ ? (
+ this.getColor(sq[0], sq[1]) == color
+ ? super.getPotentialMovesFrom(sq)
+ : []
+ )
: super.getAllValidMoves();
newValidMoves.forEach(vm => {
const mHash = "m" + vm.start.x + vm.start.y + vm.end.x + vm.end.y;
if (!moveSet[mHash]) {
moveSet[mHash] = true;
- vm.illegal = true; //potentially illegal!
+ vm.end.illegal = true; //potentially illegal!
speculations.push(vm);
}
});
// If 0 or 1 horsemen, promote in knight
let knightCounter = 0;
let emptySquares = [];
- for (let i=0; i<V.size.x; i++) {
- for (let j=0; j<V.size.y; j++) {
+ for (let i = 0; i < V.size.x; i++) {
+ for (let j = 0; j < V.size.y; j++) {
if (this.board[i][j] == V.EMPTY) emptySquares.push([i, j]);
else if (
this.getColor(i, j) == color &&
}
if (knightCounter <= 1) finalPieces = [V.KNIGHT];
else {
- // Generate all possible landings
+ // Generate all possible landings, maybe capturing something on the way
+ let capture = undefined;
+ if (this.board[x2][y2] != V.EMPTY) {
+ capture = JSON.parse(JSON.stringify({
+ x: x2,
+ y: y2,
+ c: this.getColor(x2, y2),
+ p: this.getPiece(x2, y2)
+ }));
+ }
emptySquares.forEach(sq => {
- if (sq[0] != lastRank)
- moves.push(this.getBasicMove([x1, y1], [sq[0], sq[1]]));
+ if (sq[0] != lastRank) {
+ let newMove = this.getBasicMove([x1, y1], [sq[0], sq[1]]);
+ if (!!capture) newMove.vanish.push(capture);
+ moves.push(newMove);
+ }
});
return;
}
// White and black (partial) moves were played: merge
resolveSynchroneMove(move) {
- let m = [this.whiteMove, move];
- for (let i of [0, 1]) {
- if (!!m[i].illegal) {
- // Either an anticipated capture of something which didn't move
- // (or not to the right square), or a push through blocus.
- if (
+ let m1 = this.whiteMove;
+ let m2 = move;
+ const movingLikeCapture = (m) => {
+ const shift = (m.vanish[0].c == 'w' ? -1 : 1);
+ return (
+ m.start.x + shift == m.end.x &&
+ Math.abs(m.end.y - m.start.y) == 1
+ );
+ };
+ const isPossible = (m, other) => {
+ return (
+ (
+ m.vanish[0].p == V.KNIGHT &&
(
- // Push attempt
- m[i].start.y == m[i].end.y &&
- (m[1-i].start.x != m[i].end.x || m[1-i].start.y != m[i].end.y)
+ m.vanish.length == 1 ||
+ m.vanish[1].c != m.vanish[0].c ||
+ // Self-capture attempt
+ (
+ !other.end.illegal &&
+ other.end.x == m.end.x &&
+ other.end.y == m.end.y
+ )
)
- ||
+ )
+ ||
+ (
+ m.vanish[0].p == V.PAWN &&
+ !other.end.illegal &&
(
- // Capture attempt
- Math.abs(m[i].start.y - m[i].end.y) == 1 &&
- (m[1-i].end.x != m[i].end.x || m[1-i].end.y != m[i].end.y)
+ (
+ // Promotion attempt
+ m.end.x == (m.vanish[0].c == "w" ? 0 : V.size.x - 1) &&
+ other.vanish.length == 2 &&
+ other.vanish[1].p == V.KNIGHT &&
+ other.vanish[1].c == m.vanish[0].c
+ )
+ ||
+ (
+ // Moving attempt
+ !movingLikeCapture(m) &&
+ other.start.x == m.end.x &&
+ other.start.y == m.end.y
+ )
+ ||
+ (
+ // Capture attempt
+ movingLikeCapture(m) &&
+ other.end.x == m.end.x &&
+ other.end.y == m.end.y
+ )
)
- ) {
- // Just discard the move, and add a penalty point
- this.penaltyFlags[m[i].vanish[0].c]++;
- m[i] = null;
- }
- }
+ )
+ );
+ };
+ if (!!m1.end.illegal && !isPossible(m1, m2)) {
+ // Either an anticipated capture of something which didn't move
+ // (or not to the right square), or a push through blocus.
+ // ==> Just discard the move, and add a penalty point
+ this.penaltyFlags[m1.vanish[0].c]++;
+ m1.isNull = true;
}
-
+ if (!!m2.end.illegal && !isPossible(m2, m1)) {
+ this.penaltyFlags[m2.vanish[0].c]++;
+ m2.isNull = true;
+ }
+ if (!!m1.isNull) m1 = null;
+ if (!!m2.isNull) m2 = null;
+ // If one move is illegal, just execute the other
+ if (!m1 && !!m2) return m2;
+ if (!m2 && !!m1) return m1;
// For PlayOnBoard (no need for start / end, irrelevant)
let smove = {
appear: [],
vanish: []
};
- const m1 = m[0],
- m2 = m[1];
- // If one move is illegal, just execute the other
- if (!m1 && !!m2) return m2;
- if (!m2 && !!m1) return m1;
if (!m1 && !m2) return smove;
- // Both move are now legal:
+ // Both moves are now legal or at least possible:
smove.vanish.push(m1.vanish[0]);
smove.vanish.push(m2.vanish[0]);
if ((m1.end.x != m2.end.x) || (m1.end.y != m2.end.y)) {
smove.vanish.push(m2.vanish[1]);
}
} else {
- // Collision: both disappear except if different kinds (knight remains)
+ // Collision: priority to the anticipated capture, if any.
+ // If ex-aequo: knight wins (higher risk), or both disappears.
+ // Then, priority to the knight vs pawn: remains.
+ // Finally: both disappears.
+ let remain = null;
const p1 = m1.vanish[0].p;
const p2 = m2.vanish[0].p;
- if ([p1, p2].includes(V.KNIGHT) && [p1, p2].includes(V.PAWN)) {
+ if (!!m1.end.illegal && !m2.end.illegal) remain = { c: 'w', p: p1 };
+ else if (!!m2.end.illegal && !m1.end.illegal) remain = { c: 'b', p: p2 };
+ if (!remain) {
+ // Either both are illegal or both are legal
+ if (p1 == V.KNIGHT && p2 == V.PAWN) remain = { c: 'w', p: p1 };
+ else if (p2 == V.KNIGHT && p1 == V.PAWN) remain = { c: 'b', p: p2 };
+ // If remain is still null: same type same risk, both disappear
+ }
+ if (!!remain) {
smove.appear.push({
x: m1.end.x,
y: m1.end.y,
- p: V.KNIGHT,
- c: (p1 == V.KNIGHT ? 'w' : 'b')
+ p: remain.p,
+ c: remain.c
});
}
}
this.whiteMove = move;
return;
}
-
// A full turn just ended:
const smove = this.resolveSynchroneMove(move);
V.PlayOnBoard(this.board, smove);
else this.whiteMove = move.whiteMove;
}
- getCheckSquares(color) {
+ getCheckSquares() {
return [];
}
return "1-0"; //fmCount['b'] == 0
}
// Check penaltyFlags: if a side has 2 or more, it loses
- if (this.penaltyFlags.every(f => f == 2)) return "1/2";
- if (this.penaltyFlags[0] == 2) return "0-1";
- if (this.penaltyFlags[1] == 2) return "1-0";
+ if (Object.values(this.penaltyFlags).every(v => v == 2)) return "1/2";
+ if (this.penaltyFlags['w'] == 2) return "0-1";
+ if (this.penaltyFlags['b'] == 2) return "1-0";
if (!this.atLeastOneMove('w') || !this.atLeastOneMove('b'))
// Stalemate (should be very rare)
return "1/2";
// TODO: this situation should not happen
return null;
- if (Math.random() < 0.5)
- // Return a random move
- return moves[randInt(moves.length)];
-
// Rank moves at depth 1:
- // try to capture something (not re-capturing)
+ let validMoves = [];
+ let illegalMoves = [];
moves.forEach(m => {
- V.PlayOnBoard(this.board, m);
- m.eval = this.evalPosition();
- V.UndoOnBoard(this.board, m);
+ // Warning: m might be illegal!
+ if (!m.end.illegal) {
+ V.PlayOnBoard(this.board, m);
+ m.eval = this.evalPosition();
+ V.UndoOnBoard(this.board, m);
+ validMoves.push(m);
+ } else illegalMoves.push(m);
});
- moves.sort((a, b) => {
+
+ const illegalRatio = illegalMoves.length / moves.length;
+ if (Math.random() < illegalRatio)
+ // Return a random illegal move
+ return illegalMoves[randInt(illegalMoves.length)];
+
+ validMoves.sort((a, b) => {
return (color == "w" ? 1 : -1) * (b.eval - a.eval);
});
let candidates = [0];
- for (let i = 1; i < moves.length && moves[i].eval == moves[0].eval; i++)
+ for (
+ let i = 1;
+ i < validMoves.length && validMoves[i].eval == moves[0].eval;
+ i++
+ ) {
candidates.push(i);
- return moves[candidates[randInt(candidates.length)]];
+ }
+ return validMoves[candidates[randInt(candidates.length)]];
}
getNotation(move) {
// Basic system: piece + init + dest square
return (
- move.vanish[0].p.toUpperCase() +
+ (move.vanish[0].p == V.KNIGHT ? "N" : "") +
V.CoordsToSquare(move.start) +
V.CoordsToSquare(move.end)
);