Procedural Generation any advice?

I have been trying to achieve procedural generation à la The Binding of Isaac. I have never successfully created any procedural generation algorithm before so this is new territory for me.

The way I tried to re-create this is this way

Where the black cube with a red zero inside is the starting point where I would generate a room depending on the random number generated from 0 to 3. These are representing directions as
0 is -z
1 is +x
2 is +z
3 is -z

These numbers are named after a bool value inside the model of the room to determine if the door is taken by another occupied room in that direction.
image
These bools instances also have an attribute to determine by which room they are each taken from.

From that logic would it work ? I’m clearly lost here and would like some advice thanks in advance :slight_smile:

Here’s my script:

local SSS = game.ServerScriptService
local Rooms =game.Workspace.Rooms
local R0 = Rooms['0']
local rand
local RoomsTotal
local newRoom
local newPart
local ui = SSS:FindFirstChild('NumberRoom')
local function CreateRoom()
	local uis = ui:Clone()

	newRoom = Instance.new('Model')
	newRoom.Name = tostring(#Rooms:GetChildren())
	-- [[ Attributes
	local p0 = Instance.new('BoolValue')
	local p1 = Instance.new('BoolValue')
	local p2 = Instance.new('BoolValue')
	local p3 = Instance.new('BoolValue')

	p0:SetAttribute('RoomAssociatedWith', nil)
	p1:SetAttribute('RoomAssociatedWith', nil)
	p2:SetAttribute('RoomAssociatedWith', nil)
	p3:SetAttribute('RoomAssociatedWith', nil)

	p0.Name = '0'
	p1.Name = '1'
	p2.Name = '2'
	p3.Name = '3'

	p0.Parent = newRoom
	p1.Parent = newRoom
	p2.Parent = newRoom
	p3.Parent = newRoom
	--]]

	newRoom.Parent = Rooms

	newPart = Instance.new('Part')
	newPart.Size = R0.Room.Size
	newPart.Anchored = true
	newPart.Name = "Room"
	newPart.Parent = newRoom
	uis.Parent = newPart
	uis:FindFirstChild('RoomNumber').Text = #Rooms:GetChildren() - 1

	return newRoom

end

local function AbortOperation(room)
	for v,i in ipairs(Rooms:GetChildren()) do
		if i:FindFirstChild('Room').Position == room.Position and i.Name ~= room.Parent.Name then
			warn(room.Parent)
			return true
		end
		continue
	end
	return false
end
local function AbortIfDoesntRespectDoorLimit(room, numberOfDoorAllowed)
	--local count = 0
	--if room then
	--	for v,i in ipairs(Rooms:GetChildren()) do

	--		if i:FindFirstChild('Room').Position == room.Position + Vector3.new(-10,0,0) or
	--			i:FindFirstChild('Room').Position == room.Position + Vector3.new(10,0,0) or 
	--			i:FindFirstChild('Room').Position == room.Position + Vector3.new(0,0,10) or 
	--			i:FindFirstChild('Room').Position == room.Position + Vector3.new(0,0,-10)
	--		then
	--			count += 1
	--		end
	--		if count > numberOfDoorAllowed then
	--			return true
	--		end
	--	end
	--end
	--return false
end

local function SetRoom(RoomSelected, rand, doorAllowed)
	local TheNewRoom = CreateRoom()
	--print("No room there")
	warn('Creating a room at ' .. tostring(rand) .. ' door. On the room labeled ' .. RoomSelected.Name)

	local DoorTakenFromSelected = RoomSelected:FindFirstChild(tostring(rand))
	DoorTakenFromSelected.Value = true


	local DoorTakenFromNewRoom

	if rand == 0 then DoorTakenFromNewRoom = TheNewRoom:FindFirstChild('2') end
	if rand == 1 then DoorTakenFromNewRoom = TheNewRoom:FindFirstChild('3') end
	if rand == 2 then DoorTakenFromNewRoom = TheNewRoom:FindFirstChild('0') end
	if rand == 3 then DoorTakenFromNewRoom = TheNewRoom:FindFirstChild('1') end

	DoorTakenFromNewRoom.Value = true

	--if AbortIfDoesntRespectDoorLimit(TheNewRoom:FindFirstChild('Room'), doorAllowed) then TheNewRoom:Destroy() end
	if DoorTakenFromSelected.Name == '0' then TheNewRoom:FindFirstChild('Room').Position = RoomSelected:FindFirstChild('Room').Position + Vector3.new(0,0,-10) end
	if DoorTakenFromSelected.Name == '1' then TheNewRoom:FindFirstChild('Room').Position = RoomSelected:FindFirstChild('Room').Position + Vector3.new(10,0,0) end
	if DoorTakenFromSelected.Name == '2' then TheNewRoom:FindFirstChild('Room').Position = RoomSelected:FindFirstChild('Room').Position + Vector3.new(0,0,10) end
	if DoorTakenFromSelected.Name == '3' then TheNewRoom:FindFirstChild('Room').Position = RoomSelected:FindFirstChild('Room').Position + Vector3.new(-10,0,0) end


	if AbortOperation(TheNewRoom:FindFirstChild('Room')) then TheNewRoom:Destroy() end
	if TheNewRoom then
		DoorTakenFromSelected:SetAttribute('RoomAssociatedWith', TheNewRoom.Name)
		DoorTakenFromNewRoom:SetAttribute('RoomAssociatedWith', RoomSelected.Name)
	else

		
	end
	--Have to redo the set room as a whole doesnt work have to check every instance near the part to check i a room is there ? dunno sob
	--for v,i in ipairs(Rooms:GetChildren()) do
	--	if i:FindFirstChild('Room').Position == TheNewRoom:FindFirstChild('Room').Position + Vector3.new(-10,0,0) and i['3'] then
			
	--	end
		

	--end

	--if AbortIfDoesntRespectDoorLimit(TheNewRoom:FindFirstChild('Room'), doorAllowed) then TheNewRoom:Destroy() end
end

local function Main()
	newPart = nil
	newRoom = nil
	rand = math.random(0,3)
	RoomsTotal = math.random(0,25)

	while not Rooms:FindFirstChild(tostring(RoomsTotal)) do
		warn('Trying Step #1')
		RoomsTotal = math.random(0,25)
	end
	if #Rooms:GetChildren() <= 50 then
		local RoomSelected = nil
		if Rooms:FindFirstChild(tostring(RoomsTotal)) then
			RoomSelected = Rooms:FindFirstChild(tostring(RoomsTotal))
		end

		if RoomSelected and RoomSelected:FindFirstChild(tostring(rand)).Value == false then
			local doorAllowed = math.random(1 , 2)
			-- 1 -> 2 room available
			-- 2 -> 3 room available
			local count = 0
			for v,i in ipairs(RoomSelected:GetChildren()) do
				if i:IsA('BoolValue') and i.Value == true then
					count += 1
				end
			end
			if count < 2 and (doorAllowed == 1) then
				SetRoom(RoomSelected, rand, doorAllowed)
			end
			if count < 3 and (doorAllowed == 2) then
				SetRoom(RoomSelected, rand, doorAllowed)
			end
		end
	else 
		return false
	end
end


while true do
	while Main() ~= false do
		task.wait()
	end
	task.wait(2)
	for v,i in ipairs(Rooms:GetChildren()) do
		if i.Name ~= '0' then
			i:Destroy()
		end
	end
	Rooms['0']['0'].Value = false
	Rooms['0']['1'].Value = false
	Rooms['0']['2'].Value = false
	Rooms['0']['3'].Value = false
end

Sorry for ugly code I’m testing if it would work before optimizing anything :sob:

1 Like

That seems fine but don’t create value objects unless you actually need them. You can just store this information in variables / as table elements for each room. I.e. one table per room with elements pointing to connected rooms. You will need a way to pick which room to spawn a new room from, otherwise you’ll just get a line or + shape.

Using tables is a good idea I’ll try that !