1// Conway's Game of Life on Gno — the entire board lives on-chain.2// Anyone can call Step() to tick the simulation forward, load classic3// patterns, or sculpt cells one by one.4package life56import (7 "strconv"8 "strings"9)1011const (12 MaxWidth = 5013 MaxHeight = 4014)1516var (17 width = 2018 height = 1519 cells []bool20 generation int21 population int22)2324func init() {25 initBoard(width, height)26 LoadPreset("glider")27}2829func initBoard(w, h int) {30 if w < 1 {31 w = 132 }33 if w > MaxWidth {34 w = MaxWidth35 }36 if h < 1 {37 h = 138 }39 if h > MaxHeight {40 h = MaxHeight41 }42 width = w43 height = h44 cells = make([]bool, width*height)45 generation = 046 population = 047}4849func cellIdx(x, y int) int {50 return y*width + x51}5253func alive(x, y int) bool {54 if x < 0 || x >= width || y < 0 || y >= height {55 return false56 }57 return cells[cellIdx(x, y)]58}5960func countNeighbors(x, y int) int {61 n := 062 for dy := -1; dy <= 1; dy++ {63 for dx := -1; dx <= 1; dx++ {64 if dx == 0 && dy == 0 {65 continue66 }67 if alive(x+dx, y+dy) {68 n++69 }70 }71 }72 return n73}7475// Step advances the simulation by exactly one generation.76func Step() {77 next := make([]bool, width*height)78 newPop := 079 for y := 0; y < height; y++ {80 for x := 0; x < width; x++ {81 n := countNeighbors(x, y)82 var live bool83 if alive(x, y) {84 live = n == 2 || n == 385 } else {86 live = n == 387 }88 next[cellIdx(x, y)] = live89 if live {90 newPop++91 }92 }93 }94 cells = next95 population = newPop96 generation++97}9899// MultiStep advances the simulation by n generations (capped at 100).100func MultiStep(n int) {101 if n < 1 {102 n = 1103 }104 if n > 100 {105 n = 100106 }107 for i := 0; i < n; i++ {108 Step()109 }110}111112// SetCell sets or clears a specific cell.113func SetCell(x, y int, live bool) {114 if x < 0 || x >= width || y < 0 || y >= height {115 return116 }117 i := cellIdx(x, y)118 was := cells[i]119 cells[i] = live120 if live && !was {121 population++122 } else if !live && was {123 population--124 }125}126127// Reset clears the board and resizes it to w×h.128func Reset(w, h int) {129 initBoard(w, h)130}131132// LoadPreset loads a classic Game of Life pattern onto the current board.133// Names: glider, blinker, toad, beacon, block, pulsar.134func LoadPreset(name string) {135 for i := range cells {136 cells[i] = false137 }138 generation = 0139 population = 0140141 cx := width / 2142 cy := height / 2143144 switch name {145 case "glider":146 // .O.147 // ..O148 // OOO149 SetCell(1, 0, true)150 SetCell(2, 1, true)151 SetCell(0, 2, true)152 SetCell(1, 2, true)153 SetCell(2, 2, true)154155 case "blinker":156 // OOO (period-2 oscillator)157 SetCell(cx-1, cy, true)158 SetCell(cx, cy, true)159 SetCell(cx+1, cy, true)160161 case "toad":162 // period-2 oscillator163 SetCell(cx, cy-1, true)164 SetCell(cx+1, cy-1, true)165 SetCell(cx+2, cy-1, true)166 SetCell(cx-1, cy, true)167 SetCell(cx, cy, true)168 SetCell(cx+1, cy, true)169170 case "beacon":171 // period-2 oscillator172 SetCell(cx-2, cy-2, true)173 SetCell(cx-1, cy-2, true)174 SetCell(cx-2, cy-1, true)175 SetCell(cx, cy, true)176 SetCell(cx+1, cy, true)177 SetCell(cx, cy+1, true)178 SetCell(cx+1, cy+1, true)179180 case "block":181 // 2×2 still life182 SetCell(cx, cy, true)183 SetCell(cx+1, cy, true)184 SetCell(cx, cy+1, true)185 SetCell(cx+1, cy+1, true)186187 case "pulsar":188 // period-3 oscillator (needs at least 17×17 board)189 for _, d := range []int{-4, -3, -2, 2, 3, 4} {190 SetCell(cx+d, cy-6, true)191 SetCell(cx+d, cy-1, true)192 SetCell(cx+d, cy+1, true)193 SetCell(cx+d, cy+6, true)194 SetCell(cx-6, cy+d, true)195 SetCell(cx-1, cy+d, true)196 SetCell(cx+1, cy+d, true)197 SetCell(cx+6, cy+d, true)198 }199200 default:201 LoadPreset("glider")202 }203}204205// Render returns a Markdown snapshot of the current board.206func Render(path string) string {207 out := "# Conway's Game of Life\n\n"208 out += "> A classic cellular automaton, ported to Gno — board state lives on-chain.\n\n"209 out += "**Generation:** " + strconv.Itoa(generation)210 out += " | **Population:** " + strconv.Itoa(population)211 out += " | **Board:** " + strconv.Itoa(width) + "×" + strconv.Itoa(height) + "\n\n"212213 border := "+" + strings.Repeat("-", width) + "+\n"214 out += "```\n"215 out += border216 for y := 0; y < height; y++ {217 row := "|"218 for x := 0; x < width; x++ {219 if alive(x, y) {220 row += "O"221 } else {222 row += "."223 }224 }225 row += "|\n"226 out += row227 }228 out += border229 out += "```\n\n"230231 out += "## Transactions\n\n"232 out += "| Call | Effect |\n"233 out += "|---|---|\n"234 out += "| `Step()` | Advance one generation |\n"235 out += "| `MultiStep(n)` | Advance *n* generations (max 100) |\n"236 out += "| `SetCell(x, y, alive)` | Flip a single cell |\n"237 out += "| `Reset(w, h)` | Clear and resize (max " + strconv.Itoa(MaxWidth) + "×" + strconv.Itoa(MaxHeight) + ") |\n"238 out += "| `LoadPreset(name)` | `glider` · `blinker` · `toad` · `beacon` · `block` · `pulsar` |\n\n"239240 out += "## Rules\n\n"241 out += "1. A live cell with 2 or 3 live neighbours survives.\n"242 out += "2. A dead cell with exactly 3 live neighbours becomes alive.\n"243 out += "3. All other cells die or remain dead.\n"244245 return out246}247LoadPreset(name string)
MultiStep(n int)
Render(path string) string
Reset(w int, h int)
SetCell(x int, y int, live bool)
Step()
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.