Greetings devs!
I am now open sourcing two of my projects and this is one of them!
This is a simple implementation of the famous cellular automata - Conway’s game of life!
For anyone who does not know what that is, here is an explantation.
Demonstration:
Uncopylocked place
Download here (50.2 KB)
Source code:
-- Main
local Players = game.Players
local ReplicatedFirst = game.ReplicatedFirst
local ReplicatedStorage = game.ReplicatedStorage
-- Services
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")
local Frame = script.Parent
local Settings = Frame.Parent.Settings
local CellOriginal = script.Cell
local Test = false
local Playing = false
local AliveNeighborSpawnInt, MinimumAliveNeighborAmount, MaximumAliveNeighborAmount = 3,2,3
wait(2)
local GridSize = Vector2.new(math.floor(Frame.AbsoluteSize.X / (Frame.UIGridLayout.CellSize.X.Offset + Frame.UIGridLayout.CellPadding.X.Offset)),math.floor(Frame.AbsoluteSize.Y / (Frame.UIGridLayout.CellSize.Y.Offset + Frame.UIGridLayout.CellPadding.Y.Offset)))
local CellTable = {}
local function GetCellAliveFromVector2(Vector) -- Cell coordinates go in and alive status goes out.
-- This checks if the requested cell is out of the grid
if Vector.X == 0 or Vector.Y == 0 or Vector.X == GridSize.X + 1 or Vector.X == GridSize.Y + 1 or CellTable[Vector.X][Vector.Y] == nil then
return false
end
return CellTable[Vector.X][Vector.Y]:GetAttribute("Alive")
end
local function GenerateGrid() -- Generates grid.
-- Deletes any old cells
for index,value in pairs(Frame:GetChildren()) do
if value:IsA("TextButton") then
value:Destroy()
end
end
-- Made for debuging, just couns the cells
local Count = 0
for Y = 1,GridSize.Y do
for X = 1,GridSize.X do
-- Creates cell and configures it here, gives it attributes like X,Y and Alive
local Cell = CellOriginal:Clone()
Cell.Parent = Frame
-- Assigns a function to run when the cell changes the "Alive" attribute
Cell.AttributeChanged:Connect(function(attribute)
if attribute == "Alive" then
if Cell:GetAttribute("Alive") == true then
local Tween = TweenService:Create(Cell.Glow,TweenInfo.new(0.5,Enum.EasingStyle.Quad),{ImageTransparency = 0.8})
Tween:Play()
Cell.BackgroundColor3 = Color3.new(0.933333, 0.933333, 0.933333)
else
local Tween = TweenService:Create(Cell.Glow,TweenInfo.new(0.5,Enum.EasingStyle.Quad),{ImageTransparency = 1})
Tween:Play()
Cell.BackgroundColor3 = Color3.new(0.14902, 0.14902, 0.14902)
end
end
end)
Cell:SetAttribute("X",X)
Cell:SetAttribute("Y",Y)
Cell:SetAttribute("Alive",false)
if CellTable[X] == nil then
CellTable[X] = {}
end
CellTable[X][Y] = Cell
-- Set the "Test" variable (at the begginning of the script) to true and you will see what this does.
if Test == true then
Cell.TextTransparency = 0
Cell.TextStrokeTransparency = 0
Cell.Text = "X: " .. X .. " Y: " .. Y
end
-- Reacts to mouse clicks
Cell.MouseButton1Click:Connect(function()
if Cell:GetAttribute("Alive") == true then
Cell:SetAttribute("Alive",false)
else
Cell:SetAttribute("Alive",true)
end
end)
-- Counts cells
if Cell ~= nil and Cell.Parent == Frame then
Count += 1
end
end
-- Waits so your pc won't get annihilated
wait()
end
if Test == true then
print(Count)
end
end
local function Generation() -- Will calculate 1 generation when called
local OperationsPlanned = {} -- Records changes to do when everything is calculated
for index,value in pairs(Frame:GetChildren()) do -- Goes through every cell
if value:IsA("TextButton") then
-- Gets the cell X,Y and Alive status
local X = value:GetAttribute("X")
local Y = value:GetAttribute("Y")
local Alive = value:GetAttribute("Alive")
local TotalAliveNeighbors = 0
-- Counts how many of the eight neighbors are alive
if GetCellAliveFromVector2(Vector2.new(X - 1,Y)) == true then
TotalAliveNeighbors += 1
end
if GetCellAliveFromVector2(Vector2.new(X - 1,Y + 1)) == true then
TotalAliveNeighbors += 1
end
if GetCellAliveFromVector2(Vector2.new(X,Y + 1)) == true then
TotalAliveNeighbors += 1
end
if GetCellAliveFromVector2(Vector2.new(X + 1,Y + 1)) == true then
TotalAliveNeighbors += 1
end
if GetCellAliveFromVector2(Vector2.new(X + 1,Y)) == true then
TotalAliveNeighbors += 1
end
if GetCellAliveFromVector2(Vector2.new(X + 1,Y - 1)) == true then
TotalAliveNeighbors += 1
end
if GetCellAliveFromVector2(Vector2.new(X,Y - 1)) == true then
TotalAliveNeighbors += 1
end
if GetCellAliveFromVector2(Vector2.new(X - 1,Y - 1)) == true then
TotalAliveNeighbors += 1
end
-- The game rules are here, you can change the numbers and see what happens!
if Alive == true then
if TotalAliveNeighbors < MinimumAliveNeighborAmount or TotalAliveNeighbors > MaximumAliveNeighborAmount then
OperationsPlanned[value] = false
end
else
if TotalAliveNeighbors == AliveNeighborSpawnInt then
OperationsPlanned[value] = true
end
end
end
end
--[[ Applies all the operations that where planned before, this is done later, so that we calculate the
everything and ONLY THEN apply the chanages
]]
for Object, AliveStatus in pairs(OperationsPlanned) do
Object:SetAttribute("Alive",AliveStatus)
end
end
Settings.Generate.MouseButton1Click:Connect(function() -- This runs when the user generates the grid
local Size = Settings.CellSize.Text
Frame.UIGridLayout.CellSize = UDim2.fromOffset(tonumber(Size),tonumber(Size))
GridSize = Vector2.new(math.floor(Frame.AbsoluteSize.X / (Frame.UIGridLayout.CellSize.X.Offset + Frame.UIGridLayout.CellPadding.X.Offset)),math.floor(Frame.AbsoluteSize.Y / (Frame.UIGridLayout.CellSize.Y.Offset + Frame.UIGridLayout.CellPadding.Y.Offset)))
GenerateGrid()
end)
Settings.Play.MouseButton1Click:Connect(function() -- This runs when the user wants to itterate generations.
Playing = not Playing
if Playing == true then
Settings.Play.Text = "Pause"
Settings.One.Visible = false
else
Settings.Play.Text = "Play"
Settings.One.Visible = true
end
end)
Settings.One.MouseButton1Click:Connect(function() -- One generation
Generation()
end)
GenerateGrid() -- Generates start grid
while wait(0.3) do
if Playing == true then
Generation()
end
end