diff --git a/examples/community/game_of_life/life/grid.go b/examples/community/game_of_life/life/grid.go new file mode 100644 index 0000000..ea59cc8 --- /dev/null +++ b/examples/community/game_of_life/life/grid.go @@ -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) +} diff --git a/examples/community/game_of_life/life/life.go b/examples/community/game_of_life/life/life.go new file mode 100644 index 0000000..2ee7fca --- /dev/null +++ b/examples/community/game_of_life/life/life.go @@ -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 +} diff --git a/examples/community/game_of_life/main.go b/examples/community/game_of_life/main.go new file mode 100644 index 0000000..11b02d6 --- /dev/null +++ b/examples/community/game_of_life/main.go @@ -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() + } +}