function Grid(other) { 
  var pieces = other || Grid.BLANK

  this._get = function(row, col) {
    var index = row * Grid.NUM_COLS + parseInt(col)
    return pieces.charAt(index)
  }

  this._set = function(row, col, value) {
    var index = row * Grid.NUM_COLS + parseInt(col)
    var changed = pieces.slice(0, index) + value + pieces.slice(index + 1)
    return new Grid(changed)
  }
  
  this.hash = function() {
    return pieces
  }
}

Grid.NUM_ROWS = 6
Grid.NUM_COLS = 7
Grid.DIR_PAIRS = [['NE','SW'], ['E','W'], ['SE','NW'], ['N','S']]
Grid.BLANK = '                                          '

Grid.prototype.drop = function(col, player) {
  var row = this.topFreeRow(col)
  if (row == -1) throw 'column full'
  return this._set(row, col, player.id)
}

Grid.prototype.topFreeRow = function(col) {
  for (var row = 0; row < Grid.NUM_ROWS; row += 1) {
    if (this._get(row,col)==' ') {
      return row
    }
  }
  return -1
}

Grid.prototype.connects4 = function(player, col) {
  var row = this.topFreeRow(col)-1
  if (row < 0) { row = Grid.NUM_ROWS-1 } // TODO clean up
  for (var i in Grid.DIR_PAIRS) {
    var dirs = Grid.DIR_PAIRS[i]
    var first = this.lineLength(player, row, col, dirs[0], 0)
    var second = this.lineLength(player, row, col, dirs[1], 0)
    var length = first + second
    if (length > 4) { 
      return true
    }
  }
  return false
}

Grid.prototype.lineLength = function(player, row, col, direction) {
  if (0 > row || row >= Grid.NUM_ROWS || 0 > col || col >= Grid.NUM_COLS) {
    return 0
  }
  var whose = this._get(row, col)
  if (whose == player.id) { // still on the line 
    var next = this.nextCoords(row, col, direction)
    return 1 + this.lineLength(player, next[0], next[1], direction)
  } else {
    return 0 
  }
}

Grid.prototype.nextCoords = function(row, col, direction) {
  switch(direction) {
    case 'NE': return [row+1, col+1];
    case 'SW': return [row-1, col-1];
    case 'E' : return [row  , col+1];
    case 'W' : return [row  , col-1];
    case 'SE': return [row-1, col+1];
    case 'NW': return [row+1, col-1];
    case 'N' : return [row+1, col  ];
    case 'S' : return [row-1, col  ];
    default: throw 'WTF is '+direction+'?'
  }
}

Grid.prototype.evaluate = function(player1, player2) {
  return 0
}

Grid.prototype.toString = function() {
  var str = ''
  for (var row = Grid.NUM_ROWS; row > 0; row -= 1) {
    str += row + ' '
    for (var col = 0; col < Grid.NUM_COLS; col += 1) {
      str += this._get(row-1, col) + ' '
    }
    str += '\n'
  }
  str += '  1 2 3 4 5 6 7 \n'
  return str
}
