2024-04-12 13:14:33 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-04-14 19:20:26 +00:00
|
|
|
"errors"
|
2024-04-14 20:01:42 +00:00
|
|
|
"math/rand"
|
2024-04-14 19:20:26 +00:00
|
|
|
"time"
|
2024-04-12 13:14:33 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Cell struct {
|
2024-04-17 06:11:23 +00:00
|
|
|
x uint
|
|
|
|
y uint
|
2024-04-12 13:14:33 +00:00
|
|
|
live bool
|
|
|
|
}
|
|
|
|
|
2024-04-17 06:11:23 +00:00
|
|
|
func (c *Cell) NeighborCount(arr [][]Cell) uint {
|
2024-04-14 23:26:37 +00:00
|
|
|
// Read grid dimensions
|
2024-04-17 06:11:23 +00:00
|
|
|
width := uint(len(arr))
|
|
|
|
height := uint(len(arr[0]))
|
2024-04-14 23:26:37 +00:00
|
|
|
|
2024-04-17 06:11:23 +00:00
|
|
|
count := uint(0)
|
2024-04-14 19:20:26 +00:00
|
|
|
// Iterate through 3x3 neighboring grid
|
|
|
|
for i := -1; i <= 1; i++ {
|
|
|
|
for j := -1; j <= 1; j++ {
|
|
|
|
// Ignore own position
|
|
|
|
if i == 0 && j == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Get neighbor coordinates
|
2024-04-17 06:11:23 +00:00
|
|
|
nx := uint(int(c.x+width)+i) % width
|
|
|
|
ny := uint(int(c.y+height)+j) % height
|
2024-04-14 19:20:26 +00:00
|
|
|
// Count if neighbor is alive
|
|
|
|
if arr[nx][ny].live {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count
|
2024-04-12 13:14:33 +00:00
|
|
|
}
|
|
|
|
|
2024-04-17 06:11:23 +00:00
|
|
|
func initGrid(width, height uint, parent ...[][]Cell) ([][]Cell, error) {
|
2024-04-14 19:20:26 +00:00
|
|
|
exists := len(parent) == 1
|
|
|
|
if exists && len(parent) > 1 {
|
|
|
|
return nil, errors.New("Too many parents specified")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make first dimension
|
|
|
|
cells := make([][]Cell, width)
|
2024-04-17 06:11:23 +00:00
|
|
|
for i := uint(0); i < width; i++ {
|
2024-04-14 19:20:26 +00:00
|
|
|
// Make second dimension
|
|
|
|
cells[i] = make([]Cell, height)
|
2024-04-17 06:11:23 +00:00
|
|
|
for j := uint(0); j < height; j++ {
|
2024-04-14 19:20:26 +00:00
|
|
|
// Make cells
|
|
|
|
cells[i][j].x = i
|
|
|
|
cells[i][j].y = j
|
|
|
|
// If specified, copy state from parent
|
|
|
|
if exists {
|
|
|
|
cells[i][j].live = parent[0][i][j].live
|
2024-04-14 20:01:42 +00:00
|
|
|
} else {
|
|
|
|
cells[i][j].live = rand.Intn(2) == 1
|
2024-04-14 19:20:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cells, nil
|
2024-04-12 13:14:33 +00:00
|
|
|
}
|
|
|
|
|
2024-04-17 06:11:23 +00:00
|
|
|
func setup(callback func([][]Cell), scale, width, height uint, FPS time.Duration) chan bool {
|
|
|
|
// Initialize grid at specified scale
|
|
|
|
grid, _ := initGrid(width/scale, height/scale)
|
2024-04-12 13:14:33 +00:00
|
|
|
|
2024-04-14 19:29:40 +00:00
|
|
|
// Prepare ticker and finishing flag
|
2024-04-14 20:00:37 +00:00
|
|
|
ticker := time.NewTicker((1000 / FPS) * time.Millisecond)
|
2024-04-14 19:20:26 +00:00
|
|
|
done := make(chan bool)
|
|
|
|
|
2024-04-14 19:29:40 +00:00
|
|
|
// Run game loop
|
2024-04-14 19:20:26 +00:00
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
case <-ticker.C:
|
2024-04-14 22:55:34 +00:00
|
|
|
grid = draw(grid, callback)
|
2024-04-14 19:20:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2024-04-14 19:29:40 +00:00
|
|
|
|
|
|
|
// Return flag to be stopped outside
|
|
|
|
return done
|
2024-04-14 19:20:26 +00:00
|
|
|
}
|
2024-04-12 13:14:33 +00:00
|
|
|
|
2024-04-14 22:55:34 +00:00
|
|
|
func draw(grid [][]Cell, callback func([][]Cell)) [][]Cell {
|
2024-04-17 06:11:23 +00:00
|
|
|
width := uint(len(grid))
|
|
|
|
height := uint(len(grid[0]))
|
|
|
|
generation, _ := initGrid(width, height, grid)
|
|
|
|
|
2024-04-14 19:20:26 +00:00
|
|
|
// Iterate through grid
|
2024-04-17 06:11:23 +00:00
|
|
|
for i := uint(0); i < width; i++ {
|
|
|
|
for j := uint(0); j < height; j++ {
|
2024-04-14 19:20:26 +00:00
|
|
|
// Count neighbors
|
|
|
|
cout := grid[i][j].NeighborCount(grid)
|
|
|
|
// Get state
|
|
|
|
curr := grid[i][j].live
|
|
|
|
// Apply rules
|
|
|
|
died := curr && (cout < 2 || cout > 3)
|
|
|
|
born := !curr && cout == 3
|
|
|
|
next := curr && !died || born
|
|
|
|
// Set state
|
|
|
|
generation[i][j].live = next
|
|
|
|
// A * !(A * (B + C)) + !A * D
|
|
|
|
// = A * !B * !C + !A * D
|
|
|
|
// curr && cout >= 2 && cout <= 3 || !curr && cout == 3
|
|
|
|
}
|
|
|
|
}
|
|
|
|
callback(generation)
|
2024-04-14 22:55:34 +00:00
|
|
|
return generation
|
2024-04-12 13:14:33 +00:00
|
|
|
}
|