title: Tic Tac Toe with Minimax and Javascript
body: <p>I am attempting to create a Tic Tac Toe game using Javascript as part of my learning on FreeCodeCamp and after my 5th attempt still haven't managed to get it to work. I think i'm doing the correct thing, but the computer AI is still making very stupid decisions and loosing.</p>
  
  <p>Here is my entire AI function, which can be called using a console log to see the recommended move from the AI. However when i hard code values for moves already taken and ask for the next move, it doesn't pick what i would expect.</p>
  
  <pre><code>    /*
   Attempt to inplement a Tic Tac Toe minimax game
   5th attempt, so hopefully this time it works!
   */
  
  var util = require("util");
  
  //These are all the winning square sequences, as string to make various things easier
  var validRows = [
      ['00','01','02'],  // left column
      ['10','11','12'],  // middle column
      ['20','21','22'],  // right column
      ['00','10','20'],  // top row
      ['01','11','21'],  // middle row
      ['02','12','22'],  // bottom row
      ['00','11','22'],  // Diag TL to BR
      ['20','11','02']   // Diag BL to TR
  ];
  
  //Our scoring arrays for evaulating moves
  var max1 = ['100','010','001'];
  var min1 = ['200','020','002'];
  var max2 = ['110','011'];
  var min2 = ['220','022'];
  var max3 = ['111'];
  var min3 = ['222'];
  
  //All the valid squares
  var validSquaresFactory = ['00','10','20','01','11','21','02','12','22'];
  
  //Store all the moves somewhere
  //var computerMoves = ['10','22'];
  //var userMoves = ['00','02'];
  
  //Store all the moves somewhere
  var computerMoves = ['11','22'];
  var userMoves = ['00','01'];
  
  function Board(minOrMax,computerMoves,userMoves){
      this.computer = computerMoves;
      this.user = userMoves;
      this.minOrMax = minOrMax;
      this.remaining = this.genRemaining();
      this.complete = this.genComplete();
      var results = this.getScore();
      this.score = results[0];
      this.winOrLoose = results[1];
  }
  
  Board.prototype.genRemaining = function(){
      //Create an array of all moves taken
      var takenMoves = this.computer.concat(this.user);
      //Calculate all remaining squares
      var validSquares = validSquaresFactory.filter(function(object){
          return takenMoves.indexOf(object) === -1;
      });
      return validSquares;
  }
  
  Board.prototype.genComplete = function(){
      return ((this.computer.length + this.user.length) === 9);
  }
  
  Board.prototype.flipMinOrMax = function(){
      return (this.minOrMax === 'max') ? 'min' : 'max'
  }
  
  Board.prototype.genArrays = function(minOrMax,square){
      var tempUser = this.user.slice(0);
      var tempComputer = this.computer.slice(0);
      if(minOrMax === 'min'){
          tempUser.push(square);
      } else {
          tempComputer.push(square);
      }
      return [tempComputer,tempUser];
  }
  
  Board.prototype.generateBoards = function(minOrMax){
      var boards = [];
      var that = this;
      this.remaining.forEach(function(remainingSquare){
          var moves = that.genArrays(minOrMax,remainingSquare);
          boards.push(new Board(minOrMax,moves[0],moves[1]));
      })
      //console.log(boards);
      return boards;
  }
  
  Board.prototype.getScore = function(){
      var that = this;
      var winOrLoose = false;
      var returnScore =  validRows.reduce(function(storage,row,index,array){
          var score = row.reduce(function(storage1,square){
              if (that.computer.indexOf(square) !== -1) {
                  storage1 += '1';
              } else if (that.user.indexOf(square) !== -1) {
                  storage1 += '2';
              } else {
                  storage1 += '0';
              }
              return storage1;
          },'')
          var finalScore = 0;
          if(max1.indexOf(score) != -1){
              finalScore = 1;
          } else if(min1.indexOf(score) != -1){
              finalScore = -1;
          } else if(max2.indexOf(score) != -1){
              finalScore = 10;
          } else if(min2.indexOf(score) != -1){
              finalScore = -10;
          } else if(max3.indexOf(score) != -1){
              winOrLoose = true;
              finalScore = 100;
          } else if(min3.indexOf(score) != -1){
              winOrLoose = false;
              finalScore = -100;
          }
          storage.push(finalScore);
          return storage;
      },[])
      var condensedReturnScore =  returnScore.reduce(function(storage,score){
          return storage+score;
      })
      return [condensedReturnScore,winOrLoose];
  }
  
  
  function generateMove(){
      var board = new Board('max',computerMoves,userMoves);
      var scores = [];
      var boards = board.generateBoards('max');
      boards.forEach(function(board){
              scores.push(testMove(board,4));
      });
  
      scores = scores.map(function(score,index){
          return [board.remaining[index],score];
      });
      var returnValue = scores.reduce(function(storage,score){
          return (score[1].score &gt; storage[1].score) ? score : storage;
      });
      return [returnValue[0],returnValue[1].score];
  }
  
  
  function testMove(masterBoard,count){
      count --;
      var boards = [];
      boards = masterBoard.generateBoards(generateMinOrMax(masterBoard.minOrMax));
      //console.log('/////////Master Board/////////');
      //console.log(masterBoard);
      //console.log(boards);
      //console.log('//////////////////////////////');
      boards = boards.map(function (move) {
          if (move.complete === true || count === 0 || move.winOrLoose === true){
              return move;
          } else {
              var returnScore = testMove(move,count);
              return returnScore;
          }
      });
      returnBoard = boards.reduce(function(storage,board,index,array){
          if(board.minOrMax === 'max'){
              return (board.score &gt; storage.score) ? board : storage;
          } else {
              return (board.score &lt; storage.score) ? board : storage;
          }
      });
      return returnBoard;
  }
  
  
  function generateMinOrMax(minOrMax){
      return (minOrMax === 'max') ? 'min' : 'max'
  }
  </code></pre>
  
  <p>I've checked the scoring function and from what i can see it is returning the expected score for any move i try, but because of the shear number of possibilities calculated it's very hard to debug efficiently.</p>
  
  <p>Any help/pointers on this would be most appreciated as i have really hit a brick wall with this, can't see the forrest for the trees e.t.c</p>
  
  <p>If you would like to test this with the GUI, it's on codepen at - <a href="http://codepen.io/gazzer82/pen/yYZmvJ?editors=001" rel="nofollow">http://codepen.io/gazzer82/pen/yYZmvJ?editors=001</a></p>