"""Cell classes."""
from abc import ABC, abstractmethod
from typing import Callable, Dict, Type, TypeVar
from day16.lib.direction import Direction
from day16.lib.laser import Laser
[docs]
class Cell(ABC):
"""Abstract cell class."""
contents: str
# each cell can register itself to us
CELL_TYPES: Dict[str, Type["Cell"]] = {}
def __init__(self, contents: str):
"""Default constructor, sets our contents."""
self.contents = contents
[docs]
@staticmethod
def register_cell_type(
cell_contents: str,
) -> Callable[[type["Cell"]], type["Cell"]]:
"""Registers a cell type to this factory."""
def decorator(cls: Type["Cell"]) -> Type["Cell"]:
Cell.CELL_TYPES[cell_contents] = cls
return cls
return decorator
[docs]
@staticmethod
def construct(contents: str) -> "Cell":
"""Construct proper cell from given contents."""
try:
return Cell.CELL_TYPES[contents](contents)
except KeyError:
raise AssertionError(f"unrecognized content {contents}")
[docs]
@abstractmethod
def next_lasers(self, laser: Laser) -> list[Laser]:
"""Return next lasers given a laser entering this cell."""
raise AssertionError("Not supported", laser)
[docs]
@Cell.register_cell_type(".")
class DotCell(Cell):
"""A dot cell."""
[docs]
def next_lasers(self, laser: Laser) -> list[Laser]:
"""Lasers pass directly through this tile."""
row, col = laser.direction.offset(laser.row, laser.col)
return [Laser(row, col, laser.direction)]
[docs]
@Cell.register_cell_type("-")
class DashCell(Cell):
"""A ``-`` cell."""
[docs]
def next_lasers(self, laser: Laser) -> list[Laser]:
"""Lasers must end up going east/west after passing through this cell."""
if laser.direction in [Direction.EAST, Direction.WEST]:
row, col = laser.direction.offset(laser.row, laser.col)
return [Laser(row, col, laser.direction)]
elif laser.direction in [Direction.NORTH, Direction.SOUTH]:
row, col = laser.row, laser.col
return [
Laser(row, col + 1, Direction.EAST),
Laser(row, col - 1, Direction.WEST),
]
raise AssertionError(f"Unknown direction {laser.direction}")
[docs]
@Cell.register_cell_type("|")
class PipeCell(Cell):
"""A ``|`` cell."""
[docs]
def next_lasers(self, laser: Laser) -> list[Laser]:
"""Lasers must end up going north/south after passing through this cell."""
if laser.direction in [Direction.NORTH, Direction.SOUTH]:
row, col = laser.direction.offset(laser.row, laser.col)
return [Laser(row, col, laser.direction)]
elif laser.direction in [Direction.EAST, Direction.WEST]:
row, col = laser.row, laser.col
return [
Laser(row - 1, col, Direction.NORTH),
Laser(row + 1, col, Direction.SOUTH),
]
raise AssertionError(f"Unknown direction {laser.direction}")
[docs]
@Cell.register_cell_type("/")
class ForwardSlashCell(Cell):
"""A ``/`` cell."""
[docs]
def next_lasers(self, laser: Laser) -> list[Laser]:
"""Lasers go diagonal mode."""
row, col = laser.row, laser.col
if laser.direction == Direction.EAST:
return [Laser(row - 1, col, Direction.NORTH)]
if laser.direction == Direction.NORTH:
return [Laser(row, col + 1, Direction.EAST)]
if laser.direction == Direction.SOUTH:
return [Laser(row, col - 1, Direction.WEST)]
if laser.direction == Direction.WEST:
return [Laser(row + 1, col, Direction.SOUTH)]
raise AssertionError(f"Unknown direction {laser.direction}")
[docs]
@Cell.register_cell_type("\\")
class BackSlashCell(Cell):
r"""A ``\`` cell."""
[docs]
def next_lasers(self, laser: Laser) -> list[Laser]:
"""Lasers go diagonal mode."""
row, col = laser.row, laser.col
if laser.direction == Direction.EAST:
return [Laser(row + 1, col, Direction.SOUTH)]
if laser.direction == Direction.SOUTH:
return [Laser(row, col + 1, Direction.EAST)]
if laser.direction == Direction.NORTH:
return [Laser(row, col - 1, Direction.WEST)]
if laser.direction == Direction.WEST:
return [Laser(row - 1, col, Direction.NORTH)]
raise AssertionError(f"Unknown direction {laser.direction}")