var w,h,ratio,el,el2,g,g2,my={};function gomokuMain(mode){var version='0.64';my.typ=typeof mode!=='undefined'?mode:'bla';my.last={xn:-1,yn:-1} my.hdrHt=65 w=Math.min(500,window.innerWidth,window.innerHeight-my.hdrHt) h=w+my.hdrHt my.bdSzs=[{title:'9 by 9',sz:9},{title:'13 by 13',sz:13},{title:'19 by 19',sz:19}];my.bdSz=19 my.game={board:[]} my.players=[{name:'empty'},{name:'Black',clr:'black',lvl:0},{name:'White',clr:'white',lvl:1},];my.playerNo=1 my.player=my.players[my.playerNo] my.lvls=[{name:'Human',title:'Human'},{name:'Easy',title:'AI Easy',depth:0,bestn:3},{name:'Medium',title:'AI Medium',depth:1,bestn:2},{name:'Hard',title:'AI Hard',depth:2,bestn:0},] var s='';my.sndHome=(document.domain=='localhost')?'/mathsisfun/images/sounds/':'/images/sounds/' s+='' s+='' s+='' my.snds=[];my.imgHome=(document.domain=='localhost')?'/mathsisfun/games/images/':'/games/images/' s+='
';s+='';s+='';s+='';s+='';s+='
' my.soundQ=true s+=soundBtnHTML() s+='
';s+=popHTML() s+='
' s+='
Black
';s+='
';s+='
© 2018 MathsIsFun.com v'+version+'
';s+='
';document.write(s);ratio=3;el=document.getElementById('canvas1');el.width=w*ratio;el.height=h*ratio;el.style.width=w+"px";el.style.height=h+"px";g=el.getContext("2d");g.setTransform(ratio,0,0,ratio,0,0) el2=document.getElementById('canvas2');el2.width=w*ratio;el2.height=h*ratio;el2.style.width=w+"px";el2.style.height=h+"px";g2=el2.getContext("2d");g2.setTransform(ratio,0,0,ratio,0,0) el2.addEventListener('touchstart',touchStart,false);el2.addEventListener("mousemove",mouseMove,false);el2.addEventListener("mousedown",mouseDown,false);my.playQ=true gameNew()} function gameNew(){clearTimeout(my.timeid) var pcWd=Math.min(60,w/my.bdSz,(h-my.hdrHt)/my.bdSz) var bdWd=pcWd*(my.bdSz-1) my.rect={lt:pcWd/2,tp:pcWd/2+my.hdrHt,wd:bdWd,ht:bdWd} my.rect.rt=my.rect.lt+my.rect.wd my.rect.bt=my.rect.tp+my.rect.ht my.rect.pcWd=pcWd console.log('my.rect',my,my.rect) my.moveN=0 var player=my.players[my.playerNo] document.getElementById('info').style.color=player.clr document.getElementById('info').innerHTML=player.name+" goes first" var b=[] for(var i=0;i0){my.playQ=false var mid=Math.round(my.bdSz/2)-1 my.game.board[mid][mid]=my.playerNo soundPlay('sndmove') turnNext()}else{my.playQ=true}} function boardDraw(){var rowN=my.bdSz var colN=my.bdSz g.clearRect(0,0,g.canvas.width,g.canvas.height) g.lineWidth=0.7 var bit=0.8 for(var i=0;i';s+='
Black:
' s+=radioHTML('','blackLvl',my.lvls,'',my.players[1].lvl) s+='
' s+='
White:
' s+=radioHTML('','whiteLvl',my.lvls,'',my.players[2].lvl) s+='
' s+='
' s+='
' s+='
Board Size:
' s+=radioHTML('','bdSz',my.bdSzs,'',2);s+='
' s+='
' s+='
';s+='(will start new game)  ' s+='';s+='';s+='
';s+='';return s;} function radioHTML(prompt,id,lbls,func,n){var s='';s+='
';s+=prompt;for(var i=0;i';s+='';s+='
'} s+='
';return s;} function posLegal(xn,yn){if(xn<0)return false if(yn<0)return false if(xn>=my.bdSz)return false if(yn>=my.bdSz)return false if(my.game.board[xn][yn]>0)return false return true} function turnNext(){my.playQ=false var c=new AI() if(c.full(my.game.board)){boardDraw() document.getElementById('info').style.color='red' document.getElementById('info').innerHTML="A Draw!" my.playQ=false soundPlay('sndlose') return} if(c.terminalState(my.game.board)){boardDraw() var player=my.players[my.playerNo] document.getElementById('info').style.color=player.clr document.getElementById('info').innerHTML=player.name+" Wins!" my.playQ=false if(player.lvl==0){soundPlay('sndwin')}else{soundPlay('sndlose')} return} my.playerNo=(my.playerNo==1)?2:1 var player=my.players[my.playerNo] document.getElementById('info').style.color=player.clr document.getElementById('info').innerHTML=player.name boardDraw() my.moveN++ if(my.players[my.playerNo].lvl>0){my.playQ=false my.timeid=setTimeout(aiMove,1000)}else{my.playQ=true}} function aiMove(){var c=new AI() var move=c.getMove(my.game.board) console.log('move',move) if(typeof move==="undefined"){boardDraw() document.getElementById('info').style.color='red' document.getElementById('info').innerHTML="AI stuck!" my.playQ=false soundPlay('sndlose') return} var xn=move[0] var yn=move[1] my.last={xn:xn,yn:yn} my.game.board[xn][yn]=my.playerNo soundPlay('sndmove') turnNext() my.last={xn:-1,yn:-1} setTimeout(boardDraw,2000);} CanvasRenderingContext2D.prototype.pieceDraw=function(xn,yn,type,hoverq){var size=my.rect.pcWd/2 var x=my.rect.lt+xn*my.rect.pcWd var y=my.rect.tp+yn*my.rect.pcWd if(hoverq){this.beginPath() this.fillStyle='hsla(60,90%,10%,0.2)' this.arc(x+size*0.35,y+size*0.35,size*1.1,0,Math.PI*2,true) this.fill() this.beginPath() this.lineWidth=4 this.strokeStyle='hsla(50,100%,90%,0.3)' this.arc(x,y,size*1.1,0,Math.PI*2,true) this.stroke()}else{this.beginPath() this.fillStyle='hsla(240,0%,0%,0.3)' this.arc(x+size*0.13,y+size*0.13,size*1.03,0,Math.PI*2,true) this.fill()} let hiliteQ=(xn==my.last.xn&&yn==my.last.yn) if(hiliteQ){this.beginPath() this.fillStyle='hsla(240,100%,50%,0.3)' this.arc(x,y,size*1.15,0,Math.PI*2,true) this.fill()} var gradient if(type==1){this.beginPath() gradient=this.createRadialGradient(x+size*0.3,y+size*0.2,0,x,y,size*1.6);gradient.addColorStop(0,'hsla(240,10%,50%,1)');gradient.addColorStop(1,'black');this.fillStyle=gradient this.arc(x,y,size,0,Math.PI*2,true) this.fill() this.beginPath() gradient=this.createRadialGradient(x-size*0.5,y-size*0.5,0,x,y,size*1.6);gradient.addColorStop(0,'hsla(0,0%,70%,1)');gradient.addColorStop(0.3,'hsla(0,100%,100%,0)');this.fillStyle=gradient this.arc(x,y,size*1,0,Math.PI*2,true) this.fill()}else{this.beginPath() gradient=this.createRadialGradient(x-size*0.4,y-size*0.6,0,x,y,size*1.4);gradient.addColorStop(0,'white');gradient.addColorStop(0.4,'hsla(240,100%,93%,1)');this.fillStyle=gradient this.arc(x,y,size,0,Math.PI*2,true) this.fill()}} CanvasRenderingContext2D.prototype.ball=function(size,clr,hiClr,x,y){this.beginPath();var gradient=this.createRadialGradient(x-size*0.4,y-size*0.6,0,x,y,size*1.4);gradient.addColorStop(0,hiClr);gradient.addColorStop(0.6,clr);this.fillStyle=gradient;this.arc(x,y,size,0,Math.PI*2,true);this.fill();} function soundBtnHTML(){var onClr='#444' var offClr='#888' var s='' s+='' s+='
' return s} function soundPlay(id,simulQ){if(!my.soundQ)return simulQ=typeof simulQ!=='undefined'?simulQ:false if(simulQ){if(id.length>0)document.getElementById(id).play()}else{my.snds.push(id) soundPlayQueue(id)}} function soundPlayQueue(id){var div=document.getElementById(my.snds[0]) div.play() div.onended=function(){my.snds.shift();if(my.snds.length>0)soundPlayQueue(my.snds[0]);};} function soundToggle(){var btn='sound' if(my.soundQ){my.soundQ=false document.getElementById(btn).classList.add("mute")}else{my.soundQ=true document.getElementById(btn).classList.remove("mute")}} function AI(){this.minimaxCache={};} AI.prototype.nearbyMoves=function(grid){var nearby=[];for(var i=0;iscore){score=moveScore;position=[i,j];} if(moveScore>bestlow){if(bests.length==0){bests.push({pos:[i,j],score:moveScore})}else{for(var n=0;nbests[n].score){bests.splice(n,0,{pos:[i,j],score:moveScore}) break}} if(bests.length>4)bests.length=4} bestlow=bests[bests.length-1].score}}}} this.minimaxCache={};console.log('bests final',bestsFmt(bests),position) var choosen=Math.floor(Math.random()*lvl.bestn) choosen=Math.min(choosen,bests.length-1) position=bests[choosen].pos var elapsed=(performance.now()-timeStt)/1000 console.log('position',player.name,choosen,elapsed,position) return position;} function bestsFmt(bests){var s='' for(var i=0;icoord!==offsetCoord);if((j+4)in grid&&!grid[i][j+offsetCoord]&&grid[i][j+otherCoords[0]]===color&&grid[i][j+otherCoords[1]]===color&&grid[i][j+otherCoords[2]]===color&&grid[i][j+otherCoords[3]]===color)return[i,j+offsetCoord];if((i+4)in grid&&!grid[i+offsetCoord][j]&&grid[i+otherCoords[0]][j]===color&&grid[i+otherCoords[1]][j]===color&&grid[i+otherCoords[2]][j]===color&&grid[i+otherCoords[3]][j]===color)return[i+offsetCoord,j];if((i+4)in grid&&(j+4)in grid&&!grid[i+offsetCoord][j+offsetCoord]&&grid[i+otherCoords[0]][j+otherCoords[0]]===color&&grid[i+otherCoords[1]][j+otherCoords[1]]===color&&grid[i+otherCoords[2]][j+otherCoords[2]]===color&&grid[i+otherCoords[3]][j+otherCoords[3]]===color)return[i+offsetCoord,j+offsetCoord];if((i-4)in grid&&(j+4)in grid&&!grid[i-offsetCoord][j+offsetCoord]&&grid[i-otherCoords[0]][j+otherCoords[0]]===color&&grid[i-otherCoords[1]][j+otherCoords[1]]===color&&grid[i-otherCoords[2]][j+otherCoords[2]]===color&&grid[i-otherCoords[3]][j+otherCoords[3]]===color)return[i-offsetCoord,j+offsetCoord];}}}} AI.prototype.full=function(grid){for(var i=0;i