function TicTacToe() { var ended=false; var moves=0; var pos=[0,0,0, 0,0,0, 0,0,0]; var nextSide=1; var aiSwitch=function(){ nextSide=nextSide==1?2:1; } var prompts={ ended:"Game ended", userWin:"You win!", draw:"Draw!", aiWin:"I win!", next:"Your move?", illegal:"Illegal move" }; var checkWin=function(lastMove){ // Use fast lookup table var wins=[[[1,2],[3,6],[4,8]],//0 [[0,2],[4,7]],//1 [[0,1],[5,8],[4,6]],//2 [[4,5],[0,6]],//3 [[3,5],[2,7],[0,8],[2,6]],//4 [[2,8],[3,4]],//5 [[7,8],[1,3],[2,4]],//6 [[6,8],[1,4]],//7 [[6,7],[2,5],[0,4]]//8 ]; return wins[lastMove].reduce(function(last,curr){ return last||curr.reduce(function(all,mov){ return all&&pos[mov]==pos[lastMove]; },true); },false); }; var aiMove=function(){ var moveList=[4,0,2,6,8,1,3,5,7]; return moveList.reduce(function(empty,next){ return (empty>=0||pos[next])?empty:next; },-1); }; var renderPos=function(){ var renderXO=function(i){return ["_","X","0"][i];}; console.log(pos.slice(0,3).map(renderXO).join("|")); console.log(pos.slice(3,6).map(renderXO).join("|")); console.log(pos.slice(6,9).map(renderXO).join("|")); }; this.move = function(field) { var realMove=field-1; if(ended){ return [0,prompts.ended]; } if(pos[realMove]){ return [0,prompts.illegal]; } if(moves==0&&!isFinite(realMove)){ moves++; pos[4]=nextSide; renderPos(); aiSwitch(); return [5, prompts.next]; } if(isFinite(realMove)){ pos[realMove]=nextSide; aiSwitch(); moves++; if(checkWin(realMove)){ ended=true; renderPos(); return [0, prompts.userWin]; } if(moves>=9){ ended=true; renderPos(); return [0, prompts.draw]; } } var next=aiMove(); pos[next]=nextSide; moves++; if(checkWin(next)){ ended=true; renderPos(); return [next+1,prompts.aiWin]; } if(moves>=9){ ended=true; renderPos(); return [next+1, prompts.draw]; } renderPos(); aiSwitch(); return [next+1,prompts.next]; }; }