def parse(string): return { complex(x, y): val for y, row in enumerate(string.splitlines()) for x, val in enumerate(row) } def render(grid): xmin, *_, xmax = sorted({int(p.real) for p in grid}) ymin, *_, ymax = sorted({int(p.imag) for p in grid}) for y in range(ymin, ymax + 1): for x in range(xmin, xmax + 1): print(grid[complex(x, y)], end='') print() def rotate(grid, angle): swaps_ongoing = 1 while swaps_ongoing: swaps_ongoing = 0 for pos, val in grid.items(): if val == 'O': while grid.get(pos + angle) == '.': pos, old = pos + angle, pos grid[pos], grid[old] = val, '.' swaps_ongoing += 1 return grid def calc_load(state): return int(sum(height - p.imag + 1 for p, val in state.items() if val == 'O')) text = open(0).read() grid = parse(text) height = max(p.imag for p in grid) print(calc_load(rotate(grid, -1j))) seq = [] while True: signature = frozenset(grid.items()) if signature in seq: break seq.append(signature) for angle in [-1j, -1, 1j, 1]: grid = rotate(grid, angle) prelude = seq.index(signature) cycle_length = len(seq) - prelude print(calc_load(dict(seq[prelude + (1_000_000_000 - prelude) % cycle_length])))