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.
114 lines
2.4 KiB
114 lines
2.4 KiB
// An implementation of Conway's Game of Life.
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math/rand"
|
|
"time"
|
|
)
|
|
|
|
// Field represents a two-dimensional field of cells.
|
|
type Field struct {
|
|
s [][]bool
|
|
w, h int
|
|
}
|
|
|
|
// NewField returns an empty field of the specified width and height.
|
|
func NewField(w, h int) *Field {
|
|
s := make([][]bool, h)
|
|
for i := range s {
|
|
s[i] = make([]bool, w)
|
|
}
|
|
return &Field{s: s, w: w, h: h}
|
|
}
|
|
|
|
// Set sets the state of the specified cell to the given value.
|
|
func (f *Field) Set(x, y int, b bool) {
|
|
f.s[y][x] = b
|
|
}
|
|
|
|
// Alive reports whether the specified cell is alive.
|
|
// If the x or y coordinates are outside the field boundaries they are wrapped
|
|
// toroidally. For instance, an x value of -1 is treated as width-1.
|
|
func (f *Field) Alive(x, y int) bool {
|
|
x += f.w
|
|
x %= f.w
|
|
y += f.h
|
|
y %= f.h
|
|
return f.s[y][x]
|
|
}
|
|
|
|
// Next returns the state of the specified cell at the next time step.
|
|
func (f *Field) Next(x, y int) bool {
|
|
// Count the adjacent cells that are alive.
|
|
alive := 0
|
|
for i := -1; i <= 1; i++ {
|
|
for j := -1; j <= 1; j++ {
|
|
if (j != 0 || i != 0) && f.Alive(x+i, y+j) {
|
|
alive++
|
|
}
|
|
}
|
|
}
|
|
// Return next state according to the game rules:
|
|
// exactly 3 neighbors: on,
|
|
// exactly 2 neighbors: maintain current state,
|
|
// otherwise: off.
|
|
return alive == 3 || alive == 2 && f.Alive(x, y)
|
|
}
|
|
|
|
// Life stores the state of a round of Conway's Game of Life.
|
|
type Life struct {
|
|
a, b *Field
|
|
w, h int
|
|
}
|
|
|
|
// NewLife returns a new Life game state with a random initial state.
|
|
func NewLife(w, h int) *Life {
|
|
a := NewField(w, h)
|
|
for i := 0; i < (w * h / 4); i++ {
|
|
a.Set(rand.Intn(w), rand.Intn(h), true)
|
|
}
|
|
return &Life{
|
|
a: a, b: NewField(w, h),
|
|
w: w, h: h,
|
|
}
|
|
}
|
|
|
|
// Step advances the game by one instant, recomputing and updating all cells.
|
|
func (l *Life) Step() {
|
|
// Update the state of the next field (b) from the current field (a).
|
|
for y := 0; y < l.h; y++ {
|
|
for x := 0; x < l.w; x++ {
|
|
l.b.Set(x, y, l.a.Next(x, y))
|
|
}
|
|
}
|
|
// Swap fields a and b.
|
|
l.a, l.b = l.b, l.a
|
|
}
|
|
|
|
// String returns the game board as a string.
|
|
func (l *Life) String() string {
|
|
var buf bytes.Buffer
|
|
for y := 0; y < l.h; y++ {
|
|
for x := 0; x < l.w; x++ {
|
|
b := byte(' ')
|
|
if l.a.Alive(x, y) {
|
|
b = '*'
|
|
}
|
|
buf.WriteByte(b)
|
|
}
|
|
buf.WriteByte('\n')
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
func main() {
|
|
l := NewLife(40, 15)
|
|
for i := 0; i < 300; i++ {
|
|
l.Step()
|
|
fmt.Print("\x0c", l) // Clear screen and print field.
|
|
time.Sleep(time.Second / 30)
|
|
}
|
|
}
|