chess-ai-kong
Version:
Chess AI based on Alpha-Beta pruning algorithm.
4,741 lines • 141 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.chessAiKong = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/*
The MIT License (MIT)
Original Library
- Copyright (c) Marak Squires
Additional functionality
- Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
var colors = {};
module['exports'] = colors;
colors.themes = {};
var ansiStyles = colors.styles = require('./styles');
var defineProps = Object.defineProperties;
colors.supportsColor = require('./system/supports-colors');
if (typeof colors.enabled === "undefined") {
colors.enabled = colors.supportsColor;
}
colors.stripColors = colors.strip = function(str){
return ("" + str).replace(/\x1B\[\d+m/g, '');
};
var stylize = colors.stylize = function stylize (str, style) {
if (!colors.enabled) {
return str+'';
}
return ansiStyles[style].open + str + ansiStyles[style].close;
}
var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
var escapeStringRegexp = function (str) {
if (typeof str !== 'string') {
throw new TypeError('Expected a string');
}
return str.replace(matchOperatorsRe, '\\$&');
}
function build(_styles) {
var builder = function builder() {
return applyStyle.apply(builder, arguments);
};
builder._styles = _styles;
// __proto__ is used because we must return a function, but there is
// no way to create a function with a different prototype.
builder.__proto__ = proto;
return builder;
}
var styles = (function () {
var ret = {};
ansiStyles.grey = ansiStyles.gray;
Object.keys(ansiStyles).forEach(function (key) {
ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g');
ret[key] = {
get: function () {
return build(this._styles.concat(key));
}
};
});
return ret;
})();
var proto = defineProps(function colors() {}, styles);
function applyStyle() {
var args = arguments;
var argsLen = args.length;
var str = argsLen !== 0 && String(arguments[0]);
if (argsLen > 1) {
for (var a = 1; a < argsLen; a++) {
str += ' ' + args[a];
}
}
if (!colors.enabled || !str) {
return str;
}
var nestedStyles = this._styles;
var i = nestedStyles.length;
while (i--) {
var code = ansiStyles[nestedStyles[i]];
str = code.open + str.replace(code.closeRe, code.open) + code.close;
}
return str;
}
function applyTheme (theme) {
for (var style in theme) {
(function(style){
colors[style] = function(str){
if (typeof theme[style] === 'object'){
var out = str;
for (var i in theme[style]){
out = colors[theme[style][i]](out);
}
return out;
}
return colors[theme[style]](str);
};
})(style)
}
}
colors.setTheme = function (theme) {
if (typeof theme === 'string') {
try {
colors.themes[theme] = require(theme);
applyTheme(colors.themes[theme]);
return colors.themes[theme];
} catch (err) {
console.log(err);
return err;
}
} else {
applyTheme(theme);
}
};
function init() {
var ret = {};
Object.keys(styles).forEach(function (name) {
ret[name] = {
get: function () {
return build([name]);
}
};
});
return ret;
}
var sequencer = function sequencer (map, str) {
var exploded = str.split(""), i = 0;
exploded = exploded.map(map);
return exploded.join("");
};
// custom formatter methods
colors.trap = require('./custom/trap');
colors.zalgo = require('./custom/zalgo');
// maps
colors.maps = {};
colors.maps.america = require('./maps/america');
colors.maps.zebra = require('./maps/zebra');
colors.maps.rainbow = require('./maps/rainbow');
colors.maps.random = require('./maps/random')
for (var map in colors.maps) {
(function(map){
colors[map] = function (str) {
return sequencer(colors.maps[map], str);
}
})(map)
}
defineProps(colors, init());
},{"./custom/trap":2,"./custom/zalgo":3,"./maps/america":6,"./maps/rainbow":7,"./maps/random":8,"./maps/zebra":9,"./styles":10,"./system/supports-colors":11}],2:[function(require,module,exports){
module['exports'] = function runTheTrap (text, options) {
var result = "";
text = text || "Run the trap, drop the bass";
text = text.split('');
var trap = {
a: ["\u0040", "\u0104", "\u023a", "\u0245", "\u0394", "\u039b", "\u0414"],
b: ["\u00df", "\u0181", "\u0243", "\u026e", "\u03b2", "\u0e3f"],
c: ["\u00a9", "\u023b", "\u03fe"],
d: ["\u00d0", "\u018a", "\u0500" , "\u0501" ,"\u0502", "\u0503"],
e: ["\u00cb", "\u0115", "\u018e", "\u0258", "\u03a3", "\u03be", "\u04bc", "\u0a6c"],
f: ["\u04fa"],
g: ["\u0262"],
h: ["\u0126", "\u0195", "\u04a2", "\u04ba", "\u04c7", "\u050a"],
i: ["\u0f0f"],
j: ["\u0134"],
k: ["\u0138", "\u04a0", "\u04c3", "\u051e"],
l: ["\u0139"],
m: ["\u028d", "\u04cd", "\u04ce", "\u0520", "\u0521", "\u0d69"],
n: ["\u00d1", "\u014b", "\u019d", "\u0376", "\u03a0", "\u048a"],
o: ["\u00d8", "\u00f5", "\u00f8", "\u01fe", "\u0298", "\u047a", "\u05dd", "\u06dd", "\u0e4f"],
p: ["\u01f7", "\u048e"],
q: ["\u09cd"],
r: ["\u00ae", "\u01a6", "\u0210", "\u024c", "\u0280", "\u042f"],
s: ["\u00a7", "\u03de", "\u03df", "\u03e8"],
t: ["\u0141", "\u0166", "\u0373"],
u: ["\u01b1", "\u054d"],
v: ["\u05d8"],
w: ["\u0428", "\u0460", "\u047c", "\u0d70"],
x: ["\u04b2", "\u04fe", "\u04fc", "\u04fd"],
y: ["\u00a5", "\u04b0", "\u04cb"],
z: ["\u01b5", "\u0240"]
}
text.forEach(function(c){
c = c.toLowerCase();
var chars = trap[c] || [" "];
var rand = Math.floor(Math.random() * chars.length);
if (typeof trap[c] !== "undefined") {
result += trap[c][rand];
} else {
result += c;
}
});
return result;
}
},{}],3:[function(require,module,exports){
// please no
module['exports'] = function zalgo(text, options) {
text = text || " he is here ";
var soul = {
"up" : [
'̍', '̎', '̄', '̅',
'̿', '̑', '̆', '̐',
'͒', '͗', '͑', '̇',
'̈', '̊', '͂', '̓',
'̈', '͊', '͋', '͌',
'̃', '̂', '̌', '͐',
'̀', '́', '̋', '̏',
'̒', '̓', '̔', '̽',
'̉', 'ͣ', 'ͤ', 'ͥ',
'ͦ', 'ͧ', 'ͨ', 'ͩ',
'ͪ', 'ͫ', 'ͬ', 'ͭ',
'ͮ', 'ͯ', '̾', '͛',
'͆', '̚'
],
"down" : [
'̖', '̗', '̘', '̙',
'̜', '̝', '̞', '̟',
'̠', '̤', '̥', '̦',
'̩', '̪', '̫', '̬',
'̭', '̮', '̯', '̰',
'̱', '̲', '̳', '̹',
'̺', '̻', '̼', 'ͅ',
'͇', '͈', '͉', '͍',
'͎', '͓', '͔', '͕',
'͖', '͙', '͚', '̣'
],
"mid" : [
'̕', '̛', '̀', '́',
'͘', '̡', '̢', '̧',
'̨', '̴', '̵', '̶',
'͜', '͝', '͞',
'͟', '͠', '͢', '̸',
'̷', '͡', ' ҉'
]
},
all = [].concat(soul.up, soul.down, soul.mid),
zalgo = {};
function randomNumber(range) {
var r = Math.floor(Math.random() * range);
return r;
}
function is_char(character) {
var bool = false;
all.filter(function (i) {
bool = (i === character);
});
return bool;
}
function heComes(text, options) {
var result = '', counts, l;
options = options || {};
options["up"] = typeof options["up"] !== 'undefined' ? options["up"] : true;
options["mid"] = typeof options["mid"] !== 'undefined' ? options["mid"] : true;
options["down"] = typeof options["down"] !== 'undefined' ? options["down"] : true;
options["size"] = typeof options["size"] !== 'undefined' ? options["size"] : "maxi";
text = text.split('');
for (l in text) {
if (is_char(l)) {
continue;
}
result = result + text[l];
counts = {"up" : 0, "down" : 0, "mid" : 0};
switch (options.size) {
case 'mini':
counts.up = randomNumber(8);
counts.mid = randomNumber(2);
counts.down = randomNumber(8);
break;
case 'maxi':
counts.up = randomNumber(16) + 3;
counts.mid = randomNumber(4) + 1;
counts.down = randomNumber(64) + 3;
break;
default:
counts.up = randomNumber(8) + 1;
counts.mid = randomNumber(6) / 2;
counts.down = randomNumber(8) + 1;
break;
}
var arr = ["up", "mid", "down"];
for (var d in arr) {
var index = arr[d];
for (var i = 0 ; i <= counts[index]; i++) {
if (options[index]) {
result = result + soul[index][randomNumber(soul[index].length)];
}
}
}
}
return result;
}
// don't summon him
return heComes(text, options);
}
},{}],4:[function(require,module,exports){
var colors = require('./colors');
module['exports'] = function () {
//
// Extends prototype of native string object to allow for "foo".red syntax
//
var addProperty = function (color, func) {
String.prototype.__defineGetter__(color, func);
};
var sequencer = function sequencer (map, str) {
return function () {
var exploded = this.split(""), i = 0;
exploded = exploded.map(map);
return exploded.join("");
}
};
addProperty('strip', function () {
return colors.strip(this);
});
addProperty('stripColors', function () {
return colors.strip(this);
});
addProperty("trap", function(){
return colors.trap(this);
});
addProperty("zalgo", function(){
return colors.zalgo(this);
});
addProperty("zebra", function(){
return colors.zebra(this);
});
addProperty("rainbow", function(){
return colors.rainbow(this);
});
addProperty("random", function(){
return colors.random(this);
});
addProperty("america", function(){
return colors.america(this);
});
//
// Iterate through all default styles and colors
//
var x = Object.keys(colors.styles);
x.forEach(function (style) {
addProperty(style, function () {
return colors.stylize(this, style);
});
});
function applyTheme(theme) {
//
// Remark: This is a list of methods that exist
// on String that you should not overwrite.
//
var stringPrototypeBlacklist = [
'__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', 'charAt', 'constructor',
'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf', 'charCodeAt',
'indexOf', 'lastIndexof', 'length', 'localeCompare', 'match', 'replace', 'search', 'slice', 'split', 'substring',
'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toUpperCase', 'trim', 'trimLeft', 'trimRight'
];
Object.keys(theme).forEach(function (prop) {
if (stringPrototypeBlacklist.indexOf(prop) !== -1) {
console.log('warn: '.red + ('String.prototype' + prop).magenta + ' is probably something you don\'t want to override. Ignoring style name');
}
else {
if (typeof(theme[prop]) === 'string') {
colors[prop] = colors[theme[prop]];
addProperty(prop, function () {
return colors[theme[prop]](this);
});
}
else {
addProperty(prop, function () {
var ret = this;
for (var t = 0; t < theme[prop].length; t++) {
ret = colors[theme[prop][t]](ret);
}
return ret;
});
}
}
});
}
colors.setTheme = function (theme) {
if (typeof theme === 'string') {
try {
colors.themes[theme] = require(theme);
applyTheme(colors.themes[theme]);
return colors.themes[theme];
} catch (err) {
console.log(err);
return err;
}
} else {
applyTheme(theme);
}
};
};
},{"./colors":1}],5:[function(require,module,exports){
var colors = require('./colors');
module['exports'] = colors;
// Remark: By default, colors will add style properties to String.prototype
//
// If you don't wish to extend String.prototype you can do this instead and native String will not be touched
//
// var colors = require('colors/safe);
// colors.red("foo")
//
//
require('./extendStringPrototype')();
},{"./colors":1,"./extendStringPrototype":4}],6:[function(require,module,exports){
var colors = require('../colors');
module['exports'] = (function() {
return function (letter, i, exploded) {
if(letter === " ") return letter;
switch(i%3) {
case 0: return colors.red(letter);
case 1: return colors.white(letter)
case 2: return colors.blue(letter)
}
}
})();
},{"../colors":1}],7:[function(require,module,exports){
var colors = require('../colors');
module['exports'] = (function () {
var rainbowColors = ['red', 'yellow', 'green', 'blue', 'magenta']; //RoY G BiV
return function (letter, i, exploded) {
if (letter === " ") {
return letter;
} else {
return colors[rainbowColors[i++ % rainbowColors.length]](letter);
}
};
})();
},{"../colors":1}],8:[function(require,module,exports){
var colors = require('../colors');
module['exports'] = (function () {
var available = ['underline', 'inverse', 'grey', 'yellow', 'red', 'green', 'blue', 'white', 'cyan', 'magenta'];
return function(letter, i, exploded) {
return letter === " " ? letter : colors[available[Math.round(Math.random() * (available.length - 1))]](letter);
};
})();
},{"../colors":1}],9:[function(require,module,exports){
var colors = require('../colors');
module['exports'] = function (letter, i, exploded) {
return i % 2 === 0 ? letter : colors.inverse(letter);
};
},{"../colors":1}],10:[function(require,module,exports){
/*
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
var styles = {};
module['exports'] = styles;
var codes = {
reset: [0, 0],
bold: [1, 22],
dim: [2, 22],
italic: [3, 23],
underline: [4, 24],
inverse: [7, 27],
hidden: [8, 28],
strikethrough: [9, 29],
black: [30, 39],
red: [31, 39],
green: [32, 39],
yellow: [33, 39],
blue: [34, 39],
magenta: [35, 39],
cyan: [36, 39],
white: [37, 39],
gray: [90, 39],
grey: [90, 39],
bgBlack: [40, 49],
bgRed: [41, 49],
bgGreen: [42, 49],
bgYellow: [43, 49],
bgBlue: [44, 49],
bgMagenta: [45, 49],
bgCyan: [46, 49],
bgWhite: [47, 49],
// legacy styles for colors pre v1.0.0
blackBG: [40, 49],
redBG: [41, 49],
greenBG: [42, 49],
yellowBG: [43, 49],
blueBG: [44, 49],
magentaBG: [45, 49],
cyanBG: [46, 49],
whiteBG: [47, 49]
};
Object.keys(codes).forEach(function (key) {
var val = codes[key];
var style = styles[key] = [];
style.open = '\u001b[' + val[0] + 'm';
style.close = '\u001b[' + val[1] + 'm';
});
},{}],11:[function(require,module,exports){
(function (process){
/*
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
var argv = process.argv;
module.exports = (function () {
if (argv.indexOf('--no-color') !== -1 ||
argv.indexOf('--color=false') !== -1) {
return false;
}
if (argv.indexOf('--color') !== -1 ||
argv.indexOf('--color=true') !== -1 ||
argv.indexOf('--color=always') !== -1) {
return true;
}
if (process.stdout && !process.stdout.isTTY) {
return false;
}
if (process.platform === 'win32') {
return true;
}
if ('COLORTERM' in process.env) {
return true;
}
if (process.env.TERM === 'dumb') {
return false;
}
if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)) {
return true;
}
return false;
})();
}).call(this,require('_process'))
},{"_process":24}],12:[function(require,module,exports){
(function (process){
(function() {
var isEqual, isPlainObject, macModifierKeyMap, nonMacModifierKeyMap, plus, shiftKeyMap, splitKeyPath, _,
__slice = [].slice;
_ = require('underscore');
macModifierKeyMap = {
cmd: '\u2318',
ctrl: '\u2303',
alt: '\u2325',
option: '\u2325',
shift: '\u21e7',
enter: '\u23ce',
left: '\u2190',
right: '\u2192',
up: '\u2191',
down: '\u2193'
};
nonMacModifierKeyMap = {
cmd: 'Cmd',
ctrl: 'Ctrl',
alt: 'Alt',
option: 'Alt',
shift: 'Shift',
enter: 'Enter',
left: 'Left',
right: 'Right',
up: 'Up',
down: 'Down'
};
shiftKeyMap = {
'~': '`',
'_': '-',
'+': '=',
'|': '\\',
'{': '[',
'}': ']',
':': ';',
'"': '\'',
'<': ',',
'>': '.',
'?': '/'
};
splitKeyPath = function(keyPath) {
var char, i, keyPathArray, startIndex, _i, _len;
startIndex = 0;
keyPathArray = [];
if (keyPath == null) {
return keyPathArray;
}
for (i = _i = 0, _len = keyPath.length; _i < _len; i = ++_i) {
char = keyPath[i];
if (char === '.' && (i === 0 || keyPath[i - 1] !== '\\')) {
keyPathArray.push(keyPath.substring(startIndex, i));
startIndex = i + 1;
}
}
keyPathArray.push(keyPath.substr(startIndex, keyPath.length));
return keyPathArray;
};
isPlainObject = function(value) {
return _.isObject(value) && !_.isArray(value);
};
plus = {
adviseBefore: function(object, methodName, advice) {
var original;
original = object[methodName];
return object[methodName] = function() {
var args;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
if (advice.apply(this, args) !== false) {
return original.apply(this, args);
}
};
},
camelize: function(string) {
if (string) {
return string.replace(/[_-]+(\w)/g, function(m) {
return m[1].toUpperCase();
});
} else {
return '';
}
},
capitalize: function(word) {
if (!word) {
return '';
}
if (word.toLowerCase() === 'github') {
return 'GitHub';
} else {
return word[0].toUpperCase() + word.slice(1);
}
},
compactObject: function(object) {
var key, newObject, value;
newObject = {};
for (key in object) {
value = object[key];
if (value != null) {
newObject[key] = value;
}
}
return newObject;
},
dasherize: function(string) {
if (!string) {
return '';
}
string = string[0].toLowerCase() + string.slice(1);
return string.replace(/([A-Z])|(_)/g, function(m, letter) {
if (letter) {
return "-" + letter.toLowerCase();
} else {
return "-";
}
});
},
deepClone: function(object) {
if (_.isArray(object)) {
return object.map(function(value) {
return plus.deepClone(value);
});
} else if (_.isObject(object) && !_.isFunction(object)) {
return plus.mapObject(object, (function(_this) {
return function(key, value) {
return [key, plus.deepClone(value)];
};
})(this));
} else {
return object;
}
},
deepExtend: function(target) {
var i, key, object, result, _i, _len, _ref;
result = target;
i = 0;
while (++i < arguments.length) {
object = arguments[i];
if (isPlainObject(result) && isPlainObject(object)) {
_ref = Object.keys(object);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
key = _ref[_i];
result[key] = plus.deepExtend(result[key], object[key]);
}
} else {
result = plus.deepClone(object);
}
}
return result;
},
deepContains: function(array, target) {
var object, _i, _len;
if (array == null) {
return false;
}
for (_i = 0, _len = array.length; _i < _len; _i++) {
object = array[_i];
if (_.isEqual(object, target)) {
return true;
}
}
return false;
},
endsWith: function(string, suffix) {
if (suffix == null) {
suffix = '';
}
if (string) {
return string.indexOf(suffix, string.length - suffix.length) !== -1;
} else {
return false;
}
},
escapeAttribute: function(string) {
if (string) {
return string.replace(/"/g, '"').replace(/\n/g, '').replace(/\\/g, '-');
} else {
return '';
}
},
escapeRegExp: function(string) {
if (string) {
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
} else {
return '';
}
},
humanizeEventName: function(eventName, eventDoc) {
var event, namespace, namespaceDoc, _ref;
_ref = eventName.split(':'), namespace = _ref[0], event = _ref[1];
if (event == null) {
return plus.undasherize(namespace);
}
namespaceDoc = plus.undasherize(namespace);
if (eventDoc == null) {
eventDoc = plus.undasherize(event);
}
return "" + namespaceDoc + ": " + eventDoc;
},
humanizeKey: function(key, platform) {
var modifierKeyMap;
if (platform == null) {
platform = process.platform;
}
if (!key) {
return key;
}
modifierKeyMap = platform === 'darwin' ? macModifierKeyMap : nonMacModifierKeyMap;
if (modifierKeyMap[key]) {
return modifierKeyMap[key];
} else if (key.length === 1 && (shiftKeyMap[key] != null)) {
return [modifierKeyMap.shift, shiftKeyMap[key]];
} else if (key.length === 1 && key === key.toUpperCase() && key.toUpperCase() !== key.toLowerCase()) {
return [modifierKeyMap.shift, key.toUpperCase()];
} else if (key.length === 1 || /f[0-9]{1,2}/.test(key)) {
return key.toUpperCase();
} else {
if (platform === 'darwin') {
return key;
} else {
return plus.capitalize(key);
}
}
},
humanizeKeystroke: function(keystroke, platform) {
var humanizedKeystrokes, index, key, keys, keystrokes, splitKeystroke, _i, _j, _len, _len1;
if (platform == null) {
platform = process.platform;
}
if (!keystroke) {
return keystroke;
}
keystrokes = keystroke.split(' ');
humanizedKeystrokes = [];
for (_i = 0, _len = keystrokes.length; _i < _len; _i++) {
keystroke = keystrokes[_i];
keys = [];
splitKeystroke = keystroke.split('-');
for (index = _j = 0, _len1 = splitKeystroke.length; _j < _len1; index = ++_j) {
key = splitKeystroke[index];
if (key === '' && splitKeystroke[index - 1] === '') {
key = '-';
}
if (key) {
keys.push(plus.humanizeKey(key, platform));
}
}
keys = _.uniq(_.flatten(keys));
if (platform === 'darwin') {
keys = keys.join('');
} else {
keys = keys.join('+');
}
humanizedKeystrokes.push(keys);
}
return humanizedKeystrokes.join(' ');
},
isSubset: function(potentialSubset, potentialSuperset) {
return _.every(potentialSubset, function(element) {
return _.include(potentialSuperset, element);
});
},
losslessInvert: function(hash) {
var inverted, key, value;
inverted = {};
for (key in hash) {
value = hash[key];
if (inverted[value] == null) {
inverted[value] = [];
}
inverted[value].push(key);
}
return inverted;
},
mapObject: function(object, iterator) {
var key, newObject, value, _i, _len, _ref, _ref1;
newObject = {};
_ref = Object.keys(object);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
key = _ref[_i];
_ref1 = iterator(key, object[key]), key = _ref1[0], value = _ref1[1];
newObject[key] = value;
}
return newObject;
},
multiplyString: function(string, n) {
var finalString, i;
finalString = "";
i = 0;
while (i < n) {
finalString += string;
i++;
}
return finalString;
},
pluralize: function(count, singular, plural) {
if (count == null) {
count = 0;
}
if (plural == null) {
plural = singular + 's';
}
if (count === 1) {
return "" + count + " " + singular;
} else {
return "" + count + " " + plural;
}
},
remove: function(array, element) {
var index;
index = array.indexOf(element);
if (index >= 0) {
array.splice(index, 1);
}
return array;
},
setValueForKeyPath: function(object, keyPath, value) {
var key, keys;
keys = splitKeyPath(keyPath);
while (keys.length > 1) {
key = keys.shift();
if (object[key] == null) {
object[key] = {};
}
object = object[key];
}
if (value != null) {
return object[keys.shift()] = value;
} else {
return delete object[keys.shift()];
}
},
hasKeyPath: function(object, keyPath) {
var key, keys, _i, _len;
keys = splitKeyPath(keyPath);
for (_i = 0, _len = keys.length; _i < _len; _i++) {
key = keys[_i];
if (!object.hasOwnProperty(key)) {
return false;
}
object = object[key];
}
return true;
},
spliceWithArray: function(originalArray, start, length, insertedArray, chunkSize) {
var chunkStart, _i, _ref, _results;
if (chunkSize == null) {
chunkSize = 100000;
}
if (insertedArray.length < chunkSize) {
return originalArray.splice.apply(originalArray, [start, length].concat(__slice.call(insertedArray)));
} else {
originalArray.splice(start, length);
_results = [];
for (chunkStart = _i = 0, _ref = insertedArray.length; chunkSize > 0 ? _i <= _ref : _i >= _ref; chunkStart = _i += chunkSize) {
_results.push(originalArray.splice.apply(originalArray, [start + chunkStart, 0].concat(__slice.call(insertedArray.slice(chunkStart, chunkStart + chunkSize)))));
}
return _results;
}
},
sum: function(array) {
var elt, sum, _i, _len;
sum = 0;
for (_i = 0, _len = array.length; _i < _len; _i++) {
elt = array[_i];
sum += elt;
}
return sum;
},
uncamelcase: function(string) {
var result;
if (!string) {
return '';
}
result = string.replace(/([A-Z])|_+/g, function(match, letter) {
if (letter == null) {
letter = '';
}
return " " + letter;
});
return plus.capitalize(result.trim());
},
undasherize: function(string) {
if (string) {
return string.split('-').map(plus.capitalize).join(' ');
} else {
return '';
}
},
underscore: function(string) {
if (!string) {
return '';
}
string = string[0].toLowerCase() + string.slice(1);
return string.replace(/([A-Z])|-+/g, function(match, letter) {
if (letter == null) {
letter = '';
}
return "_" + (letter.toLowerCase());
});
},
valueForKeyPath: function(object, keyPath) {
var key, keys, _i, _len;
keys = splitKeyPath(keyPath);
for (_i = 0, _len = keys.length; _i < _len; _i++) {
key = keys[_i];
object = object[key];
if (object == null) {
return;
}
}
return object;
},
isEqual: function(a, b, aStack, bStack) {
if (_.isArray(aStack) && _.isArray(bStack)) {
return isEqual(a, b, aStack, bStack);
} else {
return isEqual(a, b);
}
},
isEqualForProperties: function() {
var a, b, properties, property, _i, _len;
a = arguments[0], b = arguments[1], properties = 3 <= arguments.length ? __slice.call(arguments, 2) : [];
for (_i = 0, _len = properties.length; _i < _len; _i++) {
property = properties[_i];
if (!_.isEqual(a[property], b[property])) {
return false;
}
}
return true;
}
};
isEqual = function(a, b, aStack, bStack) {
var aCtor, aCtorValid, aElement, aKeyCount, aValue, bCtor, bCtorValid, bKeyCount, bValue, equal, i, key, stackIndex, _i, _len;
if (aStack == null) {
aStack = [];
}
if (bStack == null) {
bStack = [];
}
if (a === b) {
return _.isEqual(a, b);
}
if (_.isFunction(a) || _.isFunction(b)) {
return _.isEqual(a, b);
}
stackIndex = aStack.length;
while (stackIndex--) {
if (aStack[stackIndex] === a) {
return bStack[stackIndex] === b;
}
}
aStack.push(a);
bStack.push(b);
equal = false;
if (_.isFunction(a != null ? a.isEqual : void 0)) {
equal = a.isEqual(b, aStack, bStack);
} else if (_.isFunction(b != null ? b.isEqual : void 0)) {
equal = b.isEqual(a, bStack, aStack);
} else if (_.isArray(a) && _.isArray(b) && a.length === b.length) {
equal = true;
for (i = _i = 0, _len = a.length; _i < _len; i = ++_i) {
aElement = a[i];
if (!isEqual(aElement, b[i], aStack, bStack)) {
equal = false;
break;
}
}
} else if (_.isRegExp(a) && _.isRegExp(b)) {
equal = _.isEqual(a, b);
} else if (_.isElement(a) && _.isElement(b)) {
equal = a === b;
} else if (_.isObject(a) && _.isObject(b)) {
aCtor = a.constructor;
bCtor = b.constructor;
aCtorValid = _.isFunction(aCtor) && aCtor instanceof aCtor;
bCtorValid = _.isFunction(bCtor) && bCtor instanceof bCtor;
if (aCtor !== bCtor && !(aCtorValid && bCtorValid)) {
equal = false;
} else {
aKeyCount = 0;
equal = true;
for (key in a) {
aValue = a[key];
if (!_.has(a, key)) {
continue;
}
aKeyCount++;
if (!(_.has(b, key) && isEqual(aValue, b[key], aStack, bStack))) {
equal = false;
break;
}
}
if (equal) {
bKeyCount = 0;
for (key in b) {
bValue = b[key];
if (_.has(b, key)) {
bKeyCount++;
}
}
equal = aKeyCount === bKeyCount;
}
}
} else {
equal = _.isEqual(a, b);
}
aStack.pop();
bStack.pop();
return equal;
};
module.exports = _.extend({}, _, plus);
}).call(this);
}).call(this,require('_process'))
},{"_process":24,"underscore":13}],13:[function(require,module,exports){
// Underscore.js 1.6.0
// http://underscorejs.org
// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
(function() {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `exports` on the server.
var root = this;
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Establish the object that gets returned to break out of a loop iteration.
var breaker = {};
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
var
push = ArrayProto.push,
slice = ArrayProto.slice,
concat = ArrayProto.concat,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
// Create a safe reference to the Underscore object for use below.
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier,
// for Closure Compiler "advanced" mode.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
// Current version.
_.VERSION = '1.6.0';
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return obj;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, length = obj.length; i < length; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
}
}
return obj;
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function(value, index, list) {
results.push(iterator.call(context, value, index, list));
});
return results;
};
var reduceError = 'Reduce of empty array with no initial value';
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
}
each(obj, function(value, index, list) {
if (!initial) {
memo = value;
initial = true;
} else {
memo = iterator.call(context, memo, value, index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
var length = obj.length;
if (length !== +length) {
var keys = _.keys(obj);
length = keys.length;
}
each(obj, function(value, index, list) {
index = keys ? keys[--length] : --length;
if (!initial) {
memo = obj[index];
initial = true;
} else {
memo = iterator.call(context, memo, obj[index], index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo;
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, predicate, context) {
var result;
any(obj, function(value, index, list) {
if (predicate.call(context, value, index, list)) {
result = value;
return true;
}
});
return result;
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function(obj, predicate, context) {
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context);
each(obj, function(value, index, list) {
if (predicate.call(context, value, index, list)) results.push(value);
});
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function(obj, predicate, context) {
return _.filter(obj, function(value, index, list) {
return !predicate.call(context, value, index, list);
}, context);
};
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function(obj, predicate, context) {
predicate || (predicate = _.identity);
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context);
each(obj, function(value, index, list) {
if (!(result = result && predicate.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function(obj, predicate, context) {
predicate || (predicate = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context);
each(obj, function(value, index, list) {
if (result || (result = predicate.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_.contains = _.include = function(obj, target) {
if (obj == null) return false;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
return any(obj, function(value) {
return value === target;
});
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
var isFunc = _.isFunction(method);
return _.map(obj, function(value) {
return (isFunc ? method : value[method]).apply(value, args);
});
};
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
return _.map(obj, _.property(key));
};
// Convenience version of a common use case of `filter`: selecting only objects
// containing specific `key:value` pairs.
_.where = function(obj, attrs) {
return _.filter(obj, _.matches(attrs));
};
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
_.findWhere = function(obj, attrs) {
return _.find(obj, _.matches(attrs));
};
// Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements.
// See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
_.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.max.apply(Math, obj);
}
var result = -Infinity, lastComputed = -Infinity;
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
if (computed > lastComputed) {
result = value;
lastComputed = computed;
}
});
return result;
};
// Return the minimum element (or element-based computation).
_.min = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.min.apply(Math, obj);
}
var result = Infinity, lastComputed = Infinity;
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
if (computed < lastComputed) {
result = value;
lastComputed = computed;
}
});
return result;
};
// Shuffle an array, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
_.shuffle = function(obj) {
var rand;
var index = 0;
var shuffled = [];
each(obj, function(value) {
rand = _.random(index++);
shuffled[index - 1] = shuffled[rand];
shuffled[rand] = value;
});
return shuffled;
};
// Sample **n** random values from a collection.
// If **n** is not specified, returns a single random element.
// The internal `guard` argument allows it to work with `map`.
_.sample = function(obj, n, guard) {
if (n == null || guard) {
if (obj.length !== +obj.length) obj = _.values(obj);
return obj[_.random(obj.length - 1)];
}
return _.shuffle(obj).slice(0, Math.max(0, n));
};
// An internal function to generate lookup iterators.
var lookupIterator = function(value) {
if (value == null) return _.identity;
if (_.isFunction(value)) return value;
return _.property(value);
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, iterator, context) {
iterator = lookupIterator(iterator);
return _.pluck(_.map(obj, function(value, index, list) {
return {
value: value,
index: index,
criteria: iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria;
var b = right.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
return left.index - right.index;
}), 'value');
};
// An internal function used for aggregate "group by" operations.
var group = function(behavior) {
return function(obj, iterator, context) {
var result = {};
iterator = lookupIterator(iterator);
each(obj, function(value, index) {
var key = iterator.call(context, value, index, obj);
behavior(result, key, value);
});
return result;
};
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = group(function(result, key, value) {
_.has(result, key) ? result[key].push(value) : result[key] = [value];
});
// Indexes the object's values by a criterion, similar to `groupBy`, but for
// when you know that your index values will be unique.
_.indexBy = group(function(result, key, value) {
result[key] = value;
});
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
_.countBy = group(function(result, key) {
_.has(result, key) ? result[key]++ : result[key] = 1;
});
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator, context) {
iterator = lookupIterator(iterator);
var value = iterator.call(context, obj);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >>> 1;
iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
}
return low;
};
// Safely create a real, live array from anything iterable.
_.toArray = function(obj) {
if (!obj) return [];
if (_.isArray(obj)) return slice.call(obj);
if (obj.length === +obj.length) return _.map(obj, _.identity);
return _.values(obj);
};
// Return the number of elements in an object.
_.size = function(obj) {
if (obj == null) return 0;
return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
};
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head` and `take`. The **guard** check
// allows it to work with `_.map`.
_.first = _.head = _.take = function(array, n, guard) {
if (array == null) return void 0;
if ((n == null) || guard) return array[0];
if (n < 0) return [];
return slice.call(array, 0, n);
};
// Returns everything but the last entry of the array. Especially useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N. The **guard** check allows it to work with
// `_.map`.
_.initial = function(array, n, guard) {
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
};
// Get the last element of an array. Passing **n** will return the last N
// values in the array. The **guard** check allows it to work with `_.map`.
_.last = function(array, n, guard) {
if (array == null) return void 0;
if ((n == null) || guard) return array[array.length - 1];
return slice.call(array, Math.max(array.length - n, 0));
};
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Especially useful on the arguments object. Passing an **n** will return
// the rest N values in the array. The **guard**
// check allows it to work with `_.map`.
_.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, (n == null) || guard ? 1 : n);
};
// Trim out all falsy values from an array.
_.compact = function(array) {
return _.filter(array, _.identity);
};
// Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, output) {
if (shallow && _.every(input, _.isArray)) {
return concat.apply(output, input);
}
each(input, function(value) {
if (_.isArray(value) || _.isArguments(value)) {
shallow ? push.apply(output, value) : flatten(value, shallow, output);
} else {
output.push(value);
}
});
return output;
};
// Flatten out an array, either recursively (by default), or just one level.
_.flatten = function(array, shallow) {
return flatten(array, shallow, []);
};
// Return a version of the array that does not contain the specified value(s).
_.without = function(array) {
return _.difference(array, slice.call(arguments, 1));
};
// Split an array into two arrays: one whose elements all satisfy the given
// predicate, and one whose elements all do not satisfy the predicate.
_.partition = function(array, predicate) {
var pass = [], fail = [];
each(array, function(elem) {
(predicate(elem) ? pass : fail).push(elem);
});
return [pass, fail];
};
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iterator, context) {
if (_.isFunction(isSorted)) {
context = iterator;
iterator = isSorted;
isSorted = false;
}
var initial = iterator ? _.map(array, iterator, context) : array;
var results = [];
var seen = [];
each(initial, function(value, index) {
if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
seen.push(value);
results.push(array[index]);
}
});
return results;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
return _.uniq(_.flatten(arguments, true));
};
// Produce an array that contains every item shared between all the
// passed-in arrays.
_.intersection = function(array) {
var rest = slice.call(arguments, 1);
return _.filter(_.uniq(array), function(item) {
return _.every(rest, function(other) {
return _.contains(other, item);
});
});
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
return _.filter(array, function(value){ return !_.contains(rest, value); });
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
var length = _.max(_.pluck(arguments, 'length').concat(0));
var results = new Array(length);
for (var i = 0; i < length; i++) {
results[i] = _.pluck(arguments, '' + i);
}
return results;
};
// Converts lists into objects. Pass either a single array of `[key, value]`
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values.
_.object = function(list, values) {
if (list == null) return {};
var result = {};
for (var i = 0, length = list.length; i < length; i++) {
if (values) {
result[list[i]] = values[i];
} else {
result[list[i][0]] = list[i][1];
}
}
return result;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
if (array == null) return -1;
var i = 0, length = array.length;
if (isSorted) {
if (typeof isSorted == 'number') {
i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
} else {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < length; i++) if (array[i] === item) return i;
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item, from) {
if (array == null) return -1;
var hasIndex = from != null;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
}
var i = (hasIndex ? from : array.length);
while (i--) if (array[i] === item) return i;
return -1;
};
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function(start, stop, step) {
if (arguments.length <= 1) {
stop = start || 0;
start = 0;
}
step = arguments[2] || 1;
var length = Math.max(Math.ceil((stop - start) / step), 0);
var idx = 0;
var range = new Array(length);
while(idx < length) {
range[idx++] = start;
start += step;
}
return range;
};
// Function (ahem) Functions
// ------------------
// Reusable constructor function for prototype setting.
var ctor = function(){};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// available.
_.bind = function(func, context) {
var args, bound;
if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
ctor.prototype = null;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
// Partially apply a function by creating a version that has had some of its
// arguments pre-filled, without changing its dynamic `this` context. _ acts
// as a placeholder, allowing any combination of arguments to be pre-filled.
_.partial = function(func) {
var boundArgs = slice.call(arguments, 1);
return function() {
var position = 0;
var args = boundArgs.slice();
for (var i = 0, length = args.length; i < length; i++) {
if (args[i] === _) args[i] = arguments[position++];
}
while (position < arguments.length) args.push(arguments[position++]);
return func.apply(this, args);
};
};
// Bind a number of an object's methods to that object. Remaining arguments
// are the method names to be bound. Useful for ensuring that all callbacks
// defined on an object belong to it.
_.bindAll = function(obj) {
var funcs = slice.call(arguments, 1);
if (funcs.length === 0) throw new Error('bindAll must be passed function names');
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj;
};
// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
var memo = {};
hasher || (hasher = _.identity);
return function() {
var key = hasher.apply(this, arguments);
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
};
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){ return func.apply(null, args); }, wait);
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = function(func) {
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
options || (options = {});
var later = function() {
previous = options.leading === false ? 0 : _.now();
timeout = null;
result = func.apply(context, args);
context = args = null;
};
return function() {
var now = _.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, args, context, timestamp, result;
var later = function() {
var last = _.now() - timestamp;
if (last < wait) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
context = args = null;
}
}
};
return function() {
context = this;
args = arguments;
timestamp = _.now();
var callNow = immediate && !timeout;
if (!timeout) {
timeout = setTimeout(later, wait);
}
if (callNow) {
result = func.apply(context, args);
context = args = null;
}
return result;
};
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
};
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_.wrap = function(func, wrapper) {
return _.partial(wrapper, func);
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
var funcs = arguments;
return function() {
var args = arguments;
for (var i = funcs.length - 1; i >= 0; i--) {
args = [funcs[i].apply(this, args)];
}
return args[0];
};
};
// Returns a function that will only be executed after being called N times.
_.after = function(times, func) {
return function() {
if (--times < 1) {
return func.apply(this, arguments);
}
};
};
// Object Functions
// ----------------
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = function(obj) {
if (!_.isObject(obj)) return [];
if (nativeKeys) return nativeKeys(obj);
var keys = [];
for (var key in obj) if (_.has(obj, key)) keys.push(key);
return keys;
};
// Retrieve the values of an object's properties.
_.values = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var values = new Array(length);
for (var i = 0; i < length; i++) {
values[i] = obj[keys[i]];
}
return values;
};
// Convert an object into a list of `[key, value]` pairs.
_.pairs = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var pairs = new Array(length);
for (var i = 0; i < length; i++) {
pairs[i] = [keys[i], obj[keys[i]]];
}
return pairs;
};
// Invert the keys and values of an object. The values must be serializable.
_.invert = function(obj) {
var result = {};
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
result[obj[keys[i]]] = keys[i];
}
return result;
};
// Return a sorted list of the function names available on the object.
// Aliased as `methods`
_.functions = _.methods = function(obj) {
var names = [];
for (var key in obj) {
if (_.isFunction(obj[key])) names.push(key);
}
return names.sort();
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
}
}
});
return obj;
};
// Return a copy of the object only containing the whitelisted properties.
_.pick = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
each(keys, function(key) {
if (key in obj) copy[key] = obj[key];
});
return copy;
};
// Return a copy of the object without the blacklisted properties.
_.omit = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
for (var key in obj) {
if (!_.contains(keys, key)) copy[key] = obj[key];
}
return copy;
};
// Fill in a given object with default properties.
_.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
if (obj[prop] === void 0) obj[prop] = source[prop];
}
}
});
return obj;
};
// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_.tap = function(obj, interceptor) {
interceptor(obj);
return obj;
};
// Internal recursive comparison function for `isEqual`.
var eq = function(a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
if (a === b) return a !== 0 || 1 / a == 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
if (a instanceof _) a = a._wrapped;
if (b instanceof _) b = b._wrapped;
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className != toString.call(b)) return false;
switch (className) {
// Strings, numbers, dates, and booleans are compared by value.
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return a == String(b);
case '[object Number]':
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
// other numeric values.
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return +a == +b;
// RegExps are compared by their source patterns and flags.
case '[object RegExp]':
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
}
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (aStack[length] == a) return bStack[length] == b;
}
// Objects with different constructors are not equivalent, but `Object`s
// from different frames are.
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
_.isFunction(bCtor) && (bCtor instanceof bCtor))
&& ('constructor' in a && 'constructor' in b)) {
return false;
}
// Add the first object to the stack of traversed objects.
aStack.push(a);
bStack.push(b);
var size = 0, result = true;
// Recursively compare objects and arrays.
if (className == '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary.
size = a.length;
result = size == b.length;
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
if (!(result = eq(a[size], b[size], aStack, bStack))) break;
}
}
} else {
// Deep compare objects.
for (var key in a) {
if (_.has(a, key)) {
// Count the expected number of properties.
size++;
// Deep compare each member.
if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
}
}
// Ensure that both objects contain the same number of properties.
if (result) {
for (key in b) {
if (_.has(b, key) && !(size--)) break;
}
result = !size;
}
}
// Remove the first object from the stack of traversed objects.
aStack.pop();
bStack.pop();
return result;
};
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
return eq(a, b, [], []);
};
// Is a given array, string, or object empty?
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
if (obj == null) return true;
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false;
return true;
};
// Is a given value a DOM element?
_.isElement = function(obj) {
return !!(obj && obj.nodeType === 1);
};
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) == '[object Array]';
};
// Is a given variable an object?
_.isObject = function(obj) {
return obj === Object(obj);
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) == '[object ' + name + ']';
};
});
// Define a fallback version of the method in browsers (ahem, IE), where
// there isn't any inspectable "Arguments" type.
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return !!(obj && _.has(obj, 'callee'));
};
}
// Optimize `isFunction` if appropriate.
if (typeof (/./) !== 'function') {
_.isFunction = function(obj) {
return typeof obj === 'function';
};
}
// Is a given object a finite number?
_.isFinite = function(obj) {
return isFinite(obj) && !isNaN(parseFloat(obj));
};
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
_.isNaN = function(obj) {
return _.isNumber(obj) && obj != +obj;
};
// Is a given value a boolean?
_.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};
// Is a given value equal to null?
_.isNull = function(obj) {
return obj === null;
};
// Is a given variable undefined?
_.isUndefined = function(obj) {
return obj === void 0;
};
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
// Utility Functions
// -----------------
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object.
_.noConflict = function() {
root._ = previousUnderscore;
return this;
};
// Keep the identity function around for default iterators.
_.identity = function(value) {
return value;
};
_.constant = function(value) {
return function () {
return value;
};
};
_.property = function(key) {
return function(obj) {
return obj[key];
};
};
// Returns a predicate for checking whether an object has a given set of `key:value` pairs.
_.matches = function(attrs) {
return function(obj) {
if (obj === attrs) return true; //avoid comparing an object to itself.
for (var key in attrs) {
if (attrs[key] !== obj[key])
return false;
}
return true;
}
};
// Run a function **n** times.
_.times = function(n, iterator, context) {
var accum = Array(Math.max(0, n));
for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
return accum;
};
// Return a random integer between min and max (inclusive).
_.random = function(min, max) {
if (max == null) {
max = min;
min = 0;
}
return min + Math.floor(Math.random() * (max - min + 1));
};
// A (possibly faster) way to get the current timestamp as an integer.
_.now = Date.now || function() { return new Date().getTime(); };
// List of HTML entities for escaping.
var entityMap = {
escape: {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
}
};
entityMap.unescape = _.invert(entityMap.escape);
// Regexes containing the keys and values listed immediately above.
var entityRegexes = {
escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
};
// Functions for escaping and unescaping strings to/from HTML interpolation.
_.each(['escape', 'unescape'], function(method) {
_[method] = function(string) {
if (string == null) return '';
return ('' + string).replace(entityRegexes[method], function(match) {
return entityMap[method][match];
});
};
});
// If the value of the named `property` is a function then invoke it with the
// `object` as context; otherwise, return it.
_.result = function(object, property) {
if (object == null) return void 0;
var value = object[property];
return _.isFunction(value) ? value.call(object) : value;
};
// Add your own custom functions to the Underscore object.
_.mixin = function(obj) {
each(_.functions(obj), function(name) {
var func = _[name] = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return result.call(this, func.apply(_, args));
};
});
};
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function(prefix) {
var id = ++idCounter + '';
return prefix ? prefix + id : id;
};
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) {
var render;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
var matcher = new RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset)
.replace(escaper, function(match) { return '\\' + escapes[match]; });
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
}
if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
}
if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
index = offset + match.length;
return match;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n";
try {
render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
if (data) return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled function source as a convenience for precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
return template;
};
// Add a "chain" function, which will delegate to the wrapper.
_.chain = function(obj) {
return _(obj).chain();
};
// OOP
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
// Helper function to continue chaining intermediate results.
var result = function(obj) {
return this._chain ? _(obj).chain() : obj;
};
// Add all of the Underscore functions to the wrapper object.
_.mixin(_);
// Add all mutator Array functions to the wrapper.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
var obj = this._wrapped;
method.apply(obj, arguments);
if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
return result.call(this, obj);
};
});
// Add all accessor Array functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
return result.call(this, method.apply(this._wrapped, arguments));
};
});
_.extend(_.prototype, {
// Start chaining a wrapped Underscore object.
chain: function() {
this._chain = true;
return this;
},
// Extracts the result from a wrapped and chained object.
value: function() {
return this._wrapped;
}
});
// AMD registration happens at the end for compatibility with AMD loaders
// that may not enforce next-turn semantics on modules. Even though general
// practice for AMD registration is to be anonymous, underscore registers
// as a named module because, like jQuery, it is a base library that is
// popular enough to be bundled in a third party lib, but not be part of
// an AMD load request. Those cases could generate an error when an
// anonymous define() is called outside of a loader request.
if (typeof define === 'function' && define.amd) {
define('underscore', [], function() {
return _;
});
}
}).call(this);
},{}],14:[function(require,module,exports){
'use strict';
function BoardCoordinates(x, y) {
if (y === undefined) {
this.offset = x;
this.x = this.offset % 8;
this.y = Math.floor(this.offset / 8);
} else {
this.offset = x + 8 * y;
this.x = x;
this.y = y;
}
}
BoardCoordinates.prototype.add = function (offset) {
return new BoardCoordinates(this.x + offset.x, this.y + offset.y);
};
BoardCoordinates.prototype.sub = function (offset) {
return new BoardCoordinates(this.x - offset.x, this.y - offset.y);
};
BoardCoordinates.prototype.isValid = function () {
return this.x >= 0 && this.x < 8 && this.y >= 0 && this.y < 8;
};
module.exports = {
BoardCoordinates: BoardCoordinates
};
},{}],15:[function(require,module,exports){
'use strict';
var Coord = require('./coordinates').BoardCoordinates;
/*
* Board access utility functions
*/
function isFree(position, coord) {
return coord.isValid() && position.board[coord.offset] == null;
}
function isOpponent(position, coord) {
if (!coord.isValid()) {
return false;
}
var piece = position.board[coord.offset];
return piece != null
&& position.turn !== piece.side;
}
function isFreeOrOpponent(position, coord) {
if (!coord.isValid()) {
return false;
}
var piece = position.board[coord.offset];
return piece == null
|| position.turn !== piece.side;
}
function iterateMovementInDirection(position, coord, direction, destinations) {
var c = coord;
while (true) {
c = c.add(direction);
if (!c.isValid()) {
break;
}
if (isFree(position, c)) {
destinations.push(c);
} else {
if (isOpponent(position, c)) {
destinations.push(c);
}
break;
}
}
}
/*
* Piece movement logic
*/
var pieceDestinationsEvaluator = [];
pieceDestinationsEvaluator.P = function (position, coord) {
// Movement vector
var dy = position.turn == 'W' ? 1 : -1;
// Original row of a pawn
var iy = position.turn == 'W' ? 1 : 6;
var coordAhead1 = coord.add(new Coord(0, dy));
var coordAhead2 = coordAhead1.add(new Coord(0, dy));
var coordSide1 = coord.add(new Coord(1, dy));
var coordSide2 = coord.add(new Coord(-1, dy));
var destinations = [];
if (isFree(position, coordAhead1)) {
destinations.push(coordAhead1);
}
if (isOpponent(position, coordSide2)) {
destinations.push(coordSide2);
} else if (position.lastPawnMoveColumn == coord.x - 1 && (coord.y - iy) / dy == 3) {
destinations.push(coordSide2);
}
if (isOpponent(position, coordSide1)) {
destinations.push(coordSide1);
} else if (position.lastPawnMoveColumn == coord.x + 1 && (coord.y - iy) / dy == 3) {
destinations.push(coordSide1);
}
if (coord.y == iy
&& isFree(position, coordAhead1)
&& isFree(position, coordAhead2)) {
destinations.push(coordAhead2);
}
return destinations;
};
pieceDestinationsEvaluator.N = function (position, coord) {
var destinations = [];
[
coord.add(new Coord(-1, 2)),
coord.add(new Coord(1, 2)),
coord.add(new Coord(2, -1)),
coord.add(new Coord(2, 1)),
coord.add(new Coord(1, -2)),
coord.add(new Coord(-1, -2)),
coord.add(new Coord(-2, 1)),
coord.add(new Coord(-2, -1))
].forEach(function (potentialDestination) {
if (isFreeOrOpponent(position, potentialDestination)) {
destinations.push(potentialDestination);
}
});
return destinations;
};
pieceDestinationsEvaluator.B = function (position, coord) {
var destinations = [];
[
new Coord(-1, -1),
new Coord(-1, 1),
new Coord(1, -1),
new Coord(1, 1)
].forEach(function (direction) {
iterateMovementInDirection(position, coord, direction, destinations);
});
return destinations;
};
pieceDestinationsEvaluator.R = function (position, coord) {
var destinations = [];
[
new Coord(-1, 0),
new Coord(1, 0),
new Coord(0, -1),
new Coord(0, 1)
].forEach(function (direction) {
iterateMovementInDirection(position, coord, direction, destinations);
});
return destinations;
};
pieceDestinationsEvaluator.K = function (position, coord) {
var destinations = [];
for (var dx = -1; dx < 2; dx++) {
for (var dy = -1; dy < 2; dy++) {
if (dx != 0 || dy != 0) {
var delta = new Coord(dx, dy);
var dest = coord.add(delta);
if (isFreeOrOpponent(position, dest)) {
destinations.push(dest);
}
}
}
}
if (position.castlingFlags[position.turn].K) {
var cStep1 = coord.add(new Coord(1, 0));
var cStep2 = coord.add(new Coord(2, 0));
if (isFree(position, cStep1) && isFree(position, cStep2)) {
destinations.push(cStep2);
}
}
if (position.castlingFlags[position.turn].Q) {
var cStep1 = coord.add(new Coord(-1, 0));
var cStep2 = coord.add(new Coord(-2, 0));
if (isFree(position, cStep1) && isFree(position, cStep2)) {
destinations.push(cStep2);
}
}
return destinations;
};
pieceDestinationsEvaluator.Q = function (position, coord) {
var destinations = [];
[
new Coord(-1, 0),
new Coord(1, 0),
new Coord(0, -1),
new Coord(0, 1),
new Coord(-1, -1),
new Coord(-1, 1),
new Coord(1, -1),
new Coord(1, 1)
].forEach(function (direction) {
iterateMovementInDirection(position, coord, direction, destinations);
});
return destinations;
};
/*
* Exported functions
*/
function computeAllMoves(position) {
// Compute possible moves based only on piece movement.
var availableMoves = [];
for (var offset = 0; offset <= 64; offset++) {
var piece = position.board[offset];
var coord = new Coord(offset);
if (piece != null && piece.side === position.turn) {
var targets = pieceDestinationsEvaluator[piece.type](position, coord);
targets.forEach(function (dest) {
availableMoves.push({src: coord.offset, dst: dest.offset});
});
}
}
return availableMoves;
}
module.exports = {
computeAllMoves: computeAllMoves,
pieceDestinationsEvaluator: pieceDestinationsEvaluator
};
},{"./coordinates":14}],16:[function(require,module,exports){
'use strict';
var coordinates = require('./coordinates');
var updates = require('./updates');
var movesPieces = require('./moves-pieces');
var Coord = coordinates.BoardCoordinates;
function findPiece(position, pieceType, pieceSide) {
var result = null;
for (var offset = 0; offset < 64; offset++) {
var piece = position.board[offset];
if (piece != null && piece.type === pieceType && piece.side === pieceSide) {
result = offset;
break;
}
}
return result;
}
function getAvailableMoves(position) {
var availableMoves = movesPieces.computeAllMoves(position);
// Prune moves that would lead to a check situation.
var legalmoves = [];
var isMoveLeadingToThreat = function (move, pieceType, pieceSide) {
var updatedPosition = updates.applyMove(position, move);
var opponentMoves = movesPieces.computeAllMoves(updatedPosition);
var kingOffset = findPiece(updatedPosition, pieceType, pieceSide);
var kingThreat = false;
opponentMoves.forEach(function (move) {
if (move.dst == kingOffset) {
kingThreat = true;
}
});
return kingThreat;
};
availableMoves.forEach(function (move) {
var src = new Coord(move.src);
var dst = new Coord(move.dst);
var delta = dst.sub(src);
var kingThreat = isMoveLeadingToThreat(move, 'K', position.turn);
// Kingside castling
if (position.board[move.src].type == 'K' && delta.x == 2) {
kingThreat = kingThreat || isMoveLeadingToThreat({src: move.src, dst: move.src + 1}, 'K', position.turn)
}
// Queenside castling
if (position.board[move.src].type == 'K' && delta.x == -2) {
kingThreat = kingThreat || isMoveLeadingToThreat({src: move.src, dst: move.src - 1}, 'K', position.turn)
}
if (!kingThreat) {
legalmoves.push(move);
}
});
return legalmoves;
}
function isCurrentPlayerInCheck(position) {
var isCheck = false;
var initialTurn = position.turn;
position.turn = initialTurn === 'W' ? 'B' : 'W';
for (var offset = 0; offset <= 64; offset++) {
var piece = position.board[offset];
var coord = new Coord(offset);
if (piece != null && piece.side === position.turn) {
var targets = movesPieces.pieceDestinationsEvaluator[piece.type](position, coord);
targets.forEach(function (dest) {
var destPiece = position.board[dest.offset];
if (destPiece && destPiece.type === 'K' && destPiece.side !== position.turn) {
isCheck = true;
}
});
}
}
position.turn = initialTurn;
return isCheck;
}
module.exports = {
getAvailableMoves: getAvailableMoves,
isCurrentPlayerInCheck: isCurrentPlayerInCheck
};
},{"./coordinates":14,"./moves-pieces":15,"./updates":22}],17:[function(require,module,exports){
'use strict';
var chessMoves = require('./moves');
var chessUpdates = require('./updates');
var parser = require('./pgnParser');
var specialMoves = {
'O-O': {
'W': {src: 4, dst: 6},
'B': {src: 60, dst: 62}
},
'O-O-O': {
'W': {src: 4, dst: 2},
'B': {src: 60, dst: 58}
}
};
/**
* Convert an offset to a PGN coord: 0 -> a1, ... , 63 -> h8
*/
function coordToName(offset) {
var move = '';
move += String.fromCharCode('a'.charCodeAt(0) + (offset % 8));
move += String.fromCharCode('1'.charCodeAt(0) + Math.floor(offset / 8));
return move;
}
function pgnToMove(position, pgnMove) {
var move = null;
var specialMove = specialMoves[pgnMove];
if (specialMove) {
move = specialMove[position.turn];
} else {
var pgnFields = parser.parsePgnMove(pgnMove);
if (pgnFields) {
var availableMoves = chessMoves.getAvailableMoves(position);
availableMoves.forEach(function (m) {
if (pgnFields.srcCol != null && m.src % 8 != pgnFields.srcCol) {
return;
}
if (pgnFields.srcRow != null && Math.floor(m.src / 8) != pgnFields.srcRow) {
return;
}
if (m.dst == pgnFields.dst && position.board[m.src].type == pgnFields.type) {
pgnFields.src = m.src;
}
});
if (pgnFields.src != null && pgnFields.dst != null) {
move = {src: pgnFields.src, dst: pgnFields.dst};
}
}
}
return move;
}
function moveToPgn(position, move) {
var pgn = '';
var piece = position.board[move.src];
if (piece.type != 'P') {
pgn += piece.type;
}
if (piece.type === 'K' && (move.dst - move.src) == 2) {
return 'O-O';
}
if (piece.type === 'K' && (move.dst - move.src) == -2) {
return 'O-O-O';
}
var availableMoves = chessMoves.getAvailableMoves(position);
var possibleSources = [];
availableMoves.forEach(function (m) {
if (m.dst == move.dst && piece.type == position.board[m.src].type) {
possibleSources.push(m.src);
}
});
var src = coordToName(move.src);
if (possibleSources.length > 1) {
var s1 = coordToName(possibleSources[0]);
var s2 = coordToName(possibleSources[1]);
if (s1[0] == s2[0]) {
pgn += src[1];
} else {
pgn += src[0];
}
}
if (position.board[move.dst] != null) {
if (pgn == '' && piece.type == 'P') {
pgn += src[0];
}
pgn += 'x';
}
pgn += coordToName(move.dst);
var nextPosition = chessUpdates.applyMove(position, move);
// Pawn promotion
if (piece.type == 'P' && (move.dst < 8 || move.dst > 56)) {
pgn += '=Q';
}
// Check
if (chessMoves.isCurrentPlayerInCheck(nextPosition)) {
pgn += '+';
}
return pgn;
}
module.exports = {
pgnToMove: pgnToMove,
moveToPgn: moveToPgn
};
},{"./moves":16,"./pgnParser":18,"./updates":22}],18:[function(require,module,exports){
'use strict';
var CHARCODE_A = 'a'.charCodeAt(0);
var CHARCODE_1 = '1'.charCodeAt(0);
function computeOffset(pgn, cursor) {
var offs = null;
var col = pgn.charCodeAt(cursor) - CHARCODE_A;
var row = (pgn.charCodeAt(cursor + 1) - CHARCODE_1);
if (col >= 0 && col < 8 && row >= 0 && row < 8) {
offs += col;
offs += row * 8;
}
return offs;
}
function parsePgnMove(pgn) {
var fields = {
type: null,
srcCol: null,
srcRow: null,
dst: null,
promotion: null,
capture: false,
checking: false,
mate: false
};
// Coodinates are bound by a cursor offset to the left and a payload length
var cursor = 0;
var length = pgn.length;
// Piece type prefix will shift cursor
if (['P', 'R', 'N', 'B', 'K', 'Q'].indexOf(pgn[cursor]) != -1) {
fields.type = pgn[cursor];
cursor++;
} else {
fields.type = 'P';
}
// Checking, mate and promotions will decrease length
var checkingIndex = pgn.indexOf('+');
var mateIndex = pgn.indexOf('#');
var promotionIndex = pgn.indexOf('=');
if (checkingIndex >= 0) {
fields.checking = true;
length = checkingIndex;
}
if (mateIndex >= 0) {
fields.mate = true;
length = mateIndex;
}
if (promotionIndex >= 0) {
fields.promotion = pgn.substring(promotionIndex + 1, promotionIndex + 2);
length = promotionIndex;
}
// Now the destination is at the right of the payload
var dstOffset = computeOffset(pgn, length - 2);
if (dstOffset != null) {
fields.dst = dstOffset;
} else {
// Reject invalid syntax here
return null;
}
length -= 2;
if (pgn[length - 1] === 'x') {
length -= 1;
fields.capture = true;
}
// Only src col/row hints should remain
for (var i = cursor; i < length; i++) {
var c = pgn.charCodeAt(cursor);
var row = c - CHARCODE_1;
var col = c - CHARCODE_A;
if (row >= 0 && row < 8) {
fields.srcRow = row;
}
if (col >= 0 && col < 8) {
fields.srcCol = col;
}
}
return fields;
}
module.exports = {
parsePgnMove: parsePgnMove
};
},{}],19:[function(require,module,exports){
'use strict';
var asciiToExtended = {
"PW": "♙",
"PB": "♟",
"NW": "♘",
"NB": "♞",
"BW": "♗",
"BB": "♝",
"RW": "♖",
"RB": "♜",
"QW": "♕",
"QB": "♛",
"KW": "♔",
"KB": "♚"
};
function pieceFactory(piece, side) {
return {type: piece, side: side};
}
function pieceToUTF8(piece) {
return(asciiToExtended[piece.type.concat(piece.side)]);
}
module.exports = {
pieceFactory: pieceFactory,
pieceToUTF8: pieceToUTF8
};
},{}],20:[function(require,module,exports){
'use strict';
var piece = require('./piece');
var colors = require('colors');
colors.setTheme({
BW: ['black', 'bgWhite'],
BB: ['black', 'bgMagenta'],
WW: ['white', 'bgWhite'],
WB: ['white', 'bgMagenta']
});
var _ = require('underscore-plus');
function clone(position) {
return _.deepClone(position);
}
function getInitialPosition() {
return {
turn: 'W',
castlingFlags: {
'W': {'K': true, 'Q': true},
'B': {'K': true, 'Q': true}
},
lastPawnMoveColumn: null,
check: false,
board: [
piece.pieceFactory("R", "W"),
piece.pieceFactory("N", "W"),
piece.pieceFactory("B", "W"),
piece.pieceFactory("Q", "W"),
piece.pieceFactory("K", "W"),
piece.pieceFactory("B", "W"),
piece.pieceFactory("N", "W"),
piece.pieceFactory("R", "W"),
piece.pieceFactory("P", "W"),
piece.pieceFactory("P", "W"),
piece.pieceFactory("P", "W"),
piece.pieceFactory("P", "W"),
piece.pieceFactory("P", "W"),
piece.pieceFactory("P", "W"),
piece.pieceFactory("P", "W"),
piece.pieceFactory("P", "W"),
null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null,
piece.pieceFactory("P", "B"),
piece.pieceFactory("P", "B"),
piece.pieceFactory("P", "B"),
piece.pieceFactory("P", "B"),
piece.pieceFactory("P", "B"),
piece.pieceFactory("P", "B"),
piece.pieceFactory("P", "B"),
piece.pieceFactory("P", "B"),
piece.pieceFactory("R", "B"),
piece.pieceFactory("N", "B"),
piece.pieceFactory("B", "B"),
piece.pieceFactory("Q", "B"),
piece.pieceFactory("K", "B"),
piece.pieceFactory("B", "B"),
piece.pieceFactory("N", "B"),
piece.pieceFactory("R", "B")
]
};
}
function getColoredPiece(row, col, side, pieceStr) {
var coloredStr;
if((row+col)%2==0) {
if(side === 'W') {
coloredStr = pieceStr.WB;
} else {
coloredStr = pieceStr.BB;
}
} else if(side === 'W') {
coloredStr = pieceStr.WW;
} else {
coloredStr = pieceStr.BW;
}
return coloredStr;
}
function positionToString(position, utfFlag) {
var strings = [];
strings.push(position.turn == 'W' ? 'WHITE' : 'BLACK');
strings.push(' ');
strings.push(position.castlingFlags.W.K ? 'K' : '');
strings.push(position.castlingFlags.W.Q ? 'Q' : '');
strings.push(position.castlingFlags.B.K ? 'k' : '');
strings.push(position.castlingFlags.B.Q ? 'q' : '');
var row;
var col;
for (row = 7; row >= 0; row--) {
strings.push('\n');
strings.push(row+1);
strings.push(' ');
for (col = 0; col < 8; col++) {
var currentPiece = position.board[row * 8 + col];
if(utfFlag) {
if (currentPiece == null) {
strings.push(getColoredPiece(row, col, 'W', ' '));
} else {
strings.push(getColoredPiece(row, col, currentPiece.side, piece.pieceToUTF8(currentPiece) + ' '));
}
} else {
if(currentPiece == null) {
strings.push('. ');
} else if (currentPiece.side == 'W') {
strings.push(currentPiece.type.toUpperCase() + ' ');
} else {
strings.push(currentPiece.type.toLowerCase() + ' ');
}
}
}
}
strings.push('\n a b c d e f g h ');
return strings.join('');
}
module.exports = {
getInitialPosition: getInitialPosition,
positionToString: positionToString,
clone: clone
};
},{"./piece":19,"colors":5,"underscore-plus":12}],21:[function(require,module,exports){
'use strict';
var moves = require('./moves');
function getGameStatus(position) {
var availableMoves = moves.getAvailableMoves(position);
var status = 'OPEN';
if (availableMoves.length == 0) {
if (position.check) {
status = position.turn == 'W' ? 'BLACKWON' : 'WHITEWON';
} else {
status = 'PAT';
}
}
return status;
}
module.exports = {
getGameStatus: getGameStatus
};
},{"./moves":16}],22:[function(require,module,exports){
'use strict';
var positions = require('./position');
var coordinates = require('./coordinates');
var movesPieces = require('./moves-pieces');
var Coord = coordinates.BoardCoordinates;
function computeDiffs(position, move) {
var diffs = [];
var src = new Coord(move.src);
var dst = new Coord(move.dst);
var delta = dst.sub(src);
var destinationPiece = position.board[move.dst];
if (destinationPiece != null) {
diffs.push({action: 'remove', src: move.dst});
}
diffs.push({action: 'move', src: move.src, dst: move.dst});
// Special case for 'en passant'
if (destinationPiece == null && position.board[move.src].type == 'P' && Math.abs(delta.x)) {
var takenPieceCoord = src.add(new Coord(delta.x, 0));
diffs.push({action: 'remove', src: takenPieceCoord.offset});
}
if (position.board[move.src].type == 'P' && Math.abs(delta.y) == 2) {
diffs.push({action: 'pawnColumn', col: move.src % 8});
} else {
diffs.push({action: 'pawnColumn', col: null});
}
if (position.board[move.src].type == 'P' && (dst.y == 0 || dst.y == 7)) {
diffs.push({action: 'promote', src: move.dst});
}
if (position.board[move.src].type == 'K') {
diffs.push({action: 'resetCastling', sides: ['K', 'Q']});
if (src.x == 4 && dst.x == 6) {
// Move the rook for kingside castling
diffs.push({
action: 'move',
src: dst.add(new Coord(1, 0)).offset,
dst: dst.add(new Coord(-1, 0)).offset
});
}
if (src.x == 4 && dst.x == 2) {
// Move the rook for queenside castling
diffs.push({
action: 'move',
src: dst.add(new Coord(-2, 0)).offset,
dst: dst.add(new Coord(1, 0)).offset
});
}
}
if (position.board[move.src].type == 'R' && src.x == 0) {
diffs.push({action: 'resetCastling', sides: ['Q']});
}
if (position.board[move.src].type == 'R' && src.x == 7) {
diffs.push({action: 'resetCastling', sides: ['K']});
}
// Update the check flag if the move threatends the opponent's king.
var updatedPosition = applyDiffs(position, diffs);
updatedPosition.turn = position.turn;
var checkFlag = false;
movesPieces.computeAllMoves(updatedPosition).forEach(function (m) {
var targetPiece = position.board[m.dst];
if (targetPiece && targetPiece.type == 'K' && targetPiece.side != position.turn) {
checkFlag = true;
}
});
diffs.push({action: 'updateCheckFlag', value: checkFlag});
return diffs;
}
function applyDiffs(position, diffs) {
var targetPosition = positions.clone(position);
diffs.forEach(function (diff) {
if (diff.action === 'remove') {
var offset = diff.src;
targetPosition.board[offset] = null;
} else if (diff.action === 'move') {
targetPosition.board[diff.dst] = targetPosition.board[diff.src];
targetPosition.board[diff.src] = null;
} else if (diff.action === 'pawnColumn') {
targetPosition.lastPawnMoveColumn = diff.col;
} else if (diff.action === 'resetCastling') {
diff.sides.forEach(function (side) {
targetPosition.castlingFlags[position.turn][side] = false;
});
} else if (diff.action === 'updateCheckFlag') {
targetPosition.check = diff.value;
} else if (diff.action === 'promote') {
targetPosition.board[diff.src].type = 'Q';
}
});
targetPosition.turn = position.turn === 'W' ? 'B' : 'W';
return targetPosition;
}
function applyMove(position, move) {
var diffs = computeDiffs(position, move);
return applyDiffs(position, diffs);
}
module.exports = {
computeDiffs: computeDiffs,
applyDiffs: applyDiffs,
applyMove: applyMove
};
},{"./coordinates":14,"./moves-pieces":15,"./position":20}],23:[function(require,module,exports){
'use strict';
var position = require('./chess/position');
var moves = require('./chess/moves');
var updates = require('./chess/updates');
var pgn = require('./chess/pgn');
var status = require('./chess/status');
var chessRules = {
getInitialPosition: position.getInitialPosition,
positionToString: position.positionToString,
getAvailableMoves: moves.getAvailableMoves,
applyMove: updates.applyMove,
computeDiffs: updates.computeDiffs,
applyDiffs: updates.applyDiffs,
pgnToMove: pgn.pgnToMove,
moveToPgn: pgn.moveToPgn,
getGameStatus: status.getGameStatus
};
module.exports = chessRules;
},{"./chess/moves":16,"./chess/pgn":17,"./chess/position":20,"./chess/status":21,"./chess/updates":22}],24:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;
function cleanUpNextTick() {
draining = false;
if (currentQueue.length) {
queue = currentQueue.concat(queue);
} else {
queueIndex = -1;
}
if (queue.length) {
drainQueue();
}
}
function drainQueue() {
if (draining) {
return;
}
var timeout = setTimeout(cleanUpNextTick);
draining = true;
var len = queue.length;
while(len) {
currentQueue = queue;
queue = [];
while (++queueIndex < len) {
currentQueue[queueIndex].run();
}
queueIndex = -1;
len = queue.length;
}
currentQueue = null;
draining = false;
clearTimeout(timeout);
}
process.nextTick = function (fun) {
var args = new Array(arguments.length - 1);
if (arguments.length > 1) {
for (var i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
}
queue.push(new Item(fun, args));
if (queue.length === 1 && !draining) {
setTimeout(drainQueue, 0);
}
};
// v8 likes predictible objects
function Item(fun, array) {
this.fun = fun;
this.array = array;
}
Item.prototype.run = function () {
this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};
function noop() {}
process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.binding = function (name) {
throw new Error('process.binding is not supported');
};
// TODO(shtylman)
process.cwd = function () { return '/' };
process.chdir = function (dir) {
throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };
},{}],25:[function(require,module,exports){
'use-strict';
var strategy = require('./strategy');
var _monitor = require('./../monitoring/monitoring');
//Settings
var castlingRate = 40;
var checkRate = 100;
//var checkMateRate = 200000;
//var staleMateRate = 150000;
//var movabilityRate = 5;
/**
* Evaluate the current position for the current player (turn).
*
* @param position The current position and turn
* @param strategyName The name of the strategy to use
* @returns {number} The score (regarding the strategy currently set)
*/
function ratePositionAndPieces(position, strategyName) {
var score = 0;
var ind;
for (ind = 0; ind < position.board.length; ind++) {
var currentPiece = position.board[ind];
if (currentPiece != null && currentPiece.side == position.turn) {
score += strategy.getPieceScore(currentPiece, strategyName);
score += strategy.getPositionScore(currentPiece, ind, strategyName);
}
}
return score;
}
/**
* Rate the defense (castlings).
*
* @param position The current position and turn
* @returns {number} The score (regarding the strategy currently set)
*/
function rateKingSafety(position) {
var score = 0;
/**
* TODO: increase the score for castled kings.
*/
//Castlings: if castled, increase the score
//if(castled) {
// score += castlingRate;
//} else {
//Castlings: if castling is not possible anymore, reduce the score
if(!position.castlingFlags[position.turn].K) {
score -= castlingRate;
}
if(!position.castlingFlags[position.turn].Q) {
score -= castlingRate;
}
//}
return score;
}
/**
* Rate the Movability including checks and stale situations.
*
* @param position The current position and turn
* @param depth The depth in the search algorithm
* @param playerTurn True if the function is called to rate the player's turn, false if opponent
* @returns {number} The score (regarding the strategy currently set)
*/
function rateMovability(position, depth, playerTurn) {
var score = 0;
//score += movesLength*movabilityRate;
if(playerTurn) {
//if (movesLength == 0) {
// if (position.check) {
// score -= checkMateRate * (depth+1);
// } else {
// score -= staleMateRate * (depth+1);
// }
//} else
if (position.check) {
score -= checkRate * (depth+1);
}
}
return score;
}
/**
* Evaluate the board for the current player (turn).
*
* @param currentPosition The current position and turn
* @param depth the depth in the search algorithm
* @param strategyName The name of the strategy to use
* @returns {number} The score (regarding the strategy currently set)
*/
function evaluateBoard(currentPosition, depth, strategyName) {
_monitor.startWatch('evaluateBoard');
var score = ratePositionAndPieces(currentPosition, strategyName);
score += rateKingSafety(currentPosition);
score += rateMovability(currentPosition, depth, true);
currentPosition.turn = currentPosition.turn === 'W' ? 'B' : 'W';
score -= ratePositionAndPieces(currentPosition, strategyName);
score -= rateKingSafety(currentPosition);
score -= rateMovability(currentPosition, depth, false);
currentPosition.turn = currentPosition.turn === 'W' ? 'B' : 'W';
_monitor.stopWatch('evaluateBoard');
return score;
}
module.exports.evaluateBoard = evaluateBoard;
},{"./../monitoring/monitoring":31,"./strategy":29}],26:[function(require,module,exports){
'use-strict';
var chessRules = require('chess-rules');
var strategy = require('./strategy');
var _monitor = require('./../monitoring/monitoring');
/**
* Evaluate a move for the current position.
* - Evaluate capture (piece attacked)
* - Evaluate castling
* - Evaluate piece position
* - Evaluate piece promotion
*
* @param position The position
* @param move The move
* @param aiStrategy The strategy
* @returns {number} The value (higher the value, better the move)
*/
function evaluateMove(position, move, aiStrategy) {
_monitor.startWatch('evaluateMove');
var score = 0;
_monitor.startWatch('evaluateMove-applyMove');
var appliedPosition = chessRules.applyMove(position, move);
_monitor.stopWatch('evaluateMove-applyMove');
if(appliedPosition.check) {
//Check
score += 1000;
}
var piece = position.board[move.src];
var pieceAfter = appliedPosition.board[move.dst];
if (piece.type === 'K' && ((move.dst - move.src) == 2 || (move.dst - move.src) == -2)) {
//Castling
score += 100;
} else {
var pieceAttacked = position.board[move.dst];
if(pieceAttacked !== null) {
//Capture
var pieceScore = strategy.getPieceScore(piece, aiStrategy);
score += pieceScore;
var pieceAttackedScore = strategy.getPieceScore(pieceAttacked, aiStrategy);
if(pieceScore < pieceAttackedScore) {
score += pieceAttackedScore - pieceScore;
}
}
if(pieceAfter.type !== piece.type) {
//Promotion
score += strategy.getPieceScore(pieceAfter, aiStrategy);
}
}
//Evaluate position
score += strategy.getPositionScore(pieceAfter, move.dst, aiStrategy);
score -= strategy.getPositionScore(piece, move.src, aiStrategy);
_monitor.stopWatch('evaluateMove');
return score;
}
/**
* Evaluate each move based on the position and strategy.
*
* @param moves The array of available moves
* @param position The current position
* @param aiStrategy The current strategy
* @returns {Array} The array of evaluated moves (pgn, move, value)
*/
function evaluateMoves(moves, position, aiStrategy) {
_monitor.startWatch('evaluateMoves');
var evaluatedMoves = new Array(moves.length);
var i;
for (i = 0; i < moves.length; i++) {
var move = moves[i];
var value = evaluateMove(position, move, aiStrategy);
evaluatedMoves[i] = {
move: move,
value: value
};
}
_monitor.stopWatch('evaluateMoves');
return evaluatedMoves;
}
module.exports.evaluateMoves = evaluateMoves;
},{"./../monitoring/monitoring":31,"./strategy":29,"chess-rules":23}],27:[function(require,module,exports){
'use strict';
/**
* Get the basic strategy tables for White player (Black should be the opposite index):
* - Pawns go forward and prefer center
* - Rooks stick to the edge
* - Bishops and Knights avoid the corners and edges
* - Queen prefers the edge and avoid the center
* - King hide
*/
function getStrategyPositionTable() {
/**
* TODO: need to be tuned.
*/
return {
'P': [
0, 0, 0, 0, 0, 0, 0, 0,
5, 10, 10,-25,-25, 10, 10, 5,
5, -5,-10, 0, 0,-10, -5, 5,
0, 0, 0, 25, 25, 0, 0, 0,
5, 5, 10, 27, 27, 10, 5, 5,
10, 10, 20, 30, 30, 20, 10, 10,
30, 30, 30, 40, 40, 30, 30, 30,
50, 50, 50, 50, 50, 50, 50, 50
],
'N': [
-50,-40,-30,-30,-30,-30,-40,-50,
-40,-20, 0, 5, 5, 0,-20,-40,
-30, 5, 10, 15, 15, 10, 5,-30,
-30, 0, 15, 20, 20, 15, 0,-30,
-30, 5, 15, 20, 20, 15, 5,-30,
-30, 0, 10, 15, 15, 10, 0,-30,
-40,-20, 0, 0, 0, 0,-20,-40,
-50,-40,-20,-30,-30,-20,-40,-50
],
'B': [
-20,-10,-40,-10,-10,-40,-10,-20
-10, 5, 0, 0, 0, 0, 5,-10,
-10, 10, 10, 10, 10, 10, 10,-10,
-10, 0, 10, 10, 10, 10, 0,-10,
-10, 5, 5, 10, 10, 5, 5,-10,
-10, 0, 5, 10, 10, 5, 0,-10,
-10, 0, 0, 0, 0, 0, 0,-10,
-20,-10,-10,-10,-10,-10,-10,-20
],
'R': [
20, 10, 10, 10, 10, 10, 10, 20,
20, 0, 0, 0, 0, 0, 0, 20,
20, 0, -5,-10,-10, -5, 0, 20,
10, 0,-10,-15,-15,-10, 0, 10,
10, 0,-10,-15,-15,-10, 0, 10,
10, 0, -5,-10,-10, -5, 0, 10,
20, 0, 0, 0, 0, 0, 0, 20,
20, 20, 10, 10, 10, 10, 20, 20
],
'Q': [
30, 20, 20, 50, 50, 20, 20, 30,
20, 20, 10, 5, 5, 10, 20, 20,
20, 10, -5,-10,-10, -5, 10, 20,
30, 0,-15,-20,-20,-15, 0, 30,
30, 0,-15,-20,-20,-15, 0, 30,
20, 0,-10,-50,-50,-20, 0, 20,
20, 20, 0, 0, 0, 0, 20, 20,
30, 20, 20, 40, 40, 20, 20, 30
],
'K': [
20, 30, 10, 0, 0, 10, 30, 20,
20, 20, 0, 0, 0, 0, 20, 20,
-10, -20, -20, -20, -20, -20, -20, -10,
-20, -30, -30, -40, -40, -30, -30, -20,
-30, -40, -40, -50, -50, -40, -40, -30,
-30, -40, -40, -50, -50, -40, -40, -30,
-30, -40, -40, -50, -50, -40, -40, -30,
-30, -40, -40, -50, -50, -40, -40, -30
]
};
}
function getStrategyPiecesTable() {
return {
'P': 100
,'R': 500
,'B': 325
,'N': 300
,'Q': 900
,'K': 32767
}
}
module.exports.getStrategyPositionTable = getStrategyPositionTable;
module.exports.getStrategyPiecesTable = getStrategyPiecesTable;
},{}],28:[function(require,module,exports){
'use strict';
/**
* Get the random strategy tables: no preferences of position for pieces whatsoever.
*/
function getStrategyPositionTable() {
return {
'P': [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
],
'R': [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
],
'B': [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
],
'N': [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
],
'Q': [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
],
'K': [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]
};
}
function getStrategyPiecesTable() {
return {
'P': 0
,'R': 0
,'B': 0
,'N': 0
,'Q': 0
,'K': 0
}
}
module.exports.getStrategyPositionTable = getStrategyPositionTable;
module.exports.getStrategyPiecesTable = getStrategyPiecesTable;
},{}],29:[function(require,module,exports){
'use strict';
var basic = require('./strategies/basic');
var random = require('./strategies/random');
var strategyPositions = {
'basic': basic.getStrategyPositionTable,
'random': random.getStrategyPositionTable
};
var strategyPieces = {
'basic': basic.getStrategyPiecesTable,
'random': random.getStrategyPiecesTable
};
/**
* Get the strategy table with the scores per piece positions.
* @param name The name of the strategy
* @returns {*} The table
*/
function getStrategyPositionTable(name) {
return strategyPositions[name]();
}
/**
* Get the strategy table with the scores for each piece.
* @param name The name of the strategy
* @returns {*} The table
*/
function getStrategyPiecesTable(name) {
return strategyPieces[name]();
}
/**
* Get the score of the piece for the current turn, index and strategy.
* @param piece The chess piece
* @param index The position index
* @param strategy The strategy name
* @returns {number} The score
*/
function getPositionScore(piece, index, strategy) {
var score = 0;
var strategyTables = getStrategyPositionTable(strategy);
if('B' === piece.side) {
//The table are defined for White player, so Black scores can be found by negating the index
var blackIndex = 63 - index;
score = strategyTables[piece.type][blackIndex];
} else {
score = strategyTables[piece.type][index];
}
//console.log('getScore(piece:' + piece.type
// + ',turn:' + piece.side
// + ',index:' + index
// + ') = ' + score
//);
return score;
}
/**
* Get the score of the piece with the strategy passed in.
* @param piece The chess piece
* @param strategy The strategy name
* @returns {number} The score
*/
function getPieceScore(piece, strategy) {
var piecesWeigh = getStrategyPiecesTable(strategy);
var score = piecesWeigh[piece.type];
//console.log('getPieceScore(piece:' + piece.type + ') = ' + score);
return score;
}
module.exports.getPositionScore = getPositionScore;
module.exports.getPieceScore = getPieceScore;
},{"./strategies/basic":27,"./strategies/random":28}],30:[function(require,module,exports){
'use strict';
var chessRules = require('chess-rules');
var aiSearch = require('./search/alpha-beta');
var _monitor = require('./monitoring/monitoring');
// Defaults options
var defaults = {
depth: 3, // Depth of the search algorithm
monitor: false, // Enable/Disable the monitoring
strategy: 'basic' , // Strategy to use, 'basic' as default
timeout: 10000 // Timeout after which the AI returns a move
};
/**
* Merge options arrays passed in.
*/
function mergeOptions(options) {
for (var i=1; i < arguments.length; i++) {
var def = arguments[i];
for (var n in def) {
if (options[n] === undefined) {
options[n] = def[n];
}
}
}
return options;
}
/**
* Apply the options passed in argument.
*
* @param options The options as an array of objects
*/
function refreshOptions(options) {
aiSearch.setTimeout(options.timeout);
aiSearch.setDepth(options.depth);
aiSearch.setStrategy(options.strategy);
_monitor.setEnabled(options.monitor);
}
/**
* Set the AI options including search Depth, timeout and monitoring.
*
* @param options The options as an array of objects
*/
function setOptions(options) {
var opts = mergeOptions(options || {}, defaults);
refreshOptions(opts);
}
/**
* Get the next move from the current status of the game.
*
* @param position The actual positions
* @returns {*} the pgn move chosen by the AI
*/
function playPosition(position) {
var aiMove = aiSearch.getNextMove(position);
return aiMove == null ? null : chessRules.moveToPgn(position, aiMove);
}
/**
* Get the next move from the complete sequence of moves of the game.
*
* @param pgnMoves the complete sequence of moves since the beginning of the game
*/
function playMoves(pgnMoves) {
var position = chessRules.getInitialPosition();
pgnMoves.forEach(function (moveText) {
var moveCoords = chessRules.pgnToMove(position, moveText);
position = chessRules.applyMove(position, moveCoords);
});
return playPosition(position);
}
refreshOptions(defaults);
module.exports.setOptions = setOptions;
module.exports.play = playMoves;
module.exports.playPosition = playPosition;
},{"./monitoring/monitoring":31,"./search/alpha-beta":35,"chess-rules":23}],31:[function(require,module,exports){
'use strict';
var cutoffs = [];
var consoleTree = [];
var nbNodeSearched = 0;
var nbCutoffs = 0;
var watches = require('./watches');
//Settings
var enabled = false;
function isEnabled() {
return enabled;
}
function setEnabled(enabledFlag) {
if(enabledFlag === undefined || typeof enabledFlag !== 'boolean') {
throw new Error('monitor value type!');
} else {
enabled = enabledFlag;
}
}
/**
* Add a cutoff node.
*
* @param path The node path
* @param alpha The alpha
* @param beta The beta
* @param type The type of search (Maximizing or Minimizing)
* @param score The score
*/
function addCutoffNode(path, alpha, beta, type, score) {
if(enabled) {
cutoffs.push(
{
path: path,
alpha: alpha,
beta: beta,
type: type,
score: score
}
);
}
}
/**
* Add a search node.
*
* @param path The node path
* @param alpha The alpha
* @param beta The beta
* @param type The type (min or max)
* @param score The score
*/
function addSearchNode(path, alpha, beta, type, score) {
if(enabled) {
consoleTree.push(
{
path: path,
alpha: alpha,
beta: beta,
type: type,
score: score
}
);
}
}
/**
* Start the watch. If the watch does not exist, it is created.
*
* @param itemKey The watch key
*/
function startWatch(itemKey) {
if(enabled) {
watches.startWatch(itemKey)
}
}
/**
* Stop the watch.
*
* @param itemKey The watch key
*/
function stopWatch(itemKey) {
if(enabled) {
watches.stopWatch(itemKey)
}
}
/**
* Reset all monitoring variables.
*/
function reset() {
if(enabled) {
cutoffs.splice(0, cutoffs.length);
consoleTree.splice(0, consoleTree.length);
nbNodeSearched = 0;
nbCutoffs = 0;
watches.reset();
}
}
/**
* Clear all monitoring devices.
*/
function clear() {
if (enabled) {
cutoffs.splice(0, cutoffs.length);
consoleTree.splice(0, consoleTree.length);
nbNodeSearched = 0;
nbCutoffs = 0;
watches.clear();
}
}
/**
* Print logs monitored logs in the console.
*
* @param full true to dump the full logs.
* @param string true to dump logs as String, false to dump objects
*/
function dumpLogs(full, string) {
if (enabled) {
console.log(consoleTree.length + ' node searched');
console.log(cutoffs.length + ' cut-offs');
//Log watches
watches.dumpLogs();
if(full) {
if (string) {
var strings;
if (cutoffs.length > 0) {
strings = ['--CUTOFFS--'];
cutoffs.forEach(function (cutoff) {
strings.push('\n');
strings.push('{'
+ 'path: ' + cutoff.path
+ ', type: ' + cutoff.type
+ ', alpha: ' + cutoff.alpha
+ ', beta: ' + cutoff.beta
+ ', score: ' + cutoff.score
+ '}');
});
console.log(strings.join(''));
}
if (consoleTree.length > 0) {
strings = ['--TREE--'];
consoleTree.forEach(function (node) {
strings.push('\n');
strings.push('{'
+ 'path: ' + node.path
+ ', type: ' + node.type
+ ', alpha: ' + node.alpha
+ ', beta: ' + node.beta
+ ', score: ' + node.score
+ '}');
});
console.log(strings.join(''));
}
} else {
console.log(consoleTree);
console.log(cutoffs);
}
}
}
}
module.exports.addSearchNode = addSearchNode;
module.exports.addCutoffNode = addCutoffNode;
module.exports.setEnabled = setEnabled;
module.exports.isEnabled = isEnabled;
module.exports.startWatch = startWatch;
module.exports.stopWatch = stopWatch;
module.exports.dumpLogs = dumpLogs;
module.exports.clear = clear;
module.exports.reset = reset;
},{"./watches":33}],32:[function(require,module,exports){
'use strict';
function StopWatch(startAt, duration, startCount) {
this.startAt = startAt ? startAt : 0;
this.duration = duration ? duration : 0;
this.startCount = startCount ? startCount : 0;
}
StopWatch.prototype.start = function () {
this.startAt = new Date().getTime();
this.startCount++;
};
StopWatch.prototype.stop = function () {
this.duration += new Date().getTime()- this.startAt;
};
StopWatch.prototype.reset = function () {
this.duration = 0;
this.startAt = 0;
this.startCount = 0;
};
StopWatch.prototype.watchToString = function () {
return this.startCount + 'x, ' + this.duration + 'ms';
};
module.exports = {
StopWatch: StopWatch
};
},{}],33:[function(require,module,exports){
'use strict';
var StopWatch = require('./stopwatch').StopWatch;
var watches = new Object();
/**
* Add a watch with the key passed in.
*
* @param itemKey The key
*/
function addWatch(itemKey) {
watches[itemKey] = new StopWatch(0,0,0);
}
/**
* Start the watch. If the watch does not exist, iy is created.
*
* @param itemKey The watch key
*/
function startWatch(itemKey) {
if(!watches[itemKey]) {
addWatch(itemKey);
}
watches[itemKey].start();
}
/**
* Stop the watch.
*
* @param itemKey The watch key
*/
function stopWatch(itemKey) {
watches[itemKey].stop();
}
/**
* Get the watch from the key.
* @param itemKey The watch key
* @returns {*} The watch
*/
function getWatch(itemKey) {
if(watches[itemKey]) {
return new StopWatch(watches[itemKey].startAt, watches[itemKey].duration, watches[itemKey].startCount);
} else {
return undefined;
}
}
/**
* Reset a watch to 0.
*
* @param itemKey The watch key
*/
function resetWatch(itemKey) {
if(!watches[itemKey]) {
addWatch(itemKey);
} else {
watches[itemKey].reset();
}
}
/**
* Clear all the watches.
*/
function clearWatches() {
var keys = [];
for (var watch in watches) {
keys.push(watch);
}
keys.forEach(function (key) {
delete watches[key];
});
}
/**
* Reset the watches.
*/
function resetWatches() {
for (var watch in watches) {
watches[watch].reset();
}
}
function resetAll() {
resetWatches();
}
/**
* Clear all monitoring devices.
*/
function clearAll() {
clearWatches();
}
function dumpLogs() {
for (var watch in watches) {
console.log('[' + watch + '] ' + watches[watch].watchToString());
}
}
module.exports.startWatch = startWatch;
module.exports.stopWatch = stopWatch;
module.exports.getWatch = getWatch;
module.exports.resetWatch = resetWatch;
module.exports.clear = clearAll;
module.exports.reset = resetAll;
module.exports.dumpLogs = dumpLogs;
},{"./stopwatch":32}],34:[function(require,module,exports){
'use-strict';
function AlphaBetaData(path, startTime) {
if (path !== undefined) {
this.path = path;
}
if(startTime !== undefined) {
this.startTime = startTime;
} else {
this.startTime = new Date().getTime();
}
}
AlphaBetaData.prototype.next = function (pgn) {
return new AlphaBetaData(this.path + '-' + pgn, this.startTime);
};
module.exports = {
AlphaBetaData: AlphaBetaData
};
},{}],35:[function(require,module,exports){
'use strict';
var chessRules = require('chess-rules');
//Search
var alphaBetaData = require('./alpha-beta-data');
var AlphaBetaData = alphaBetaData.AlphaBetaData;
var sorter = require('./quick-sort');
//Evaluation
var evaluatorMoves = require('./../evaluation/evaluator-moves');
var evaluatorBoard = require('./../evaluation/evaluator-board');
//Monitoring
var _monitor = require('./../monitoring/monitoring');
//Settings
var aiDepth = 3;
var aiTimeout = 10000;
var aiStrategy = 'basic';
/**
* Set the timeout around which the search shall return a move.
*
* @param timeout The timeout in millisecond
*/
function setTimeout(timeout) {
if(timeout === undefined || typeof timeout != 'number' || timeout%1 != 0) {
throw new Error('timeout value type!');
} else {
aiTimeout = timeout;
}
}
/**
* Set the strategy to use in the evaluation.
* @param strategyName The strategy name ('basic' by default, 'random')
*/
function setStrategy(strategyName) {
if(strategyName === undefined || typeof strategyName !== 'string') {
throw new Error('strategy value type!');
} else {
aiStrategy = strategyName;
}
}
/**
* Set the depth of the alpha-beta algorithm.
* @param depth The depth (2 by default)
*/
function setDepth(depth) {
if(depth === undefined || typeof depth != 'number' || depth%1 != 0) {
throw new Error('depth value type!');
} else {
aiDepth = depth;
}
}
/**
* Get the AI next move for the position passed in, this method follow the alpha beta max algorithm.
* @param position The position and AI turn
* @returns {*} The move
*/
function getNextMove(position) {
_monitor.clear();
_monitor.startWatch('getNextMove');
_monitor.startWatch('setup');
var alpha = -1000000;
var beta = 1000000;
var bestMove = null;
var startTime = new Date().getTime();
//Initialize the data for AlphaBeta Search
_monitor.stopWatch('setup');
//Get the available moves
_monitor.startWatch('availableMoves');
var availableMoves = chessRules.getAvailableMoves(position);
_monitor.stopWatch('availableMoves');
//Evaluate the moves
var evaluatedMoves = evaluatorMoves.evaluateMoves(availableMoves, position, aiStrategy);
//Order moves to enhance pruning
evaluatedMoves = sorter.sortMoves(evaluatedMoves);
evaluatedMoves.some(function (move) {
var _path;
if(_monitor.isEnabled()) {
_monitor.startWatch('pgn');
_path = chessRules.moveToPgn(position, move.move);
_monitor.stopWatch('pgn');
}
_monitor.startWatch('applyMove');
var nextPosition = chessRules.applyMove(position, move.move);
_monitor.stopWatch('applyMove');
//var score = alphaBetaMin(nextPosition, alpha, beta, aiDepth - 1,
// new AlphaBetaData(_pgn, startTime));
var score = -alphaBeta(nextPosition, -beta, -alpha, aiDepth - 1,
new AlphaBetaData(_path, startTime));
//Use of alpha-beta max for the first step
if(score >= beta) {
//Cut-off
_monitor.addCutoffNode(_path, alpha, beta, 'max', score);
alpha = beta;
_monitor.stopWatch('return');
return true;
}
if(score > alpha) {
//we have found a better best move (a new max)
alpha = score;
bestMove = move;
}
_monitor.stopWatch('return');
return false;
});
_monitor.stopWatch('getNextMove');
_monitor.dumpLogs(true, false);
return bestMove == null ? null : bestMove.move;
}
function isTerminal(position) {
return chessRules.getGameStatus(position) !== 'OPEN';
}
/**
* Single alpha-beta algorithm (Negamax).
*
* @param position The current position
* @param alpha The current best score
* @param beta The current worst score
* @param depth The depth
* @param alphaBetaData Data gathered at recursion depth+1
* @returns {number} The score evaluated
*/
function alphaBeta(position, alpha, beta, depth, alphaBetaData) {
if(depth == 0
|| new Date().getTime() - alphaBetaData.startTime > aiTimeout*0.98-200
|| isTerminal(position)) {
/**
* TODO: Enhance with Quiescence algorithm.
*/
_monitor.startWatch('return');
var score = evaluatorBoard.evaluateBoard(position, depth, aiStrategy);
_monitor.addSearchNode(alphaBetaData.path, alpha, beta, (aiDepth-depth)%2==0?'max':'min', score);
return score;
}
//Get the available moves
_monitor.startWatch('availableMoves');
var availableMoves = chessRules.getAvailableMoves(position);
_monitor.stopWatch('availableMoves');
//Evaluate the moves
var evaluatedMoves = evaluatorMoves.evaluateMoves(availableMoves, position, aiStrategy);
//Order moves to enhance pruning
evaluatedMoves = sorter.sortMoves(evaluatedMoves);
evaluatedMoves.some(function (move) {
var _path;
if(_monitor.isEnabled()) {
_monitor.startWatch('pgn');
_path = chessRules.moveToPgn(position, move.move);
_monitor.stopWatch('pgn');
}
_monitor.startWatch('applyMove');
var nextPosition = chessRules.applyMove(position, move.move);
_monitor.stopWatch('applyMove');
var score = -alphaBeta(nextPosition, -beta, -alpha, depth - 1, alphaBetaData.next(_path));
//Cut off
if (score >= beta) {
_monitor.addCutoffNode(alphaBetaData.path, alpha, beta, (aiDepth-depth)%2==0?'max':'min', score);
alpha = beta;
_monitor.stopWatch('return');
return true;
}
//we have found a better best move
if(score > alpha) {
alpha = score;
}
_monitor.stopWatch('return');
return false;
});
_monitor.addSearchNode(alphaBetaData.path, alpha, beta, (aiDepth-depth)%2==0?'max':'min', alpha);
return alpha;
}
module.exports.setDepth = setDepth;
module.exports.setStrategy = setStrategy;
module.exports.setTimeout = setTimeout;
module.exports.getNextMove = getNextMove;
},{"./../evaluation/evaluator-board":25,"./../evaluation/evaluator-moves":26,"./../monitoring/monitoring":31,"./alpha-beta-data":34,"./quick-sort":36,"chess-rules":23}],36:[function(require,module,exports){
'use strict';
var _monitor = require('./../monitoring/monitoring');
/**
* Sort the list of evaluated moves passed in.
*
* @param evaluatedMoves The array of evaluated moves
* @returns {*} The list of evaluated moves sorted
*/
function sortMoves(evaluatedMoves) {
_monitor.startWatch('sortMoves');
var resultMoves = quickSort(evaluatedMoves, 0, evaluatedMoves.length-1);
_monitor.stopWatch('sortMoves');
return resultMoves;
}
function quickSort(fullMoves, lowInd, highInd) {
var i = lowInd;
var j = highInd;
var pivot = checkValue(fullMoves[lowInd + Math.floor((highInd - lowInd) / 2)]);
while (i <= j) {
while(checkValue(fullMoves[i]) > pivot) {
i++;
}
while(checkValue(fullMoves[j]) < pivot) {
j--;
}
if (i <= j) {
swapMoves(fullMoves, i, j);
i++;
j--;
}
}
if (lowInd < j) {
quickSort(fullMoves, lowInd, j);
}
if (i < highInd) {
quickSort(fullMoves, i, highInd);
}
return fullMoves;
}
function checkValue(move) {
if(move.value === undefined) {
throw new Error("move.value is undefined");
}
if(move.value === null) {
throw new Error("move.value is null");
}
return move.value;
}
function swapMoves(moves, indA, indB) {
var temp = moves[indA];
moves[indA] = moves[indB];
moves[indB] = temp;
}
module.exports.sortMoves = sortMoves;
},{"./../monitoring/monitoring":31}]},{},[30])(30)
});