adds a game of life example
This commit is contained in:
parent
435cc32abf
commit
17c9db7140
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue