Matrix/conway.go

116 lines
2.5 KiB
Go
Raw Normal View History

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
}