Merge pull request #89 from terakilobyte/master
adds a game of life example
This commit is contained in:
commit
e788339625
|
@ -0,0 +1,20 @@
|
||||||
|
# Conway's Game of Lfe
|
||||||
|
|
||||||
|
Created by [Nathan Leniz](https://github.com/terakilobyte).
|
||||||
|
Inspired by and heavily uses [the doc](https://golang.org/doc/play/life.go)
|
||||||
|
|
||||||
|
> The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970. The "game" is a zero-player game, meaning that its evolution is determined by its initial state, requiring no further input. One interacts with the Game of Life by creating an initial configuration and observing how it evolves, or, for advanced "players", by creating patterns with particular properties. The Game has been reprogrammed multiple times in various coding languages.
|
||||||
|
|
||||||
|
For more information, please see the [wikipedia](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) article.
|
||||||
|
|
||||||
|
## Use
|
||||||
|
|
||||||
|
go run main.go -h
|
||||||
|
-frameRate duration
|
||||||
|
The framerate in milliseconds (default 33ms)
|
||||||
|
-size int
|
||||||
|
The size of each cell (default 5)
|
||||||
|
-windowSize float
|
||||||
|
The pixel size of one side of the grid (default 800)
|
||||||
|
|
||||||
|

|
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
|
@ -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++ {
|
||||||
|
if g.Alive(i, j) {
|
||||||
|
imd.Color = colornames.Black
|
||||||
|
} else {
|
||||||
|
imd.Color = colornames.White
|
||||||
|
}
|
||||||
|
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)),
|
||||||
|
)
|
||||||
|
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 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 *time.Duration
|
||||||
|
)
|
||||||
|
|
||||||
|
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.Duration("frameRate", 33*time.Millisecond, "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)
|
||||||
|
tick := time.Tick(*frameRate)
|
||||||
|
for !win.Closed() {
|
||||||
|
// game loop
|
||||||
|
select {
|
||||||
|
case <-tick:
|
||||||
|
gridDraw.Clear()
|
||||||
|
game.A.Draw(gridDraw)
|
||||||
|
gridDraw.Draw(win)
|
||||||
|
game.Step()
|
||||||
|
}
|
||||||
|
win.Update()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue