From 2171bb00ee0473fbdc6c7e0f3b2b0de38d94ce1d Mon Sep 17 00:00:00 2001 From: Jesse McDonald Date: Fri, 24 Apr 2020 22:07:16 -0500 Subject: [PATCH] block chain moves which are known dead ends --- js/pacosako.js | 66 +++++++++++++++++++++++++++++++++++++++++++++++ js/pacosako_ui.js | 11 +++----- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/js/pacosako.js b/js/pacosako.js index 24121b8..f57a881 100644 --- a/js/pacosako.js +++ b/js/pacosako.js @@ -757,6 +757,72 @@ class Game { return recordCheck(this, false); } + isDeadEnd(from, to) { + const side = this._player; + const sim = new Game(this); + sim.dropHistory(); + + try { + sim.move(from, to); + } catch (err) { + /* illegal moves are automatic dead ends */ + return true; + } + + if (!sim._board.phantom) { + /* moves that leave the player in check are also dead ends */ + /* otherwise this is a legal non-chain move, so not a dead end */ + return sim.isInCheck(side); + } + + const seen = new Set(); + let queue = [sim]; + let counter = 0; + + while (queue.length > 0) { + const game = queue[0]; + queue = queue.slice(1); + + seen.add(game._board.toString()); + + /* look for another piece that can reach the king or continue the chain */ + for (const toNext of game.legalMoves(side, PHANTOM, true)) { + if (counter >= 300) { + /* this is taking too long */ + const newMoves = game._moves.length - this._moves.length; + console.log(`stopped looking for dead end after ${counter} moves (max length was ${newMoves})`); + return undefined; + } + + ++counter; + + const game2 = new Game(game); + try { + game2.move(PHANTOM, toNext); + } catch (err) { + /* internal error */ + console.log('isDeadEnd:', err); + continue; + } + + if (!game2._board.phantom) { + /* skip moves that would leave the player in check */ + if (!game2.isInCheck(side)) { + /* it's a legal non-chain move, so not a dead end */ + return false; + } + } + else if (!seen.has(game2._board.toString())) { + /* we haven't seen this exact board state before */ + queue.push(game2); + } + } + } + + /* an exhaustive search didn't uncover any legal ways to end the chain */ + return true; + } + move(from, to, meta) { if (this._status !== PLAYING) { throw { message: "can't move, game is already over" }; diff --git a/js/pacosako_ui.js b/js/pacosako_ui.js index 8e784f7..79ab83a 100644 --- a/js/pacosako_ui.js +++ b/js/pacosako_ui.js @@ -147,14 +147,9 @@ $(function (){ const from = piece.data('location'); const legals = currentGame.legalMoves(side, from); for (const there of legals) { - try { - const preview = new PS.Game(currentGame); - preview.dropHistory(); - preview.move(from, there); - if (preview.isInCheck(side)) { - continue; - } - } catch (err) {} + if (currentGame.isDeadEnd(from, there)) { + continue; + } const square = cbSquare(there); square.addClass('cb-legal')