Packages

COnway's Game Of LIfe (with variations)

Current section

Files

Jump to
cogoli lib cogoli.ex
Raw

lib/cogoli.ex

defmodule Cogoli do
@moduledoc """
A highly modular implementation of Conway's Game of Life.
The implementation itself is provided as a callback module, and as such
alternative callback modules can be written to explore variations on the
original Game of Life. A number of variations are in fact included with
the app which implement purely aesthetic game alternatives in the form
of colouration, and others that implement actual competition between
different 'species' which individually implement the Game of Life rules
on their own, but experience conflict and cross breading when there is
a conflict for the same square!
The display and back end store are also implemented as callbacks, so it
is possible to implement more efficient back end storage, or present the
game as something other than by X Windows, if desirable.
"""
@doc """
Start a game.
Start a game. The single parameter is a keywords list and at least the
Width and height properties must be provided, like this:
iex(1)> Cogoli.start(
width: 100,
height: 100
)
{:ok, #PID<0.83.0>}
Other properties that may be given are as follows:
rules: [RulesModule: RulesConfig]
rules: RulesModule
The default is `cgolam_rules_normal: []` but other modules, which implement
the cgolam_rules behaviour, may be used to provide a different set of rules
to Conway's Game of Life.
Another rules implementation is cgolam_rules_coloured, which also implements
Conway's Game of Life, but uses different colours for the cells, with new
cells (becoming alive from dead) assuming a colour mix of the surrounding
cells.
field: [FieldModule: FieldConfig]
field: FieldModule
The 'field' module is the back end implementation containing the data
representing the game state.
The default is `cgolam_rules_ets: []`, which uses ETS tables, but
cgolam_rules_gb_trees and cgolam_rules_tuples also exist.
init: [InitModule: [InitType: InitConfig]]
init: [InitModule: InitType]
The 'init' module is used to initialise the field with contents. The default
is to use RulesModule, which initialises the field with data that makes sense
the rules. The default InitType is `:default`.
InitType is an atom, by default it is `:default` but can be other values if
supported by the module. InitConfig is a keywords list, and the properties
supported depends entirely on the module, but cgolam_rules_normal end
cgolam_rules_coloured both `:support cluster_size` and `:cluster_density`,
which increase or decrease the size and densities of the clusters of cells
(the default is 100, it is a percentage, so 50 halves the density (sort of)
and 200 doubles it). Also both support clusters, which determine the number
of clusters.
display: [DisplayModule: DisplayConfig]
display: DisplayModule
The 'display' module is used to display the game state. Currently this can
only be `:cgolam_display_wx`, which uses WX Widgets / X Windows. A property
of `:sqsize` may be given in DisplayConfig to change the size of the cells in
pixels.
For example, this, with the width and height added is the default game
(note that the 'init' config is redundant here):
cgolam:start(
rules: [cgolam_rules_normal: []],
field: [cgolam_field_ets: []],
width: 100,
height, 100,
display: [cgolam_display_wx: [sqsize: 1]],
init: [cgolam_rules_normal: [default: []]],
)
Here's a more interesting one which shows you more features:
cgolam:start(
title: "Coloured Conway's Game of Life",
interval: 20,
rules: [cgolam_rules_coloured: []],
field: [cgolam_field_ets: []],
width: 100,
height: 100,
display: [cgolam_display_wx: [sqsize: 5]],
init: [cgolam_rules_coloured: [default: [cluster_size: 200]]],
])
"""
@spec start([{atom, term}]) :: {:ok, pid}
def start(game_cfg) do
:cgolam.start(cfg_iex_to_erl(game_cfg))
end
@spec stop(pid) :: :ok
def stop(pid) do
:cgolam.stop(pid)
end
@spec list() :: [pid]
def list() do
:cgolam.list()
end
defp cfg_iex_to_erl ([{:rules, [{rules_mod, rules_cfg}]} | more_cfg]) do
[{:rules, rules_mod, rules_cfg} | cfg_iex_to_erl(more_cfg)]
end
defp cfg_iex_to_erl ([{:field, [{field_mod, field_cfg}]} | more_cfg]) do
[{:field, field_mod, field_cfg} | cfg_iex_to_erl(more_cfg)]
end
defp cfg_iex_to_erl ([{:display, [{display_mod, display_cfg}]} | more_cfg]) do
[{:display, display_mod, display_cfg} | cfg_iex_to_erl(more_cfg)]
end
defp cfg_iex_to_erl ([{:init, [{init_mod, [{init_type, init_cfg}]}]} | more_cfg]) do
[{:init, init_mod, init_type, init_cfg} | cfg_iex_to_erl(more_cfg)]
end
defp cfg_iex_to_erl ([{:init, [{init_mod, init_type}]} | more_cfg]) do
[{:init, init_mod, init_type} | cfg_iex_to_erl(more_cfg)]
end
defp cfg_iex_to_erl ([{cfg_opt, cfg_val} | more_cfg]) do
[{cfg_opt, cfg_val} | cfg_iex_to_erl(more_cfg)]
end
defp cfg_iex_to_erl ([]) do
[]
end
end