Skip to content

Commit

Permalink
refactor: day16 factory to registry pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-ong committed Dec 30, 2023
1 parent 032fd83 commit 3ec5588
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 36 deletions.
60 changes: 29 additions & 31 deletions day16/lib/cells.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""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
Expand All @@ -10,47 +11,53 @@ class Cell(ABC):

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

@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

@staticmethod
def construct(contents: str) -> "Cell":
"""Construct proper cell from given contents."""
if contents == ".":
return DotCell()
elif contents == "|":
return PipeCell()
elif contents == "-":
return DashCell()
elif contents == "/":
return ForwardSlashCell()
elif contents == "\\":
return BackSlashCell()
raise AssertionError(f"unrecognized content {contents}")
try:
return Cell.CELL_TYPES[contents](contents)
except KeyError:
raise AssertionError(f"unrecognized content {contents}")

@abstractmethod
def next_lasers(self, laser: Laser) -> list[Laser]:
"""Return next lasers given a laser entering this cell."""
raise AssertionError("Not supported", laser)


@Cell.register_cell_type(".")
class DotCell(Cell):
"""A dot cell."""

def __init__(self) -> None:
"""Sets our contents to ``.``."""
self.contents = "."

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)]


@Cell.register_cell_type("-")
class DashCell(Cell):
"""A ``-`` cell."""

def __init__(self) -> None:
"""Sets our contents to ``-``."""
self.contents = "-"

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]:
Expand All @@ -65,13 +72,10 @@ def next_lasers(self, laser: Laser) -> list[Laser]:
raise AssertionError(f"Unknown direction {laser.direction}")


@Cell.register_cell_type("|")
class PipeCell(Cell):
"""A ``|`` cell."""

def __init__(self) -> None:
"""Sets our contents to ``|``."""
self.contents = "|"

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]:
Expand All @@ -86,13 +90,10 @@ def next_lasers(self, laser: Laser) -> list[Laser]:
raise AssertionError(f"Unknown direction {laser.direction}")


@Cell.register_cell_type("/")
class ForwardSlashCell(Cell):
"""A ``/`` cell."""

def __init__(self) -> None:
"""Sets our contents to ``/``."""
self.contents = "/"

def next_lasers(self, laser: Laser) -> list[Laser]:
"""Lasers go diagonal mode."""
row, col = laser.row, laser.col
Expand All @@ -107,13 +108,10 @@ def next_lasers(self, laser: Laser) -> list[Laser]:
raise AssertionError(f"Unknown direction {laser.direction}")


@Cell.register_cell_type("\\")
class BackSlashCell(Cell):
r"""A ``\`` cell."""

def __init__(self) -> None:
r"""Sets our contents to ``\``."""
self.contents = "\\"

def next_lasers(self, laser: Laser) -> list[Laser]:
"""Lasers go diagonal mode."""
row, col = laser.row, laser.col
Expand Down
28 changes: 23 additions & 5 deletions day16/tests/test_cells.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,28 @@
SOUTH_LASER = Laser(6, 5, Direction.SOUTH)


def test_cell() -> None:
"""Tests ``Cell.construct()``."""
dot_cell = Cell.construct(".")
assert isinstance(dot_cell, DotCell)

dash_cell = Cell.construct("-")
assert isinstance(dash_cell, DashCell)

pipe_cell = Cell.construct("|")
assert isinstance(pipe_cell, PipeCell)

fslash_cell = Cell.construct("/")
assert isinstance(fslash_cell, ForwardSlashCell)

bslash_cell = Cell.construct("\\")
assert isinstance(bslash_cell, BackSlashCell)


def test_dotcell() -> None:
"""Test ``DotCell``."""
laser: Laser = Laser(5, 5, Direction.NORTH)
cell: Cell = DotCell()
cell: Cell = DotCell(".")
assert cell.next_lasers(laser) == [NORTH_LASER]

laser = Laser(5, 5, Direction.EAST)
Expand All @@ -35,7 +53,7 @@ def test_dotcell() -> None:
def test_dashcell() -> None:
"""Test ``DashCell``."""
laser: Laser = Laser(5, 5, Direction.NORTH)
cell: Cell = DashCell()
cell: Cell = DashCell("-")

assert set(cell.next_lasers(laser)) == {WEST_LASER, EAST_LASER}

Expand All @@ -52,7 +70,7 @@ def test_dashcell() -> None:
def test_pipecell() -> None:
"""Test ``PipeCell``."""
laser: Laser = Laser(5, 5, Direction.NORTH)
cell: Cell = PipeCell()
cell: Cell = PipeCell("|")

assert cell.next_lasers(laser) == [NORTH_LASER]

Expand All @@ -69,7 +87,7 @@ def test_pipecell() -> None:
def test_forwardslashcell() -> None:
"""Test ``ForwardSlashCell``."""
laser: Laser = Laser(5, 5, Direction.NORTH)
cell: Cell = ForwardSlashCell()
cell: Cell = ForwardSlashCell("/")

assert cell.next_lasers(laser) == [EAST_LASER]

Expand All @@ -86,7 +104,7 @@ def test_forwardslashcell() -> None:
def test_backslashcell() -> None:
"""Test ``BackSlashCell``."""
laser: Laser = Laser(5, 5, Direction.NORTH)
cell: Cell = BackSlashCell()
cell: Cell = BackSlashCell("\\")

assert cell.next_lasers(laser) == [WEST_LASER]

Expand Down

0 comments on commit 3ec5588

Please sign in to comment.