All Projects → JnyJny → GameOfLife

JnyJny / GameOfLife

Licence: MIT license
Conway's Game of Life

Programming Languages

python
139335 projects - #7 most used programming language
Makefile
30231 projects

Projects that are alternatives of or similar to GameOfLife

logic-life-search
Cellular automata search program
Stars: ✭ 23 (+27.78%)
Mutual labels:  game-of-life, life, gameoflife, conways-game-of-life
cs1501-final
Haskell class final project -- game in Haskell.
Stars: ✭ 21 (+16.67%)
Mutual labels:  life, conway-game, conways-game-of-life
game-of-life
An implementation of Conway's Game of Life in Rust using Piston
Stars: ✭ 30 (+66.67%)
Mutual labels:  life, conway-game
shorelark
Simulation of life & evolution
Stars: ✭ 109 (+505.56%)
Mutual labels:  life, simulation
cgolGo
Conway's Game of Life in Golang
Stars: ✭ 38 (+111.11%)
Mutual labels:  gameoflife, conway-game
orbital-sim
A simple physics engine build over a PyGame simulation to accurately model planetary orbits in space
Stars: ✭ 31 (+72.22%)
Mutual labels:  simulation, pygame
Ecosim
An interactive ecosystem and evolution simulator written in C and OpenGL, for GNU/Linux.
Stars: ✭ 382 (+2022.22%)
Mutual labels:  life, simulation
js-simulator
General-purpose discrete-event multiagent simulation library for agent-based modelling and simulation
Stars: ✭ 52 (+188.89%)
Mutual labels:  game-of-life, simulation
go-life
Conway's Game of Life in Go
Stars: ✭ 13 (-27.78%)
Mutual labels:  life, conway-game
glider
Generative music using conway's game of life
Stars: ✭ 38 (+111.11%)
Mutual labels:  life, conway-game
cgol
Conway's Game of Life in the Terminal
Stars: ✭ 32 (+77.78%)
Mutual labels:  game-of-life, conways-game-of-life
go-life
Terminal based Conway's Game of Life. Implemented in Go.
Stars: ✭ 95 (+427.78%)
Mutual labels:  life, conway-game
melodyoflife
Melody of Life is a step sequencer using cellular automata
Stars: ✭ 38 (+111.11%)
Mutual labels:  game-of-life, conway-game
life-progress
🚼->🚶->👻
Stars: ✭ 20 (+11.11%)
Mutual labels:  life
lifeee-rs
An implementation of the Game of Life
Stars: ✭ 53 (+194.44%)
Mutual labels:  game-of-life
python-game-snake
A Game-Snake version of AI to play Snake automatically.
Stars: ✭ 30 (+66.67%)
Mutual labels:  pygame
carAI-Demo
使用浅层神经网络和遗传算法训练一个可以自动驾驶小车的Demo
Stars: ✭ 64 (+255.56%)
Mutual labels:  pygame
Incoherent-Light-Simulation
Simulation of the propagation of incoherent light, aiming to illustrate the concept of spatial coherence.
Stars: ✭ 98 (+444.44%)
Mutual labels:  simulation
Python Scripts
It contains all the Python Programs, whether it's a GUI, basic, Data Structures, etc. It's a collection of some great Python scripts from basic to advance levels for automating some monotonous tasks.
Stars: ✭ 23 (+27.78%)
Mutual labels:  pygame
AbsTK
The Abstract Toolkit – a widget toolkit for GUI and text-mode applications.
Stars: ✭ 67 (+272.22%)
Mutual labels:  curses

GameOfLife

Conway's Game of Life - Cellular Automata in Python

This is a python3 package that provides two classes that together implement Conway's Game of Life.

>>> from GameOfLife import *
>>> w = World()
>>> w.addPattern('glider')
>>> while True:
>>>     w.step()
>>>     print(w)

Install

You can use pip to install GameOfLife:

$ sudo pip3 install GameOfLife

You can clone this repository

$ git clone https://github.com/JnyJny/GameOfLife.git
$ cd GameOfLife
$ python3 setup.py install

Examples

I've provided two examples in the contrib directory of the repo: CGameOfLife and PGameOfLife. The C stands for curses and will show a simulation in a terminal window using the time-honored curses library. The P in PGameOfLife stands for PyGame and draws the simulation in a pygame window.

CGameOfLife Demo

CGameOfLife Demo

PGameOfLife Demo

PGameOfLife Demo

[CP]GameOfLife Usage

$ [CP]GameOfLife.py [pattern_name[,X,Y]] ...
...
$ [CP]GameOfLife.py foo
unknown pattern: 'foo'
known patterns:
	block
	lws
	toad
	pulsar
	loaf
	glider
	blinker
	beehive
	beacon
	boat
$ [CP]GameOfLife.py glider,10,10 pulsar,0,0 lws,0,20
...	

Design Notes - The Fun Stuff

There are lots of ways of representing a Game of Life board but I decided to write it as a two dimensional grid of cell objects. The grid would organize the cells and the cells would implement the rules that would determine their state: alive or dead. The grid is called the World and the cells are called... Cell.

An obvious drawback of this design is that it is memory inefficient: each display element is represented by a Cell object. Assuming a two color display, you would only need one bit per display element to model the state of each state. A Cell object is much larger than that. That said, my goal was to practice writing good objects and then experiencing how this solution would evolve.

The first technical problem, of course, is that python doesn't have a native two dimensional grid object. I picked a list object as my foundational grid object and overrode the __getitem__ method to implement accessing elements using x and y coordinates.

This also made it easier to implement an "infinite" grid by wrapping the edges when accessed by (x,y) and still allowed iterating through all the cells serially.

Now that we can access Cells, it's time to decide what the Cell should do. Each cell tracks it's current state: alive or dead and has to know what rules it should be following. In order to to compute it's next state, it requires the number of living cells in it's immediate environ. Consider a cell embedded in a rectangular matrix; this cell has eight neigbors in the 3x3 sub-matrix where the target cell has coordinates (1,1).

Technically, cells don't really need to know where they are in the world, they only care about the current state of their neighbors. However the cells cannot directly access neighboring cells, as that would violate division of labor between the cell and grid data structures. I also didn't want the Cell to have some weird back reference to the World object.

Because the cells know their own address, they can compute the addresses of their neighbors. This simplifies the World.step method since it doesn't need to compute each cell's neighbors as it iterates through all the cells. One step further would be to keep a list of the actual neighbor cells. The World.step method would become even more simple since it would not have to provide any information to a cell other than it's time to compute the next state.

At the end of the day, the step function was a O(2*n) method since each cell was visited in two batches: the first to record the number of alive neighbors for each cell and the second to change state based on the number of alive neighbors. Most of the time this is implemented as a frame buffer type data structure, with one frame indicating the n-th step and the other frame the n+1-th step. It's a classic space for time trade off.

Patterns

I have provided a few simple patterns to prove that the simulation is working. The Patterns dictionary is keyed with each pattern's name and then a string representing cell states: spaces are not-alive cells, non-spaces are alive cells. A newline in the string indicates a new row.

Optimizations

After I wrote it and started thinking about optimizations, I began to think about replacing the Cell model with a numpy array to take advantage of presumably optimized array accessors. Or even just an array of characters and letting the character value encode the cell state in a more compact (and obtuse) manner. Those changes are pretty disruptive and I decided to postpone them.

Neighbors

As far as simple but useful optimizations, caching each cell's neighbor coordinates traded space for time when calculating the status of each cell's neighbors in the step method. This simplifies the method since it isn't necessary to pretend the list is a two dimension object; just iterate through all the cells and ask each cell who it's neighbors are. A better optimization might be to calculate the 1D index rather than the 2D, but I think this brings too much knowledge of the World's implementation into the cell.

Hash Cache FTW

In order for a python object to participate in a set container, it needs to implement a __hash__ method which should return a unique hash value for the object. One lesson I learned: cache the hash value of an object. It is much more efficient that computing the hash value everytime the method is called. I suppose this is not a valid optimization for object's whose hash value can mutate over time, however Cells do not migrate around the World and the uniqueness of the hash is driven by the location of the cell.

Dead Cells Don't Matter

The best optimization came when I realized that the World is only affected by living cells and I could achieve a more efficient step method if I only visited living cells and their immediate surroundings. It is not a perfect solution as a World could have nearly all cells living and thus the number of alive cells would tend to the number of total cells. However, that case is generally rare and the number of live cells is usually much less than the total number of cells in the world. [Cite needed :]

OptimizedWorld

I added a new class, OptimizedWorld, and overrode only a couple of methods on World: reset, addPattern, and step. I then imbued the class with a new property, a set named alive which is initialized with all the live cells in the addPattern method.

The magic happens in the step method:

  1. Build a set of all the neighbors of all the live cells in the world.
  2. Update each cell with the number of live neighbors it has.
  3. Apply the live neighbor count to the cell to drive a state change.
  4. Finally, pull out any dead cells from the live set.
  5. Rinse and repeat.

This optimization resulted in some very impressive gains in speed; from roughly 10 generations a second to around 100 generations a second measured in the curses CGameOfLife implementation (on a Mid 2014 Mac Book Pro with a 2.8Ghz i7, 16G of memory and NVIDIA graphics).

The algorithm is O((alive+neighbors) x 2) where normally alive << total number of cells and neighbors is bounded by [0,alive x 8]. I think, my algorithm analysis is admittedly rusty.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].