def read_problem(filename):
    """
    Read a 0hh1 puzzle from file <filename>

    Takes a filename, and returns a problem dictionary with
    the following fields:
    - 'size': the number of rows/columns (assumed to be the same;
      an error will be raised if there is a size mismatch)
    - 'variables': the puzzle itself, a dictionary with (row, column)
      tuples as keys and the corresponding value 'r', 'b', or '.'
    - 'state': initially 'unsolved', to be updated by other methods
    """
    with open(filename, 'r') as problem_file:
        problem = [['rb' if c == '.' else c for c in line if c in '.rb']
                   for line in problem_file]
    size = len(problem)
    assert all(len(v) == size for v in problem)
    cells = {(r, c): problem[r][c] for r in range(size) for c in range(size)}
    problem_dict = {'size': size, 'variables': cells, 'state': 'unsolved'}
    return problem_dict