Nulled array returning after function

  1. What do you want to achieve? Keep it simple and clear!

Ive been coding a WFC algorithm and have been running into a very frustrating bug.

  1. What is the issue? Include screenshots / videos if possible!

The generator:GenChunks() function takes in a 3 dimensional array where each entry (chunk) is an array of numbers called the domain. The generator:Collapse() function chooses a random one of the domain entries and is meant to then remove the unloaded chunk and place it in self.Chunks(). Then the changes are “propagated” to other chunks and the cycle repeats. Oddly enough the unloaded chunk that is meant to be removed seems to return after collapse, not only does it “come back from death” its also changed into the loaded version of the chunk?

function generator:Collapse(chunkCoords)
	if not chunkCoords then return end
	
	local x,y,z = chunkCoords.X,chunkCoords.Y,chunkCoords.Z
	print(x.." "..y.." "..z)
	local key = ((x+y+z+1)*(x+y+(x-z)+1)+y+x+z)%((x+z+y)*(x+z+y))+1000*x+y+z
	local random = Random.new(self.Seed*key+self.Seed)
	local tile = self.UnloadedChunks[x][y][z]
	
	if not tile then return end
	
	local randint 
	local prototype
	
	if not self:GenerateStructure(x,y,z) then
		local errorIdentify = nil
		
		if #tile.Domain ~= 0 then
			repeat randint = random:NextInteger(1,#tile.Domain) until random:NextInteger(1,self.Settings.Prototypes.Prototypes[tile.Domain[randint]].Weight) == 1
			prototype = self.Settings.Prototypes.Prototypes[tile.Domain[randint]]
			
		else
			
			prototype = self.Settings.Prototypes.Prototypes[1]
			
			errorIdentify = true
		end
		
		if not self.Chunks[x] then self.Chunks[x] = {} end
		if not self.Chunks[x][y] then self.Chunks[x][y] = {} end
		
		self.Chunks[x][y][z] = chunkState.new(chunkCoords, prototype)
		if not errorIdentify then
			self.Chunks[x][y][z].Domain = tile.Domain[randint]
		else
			self.Chunks[x][y][z].Domain = 1
		end
		self.UnloadedChunks[x][y][z] = nil
		print("COLLAPSE: "..x.." "..y.." "..z)
		print("set it to nil")
		
		self:Propagate(chunkCoords)
		print(self.UnloadedChunks[x][y][z])
		
		local models = prototype.Model:GetChildren()
		local model
		
		local totalWeight = 0
		local weightArray = {}
		
		for i,v in pairs(models) do
			weightArray[i] = v.Weight.Value
			totalWeight += weightArray[i]
		end
		
		table.sort(weightArray)
		
		local random1 = Random.new(self.Seed*key+key*key+self.Seed)
		local random2 = Random.new(self.Seed%key*key - self.Seed)
		local randomint = random1:NextInteger(1,totalWeight)
		
		for i,v in pairs(weightArray) do
			local index = v
			
			if (i-1)>=1 then
				for _=1,i-1 do
				
					index += weightArray[_]
				end
			end
			
			
			
			if (randomint <= index) then
				local candidates = {}
				
				for _,mdl in pairs(models) do
					if mdl.Weight.Value == v then
						table.insert(candidates,mdl)
						
					end
				end
				
				model = candidates[random2:NextInteger(1,#candidates)]
				break
			end
			
		end
		
		self.Chunks[x][y][z].Model = model
		
		print(self.UnloadedChunks[x][y][z])
		
		return self.Chunks[x][y][z]
	end
	
end

function generator:GenChunks(array)
	
	self.UnloadedChunks = array
	
	local lowestEntropyChunkCoords
	local previousEntropy = nil
	
	local check = false
	
	--Propagates all currently loaded chunks to update the UnloadedChunks array NOTE: removed chunk destruction portion
	if self.Chunks then
		for i,v in pairs(self.Chunks) do
			for j,x in pairs(v) do
				for k,y in pairs(x) do
					if not y.Domain then continue end
					
					self:Propagate(Vector3.new(i,j,k)) 
				end
			end
		end
	end
	
	--loops through chunks, finds chunk with lowest entropy value, collapses chunk
	local repeats = 0
	
	local returnArray = array
	repeat 
		
		if repeats%10 == 0 then
			wait()
		end
		
		check = false
		previousEntropy = nil
		lowestEntropyChunkCoords = nil
		
		for i,v in pairs(self.UnloadedChunks) do
			for j,x in pairs(v) do
				for k,y in pairs(x) do
					
					print("LOOP: "..i.." "..j.." "..k)
					printTable(y)
					local entropy = self:computeEntropy(y)
					
					if previousEntropy then
						if entropy <= previousEntropy then
							previousEntropy = entropy
							lowestEntropyChunkCoords = Vector3.new(i,j,k)
							check = true
						end
					else
						previousEntropy = entropy
						lowestEntropyChunkCoords = Vector3.new(i,j,k)
						check = true
					end
					
				end
			end
		end
		
		if check == true then
			returnArray[lowestEntropyChunkCoords.X][lowestEntropyChunkCoords.Y][lowestEntropyChunkCoords.Z] = self:Collapse(lowestEntropyChunkCoords)
			print(returnArray[lowestEntropyChunkCoords.X][lowestEntropyChunkCoords.Y][lowestEntropyChunkCoords.Z].Position)
			print(self.UnloadedChunks[lowestEntropyChunkCoords.X][lowestEntropyChunkCoords.Y][lowestEntropyChunkCoords.Z])
			print("collapsed")
		end
		
		repeats += 1
	until check == false
	
	print("returning collapsed chunks")
	return returnArray
end
  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

Obviously Im not gonna find something about this on dev hub. Ive tried placing checks using print() in relevant positions within the two functions. The results show that the unloaded chunk is indeed equal to nil within the collapse function but is equal to an array after the function. There is a possibilitiy that the problem lies outside of these two functions though I strongly doubt it since nothing seems to directly change the UnloadedChunks array outside of the script

What Roblox version and platform are you using? Are you using any plugins or modules?

ROBLOX Studio Version: 0.432.0.394541

Operating System: Windows 10 Home

Platform: Windows

Are you able to consistently reproduce this issue? If so what are the steps?

Yes. Run the generator:GenChunks() function with a 3 dimensional array of arrays of numbers as the argument. The function will collapse the chunks and return the modified 3 dimensional array. Print the array and it will look the same as the input except that the unloaded chunk will have become an array of numbers instead of nil