Day 11 (ugly)

This commit is contained in:
dece 2020-12-11 19:24:09 +01:00
parent cbedbccd57
commit 1b09653fcc
2 changed files with 106 additions and 14 deletions

73
2020/day11.py Normal file
View file

@ -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()

View file

@ -3,6 +3,30 @@
from collections import defaultdict 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: class Grid:
def __init__(self, value_factory=int, lines=None): def __init__(self, value_factory=int, lines=None):
@ -10,6 +34,9 @@ class Grid:
if lines: if lines:
self.load(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): def getv(self, x, y):
return self.g[y][x] return self.g[y][x]
@ -26,9 +53,12 @@ class Grid:
for x, v in row.items(): for x, v in row.items():
yield (x, y, v) yield (x, y, v)
def near_objects(self, p): def near_objects(self, p, near_f=near):
"""Return a dict of neighbor positions to values (U, L, D, R).""" return {
return {q: self.g[q[1]][q[0]] for q in Grid.near(p)} 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): def dumb_print(self, f=lambda v: v):
for row in self.g.values(): for row in self.g.values():
@ -37,19 +67,8 @@ class Grid:
print() print()
def print_near(self, p, view_size=2): 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): for dy in range(-view_size, view_size + 1):
print("".join([ print("".join([
self.g[p[1] + dy][p[0] + dx] self.g[p[1] + dy][p[0] + dx]
for dx in range(-view_size, view_size + 1) 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] ),
)