UNPKG

jocly

Version:

Board games 3D/2D UI and AI web library

1 lines 12.6 kB
"use strict";var JoclyUCT={};"undefined"==typeof WorkerGlobalScope&&"undefined"==typeof SystemJS?(module.exports.JoclyUCT=JoclyUCT,function(){var e=require,o=e("./jocly.util.js");global.MersenneTwister=o.MersenneTwister,global.JocUtil=o.JocUtil}()):(void 0).JoclyUCT=JoclyUCT,function(){function e(e,o){this.visits=1,this.children=null,this.who=o,this.parents=[],e&&this.parents.push(e),this.known=!1,this.evaluation=0,this.staticEvalSum=0,this.staticEvalCount=0,this.depth=e?e.depth+1:0}e.prototype={addParent:function(e){this.parents.push(e),e.depth+1<this.depth&&(this.depth=e.depth+1)}};var o={"-1":-1,1:1,2:0,0:0};JoclyUCT.startMachine=function(t,n){function i(e){var o=D,t=!1,n=0,i=1;if(0==e)return 0;for(e<0&&(e=-e,o=A,t=!0);;){if(e>o.v)n+=i,o.m||(o.m={v:e,l:null,m:null}),o=o.m;else{if(!(e<o.v))break;n-=i,o.l||(o.l={v:e,l:null,m:null}),o=o.l}i/=2}return t&&(n=-n),n}function a(e,o){for(var t=void 0,n=0;n<o.length;n++){var i=o[n];(void 0===t||i.evaluation*i.who>t*i.who)&&(t=i.evaluation)}return t}function l(e,o){for(var t=void 0,n=0;n<o.length;n++){var i=o[n];(void 0===t||i.evaluation*i.who<t*i.who)&&(t=i.evaluation)}return-t}function r(e,o){for(var t=1,n=0;n<o.length;n++){var i=o[n];value1=(i.evaluation+1)/2,1==e.who?t*=1-i.evaluation:t*=i.evaluation}return 1==e.who?2*(1-t)-1:2*t-1}function s(e,o,n){if(!(t.mOptions.uctTransposition&&!t.mOptions.uctIgnoreLoop&&e.sign in n)){var i=[];if(T.ignoreLeaf){for(var s=!1,h=0;h<e.children.length;h++)if(e.children[h].n.children){s=!0;break}if(s)for(var h=0;h<e.children.length;h++){var c=e.children[h].n;(c.known||c.children)&&i.push(c)}}if(0==i.length)for(var h=0;h<e.children.length;h++)i.push(e.children[h].n);var p;switch(T.propagation){case"maximin":p=l(e,i);break;case"minimax2min-avg":var d=l(e,i),m=a(e,i);p=(d+m)/2;break;case"minimax2min-best":var d=l(e,i),m=a(e,i);p=1==e.who?Math.max(d,m):Math.min(d,m);break;case"product":p=GetPropabilityProductEval(e,i);break;case"minimax":case"mixed":default:p=a(e,i),"mixed"==T.propagation&&T.productRatio>0&&(m=r(e,i),p=T.productRatio*m+(1-T.productRatio)*p),T.useDepthWeights&&(p=f(p,e.depth+1))}T.uncertaintyFactor&&(p*=1-Math.pow(10,-T.uncertaintyFactor)*Math.log(e.depth+1)),e.evaluation!==p?(e.evaluation=p,T.directVisits||(e.visits+=o),v(e,o,n)):T.directVisits||u(e,o,n)}}function v(e,o,n){if(0!=e.parents.length){t.mOptions.uctTransposition&&!t.mOptions.uctIgnoreLoop&&(n||(n={}),n[e.sign]=!0);for(var i=0;i<e.parents.length;i++){var a=e.parents[i];T.useAlphaBeta&&a.children.sort(function(o,t){return(t.evaluation-o.evaluation)*e.who}),s(a,o,n)}}}function u(e,o,n){if(0!=e.parents.length){t.mOptions.uctTransposition&&!t.mOptions.uctIgnoreLoop&&(n||(n={}),n[e.sign]=!0);for(var i=0;i<e.parents.length;i++){var a=e.parents[i];a.sign in n||(a.visits+=o,n[a.sign]=!0,u(a,o,n))}}}function h(e,o){if(!(t.mOptions.uctTransposition&&!t.mOptions.uctIgnoreLoop&&e.sign in o)){for(var n=!0,i=0;i<e.children.length;i++){if(0==e.children[i].n.known){n=!1;break}}1==n&&(e.known=!0,c(e,o))}}function c(e,o){if(0!=e.known&&0!=e.parents.length){t.mOptions.uctTransposition&&!t.mOptions.uctIgnoreLoop&&(o||(o={}),o[e.sign]=!0);for(var n=0;n<e.parents.length;n++)h(e.parents[n],o)}}function p(e){for(var o=1,t=0,n=0;n<32;n++){var i=e>>>n&1;t=o?t<<1|i:t<<1|1-i,o=i}return t}function d(){function n(e,n,a){for(var l=null,r=Date.now(),s=0;s<T.playoutDepth||n.mWho==T.playoutCeil*O.who;s++){if(n.mMoves&&0!=n.mMoves.length||n.GenerateMoves(t),n.mFinished){l={finished:!0,winner:o[n.mWinner]};break}for(var v=[],u=0;u<n.mMoves.length;u++){var h=new(t.GetBoardClass())(t);h.CopyFrom(n),h.ApplyMove(t,n.mMoves[u]),t.AddVisit(h),h.mMoves=[],h.Evaluate(t);var c=h.mEvaluation;h.mFinished?c=1==h.mWinner?Number.MAX_VALUE:-1==h.mWinner?-Number.MAX_VALUE:0:isNaN(h.mEvaluation)&&console.error("Evaluation in not a number !"),v.push({move:n.mMoves[u],evaluation:c,board:h}),t.RemoveVisit(h)}v.sort(function(e,o){var t=e.evaluation*n.mWho;return o.evaluation*n.mWho-t});for(var p,d=v.length,f=1/T.playoutSpread,g=(1-Math.pow(f,d+1))/(1-f)-1,w=Math.random()*g,M=void 0,x=0,b=!1,u=0;u<d;u++){var y=v[u],C=y.evaluation;if(C!==M){if(b)break;p=[y],M=C}else p.push(y);x+=Math.pow(f,u+1),x>=w&&(b=!0)}var V=p[Math.floor(Math.random()*p.length)];if(n=V.board,t.AddVisit(n),a.push(n.GetSignature()),n.mWho=-n.mWho,n.mFinished){l={finished:!0,winner:o[n.mWinner]};break}}null===l&&(l={finished:!1,eval:n.mEvaluation}),k+=Date.now()-r,E++;var B;if(l.finished)B=l.winner;else{T.debugRawEval&&(e.rawEval=l.eval);var D=i(l.eval);m(D,e.depth),B=D}return B}x++;var a=new(t.GetBoardClass())(t);a.CopyFrom(t.mBoard);for(var l,r,s,u,h,r,d,f,g,w,D,A=0,L=O,S=0,W=0,N=[],R=[],U=x,G={},F=-2,J=2;;){if(0===(D=function(){if(R.push(L),S>W&&(W=S),null===L.children)return 0;if(T.useAlphaBeta){for(l=[],r=0;r<L.children.length;r++)if(s=L.children[r],u=s.n,l.push(s),1==u.who&&u.evaluation>F&&(F=u.evaluation),-1==u.who&&u.evaluation<J&&(J=u.evaluation),J<F){C+=L.children.length-1-r;break}}else l=L.children;if(t.mOptions.uctTransposition&&!t.mOptions.uctIgnoreLoop){for(h=[],r=0;r<l.length;r++)l[r].n.sign in G||h.push(l[r]);l=h}if(d=[],g=T.directVisits?Math.log(U):Math.log(L.visits),T.distributeEval?function(){for(var e={},o=[],t=0;t<l.length;t++){var n=l[t],i=n.n;if(!i.known){var a=(i.evaluation*i.who+1)/2;void 0===e[a]&&(e[a]=[],o.push(a)),e[a].push(n)}}o.sort(function(e,o){return e-o});for(var r=1/(o.length+1),s=0,v=0;v<o.length;v++){s++;for(var u=o[v],h=e[u],c=r*s,t=0;t<h.length;t++){var p,n=h[t],i=n.n;p=T.directVisits?c+T.c*Math.sqrt(g/n.f):c+T.c*Math.sqrt(g/i.visits),(0==d.length||p>=f)&&(d.length>0&&p>f&&(d=[]),f=p,d.push(n))}}}():function(){for(var e=0;e<l.length;e++){var o=l[e],t=o.n;if(!t.known){var n,i=(t.evaluation*t.who+1)/2;n=T.directVisits?i+T.c*Math.sqrt(g/o.f):i+T.c*Math.sqrt(g/t.visits),(0==d.length||n>=f)&&(d.length>0&&n>f&&(d=[]),f=n,d.push(o))}}}(),0==d.length)return{v:void 0};w=d[Math.floor(Math.random()*d.length)],T.directVisits&&(w.f++,U=w.f),L=w.n,t.mOptions.uctTransposition&&!t.mOptions.uctIgnoreLoop&&(G[L.sign]=1),S++,N.push(w.m),a.ApplyMove(t,w.m),t.AddVisit(a),a.mMoves=[],M.push(a.GetSignature()),"states"==t.mOptions.uctTransposition&&(A^=p(a.GetSignature())),a.mWho=-a.mWho}()))break;if(D)return D.v}if(L==O||L.visits>=T.minVisitsExpand)if(a.mMoves&&0!=a.mMoves.length||a.GenerateMoves(t),a.mFinished)L.known=!0,L.evaluation=o[a.mWinner],c(L);else{L.children=[];for(var I=void 0,P=!0,r=0;r<a.mMoves.length;r++){var q=a.mMoves[r],X=[],_=new(t.GetBoardClass())(t);_.CopyFrom(a),_.ApplyMove(t,q),t.AddVisit(_),_.mMoves=[],_.mWho=-_.mWho,S>V&&(V=S);var j=_.GetSignature();X.push(j);var z;"states"==t.mOptions.uctTransposition?(z=A^j,z^=S):"state"==t.mOptions.uctTransposition&&(z=j);var u=null;t.mOptions.uctTransposition&&(u=B[z]),u?(y++,u.addParent(L)):(u=new e(L,-L.who),b++,t.mOptions.uctTransposition&&(B[z]=u,u.sign=z),_.Evaluate(t),_.mFinished?(u.known=!0,u.evaluation=o[_.mWinner]):(isNaN(_.mEvaluation)&&console.error("Evaluation in not a number !",_.mEvaluation),u.evaluation=n(u,_,X)),u.staticEvalSum=u.evaluation,u.staticEvalCount=1),0==u.known&&(P=!1);var H={n:u,m:new(t.GetMoveClass())(q).Strip()};T.directVisits&&(H.f=1),L.children.push(H);var K=u.evaluation*u.who;(void 0===I||K>I*u.who)&&(I=u.evaluation);for(var Q=0;Q<X.length;Q++)t.RemoveVisit(null,X[Q])}if(L.evaluation=I,v(L,T.propagateMultiVisits?a.mMoves.length:1),T.directVisits)for(var r=0;r<R.length;r++)R[r].visits+=T.propagateMultiVisits?a.mMoves.length:1;P&&(L.known=!0,c(L))}else if(!L.known){var Y=n(L,a,M);if(L.staticEvalSum+=Y,L.staticEvalCount++,L.evaluation=L.staticEvalSum/L.staticEvalCount,v(L,1),T.directVisits)for(var r=0;r<R.length;r++)R[r].visits++}}function m(e,o){for(;L.length<=o;)L.push({count:0,sum:0});var t=L[o];t.sum+=e,t.count++}function f(e,o){var t=L[o];if(void 0===t){for(;L.length<o;)L.push({count:0,sum:0});return L.push({count:1,sum:e}),e}var n=t.count>0?t.sum/t.count:0;return e>n?e=(e-n)/(1-n):e<n&&(e=-(n-e)/(n+1)),e}function g(){if(t.mAborted)return void t.mAbortCallback.call(t);var e=Date.now(),o=0;if(T.maxDuration>0&&(o=Math.round(100*(e-S)/(1e3*T.maxDuration))),T.maxLoops>0&&(o=Math.max(o,100*x/T.maxLoops)),T.maxNodes>0&&(o=Math.max(o,100*b/T.maxNodes)),o=Math.min(100,o),o!=W&&(W=o,t.mProgressCallback&&t.mProgressCallback(o)),!O.children||0==O.known&&(T.maxDuration<=0||e<1e3*T.maxDuration+S)&&(T.maxLoops<=0||x<T.maxLoops)&&(T.maxNodes<=0||b<T.maxNodes)){do{M=[];try{d()}catch(e){console.error("UCT step",e)}for(var n=0;n<M.length;n++)t.RemoveVisit(null,M[n])}while(Date.now()-100<e);setTimeout(g,0)}else{T.log&&w(O);var i=void 0;if(t.mBestMoves=[],"maxvisits"==T.pickMove&&T.directVisits){for(var n=0;n<O.children.length;n++){var a=O.children[n],l=a.n;l.evaluation==l.who&&t.mBestMoves.push(a.m)}if(0==t.mBestMoves.length)for(var n=0;n<O.children.length;n++){var a=O.children[n];(void 0===i||i<=a.f)&&((void 0===i||i<a.f)&&(t.mBestMoves=[]),i=a.f,t.mBestMoves.push(a.m))}}else{var r=void 0,s=[];"besteval-multivisits"==T.pickMove&&O.children.forEach(function(e){(e.n.visits>1||1==e.n.known)&&s.push(e)}),0==s.length&&(s=O.children);for(var n=0;n<s.length;n++){var a=s[n],l=a.n,v=l.staticEvalSum/l.staticEvalCount;(void 0===i||i>=l.evaluation*O.who)&&((void 0===i||i>l.evaluation*O.who||i==l.evaluation*O.who&&(void 0===r||r>O.who*v))&&(r=v,t.mBestMoves=[]),i=l.evaluation*O.who,t.mBestMoves.push(a.m))}}JocUtil.schedule(t,"Done",{})}}function w(e){function o(e,n){if("minimax"!=T.propagation&&("mixed"!=T.propagation||T.productRatio>0))return void console.warn("Cannot display minimax tree on propagation",T.propagation,"pp ratio",T.productRatio);for(var i="",a=0;a<n;a++)i+=" ";console.log(i+"*",n,"*",-e.who,"eval",e.evaluation);for(var a=0;a<e.children.length;a++){var l=e.children[a],r=l.n;console.log(i," "+(r.evaluation==e.evaluation?"*":" ")+" move",new(t.GetMoveClass())(l.m).ToString(),"visits",r.visits,"eval",r.evaluation,"known",r.known,"sev",r.staticEvalSum+"/"+r.staticEvalCount,"who",r.who,"children",r.children?r.children.length:"no"),r.children&&r.evaluation==e.evaluation&&o(r,n+1)}}if(console.log(" duration",Date.now()-S),console.log(" evaluation:",e.evaluation),console.log(" fully explored:",e.known),console.log(" node count:",b),console.log(" redundant node count:",y),console.log(" max depth:",V),console.log(" alpha-beta",T.useAlphaBeta,"skipped",C),console.log(" ",x,"steps, per step",(Date.now()-S)/x,"ms"),console.log(" ",E,"playouts",k,"ms, per playout",k/E,"ms"),console.log(" UCT c",T.c),console.log(" tree",O),T.showMinimaxTree&&(console.log("Minimax tree"),o(e,0)),T.checkSide){var n=0,i=0;!function e(o){if(n++,o.children)for(var t=0;t<o.children.length;t++){var a=o.children[t];a.n.who!=-o.who&&i++,e(a.n)}}(O),console.log(" tree side alternance","node",n,"errors",i)}}var M,x=0,b=0,y=0,k=0,E=0,C=0,V=0,T={minVisitsExpand:n.level.minVisitsExpand||1,playoutSpread:n.level.playoutSpread||2,playoutDepth:void 0!==n.level.playoutDepth?n.level.playoutDepth:0,c:void 0!==n.level.c?n.level.c:.3,playoutCeil:void 0!==n.level.playoutCeil?n.level.playoutCeil:0,log:!!n.level.log,maxDuration:void 0!==n.level.maxDuration?n.level.maxDuration:2,maxLoops:void 0!==n.level.maxLoops?n.level.maxLoops:0,maxNodes:void 0!==n.level.maxNodes?n.level.maxNodes:0,showMinimaxTree:!!n.level.showMinimaxTree,showBestLine:!!n.level.showBestLine,ignoreLeaf:void 0!==n.level.ignoreLeaf&&n.level.ignoreLeaf,propagateMultiVisits:void 0===n.level.propagateMultiVisits||n.level.propagateMultiVisits,propagation:void 0===n.level.propagation?"mixed":n.level.propagation,useDepthWeights:void 0!==n.level.useDepthWeights&&n.level.useDepthWeights,productRatio:void 0===n.level.productRatio?0:n.level.productRatio,useAlphaBeta:void 0!==n.level.useAlphaBeta&&n.level.useAlphaBeta,uncertaintyFactor:void 0===n.level.uncertaintyFactor?0:n.level.uncertaintyFactor,directVisits:void 0===n.level.directVisits||n.level.directVisits,distributeEval:void 0===n.level.distributeEval||n.level.distributeEval,pickMove:void 0===n.level.pickMove?"besteval":n.level.pickMove,debugRawEval:void 0!==n.level.debugRawEval&&n.level.debugRawEval},B={};T.log&&console.log("Running UCT AI - ",n.level.label,"- Player",1==t.mWho?"A":"B");var D={v:0,l:null,m:{v:Number.MAX_VALUE,l:null,m:null}},A=JSON.parse(JSON.stringify(D)),L=[];if(t.mBoard.mMoves&&0!=t.mBoard.mMoves.length||t.mBoard.GenerateMoves(t),1==t.mBoard.mMoves.length)return t.mBestMoves=[t.mBoard.mMoves[0]],void JocUtil.schedule(t,"Done",{});0==t.mBoard.mMoves.length&&console.error("No move available",t);var O=new e(null,-t.mWho);b++,t.mOptions.uctTransposition&&(B[t.mBoard.GetSignature()]=O);var S=Date.now(),W=-1;g()}}();