diff --git a/build/ludorum.min.js b/build/ludorum.min.js index b4491fb..1866e45 100644 --- a/build/ludorum.min.js +++ b/build/ludorum.min.js @@ -1,4 +1,4 @@ -//! ludorum 0.1.3 +//! ludorum 0.1.4 !function(a,b){"use strict";"function"==typeof define&&define.amd?define(["creatartis-base"],b):"object"==typeof module&&module.exports?module.exports=b(require("creatartis-base")):a.ludorum=b(a.base)}(this,function a(b){"use strict";{var c=b.declare,d=b.objects.unimplemented,e=b.obj,f=b.copy,g=b.raise,h=b.raiseIf,i=b.Iterable,j=b.iterable,k=b.Future,l=b.Randomness,m=b.initialize,n=b.Statistics,o=b.Events,p={__name__:"ludorum",__init__:a,__dependencies__:[b]},q=p.utils={},r=p.Game=c({constructor:function(a){this.activePlayers=a?Array.isArray(a)?a:[a]:[this.players[0]]},name:"?",players:[],moves:d("Game","moves"),next:d("Game","next"),result:d("Game","result"),scores:function(){return this.results()},view:function(){return this},isActive:function(){for(var a=0;aa,"There is no active player."),h(a>1,"More than one player is active."),this.activePlayers[0]},opponents:function(a){return a=a||this.activePlayers,this.players.filter(function(b){return a.indexOf(b)<0})},opponent:function(a){var b=this.players.indexOf(a||this.activePlayer());return this.players[(b+1)%this.players.length]},perform:function(){for(var a,b={},c=0;c+a?this.ply()+ +a:+a,this.history[0|a]},result:function(){return this.state().result()},decisions:function(a){a=a||this.state();var b=this,c=this.players,d=a.activePlayers;return k.all(d.map(function(b){return c[b].decision(a.view(b),b)})).then(function(c){var e=j(d).zip(c).toObject();return b.onMove(a,e),e})},run:function(a){if(a=isNaN(a)?1/0:+a,1>a)return k.when(this);var b,c=this.ply(),d=this.state();if(1>c&&this.onBegin(d),d=this.__advanceAleatories__(d),b=d.result())return this.onEnd(d,b),k.when(this);var e=this;return this.decisions(d).then(function(b){return e.__advance__(d,b)?e.run(a-1):e})},__advanceAleatories__:function(a){for(var b;a instanceof y;a=b)b=a.next(),this.history.push(b),this.onNext(a,b);return a},__advance__:function(a,b){var c=this,d=a.activePlayers.filter(function(a){return b[a]instanceof v.CommandQuit});if(d.length>0)return c.onQuit(a,d[0]),!1;var e=a.next(b);return this.history.push(e),this.onNext(a,e),!0},"static CommandQuit":function(){},onBegin:function(a){this.events.emit("begin",a,this),this.logger&&this.logger.info("Match begins with ",j(this.players).map(function(a){return a[1]+" as "+a[0]}).join(", "),"; for ",a,".")},onMove:function(a,b){this.events.emit("move",a,b,this),this.logger&&this.logger.info("Players move: ",JSON.stringify(b)," in ",a)},onNext:function(a,b){this.events.emit("next",a,b,this),this.logger&&this.logger.info("Match advances from ",a," to ",b)},onEnd:function(a,b){this.events.emit("end",a,b,this),this.logger&&this.logger.info("Match for ",a,"ends with ",JSON.stringify(b))},onQuit:function(a,b){this.events.emit("quit",a,b,this),this.logger&&this.logger.info("Match for ",a," aborted because player "+b+" quitted.")},toString:function(){return"Match("+this.game+", "+JSON.stringify(this.players)+")"}}),w=p.Tournament=c({constructor:function(a,b){this.game=a,this.players=Array.isArray(b)?b:iterables.iterable(b).toArray(),this.statistics=new n,this.events=new o({events:["begin","beforeMatch","afterMatch","end"]})},__advance__:d("Tournament","__advance__"),run:function(){this.onBegin();var a=this;return k.doWhile(function(){return k.then(a.__advance__(),function(b){return b?(a.beforeMatch(b),a.__runMatch__(b).then(function(b){return a.account(b),a.afterMatch(b),b})):null})}).then(this.onEnd.bind(this))},__runMatch__:function(a){return a.run()},account:function(a){var b=this.game,c=a.result(),d=this.statistics;h(!c,"Match doesn't have results. Has it finished?"),j(a.players).forEach(function(e){var f=e[0],g=e[1],h=c[e[0]];d.add({key:"results",game:b.name,role:f,player:g.name},h),d.add({key:h>0?"victories":0>h?"defeats":"draws",game:b.name,role:f,player:g.name},h),d.add({key:"length",game:b.name,role:f,player:g.name},a.ply()),a.history.forEach(function(a){if("function"==typeof a.moves){var c=a.moves();c&&c.hasOwnProperty(f)&&c[f].length>0&&d.add({key:"width",game:b.name,role:f,player:g.name},c[f].length)}})})},onBegin:function(){this.events.emit("begin",this),this.logger&&this.logger.info("Tournament begins for game ",game.name,".")},beforeMatch:function(a){this.events.emit("beforeMatch",a,this),this.logger&&this.logger.debug("Beginning match with ",JSON.stringify(a.players),".")},afterMatch:function(a){this.events.emit("afterMatch",a,this),this.logger&&this.logger.debug("Finishing match with ",JSON.stringify(a.players),".")},onEnd:function(){this.events.emit("end",this.statistics,this),this.logger&&this.logger.info("Tournament ends for game ",game.name,":\n",this.statistics,"\n")}}),x=p.tournaments={},y=p.Aleatory=c({constructor:function(a,b){this.random=b||l.DEFAULT,"function"==typeof a&&(this.next=a)},value:function(){var a,b=this.random.random();if(j(this.distribution()).forEach(function(c){if(b-=c[1],0>=b)throw a=c[0],i.STOP_ITERATION}),"undefined"==typeof a)throw new Error("Random value could not be obtained.");return a},next:d("Aleatory","next"),distribution:d("Aleatory","distribution"),"static fromDistribution":function(a,b,c){var d=new y(b,c);return d.distribution=function(){return a},d},"static withDistribution":function(a){return c(y,{distribution:function(){return a}})},"static fromValues":function(a,b,c){a=j(a).toArray();var d=1/a.length,e=new y(b,c);return e.value=function(){return this.random.choice(a)},e.distribution=function(){return a.map(function(a){return[a,d]})},e},"static withValues":function(a){a=j(a).toArray();var b=1/a.length;return c(y,{value:function(){return this.random.choice(a)},distribution:function(){return a.map(function(a){return[a,b]})}})},"static fromRange":function(a,b,c,d){var e=new y(c,d);return e.value=function(){return this.random.randomInt(a,b+1)},e.distribution=function(){return i.range(a,b+1).map(function(c){return[c,1/(b+1-a)]})},e},"static withRange":function(a,b){return c(y,{value:function(){return this.random.randomInt(a,b+1)},distribution:function(){return i.range(a,b+1).map(function(c){return[c,1/(b+1-a)]})}})}}),z=p.aleatories={},A=q.Checkerboard=c({constructor:function(a,b){isNaN(a)||(this.height=0|a),isNaN(b)||(this.width=0|b)},emptySquare:null,isValidCoord:function(a){return Array.isArray(a)&&!isNaN(a[0])&&!isNaN(a[1])&&a[0]>=0&&a[0]=0&&a[1]=b}).map(function(a){return i.range(0,a.length-b+1).map(function(c){return a.slice(c,c+b)})}).flatten()},walk:function(a,b){var c=this;return new i(function(){var d=a.slice();return function(){if(c.isValidCoord(d)){var a=d.slice();return d[0]+=b[0],d[1]+=b[1],a}throw i.STOP_ITERATION}})},walks:function(a,b){var c=this;return b.map(function(b){return c.walk(a,b)})},"static DIRECTIONS":{HORIZONTAL:[[0,-1],[0,1]],VERTICAL:[[-1,0],[1,0]],ORTHOGONAL:[[0,-1],[0,1],[-1,0],[1,0]],DIAGONAL:[[-1,-1],[-1,1],[1,-1],[1,1]],EVERY:[[0,-1],[0,1],[-1,0],[1,0],[-1,-1],[-1,1],[1,-1],[1,1]]},clone:d("utils.Checkerboard","clone"),__place__:d("utils.Checkerboard","place"),place:function(a,b){return this.clone().__place__(a,b)},__move__:function(a,b,c){return this.__place__(b,this.square(a)).__place__(a,"undefined"==typeof c?this.emptySquare:c)},move:function(a,b,c){return this.clone().__move__(a,b,c)},__swap__:function(a,b){var c=this.square(b);return this.__place__(b,this.square(a)).__place__(a,c)},swap:function(a,b){return this.clone().__swap__(a,b)},renderAsHTMLTable:function(a,c,d){var e=this,f=a.createElement("table");return c.appendChild(f),e.horizontals().reverse().forEach(function(c){var g=a.createElement("tr");f.appendChild(g),c.forEach(function(c){var f=e.square(c),h=a.createElement("td"),i={id:"ludorum-square-"+c.join("-"),className:"ludorum-square",square:f,coord:c,innerHTML:b.Text.escapeXML(f)};d&&(i=d(i)||i),h["ludorum-data"]=i,h.id=i.id,h.className=i.className,h.innerHTML=i.innerHTML,i.onclick&&(h.onclick=i.onclick),g.appendChild(h)})}),f},weightedSum:function(a,b){var c=this;return this.coordinates().zip(a).mapApply(function(a,d){return b[c.square(a)]*d||0}).sum()}}),B=q.CheckerboardFromString=c(A,{constructor:function(a,b,c,d){if(A.call(this,a,b),d&&(this.emptySquare=(d+this.emptySquare).charAt(0)),c&&c.length!==a*b)throw new Error("Given string "+JSON.stringify(c)+" does not match board dimensions.");this.string=c||this.emptySquare.repeat(a*b)},emptySquare:".",toString:function(){var a=this.string,b=this.height,c=this.width;return i.range(b).map(function(d){return a.substr((b-d-1)*c,c)}).join("\n")},square:function(a,b){var c=a[0],d=a[1],e=this.width;return c>=0&&c=0&&e>d?this.string.charAt(c*e+d):b},asString:function(a){var b=this;return a.map(function(a){return b.square(a)}).join("")},asStrings:function(a){var b=this;return a.map(function(a){return b.asString(a)})},asRegExp:function(a,b,c){c=c||".";var d=this.width,e=i.repeat(!1,d*this.height).toArray();a.forEach(function(a){e[a[0]*d+a[1]]=!0});for(var f,g="",h=0,j=0;jh?f?b:c:(f?b:c)+"{"+h+"}"}return g},asRegExps:function(a,b,c){var d=this;return a.map(function(a){return d.asRegExp(a,b,c)}).join("|")},clone:function(){return new this.constructor(this.height,this.width,this.string,this.hasOwnProperty("emptySquare")?this.emptySquare:void 0)},__place__:function(a,b){h(!this.isValidCoord(a),"Invalid coordinate ",a,"."),b=(b+this.emptySquare).charAt(0);var c=a[0]*this.width+a[1];return this.string=this.string.substr(0,c)+b+this.string.substr(c+1),this}});q.CheckerboardFromPieces=c(A,{constructor:function(a,b,c,d){A.call(this,a,b),arguments.length>3&&(this.emptySquare=d),this.pieces=j(c||[]).map(function(a){var c=a.position;return[c[0]*b+c[1],a]}).toObject()},emptySquare:null,toString:function(){return"["+j(this.pieces).select(1).join(", ")+"]"},square:function(a,b){var c=a[0]*this.width+a[1];return this.pieces.hasOwnProperty(c)?this.pieces[c]:b},clone:function(){var a=[].concat(this.pieces);return this.hasOwnProperty("emptySquare")?new this.constructor(this.height,this.width,a,this.emptySquare):new this.constructor(this.height,this.width,a)},__place__:function(a,b){h(!this.isValidCoord(a),"Invalid coordinate ",a,"!");var c=a[0]*this.width+a[1];return delete this.pieces[c],b&&(this.pieces[c]=b),this}})}p.utils.Scanner=c({constructor:function(a){m(this,a).object("game",{ignore:!0}).integer("maxWidth",{defaultValue:1e3,coerce:!0}).integer("maxLength",{defaultValue:50,coerce:!0}).object("random",{defaultValue:l.DEFAULT}).object("statistics",{defaultValue:new n})},scan:function(a){var b=this,c=arguments.length<2?this.game?[this.game]:[]:Array.prototype.slice.call(arguments,1),d=0;return k.whileDo(function(){return c.length>0&&dg?(e.add({key:"defeat.result",game:b.name,role:f,player:h},g,b),e.add({key:"defeat.length",game:b.name,role:f,player:h},c,b)):g>0?(e.add({key:"victory.result",game:b.name,role:f,player:h},g,b),e.add({key:"victory.length",game:b.name,role:f,player:h},c,b)):e.add({key:"draw.length",game:b.name,role:f,player:h},c,b)}),!0;var f=b.moves();return j(b.activePlayers).forEach(function(a){e.add({key:"game.width",game:b.name,role:a},f[a].length)}),!1}}),q.Cache=c({constructor:function(a){this.clear(),a&&this.root(a)},stateIdentifier:function(a){return a.identifier()},moveIdentifier:function(a){return JSON.stringify(a)},has:function(a){var b="string"==typeof a?a:this.stateIdentifier(a);return this.__entries__.hasOwnProperty(b)},get:function(a){var b="string"==typeof a?a:this.stateIdentifier(a);return this.__entries__[b]},size:function(){return Object.keys(this.__entries__).length},entry:function(a,b){if(b=b||this.stateIdentifier(a),this.has(b))return this.get(b);var c={id:b,state:a,precursors:[],descendants:{}};return this.__entries__[b]=c,c},descendant:function(a,b){var c=this.moveIdentifier(b),d=a.descendants;if(d.hasOwnProperty(c))return d[c][1];var e=a.state.next(b),f=this.stateIdentifier(e),g=this.get(f)||this.entry(e,f);return d[c]=[b,g],g.precursors.push([b,a]),g},descendants:function(a){var b=this.descendant.bind(this,a);return arguments.length>1?Array.prototype.slice.call(arguments,1).map(b):a.state.possibleMoves().map(b)},clear:function(){this.__entries__={},this.__root__=null},root:function(a){if(arguments.length>0){var b=this.stateIdentifier(a);this.__root__=this.get(b)||this.entry(a,b),this.prune(b)}return this.__root__},prune:function(a){for(var b,c=[a||this.__root__.id],d={};a=c.shift();)d.hasOwnProperty(a)||(b=this.get(a),d[a]=b,c.push.apply(c,j(b.descendants).mapApply(function(a,b){return b[1][a]}).toArray()));return this.__entries__=d}});var C=c({constructor:function(a,b,c){this.parent=a,this.state=b,this.transition=c,this.children={}},childrenCount:function(){return Object.keys(this.children).length},__childSerialization__:function(a){return JSON.stringify(a)},expand:function(a){var b,c=this.__childSerialization__(a),d=this.children[c];if(!d){try{b=this.state.next(a)}catch(e){g("Node expansion for ",this.state," with ",JSON.stringify(a)," failed with: ",e)}d=new this.constructor(this,b,a),this.children[c]=d}return d},possibleTransitions:function(){return this.state instanceof y?this.state.distribution():this.state instanceof r?this.state.possibleMoves():void g("Cannot get possible transitions from ("+this.state+")! Is it a Game or Aleatory?")},expandAll:function(){var a=this;return this.possibleTransitions().map(function(b){return a.expand(b)})}});u.RandomPlayer=c(t,{constructor:function(a){t.call(this,a),m(this,a).object("random",{defaultValue:l.DEFAULT})},decision:function(a,b){return this.random.choice(this.movesFor(a,b))}}),u.TracePlayer=c(t,{constructor:function(a){t.call(this,a),this.trace=j(a.trace),this.__iter__=this.trace.__iter__(),this.__decision__=this.__iter__()},decision:function(){try{this.__decision__=this.__iter__()}catch(a){i.prototype.catchStop(a)}return this.__decision__},__serialize__:function(){return["TracePlayer",{name:this.name,trace:this.trace.toArray()}]}});var D=u.HeuristicPlayer=c(t,{constructor:function(a){t.call(this,a),m(this,a).object("random",{defaultValue:l.DEFAULT}).func("heuristic",{ignore:!0})},moveEvaluation:function(a,b,c){if(Object.keys(a).length<2)return this.stateEvaluation(b.next(a),c);var d=0,g=0;return a=f(e(c,[a[c]]),a),b.possibleMoves(a).forEach(function(a){d+=this.stateEvaluation(b.next(a),c),++g}),g>0?d/g:0},stateEvaluation:function(a,b){var c=a.result();return c?c[b]:this.heuristic(a,b)},heuristic:function(){return this.random.random(-.5,.5)},bestMoves:function(a){return j(a).greater(function(a){return a[1]}).map(function(a){return a[0]})},selectMoves:function(a,b,c){var d=this,e=!1,f=a.map(function(a){var f=d.moveEvaluation(a,b,c);return f instanceof k?(e=e||!0,f.then(function(b){return[a,b]})):[a,f]});return e?k.all(f).then(this.bestMoves):this.bestMoves(f)},decision:function(a,b){var c=this,d=a.moves();h(!d||!d.hasOwnProperty(b),"Player "+b+" is not active (moves= "+JSON.stringify(d)+")!");var g=d[b];if(h(!Array.isArray(g)||g.length<1,"Player "+b+" has no moves ("+g+")!"),1==g.length)return g[0];d=g.map(function(a){return f(e(b,a),d)});var i=c.selectMoves(d,a,b);return k.then(i,function(d){return h(!d||!d.length,"No moves where selected at ",a," for player ",b,"!"),c.random.choice(d)[b]})},"static composite":function(){var a=Array.prototype.slice.call(arguments);h(a.length<1,"HeuristicPlayer.composite() cannot take an odd number of arguments!");for(var b=0;b1,"HeuristicPlayer.composite() argument ",b+1," (",a[b+1],") is not a valid weight!");return function(b,c){for(var d=0,e=0;e+1=this.horizon?this.heuristics(a):null},maxN:function(a,b,c){var d=this.quiescence(a,b,c);if(!d){var f,g,h=a.activePlayer(),i=this.movesFor(a,h);if(d={},i.length<1)throw new Error("No moves for unfinished game "+a+".");for(var j=0;j(d[h]||-1/0)&&(d=f)}return d},toString:function(){return(this.constructor.name||"MaxNPlayer")+"("+JSON.stringify({name:this.name,horizon:this.horizon})+")"}}),u.MiniMaxPlayer=c(D,{constructor:function(a){D.call(this,a),m(this,a).integer("horizon",{defaultValue:4,coerce:!0})},stateEvaluation:function(a,b){return this.minimax(a,b,0)},quiescence:function(a,b,c){var d=a.result();return d?d[b]:c>=this.horizon?this.heuristic(a,b):0/0},minimax:function(a,b,c){var d=this.quiescence(a,b,c);if(isNaN(d)){var f,g,h=a.activePlayer(),i=this.movesFor(a,h);if(i.length<1)throw new Error("No moves for unfinished game "+a+".");h==b?(d=-1/0,f=Math.max):(d=+1/0,f=Math.min);for(var j=0;jd&&(d=g):f>g&&(f=g),!(d>=f));l++);return j?d:f}});var F=u.MonteCarloPlayer=c(D,{constructor:function(a){if(D.call(this,a),m(this,a).number("simulationCount",{defaultValue:30,coerce:!0}).number("timeCap",{defaultValue:1e3,coerce:!0}).number("horizon",{defaultValue:500,coerce:!0}),a)switch(typeof a.agent){case"function":this.agent=new D({heuristic:a.agent});break;case"object":this.agent=a.agent;break;default:this.agent=null}},selectMoves:function(a,b,c){for(var d=this,g=Date.now()+this.timeCap,i=b.next.bind(b),k=a.map(function(a){return{move:a,nexts:Object.keys(a).length<2?[b.next(a)]:b.possibleMoves(f(e(c,[a[c]]),a)).map(i),sum:0,count:0}}),l=0;l0})});return k=j(k).greater(function(a){return h(isNaN(a.sum),"State evaluation is NaN for move ",a.move,"!"),a.count>0?a.sum/a.count:0}).map(function(a){return a.move})},stateEvaluation:function(a,b){for(var c,d=0,e=this.simulationCount,f=0;e>f&&(c=this.simulation(a,b),d+=c.result[b],!(c.plies<1));++f);return e>0?d/e:0},simulation:function(a,b){var c,d,f,h=this;for(c=0;!0;++c)if(a instanceof y)a=a.next();else{if(f=a.moves(),!f)return{game:a,result:a.result(),plies:c};if(c>this.horizon)return{game:a,result:e(b,this.heuristic(a,b)),plies:c};d={},a.activePlayers.forEach(function(b){d[b]=h.agent?h.agent.decision(a,b):h.random.choice(f[b])}),a=a.next(d)}g("Simulation ended unexpectedly for player ",b," in game ",a,"!")},__serialize__:function(){return[this.constructor.name,{name:this.name,simulationCount:this.simulationCount,timeCap:this.timeCap,agent:this.agent}]}});u.UCTPlayer=c(F,{constructor:function(a){F.call(this,a),m(this,a).number("explorationConstant",{defaultValue:Math.sqrt(2),coerce:!0})},selectNode:function(a,b,c){return c=isNaN(c)?this.explorationConstant:+c,this.random.choice(j(a.children).select(1).greater(function(a){return a.uct.rewards/a.uct.visits+c*Math.sqrt(Math.log(b)/a.uct.visits)}))},selectMoves:function(a,b,c){var d,e,f=new C(null,b),g=Date.now()+this.timeCap;f.uct={pending:this.random.shuffle(f.possibleTransitions()),visits:0,rewards:0};for(var h=0;h0;)d=this.selectNode(d,h+1,this.explorationConstant);for(d.uct.pending.length>0&&(d=d.expand(d.uct.pending.pop()),d.uct={pending:this.random.shuffle(d.possibleTransitions()),visits:0,rewards:0}),e=this.simulation(d.state,c);d;d=d.parent)++d.uct.visits,d.uct.rewards+=(b.normalizedResult(e.result)[c]+1)/2}return a=j(f.children).select(1).greater(function(a){return a.uct.visits}).map(function(a){return a.transition})},__serialize__:function(){return[this.constructor.name,{name:this.name,simulationCount:this.simulationCount,timeCap:this.timeCap,explorationConstant:this.explorationConstant}]}});var G=u.UserInterfacePlayer=c(t,{constructor:function(a){t.call(this,a)},participate:function(a,b){return this.role=b,this},decision:function(){return this.__future__&&this.__future__.isPending()&&this.__future__.resolve(new v.CommandQuit),this.__future__=new k,this.__future__},perform:function(a){var b=this.__future__;return b&&(this.__future__=null,b.resolve(a)),!!b}}),H=u.UserInterface=c({constructor:function(a){this.onBegin=this.onBegin.bind(this),this.onNext=this.onNext.bind(this),this.onEnd=this.onEnd.bind(this),a.match&&this.show(a.match)},show:function(a){this.match&&(a.events.off("begin",this.onBegin),a.events.off("next",this.onNext),a.events.off("end",this.onEnd)),this.match=a,a.events.on("begin",this.onBegin),a.events.on("next",this.onNext),a.events.on("end",this.onEnd)},onBegin:function(a){this.display(a)},onNext:function(a,b){this.display(b)},onEnd:function(a,b){this.results=b,this.display(a)},display:d("UserInterface","display"),perform:function(a,b){j(this.match.players).forEach(function(c){var d=(c[0],c[1]);d instanceof G&&(!b||d.role===b)&&d.perform(a)})}});H.BasicHTMLInterface=c(H,{constructor:function(a){H.call(this,a),this.document=a.document||b.global.document,this.container=a.container,"string"==typeof this.container&&(this.container=this.document.getElementById(this.container))},display:function(a){for(var b,c=this.container;b=c.firstChild;)c.removeChild(b);a.display(this)},build:function(a,b){var c=this;return b.forEach(function(b){var d;if(Array.isArray(b)){if(d=c.document.createElement(b[0]),b.length>2&&b[1]){var e=b[1];for(var f in e)attr.hasOwnProperty(f)&&d.setAttribute(f,e[f])}b.length>1&&b[b.length-1]&&c.build(d,b[b.length-1])}else"string"==typeof b&&(d=c.document.createTextNode(b));d&&a&&a.appendChild(d)}),a}});u.WebWorkerPlayer=c(t,{constructor:function(a){t.call(this,a),m(this,a).object("worker"),this.worker.onmessage=b.Parallel.prototype.__onmessage__.bind(this)},"static createWorker":function(a,c){h("string function".indexOf(typeof a)<0,"Invalid player builder: "+a+"!");var d=new b.Parallel;return d.run("self.ludorum = ("+p.__init__+')(self.base), "OK"').then(function(){return"function"==typeof c?d.run("("+c+')(), "OK"'):void 0}).then(function(){return d.run("self.PLAYER = ("+a+').call(self), "OK"')}).then(function(){return d.worker})},"static create":function(a){var b=this;return b.createWorker(a.playerBuilder,a.workerSetup).then(function(a){return new b({name:name,worker:a})})},decision:function(a,b){return this.__future__&&this.__future__.isPending()&&this.__future__.resolve(v.commandQuit),this.__future__=new k,this.worker.postMessage("PLAYER.decision(ludorum.Game.fromJSON("+a.toJSON()+"), "+JSON.stringify(b)+")"),this.__future__}});return z.dice={D4:y.withRange(1,4),D6:y.withRange(1,6),D8:y.withRange(1,8),D10:y.withRange(1,10),D12:y.withRange(1,12),D20:y.withRange(1,20),D100:y.withRange(1,100)},s.Predefined=c(r,{constructor:function(a,b,c,d){b&&(this.__results__=b,this.players=Object.keys(b)),r.call(this,a),this.height=isNaN(c)?5:+c,this.width=isNaN(d)?5:+d},name:"Predefined",players:["A","B"],__results__:{A:0,B:0},moves:function(){return this.height>0?e(this.activePlayer(),i.range(1,this.width+1).toArray()):void 0},result:function(){return this.height>0?null:this.__results__},next:function(){return new this.constructor(this.opponent(),this.__results__,this.height-1,this.width)},__serialize__:function(){return[this.name,this.activePlayer(),this.results,this.height,this.width]}}),s.Choose2Win=c(r,{constructor:function(a,b,c){r.call(this,b),this.__turns__=isNaN(a)?1/0:+a,this.__winner__=c},name:"Choose2Win",players:["This","That"],moves:function(){return!this.__winner__&&this.__turns__>0?e(this.activePlayer(),["win","lose","pass"]):void 0},result:function(){return this.__winner__?this.victory(this.__winner__):this.__turns__<1?this.draw():null},next:function(a){var b=this.activePlayer(),c=this.opponent(b);switch(h(!a.hasOwnProperty(b),"No move for active player ",b," at ",this,"!"),a[b]){case"win":return new this.constructor(this.__turns__-1,c,b);case"lose":return new this.constructor(this.__turns__-1,c,c);case"pass":return new this.constructor(this.__turns__-1,c);default:g("Invalid move ",a[b]," for player ",b," at ",this,"!")}},__serialize__:function(){return[this.name,this.__turns__,this.activePlayer(),this.__winner__]}}),s.ConnectionGame=c(r,{height:9,width:9,lineLength:5,constructor:function(a,b){r.call(this,a),this.board=b instanceof B?b:new B(this.height,this.width,(b||".".repeat(this.height*this.width))+"")},name:"ConnectionGame",players:["First","Second"],__lines__:function(){function a(a,c,d){var e=a+"x"+c+"/"+d;if(!b.hasOwnProperty(e)){var f=new B(a,c,".".repeat(a*c));b[e]=f.lines().map(function(a){return a.toArray()},function(a){return a.length>=d}).toArray()}return b[e]}var b={};return a.CACHE=b,a}(),result:function(){if(this.hasOwnProperty("__result__"))return this.__result__;for(var a=this.lineLength,b=this.board.asStrings(this.__lines__(this.height,this.width,a)).join(" "),c=0;c=0)return this.__result__=this.victory([this.players[c]]);return this.__result__=b.indexOf(".")<0?this.draw():null},moves:function(){return this.hasOwnProperty("__moves__")?this.__moves__:this.__moves__=this.result()?null:e(this.activePlayer(),j(this.board.string).filter(function(a){return"."===a},function(a,b){return b }).toArray())},next:function(a){var b=this.activePlayer(),c=this.players.indexOf(b),d=+a[b],e=d/this.width>>0,f=d%this.width;return new this.constructor((c+1)%this.players.length,this.board.place([e,f],c.toString(36)))},display:function(a){h(!(a&&a instanceof H.BasicHTMLInterface),"Unsupported UI!");var b=this.moves(),c=this.activePlayer(),d=this.board;b=b&&b[c];this.board.renderAsHTMLTable(a.document,a.container,function(e){e.className="."===e.square?"ludorum-empty":"ludorum-player"+e.square,e.innerHTML="."===e.square?" ":"●";var f=e.coord[0]*d.height+e.coord[1];b&&b.indexOf(f)>=0&&(e.move=f,e.activePlayer=c,e.onclick=a.perform.bind(a,e.move,c))});return a},__serialize__:function(){return[this.name,this.activePlayer(),this.board.string]}}),s.OddsAndEvens=c(r,{constructor:function(a,b){r.call(this,this.players),this.turns=isNaN(a)?1:+a,this.points=b||{Evens:0,Odds:0}},name:"OddsAndEvens",players:["Evens","Odds"],moves:function(){return this.turns<1?null:{Evens:[1,2],Odds:[1,2]}},result:function(){var a=this.points.Evens-this.points.Odds;return this.turns>0?null:{Evens:+a,Odds:-a}},next:function(a){h("number"!=typeof a.Evens||"number"!=typeof a.Odds,"Invalid moves "+(JSON.stringify(a)||a)+"!");var b=(a.Evens+a.Odds)%2===0;return new this.constructor(this.turns-1,{Evens:this.points.Evens+(b?1:0),Odds:this.points.Odds+(b?0:1)})},__serialize__:function(){return[this.name,this.turns,this.points]}}),s.TicTacToe=c(r,{name:"TicTacToe",constructor:function(a,b){r.call(this,a),this.board=b||"_________"},players:["Xs","Os"],result:function(){return function(){return this.board.match(this.WIN_X)?this.victory(["Xs"]):this.board.match(this.WIN_O)?this.victory(["Os"]):this.board.indexOf("_")<0?this.draw():null}}(),moves:function(){if(this.result())return null;var a={};return a[this.activePlayer()]=j(this.board).filter(function(a){return"_"===a},function(a,b){return b}).toArray(),a},next:function(a){var b=this.activePlayer(),c=+a[b];if(isNaN(c)||"_"!==this.board.charAt(c))throw new Error("Invalid move "+JSON.stringify(a)+" for board "+this.board+" (moves= "+JSON.stringify(a)+").");var d=this.board.substring(0,c)+b.charAt(0)+this.board.substring(c+1);return new this.constructor(this.opponent(b),d)},__serialize__:function(){return[this.name,this.activePlayer(),this.board]},printBoard:function(){var a=this.board;return[a.substr(0,3).split("").join("|"),"-+-+-",a.substr(3,3).split("").join("|"),"-+-+-",a.substr(6,3).split("").join("|")].join("\n")},display:function(a){h(!(a&&a instanceof H.BasicHTMLInterface),"Unsupported UI!");var b=this.activePlayer(),c=this.moves(),d=(this.board,{X:"ludorum-square-Xs",O:"ludorum-square-Os",_:"ludorum-square-empty"}),e={X:"X",O:"O",_:" "};return c=c&&c[b]&&c[b].length>0,new B(3,3,this.board,"_").renderAsHTMLTable(a.document,a.container,function(f){f.className=d[f.square],f.innerHTML=e[f.square],c&&"_"===f.square&&(f.move=3*f.coord[0]+f.coord[1],f.activePlayer=b,f.onclick=a.perform.bind(a,f.move,b))}),a},"static heuristics":{heuristicFromWeights:function(a){function b(b,d){var e=d.charAt(0);return j(b.board).map(function(b,c){return"_"===b?0:a[c]*(b===e?1:-1)}).sum()/c}var c=j(a).map(Math.abs).sum();return b.weights=a,b}},"":function(){var a=new B(3,3,"_".repeat(9)),b=a.sublines(a.lines(),3);this.prototype.WIN_X=new RegExp(a.asRegExps(b,"X",".")),this.prototype.WIN_O=new RegExp(a.asRegExps(b,"O",".")),this.heuristics.defaultHeuristic=this.heuristics.heuristicFromWeights([2,1,2,1,5,1,2,1,2])}}),s.ToadsAndFrogs=c(r,{constructor:function I(a,b){r.call(this,a),this.board=b||I.board()},"static board":function(a,b){return a=isNaN(a)?3:+a,b=isNaN(b)?2:+b,"T".repeat(a)+"_".repeat(b)+"F".repeat(a)},name:"ToadsAndFrogs",players:["Toads","Frogs"],result:function(){return this.moves()?null:this.defeat()},moves:function(){var a=this.activePlayer(),b={},c=b[a]=[];return this.board.replace(a==this.players[0]?/TF?_/g:/_T?F/g,function(a,b){return c.push(b),a}),c.length>0?b:null},next:function(a){var b=this.activePlayer(),c=a[b],d=(b.charAt(0),this.board);if("T_"==d.substr(c,2))d=d.substring(0,c)+"_T"+d.substring(c+2);else if("_F"==d.substr(c,2))d=d.substring(0,c)+"F_"+d.substring(c+2);else if("TF_"==d.substr(c,3))d=d.substring(0,c)+"_FT"+d.substring(c+3);else{if("_TF"!=d.substr(c,3))throw new Error("Invalid move ",c," for board <",d,">.");d=d.substring(0,c)+"FT_"+d.substring(c+3)}return new this.constructor(this.opponent(),d)},__serialize__:function(){return[this.name,this.activePlayer,this.board]}}),s.Pig=c(r,{constructor:function(a,b,c,d){r.call(this,a),this.goal=isNaN(b)?100:+b,this.__scores__=c||j(this.players).zip([0,0]).toObject(),this.__rolls__=d||[]},name:"Pig",players:["One","Two"],moves:function(){if(!this.result()){var a=this.activePlayer(),b=this.__scores__[a]+j(this.__rolls__).sum();return e(a,this.__rolls__.length<1?["roll"]:b>=this.goal?["hold"]:["roll","hold"])}},result:function(){var a=this.__scores__[this.players[0]],b=this.__scores__[this.players[1]];if(a>=this.goal||b>=this.goal){var c={};return c[this.players[0]]=Math.min(this.goal,a)-Math.min(this.goal,b),c[this.players[1]]=-c[this.players[0]],c}},next:function(a){var b=this.activePlayer(),c=a[b];if(h("undefined"==typeof c,"No move for active player ",b," at ",this,"!"),"hold"===c){var d=f(this.__scores__);return d[b]+=j(this.__rolls__).sum(),new this.constructor(this.opponent(),this.goal,d,[])}if("roll"===c){var e=this;return new z.dice.D6(function(a){return a=isNaN(a)?this.value():+a,a>1?new e.constructor(b,e.goal,e.__scores__,e.__rolls__.concat(a)):new e.constructor(e.opponent(),e.goal,e.__scores__,[])})}g("Invalid moves ",JSON.stringify(a)," at ",this,"!")},resultBounds:function(){return[-this.goal,+this.goal]},__serialize__:function(){return[this.name,this.activePlayer(),this.goal,this.__scores__,this.__rolls__]}}),s.Mutropas=c(r,{name:"Mutropas",players:["Left","Right"],constructor:function(a){r.call(this,this.players),a=a||{},this.random=a.random||l.DEFAULT,this.playedPieces=a.playedPieces||[],this.pieces=a.pieces||this.dealPieces(),this.__scores__=a.scores||e(this.players[0],0,this.players[1],0)},allPieces:i.range(9).toArray(),dealPieces:function(a){a=a||this.random;var b=this.allPieces.length/2|0,c=a.split(b,this.allPieces),d=a.split(b,c[1]);return e(this.players[0],c[0],this.players[1],d[0])},moves:function(){return this.result()?null:f({},this.pieces)},moveResult:function(a,b){var c=j(this.allPieces).max(0)+1;return b>a?c/2>=b-a?1:-1:a>b?a-b>=c/2+1?1:-1:0},next:function(a){var b=this.players[0],c=this.players[1],d=a[b],f=a[c],g=this.pieces;h(g[b].indexOf(d)<0,"Invalid move ",JSON.stringify(d)," for player ",b,"! (moves= ",JSON.stringify(a),")"),h(g[c].indexOf(f)<0,"Invalid move ",JSON.stringify(f)," for player ",c,"! (moves= ",JSON.stringify(a),")");var i=this.moveResult(d,f);return new this.constructor({random:this.random,playedPieces:this.playedPieces.concat([d,f]),pieces:e(b,g[b].filter(function(a){return a!==d}),c,g[c].filter(function(a){return a!==f})),scores:e(b,this.__scores__[b]+i,c,this.__scores__[c]-i)})},scores:function(){return f({},this.__scores__)},result:function(){var a=this.players;if(this.playedPieces.length>=this.allPieces.length-1){var b=this.scores();return this.zerosumResult(b[a[0]]-b[a[1]],a[0])}return null},__possiblePieces__:function(a){var b=this.playedPieces,c=this.pieces[this.opponent(a)],d=j(this.allPieces).filter(function(a){return b.indexOf(a)<0&&c.indexOf(a)<0});return d.combinations(d.count()-1)},view:function(a){var b=this,c=this.opponent(a),d=this.random;return y.withValues(this.__possiblePieces__(c),d,function(f){return f=f||this.value(),new b.constructor({random:d,playedPieces:b.playedPieces,scores:b.scores(),pieces:e(a,b.pieces[a],c,f)})})},__serialize__:function(){return[this.name,{pieces:this.__pieces__,scores:this.__scores__}]}}),s.Bahab=c(r,{name:"Bahab",players:["Uppercase","Lowercase"],constructor:function(a,b){r.call(this,a),this.board=b instanceof B?b:new B(5,5,b||this.initialBoard)},initialBoard:["BBABB","BBBBB",".....","bbbbb","bbabb"].join(""),__PLAYER_ENDGAME_RE__:{Uppercase:/^[.Bab]+$|^.{0,4}[a]/,Lowercase:/^[.bAB]+$|[A].{0,4}$/},result:function(){for(var a,b=this.board.string,c=0;2>c;++c)if(a=this.players[c],b.match(this.__PLAYER_ENDGAME_RE__[a]))return this.defeat(a);return this.moves()?null:this.defeat(this.activePlayer())},__PLAYER_PIECES_RE__:{Uppercase:/[AB]/g,Lowercase:/[ab]/g},moves:function(){var a=this.activePlayer(),b=this.__PLAYER_PIECES_RE__[a],c=this.board,d=[];return c.string.replace(b,function(a,e){var f,g=[e/5|0,e%5];switch(a){case"A":f=[[1,-1],[-1,0],[1,1]];break;case"B":f=[[1,-1],[1,1]];break;case"a":f=[[-1,-1],[1,0],[-1,1]];break;case"b":f=[[-1,-1],[-1,1]]}return j(f).forEachApply(function(e,f){var h=[g[0]+e,g[1]+f],i=c.square(h);!c.isValidCoord(h)||i.match(b)||"."!=i&&a.toLowerCase()!=i.toLowerCase()||d.push([g,h])}),a}),d.length>0?e(a,d):null},next:function(a){if(!a)throw new Error("Invalid moves "+a+"!");var b=this.activePlayer(),c=a[b];if(!Array.isArray(a[b]))throw new Error("Invalid moves "+JSON.stringify(a)+"!");return new this.constructor(this.opponent(),this.board.move(c[0],c[1]))},display:function(a){return h(!(a&&a instanceof H.BasicHTMLInterface),"Unsupported UI!"),this.__displayHTML__(a)},__displayHTML__:function(a){var b=this,c=this.moves(),d=this.activePlayer(),e=this.board,f={A:"ludorum-square-Uppercase-A",B:"ludorum-square-Uppercase-B",a:"ludorum-square-Lowercase-A",b:"ludorum-square-Lowercase-B",".":"ludorum-square-empty"},g=c?j(c[d]).groupAll(function(a){return JSON.stringify(a[0])}):{},h=a.selectedPiece&&g[JSON.stringify(a.selectedPiece)].map(function(a){return JSON.stringify(a[1])});return e.renderAsHTMLTable(a.document,a.container,function(c){c.className=f[c.square],c.innerHTML="."==c.square?" ":c.square,a.selectedPiece&&h&&h.indexOf(JSON.stringify(c.coord))>=0&&(c.className="ludorum-square-"+d+"-move",c.onclick=function(){var b=a.selectedPiece;a.selectedPiece=void 0,a.perform([b,c.coord],d)}),g.hasOwnProperty(JSON.stringify(c.coord))&&(c.onclick=function(){a.selectedPiece=c.coord,a.display(b)})}),a},__serialize__:function(){return[this.name,this.activePlayer(),this.board.string]}}),x.RoundRobin=c(w,{constructor:function(a,b,c){w.call(this,a,b),this.matchCount=isNaN(c)?a.players.length:+c,this.__advance__=this.__matches__().chain(i.repeat(null)).__iter__()},__matches__:function(){var a=this.game,b=j(this.players);return b=b.product.apply(b,i.repeat(this.players,a.players.length-1).toArray()),b.filter(function(a){for(var b=1;bc;c++)if(a[b]===a[c])return!1;return!0}).product(i.range(this.matchCount)).map(function(b){return new v(a,b[0])})}}),x.Measurement=c(w,{constructor:function(a,b,c,d){w.call(this,a,Array.isArray(b)?b:[b]),this.opponents=Array.isArray(c)?c:[c],h(this.opponents.length2?c.product.apply(c,i.repeat(this.opponents,b-2).toArray()):c.map(function(a){return[a]}),j(this.players).product(i.range(b),c,i.range(this.matchCount)).map(function(b){var c=b[2].slice(0);return c.splice(b[1],0,b[0]),new v(a,c)})}}),x.Elimination=c(w,{constructor:function(a,b,c){w.call(this,a,b),this.matchCount=isNaN(c)?1:+c>>0},__bracket__:function(a){var b=this.game,c=this.matchCount,d=this.game.players.length;return a=a||this.players,a.length