-- Simple TerraME model of the game of life --[[ The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970. One interacts with the Game of Life by creating an initial configuration and observing how it evolves or, for advanced players, by creating patterns with particular properties. 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. 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 under-population. 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 over-population. 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. ]]-- -- What do I need? -- A cell space -- timer to implement the dynamics -- rules to get the next stage -- starting configuration -- cs = CellularSpace{xdim = 10} -- insertPattern(cs, getLife("glider"), 0, 0) function insertPattern(cs1, cs2, x0, y0) for i = 0, (cs2.ydim - 1) do for j = 0, (cs2.xdim - 1) do cs1:get(x0 + j, y0 + i).state = cs2:get(j, i).state end end end --- Return a CellularSpace from a data file available in the ca package. -- It works with .life files, where the CellularSpace is stored as spaces -- (dead) or Xs (alive). -- @arg pattern A string with a file name without .file. -- @usage import("ca") -- -- glider = getLife("glider") function getLife(pattern) local mfile = file(pattern..".life", "ca") local lines = {} for line in io.lines(mfile) do lines[#lines + 1] = line end local xdim = string.len(lines[1]) local ydim = #lines if ydim == xdim then ydim = nil end local cs = CellularSpace{ xdim = xdim, ydim = ydim } local ydim = #lines forEachElement(lines, function(y, line) if xdim ~= string.len(line) then customError("Line "..y.." of file "..mfile.." does not have the same length (" ..string.len(line)..") of the first line ("..xdim..").") end for x = 1, xdim do if string.sub(line, x, x) == "X" then cs:get(x - 1, y - 1).state = "alive" elseif string.sub(line, x, x) == " " then cs:get(x - 1, y - 1).state = "dead" else customError("Invalid character '"..string.sub(line, x, x) .."' in file "..mfile.." (line "..y..").") end end end) return cs end --- main program random = Random{seed = os.time()} Life = Model { dim = 50, prob = 0.2, x0 = 20, y0 = 20, pattern = "heavySpaceship", init = function (model) model.finalTime = 500 model.myCell = Cell { -- starting configuration (random) init = function (cell) cell.state = "dead" end, countLiveNeighbors = function (cell) local liveNeigh = 0 forEachNeighbor (cell, function (cell, neigh) if (neigh.past.state == "alive") then liveNeigh = liveNeigh + 1 end end) return liveNeigh end, -- rules to implement the game execute = function (cell) local liveNeigh = cell:countLiveNeighbors() -- Any live cell with fewer than two live neighbours dies if cell.past.state == "alive" and liveNeigh < 2 then cell.state = "dead" end -- Any live cell with two or three live neighbours lives if cell.past.state == "alive" and (liveNeigh == 2 or liveNeigh == 3) then cell.state = "alive" end -- Any live cell with more than three live neighbours dies if cell.past.state == "alive" and liveNeigh > 3 then cell.state = "dead" end -- Any dead cell with exactly three live neighbours becomes a live cell if cell.past. state == "dead" and liveNeigh == 3 then cell.state = "alive" end end } model.cells = CellularSpace { xdim = model.dim, instance = model.myCell, } model.cs2 = getLife(model.pattern) -- this is the small cell space with the pattern insertPattern(model.cells, model.cs2, model.x0, model.y0) model.cells:createNeighborhood { strategy = "moore", wrap = true } model.timer = Timer { Event{action = function () model.cells:synchronize() model.cells:execute() model.cells:notify() end} } model.map = Map{ target = model.cells, select = "state", color = "Blues", value = {"dead", "alive"} } end } mylife = Life{ pattern = "glider" } mylife:execute()