var my={};var g_startOffset=null;var g_selectedPiece=null;var moveNumber=1;var g_allMoves=[];var g_playerWhite=true;var g_changingFen=false;var g_analyzing=false;var g_uiBoard;var g_cellSize;function chesswsMain(){var version='0.84';my.sttTime=performance.now() my.opts={name:'user'} my.bdSize=8 my.pcWd=Math.min(60,(window.innerWidth-30)/my.bdSize,(window.innerHeight-150)/my.bdSize) g_cellSize=my.pcWd my.bdWd=my.pcWd*my.bdSize my.bdHt=my.bdWd my.msg='' my.dragQ=false;my.players=[{name:'White',clr:'white',typeN:0,time:'endless',style:'drop-shadow(2px 2px 5px #88f) drop-shadow(-2px -2px 5px #88f)'},{name:'Black',clr:'black',typeN:1,time:'endless',style:'drop-shadow(2px 2px 5px #f88) drop-shadow(-2px -2px 5px #f88)'}];my.currPlayerNum=0 my.playerTypes=[{name:'Human',aiQ:false,sockQ:false},{name:'Human (Remote)',aiQ:false,sockQ:true},{name:'Computer (Beginner)',aiQ:true,aiLevel:1,timeMax:3000,sockQ:false},{name:'Computer (Medium)',aiQ:true,aiLevel:2,sockQ:false},{name:'Computer (Challenging)',aiQ:true,aiLevel:3,sockQ:false},{name:'Computer (Hard)',aiQ:true,aiLevel:4,sockQ:false}] my.imgSets=[{name:'Standard',prefix:'std',ext:'.png'},{name:'Maya',prefix:'maya',ext:'.svg'},{name:'Leipzig',prefix:'lpz',ext:'.svg'},{name:'Merida',prefix:'mer',ext:'.svg'},];my.imgSetN=0 my.times=[{name:'Endless',mm:60},{name:'1 Hour',mm:60},{name:'30 min',mm:30},{name:'20 min',mm:20},{name:'15 min',mm:15},{name:'10 min',mm:10},{name:'7 min',mm:7},{name:'5 min',mm:5}];my.timeN=0 my.sockQ=true my.sock=new Sock('chess') my.partnerQ=false var s='' my.sndHome='../images/sounds/index.html' console.log('sounds',document.domain,my.sndHome) s+='' s+='' s+='' s+='' s+='' my.snds=[];s+='
';s+='
';s+='
';s+='';s+='
';s+='
';my.soundQ=true s+=soundBtnHTML() s+='
';s+='
';s+='
';s+='' s+='
';s+='
'+optGet('name')+'
' s+=' vs ' s+='
Them
' s+=' ' s+='
Them
' s+='
' s+='
';s+='
';s+='
';s+='
';s+='
';s+=optPopHTML();s+='
';s+='';s+='
';if(false){s+='
';s+='Analysis: Off';s+='
';} s+='';s+='';s+='';s+='
© 2020 MathsIsFun.com v'+version+'
';s+='
';document.write(s);soundToggle() for(var i=0;i0)s+=my.msg+'
' if(my.partnerQ){if(my.localPlayerNum==my.currPlayerNum){s+='Your Turn ('+my.players[my.currPlayerNum].name+")"}else{s+='Waiting for '+my.players[my.currPlayerNum].name}} document.getElementById("msg").innerHTML=s} function playerChg(){my.currPlayerNum=1-my.currPlayerNum console.log('playerChg',my.currPlayerNum,my.players[my.currPlayerNum].name) my.players[my.currPlayerNum].clock.resume() my.players[1-my.currPlayerNum].clock.stop()} function gameOver(){my.gameOverQ=true for(var i=0;i0){my.dragQ=true;my.dragDiv=document.getElementById('drag');my.dragDiv.innerHTML='';my.dragDiv.style.left=(mouseX-my.dragHoldX)+'px';my.dragDiv.style.top=(mouseY-my.dragHoldY)+'px';my.dragDiv.style.visibility='visible';hilite();for(i=0;i>8)&0xFF)==MakeSquare(my.endY,my.endX)){move=moves[i];}} moveDo(move,false)} function moveDo(move,toldAboutMoveQ){console.log('Chosen move: '+move,move2coords(move)) if(move!=null){my.players[my.currPlayerNum].clock.stop() UpdatePgnTextBox(move);g_allMoves[g_allMoves.length]=move;MakeMove(move);my.currPlayerNum=1-my.currPlayerNum my.players[my.currPlayerNum].clock.resume() console.log('player changed to:',my.currPlayerNum,my.players[my.currPlayerNum].name,'type '+my.players[my.currPlayerNum].typeN,elapsed()) namesUpdate() var currType=my.players[my.currPlayerNum].typeN console.log('currType',currType,my.playerTypes[currType]) if(my.playerTypes[currType].aiQ){if(InitializeBackgroundEngine()){g_backgroundEngine.postMessage('move '+FormatMove(move));} UpdateFromMove(move);document.getElementById("FenTextBox").value=GetFen() aiMove()}else{if(my.sockQ){if(!toldAboutMoveQ){my.sock.moveSend({move:move})}} RedrawPieces();}}else{RedrawPieces();} hilite();} function setTime(ms){my.timeout=ms;} function hilite(td,clr){if(td==null){for(y=0;y<8;++y){for(x=0;x<8;++x){var td=g_uiBoard[bdIndex(x,y)];td.style.boxShadow='';}}}else{var inset='inset 0px 0px 18px '+clr;td.style.boxShadow=inset+', '+inset;}} function move2td(move,fromQ){var n=fromQ?move&0xFF:(move>>8)&0xFF;var row=((n-36)/16)<<0;var col=(n-4)%16;var td=g_uiBoard[bdIndex(col,row)];return td;} function move2coords(move){var coords=[] for(var i=0;i<2;i++){var fromQ=(i==0) var n=fromQ?move&0xFF:(move>>8)&0xFF;var row=((n-36)/16)<<0;var col=(n-4)%16;coords.push([row,col])} return coords} function getRandomInt(min,max){return Math.floor(Math.random()*(max-min+1))+min;} function soundBtnHTML(){var s='' s+='' s+='
' return s} 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 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 optPopHTML(){var s='';s+='
';s+='
';s+='';s+='';s+='
';var times=[] for(var i=0;i=0;i--){var p=my.players[i];s+='
';s+='

'+p.name+' player:

';s+='Type: ';s+=getDropdownHTML(playerTypes,'','playerType'+i,p.typeN);s+='
' s+='Time: ' s+=getDropdownHTML(times,'','timeType'+i,0);s+='
';s+='
';} s+='
';s+='
';var imgSets=[] for(var i=0;i';my.code=nameRand() s+='
';s+='Name (optional): ' s+='';s+='
' s+='Code: ' s+='';s+='
' s+='Partner must have code: ' s+='' s+='
' s+='
';return s;} function getDropdownHTML(opts,funcName,id,chkNo){var s='';s+='';return s;} function optpop(){var pop=document.getElementById('optpop');pop.style.transitionDuration="0.3s";pop.style.opacity=1;pop.style.zIndex=12;pop.style.left='10px';} function optYes(){var pop=document.getElementById('optpop');pop.style.opacity=0;pop.style.zIndex=1;pop.style.left='-999px';gameNew(false);} function optNo(){var pop=document.getElementById('optpop');pop.style.opacity=0;pop.style.zIndex=1;pop.style.left='-999px';} function EnsureAnalysisStopped(){if(g_analyzing&&g_backgroundEngine!=null){g_backgroundEngine.terminate();g_backgroundEngine=null;}} function UIAnalyzeToggle(){if(InitializeBackgroundEngine()){if(!g_analyzing){g_backgroundEngine.postMessage("analyze");}else{EnsureAnalysisStopped();} g_analyzing=!g_analyzing;document.getElementById("AnalysisToggleLink").innerText=g_analyzing?"Analysis: On":"Analysis: Off";}else{alert("Your browser must support web workers for analysis - (chrome4, ff4, safari)");}} function UIChangeFEN(){if(!g_changingFen){var fenTextBox=document.getElementById("FenTextBox");var result=InitializeFromFen(fenTextBox.value);if(result.length!=0){UpdatePVDisplay(result);return;}else{UpdatePVDisplay('');} g_allMoves=[];EnsureAnalysisStopped();InitializeBackgroundEngine();g_playerWhite=!!g_toMove;g_backgroundEngine.postMessage("position "+GetFen());RedrawBoard();}} function moveListAdd(s){var div=document.getElementById("movelist") div.value+=s+' '} function UpdatePgnTextBox(move){var s='' if(g_toMove!=0){if(moveNumber>1)s+=' ' s+=moveNumber+'. ' moveNumber++;} console.log('UpdatePgnTextBox',move) var mv=GetMoveSAN(move);s+=mv moveListAdd(s) if(mv.charAt(mv.length-1)=='+'){msg('Check') soundPlay('sndcheck',true)} if(mv.charAt(mv.length-1)=='#'){msg('CheckMate!') console.log('CheckMate!') gameOver() soundPlay('sndcheck',true)} if(mv.indexOf('x')>0){soundPlay('sndtake',true)}} function msg(s){my.msg=s namesUpdate()} function UIChangeTimePerMove(){var timePerMove=document.getElementById("TimePerMove");g_timeout=parseInt(timePerMove.value,10);} function FinishMove(bestMove,value,timeTaken,ply){if(bestMove!=null){UIPlayMove(bestMove,BuildPVMessage(bestMove,value,timeTaken,ply));}else{alert("Checkmate!");}} function UIPlayMove(move,pv){UpdatePgnTextBox(move);g_allMoves[g_allMoves.length]=move;MakeMove(move);UpdatePVDisplay(pv);UpdateFromMove(move);td=move2td(move,false);hilite(td,'#dd0');td=move2td(move,true);hilite(td,'green');} function UIUndoMove(){if(g_allMoves.length==0){return;} if(g_backgroundEngine!=null){g_backgroundEngine.terminate();g_backgroundEngine=null;} var curTyp=my.players[my.currPlayerNum].typeN var nxtTyp=my.players[1-my.currPlayerNum].typeN if(curTyp==0&&nxtTyp==0){UnmakeMove(g_allMoves[g_allMoves.length-1]);g_allMoves.pop();moveListAdd('Undo') my.currPlayerNum=1-my.currPlayerNum}else{if(g_allMoves.length>=2){UnmakeMove(g_allMoves[g_allMoves.length-1]);g_allMoves.pop();UnmakeMove(g_allMoves[g_allMoves.length-1]);g_allMoves.pop();moveListAdd('Undo2')}} RedrawBoard();} function UpdatePVDisplay(pv){if(pv!=null){var outputDiv=document.getElementById("output");if(outputDiv.firstChild!=null){outputDiv.removeChild(outputDiv.firstChild);} outputDiv.appendChild(document.createTextNode(pv));}} function aiMove(){console.log('aiMove',moveNumber,my.currPlayerNum,my.players[my.currPlayerNum].name,elapsed()) if(moveNumber>250){msg('Draw (too many moves)') return;} if(my.gameOverQ)return hilite() if(g_analyzing){EnsureAnalysisStopped();InitializeBackgroundEngine();g_backgroundEngine.postMessage("position "+GetFen());g_backgroundEngine.postMessage("analyze");return;} var currType=my.players[my.currPlayerNum].typeN;var playerType=my.playerTypes[currType];var aiLevel=playerType.aiLevel console.log('ai',currType,playerType,aiLevel) var maxPly=1;var rand='n';switch(aiLevel){case 1:var prevRate=my.randCount/moveNumber;if(prevRate>0.3){rand='n';}else{if(prevRate<0.1){rand='y';}else{if(Math.random()<0.2){rand='y';}}} if(rand=='y')my.randCount++;maxPly=1;g_timeout=40;break;case 2:maxPly=getRandomInt(1,3);g_timeout=100;break;case 3:maxPly=getRandomInt(2,5);g_timeout=500;break;case 4:maxPly=99;g_timeout=3000;break;} if(InitializeBackgroundEngine()){console.log('aiMove postMessage ',g_timeout,maxPly,rand,elapsed()) g_backgroundEngine.postMessage("search "+g_timeout+' '+maxPly+' '+rand);}else{console.log('QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ') Search(FinishMove,2,null);} setTimeout("aiDone()",g_timeout+300);} function aiDone(){soundPlay('sndmove') console.log('aiDone',my.currPlayerNum,my.players[my.currPlayerNum].name,elapsed()) if(my.gameOverQ)return msg('') my.players[my.currPlayerNum].clock.stop() my.currPlayerNum=1-my.currPlayerNum my.players[my.currPlayerNum].clock.resume() var currType=my.players[my.currPlayerNum].typeN console.log('aiDone type',currType,my.playerTypes[currType]) if(my.playerTypes[currType].aiQ){aiMove();}} function plyDone(bestMove,value,timeTaken,ply){console.log("plyDone",bestMove,value,timeTaken,ply);} var g_backgroundEngineValid=true;var g_backgroundEngine;function InitializeBackgroundEngine(){if(!g_backgroundEngineValid){return false;} if(g_backgroundEngine==null){g_backgroundEngineValid=true;try{g_backgroundEngine=new Worker("images/garbochess.js");g_backgroundEngine.onmessage=function(e){if(e.data.match("^pv")=="pv"){UpdatePVDisplay(e.data.substr(3,e.data.length-3));}else if(e.data.match("^message")=="message"){EnsureAnalysisStopped();UpdatePVDisplay(e.data.substr(8,e.data.length-8));}else{UIPlayMove(GetMoveFromString(e.data),null);}} g_backgroundEngine.error=function(e){console.log("Error from background worker:"+e.message);} g_backgroundEngine.postMessage("position "+GetFen());}catch(error){g_backgroundEngineValid=false;}} return g_backgroundEngineValid;} function UpdateFromMove(move){var fromX=(move&0xF)-4;var fromY=((move>>4)&0xF)-2;var toX=((move>>8)&0xF)-4;var toY=((move>>12)&0xF)-2;if(!g_playerWhite){fromY=7-fromY;toY=7-toY;fromX=7-fromX;toX=7-toX;} if((move&moveflagCastleKing)||(move&moveflagCastleQueen)||(move&moveflagEPC)||(move&moveflagPromotion)){RedrawPieces();}else{var fromSquare=g_uiBoard[bdIndex(fromX,fromY)];var toDiv=g_uiBoard[bdIndex(toX,toY)];toDiv.innerHTML='';RedrawPieces();}} function pcName(piece){var pcTyp=null;switch(piece&0x7){case piecePawn:pcTyp='p';break;case pieceKnight:pcTyp='n';break;case pieceBishop:pcTyp='b';break;case pieceRook:pcTyp='r';break;case pieceQueen:pcTyp='q';break;case pieceKing:pcTyp='k';break;} if(pcTyp==null){return null;}else{var imgSet=my.imgSets[my.imgSetN];var pcClr=(piece&0x8)?"w":"b";return 'images/chess/'+imgSet.prefix+pcClr+pcTyp+imgSet.ext}} function bdIndex(xn,yn,flipQ){flipQ=typeof flipQ!=='undefined'?flipQ:false if(flipQ){return yn*8+xn}else{return(7-yn)*8+(7-xn)}} function RedrawPieces(){for(y=0;y<8;++y){for(x=0;x<8;++x){var td=g_uiBoard[bdIndex(x,y)];var pieceY=g_playerWhite?y:7-y;var piece=g_board[((pieceY+2)*0x10)+(g_playerWhite?x:7-x)+4];var pieceName=pcName(piece);if(pieceName!=null){var img=document.createElement("div");var setNo=1;var pcClr=(piece&0x8)?"w":"b";img.style.backgroundImage='url("'+pieceName+'")';img.style.backgroundSize=g_cellSize+'px '+g_cellSize+'px';img.style.width=g_cellSize+'px';img.style.height=g_cellSize+'px';var divimg=document.createElement("div");divimg.appendChild(img);td.innerHTML='';td.appendChild(divimg);}else{td.innerHTML='';}}}} function RedrawBoard(){var div=document.getElementById('board');var table=document.createElement("table");table.cellPadding="0px";table.cellSpacing="0px";if(table.classList) table.classList.add('no-highlight');else table.className+=' '+'no-highlight';var tbody=document.createElement("tbody");g_uiBoard=[];for(y=0;y<8;++y){var tr=document.createElement("tr");for(x=0;x<8;++x){var td=document.createElement("td");td.style.width=g_cellSize+"px";td.style.height=g_cellSize+"px";td.style.backgroundColor=((y^x)&1)?"#b58863":"#f0d9b5";tr.appendChild(td);g_uiBoard[bdIndex(x,y)]=td;} tbody.appendChild(tr);} table.appendChild(tbody);RedrawPieces();div.innerHTML='';div.appendChild(table);g_changingFen=true;document.getElementById("FenTextBox").value=GetFen();g_changingFen=false;} function timesUp(me){console.log('timesUp',me.id) var p=my.players[me.id] msg(p.name+"'s Time is Up! ")} function DigiClock(ht,mode,divName){this.numHt=ht this.mode=mode this.upQ=true this.fromTime=0 this.hhQ=false this.mmQ=true this.ssQ=true this.msQ=false this.numWd=this.numHt*0.45 this.numGap=this.numWd*0.5 this.border=this.numHt*0.3 this.ht=this.numHt+this.border*2 var wd=0 var colonWd=this.numWd*0.7 if(this.hhQ)wd+=2*(this.numWd+this.numGap)+colonWd if(this.mmQ)wd+=2*(this.numWd+this.numGap)+colonWd if(this.ssQ)wd+=2*(this.numWd+this.numGap)+colonWd if(this.msQ)wd+=3*(this.numWd+this.numGap)+colonWd wd-=this.numGap wd-=colonWd wd+=this.numGap/2 this.wd=wd+this.border*2 this.type='led' switch(this.type){case 'lcd':this.clr={bg:'#ccc',border:'2px solid #888',on:'#222',off:'#ccc',shadow:'#ccc',shadowBlur:0} break case 'led':this.clr={bg:'#222',border:'2px solid black',on:'rgb(100, 255, 0)',off:'rgb(50, 80, 0)',shadow:'rgb(100, 255, 0)',shadowBlur:33} break default:} var div=document.getElementById(divName) div.style.height=this.ht+'px' div.style.width=this.wd+'px' this.el=document.createElement("canvas");div.appendChild(this.el) this.el.style.backgroundColor=this.clr.bg;this.el.style.borderRadius="10px";var ratio=2;this.el.width=this.wd*ratio;this.el.height=this.ht*ratio;this.el.style.width=this.wd+"px";this.el.style.height=this.ht+"px";this.g=this.el.getContext("2d");this.g.setTransform(ratio,0,0,ratio,0,0);this.numbers={n0:[1,1,1,0,1,1,1],n1:[0,0,1,0,0,1,0],n2:[1,0,1,1,1,0,1],n3:[1,0,1,1,0,1,1],n4:[0,1,1,1,0,1,0],n5:[1,1,0,1,0,1,1],n6:[0,1,0,1,1,1,1],n7:[1,0,1,0,0,1,0],n8:[1,1,1,1,1,1,1],n9:[1,1,1,1,0,1,1],A:[1,1,1,1,1,1,0],P:[1,1,1,1,1,0,0]};this.stt=performance.now();this.sofar=0 this.update()} DigiClock.prototype.setTime=function(h,m,s){this.fromTime=(((h*60)+m)*60+s)*1000 this.sofar=0 this.stt=performance.now();this.update()} DigiClock.prototype.start=function(){this.stt=performance.now();this.stopQ=false;this.sofar=0 this.loop()} DigiClock.prototype.stop=function(){this.stopQ=true this.sofar=this.total()} DigiClock.prototype.resume=function(){this.stt=performance.now() this.stopQ=false console.log('resume',this.id,this.sofar/1000,elapsed(),this.resume.caller.name) this.loop()} DigiClock.prototype.loop=function(){if(!this.stopQ){this.update() requestAnimationFrame(this.loop.bind(this));}} DigiClock.prototype.total=function(){var elapsed=performance.now()-this.stt elapsed+=this.sofar return elapsed} DigiClock.prototype.clear=function(){this.g.clearRect(0,0,this.wd,this.ht);} DigiClock.prototype.update=function(){this.g.clearRect(0,0,this.wd,this.ht);var elapsed=this.total();if(!this.upQ){elapsed=this.fromTime-elapsed if(elapsed<0){if(!this.stopQ){this.stopQ=true timesUp(this);elapsed=0}}} var hours=elapsed/3.6e6|0;var minutes=elapsed%3.6e6/6e4|0;var seconds=elapsed%6e4/1e3|0;var millis=elapsed%1e3|0;if(hours<10){hours='0'+hours;} if(minutes<10){minutes='0'+minutes;} if(seconds<10){seconds='0'+seconds;} millis=pad('000',''+millis,true) var timeStr='' if(this.hhQ)timeStr+=hours if(this.mmQ)timeStr+=minutes if(this.ssQ)timeStr+=seconds if(this.msQ)timeStr+=millis var posX=this.border var posY=this.border for(var i=0;i';return s;} PlayBtn.prototype.toggle=function(){var div=document.getElementById(this.id) if(my.playQ){my.playQ=false;div.classList.add(this.offClass);div.classList.remove(this.onClass);my.digi.stop()}else{my.playQ=true;div.classList.add(this.onClass);div.classList.remove(this.offClass);my.digi.resume()}} function pad(padFull,str,leftPadded){if(str==undefined)return padFull;if(leftPadded){return(padFull+str).slice(-padFull.length);}else{return(str+padFull).substring(0,padFull.length);}} function elapsed(){return(performance.now()-my.sttTime)/1000} function optGet(name){var val=localStorage.getItem(`remote.${name}`) if(val==null)val=my.opts[name] return val} function optSet(name,val){localStorage.setItem(`remote.${name}`,val) my.opts[name]=val} class Sock{constructor(gameType){this.socket=null this.queue=[] this.lines=[] this.gameType=gameType} send(msg){if(this.socket==null){this.queue.push(msg) this.connect()} if(this.socket.readyState===1){this.socket.send(msg)}} waitForConnection(callback,interval){if(this.socket.readyState===1){callback();}else{var that=this;interval*=2 setTimeout(function(){that.waitForConnection(callback,interval);},interval);}} connect(){var that=this var socket=new WebSocket('wss://www.mathbro.com:3002/echo');socket.onopen=function(ev){console.log("[open] Connection established");console.log("Sending to server");for(var i=0;i