Skip to content

Instantly share code, notes, and snippets.

@manveru
Created May 1, 2010 12:40
Show Gist options
  • Select an option

  • Save manveru/386299 to your computer and use it in GitHub Desktop.

Select an option

Save manveru/386299 to your computer and use it in GitHub Desktop.
Conway's Game of Life in Go
// Conway's Game of Life.
package main
import (
"sdl"
"rand"
"time"
"strings"
"strconv"
"flag"
)
// all the flags
var Rule *string = flag.String("rule", "B3/S23", "Rule")
var aliveColor *string = flag.String("alive-color", "ffffff", "Color of alive cells")
var deadColor *string = flag.String("dead-color", "000000", "Color of dead cells")
var worldWidth *int = flag.Int("width", 160, "Amount of horizontal cells")
var worldHeight *int = flag.Int("height", 120, "Amount of vertical cells")
var delay *int = flag.Int("delay", 25, "milliseconds between iterations")
var scale *int = flag.Int("scale", 4, "scale world by factor N")
// setting up variables used across most functions
var (
AliveColor, DeadColor, Delay uint32
Cells *[][]bool
Paused, Running bool
Rects *[][]*sdl.Rect
Screen *sdl.Surface
Height, Scale, Width int
)
var rule = map[bool]map[int]bool{
true: map[int]bool{}, false: map[int]bool{},
}
func init() {
flag.Parse()
Delay = uint32(*delay)
Scale = *scale
color, err := strconv.Btoui64(*aliveColor, 16)
if err == nil {
AliveColor = uint32(color)
} else {
panic(err)
}
color, err = strconv.Btoui64(*deadColor, 16)
if err == nil {
DeadColor = uint32(color)
} else {
panic(err)
}
Width = *worldWidth
Height = *worldHeight
for _, part := range strings.Split(*Rule, "/", 2) {
name, rest := string(part[0]), part[1:]
for _, s := range strings.Split(rest, "", 0) {
n, err := strconv.Atoi(s)
if err == nil {
if name == "B" { // born
rule[false][n] = true
} else if name == "S" { // survive
rule[true][n] = true
}
} else {
panic(err)
}
}
}
}
func main() {
if sdl.Init(sdl.INIT_EVERYTHING) != 0 {
panic(sdl.GetError())
}
rand.Seed(time.Nanoseconds())
Screen = NewSurface(Width*Scale, Height*Scale)
Setup()
HandleEvents()
defer sdl.Quit()
}
func Setup() {
rectsp, cellsp := MakeRects(), MakeCells()
rects, cells := *rectsp, *cellsp
for x, l := range cells {
for y, _ := range l {
rx, ry := x*Scale, y*Scale
wh := uint16(Scale)
rect := &sdl.Rect{X: int16(rx), Y: int16(ry), W: wh, H: wh}
rects[x][y] = rect
// uncomment to seed the game field with random life
// if rand.Float() > 0.95 { cells[x][y] = true }
}
}
Rects = rectsp
Cells = cellsp
// AddAcorn(100, 100)
// AddGlider(Width/2, Height/2)
// AddDiehard(100, 100)
DrawCells()
Running = true
Paused = true
}
func HandleEvents() {
ch := make(chan func(), 10)
for Running {
e := &sdl.Event{}
for e.Poll() {
switch e.Type {
case sdl.QUIT:
{
Running = false
}
case sdl.KEYDOWN:
{
switch sdl.GetKeyName(sdl.Key(e.Keyboard().Keysym.Sym)) {
case "space":
TogglePause(ch)
}
}
case sdl.MOUSEBUTTONDOWN:
{
button := e.MouseButton()
x, y := int(button.X), int(button.Y)
switch button.Button {
case 1:
queue(ch, func() { ToggleCell(x/Scale, y/Scale) })
case 2:
queue(ch, func() { AddAcorn(x/Scale, y/Scale) })
case 3:
queue(ch, func() { AddGlider(x/Scale, y/Scale) })
}
}
}
}
sdl.Delay(25)
}
}
func queue(ch chan func(), fun func()) {
if !Running { return }
if Paused {
fun()
DrawCells()
} else {
ch <- fun
}
}
// Add Glider:
//
// OO
// O O
// O
func AddGlider(x, y int) {
cells := *Cells
cells[x+2][y+2] = true
cells[x+2][y+3] = true
cells[x+3][y+2] = true
cells[x+4][y+2] = true
cells[x+3][y+4] = true
}
// Add Acorn:
// 0 1 2 3 4 5 6
// 0 O
// 1 O
// 2 O O O O O
func AddAcorn(x, y int) {
cells := *Cells
cells[x+1][y+0] = true
cells[x+3][y+1] = true
cells[x+0][y+2] = true
cells[x+1][y+2] = true
cells[x+4][y+2] = true
cells[x+5][y+2] = true
cells[x+6][y+2] = true
}
// Add Diehard:
// y
// |
//x - + 0 1 2 3 4 5 6 7
// 0 O
// 1 O O
// 2 O O O O
func AddDiehard(x, y int) {
cells := *Cells
cells[x+0][y+1] = true
cells[x+1][y+1] = true
cells[x+1][y+2] = true
cells[x+5][y+2] = true
cells[x+6][y+0] = true
cells[x+6][y+2] = true
cells[x+7][y+2] = true
}
func TogglePause(ch chan func()) {
if Paused {
Paused = false
go RefreshCells(ch)
} else {
Paused = true
}
}
func ToggleCell(x, y int) {
(*Cells)[x][y] = !(*Cells)[x][y]
}
// Wrap around world edges.
func check(x, y int) int {
cells := *Cells
// force x into world boundaries
if x >= 0 {
if len(cells) > x {
// ok
} else {
x = len(cells) - x
}
} else {
x = len(cells) + x
}
line := cells[x]
if y >= 0 {
if len(line) > y {
// ok
} else {
y = len(line) - y
}
} else {
y = len(line) + y
}
if line[y] {
return 1
}
return 0
}
func Count(x, y int) int {
return check(x-1, y-1) +
check(x, y-1) +
check(x+1, y-1) +
check(x-1, y) +
check(x+1, y) +
check(x-1, y+1) +
check(x, y+1) +
check(x+1, y+1)
}
func MakeRects() *[][]*sdl.Rect {
rects := make([][]*sdl.Rect, Width)
for i, _ := range rects {
rects[i] = make([]*sdl.Rect, Height)
}
return &rects
}
func MakeCells() *[][]bool {
cells := make([][]bool, Width)
for i, _ := range cells {
cells[i] = make([]bool, Height)
}
return &cells
}
func RefreshCells(ch chan func()) {
var targetp *[][]bool
var target [][]bool
var l []bool
var alive bool
var x, y, count int
for Running && !Paused {
targetp = MakeCells()
target = *targetp
for x, l = range *Cells {
for y, alive = range l {
DrawCell(x, y, alive)
count = Count(x, y)
target[x][y] = rule[alive][count]
}
}
if fun, ok := <-ch; ok {
fun()
}
Screen.Flip()
sdl.Delay(Delay)
Cells = targetp
}
}
func DrawCells() {
for x, l := range *Cells {
for y, alive := range l {
DrawCell(x, y, alive)
}
}
Screen.Flip()
}
func DrawCell(x, y int, c bool) {
rect := (*Rects)[x][y]
var color uint32
if c {
color = AliveColor
} else {
color = DeadColor
}
Screen.FillRect(rect, color)
}
func NewSurface(height int, width int) (surface *sdl.Surface) {
surface = sdl.SetVideoMode(height, width, 32, 0)
if surface == nil {
panic(sdl.GetError())
}
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment