add pnacl support for ceval
parent
1739bab7f0
commit
142195269d
|
@ -44,3 +44,7 @@
|
|||
path = public/vendor/Sunsetter8
|
||||
url = https://github.com/niklasf/Sunsetter8
|
||||
branch = js
|
||||
[submodule "public/vendor/stockfish.pexe"]
|
||||
path = public/vendor/stockfish.pexe
|
||||
url = https://github.com/niklasf/stockfish.pexe
|
||||
branch = ddugovic
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 6e07387f0adef9d521dd2eb4b1400031d4591e14
|
|
@ -2,8 +2,8 @@ var m = require('mithril');
|
|||
var makePool = require('./cevalPool');
|
||||
var dict = require('./cevalDict');
|
||||
var util = require('../util');
|
||||
var stockfishWorker = require('./stockfishWorker');
|
||||
var sunsetterWorker = require('./sunsetterWorker');
|
||||
var stockfishProtocol = require('./stockfishProtocol');
|
||||
var sunsetterProtocol = require('./sunsetterProtocol');
|
||||
|
||||
module.exports = function(possible, variant, emit) {
|
||||
|
||||
|
@ -16,13 +16,22 @@ 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({
|
||||
minDepth: minDepth,
|
||||
maxDepth: maxDepth,
|
||||
variant: variant
|
||||
}, engine, nbWorkers);
|
||||
var pool;
|
||||
if (variant.key !== 'crazyhouse') {
|
||||
pool = makePool(stockfishProtocol, {
|
||||
asmjs: '/assets/vendor/stockfish.js/stockfish.js',
|
||||
pnacl: '/assets/vendor/stockfish.pexe/nacl/stockfish.nmf'
|
||||
}, {
|
||||
minDepth: minDepth,
|
||||
maxDepth: maxDepth,
|
||||
variant: variant,
|
||||
});
|
||||
} else {
|
||||
pool = makePool(sunsetterProtocol, {
|
||||
asmjs: '/assets/vendor/Sunsetter8/sunsetter.js'
|
||||
});
|
||||
}
|
||||
|
||||
// adjusts maxDepth based on nodes per second
|
||||
var npsRecorder = (function() {
|
||||
|
|
|
@ -1,7 +1,65 @@
|
|||
var m = require('mithril');
|
||||
|
||||
module.exports = function(opts, makeWorker, nb) {
|
||||
function makeHelper(makeWorker, terminateWorker, poolOpts, makeProtocol, protocolOpts) {
|
||||
var worker, protocol, api;
|
||||
|
||||
var boot = function () {
|
||||
worker = makeWorker(poolOpts);
|
||||
protocol = makeProtocol(api, protocolOpts);
|
||||
worker.addEventListener('message', function(e) {
|
||||
protocol.received(e.data);
|
||||
}, true);
|
||||
};
|
||||
|
||||
var stop = function() {
|
||||
var stopped = m.deferred(false);
|
||||
setTimeout(function () {
|
||||
stopped.reject();
|
||||
}, 1000);
|
||||
return protocol.stop(stopped);
|
||||
};
|
||||
|
||||
api = {
|
||||
send: function(text) {
|
||||
worker.postMessage(text);
|
||||
},
|
||||
start: function (work) {
|
||||
stop().then(function () {
|
||||
protocol.start(work);
|
||||
}, function () {
|
||||
terminateWorker(worker);
|
||||
boot();
|
||||
});
|
||||
},
|
||||
stop: stop
|
||||
};
|
||||
|
||||
boot();
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
function makeWebWorker(makeProtocol, poolOpts, protocolOpts) {
|
||||
return makeHelper(function () {
|
||||
return new Worker(poolOpts.asmjs);
|
||||
}, function (worker) {
|
||||
worker.terminate();
|
||||
}, poolOpts, makeProtocol, protocolOpts);
|
||||
}
|
||||
|
||||
function makePNaClModule(makeProtocol, poolOpts, protocolOpts) {
|
||||
return makeHelper(function () {
|
||||
var module = document.createElement('embed');
|
||||
module.setAttribute('src', poolOpts.pnacl);
|
||||
module.setAttribute('type', 'application/x-pnacl');
|
||||
module.setAttribute('width', '0');
|
||||
module.setAttribute('height', '0');
|
||||
document.body.appendChild(module);
|
||||
return module;
|
||||
}, function () {}, poolOpts, makeProtocol, protocolOpts);
|
||||
}
|
||||
|
||||
module.exports = function(makeProtocol, poolOpts, protocolOpts) {
|
||||
var workers = [];
|
||||
var token = -1;
|
||||
|
||||
|
@ -12,9 +70,13 @@ module.exports = function(opts, makeWorker, nb) {
|
|||
};
|
||||
|
||||
var initWorkers = function() {
|
||||
if (!workers.length)
|
||||
for (var i = 1; i <= nb; i++)
|
||||
workers.push(makeWorker(opts, 'W' + i));
|
||||
if (workers.length) return;
|
||||
|
||||
if (poolOpts.pnacl && navigator.mimeTypes['application/x-pnacl'])
|
||||
workers.push(makePNaClModule(makeProtocol, poolOpts, protocolOpts));
|
||||
else
|
||||
for (var i = 1; i <= 4; i++)
|
||||
workers.push(makeWebWorker(makeProtocol, poolOpts, protocolOpts));
|
||||
}
|
||||
|
||||
var stopAll = function() {
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var legacyVariantMap = {
|
||||
fromPosition: 'Chess960',
|
||||
chess960: 'Chess960',
|
||||
atomic: 'Atomic',
|
||||
horde: 'Horde',
|
||||
crazyhouse: 'House',
|
||||
kingOfTheHill: 'KingOfTheHill',
|
||||
racingKings: 'Race',
|
||||
threeCheck: '3Check',
|
||||
antichess: 'Anti'
|
||||
};
|
||||
|
||||
module.exports = function(worker, opts) {
|
||||
|
||||
var work = null;
|
||||
var stopped = m.deferred();
|
||||
|
||||
var legacyVariant = legacyVariantMap[opts.variant.key];
|
||||
if (legacyVariant) worker.send('setoption name UCI_' + legacyVariant + ' value true');
|
||||
else worker.send('uci');
|
||||
|
||||
// TODO: Modern variant selector
|
||||
|
||||
var processOutput = function(text) {
|
||||
if (text.indexOf('bestmove ') === 0) {
|
||||
stopped.resolve(true);
|
||||
return;
|
||||
}
|
||||
if (!work) return;
|
||||
if (/currmovenumber|lowerbound|upperbound/.test(text)) return;
|
||||
var matches = text.match(/depth (\d+) .*score (cp|mate) ([-\d]+) .*nps (\d+) .*pv (.+)/);
|
||||
if (!matches) return;
|
||||
var depth = parseInt(matches[1]);
|
||||
if (depth < opts.minDepth) return;
|
||||
var cp, mate;
|
||||
if (matches[2] === 'cp') cp = parseFloat(matches[3]);
|
||||
else mate = parseFloat(matches[3]);
|
||||
if (work.ply % 2 === 1) {
|
||||
if (matches[2] === 'cp') cp = -cp;
|
||||
else mate = -mate;
|
||||
}
|
||||
var best = matches[5].split(' ')[0];
|
||||
work.emit({
|
||||
work: work,
|
||||
eval: {
|
||||
depth: depth,
|
||||
cp: cp,
|
||||
mate: mate,
|
||||
best: best,
|
||||
nps: parseInt(matches[4])
|
||||
},
|
||||
name: name
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
start: function(w) {
|
||||
work = w;
|
||||
worker.send(['position', 'fen', work.initialFen, 'moves'].concat(work.moves).join(' '));
|
||||
worker.send('go depth ' + work.maxDepth);
|
||||
},
|
||||
stop: function(s) {
|
||||
if (!work) s.resolve(true);
|
||||
else {
|
||||
work = null;
|
||||
stopped = s;
|
||||
worker.send('stop');
|
||||
}
|
||||
return s.promise;
|
||||
},
|
||||
received: processOutput
|
||||
};
|
||||
};
|
|
@ -1,86 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var variantMap = {
|
||||
fromPosition: 'Chess960',
|
||||
chess960: 'Chess960',
|
||||
atomic: 'Atomic',
|
||||
horde: 'Horde',
|
||||
crazyhouse: 'House',
|
||||
kingOfTheHill: 'KingOfTheHill',
|
||||
racingKings: 'Race',
|
||||
threeCheck: '3Check',
|
||||
antichess: 'Anti'
|
||||
};
|
||||
|
||||
module.exports = function(opts, name) {
|
||||
|
||||
var instance = null;
|
||||
var busy = false;
|
||||
var stopping = false;
|
||||
|
||||
var send = function(text) {
|
||||
instance.postMessage(text);
|
||||
};
|
||||
|
||||
var processOutput = function(text, work) {
|
||||
if (text.indexOf('bestmove ') === 0) {
|
||||
busy = false;
|
||||
stopping = false;
|
||||
return;
|
||||
}
|
||||
if (stopping) return;
|
||||
if (/currmovenumber|lowerbound|upperbound/.test(text)) return;
|
||||
var matches = text.match(/depth (\d+) .*score (cp|mate) ([-\d]+) .*nps (\d+) .*pv (.+)/);
|
||||
if (!matches) return;
|
||||
var depth = parseInt(matches[1]);
|
||||
if (depth < opts.minDepth) return;
|
||||
var cp, mate;
|
||||
if (matches[2] === 'cp') cp = parseFloat(matches[3]);
|
||||
else mate = parseFloat(matches[3]);
|
||||
if (work.ply % 2 === 1) {
|
||||
if (matches[2] === 'cp') cp = -cp;
|
||||
else mate = -mate;
|
||||
}
|
||||
var best = matches[5].split(' ')[0];
|
||||
work.emit({
|
||||
work: work,
|
||||
eval: {
|
||||
depth: depth,
|
||||
cp: cp,
|
||||
mate: mate,
|
||||
best: best,
|
||||
nps: parseInt(matches[4])
|
||||
},
|
||||
name: name
|
||||
});
|
||||
};
|
||||
|
||||
var reboot = function() {
|
||||
if (instance) instance.terminate();
|
||||
instance = new Worker('/assets/vendor/stockfish.js/stockfish.js');
|
||||
busy = false;
|
||||
stopping = false;
|
||||
var uciVariant = variantMap[opts.variant.key];
|
||||
if (uciVariant) send('setoption name UCI_' + uciVariant + ' value true');
|
||||
else send('uci'); // send something to warm up
|
||||
};
|
||||
|
||||
reboot();
|
||||
|
||||
return {
|
||||
start: function(work) {
|
||||
if (busy) reboot();
|
||||
busy = true;
|
||||
send(['position', 'fen', work.initialFen, 'moves'].concat(work.moves).join(' '));
|
||||
send('go depth ' + work.maxDepth);
|
||||
instance.onmessage = function(msg) {
|
||||
processOutput(msg.data, work);
|
||||
};
|
||||
},
|
||||
stop: function() {
|
||||
if (!busy) return;
|
||||
stopping = true;
|
||||
send('stop');
|
||||
}
|
||||
};
|
||||
};
|
|
@ -1,10 +1,11 @@
|
|||
var m = require('mithril');
|
||||
|
||||
module.exports = function(opts, name) {
|
||||
module.exports = function(worker, opts) {
|
||||
|
||||
var instance = null;
|
||||
var busy = false;
|
||||
var stopping = false;
|
||||
var work = null;
|
||||
var stopped = m.deferred();
|
||||
|
||||
worker.send('xboard');
|
||||
|
||||
// Sunsetter always plays only moves right away. Count the number of played
|
||||
// moves to show the correct mate in #n.
|
||||
|
@ -13,24 +14,19 @@ module.exports = function(opts, name) {
|
|||
var aiMoves = 0;
|
||||
var best;
|
||||
|
||||
var send = function(text) {
|
||||
instance.postMessage(text);
|
||||
};
|
||||
|
||||
var processOutput = function(text, work) {
|
||||
var processOutput = function(text) {
|
||||
if (text === 'tellics stopped') {
|
||||
busy = false;
|
||||
stopping = false;
|
||||
aiMoves = 0;
|
||||
best = undefined;
|
||||
stopped.resolve(true);
|
||||
return;
|
||||
}
|
||||
if (stopping) return;
|
||||
if (!work) return;
|
||||
|
||||
if (text.indexOf('move ') == 0) {
|
||||
aiMoves++;
|
||||
best = text.split(' ')[1];
|
||||
if (!stopping) send('analyze');
|
||||
if (work) worker.send('analyze');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -42,14 +38,13 @@ module.exports = function(opts, name) {
|
|||
cp = parseInt(matches[2], 10);
|
||||
if (!aiMoves) {
|
||||
best = matches[5];
|
||||
if (depth < opts.minDepth) return;
|
||||
}
|
||||
} else {
|
||||
matches = text.match(/Found move:\s+([a-h1-8=@PNBRQK]+)\s+([-+]?\d+)\s.*/);
|
||||
if (matches) {
|
||||
cp = parseInt(matches[2], 10);
|
||||
if (!aiMoves) best = matches[1];
|
||||
if (!stopping) send('analyze');
|
||||
if (work) worker.send('analyze');
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
@ -81,40 +76,30 @@ module.exports = function(opts, name) {
|
|||
});
|
||||
};
|
||||
|
||||
var reboot = function() {
|
||||
if (instance) instance.terminate();
|
||||
instance = new Worker('/assets/vendor/Sunsetter8/sunsetter.js');
|
||||
busy = false;
|
||||
stopping = false;
|
||||
aiMoves = 0;
|
||||
send('xboard');
|
||||
};
|
||||
|
||||
reboot();
|
||||
|
||||
return {
|
||||
start: function(work) {
|
||||
if (busy) reboot();
|
||||
busy = true;
|
||||
send('reset ' + opts.variant.key);
|
||||
send('setboard ' + work.initialFen);
|
||||
send('easy');
|
||||
send('force');
|
||||
start: function(w) {
|
||||
work = w;
|
||||
worker.send('reset crazyhouse');
|
||||
worker.send('setboard ' + work.initialFen);
|
||||
worker.send('easy');
|
||||
worker.send('force');
|
||||
for (var i = 0; i < work.moves.length; i++) {
|
||||
send(work.moves[i]);
|
||||
worker.send(work.moves[i]);
|
||||
}
|
||||
send('go');
|
||||
instance.onmessage = function(msg) {
|
||||
processOutput(msg.data, work);
|
||||
};
|
||||
worker.send('go');
|
||||
},
|
||||
stop: function() {
|
||||
if (!busy) return;
|
||||
stopping = true;
|
||||
aiMoves = 0;
|
||||
best = undefined;
|
||||
send('exit');
|
||||
send('tellics stopped');
|
||||
}
|
||||
stop: function(s) {
|
||||
if (!work) s.resolve(true);
|
||||
else {
|
||||
stopped = s;
|
||||
work = null;
|
||||
aiMoves = 0;
|
||||
best = undefined;
|
||||
worker.send('exit');
|
||||
worker.send('tellics stopped');
|
||||
}
|
||||
return s.promise;
|
||||
},
|
||||
received: processOutput
|
||||
};
|
||||
};
|
Loading…
Reference in New Issue