from abc import ABC, abstractmethod from dataclasses import dataclass from typing import Generator @dataclass class CellUpdate: x: int y: int data: dict class Rule(ABC): @abstractmethod def step( self, x: int, y: int, cell: dict, grid: "Grid" ) -> Generator[CellUpdate, None, None]: pass class SimpleMapping(Rule): def __init__(self, prop, mapping): self.mapping = mapping self.prop = prop def step(self, x, y, cell, grid): yield CellUpdate(x, y, {self.prop: self.mapping.get(cell.get(self.prop))}) class Movement(Rule): def __init__(self, prop, dx, dy): self.prop = prop self.dx = dx self.dy = dy def step(self, x, y, cell, grid): yield CellUpdate(x + self.dx, y + self.dy, cell) class Combine(Rule): def __init__(self, *rules): self.rules = rules def step(self, x, y, cell, grid): updates = list(self.rules[0].step(x, y, cell, grid)) for rule in self.rules[1:]: next_updates = [] for upd in updates: next_updates.extend(rule.step(upd.x, upd.y, upd.data, grid)) updates = next_updates yield from updates