public static Board parse(String filename) throws IOException {
    Preconditions.checkNotNull(filename, "filename cannot be null");
    
    String line;
    StringBuilder boardBuilder = new StringBuilder();
    
    // Read input file
    BufferedReader reader = new BufferedReader(new FileReader(filename));
    while ((line = reader.readLine()) != null) {
      boardBuilder.append(line);
      boardBuilder.append('\n');
    }
    reader.close();
    
    // Convert to board representation
    String[] rows = boardBuilder.toString().split("\n");
    int size = rows.length;
    
    int[][] newBoard = new int[size][size];
    Point empty = null;
    
    for (int i = 0; i < size; i++) {
      String[] cols = rows[i].split(" ");
      
      Preconditions.checkArgument(cols.length == size, "cols and rows must be of the same size");
      
      for (int j = 0; j < size; j++) {
        if (cols[j].equalsIgnoreCase("x")) {
          // Found empty square
          Preconditions.checkArgument(empty == null, "Found multiple start positions");
          
          empty = new Point(j, i); // Switch params because array transposed later
          newBoard[i][j] = -1;
        } else {
          newBoard[i][j] = Integer.parseInt(cols[j], 10);
        }
      }
    }
    
    // Transpose array to make it more intuitive
    for (int i = 1; i < size; i++) {
      for (int j = 0; j < i; j++) {
        int tmp = newBoard[i][j];
        newBoard[i][j] = newBoard[j][i];
        newBoard[j][i] = tmp;
      }
    }
    
    Preconditions.checkNotNull(empty, "Input must contain one empty square");
    
    return new Board(newBoard, empty, null, null);
  }