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