adventOfCode/challenges/2022/23-unstableDiffusion/py/__init__.py
AKP b22f34cdc6
2022-23
Signed-off-by: AKP <tom@tdpain.net>
2022-12-23 15:11:07 +00:00

146 lines
3.3 KiB
Python

from __future__ import annotations
from typing import *
from aocpy import BaseChallenge, Vector, min_max
from enum import Enum
class CellState(Enum):
OCCUPIED = "#"
EMPTY = "."
class Direction(Enum):
NORTH = (0, -1)
EAST = (1, 0)
SOUTH = (0, 1)
WEST = (-1, 0)
ADJACENT_POSITIONS = [
(0, -1),
(1, -1),
(1, 0),
(1, 1),
(0, 1),
(-1, 1),
(-1, 0),
(-1, -1),
]
CHECK_POSITIONS = {
Direction.NORTH.value: [Direction.NORTH.value, (1, -1), (-1, -1)],
Direction.EAST.value: [Direction.EAST.value, (1, 1), (1, -1)],
Direction.SOUTH.value: [Direction.SOUTH.value, (1, 1), (-1, 1)],
Direction.WEST.value: [Direction.WEST.value, (-1, 1), (-1, -1)],
}
Crater = Dict[Vector, CellState]
def parse(instr: str) -> Crater:
res: Crater = {}
for y, line in enumerate(instr.strip().splitlines()):
for x, char in enumerate(line):
if char == CellState.OCCUPIED.value:
res[Vector(x, y)] = CellState.OCCUPIED
return res
def step(state: Crater, directions: List[Tuple[int, int]]) -> bool:
next_moves: Dict[Vector, List[Vector]] = {}
for elf in state:
for direction in directions:
has_any_adjacent = False
for adj_pos in ADJACENT_POSITIONS:
if elf + adj_pos in state:
has_any_adjacent = True
break
if not has_any_adjacent:
break
is_avail = True
for check_pos in CHECK_POSITIONS[direction.value]:
if elf + check_pos in state:
is_avail = False
break
if is_avail:
pos = elf + direction.value
x = next_moves.get(pos, [])
x.append(elf)
next_moves[pos] = x
break
for target_pos in next_moves:
if len(next_moves[target_pos]) != 1:
continue
state[target_pos] = CellState.OCCUPIED
del state[next_moves[target_pos][0]]
return len(next_moves) != 0
def print_crater(cr: Crater):
for y in range(-2, -2 + 12):
for x in range(-3, -3 + 14):
print(cr[(x, y)].value if (x, y) in cr else ".", end="")
print()
print("________________", flush=True)
def calc_open_area(state: Crater) -> int:
min_x, max_x = min_max(p.x for p in state)
min_y, max_y = min_max(p.y for p in state)
max_x += 1
max_y += 1
return ((max_x - min_x) * (max_y - min_y)) - len(state)
class Challenge(BaseChallenge):
@staticmethod
def one(instr: str) -> int:
inp = parse(instr)
directions = [
Direction.NORTH,
Direction.SOUTH,
Direction.WEST,
Direction.EAST,
]
for _ in range(10):
step(inp, directions)
directions.append(directions.pop(0))
return calc_open_area(inp)
@staticmethod
def two(instr: str) -> int:
inp = parse(instr)
directions = [
Direction.NORTH,
Direction.SOUTH,
Direction.WEST,
Direction.EAST,
]
n = 0
while True:
did_something_move = step(inp, directions)
n += 1
if not did_something_move:
break
directions.append(directions.pop(0))
return n