From 1b09653fccbcec00492c78026dd5062c669714b6 Mon Sep 17 00:00:00 2001 From: dece Date: Fri, 11 Dec 2020 19:24:09 +0100 Subject: [PATCH] Day 11 (ugly) --- 2020/day11.py | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2020/grid.py | 47 +++++++++++++++++++++++---------- 2 files changed, 106 insertions(+), 14 deletions(-) create mode 100644 2020/day11.py diff --git a/2020/day11.py b/2020/day11.py new file mode 100644 index 0000000..6ff85aa --- /dev/null +++ b/2020/day11.py @@ -0,0 +1,73 @@ +import sys + +from grid import Grid, near8 + + +def main(): + lines = [line.rstrip() for line in sys.stdin] + + # Part 1 + g = Grid(value_factory=lambda: ".", lines=lines) + while update(g): + pass + print("Occupied:", sum(v == "#" for _, _, v in g.values_gen())) + + # Part 2 + g = Grid(value_factory=lambda: ".", lines=lines) + while update2(g): + pass + print("Occupied part 2:", sum(v == "#" for _, _, v in g.values_gen())) + + +def update(g): + changes = {} + for x, y, v in g.values_gen(): + if v == ".": + continue + p = (x, y) + n = list(g.near_objects(p, near_f=near8).values()) + if v == "L" and n.count("#") == 0: + changes[p] = "#" + elif v == "#" and n.count("#") >= 4: + changes[p] = "L" + for (x, y), v in changes.items(): + g.setv(x, y, v) + return bool(changes) + + +RAYS_OFS = [ + (-1, -1), ( 0, -1), ( 1, -1), + (-1, 0), ( 1, 0), + (-1, 1), ( 0, 1), ( 1, 1), +] + + +def update2(g): + changes = {} + for x, y, v in g.values_gen(): + if v == ".": + continue + occ = 0 + for ox, oy in RAYS_OFS: + n = 1 + while True: + qx, qy = x + ox * n, y + oy * n + if not g.hasv(qx, qy): + break + if (rv := g.getv(qx, qy)) == "#": + occ += 1 + break + elif rv == "L": + break + n += 1 + if v == "L" and occ == 0: + changes[(x, y)] = "#" + elif v == "#" and occ >= 5: + changes[(x, y)] = "L" + for (x, y), v in changes.items(): + g.setv(x, y, v) + return bool(changes) + + +if __name__ == "__main__": + main() diff --git a/2020/grid.py b/2020/grid.py index 87d7829..d37350c 100644 --- a/2020/grid.py +++ b/2020/grid.py @@ -3,6 +3,30 @@ from collections import defaultdict +def near(p): + """Return a tuple of neighbor positions (up, left, down, right).""" + return ( + (p[0] , p[1] - 1), + (p[0] + 1, p[1] ), + (p[0] , p[1] + 1), + (p[0] - 1, p[1] ), + ) + + +def near8(p): + """Return a tuple of neighbor positions, including diagonals.""" + return ( + (p[0] - 1, p[1] - 1), + (p[0] , p[1] - 1), + (p[0] + 1, p[1] - 1), + (p[0] - 1, p[1] ), + (p[0] + 1, p[1] ), + (p[0] - 1, p[1] + 1), + (p[0] , p[1] + 1), + (p[0] + 1, p[1] + 1), + ) + + class Grid: def __init__(self, value_factory=int, lines=None): @@ -10,6 +34,9 @@ class Grid: if lines: self.load(lines) + def hasv(self, x, y): + return y in self.g and x in self.g[y] + def getv(self, x, y): return self.g[y][x] @@ -26,9 +53,12 @@ class Grid: for x, v in row.items(): yield (x, y, v) - def near_objects(self, p): - """Return a dict of neighbor positions to values (U, L, D, R).""" - return {q: self.g[q[1]][q[0]] for q in Grid.near(p)} + def near_objects(self, p, near_f=near): + return { + q: self.getv(q[0], q[1]) + for q in near_f(p) + if self.hasv(q[0], q[1]) # do not create entries. + } def dumb_print(self, f=lambda v: v): for row in self.g.values(): @@ -37,19 +67,8 @@ class Grid: print() def print_near(self, p, view_size=2): - """Print near values in an area of view_size. Works iff keys support addition.""" for dy in range(-view_size, view_size + 1): print("".join([ self.g[p[1] + dy][p[0] + dx] for dx in range(-view_size, view_size + 1) ])) - - @staticmethod - def near(p): - """Return a tuple of neighbor positions (up, left, down, right).""" - return ( - (p[0] , p[1] - 1), - (p[0] + 1, p[1] ), - (p[0] , p[1] + 1), - (p[0] - 1, p[1] ), - )