github.com/hlts2/go@v0.0.0-20170904000733-812b34efaed8/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  }