function AIOpponent(of, DEPTH) {
  Player.call(this, 'o', 'o')

  this.nextMove = function(grid, debug) {
    var scores = new Array(Grid.NUM_COLS)
    for (var col = 0; col < Grid.NUM_COLS; col += 1) {
      try {
        scores[col] = this.minimax(grid.drop(col, this), this, of, [col], {}, col, debug)
      } catch (e) {
        if (e == 'column full')
          scores[col] = -2
        else {
          throw e
        }
      }
    }

    // columns sorted by score, desc
    var cols = [0,1,2,3,4,5,6].sort(function(a,b) {
      return scores[b] - scores[a] || (a - b) * (Math.random() - 0.5)
    })

    if (debug) {
      print(scores)
      print(cols)
    }

    return cols[0] + 1 // col with highest score
  }

  this.minimax = function(grid, prevPlayer, currPlayer, path, cache, col, debug) {
    var wantMin = (path.length % 2 == 1) 
    if (grid.connects4(prevPlayer, col)) return (wantMin ? +1 : -1)
    if (path.length >= DEPTH) return 0//return grid.evaluate()

    var scores = new Array(Grid.NUM_COLS)
    for (var nextCol = 0; nextCol < Grid.NUM_COLS; nextCol += 1) {
      try {
        var nextGrid = grid.drop(nextCol, currPlayer)
        var hash = nextGrid.hash()
        if (hash in cache) {
          scores[nextCol] = cache[hash]
        } else {
          var score = this.minimax(nextGrid, currPlayer, prevPlayer, path+[nextCol], cache, nextCol, debug)
          cache[hash] = score
          scores[nextCol] = score
        }
      } catch (e) {
        if (e == 'column full') {
          scores[nextCol] = (wantMin ? +2 : -2)
        } else {
          throw e
        }
      }
    }

    var chooser = (wantMin ? Math.min : Math.max)
    var choice = chooser.apply(Math, scores)

    if (debug) {
      print('after '+path+' '+prevPlayer.name+' just dropped')
      print(grid.toString())
      print('so choice for '+currPlayer.name+' ('+chooser.name+') '+scores+' is '+choice)
    }
    return choice
  }

}
AIOpponent.prototype = Player.prototype
