From 850d1026adfb0546bd0a6491ee95890257454282 Mon Sep 17 00:00:00 2001 From: Andrew Gerrand Date: Tue, 4 Jun 2013 12:59:28 +1000 Subject: [PATCH] doc: add Game of Life to playground toys R=golang-dev, dsymonds, r CC=golang-dev https://golang.org/cl/9961043 --- doc/play/life.go | 113 +++++++++++++++++++++++++++++++++++++++++++++++ doc/root.html | 1 + 2 files changed, 114 insertions(+) create mode 100644 doc/play/life.go diff --git a/doc/play/life.go b/doc/play/life.go new file mode 100644 index 0000000000..08271761c5 --- /dev/null +++ b/doc/play/life.go @@ -0,0 +1,113 @@ +// 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) + } +} diff --git a/doc/root.html b/doc/root.html index 81792671b6..e2d53414e7 100644 --- a/doc/root.html +++ b/doc/root.html @@ -31,6 +31,7 @@ Hello, 世界