diff --git a/src/holt59/aoc/2023/day21.py b/src/holt59/aoc/2023/day21.py index 9618081..c44a4c1 100644 --- a/src/holt59/aoc/2023/day21.py +++ b/src/holt59/aoc/2023/day21.py @@ -39,8 +39,7 @@ print(f"answer 1 is {answer_1}") # 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, and then interpolate the value to get a 2nd order -# polynomial +# after and n * 2 steps after # cycle = len(map) rhombus = (len(map) - 3) // 2 + 1 @@ -56,9 +55,9 @@ if logging.root.getEffectiveLevel() == logging.INFO: rows = [ [ map[i % n_rows][j % n_cols] if (i, j) not in tiles else "O" - for j in range(-2 * cycle, 3 * cycle + 1) + for j in range(-2 * cycle, 3 * cycle) ] - for i in range(-2 * cycle, 3 * cycle + 1) + for i in range(-2 * cycle, 3 * cycle) ] for i in range(len(rows)): @@ -71,6 +70,64 @@ if logging.root.getEffectiveLevel() == logging.INFO: logging.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 +) +print(f"answer 2 (v1) is {answer_2}") + +# 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 @@ -89,4 +146,4 @@ a, b, c = (y1 + y3) // 2 - y2, 2 * y2 - (3 * y1 + y3) // 2, y1 n = (26501365 - rhombus) // cycle answer_2 = a * n * n + b * n + c -print(f"answer 2 is {answer_2}") +print(f"answer 2 (v2) is {answer_2}")