2021-11-01 16:40:46 +01:00
|
|
|
import collections
|
|
|
|
|
|
|
|
|
|
from solutions import BaseSolution
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Rule:
|
|
|
|
|
size = 0
|
|
|
|
|
|
|
|
|
|
def __init__(self, line):
|
2025-05-06 20:14:58 +02:00
|
|
|
pattern, output = line.split(" => ")
|
|
|
|
|
self.pattern = pattern.replace("/", "")
|
|
|
|
|
self.output = output.replace("/", "")
|
2021-11-01 16:40:46 +01:00
|
|
|
self.size = int(len(self.pattern) ** 0.5)
|
|
|
|
|
|
|
|
|
|
self.patterns = [
|
|
|
|
|
self.pattern,
|
|
|
|
|
self._f(self.pattern),
|
|
|
|
|
self._r(self.pattern),
|
|
|
|
|
self._f(self._r(self.pattern)),
|
|
|
|
|
self._r(self._f(self.pattern)),
|
|
|
|
|
self._r(self._r(self.pattern)),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
2025-05-06 20:14:58 +02:00
|
|
|
return "[{}] {} -> {}".format(self.size, self.pattern, self.output)
|
2021-11-01 16:40:46 +01:00
|
|
|
|
|
|
|
|
def _r(self, b):
|
2025-05-06 20:14:58 +02:00
|
|
|
return "".join(
|
|
|
|
|
map(
|
|
|
|
|
lambda g: "".join(g),
|
|
|
|
|
zip(*[b[s : s + self.size] for s in range(0, len(b), self.size)][::-1]),
|
|
|
|
|
)
|
|
|
|
|
)
|
2021-11-01 16:40:46 +01:00
|
|
|
|
|
|
|
|
def _f(self, b):
|
2025-05-06 20:14:58 +02:00
|
|
|
return b[-self.size :] + b[self.size : len(b) - self.size] + b[0 : self.size]
|
2021-11-01 16:40:46 +01:00
|
|
|
|
|
|
|
|
def matches(self, canvas):
|
|
|
|
|
return canvas in self.patterns
|
|
|
|
|
|
|
|
|
|
def enhance(self, canvas):
|
|
|
|
|
return self.output
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Solution(BaseSolution):
|
2025-05-06 20:14:58 +02:00
|
|
|
input_file = "21.txt"
|
2021-11-01 16:40:46 +01:00
|
|
|
|
|
|
|
|
def __str__(self):
|
2025-05-06 20:14:58 +02:00
|
|
|
return "Day 21: Fractal Art"
|
2021-11-01 16:40:46 +01:00
|
|
|
|
|
|
|
|
def _get_block(self, canvas, size, n=0):
|
2025-05-06 20:14:58 +02:00
|
|
|
s = ""
|
2021-11-01 16:40:46 +01:00
|
|
|
cl = int(len(canvas) ** 0.5)
|
|
|
|
|
x = n % (cl // size)
|
|
|
|
|
y = n // (cl // size)
|
|
|
|
|
for r in range(size):
|
|
|
|
|
start = cl * ((y * size) + r) + (x * size)
|
|
|
|
|
end = start + size
|
|
|
|
|
s += canvas[start:end]
|
|
|
|
|
return s
|
|
|
|
|
|
|
|
|
|
def _join_blocks(self, blocks):
|
|
|
|
|
bl = len(blocks)
|
2025-05-06 20:14:58 +02:00
|
|
|
c = int(bl**0.5)
|
2021-11-01 16:40:46 +01:00
|
|
|
rl = int(len(blocks[0]) ** 0.5)
|
2025-05-06 20:14:58 +02:00
|
|
|
canvas = ""
|
2021-11-01 16:40:46 +01:00
|
|
|
for i in range(0, bl, c):
|
|
|
|
|
for j in range(rl):
|
2025-05-06 20:14:58 +02:00
|
|
|
canvas += "".join(
|
|
|
|
|
[block[j * rl : (j + 1) * rl] for block in blocks[i : i + c]]
|
|
|
|
|
)
|
2021-11-01 16:40:46 +01:00
|
|
|
return canvas
|
|
|
|
|
|
|
|
|
|
def solve(self, puzzle_input, iterations=5):
|
2025-05-06 20:14:58 +02:00
|
|
|
canvas = ".#...####"
|
2021-11-01 16:40:46 +01:00
|
|
|
rules = [Rule(l.strip()) for l in puzzle_input.splitlines()]
|
|
|
|
|
for _ in range(iterations):
|
|
|
|
|
size = 2 if len(canvas) % 2 == 0 else 3
|
2025-05-06 20:14:58 +02:00
|
|
|
blocks = len(canvas) // (size**2)
|
2021-11-01 16:40:46 +01:00
|
|
|
cb = []
|
|
|
|
|
for b in range(blocks):
|
|
|
|
|
bc = self._get_block(canvas, size, b)
|
|
|
|
|
rule = [r for r in rules if r.matches(bc) and r.size == size]
|
|
|
|
|
if len(rule) > 0:
|
|
|
|
|
r = rule[0]
|
|
|
|
|
cb.append(r.enhance(bc))
|
|
|
|
|
canvas = self._join_blocks(cb)
|
2025-05-06 20:14:58 +02:00
|
|
|
return collections.Counter(canvas)["#"]
|
2021-11-01 16:40:46 +01:00
|
|
|
|
|
|
|
|
def solve_again(self, puzzle_input):
|
|
|
|
|
return self.solve(puzzle_input, 18)
|
|
|
|
|
|
|
|
|
|
|
2025-05-06 20:14:58 +02:00
|
|
|
if __name__ == "__main__":
|
2021-11-01 16:40:46 +01:00
|
|
|
solution = Solution()
|
|
|
|
|
solution.show_results()
|