Created
August 3, 2023 01:18
-
-
Save michlbro/f16dec135c4232c3c4fac6e08f9d5a21 to your computer and use it in GitHub Desktop.
Conway's Game Of Life.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
Conway's Game Of Life. | |
Created by michlbro. | |
Mechanics: | |
1. Underpopulation: less than 2 neighbour cells. | |
2. Overpopulation: more than 3 neighbour cells. | |
3. Lives: 2 or 3 neighbour cells. | |
4. Reproduction: 3 neighbour cells. | |
Live cell table "origin" starts top left. | |
USAGE: | |
```lua | |
local conway = require(ConwayModule).new() -- conway object | |
conway:SpawnCell(1, 2) -- (x, y) | |
print(conway:GetCell(1, 2)) -- "{x, y}" or nil (if cell is dead) | |
conway:KillCell(1, 2) | |
conway:Step() -- Next generation. | |
conway:Clear() | |
conway:Destroy() | |
``` | |
API: | |
```lua | |
-- Creates a new conway class | |
Conway.new() -> conway | |
-- Spawns a new cell at a given x, y coordinates. | |
conway:SpawnCell(x: number, y: number) -> nil | |
-- Kills a cell at given a x, y coordinates. | |
conway:KillCell(x: number, y: number) -> nil | |
-- Gets a cells index in an array of live cells at a given x, y coordinates. | |
conway:GetCell(x: number, y: number) -> number? | |
-- Returns the array of live cells. | |
conway:GetLiveCells() -> { | |
[number]: {x: number, y: number} | |
} | |
-- Progresses conways game to next generation. | |
conway:Step() -> nil | |
-- Removes all live cells. | |
conway:Clear() -> nil | |
-- Destroys conways class. | |
conway:Destroy() -> nil | |
``` | |
]] | |
local Conway = {} | |
local function CheckCellExistance(liveCells, x, y) | |
for index, cell in pairs(liveCells) do | |
if cell[1] == x and cell[2] == y then | |
return index | |
end | |
end | |
return nil | |
end | |
function Conway:SpawnCell(x: number, y: number) | |
if not CheckCellExistance(self.liveCells, x, y) then | |
table.insert(self.liveCells, {x,y}) | |
end | |
end | |
function Conway:KillCell(x: number, y: number) | |
local index = CheckCellExistance(self.liveCells, x, y) | |
if index then | |
table.remove(self.liveCells, index) | |
end | |
end | |
function Conway:GetLiveCells() | |
return self.liveCells | |
end | |
function Conway:GetCell(x: number, y: number): number? | |
local index = CheckCellExistance(self.liveCells, x, y) | |
if index then | |
return index | |
end | |
return nil | |
end | |
function Conway:Clear() | |
table.clear(self.liveCells) | |
end | |
function Conway:Destroy() | |
table.clear(self.liveCells) | |
self.liveCells = nil | |
self = nil | |
end | |
function Conway:Step() | |
--[[ | |
Instructions: | |
- Live cell surround table starts top left to bottom right | |
o (1) For loop through liveCells and create a temporary table with 1 cell radius around them. | |
- For liveCells tag them in the temporary table | |
o For loop through temporary table and do neighbour checks. | |
- For tagged cells, if dead, remove them from liveCells | |
]] | |
local liveCells = table.clone(self.liveCells) | |
-- Create surrounding cell map (1) | |
local cellMap = {} | |
for index, cell in liveCells do | |
local x, y = cell[1], cell[2] | |
if not cellMap[x] then | |
cellMap[x] = {} | |
end | |
-- add liveCell and tag it | |
cellMap[x][y] = {true, index} -- {state, previouslyAliveIndex?} | |
-- add surrounding cell | |
for xPos = x - 1, x + 1 do | |
for yPos = y - 1, y + 1 do | |
-- skip the liveCell | |
if xPos == x and yPos == y then | |
continue | |
end | |
if not cellMap[xPos] then | |
cellMap[xPos] = {} | |
end | |
cellMap[xPos][yPos] = cellMap[xPos][yPos] or {false} | |
end | |
end | |
end | |
-- Check neighbour cells and determine cell state (2) | |
for x, yCol in pairs(cellMap) do | |
for y, cell in pairs(yCol) do | |
local _, previouslyAliveIndex = cell[1], cell[2] | |
local neighbours = 0 | |
for xPos = x - 1, x + 1 do | |
if not cellMap[xPos] then | |
continue | |
end | |
for yPos = y - 1, y + 1 do | |
-- skip the liveCell | |
if xPos == x and yPos == y then | |
continue | |
end | |
if not cellMap[yPos] then | |
continue | |
end | |
neighbours += cellMap[xPos][yPos][1] and 1 or 0 | |
end | |
end | |
local result = false | |
if neighbours == 2 or neighbours == 3 then | |
result = true | |
end | |
if result and not previouslyAliveIndex then | |
table.insert(self.liveCells, {x, y}) | |
elseif not result and previouslyAliveIndex then | |
table.remove(self.liveCells, previouslyAliveIndex) | |
end | |
end | |
end | |
end | |
local function new() | |
local self = {} | |
-- liveCells: { {x,y} } | |
self.liveCells = {} | |
setmetatable(self, { | |
__index = Conway | |
}) | |
return self | |
end | |
return table.freeze(setmetatable({ | |
new = new | |
}, { | |
__index = Conway | |
})) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment