Home | History | Annotate | Download | only in play
      1 // An implementation of Conway's Game of Life.
      2 package main
      3 
      4 import (
      5 	"bytes"
      6 	"fmt"
      7 	"math/rand"
      8 	"time"
      9 )
     10 
     11 // Field represents a two-dimensional field of cells.
     12 type Field struct {
     13 	s    [][]bool
     14 	w, h int
     15 }
     16 
     17 // NewField returns an empty field of the specified width and height.
     18 func NewField(w, h int) *Field {
     19 	s := make([][]bool, h)
     20 	for i := range s {
     21 		s[i] = make([]bool, w)
     22 	}
     23 	return &Field{s: s, w: w, h: h}
     24 }
     25 
     26 // Set sets the state of the specified cell to the given value.
     27 func (f *Field) Set(x, y int, b bool) {
     28 	f.s[y][x] = b
     29 }
     30 
     31 // Alive reports whether the specified cell is alive.
     32 // If the x or y coordinates are outside the field boundaries they are wrapped
     33 // toroidally. For instance, an x value of -1 is treated as width-1.
     34 func (f *Field) Alive(x, y int) bool {
     35 	x += f.w
     36 	x %= f.w
     37 	y += f.h
     38 	y %= f.h
     39 	return f.s[y][x]
     40 }
     41 
     42 // Next returns the state of the specified cell at the next time step.
     43 func (f *Field) Next(x, y int) bool {
     44 	// Count the adjacent cells that are alive.
     45 	alive := 0
     46 	for i := -1; i <= 1; i++ {
     47 		for j := -1; j <= 1; j++ {
     48 			if (j != 0 || i != 0) && f.Alive(x+i, y+j) {
     49 				alive++
     50 			}
     51 		}
     52 	}
     53 	// Return next state according to the game rules:
     54 	//   exactly 3 neighbors: on,
     55 	//   exactly 2 neighbors: maintain current state,
     56 	//   otherwise: off.
     57 	return alive == 3 || alive == 2 && f.Alive(x, y)
     58 }
     59 
     60 // Life stores the state of a round of Conway's Game of Life.
     61 type Life struct {
     62 	a, b *Field
     63 	w, h int
     64 }
     65 
     66 // NewLife returns a new Life game state with a random initial state.
     67 func NewLife(w, h int) *Life {
     68 	a := NewField(w, h)
     69 	for i := 0; i < (w * h / 4); i++ {
     70 		a.Set(rand.Intn(w), rand.Intn(h), true)
     71 	}
     72 	return &Life{
     73 		a: a, b: NewField(w, h),
     74 		w: w, h: h,
     75 	}
     76 }
     77 
     78 // Step advances the game by one instant, recomputing and updating all cells.
     79 func (l *Life) Step() {
     80 	// Update the state of the next field (b) from the current field (a).
     81 	for y := 0; y < l.h; y++ {
     82 		for x := 0; x < l.w; x++ {
     83 			l.b.Set(x, y, l.a.Next(x, y))
     84 		}
     85 	}
     86 	// Swap fields a and b.
     87 	l.a, l.b = l.b, l.a
     88 }
     89 
     90 // String returns the game board as a string.
     91 func (l *Life) String() string {
     92 	var buf bytes.Buffer
     93 	for y := 0; y < l.h; y++ {
     94 		for x := 0; x < l.w; x++ {
     95 			b := byte(' ')
     96 			if l.a.Alive(x, y) {
     97 				b = '*'
     98 			}
     99 			buf.WriteByte(b)
    100 		}
    101 		buf.WriteByte('\n')
    102 	}
    103 	return buf.String()
    104 }
    105 
    106 func main() {
    107 	l := NewLife(40, 15)
    108 	for i := 0; i < 300; i++ {
    109 		l.Step()
    110 		fmt.Print("\x0c", l) // Clear screen and print field.
    111 		time.Sleep(time.Second / 30)
    112 	}
    113 }
    114