patterns — automaton

generator verdict no votes yet
source — auto/a009_automaton.g59_automaton
def g59_automaton(rng):
    """AUTOMATON — a random binary field run under a majority-of-8 rule, ties at
    the threshold holding their previous value, until it locks into a fixed
    point or a short cycle. Settled cells shade by their final neighbour count,
    so blob interiors run dark and their margins stay light; the starting
    density is redrawn, still from the same seed, until the settled field
    occupies a workable fraction of the canvas."""
    N = rng.choice([32, 40, 50])
    c = Canvas(N, "GLYPH", "glyph", "automaton", tiles=True)
    NX, NY = c.cols, c.rows
    lo = 4

    def settle(density):
        grid = [[1 if rng.random() < density else 0 for _ in range(NY)] for _ in range(NX)]
        seen = []
        for step in range(60):
            cnt = [[0] * NY for _ in range(NX)]
            for i in range(NX):
                for j in range(NY):
                    n = 0
                    for di in (-1, 0, 1):
                        for dj in (-1, 0, 1):
                            if di == 0 and dj == 0:
                                continue
                            ii, jj = (i + di) % NX, (j + dj) % NY
                            if grid[ii][jj]:
                                n += 1
                    cnt[i][j] = n
            nxt = [[0] * NY for _ in range(NX)]
            for i in range(NX):
                for j in range(NY):
                    n = cnt[i][j]
                    if n > lo:
                        nxt[i][j] = 1
                    elif n == lo:
                        nxt[i][j] = grid[i][j]
                    else:
                        nxt[i][j] = 0
            changed = any(nxt[i][j] != grid[i][j] for i in range(NX) for j in range(NY))
            grid = nxt
            if not changed:
                break
            key = tuple(tuple(row) for row in grid)
            if key in seen:
                break
            seen.append(key)
            if len(seen) > 3:
                seen.pop(0)
        cnt = [[0] * NY for _ in range(NX)]
        alive = 0
        for i in range(NX):
            for j in range(NY):
                n = 0
                for di in (-1, 0, 1):
                    for dj in (-1, 0, 1):
                        if di == 0 and dj == 0:
                            continue
                        ii, jj = (i + di) % NX, (j + dj) % NY
                        if grid[ii][jj]:
                            n += 1
                cnt[i][j] = n
                alive += grid[i][j]
        return grid, cnt, alive / (NX * NY)

    best = None
    for _ in range(14):
        density = rng.uniform(0.30, 0.62)
        grid, cnt, frac = settle(density)
        score = abs(frac - 0.45)
        if best is None or score < best[0]:
            best = (score, grid, cnt, frac)
        if 0.32 <= frac <= 0.58:
            break
    _, grid, cnt, frac = best

    R = ramp(["x", "o", "#", "@", "▒", "▀", "▓", "█"])
    lo_i, hi_i = 0, len(R) - 1
    for i in range(NX):
        for j in range(NY):
            if not grid[i][j]:
                continue
            t = clamp((cnt[i][j] - lo) / (8 - lo))
            idx = lo_i + int(round(t * (hi_i - lo_i)))
            c.glyph(i, j, R[idx])
    return c
automaton-glyph-50-e406
automaton-glyph-50-e406
automaton-glyph-50-dd97
automaton-glyph-50-dd97
automaton-glyph-50-4011
automaton-glyph-50-4011

generator