forked from go/golangs_learn
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
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
|
|
}
|
|
}
|
|
}
|
|
|