add the ability to detect checkmate without taking the king

This commit is contained in:
Jesse D. McDonald 2020-04-24 23:16:21 -05:00
parent db5f56735b
commit 204d7acddc
2 changed files with 41 additions and 11 deletions

View File

@ -4,7 +4,8 @@ import {Iterator} from './iterator.js';
/* Game states */ /* Game states */
const PLAYING = 'playing'; const PLAYING = 'playing';
const ENDED = 'ended'; const RESIGNED = 'resigned';
const CHECKMATE = 'checkmate';
/* Sides */ /* Sides */
const LIGHT = 'light'; const LIGHT = 'light';
@ -393,7 +394,7 @@ function addHistory(game) {
result += '(' + move.promotion.toUpperCase() + ')'; result += '(' + move.promotion.toUpperCase() + ')';
} }
if (move.took === KING) { if (game.status === CHECKMATE) {
result += '#'; result += '#';
} else if (game.isInCheck()) { } else if (game.isInCheck()) {
result += '+'; result += '+';
@ -936,7 +937,31 @@ class Game {
this._redo = []; this._redo = [];
if (took === KING) { if (took === KING) {
this._status = ENDED; this._status = CHECKMATE;
} else if (this._history && this.isInCheck(this._player)) {
/*
* NOTE: Preemptive checkmate detection is not performed on
* simulated games (without history) to prevent recursion.
*/
let canEscape = false;
for (const tryFrom of this._board.findPieces(this._player, true)) {
for (const tryTo of this.legalMoves(this._player, tryFrom)) {
if (!this.isDeadEnd(tryFrom, tryTo)) {
canEscape = true;
break;
}
}
if (canEscape) {
break;
}
}
if (!canEscape) {
this._status = CHECKMATE;
}
} }
addHistory(this); addHistory(this);
@ -960,7 +985,7 @@ class Game {
this._checkCache = {}; this._checkCache = {};
this._moves.push(move); this._moves.push(move);
this._redo = []; this._redo = [];
this._status = ENDED; this._status = RESIGNED;
addHistory(this); addHistory(this);
} }
@ -974,11 +999,10 @@ class Game {
} }
get winner() { get winner() {
const move = this.lastMove; if (this._status === RESIGNED) {
if (move && move.resign) {
return otherSide(this.lastMove.side); return otherSide(this.lastMove.side);
} else if (move && move.took === KING) { } else if (this._status === CHECKMATE) {
return move.side; return this.lastMove.side;
} else { } else {
return null; return null;
} }
@ -1063,7 +1087,7 @@ export default {
Board, Game, Board, Game,
/* Game States */ /* Game States */
PLAYING, ENDED, PLAYING, RESIGNED, CHECKMATE,
/* Sides */ /* Sides */
LIGHT, DARK, LIGHT, DARK,

View File

@ -369,7 +369,9 @@ $(function (){
} }
if (winner) { if (winner) {
if (lastMove && lastMove.resign) { if (game.status === PS.CHECKMATE) {
msg += 'Checkmate! ';
} else if (lastMove && lastMove.resign) {
msg += (lastMove.side === PS.LIGHT ? 'Light' : 'Dark') + ' player resigned. '; msg += (lastMove.side === PS.LIGHT ? 'Light' : 'Dark') + ' player resigned. ';
} }
msg += (winner === PS.LIGHT ? 'Light' : 'Dark') + ' player won!'; msg += (winner === PS.LIGHT ? 'Light' : 'Dark') + ' player won!';
@ -486,7 +488,11 @@ $(function (){
const winner = currentGame.winner; const winner = currentGame.winner;
const lastMove = currentGame.lastMove || {}; const lastMove = currentGame.lastMove || {};
const lastMeta = lastMove.meta || {}; const lastMeta = lastMove.meta || {};
const status = !winner ? null : (lastMove.took === PS.KING) ? 'mate' : 'ended'; const status =
(currentGame.status === PS.PLAYING) ? null :
(currentGame.status === PS.CHECKMATE) ? 'checkmate' :
(currentGame.status === PS.RESIGNED) ? 'resigned' :
'ended';
const meta = { const meta = {
lightName, lightName,