TheBird

game.py

6.7 kB · python · 208 lines

1import os2import sys34# MRLYPROD56HERE = os.path.dirname(os.path.abspath(__file__))7MRLYPROD = os.path.normpath(os.path.join(HERE, ".."))89if not os.path.isdir(os.path.join(MRLYPROD, "mrlypy", "six")):10    sys.exit(f"missing mrlypy: expected it at {MRLYPROD}")1112sys.path.insert(0, MRLYPROD)1314import math15import mrlypy.two as m216import mrlypy.three as m317import mrlypy.six as m618from mrlypy.core.colors import black, blue, gradient, gray, white19from mrlypy.core.enums import Mode20import numpy as np21from colors import *22from config import DATA_DIR23from PIL import Image2425ORIENTATION = "landscape"26FRAME_SIZES = {27    "portrait": (1080, 1920),28    "landscape": (1920, 1080),29    "square": (1080, 1080),30}31IMAGE_SIZE = FRAME_SIZES[ORIENTATION]32SCALE = 103334GENERATIONS = 2535BIRTH_RULE = [2, 4, 6, 8]36SURVIVE_RULE = [2, 4, 6, 8]37TARGET = 138START = 03940KEY = "test"41os.makedirs(f"{DATA_DIR}/{KEY}", exist_ok=True)4243def save_gif(images: list[Image.Image], title: str):44    if not images:45        return46    fp = f"{DATA_DIR}/{KEY}/{title}.gif"47    images[0].save(fp, save_all=True, append_images=images[1:], duration=200, loop=0)48    print(f"Saved: {fp}")4950class Renderer:5152    def __init__(self, target_size):53        self.target_width, self.target_height = target_size5455    def render(self, cell):56        tile_img = m6.rect_draw(cell, scale=SCALE, start=START)57        ratio = math.sqrt(3) / 258        w, h = tile_img.size59        orientation = m6.get_orientation(cell.width, cell.height)60        match orientation:61            case m6.Orientation.HORIZONTAL:62                new_h = int(h * ratio)63                tile_img = tile_img.resize((w, new_h), Image.Resampling.NEAREST)64            case m6.Orientation.VERTICAL:65                new_w = int(w * ratio)66                tile_img = tile_img.resize((new_w, h), Image.Resampling.NEAREST)67        tile_arr = np.array(tile_img)68        h, w, _ = tile_arr.shape69        target_h, target_w = self.target_height, self.target_width70        if h > target_h:71            start_y = (h - target_h) // 272            tile_arr = tile_arr[start_y:start_y+target_h, :, :]73            h = target_h74        if w > target_w:75            start_x = (w - target_w) // 276            tile_arr = tile_arr[:, start_x:start_x+target_w, :]77            w = target_w78        pad_h = max(0, target_h - h)79        pad_w = max(0, target_w - w)80        pad_top = (pad_h + 1) // 281        pad_bottom = pad_h - pad_top82        pad_left = (pad_w + 1) // 283        pad_right = pad_w - pad_left84        if pad_h > 0 or pad_w > 0:85            tile_arr = np.pad(86                tile_arr,87                ((pad_top, pad_bottom), (pad_left, pad_right), (0, 0)),88                mode='reflect'89            )90        return Image.fromarray(tile_arr)9192def print_cell(cell: m2.Cell2d):93    mapping = {0: "⬜️", 1: "⬛️", 2: "🟦", 3: "🟥", 4: "🟩", 5: "🟪"}94    for row in cell.text(mapping):95        print(row)96    print()9798def new_cell():99    cell = m3.net_3d(3, 2)100    cell = m6.cut(cell)101    return cell.cell102103def new_mask():104    cell = m2.carpet_2d(3, 1)105    return cell.types106107def animate() -> list[m2.Cell2d]:108    cell = new_cell()109    mask = new_mask()110    grids = [cell.copy()]111    current_cell = cell112    for i in range(GENERATIONS):113        print(f"Generation: {i+1}")114        current_cell.neighbors(types=mask, target=TARGET, mode='constant')115        neighbors = current_cell.tags116        current_grid = current_cell.types117        birth_mask = (current_grid == 0) & (np.isin(neighbors, BIRTH_RULE))118        survive_mask = (current_grid == 1) & (np.isin(neighbors, SURVIVE_RULE))119        new_grid = np.zeros_like(current_grid)120        new_grid[birth_mask | survive_mask] = 1121        boundary_mask = (current_grid == 2)122        new_grid[boundary_mask] = 2123        current_cell.types = new_grid124        grids.append(current_cell.copy())125    return grids126127def frames(grids: list[m2.Cell2d]):128    title = "frames"129    os.makedirs(f"{DATA_DIR}/{KEY}/{title}", exist_ok=True)130    renderer = Renderer(IMAGE_SIZE)131    images = []132    gradient_colors = gradient([random_secondary(), random_secondary()], steps=5)133    mapping = {0: [white], 1: gradient_colors, 2: [alpha]}134    for i, grid in enumerate(grids):135        grid.paint(mapping, mode=Mode.RANDOM)136        img = renderer.render(grid)137        fp = f"{DATA_DIR}/{KEY}/{title}/frame_{i:02d}.png"138        img.save(fp)139        print(f"Saved: {fp}")140        images.append(img)141    save_gif(images, title)142143def heatmap(grids: list[m2.Cell2d]):144    title = "heatmap"145    os.makedirs(f"{DATA_DIR}/{KEY}/{title}", exist_ok=True)146    renderer = Renderer(IMAGE_SIZE)147    images = []148    all_types = [g.types for g in grids]149    ones_stack = [(t == 1).astype(int) for t in all_types]150    total_cumulative = np.sum(ones_stack, axis=0)151    max_val = int(np.max(total_cumulative))152    if max_val < 1: max_val = 1153    start_color = gray154    end_color = black155    gradient_colors = gradient([start_color, end_color], steps=max_val + 1)156    gradient_rgba = np.array([c.to_rgba() for c in gradient_colors], dtype=np.uint8)157    cumulative_grid = np.zeros_like(grids[0].types, dtype=np.int32)158    boundary_color = blue.to_rgba()159    background_color = white.to_rgba()160    for i, grid in enumerate(grids):161        cumulative_grid += (grid.types == 1).astype(np.int32)162        height, width = grid.height, grid.width163        colors = np.full((height, width, 4), background_color, dtype=np.uint8)164        boundary_mask = (grid.types == 2)165        colors[boundary_mask] = boundary_color166        heat_mask = (cumulative_grid > 0) & (~boundary_mask)167        heat_values = cumulative_grid[heat_mask]168        colors[heat_mask] = gradient_rgba[heat_values]169        hm_cell = grid.copy()170        hm_cell.colors = colors171        img = renderer.render(hm_cell)172        fp = f"{DATA_DIR}/{KEY}/{title}/heatmap_{i:02d}.png"173        img.save(fp)174        print(f"Saved: {fp}")175        images.append(img)176    save_gif(images, title)177178def main():179    grids = animate()180    frames(grids)181    heatmap(grids)182183def test_pad():184    cell = m3.carpet_3d(3, 1)185    cell = m6.iso(cell)186    print("ISO")187    for i in range(20):188        test_cell = cell.copy()189        test_cell = m6.pad(test_cell, k=i)190        print(f"Pad: {i}")191        print(f"Width: {test_cell.width}")192        print(f"Height: {test_cell.height}")193        print(f"Is_hex: {m6.is_hex(test_cell)}")194        print_cell(test_cell)195196def test_blank():197    orientation = "vertical"198    for i in range(1, 20):199        test_cell = m6.blank(i, orientation)200        print(f"Orientation: {orientation}")201        print(f"Radius: {i}")202        print(f"Width: {test_cell.width}")203        print(f"Height: {test_cell.height}")204        print(f"Is_hex: {m6.is_hex(test_cell)}")205        print_cell(test_cell)206207if __name__ == "__main__":208    main()