From 204d7acddc210d3177574be2fd66d8ba0804613e Mon Sep 17 00:00:00 2001 From: Jesse McDonald Date: Fri, 24 Apr 2020 23:16:21 -0500 Subject: [PATCH] add the ability to detect checkmate without taking the king --- js/pacosako.js | 42 +++++++++++++++++++++++++++++++++--------- js/pacosako_ui.js | 10 ++++++++-- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/js/pacosako.js b/js/pacosako.js index f57a881..113fb39 100644 --- a/js/pacosako.js +++ b/js/pacosako.js @@ -4,7 +4,8 @@ import {Iterator} from './iterator.js'; /* Game states */ const PLAYING = 'playing'; -const ENDED = 'ended'; +const RESIGNED = 'resigned'; +const CHECKMATE = 'checkmate'; /* Sides */ const LIGHT = 'light'; @@ -393,7 +394,7 @@ function addHistory(game) { result += '(' + move.promotion.toUpperCase() + ')'; } - if (move.took === KING) { + if (game.status === CHECKMATE) { result += '#'; } else if (game.isInCheck()) { result += '+'; @@ -936,7 +937,31 @@ class Game { this._redo = []; 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); @@ -960,7 +985,7 @@ class Game { this._checkCache = {}; this._moves.push(move); this._redo = []; - this._status = ENDED; + this._status = RESIGNED; addHistory(this); } @@ -974,11 +999,10 @@ class Game { } get winner() { - const move = this.lastMove; - if (move && move.resign) { + if (this._status === RESIGNED) { return otherSide(this.lastMove.side); - } else if (move && move.took === KING) { - return move.side; + } else if (this._status === CHECKMATE) { + return this.lastMove.side; } else { return null; } @@ -1063,7 +1087,7 @@ export default { Board, Game, /* Game States */ - PLAYING, ENDED, + PLAYING, RESIGNED, CHECKMATE, /* Sides */ LIGHT, DARK, diff --git a/js/pacosako_ui.js b/js/pacosako_ui.js index 7ed07c1..58558c6 100644 --- a/js/pacosako_ui.js +++ b/js/pacosako_ui.js @@ -369,7 +369,9 @@ $(function (){ } 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 += (winner === PS.LIGHT ? 'Light' : 'Dark') + ' player won!'; @@ -486,7 +488,11 @@ $(function (){ const winner = currentGame.winner; const lastMove = currentGame.lastMove || {}; 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 = { lightName,