151 lines
5.2 KiB
Python
151 lines
5.2 KiB
Python
from typing import Any, Iterator
|
|
|
|
from ..base import BaseSolver
|
|
|
|
|
|
def reachable(
|
|
map: list[str], tiles: set[tuple[int, int]], steps: int
|
|
) -> set[tuple[int, int]]:
|
|
n_rows, n_cols = len(map), len(map[0])
|
|
|
|
for _ in range(steps):
|
|
tiles = {
|
|
(i + di, j + dj)
|
|
for i, j in tiles
|
|
for di, dj in ((-1, 0), (+1, 0), (0, -1), (0, +1))
|
|
if map[(i + di) % n_rows][(j + dj) % n_cols] != "#"
|
|
}
|
|
return tiles
|
|
|
|
|
|
class Solver(BaseSolver):
|
|
def solve(self, input: str) -> Iterator[Any]:
|
|
map = input.splitlines()
|
|
start = next(
|
|
(i, j)
|
|
for i in range(len(map))
|
|
for j in range(len(map[i]))
|
|
if map[i][j] == "S"
|
|
)
|
|
|
|
# part 1
|
|
yield len(reachable(map, {start}, 6 if len(map) < 20 else 64))
|
|
|
|
# part 2
|
|
|
|
# the initial map is a square and contains an empty rhombus whose diameter is
|
|
# the size of the map, and has only empty cells around the middle row and column
|
|
#
|
|
# after ~n/2 steps, the first map is filled with a rhombus, after that we get a
|
|
# bigger rhombus every n steps
|
|
#
|
|
# we are going to find the number of cells reached for the initial rhombus, n
|
|
# steps after and n * 2 steps after
|
|
#
|
|
cycle = len(map)
|
|
rhombus = (len(map) - 3) // 2 + 1
|
|
|
|
values: list[int] = []
|
|
values.append(len(tiles := reachable(map, {start}, rhombus)))
|
|
values.append(len(tiles := reachable(map, tiles, cycle)))
|
|
values.append(len(tiles := reachable(map, tiles, cycle)))
|
|
|
|
if self.files:
|
|
n_rows, n_cols = len(map), len(map[0])
|
|
|
|
rows = [
|
|
[
|
|
map[i % n_rows][j % n_cols] if (i, j) not in tiles else "O"
|
|
for j in range(-2 * cycle, 3 * cycle)
|
|
]
|
|
for i in range(-2 * cycle, 3 * cycle)
|
|
]
|
|
|
|
for i in range(len(rows)):
|
|
for j in range(len(rows[i])):
|
|
if (i // cycle) % 2 == (j // cycle) % 2:
|
|
rows[i][j] = f"\033[91m{rows[i][j]}\033[0m"
|
|
|
|
self.files.create(
|
|
"cycle.txt", "\n".join("".join(row) for row in rows).encode(), True
|
|
)
|
|
|
|
self.logger.info(f"values to fit: {values}")
|
|
|
|
# version 1:
|
|
#
|
|
# after 3 cycles, the figure looks like the following:
|
|
#
|
|
# I M D
|
|
# I J A K D
|
|
# H A F A L
|
|
# C E A K B
|
|
# C G B
|
|
#
|
|
# after 4 cycles, the figure looks like the following:
|
|
#
|
|
# I M D
|
|
# I J A K D
|
|
# I J A B A K D
|
|
# H A B A B A L
|
|
# C E A B A N F
|
|
# C E A N F
|
|
# C G F
|
|
#
|
|
# the 'radius' of the rhombus is the number of cycles minus 1
|
|
#
|
|
# the 4 'corner' (M, H, L, G) are counted once, the blocks with a corner triangle (D, I,
|
|
# C, B) are each counted radius times, the blocks with everything but one corner (J, K,
|
|
# E, N) are each counted radius - 1 times
|
|
#
|
|
# there are two versions of the whole block, A and B in the above (or odd and even),
|
|
# depending on the number of cycles, either A or B will be in the center
|
|
#
|
|
|
|
# counts = [
|
|
# [
|
|
# sum(
|
|
# (i, j) in tiles
|
|
# for i in range(ci * cycle, (ci + 1) * cycle)
|
|
# for j in range(cj * cycle, (cj + 1) * cycle)
|
|
# )
|
|
# for cj in range(-2, 3)
|
|
# ]
|
|
# for ci in range(-2, 3)
|
|
# ]
|
|
|
|
# radius = (26501365 - rhombus) // cycle - 1
|
|
# A = counts[2][2] if radius % 2 == 0 else counts[2][1]
|
|
# B = counts[2][2] if radius % 2 == 1 else counts[2][1]
|
|
# answer_2 = (
|
|
# (radius + 1) * A
|
|
# + radius * B
|
|
# + 2 * radius * (radius + 1) // 2 * A
|
|
# + 2 * radius * (radius - 1) // 2 * B
|
|
# + sum(counts[i][j] for i, j in ((0, 2), (-1, 2), (2, 0), (2, -1)))
|
|
# + sum(counts[i][j] for i, j in ((0, 1), (0, 3), (-1, 1), (-1, 3)))
|
|
# * (radius + 1)
|
|
# + sum(counts[i][j] for i, j in ((1, 1), (1, 3), (-2, 1), (-2, 3))) * radius
|
|
# )
|
|
|
|
# version 2: fitting a polynomial
|
|
#
|
|
# the value we are interested in (26501365) can be written as R + K * C where R is the
|
|
# step at which we find the first rhombus, and K the repeat step, so instead of fitting
|
|
# for X values (R, R + K, R + 2 K), we are going to fit for (0, 1, 2), giving us much
|
|
# simpler equation for the a, b and c coefficient
|
|
#
|
|
# we get:
|
|
# - (a * 0² + b * 0 + c) = y1 => c = y1
|
|
# - (a * 1² + b * 1 + c) = y2 => a + b = y2 - y1
|
|
# => b = y2 - y1 - a
|
|
# - (a * 2² + b * 2 + c) = y3 => 4a + 2b = y3 - y1
|
|
# => 4a + 2(y2 - y1 - a) = y3 - y1
|
|
# => a = (y1 + y3) / 2 - y2
|
|
#
|
|
y1, y2, y3 = values
|
|
a, b, c = (y1 + y3) // 2 - y2, 2 * y2 - (3 * y1 + y3) // 2, y1
|
|
|
|
n = (26501365 - rhombus) // cycle
|
|
yield a * n * n + b * n + c
|