/Conway

Conway's game of life

Primary LanguageErlang

Conway

Conway's Game of Life

Running:

Compilation is pretty trivial:

erlc *.erl

The UI, such as it is, is run entirely within the Erlang shell. First, initialise the cell store:

> cell_store:init().
cell_store

Now individual cells can be set to live thus:

> cell_store:set_cell({5, 5}, true).
true

That's a bit cumbersome, though, so there's a set of pre made common life forms in the life_forms module. You can add them like this:

> life_forms:add(life_forms:glider(), {30, 50}).
ok

That will add a glider with top-left coordinates of 30,50. You can also transform the pre-set life forms using the mirror_x, mirror_y, rotate_l and rotate_r functions (to flip horizontally, flip vertically, rotate counter-clockwise 90 degrees and rotate clockwise 90 degrees respectively):

> life_forms:add( life_forms:rotate_r( life_forms:mirror_x( life_forms:loaf() )), {10, 10}).
ok

Running the simulation can be done a step at a time:

> conway:run_step().
ok

To display the 100x100 grid starting at 0,0, just call

> printer:print().
<Grid>
ok

You can specify a particular rectangle to print like this:

> printer:print({10,10}, {20, 20}).
<11x11 grid>
ok

To run repeated iterations at 400ms intervals, printing out the 100x100 grid at each step you can use:

> test:run().
....

This will keep running until you press RETURN/ENTER.

Persistent storage is provided by:

> cell_store:save().
ok

and

> cell_store:load().
ok

(Note that for the sake of simplicity this will always save to/load from a file called 'cell_store' in the current working directory).

Finally, the test module also contains a couple of example starting states, one with three different life forms and one with four gliders. To use those start states, run:

> test:setup_example().

or

> test:setup_gliders().

respectively. test:run/0 can then be used as described above.

Limitations/Notes:

  • The field is effectively unlimited in size due to Erlang's arbitrary precision integers - at least until you run out of memory. (I realise the spec was technically to support 2^64 x 2^64, but I figured removing that limit would not be a big problem - I can add it in if it's a strict rather than minimum requirement).
  • There's no protection on the cell storage - someone could theoretically save/load it while the simulation is running and get an inconsistent state.
  • There's no real concurrency (even though we're working with Erlang) - there's a few ways to approach concurrency in CGoL but they all complicate the implementation significantly, and the request was for a "simple, minimalist" solution :)
  • I've made no real attempt at any kind of optimisation, again in the interests of preferring simplicity.
  • Saving/loading always goes to/from the same file name in the current working directory.
  • cell_store is just a very thin wrapper around ETS to make it relatively easy to swap in a different store to test performance.
  • In the interests of simplicity, in true Erlang style there's basically no error handling (even in cases where a proper Erlang app would have it, like file access).
  • The persistent store is just a simple DETS dump of the ETS table, and is far from optimal size-wise.
  • I've lazily done -compile(export_all) on every module to make testing easier. Obviously that's generally not good form in real apps.