Scripts/life-trails.ps1

# Unique Concept: Conway's Game of Life with persistent trails showing the history of cell evolution.
# Each cell leaves a fading trail as patterns emerge, stabilize, or create gliders.


$ErrorActionPreference = 'Stop'
$esc = [char]27
$reset = "$esc[0m"

function Convert-HsvToRgb {
    param([double]$Hue, [double]$Saturation, [double]$Value)
    $h = ($Hue % 1) * 6
    $sector = [math]::Floor($h)
    $fraction = $h - $sector
    $p = $Value * (1 - $Saturation)
    $q = $Value * (1 - $fraction * $Saturation)
    $t = $Value * (1 - (1 - $fraction) * $Saturation)
    switch ($sector) {
        0 { $r = $Value; $g = $t; $b = $p }
        1 { $r = $q; $g = $Value; $b = $p }
        2 { $r = $p; $g = $Value; $b = $t }
        3 { $r = $p; $g = $q; $b = $Value }
        4 { $r = $t; $g = $p; $b = $Value }
        default { $r = $Value; $g = $p; $b = $q }
    }
    return @([int][math]::Round($r * 255), [int][math]::Round($g * 255), [int][math]::Round($b * 255))
}

$width = 90
$height = 24
$iterations = 45
$rand = [System.Random]::new(999)

# Initialize random grid
$grid = [bool[][]]::new($height)
for ($y = 0; $y -lt $height; $y++) {
    $grid[$y] = [bool[]]::new($width)
    for ($x = 0; $x -lt $width; $x++) {
        $grid[$y][$x] = $rand.NextDouble() -lt 0.25
    }
}

# Track activity history
$history = @{}

# Simulate Game of Life
for ($gen = 0; $gen -lt $iterations; $gen++) {
    $next = [bool[][]]::new($height)
    for ($y = 0; $y -lt $height; $y++) {
        $next[$y] = [bool[]]::new($width)
    }

    for ($y = 0; $y -lt $height; $y++) {
        for ($x = 0; $x -lt $width; $x++) {
            # Count neighbors
            $neighbors = 0
            for ($dy = -1; $dy -le 1; $dy++) {
                for ($dx = -1; $dx -le 1; $dx++) {
                    if ($dx -eq 0 -and $dy -eq 0) { continue }
                    $ny = ($y + $dy + $height) % $height
                    $nx = ($x + $dx + $width) % $width
                    if ($grid[$ny][$nx]) { $neighbors++ }
                }
            }

            # Apply rules
            if ($grid[$y][$x]) {
                $next[$y][$x] = ($neighbors -eq 2 -or $neighbors -eq 3)
            }
            else {
                $next[$y][$x] = ($neighbors -eq 3)
            }

            # Record history
            if ($next[$y][$x]) {
                $key = "$x,$y"
                $history[$key] = $gen
            }
        }
    }
    $grid = $next
}

# Render with trails
for ($row = $height - 1; $row -ge 0; $row--) {
    $sb = [System.Text.StringBuilder]::new()
    for ($col = 0; $col -lt $width; $col++) {
        $key = "$col,$row"

        if ($history.ContainsKey($key)) {
            $lastSeen = $history[$key] / [double]$iterations
            $isAlive = $grid[$row][$col]

            $hue = (0.3 + $lastSeen * 0.4) % 1
            $saturation = if ($isAlive) { 0.85 } else { 0.4 }
            $value = if ($isAlive) { 0.9 } else { 0.25 + 0.5 * $lastSeen }

            $rgb = Convert-HsvToRgb -Hue $hue -Saturation $saturation -Value $value
            $symbol = if ($isAlive) { '█' } else { '░' }

            $null = $sb.Append("$esc[38;2;$($rgb[0]);$($rgb[1]);$($rgb[2])m$symbol")
        }
        else {
            $null = $sb.Append("$esc[38;2;8;8;12m ")
        }
    }
    Write-Host ($sb.ToString() + $reset)
}
Write-Host $reset