Path Randomizer

Heya! I’m making a minigame based on a random Path from point A to Point B, i would like to make it random without using a table with pre-organized paths, if possible.

1

What should this script do:
The script should make a path from point A (right) to point B, and making all the exagonal parts green and with CanCollide on true, the other parts will be red and with CanCollide on false

I don’t ask for an entire script, just someone that knows if is possible explain me how to do it, thank you all :smile:

2 Likes

Well I’m not exactly sure how your game works but I think you can use math.random() or math.seed() functions. I can’t really tell you how to implement them because, again, idk your game but these give reliable random results.

You should create your own paths in replicated storage and put them in a model or folder. Then with your script, you can get a table of those paths and then randomize them using the math.random() function.

So actually using pre-created paths and randomize the level, hoping the players will not memorize them

I thinked yet about it and could be a solution if i don’t find any else

I was wondering if creating a Grid could create a random path

Better explaination:

We have a grid from A to E (Vertical) and from 1 to 5 (Horizontal)

Every square will be called A1,C2, D4 and so on according to the grid position

Now, A3 (TopCenter) and E3 (BottomCenter) will be End(A3) and Start (E3)

From E3 i want to get the Position of the square, example (0, 0, -2)

The center will be C3 with the position of (0,0,0)

According to the grid now the Start point will have a Math.Random value from 1 to 100.

If the number will be from 1 to 33 the square will clonate and move to a square on his left, on position (-1,0,-2) aka E2

At this point we have E3 and E2 as square used for the path, now E2 will repeat the Math.Random from 1 to 100 and will have 50% of chance for going up and 50% for left because the right is already picked and the bottom has nothing

I will use table to declare Picked and UnPicked squares

Also the grid will divide in more 3 Squares

The first square will be the center, named as 0 or C3-0

The second square will have all the 8 squares that are close to the center and will be marked as 1 or B3-1,D4-1 and so on.
Same for the external square with the -2 for every square

Now, the path make script will also have another number that will increase on every square created, and when reached the max (ex. 3 squares) will force the next square to be inside the internal square (the -2) and the same will happen for this square, if the C Row has been reached, this system will work at reverse, making it go to the bigger square, this until it will teach the End aka A3-1

NOTE: The more is a square near to the exit, more will be his % of being picked as path.

I would like to see if you guys think this is possible and any questions about

I have got a friend who has made a maze generator. I’m guessing you can use that concept and implement that to your random path generator?

Should work as you both have a “maze”

Here’s the youtube video: https://www.youtube.com/watch?v=Vo-oXdS6atk
(The place would be in the description)

He has used Prim’s Algorithm for this project.

In computer science, Prim’s algorithm is a greedy algorithm that finds a minimum spanning tree for a weighted undirected graph. … However, running Prim’s algorithm separately for each connected component of the graph, it can also be used to find the minimum spanning forest.

Source from Wikipedia

I suggest you do some research on Prim’s algorithm and how the equation works to have a better understanding on how to generate it.

1 Like

You could use a depth-first search algorithm Depth-first search - Wikipedia (or a breadth first search for that matter).

Here’s a script that you can just insert to an empty place and it’ll generate a path, but on a square grid and with a really bad way of finding nodes’ neighbors.

Summary
local r = Random.new()

--Directions that a given node's neighbors can be in
local dirs = {
	Vector3.new(1, 0, 0),
	Vector3.new(-1, 0, 0),
	Vector3.new(0, 0, 1),
	Vector3.new(0, 0, -1),
}
	
local r = Random.new()

--Remove and return a random value from an array
local function pickRandom( t )
	local k = r:NextInteger(1, #t)
	local v = t[k]
	table.remove(t, k)
	return v
end

--Returns a list of a node's neigbors
local function getNeighbors( node )
	local ns = {}
	for _, d in pairs(dirs) do
		local p = node.Position/2 + d
		local n = game.Workspace.Graph:FindFirstChild(string.format("(%s, %s)", p.X, p.Z))
		
		if n then
			table.insert(ns, n)
		end
	end
	return ns
end

--Turns a (node -> node) dict into a path, where each key is a node and the corresponding value
--	is the node that came before in the search 
local function reconstructPath(previousNodes, endNode)
	local path = {endNode}
	repeat
		local cn = path[#path]
		local pn = previousNodes[cn]
		if pn then
			table.insert(path, pn)
		end
	until not pn
	return path
end

--Generates a path from startNode to endNode. Returns a list of nodes from endNode to stratNode.
local function generatePath( startNode, endNode )
	local visitedNodes = {} --A (node -> bool) dict indicating wether a given node has been visited before
	local openSet = {startNode} --A list of nodes to evaluate
	
	--A (node -> node) dict into a path, where each key is a node and the corresponding value
	--	is the node that came before in the search. Used by reconstructPath
	local previousNodes = {} 

	repeat
		--Choose a random node from openSet
		local currentNode = pickRandom(openSet) --also removes it from the open set
		visitedNodes[currentNode] = true
		
		--Find that nodes neighbors
		local neighbors = getNeighbors(currentNode)
		wait() --slow it down so we can see it being visualized
		if currentNode ~= startNode and currentNode ~= endNode then
			currentNode.BrickColor = BrickColor.Blue()
		end
		
		repeat
			--Choose one of the neighbors randomly
			local n = pickRandom(neighbors)
			
			if n and not visitedNodes[n] then
				--Visualize the connection from currentNode to this neighbor
				local p = Instance.new("Part")
				p.Size = Vector3.new(0.75, 0.75, 1)
				p.Anchored = true
				p.CFrame = CFrame.new(currentNode.Position, n.Position) * CFrame.new(0, 0, -1)
				p.BrickColor = BrickColor.Black()
				p.TopSurface = "Smooth"
				p.Parent = game.Workspace
				
				previousNodes[n] = currentNode
				visitedNodes[n] = true
				table.insert(openSet, n)
				
				if n == endNode then
					return reconstructPath(previousNodes, endNode)
				end				
			end
		until not n
	until #openSet == 0
end

--Set everything up
local function generateGraph(graphSize)
	local g = Instance.new("Folder")
	g.Name = "Graph"
	
	for x = 1, graphSize do
		for z = 1, graphSize do
			local p = Instance.new("Part")
			p.Size = Vector3.new(1, 1, 1)
			p.Anchored = true
			p.CFrame = CFrame.new(x*2, 0, z*2)
			p.Name = string.format("(%s, %s)", x, z)
			p.TopSurface = "Smooth"
			p.Parent = g
		end
	end
	
	g.Parent = game.Workspace
	return g
end

local graphSize = 20
local graphFolder = generateGraph(graphSize)
local nodes = graphFolder:GetChildren()
local startNode = graphFolder:FindFirstChild(string.format("(%s, %s)", 1, 1))
local endNode = graphFolder:FindFirstChild(string.format("(%s, %s)", graphSize, graphSize))

startNode.BrickColor = BrickColor.Red()
endNode.BrickColor = startNode.BrickColor

local path = generatePath(startNode, endNode)

--Traverse the path, visualizing it along the way
if path then
	for _, node in pairs(path) do
		wait()
		if node ~= startNode and node ~= endNode then
			node.BrickColor = BrickColor.new("New Yeller")
		end
	end
else
	print("NO PATH")
end

If you come up with a way of getting the neighbors of any given hex, the algorithm still works the same. Except for all the visualization stuff, that only works for this specific grid that the script generates.

I’ll study that script too for sure, thank you for sharing :smile:

1 Like