github.com/likebike/go--@v0.0.0-20190911215757-0bd925d16e96/go/doc/play/life.go (about) 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 }