package main import ( "fmt" "math/rand" "os" tea "github.com/charmbracelet/bubbletea" ) // Create simple constants const ( ROW = 35 COL = 70 ) // Just need a grid and buffer type gol struct { grid [ROW][COL]int buffer [ROW][COL]int } // Create array that holds field's to check for neighbours var CHECK_ARRAY = [8][2]int{{-1, -1}, {-1, 0}, {-1, +1}, {0, -1}, {0, +1}, {+1, -1}, {+1, 0}, {+1, +1}} // Initialize empty game of life func initGol() gol { start := [ROW][COL]int{} // Intentionally skipping field around the array making it empty for i := 1; i < ROW-1; i++ { for j := 1; j < COL-1; j++ { if rand.Intn(2) == 1 { start[i][j] = 1 } else { start[i][j] = 0 } } } return gol{ grid: start, buffer: start, } } // Simple init func (g gol) Init() tea.Cmd { return nil } // Check neighbours func (g gol) countNeighbours(x int, y int) int { n := 0 for i := 0; i < 8; i++ { if g.grid[x+CHECK_ARRAY[i][0]][y+CHECK_ARRAY[i][1]] == 1 { n += 1 } } return n } // Life logic func (g gol) Life() tea.Model { for i := 1; i < ROW-1; i++ { for j := 1; j < COL-1; j++ { n := g.countNeighbours(i, j) if g.grid[i][j] == 1 { if n < 2 || n > 3 { g.buffer[i][j] = 0 } } else { if n == 3 { g.buffer[i][j] = 1 } } } } g.grid = g.buffer return g } // Simple update func (g gol) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { // Just quit when using ctrl+c or q case tea.KeyMsg: switch msg.String() { case "ctrl+c", "q": return g, tea.Quit case " ": g.Life() return g, nil } } return g, nil } // Let's draw this all simply func (g gol) View() string { var s string // Making sure to not draw the empty field around the array for i := 1; i < ROW-1; i++ { for j := 1; j < COL-1; j++ { if g.grid[i][j] == 1 { s += "*" } else { s += " " } } s += "\n" } return s } func main() { p := tea.NewProgram(initGol()) if _, err := p.Run(); err != nil { fmt.Printf("Oh no, an error: %v", err) os.Exit(1) } }