"""Klassisk demoscene-eld (Doom/Amiga-stil).

Algoritm:
  1) Botten-raden eldas slumpmässigt het.
  2) Varje pixel ovanför ärver värme från en pixel under sig (med slumpmässig
     horisontell offset = vind) och kyls av lite slumpmässigt.
  3) Värmevärdet mappas via en 16-stegs eldpalett: svart -> röd -> orange -> gul -> vit-het.
"""
import machine, neopixel, time, random

PIN = 4
W, H = 8, 8
NUM = W * H
np = neopixel.NeoPixel(machine.Pin(PIN), NUM)

def xy(x, y):
    return y * W + x

# 16-stegs eldpalett (svart -> röd -> orange -> gul -> vit-het). Max ~50 per kanal.
FIRE_PALETTE = [
    (0, 0, 0),
    (4, 0, 0),
    (9, 0, 0),
    (14, 0, 0),
    (20, 0, 0),
    (26, 1, 0),
    (32, 3, 0),
    (38, 6, 0),
    (44, 10, 0),
    (48, 16, 0),
    (50, 22, 0),
    (52, 28, 0),
    (52, 34, 2),
    (54, 40, 8),
    (56, 46, 16),
    (60, 52, 28),
]

heat = bytearray(NUM)

def step():
    # Stoka botten-raderna
    for x in range(W):
        heat[xy(x, H - 1)] = random.randint(200, 255)
        heat[xy(x, H - 2)] = random.randint(140, 230)

    # Propagera uppåt med kylning + horisontell vind
    for y in range(H - 3, -1, -1):
        for x in range(W):
            src_x = x + random.randint(-1, 1)
            if src_x < 0:
                src_x = 0
            elif src_x >= W:
                src_x = W - 1
            src = heat[xy(src_x, y + 1)]
            cooling = random.randint(0, 38)
            v = src - cooling
            heat[xy(x, y)] = v if v > 0 else 0

def render():
    for i in range(NUM):
        np[i] = FIRE_PALETTE[heat[i] >> 4]
    np.write()

try:
    while True:
        step()
        render()
        time.sleep_ms(130)
except KeyboardInterrupt:
    for i in range(NUM):
        np[i] = (0, 0, 0)
    np.write()
