2025-03-01 14:53:42 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2025-03-01 20:50:51 +01:00
|
|
|
"math/rand"
|
|
|
|
"os"
|
|
|
|
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Create simple constants
|
|
|
|
const (
|
|
|
|
ROW = 35
|
|
|
|
COL = 70
|
2025-03-01 14:53:42 +01:00
|
|
|
)
|
|
|
|
|
2025-03-01 20:50:51 +01:00
|
|
|
// Just need a grid and buffer
|
|
|
|
type gol struct {
|
|
|
|
grid [ROW][COL]int
|
|
|
|
buffer [ROW][COL]int
|
|
|
|
}
|
|
|
|
|
2025-03-03 11:48:02 +01:00
|
|
|
// 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}}
|
|
|
|
|
2025-03-01 20:50:51 +01:00
|
|
|
// Initialize empty game of life
|
|
|
|
func initGol() gol {
|
|
|
|
start := [ROW][COL]int{}
|
2025-03-03 10:59:17 +01:00
|
|
|
// Intentionally skipping field around the array making it empty
|
|
|
|
for i := 1; i < ROW-1; i++ {
|
|
|
|
for j := 1; j < COL-1; j++ {
|
2025-03-01 20:50:51 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2025-03-03 11:48:02 +01:00
|
|
|
// Check neighobours
|
|
|
|
func (g gol) countNeighbours(x int, y int) int {
|
|
|
|
n := 0
|
|
|
|
for i := 0; i < 8; i++ {
|
|
|
|
if g.buffer[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.buffer[i][j] == 1 {
|
|
|
|
if n < 2 || n > 3 {
|
|
|
|
g.buffer[i][j] = 0
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if n == 3 {
|
|
|
|
g.buffer[i][j] = 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return g
|
|
|
|
}
|
|
|
|
|
2025-03-01 20:50:51 +01:00
|
|
|
// 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
|
2025-03-03 11:48:02 +01:00
|
|
|
case " ":
|
|
|
|
g.Life()
|
|
|
|
return g, nil
|
2025-03-01 20:50:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return g, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Let's draw this all simply
|
|
|
|
func (g gol) View() string {
|
|
|
|
var s string
|
2025-03-03 10:59:17 +01:00
|
|
|
// 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++ {
|
2025-03-01 20:50:51 +01:00
|
|
|
if g.grid[i][j] == 1 {
|
|
|
|
s += "*"
|
|
|
|
} else {
|
|
|
|
s += " "
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s += "\n"
|
|
|
|
}
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2025-03-01 14:53:42 +01:00
|
|
|
func main() {
|
2025-03-01 20:50:51 +01:00
|
|
|
p := tea.NewProgram(initGol())
|
|
|
|
if _, err := p.Run(); err != nil {
|
|
|
|
fmt.Printf("Oh no, an error: %v", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2025-03-01 14:53:42 +01:00
|
|
|
}
|