Last active
December 15, 2017 03:40
-
-
Save cirias/74b07083e167699ff817c1471d8416ba to your computer and use it in GitHub Desktop.
life
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
x | |
x x | |
xx xx xx | |
x x xx xx | |
xx x x xx | |
xx x x xx x x | |
x x x | |
x x | |
xx | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life | |
// > cat game.txt | go run life.go | |
package main | |
import ( | |
"bufio" | |
"fmt" | |
"io" | |
"os" | |
"time" | |
) | |
func main() { | |
m, err := newGame(os.Stdin) | |
if err != nil { | |
fmt.Println(err) | |
} | |
for { | |
draw(m) | |
m = next(m) | |
time.Sleep(500 * time.Millisecond) | |
} | |
} | |
func newGame(r io.Reader) ([][]bool, error) { | |
raw := make([][]rune, 0) | |
scanner := bufio.NewScanner(r) | |
width := 0 | |
for scanner.Scan() { | |
rs := []rune(scanner.Text()) | |
raw = append(raw, rs) | |
if len(rs) > width { | |
width = len(rs) | |
} | |
} | |
if err := scanner.Err(); err != nil { | |
return nil, fmt.Errorf("could not read matrix: %v", err) | |
} | |
m := make([][]bool, len(raw)) | |
for i, _ := range raw { | |
m[i] = make([]bool, width) | |
for j, r := range raw[i] { | |
m[i][j] = r != ' ' | |
} | |
} | |
return m, nil | |
} | |
func draw(m [][]bool) { | |
fmt.Printf("\033c\033[0;0H") | |
for _, row := range m { | |
for _, c := range row { | |
if c { | |
fmt.Print("█") | |
} else { | |
fmt.Print(" ") | |
} | |
} | |
fmt.Println() | |
} | |
} | |
func next(m [][]bool) [][]bool { | |
next := make([][]bool, len(m)) | |
for i, _ := range m { | |
next[i] = make([]bool, len(m[i])) | |
for j, _ := range m[i] { | |
next[i][j] = evolve(m, i, j) | |
} | |
} | |
return next | |
} | |
func evolve(m [][]bool, i, j int) bool { | |
neighbours := []bool{ | |
isLive(m, i-1, j-1), | |
isLive(m, i-1, j), | |
isLive(m, i-1, j+1), | |
isLive(m, i, j+1), | |
isLive(m, i+1, j+1), | |
isLive(m, i+1, j), | |
isLive(m, i+1, j-1), | |
isLive(m, i, j-1), | |
} | |
count := 0 | |
for _, n := range neighbours { | |
if n { | |
count += 1 | |
} | |
} | |
if m[i][j] { | |
return count == 2 || count == 3 | |
} else { | |
return count == 3 | |
} | |
} | |
func isLive(m [][]bool, i, j int) bool { | |
if i < 0 || i >= len(m) { | |
return false | |
} | |
if j < 0 || j >= len(m[i]) { | |
return false | |
} | |
return m[i][j] | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 reports 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 { | |
rand.Seed(time.Now().UnixNano()) | |
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 = []byte("█") | |
} | |
buf.Write(b) | |
} | |
buf.WriteByte('\n') | |
} | |
return buf.String() | |
} | |
func main() { | |
l := NewLife(40, 15) | |
for { | |
l.Step() | |
fmt.Print("\x0c\033[0;0H", l) // Clear screen and print field. | |
time.Sleep(time.Second / 30) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment