Created
May 1, 2010 12:40
-
-
Save manveru/386299 to your computer and use it in GitHub Desktop.
Conway's Game of Life in Go
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
| // 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