From 1732078da71cd67efb3934bfaf88763051721ae7 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Tue, 16 Aug 2016 15:49:29 +0200 Subject: [PATCH] Use Sunsetter for local crazyhouse analysis --- .gitmodules | 3 + public/vendor/Sunsetter | 1 + ui/analyse/src/ceval/cevalCtrl.js | 6 +- ui/analyse/src/ceval/cevalPool.js | 3 +- .../{cevalWorker.js => stockfishWorker.js} | 2 +- ui/analyse/src/ceval/sunsetterWorker.js | 125 ++++++++++++++++++ ui/analyse/src/ctrl.js | 2 +- 7 files changed, 136 insertions(+), 6 deletions(-) create mode 160000 public/vendor/Sunsetter rename ui/analyse/src/ceval/{cevalWorker.js => stockfishWorker.js} (96%) create mode 100644 ui/analyse/src/ceval/sunsetterWorker.js diff --git a/.gitmodules b/.gitmodules index 438970e830..94b763b57d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "public/vendor/stockfish.js"] path = public/vendor/stockfish.js url = https://github.com/niklasf/stockfish.js +[submodule "public/vendor/Sunsetter"] + path = public/vendor/Sunsetter + url = https://github.com/niklasf/Sunsetter.git diff --git a/public/vendor/Sunsetter b/public/vendor/Sunsetter new file mode 160000 index 0000000000..3d0122e112 --- /dev/null +++ b/public/vendor/Sunsetter @@ -0,0 +1 @@ +Subproject commit 3d0122e11268a11edb9c95916f8a3fc062b8ce5c diff --git a/ui/analyse/src/ceval/cevalCtrl.js b/ui/analyse/src/ceval/cevalCtrl.js index 37b3740d5e..9d27007969 100644 --- a/ui/analyse/src/ceval/cevalCtrl.js +++ b/ui/analyse/src/ceval/cevalCtrl.js @@ -1,6 +1,8 @@ var m = require('mithril'); var makePool = require('./cevalPool'); var dict = require('./cevalDict'); +var stockfishWorker = require('./stockfishWorker'); +var sunsetterWorker = require('./sunsetterWorker'); module.exports = function(possible, variant, emit) { @@ -12,12 +14,12 @@ module.exports = function(possible, variant, emit) { var allowed = m.prop(true); var enabled = m.prop(possible() && allowed() && lichess.storage.get(storageKey) === '1'); var started = false; + var engine = variant.key !== 'crazyhouse' ? stockfishWorker : sunsetterWorker; var pool = makePool({ - path: '/assets/vendor/stockfish.js/stockfish.js', // Can't CDN because same-origin policy minDepth: minDepth, maxDepth: maxDepth, variant: variant - }, nbWorkers); + }, engine, nbWorkers); var onEmit = function(res) { curDepth = res.eval.depth; diff --git a/ui/analyse/src/ceval/cevalPool.js b/ui/analyse/src/ceval/cevalPool.js index 2233e1ec75..fbaf486b18 100644 --- a/ui/analyse/src/ceval/cevalPool.js +++ b/ui/analyse/src/ceval/cevalPool.js @@ -1,7 +1,6 @@ var m = require('mithril'); -var makeWorker = require('./cevalWorker'); -module.exports = function(opts, nb) { +module.exports = function(opts, makeWorker, nb) { var workers = []; var token = -1; diff --git a/ui/analyse/src/ceval/cevalWorker.js b/ui/analyse/src/ceval/stockfishWorker.js similarity index 96% rename from ui/analyse/src/ceval/cevalWorker.js rename to ui/analyse/src/ceval/stockfishWorker.js index e60abb9fec..c2376dfe86 100644 --- a/ui/analyse/src/ceval/cevalWorker.js +++ b/ui/analyse/src/ceval/stockfishWorker.js @@ -56,7 +56,7 @@ module.exports = function(opts, name) { var reboot = function() { if (instance) instance.terminate(); - instance = new Worker(opts.path); + instance = new Worker('/assets/vendor/stockfish.js/stockfish.js'); busy = false; stopping = false; var uciVariant = variantMap[opts.variant.key]; diff --git a/ui/analyse/src/ceval/sunsetterWorker.js b/ui/analyse/src/ceval/sunsetterWorker.js new file mode 100644 index 0000000000..1f36f2e60e --- /dev/null +++ b/ui/analyse/src/ceval/sunsetterWorker.js @@ -0,0 +1,125 @@ +var m = require('mithril'); + +module.exports = function(opts, name) { + + var instance = null; + var busy = false; + var stopping = false; + + // Sunsetter always plays only moves right away. Count the number of played + // moves to show the correct mate in #n. + var onlyMoves = 0; + var best; + + var send = function(text) { + instance.postMessage(text); + }; + + var processOutput = function(text, work) { + if (text === 'tellics stopped') { + busy = false; + stopping = false; + onlyMoves = 0; + best = undefined; + return; + } + if (stopping) return; + + if (text.indexOf('move ') == 0) { + onlyMoves++; + best = text.split(' ')[1]; + send('analyze'); + return; + } + + var depth, cp, mate; + + var matches = text.match(/(\d+)\s+([-+]?\d+)\s+(\d+)\s+(\d+)\s+([a-h1-8=@PNBRQK]+).*/); + if (matches) { + depth = parseInt(matches[1], 10); + cp = parseInt(matches[2], 10); + if (!onlyMoves) best = matches[5]; + } else { + matches = text.match(/Found move:\s+([a-h1-8=@PNBRQK]+)\s+([-+]?\d+)\s.*/); + if (matches) { + depth = opts.maxDepth; + cp = parseInt(matches[2], 10); + if (!onlyMoves) best = matches[1]; + stopping = true; + send('force'); + send('tellics stopped'); + } else { + return; + } + } + + if (!onlyMoves && depth < opts.minDepth) return; + + if ((work.ply + onlyMoves) % 2 == 1) { + if (cp) cp = -cp; + if (mate) mate = -mate; + } + + // transform mate scores + if (cp > 20000) { + mate = Math.floor((30000 - cp) / 10); + cp = undefined; + } else if (cp < -20000) { + mate = Math.floor((-30000 - cp) / 10); + cp = undefined; + } + + if (mate) { + if (mate > 0) mate += onlyMoves; + else mate -= onlyMoves; + } + + work.emit({ + work: work, + eval: { + depth: depth, + cp: cp, + mate: mate, + best: best + }, + name: name + }); + }; + + var reboot = function() { + if (instance) instance.terminate(); + instance = new Worker('/assets/vendor/Sunsetter/sunsetter.js'); + busy = false; + stopping = false; + onlyMoves = 0; + send('xboard'); + }; + + reboot(); + + return { + start: function(work) { + if (busy) reboot(); + busy = true; + send('variant ' + opts.variant.key); + send('setboard ' + work.position); + send('force'); + for (var i = 0; i < work.moves.length; i++) { + send(work.moves[i]); + } + send('analyze'); + send('go'); + instance.onmessage = function(msg) { + processOutput(msg.data, work); + }; + }, + stop: function() { + if (!busy) return; + stopping = true; + onlyMoves = 0; + best = undefined; + send('exit'); + send('tellics stopped'); + } + }; +}; diff --git a/ui/analyse/src/ctrl.js b/ui/analyse/src/ctrl.js index ab5d56e000..22df8bf0c6 100644 --- a/ui/analyse/src/ctrl.js +++ b/ui/analyse/src/ctrl.js @@ -387,7 +387,7 @@ module.exports = function(opts) { }); }.bind(this); - var cevalVariants = ['standard', 'fromPosition', 'chess960', 'kingOfTheHill', 'threeCheck', 'horde', 'racingKings', 'atomic']; + var cevalVariants = ['standard', 'fromPosition', 'chess960', 'kingOfTheHill', 'threeCheck', 'horde', 'racingKings', 'atomic', 'crazyhouse']; var cevalPossible = function() { return (util.synthetic(this.data) || !game.playable(this.data)) && cevalVariants.indexOf(this.data.game.variant.key) !== -1;