May 12, 2017
Go Game of Life
839 words, 4 minutes to read.
Going through the Awesome Go list, I came across a new library under the Game Development section named Pixel. Since I have been looking for a simple game development library in Go for visualization of cellular automata and evolutionary algorithms simulations.
Game of Life is a 2D cellular automata and a zero-player game whose evolution only depends on the initial configuration. The rules of the game as per its Wikipedia page are as follows:
The universe of the Game of Life is an infinite two-dimensional orthogonal grid of square cells, each of which is in one of two possible states, alive or dead, or “populated” or “unpopulated”. Every cell interacts with its eight neighbours, which are the cells that are horizontally, vertically, or diagonally adjacent. At each step in time, the following transitions occur:
- Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
- Any live cell with two or three live neighbours lives on to the next generation.
- Any live cell with more than three live neighbours dies, as if by overpopulation.
- Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
The initial pattern constitutes the seed of the system. The first generation is created by applying the above rules simultaneously to every cell in the seed—births and deaths occur simultaneously, and the discrete moment at which this happens is sometimes called a tick (in other words, each generation is a pure function of the preceding one). The rules continue to be applied repeatedly to create further generations.
Even with these simple and local rules, the Game of Life is known to produce some very interesting static, oscillating and repeating patterns.
I wanted to see how easy it would be to develop the following features:
- Draw individual pixels and primitives (lines, circles, polygons, etc.)
- Keyboard interaction (press, hold, release)
- Mouse interaction (press, hold, release, double-click)
With a few hours of copy-pasting from the Pixel examples and some own coding, I was able to create a demo with the following features:
- Store the state of the world in a 2D array
- Scale the world to 4x to make the cells visibly distinguishable
- Draw the cells as filled rectangles (also tried circles)
- Left-click to set a cell alive
- Alt+Left-click to set a cell to dead
- Hold the click (and Alt variant) to continuous set cell states
- Ctrl+Left-click to reinitialize a fixed 11x11 area around the mouse position
rto clear and reinitialize the entire world, respectively
Spaceto toggle pausing the simulation
double-click, which I could not figure out, all other features
were relatively easy to figure out as well as implement.
The resulting code is available at https://github.com/shivakar/go-game-of-life. Here’s a screencast of one run of the simulation.
Additional criteria that I am interested in include:
Ease of installation of the library
As with most Go packages installation was easy:
go get -u github.com/faiface/pixel
Dependencies of the library
The number and quality of dependencies of a library are something that I always look at when selecting a library. I find that too many dependencies or low-quality dependencies tend to create usability issues down the line.
Here’s the dependency graph for
github.com/faiface/pixelglgenerated using goviz
I ran these experiments on my MacBook Air (mid-2012) running macOS Sierra (10.12) and Go 1.8.1.
With cells being drawn as filled rectangles and
false, I saw frame rates between 250-500 fps depending on how many cells in the world were alive. With
true, the frame rate was synchronized to 60fps monitor refresh rate.
However when the cells were drawn as filled circles, the frame rate dropped to 5-50 fps.
pprofI noticed that the top CPU consuming functions went from
flat% cum% 37.76% 37.76% main.(*GameOfLife).NeighborsAlive 6.98% 17.73% main.(*GameOfLife).Draw 4.37% 42.13% main.(*GameOfLife).Update 2.23% 2.92% github.com/faiface/pixel/imdraw.(*IMDraw).applyMatrixAndMask 1.23% 6.60% github.com/faiface/pixel/imdraw.(*IMDraw).fillRectangle
flat% cum% 20.92% 24.35% github.com/faiface/pixel/imdraw.(*IMDraw).applyMatrixAndMask 15.07% 15.07% github.com/faiface/pixel/pixelgl.(*GLTriangles).updateData 10.82% 10.82% math.Sincos 3.72% 3.72% main.(*GameOfLife).NeighborsAlive 3.70% 50.00% github.com/faiface/pixel/imdraw.(*IMDraw).fillEllipseArc 2.15% 11.09% github.com/faiface/pixel.(*TrianglesData).SetLen
I haven’t looked (and most probably won’t) into the underlying causes but it appears that the library is just slow at drawing circles than rectangles. While not a deal breaker, I will have to develop a few more sample programs to get a better understanding of the strengths and weaknesses of the library.
All things considered, I am excited to see new packages like Pixel address the lack of good and easy-to-use GUI, visualization and game development libraries for Go. Although this does not end my search for a game development library in Go, I will certainly keep track of Pixel development.