Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Infinite multidimensional game of life with configurable rules :)
-module(game_of_life).
% -export([run/0, neighbours_for/1]).
-compile(export_all).
%%%% Coordinates %%%%
make_coordinate(List) when is_list(List) -> {coordinates, List};
make_coordinate(_) -> erlang:error(badarg).
permutations(ListOfLists) -> lists:map(
fun(List) -> lists:flatten(List) end,
permutations([], ListOfLists)
).
permutations(Acc, []) -> Acc;
permutations([], [List|OtherLists]) -> permutations(List, OtherLists);
permutations(Acc, [List|OtherLists]) ->
permutations([[X,Y] || X <- Acc, Y <- List], OtherLists) .
neighbours_for({coordinates, Coords}) ->
lists:filter(
fun({coordinates, C}) -> C =/= Coords end,
lists:map(
fun(L) -> make_coordinate(L) end,
permutations(
lists:map(
fun(CoordValue) -> [CoordValue - 1, CoordValue, CoordValue + 1] end,
Coords
))));
neighbours_for(_) -> erlang:error(badarg).
test_neighours_and_coordinates() ->
C1 = make_coordinate([0, 0]),
neighbours_for(C1).
%%%% Rules %%%%
make_rule(List) ->
lists:all(fun rule_range_is_valid/1, List),
{rule, List}.
rule_is_triggered_on({rule, Ranges}, N) when is_integer(N) ->
lists:any(fun({From, To}) -> (N >= From) and (N =< To) end, Ranges);
rule_is_triggered_on(_, _) -> erlang:error(badarg) .
rule_range_is_valid({A, B}) when is_integer(A), is_integer(B), A =< B -> true;
rule_range_is_valid(_) -> erlang:error(badarg).
test_rules() ->
Rule = game_of_life:make_rule([{2,3}, {5, 7}]),
io:format("rule: ~p~n", [Rule]),
lists:foreach(
fun(N) ->
Reply = rule_is_triggered_on(Rule, N),
io:format("On ~p: ~p~n", [N, Reply])
end,
lists:seq(0,10)
).
%%%% Multi-dimensional Universe %%%%
make_universe(Dimension, RuleForLiveCells = {rule, _}, RuleForDeadCells = {rule, _})
when is_integer(Dimension)-> {universe, Dimension, RuleForLiveCells, RuleForDeadCells, sets:new()};
make_universe(_, _, _) -> erlang:error(badarg).
set_coordinate({universe, Dimension, R1, R2, LiveCells}, {coordinates, Coordinates}) ->
case Dimension == length(Coordinates) of
true -> {universe, Dimension, R1, R2, sets:add_element(Coordinates, LiveCells)};
_ -> erlang:error('coordinates of invalid dimension')
end.
is_alive_at({universe, _, _, _, LiveCells}, {coordinates, Coordinates}) ->
sets:is_element(Coordinates, LiveCells).
dump_universe({universe, _, _, _, LiveCells}) -> lists:foreach(
fun(X) -> io:format("~p~n", [X]) end, sets:to_list(LiveCells)
) .
count_neighbours_around(U = {universe, _, _, _, _}, Coordinates = {coordinates, _}) ->
length(
lists:filter(
fun(Coord) -> is_alive_at(U, Coord) end,
neighbours_for(Coordinates)
)
).
tick(U = {universe, Dimension, RuleForLiveCells, RuleForDeadCells, LiveCells}) ->
{LiveCreaturesRecalculated, CalculatedCoordinates} = lists:foldl(
fun(Coord, {SetForNewLiveCreatures, SetWithCalculatedCoordinates}) ->
NewSetWithCalculatedCoordinates = sets:add_element({coordinates,Coord}, SetWithCalculatedCoordinates),
NumberOfNeigthbours = count_neighbours_around(U, {coordinates, Coord}),
case rule_is_triggered_on(RuleForLiveCells, NumberOfNeigthbours) of
true -> {sets:add_element(Coord, SetForNewLiveCreatures), NewSetWithCalculatedCoordinates};
_ -> {SetForNewLiveCreatures, NewSetWithCalculatedCoordinates}
end
end,
{sets:new(), sets:new()},
sets:to_list(LiveCells)
),
% io:format("CalculatedCoordinates ~p~n", [sets:to_list(CalculatedCoordinates)]),
% length(lists:flatten(
% lists:map(
% fun(LiveCell) -> neighbours_for({coordinates, LiveCell}) end,
% sets:to_list(LiveCells)
% )
% )),
AllNeighbours = sets:from_list(
lists:flatten(
lists:map(
fun(LiveCell) -> neighbours_for({coordinates, LiveCell}) end,
sets:to_list(LiveCells)
)
)
),
% io:format("AllNeighbours ~p~n", [sets:to_list(AllNeighbours)]),
UnprocessedNeighbours = lists:filter(
fun(Coord) ->
not sets:is_element(Coord, CalculatedCoordinates)
end,
sets:to_list(AllNeighbours)),
% io:format("UnprocessedNeighbours ~p~n", [UnprocessedNeighbours]),
% length(UnprocessedNeighbours)
A = lists:foldl(
fun(Coord={coordinates, C}, Acc) ->
NumberOfNeigthbours = count_neighbours_around(U, Coord),
case rule_is_triggered_on(RuleForDeadCells, NumberOfNeigthbours) of
true -> sets:add_element(C, Acc);
_ -> Acc
end
end,
LiveCreaturesRecalculated,
UnprocessedNeighbours
),
{universe,
Dimension,
RuleForLiveCells,
RuleForDeadCells,
% sets:from_list(lists:map(fun(X) -> {coordinates, X} end, sets:to_list(A)))
A
}
.
test_universe() ->
Rule1 = game_of_life:make_rule([{2,3}]),
Rule2 = game_of_life:make_rule([{3,3}]),
Universe = make_universe(2, Rule1, Rule2),
Coord1 = make_coordinate([1,2]),
Coord2 = make_coordinate([1,3]),
Coord3 = make_coordinate([1,4]),
Universe1 = set_coordinate(Universe, Coord1),
Universe2 = set_coordinate(Universe1, Coord2),
Universe3 = set_coordinate(Universe2, Coord3),
TestCoord = make_coordinate([5,5]),
dump_universe(Universe3),
io:format("Life exists at ~p: ~p~n", [Coord1, is_alive_at(Universe3, Coord1)]),
io:format("Life exists at ~p: ~p~n", [Coord1, is_alive_at(Universe3, Coord2)]),
io:format("Life exists at ~p: ~p~n", [Coord1, is_alive_at(Universe3, Coord3)]),
io:format("Life exists at ~p: ~p~n", [Coord1, is_alive_at(Universe3, TestCoord)]),
io:format("N of neighbours around ~p: ~p~n", [Coord1, count_neighbours_around(Universe3, Coord1)]),
io:format("N of neighbours around ~p: ~p~n", [Coord2, count_neighbours_around(Universe3, Coord2)]),
io:format("N of neighbours around ~p: ~p~n", [Coord3, count_neighbours_around(Universe3, Coord3)]),
tick(Universe3)
.
test_universe2() ->
Rule1 = game_of_life:make_rule([{2,3}]),
Rule2 = game_of_life:make_rule([{3,3}]),
Universe = make_universe(2, Rule1, Rule2),
Coord1 = make_coordinate([1,2]),
Coord2 = make_coordinate([1,3]),
Coord3 = make_coordinate([1,4]),
Universe1 = set_coordinate(Universe, Coord1),
Universe2 = set_coordinate(Universe1, Coord2),
Universe3 = set_coordinate(Universe2, Coord3),
dump_universe(Universe3),
io:format("------~n", []),
Universe4 = tick(Universe3),
dump_universe(Universe4),
io:format("------~n", []),
Universe5 = tick(Universe4),
dump_universe(Universe5),
io:format("------~n", [])
.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment