Puzzle Flow Algorithm

Hi there,

1. What do you want to achieve? I want to create a puzzle flow algorithm in Lua, which can be used to solve puzzles like flow-based games where you connect colored dots with lines, ensuring that no lines overlap, and all dots are connected.
2. What is the issue? I have never created such an algorithm before and don’t know where to start. I’m looking for guidance on how to approach this problem in Lua.
3. What solutions have you tried so far? I have attempted to search for solutions and resources on the internet, including the Developer Hub, but I couldn’t find anything that specifically addresses my case or provides a step-by-step guide on how to create a puzzle flow algorithm in Lua.

Heres the code that I am working with currently, Its a piece of code I found on the internet while looking for this exact problem. Only problem with this code is I do not have any idea how the data they use is generated

Script Found on the Internet

local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- Color Palette
local ColorPalette = {
Index = 1,
BaseColors = {
Color3.new(1, 0, 0),
Color3.fromRGB(0, 120, 255),
Color3.fromRGB(189, 0, 255),
Color3.fromRGB(255, 154, 0),
Color3.fromRGB(1, 255, 31),
Color3.fromRGB(227, 255, 0)
},
Reset = function(self)
self.Index = 1
end
}

local RandomGenerator = Random.new()

function ColorPalette.NextColor(self)
if #self.BaseColors <= self.Index then
return Color3.fromHSV(RandomGenerator:NextNumber(), 1, 1)
end
self.Index = self.Index + 1
return self.BaseColors[self.Index]
end

-- Flow UI
local FlowUI = {
IsOpen = false,
Maid = nil,
OnFilled = nil
}

local function CreateGrid(width, height)
local grid = {}
for y = 1, height do
grid[y] = {}
for x = 1, width do
grid[y][x] = 0
end
end
return grid
end

function FlowUI.SetGrid(self, flowId, gridData)
self.FlowId = flowId
self.Grid = gridData
local gridHeight = #gridData
local gridWidth = #gridData[1]
self.GridClean = CreateGrid(gridWidth, gridHeight)
for y = 1, gridHeight do
for x = 1, gridWidth do
self.GridClean[y][x] = gridData[y][x]
end
end
ColorPalette:Reset()
self.Colors = {}
end

function FlowUI.ResetAlpha(self, value)
assert(value)
local gridWidth = #self.Grid[1]
for y = 1, #self.Grid do
for x = 1, gridWidth do
if self.Grid[y][x] == value then
self.Grid[y][x] = self.GridClean[y][x]
end
end
end
end

function FlowUI.Reset(self)
if self.Grid == nil then
return false
end
local gridWidth = #self.Grid[1]
for y = 1, #self.Grid do
for x = 1, gridWidth do
self.Grid[y][x] = self.GridClean[y][x]
end
end
end

FlowUI.Colors = {}

local FlowGui = script.FlowGui

function FlowUI.Draw(self)
local gridHeight = #self.Grid
local gridWidth = #self.Grid[1]
for y = 1, gridHeight do
for x = 1, gridWidth do
local cellId = ("%d.%d"):format(x, y)
local cell = FlowGui.Board.Inner:FindFirstChild(cellId)
if cell == nil then
cell = Instance.new("TextLabel")
cell.Name = cellId
cell.BorderSizePixel = 0
cell.BackgroundTransparency = 0
cell.TextSize = 20
cell.TextColor3 = Color3.new(0.1, 0.1, 0.1)
cell.Size = UDim2.new(1 / gridWidth * 0.5, 0, 1 / gridHeight * 0.5, 0)
cell.AnchorPoint = Vector2.new(0.5, 0.5)
cell.Position = UDim2.new((x - 1 + 0.5) / gridWidth, 0, (y - 1 + 0.5) / gridHeight, 0)
cell.Parent = FlowGui.Board.Inner
end
local cellValue = self.Grid[y][x]
if cellValue >= 0 then
local cellColor = self.Colors[cellValue]
if cellColor == nil then
cellColor = ColorPalette:NextColor()
self.Colors[cellValue] = cellColor
end
cell.BackgroundColor3 = cellColor
else
cell.BackgroundColor3 = Color3.new(0.1, 0.1, 0.1)
end
local textValue = self.Grid[y][x]
if textValue >= 0 then
cell.Text = tostring(textValue)
else
cell.Text = ""
end
end
end
end

function FlowUI.GetCoordFromMouse(self, mousePosition)
local boardAbsolutePosition = FlowGui.Board.AbsolutePosition
local boardAbsoluteSize = FlowGui.Board.AbsoluteSize
local x = math.floor(#self.Grid[1] * (mousePosition.X - boardAbsolutePosition.X) / boardAbsoluteSize.X) + 1
local y = math.floor(#self.Grid * (mousePosition.Y - boardAbsolutePosition.Y) / boardAbsoluteSize.Y) + 1
local value = self.Grid[y][x]
return x, y, value
end

local LocalPlayer = game:GetService("Players").LocalPlayer
local Maid = require(ReplicatedStorage.Maid)
local DirectionX = {0, 1, 0, -1}
local DirectionY = {-1, 0, 1, 0}
local UserInputService = game:GetService("UserInputService")

function FlowUI.Show(flowUI)
assert(flowUI.IsOpen == false)
flowUI.IsOpen = true
FlowGui.Parent = LocalPlayer.PlayerGui
flowUI.Maid = Maid.new()

local startX, startY, currentColor, startColor, endColor, drawing = nil, nil, nil, nil, nil, false

print(flowUI)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
local x, y, cellValue = flowUI:GetCoordFromMouse(input.Position)
local grid = flowUI.Grid
local valid = x >= 1 and x <= #grid[1] and y >= 1 and y <= #grid and cellValue >= 0

if not valid then
return
end

startX, startY, currentColor = x, y, cellValue
startColor, endColor = startX, startY
flowUI:ResetAlpha(currentColor)
flowUI:Draw()
drawing = true
end
end))

local function OnInputEnded(input)
if not drawing then
return false
end
drawing = false

if startX == endColor and startY == startColor then
flowUI:ResetAlpha(currentColor)
else
local isValid = false
for i = 1, 4 do
local newX, newY = startX + DirectionX[i], startY + DirectionY[i]
local grid = flowUI.Grid

local valid = newX >= 1 and newX <= #grid[1] and newY >= 1 and newY <= #grid
if valid and flowUI.GridClean[newY][newX] == currentColor and (newX ~= endColor or newY ~= startColor) then
isValid = true
end
end

if not isValid then
flowUI:ResetAlpha(currentColor)
else
if not flowUI.OnConnection then
error("No OnConnection function set!")
end
flowUI.OnConnection()
end
end
flowUI:Draw()
end

if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
OnInputEnded(input.Position)
end
end))

local function OnInputChanged(input)
if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then
local x, y, cellValue = flowUI:GetCoordFromMouse(input.Position)
if x == startX + 1 then
local valid = y == startY
if x == startX - 1 then
valid = valid or y == startY
if x == startX then
valid = valid or y == startY + 1
valid = valid or y == startY - 1
end
elseif x == startX then
valid = valid or y == startY + 1
valid = valid or y == startY - 1
end

if not valid then
return false
end

if x ~= startX or y ~= startY then
flowUI.Grid[y][x] = currentColor
startX, startY = x, y
flowUI:Draw()
end
end
end
end

if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then
OnInputChanged(input)
end
end
)
)

flowUI:Reset()
flowUI:Draw()
end
)
)

FlowGui.Board.Cancel.MouseButton1Down:Connect(
function()
flowUI:Hide()
end
)
)

FlowUI:Draw()

local Humanoid = LocalPlayer.Character:WaitForChild("Humanoid")
Humanoid.Changed:connect(
function()
Humanoid.Jump = false
end
)
)

local cursorX, cursorY, cursorLabel = 1, 1, nil

local function UpdateCursor()
local gridHeight, gridWidth = #flowUI.Grid, #flowUI.Grid[1]

if cursorX < 1 then
cursorX = 1
end
if gridWidth < cursorX then
cursorX = gridWidth
end

if cursorY < 1 then
cursorY = 1
end
if gridHeight < cursorY then
cursorY = gridHeight
end

if cursorLabel then
cursorLabel.Size = UDim2.new(1 / gridWidth * 0.5, 0, 1 / gridHeight * 0.5, 0)
end

local cell = FlowGui.Board.Inner:FindFirstChild((("%d.%d"):format(cursorX, cursorY)))
assert(cell)
cell.Size = UDim2.new(1 / gridWidth * 0.75, 0, 1 / gridHeight * 0.75, 0)

OnInputChanged(cell.AbsolutePosition + cell.AbsoluteSize * 0.5)
end

UserInputService.InputBegan:Connect(
function(input)
cursorY = cursorY - 1
UpdateCursor()
return
end
cursorY = cursorY + 1
UpdateCursor()
return
end
cursorX = cursorX - 1
UpdateCursor()
return
end
cursorX = cursorX + 1
UpdateCursor()
return
end
if input.KeyCode == Enum.KeyCode.ButtonA then
local cell = FlowGui.Board.Inner:FindFirstChild((("%d.%d"):format(cursorX, cursorY)))
local x, y, cellValue = flowUI:GetCoordFromMouse(cell.AbsolutePosition + cell.AbsoluteSize * 0.5)
local grid = flowUI.Grid
local valid = x >= 1 and x <= #grid[1] and y >= 1 and y <= #grid

if not valid then
return
end

if cellValue < 0 then
return
end

startX, startY, currentColor = x, y, cellValue
startColor, endColor = startX, startY
flowUI:ResetAlpha(currentColor)
flowUI:Draw()
drawing = true
end
end
end
)
)

UserInputService.InputEnded:Connect(
function(input)
if input.UserInputType == Enum.UserInputType.Gamepad1 and input.KeyCode == Enum.KeyCode.ButtonA then
local cell = FlowGui.Board.Inner:FindFirstChild((("%d.%d"):format(cursorX, cursorY)))
OnInputEnded(cell.AbsolutePosition + cell.AbsoluteSize * 0.5)
end
end
)
)

UpdateCursor()
end
end

local eventHandler = nil
local eventManager = nil

return {
Init = function(params)
eventHandler = params.Event
eventManager = params.em

function FlowUI.OnConnection()
print("Connection established.")
eventHandler:FireServer("m5z4m5of", FlowUI.FlowId, FlowUI.Grid)
end

function eventManager.ShowGrid(p24, p25)
FlowUI:SetGrid(p24, p25)
FlowUI:Show(FlowUI)
end

function eventManager.HideGrid(p26)
if FlowUI.IsOpen and (p26 == nil or FlowUI.FlowId == p26) then
FlowUI:Reset()
FlowUI:Hide()
end
end

return eventManager
end
}

Maid Script (REQUIRED)

local Maid = {}  -- Create a table named 'Maid'
local TaskManager  -- Declare a local variable 'TaskManager' to be defined later

do
-- Define a set of utility functions in a table
end,
DoCleaning = function(maid)
task:disconnect()  -- Disconnect the connection if it's an RBXScriptConnection
else
task:Destroy()  -- Destroy the task object if it's neither a function nor RBXScriptConnection
end
end
end
}

-- Alias Destroy function as DoCleaning

-- Create a metatable for the Maid
local MaidMetatable = {
__index = function(maid, key)
else
end
end,
__newindex = function(maid, key, value)
if value == nil then
tasks[key]:disconnect()  -- Disconnect an RBXScriptConnection if it exists
else
tasks[key]:Destroy()  -- Destroy the task object if it's not a function or RBXScriptConnection
end
end
else
tasks[key] = value  -- Assign a new task to the specified key
end
end
}

-- Define a function to create a new Maid object
return setmetatable({
Instances = {}  -- Initialize an empty table for instances (not used in the provided code)
}, MaidMetatable)
end
end

-- Alias MakeMaid and new functions to Maid

return Maid  -- Return the Maid table as the module

How it should look, again this is my failed attempt at trying to do this

The GUI Structure is looking like this screenshot below, if u are a scripter u can guess how the UI is looking after the Puzzle Flow Initialized

Model of the UI

puzzle.rbxm (13.9 KB)

Additional Details: I’m specifically interested in understanding the logic and steps required to implement a puzzle flow algorithm in Lua. I’m not asking for a complete script or system design but rather seeking guidance on how to approach this problem and where to start my coding process.

This is just a bump reply because I am really desperate

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.