153 lines
4.2 KiB
Python
153 lines
4.2 KiB
Python
from enum import Enum
|
|
from typing import Tuple
|
|
|
|
|
|
class Direction(Enum):
|
|
E = 0
|
|
SE = 1
|
|
SW = 2
|
|
W = 3
|
|
NW = 4
|
|
NE = 5
|
|
|
|
|
|
def convert_string(path: str) -> list[Direction]:
|
|
out, i = [], 0
|
|
while i < len(path):
|
|
char = path[i]
|
|
if char in ['s', 'n']:
|
|
i += 1
|
|
char += path[i]
|
|
if char == "e":
|
|
out.append(Direction.E)
|
|
elif char == "w":
|
|
out.append(Direction.W)
|
|
elif char == "ne":
|
|
out.append(Direction.NE)
|
|
elif char == "nw":
|
|
out.append(Direction.NW)
|
|
elif char == "se":
|
|
out.append(Direction.SE)
|
|
elif char == "sw":
|
|
out.append(Direction.SW)
|
|
i += 1
|
|
return out
|
|
|
|
|
|
def determine_neighbour(row: int, col: int, direction: Direction) -> Tuple[int, int]:
|
|
if direction == Direction.E:
|
|
return row, col+1
|
|
elif direction == Direction.SE:
|
|
return (row+1, col+1) if row % 2 == 0 else (row+1, col)
|
|
elif direction == Direction.SW:
|
|
return (row+1, col) if row % 2 == 0 else (row+1, col-1)
|
|
elif direction == Direction.W:
|
|
return row, col-1
|
|
elif direction == Direction.NW:
|
|
return (row-1, col) if row % 2 == 0 else (row-1, col-1)
|
|
elif direction == Direction.NE:
|
|
return (row-1, col+1) if row % 2 == 0 else (row-1, col)
|
|
|
|
|
|
def find_tile(start_r: int, start_c: int, path: str) -> Tuple[int, int]:
|
|
path = convert_string(path)
|
|
tile_r, tile_c = start_r, start_c
|
|
for direction in path:
|
|
tile_r, tile_c = determine_neighbour(tile_r, tile_c, direction)
|
|
return tile_r, tile_c
|
|
|
|
|
|
def parse_data() -> list[str]:
|
|
out = []
|
|
with open("input.txt") as file:
|
|
for line in file:
|
|
out.append(line.rstrip())
|
|
return out
|
|
|
|
|
|
def solve_p1(data: list[str]) -> int:
|
|
black_tiles: set[Tuple[int, int]] = set()
|
|
for line in data:
|
|
found_tile = find_tile(0, 0, line)
|
|
if found_tile in black_tiles:
|
|
black_tiles.remove(found_tile)
|
|
else:
|
|
black_tiles.add(found_tile)
|
|
return len(black_tiles)
|
|
|
|
|
|
DATA = parse_data()
|
|
print(solve_p1(DATA))
|
|
|
|
|
|
# Part 2 is kind of done ad hoc today because christmas :p
|
|
# not much time to work on it
|
|
class Grid:
|
|
def __init__(self, row: int, col: int):
|
|
self.row = row
|
|
self.col = col
|
|
self.grid = []
|
|
for i in range(row):
|
|
row_list = []
|
|
for j in range(col):
|
|
row_list.append(False)
|
|
self.grid.append(row_list)
|
|
|
|
def flip(self, row: int, col: int, state: bool) -> None:
|
|
self.grid[row][col] = state
|
|
|
|
def get_state(self, row: int, col: int) -> bool:
|
|
if 0 <= row < len(self.grid) and 0 <= col < len(self.grid[0]):
|
|
return self.grid[row][col]
|
|
return False
|
|
|
|
def count_ns(self, row: int, col: int) -> int:
|
|
if row % 2 == 0:
|
|
ns = [(0, 1), (0, -1), (1, 1), (1, 0), (-1, 1), (-1, 0)]
|
|
else:
|
|
ns = [(0, 1), (0, -1), (1, 0), (1, -1), (-1, 0), (-1, -1)]
|
|
counted = 0
|
|
for ns_r, ns_c in ns:
|
|
counted += self.get_state(row+ns_r, col+ns_c)
|
|
return counted
|
|
|
|
def next(self) -> 'Grid':
|
|
next_grid = Grid(self.row, self.col)
|
|
for i in range(self.row):
|
|
for j in range(self.col):
|
|
active_ns = self.count_ns(i, j)
|
|
active = self.get_state(i, j)
|
|
if active and (active_ns == 0 or active_ns > 2):
|
|
next_grid.flip(i, j, state=False)
|
|
elif not active and active_ns == 2:
|
|
next_grid.flip(i, j, state=True)
|
|
else:
|
|
next_grid.flip(i, j, state=active)
|
|
return next_grid
|
|
|
|
def count_black(self):
|
|
counted = 0
|
|
for i in range(self.row):
|
|
for j in range(self.col):
|
|
counted += self.get_state(i, j)
|
|
return counted
|
|
|
|
|
|
grid = Grid(row=200, col=200)
|
|
black_tiles: set[Tuple[int, int]] = set()
|
|
for line in DATA:
|
|
found_tile = find_tile(0, 0, line)
|
|
if found_tile in black_tiles:
|
|
black_tiles.remove(found_tile)
|
|
else:
|
|
black_tiles.add(found_tile)
|
|
|
|
|
|
for (x, y) in black_tiles:
|
|
grid.flip(x+100, y+100, True)
|
|
|
|
for i in range(100):
|
|
grid = grid.next()
|
|
|
|
print(grid.count_black())
|