Last active
October 18, 2017 00:40
-
-
Save quillaja/5601d2c78d80fb1ab50712988751f40f to your computer and use it in GitHub Desktop.
Conway's game of life in go.
This file contains 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
package main | |
import ( | |
"flag" | |
"fmt" | |
"time" | |
) | |
// Board is a set of points that are 'alive' | |
type Board map[Point]bool | |
// Point is a x,y coord on the Board | |
type Point struct { | |
X int | |
Y int | |
} | |
// Neighbors produces a slice of the nearby cells to Point p. | |
func Neighbors(p Point) []Point { | |
pts := []Point{} | |
for x := -1; x <= 1; x++ { | |
for y := -1; y <= 1; y++ { | |
pts = append(pts, Point{X: p.X + x, Y: p.Y + y}) | |
} | |
} | |
// have to remove the point at index 4, since it is the | |
// original point itself. | |
return append(pts[:4], pts[5:]...) | |
} | |
// Advance applies the rules to the given Board and produces a new | |
// Board that is the next generation. | |
func Advance(b Board) Board { | |
next := make(Board) | |
// make a set of cells to check, initialize with all current cells | |
check := make(Board) | |
for p := range b { | |
check[p] = true | |
} | |
// put all neighbors of actives in check | |
for p := range b { | |
for _, n := range Neighbors(p) { | |
check[n] = true | |
} | |
} | |
//apply rules to all cells to check | |
for p := range check { | |
// find number of p's neighbors which are alive in current generation | |
count := 0 | |
for _, n := range Neighbors(p) { | |
if b[n] { | |
count++ | |
} | |
} | |
// 3 neighbors-> live or spawn; 2 and currently alive-> live | |
if count == 3 || (b[p] && count == 2) { | |
next[p] = true | |
} | |
} | |
return next | |
} | |
// Show displays a Board on the screen | |
func Show(b Board) { | |
// figure out extents to draw | |
high := Point{} | |
low := Point{} | |
for p := range b { | |
if p.X > high.X { | |
high.X = p.X | |
} | |
if p.X < low.X { | |
low.X = p.X | |
} | |
if p.Y > high.Y { | |
high.Y = p.Y | |
} | |
if p.Y < low.Y { | |
low.Y = p.Y | |
} | |
} | |
fmt.Println("\033[2J\033[0;0H") // clear screen & move cursor to 0,0 | |
var output string // faster than multiple prints to screen | |
for y := low.Y - 1; y < high.Y+2; y++ { | |
for x := low.X - 1; x < high.X+2; x++ { | |
if b[Point{x, y}] { | |
output += "\u2588" | |
} else { | |
output += "\u2591" | |
} | |
} | |
output += fmt.Sprintf(" %d\n", y) | |
} | |
fmt.Println(output) // display all at once w/ blank line on end | |
} | |
// Animate performs the given number of `iterations` on the initial `board` | |
// and displays it on the screen each time, `pause`ing between each. It does | |
// not alter `board`. | |
func Animate(board Board, iterations int, pause time.Duration) { | |
b := make(Board) | |
for p := range board { | |
b[p] = true | |
} | |
for ; iterations > 0; iterations-- { | |
Show(b) | |
b = Advance(b) | |
time.Sleep(pause) | |
} | |
} | |
// demos the stuff | |
func main() { | |
var pat = flag.String("p", "blinker", "name of an initial pattern.") | |
var iter = flag.Int("i", 100, "number of iterations to run") | |
var pause = flag.Duration("w", 500, "duration to wait between each iteration") | |
flag.Parse() | |
patterns := map[string]Board{ | |
// Blinker | |
"blinker": Board{ | |
Point{0, 0}: true, | |
Point{0, 1}: true, | |
Point{0, 2}: true, | |
}, | |
// R-Pentomino | |
"rpentomino": Board{ | |
Point{1, 0}: true, | |
Point{2, 0}: true, | |
Point{0, 1}: true, | |
Point{1, 1}: true, | |
Point{1, 2}: true, | |
}, | |
// Acorn | |
"acorn": Board{ | |
Point{1, 0}: true, | |
Point{3, 1}: true, | |
Point{0, 2}: true, | |
Point{1, 2}: true, | |
Point{4, 2}: true, | |
Point{5, 2}: true, | |
Point{6, 2}: true, | |
}, | |
} | |
// do one | |
Animate(patterns[*pat], *iter, *pause) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment