Skip to content

Instantly share code, notes, and snippets.

@cirias
Last active December 15, 2017 03:40
Show Gist options
  • Save cirias/74b07083e167699ff817c1471d8416ba to your computer and use it in GitHub Desktop.
Save cirias/74b07083e167699ff817c1471d8416ba to your computer and use it in GitHub Desktop.
life
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
// 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]
}
// 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