adds a game of life example

This commit is contained in:
Nathan 2018-01-18 19:52:52 -05:00
parent 435cc32abf
commit 17c9db7140
3 changed files with 173 additions and 0 deletions

View File

@ -0,0 +1,74 @@
package life
import (
"github.com/faiface/pixel"
"github.com/faiface/pixel/imdraw"
"golang.org/x/image/colornames"
)
// Shamelessly taken/inspired by https://golang.org/doc/play/life.go
// Grid is the structure in which the cellular automota live
type Grid struct {
h int
cellSize int
Cells [][]bool
}
// NewGrid constructs a new Grid
func NewGrid(h, size int) *Grid {
cells := make([][]bool, h)
for i := 0; i < h; i++ {
cells[i] = make([]bool, h)
}
return &Grid{h: h, cellSize: size, Cells: cells}
}
// Alive returns whether the specified position is alive
func (g *Grid) Alive(x, y int) bool {
x += g.h
x %= g.h
y += g.h
y %= g.h
return g.Cells[y][x]
}
// Set sets the state of a specific location
func (g *Grid) Set(x, y int, state bool) {
g.Cells[y][x] = state
}
// Draw draws the grid
func (g *Grid) Draw(imd *imdraw.IMDraw) {
for i := 0; i < g.h; i++ {
for j := 0; j < g.h; j++ {
imd.Push(
pixel.V(float64(i*g.cellSize), float64(j*g.cellSize)),
pixel.V(float64(i*g.cellSize+g.cellSize), float64(j*g.cellSize+g.cellSize)),
)
if g.Alive(i, j) {
imd.Color = colornames.Black
} else {
imd.Color = colornames.White
}
imd.Rectangle(0)
}
}
}
// Next returns the next state
func (g *Grid) 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) && g.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 && g.Alive(x, y)
}

View File

@ -0,0 +1,35 @@
// Package life is the manages the "game" state
// Shamelessly taken from https://golang.org/doc/play/life.go
package life
import "math/rand"
// Life stores the state of a round of Conway's Game of Life.
type Life struct {
A, b *Grid
h int
}
// NewLife returns a new Life game state with a random initial state.
func NewLife(h, size int) *Life {
a := NewGrid(h, size)
for i := 0; i < (h * h / 2); i++ {
a.Set(rand.Intn(h), rand.Intn(h), true)
}
return &Life{
A: a, b: NewGrid(h, size),
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.h; x++ {
l.b.Set(x, y, l.A.Next(x, y))
}
}
// Swap fields a and b.
l.A, l.b = l.b, l.A
}

View File

@ -0,0 +1,64 @@
package main
import (
"flag"
"math/rand"
"time"
"golang.org/x/image/colornames"
"github.com/faiface/pixel"
"github.com/faiface/pixel/examples/community/game_of_life/life"
"github.com/faiface/pixel/imdraw"
"github.com/faiface/pixel/pixelgl"
)
var (
size *int
windowSize *float64
frameRate *int64
)
func init() {
rand.Seed(time.Now().UnixNano())
size = flag.Int("size", 5, "The size of each cell")
windowSize = flag.Float64("windowSize", 800, "The pixel size of one side of the grid")
frameRate = flag.Int64("frameRate", 33, "The framerate in milliseconds")
flag.Parse()
}
func main() {
pixelgl.Run(run)
}
func run() {
cfg := pixelgl.WindowConfig{
Title: "Pixel Rocks!",
Bounds: pixel.R(0, 0, *windowSize, *windowSize),
VSync: true,
}
win, err := pixelgl.NewWindow(cfg)
if err != nil {
panic(err)
}
win.Clear(colornames.White)
// since the game board is square, rows and cols will be the same
rows := int(*windowSize) / *size
gridDraw := imdraw.New(nil)
game := life.NewLife(rows, *size)
last := time.Now()
for !win.Closed() {
// game loop
if time.Since(last).Nanoseconds() > *frameRate {
gridDraw.Clear()
game.A.Draw(gridDraw)
gridDraw.Draw(win)
game.Step()
last = time.Now()
}
win.Update()
}
}