You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

116 lines
2.5 KiB

// SPDX-License-Identifier: Unlicense OR MIT
package main
import (
"image"
"math/rand"
)
// Board implements game of life logic.
type Board struct {
// Size is the count of cells in a particular dimension.
Size image.Point
// Cells contains the alive or dead cells.
Cells []byte
// buffer is used to avoid reallocating a new cells
// slice for every update.
buffer []byte
}
// NewBoard returns a new game of life with the defined size.
func NewBoard(size image.Point) *Board {
return &Board{
Size: size,
Cells: make([]byte, size.X*size.Y),
buffer: make([]byte, size.X*size.Y),
}
}
// Randomize randomizes each cell state.
func (b *Board) Randomize() {
rand.Read(b.Cells)
for i, v := range b.Cells {
if v < 0x30 {
b.Cells[i] = 1
} else {
b.Cells[i] = 0
}
}
}
// Pt returns the coordinate given a index in b.Cells.
func (b *Board) Pt(i int) image.Point {
x, y := i%b.Size.X, i/b.Size.Y
return image.Point{X: x, Y: y}
}
// At returns the b.Cells index, given a wrapped coordinate.
func (b *Board) At(c image.Point) int {
if c.X < 0 {
c.X += b.Size.X
}
if c.X >= b.Size.X {
c.X -= b.Size.X
}
if c.Y < 0 {
c.Y += b.Size.Y
}
if c.Y >= b.Size.Y {
c.Y -= b.Size.Y
}
return b.Size.Y*c.Y + c.X
}
// SetWithoutWrap sets a cell to alive.
func (b *Board) SetWithoutWrap(c image.Point) {
if !c.In(image.Rectangle{Max: b.Size}) {
return
}
b.Cells[b.At(c)] = 1
}
// Advance advances the board state by 1.
func (b *Board) Advance() {
next, cur := b.buffer, b.Cells
defer func() { b.Cells, b.buffer = next, cur }()
for i := range next {
next[i] = 0
}
for y := 0; y < b.Size.Y; y++ {
for x := 0; x < b.Size.X; x++ {
var t byte
t += cur[b.At(image.Pt(x-1, y-1))]
t += cur[b.At(image.Pt(x+0, y-1))]
t += cur[b.At(image.Pt(x+1, y-1))]
t += cur[b.At(image.Pt(x-1, y+0))]
t += cur[b.At(image.Pt(x+1, y+0))]
t += cur[b.At(image.Pt(x-1, y+1))]
t += cur[b.At(image.Pt(x+0, y+1))]
t += cur[b.At(image.Pt(x+1, y+1))]
// Any live cell with fewer than two live neighbours dies, as if by underpopulation.
// Any live cell with two or three live neighbours lives on to the next generation.
// Any live cell with more than three live neighbours dies, as if by overpopulation.
// Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
p := b.At(image.Pt(x, y))
switch {
case t < 2:
t = 0
case t == 2:
t = cur[p]
case t == 3:
t = 1
case t > 3:
t = 0
}
next[p] = t
}
}
}