Files
latchd/legacy/go/tui/bg/automata.go
Zane Walker f2a885d621 refactor: split the TUI and refresh project docs
Move login state, authentication, discovery, input, rendering, and tests into focused TUI modules. Add detailed CLI error reports with recovery commands, align the build and host-check documentation, and keep the legacy Go tree outside normal builds.

Checks: make check; make integration-test; go test ./... (legacy/go).
2026-10-05 21:37:15 +05:30

122 lines
2.0 KiB
Go

// Pack two cell rows into each Unicode half-block row; two buffers keep frame
// updates allocation-free.
package bg
import (
"math/rand"
"strings"
)
const (
fgAlive = "\x1b[38;2;255;127;0m"
fgDead = "\x1b[38;2;30;30;30m"
bgAlive = "\x1b[48;2;255;127;0m"
bgDead = "\x1b[48;2;30;30;30m"
reset = "\x1b[0m"
block = "▀"
)
type Automata struct {
w, h int
a, b []uint8
useA bool
buf strings.Builder
}
func NewAutomata(w, h int) *Automata {
a := &Automata{useA: true}
a.Resize(w, h)
return a
}
func (a *Automata) Resize(w, h int) {
logH := h * 2
if a.w == w && a.h == logH {
return
}
a.w = w
a.h = logH
sz := w * logH
a.a = make([]uint8, sz)
a.b = make([]uint8, sz)
for i := 0; i < sz; i++ {
if rand.Float32() > 0.85 {
a.a[i] = 1
}
}
a.buf.Grow(w * h * 40)
}
func (a *Automata) Tick() {
src, dst := a.a, a.b
if !a.useA {
src, dst = a.b, a.a
}
w, h := a.w, a.h
for y := range h {
for x := range w {
i := y*w + x
alive := src[i]
n := a.count(src, x, y, w, h)
if alive == 1 && (n == 2 || n == 3) {
dst[i] = 1
} else if alive == 0 && n == 3 {
dst[i] = 1
} else {
dst[i] = 0
}
}
}
a.useA = !a.useA
}
func (a *Automata) count(g []uint8, x, y, w, h int) uint8 {
var c uint8
for dy := -1; dy <= 1; dy++ {
for dx := -1; dx <= 1; dx++ {
if dx == 0 && dy == 0 {
continue
}
nx, ny := x+dx, y+dy
if nx >= 0 && nx < w && ny >= 0 && ny < h {
c += g[ny*w+nx]
}
}
}
return c
}
func (a *Automata) Frame() string {
a.buf.Reset()
src := a.a
if !a.useA {
src = a.b
}
w, h := a.w, a.h
for y := 0; y < h; y += 2 {
for x := range w {
top := src[y*w+x]
bot := uint8(0)
if y+1 < h {
bot = src[(y+1)*w+x]
}
if top == 1 {
a.buf.WriteString(fgAlive)
} else {
a.buf.WriteString(fgDead)
}
if bot == 1 {
a.buf.WriteString(bgAlive)
} else {
a.buf.WriteString(bgDead)
}
a.buf.WriteString(block)
}
a.buf.WriteString(reset)
if y < h-2 {
a.buf.WriteByte('\n')
}
}
return a.buf.String()
}