animate.py

2.3 kB · python · 61 lines

1from mrlypy.two import Cell2d, carpet_2d2import mrlypy.tile3import numpy as np4import time5from typing import List6from .config import Config7from .enums import Boundary, Fate8from .helpers import logger9from .models import Life1011def get_next_grid(cell: Cell2d, birth: List[int], survive: List[int], mask: np.ndarray, mode: str = "constant") -> Cell2d:12    cell.neighbors(mask, target=1, mode=mode)13    neighbor_grid = cell.tags14    grid = cell.types15    birth_mask = np.isin(neighbor_grid, birth) & (grid == 0)16    survive_mask = np.isin(neighbor_grid, survive) & (grid == 1)17    next_grid = np.zeros_like(grid)18    next_grid[birth_mask | survive_mask] = 119    return Cell2d(types=next_grid)2021def animate(config: Config, grid: Cell2d = None, mask: Cell2d = None) -> Life:22    # SETUP23    if grid is None:24        tile = mrlypy.tile.create(config.tile)25        tile = mrlypy.tile.build(tile)26        grid = tile.cell27    if mask is None:28        mask = carpet_2d(3)29    current_grid = grid.copy()30    if config.grid_size and config.grid_size > 1:31        current_grid = current_grid.tile(config.grid_size, config.grid_size)32    # PAD33    if config.padding > 0:34        padded_types = np.pad(current_grid.types, pad_width=config.padding, mode='constant', constant_values=0)35        current_grid = Cell2d(types=padded_types)36    # ANIMATE37    grids = [current_grid.copy()]38    history = {current_grid.types.tobytes(): 0}39    fate = None40    loop = 041    start_time = time.time()42    i = 043    for i in range(1, config.max_generations):44        next_grid = get_next_grid(current_grid, config.birth_counts, config.survive_counts, mask.types, mode=config.boundary.value)45        if np.array_equal(current_grid.types, next_grid.types):46            fate = Fate.DEAD if np.sum(next_grid.types) == 0 else Fate.LIFE47            break48        grid_bytes = next_grid.types.tobytes()49        if grid_bytes in history:50            loop = i - history[grid_bytes]51            fate = Fate.LOOP52            break53        history[grid_bytes] = i54        current_grid = next_grid55        grids.append(current_grid.copy())56        logger.debug(f"Animated: {i}")57    elapsed = round(time.time() - start_time, 2)58    logger.info(f"Animated {i} generations in {elapsed}s")59    if not fate:60        fate = Fate.TIME61    return Life(grids=grids, fate=fate, count=len(grids), time=elapsed, loop=loop)