aoc-2020/d24/main.py

232 lines
6.0 KiB
Python
Executable File

#! /usr/bin/env python3
from typing import Iterable
from typing import Tuple
from typing import Optional
from expanding import AutoExpanding
from expanding import GridItem
from expanding import Point
from expanding import World
from expanding import add_points
D2P = {
"se": Point(1, -1),
"sw": Point(-1, -1),
"ne": Point(1, 1),
"nw": Point(-1, 1),
"w": Point(-2, 0),
"e": Point(2, 0),
}
def directions_to_point(s: str) -> Point:
point = Point(0, 0)
direction = ""
for c in s:
direction += c
if direction in D2P:
point = add_points(point, D2P[direction])
direction = ""
if direction != "":
raise ValueError("Something left over unexpectedly")
return point
class Tile(GridItem):
def __init__(self, loc: Point, world: World, is_black: bool = False):
# Give this tile a unique id for tracking later
self.is_black = is_black
def _tock():
# print("Default tock")
pass
self.tock = _tock
super().__init__(loc, world)
def flip(self):
self.is_black = not self.is_black
def neighbors(self, expand=False) -> Iterable["Tile"]:
for diff in D2P.values():
neighbor_point = add_points(self._loc, diff)
neighbor = self._world.get_point(neighbor_point, expand=expand)
if neighbor:
yield neighbor
def tick(self):
num_black = count_black(self.neighbors(expand=True))
if self.is_black and (num_black == 0 or num_black > 2):
self.tock = self.flip
if not self.is_black and num_black == 2:
self.tock = self.flip
def count_black(tiles: Iterable[Tile]) -> int:
return sum(map(lambda x: 1 if x.is_black else 0, tiles))
class TileRow(AutoExpanding):
def __init__(self, y: int, world: World):
self._y = y
self._world = world
super().__init__()
def _item_factory(self, x: int) -> GridItem:
return Tile(Point(x, self._y), self._world)
def __repr__(self) -> str:
return "[" + " ".join((str(c) for c in self._l)) + "]"
def to_str(self) -> str:
return "".join((c.to_str() for c in self._l))
class Floor(World):
def __init__(self):
super().__init__()
def _item_factory(self, y: int) -> TileRow:
return TileRow(y, self)
def iter_items(self) -> Iterable[GridItem]:
for r in self:
for t in r:
yield t
def get_point(self, p: Point, expand=True) -> Optional[GridItem]:
try:
return self.get(
p.y,
expand=expand,
).get(
p.x,
expand=expand,
)
except IndexError:
return None
def get_all_bounds(self) -> Tuple[Point, Point]:
min_y, max_y = self.get_bounds()
min_x, max_x = 0, 0
for r in self:
if r.min_index() < min_x:
min_x = r.min_index()
if r.max_index() > max_x:
max_x = r.max_index()
return Point(min_x, min_y, 0), Point(max_x, max_y, 0)
def true_up(self):
min_point, max_point = self.get_all_bounds()
for r in self:
r.resize(min_point.x, max_point.x)
def expand_all(self, i: int):
min_point, max_point = self.get_all_bounds()
min_point = add_points(min_point, Point(-i, -i))
max_point = add_points(max_point, Point(i, i))
self.get_point(min_point, expand=True)
self.get_point(max_point, expand=True)
def tick(self):
for t in self.iter_items():
t.tick()
def tock(self):
for t in self.iter_items():
t.tock()
def sample():
print(directions_to_point("nwwswee"))
seen = {Point(0, 0)}
floor = Floor()
for line in (
"sesenwnenenewseeswwswswwnenewsewsw",
"neeenesenwnwwswnenewnwwsewnenwseswesw",
"seswneswswsenwwnwse",
"nwnwneseeswswnenewneswwnewseswneseene",
"swweswneswnenwsewnwneneseenw",
"eesenwseswswnenwswnwnwsewwnwsene",
"sewnenenenesenwsewnenwwwse",
"wenwwweseeeweswwwnwwe",
"wsweesenenewnwwnwsenewsenwwsesesenwne",
"neeswseenwwswnwswswnw",
"nenwswwsewswnenenewsenwsenwnesesenew",
"enewnwewneswsewnwswenweswnenwsenwsw",
"sweneswneswneneenwnewenewwneswswnese",
"swwesenesewenwneswnwwneseswwne",
"enesenwswwswneneswsenwnewswseenwsese",
"wnwnesenesenenwwnenwsewesewsesesew",
"nenewswnwewswnenesenwnesewesw",
"eneswnwswnwsenenwnwnwwseeswneewsenese",
"neswnwewnwnwseenwseesewsenwsweewe",
"wseweeenwnesenwwwswnew",
):
p = directions_to_point(line)
print(f"Parsed point {p}")
if p in seen:
print("seen!")
seen.add(p)
floor.get_point(p, expand=True).flip()
black = count_black(floor.iter_items())
print("Day 0")
print(f"Number black is {black}")
for day in range(100):
floor.expand_all(1)
floor.true_up()
floor.tick()
floor.tock()
black = count_black(floor.iter_items())
total = sum(map(lambda x: 1, floor.iter_items()))
print(f"Day {day+1}: {black} / {total}")
def part1():
floor = Floor()
with open("input.txt") as f:
for line in f:
line = line.strip()
p = directions_to_point(line)
floor.get_point(p, expand=True).flip()
black = count_black(floor.iter_items())
print(f"Number black is {black}")
def part2():
floor = Floor()
with open("input.txt") as f:
for line in f:
line = line.strip()
p = directions_to_point(line)
floor.get_point(p, expand=True).flip()
black = count_black(floor.iter_items())
print("Day 0")
print(f"Number black is {black}")
for day in range(1, 11):
floor.tick()
floor.tock()
black = count_black(floor.iter_items())
print(f"Day {day}: {black}")
if __name__ == "__main__":
sample()
# part1()
# part2()