···11+import copy
22+import fileinput
33+44+from utils import Point, N, S, E, W
55+66+77+# Parse problem input.
88+BOARD = {}
99+for y, line in enumerate(fileinput.input()):
1010+ for x, c in enumerate(line.strip()):
1111+ BOARD[Point(x, y)] = c
1212+1313+WIDTH = x + 1
1414+HEIGHT = y + 1
1515+1616+1717+def tilt(board, direction):
1818+ if direction == N:
1919+ for y in range(1, HEIGHT):
2020+ for x in range(WIDTH):
2121+ p = Point(x, y)
2222+ if board[p] != 'O':
2323+ continue
2424+2525+ while p.y > 0:
2626+ np = p + S
2727+ if board[np] == '.':
2828+ board[p] = '.'
2929+ board[np] = 'O'
3030+ else:
3131+ break
3232+ p = np
3333+3434+ elif direction == S:
3535+ for y in reversed(range(HEIGHT - 1)):
3636+ for x in range(WIDTH):
3737+ p = Point(x, y)
3838+ if board[p] != 'O':
3939+ continue
4040+4141+ while p.y < HEIGHT - 1:
4242+ np = p + N
4343+ if board[np] == '.':
4444+ board[p] = '.'
4545+ board[np] = 'O'
4646+ else:
4747+ break
4848+ p = np
4949+5050+ elif direction == E:
5151+ for x in reversed(range(WIDTH)):
5252+ for y in range(HEIGHT):
5353+ p = Point(x, y)
5454+ if board[p] != 'O':
5555+ continue
5656+5757+ while p.x < WIDTH - 1:
5858+ np = p + E
5959+ if board[np] == '.':
6060+ board[p] = '.'
6161+ board[np] = 'O'
6262+ else:
6363+ break
6464+ p = np
6565+6666+ elif direction == W:
6767+ for x in range(1, WIDTH):
6868+ for y in range(HEIGHT):
6969+ p = Point(x, y)
7070+ if board[p] != 'O':
7171+ continue
7272+7373+ while p.x > 0:
7474+ np = p + W
7575+ if board[np] == '.':
7676+ board[p] = '.'
7777+ board[np] = 'O'
7878+ else:
7979+ break
8080+ p = np
8181+8282+8383+def spin_cycle(board):
8484+ for x in [N, W, S, E]:
8585+ tilt(board, x)
8686+8787+8888+def total_load(board):
8989+ return sum(HEIGHT - p.y for p, c in board.items() if c == 'O')
9090+9191+9292+def serialize(board):
9393+ s = ''
9494+ for y in range(HEIGHT):
9595+ for x in range(WIDTH):
9696+ s += board[Point(x, y)]
9797+9898+ return s
9999+100100+101101+# Solve part 1.
102102+part_1_board = copy.deepcopy(BOARD)
103103+tilt(part_1_board, N)
104104+print("Part 1:", total_load(part_1_board))
105105+106106+107107+# Detect cycle length.
108108+board_states = {}
109109+110110+c = 1
111111+while True:
112112+ spin_cycle(BOARD)
113113+114114+ s = serialize(BOARD)
115115+ if s in board_states:
116116+ cycle_len = c - board_states[s]
117117+ print(f"Determined period = {cycle_len} on iteration {c}.")
118118+ break
119119+ else:
120120+ board_states[s] = c
121121+122122+ c += 1
123123+124124+# Simulate further ahead to work out part 2 answer.
125125+while c % cycle_len != 1000000000 % cycle_len:
126126+ spin_cycle(BOARD)
127127+ c += 1
128128+129129+print("Part 2:", total_load(BOARD))
130130+