// 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 returns 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) } }