How to make table loop through random values without repeating until all values have been used?

I have a map system for my game that cycles through a random map each time the round starts. I’ve tried countless different methods but I have no clue how to make the table that stores my maps (MapTable) pick a new random index from itself each time without repeating. I want the Table to cycle through all 20 maps without repeating a single one, and then restart.

Please help!

maybe try something like this you need your maintable to stay same but setup a temp table as a random map is choosen just remove it from the table when it hits 0 or is empty then reset it again to your mainmaptable

local MainMapTable = {
	1,2,3
}


local TempMapTable = {}

function ResetTempMaps() 
    TempMapTable = {}
	for i, map in ipairs(MainMapTable) do  -- setup a tempmaptable to remove maps from
		TempMapTable[i] = map
	end
end

function GetMap()  -- this gets a new map each time
	if #TempMapTable <= 0 then   -- this can be called somewhere before calling getmap but it will reset the tempmaps table using the mainmaptable
		ResetTempMaps() 
	end
	local MapKey = math.random(1,#TempMapTable)
	local Map = TempMapTable[MapKey]  -- gets a temp map
	table.remove(TempMapTable, MapKey)  -- remove it from temp table so its not choosen again
	return Map  -- return it
end
1 Like

Here’s one way you could do it:

local GameMaps={}

local function GetRandomMap()
	
	if #GameMaps<1 then GameMaps=workspace.Maps:GetChildren() end -- Point this to your maps folder, Once GameMaps is empty is will refill with all maps
	local Rnd=math.random(1, #GameMaps) -- Get a random number between 1 and maps not yet chosen
	local NewMap=GameMaps[Rnd] -- Select your new map
	table.remove(GameMaps, Rnd) -- Remove selected map from table so it can't be selected again
	return NewMap -- returns the result
	
end


local NextMap=GetRandomMap()

I added your code to my script, and it seems to work at first but then I get repeating maps. After the “GetMap” function runs and it returns “Map” I added this code after the “end”

local NextMap=GetMap()
		print(NextMap)
		local map = NextMap:Clone()

Should I put this code anywhere else?

1 Like

Same with Nyonic above you, I added your code and then

local NextMap=GetMap()
		print(NextMap)
		local map = NextMap:Clone()

It works but it doesn’t stop repeated maps. Not sure why

Choose a random unchosen map, if all maps are chosen, refresh. This may cause a problem where if a map is the last unchosen map, it may be chosen first after the refresh, so you also check if it wasn’t previously chosen.

local maps = {} -- Insert maps
local chosenMaps = {}
local prevMaps = {}

local function RandomMap()
	local remaining = #maps

	-- Pick random map that is not previously chosen
	local chosen = math.random(remaining)
	local map = maps[chosen]
	while table.find(prevMaps,map) do
		chosen = math.random(remaining)
		map = maps[chosen]
	end

	-- Mark as previously chosen
	table.insert(prevMaps,1,map)
	prevMaps[math.floor(#chosenMaps+remaining/2)] = nil

	-- Remove in unchosen maps
	table.remove(maps,chosen)
	table.insert(chosenMaps,map)
	if remaining == 1 then -- If all maps is chosen, refresh
		maps, chosenMaps = chosenMaps, maps
	end

	return map
end

-- How to run
local nextMap = RandomMap()

I tested it and it doesn’t repeat unless all maps have been chosen.

the method is certainly not through the tables, but still it is short and simple :slight_smile:

local maps=game.ReplicatedStorage.Maps -- maps folder

locql current=nil
local map


repeat wait()
map=maps:GetChildren()[math.random(#maps:GetChildren()]
until map.Name~=current

current=map.Name

puts the map into a variable if the map is not current, otherwise iterates over the new one and puts it into a variable

Are you sure there isn’t some other code interfering with the process? I just ran the code and it worked as expected:

 local GameMaps={}

local function GetRandomMap()

	if #GameMaps<1 then
		print("No Maps in GameMaps - Refreshing..")
		GameMaps=workspace.Maps:GetChildren() 
	end -- Point this to your maps folder, Once GameMaps is empty is will refill with all maps
	local Rnd=math.random(1, #GameMaps) -- Get a random number between 1 and maps not yet chosen
	local NewMap=GameMaps[Rnd] -- Select your new map
	table.remove(GameMaps, Rnd) -- Remove selected map from table so it can't be selected again
	return NewMap -- returns the result

end

for i=1, 20 do

	local NextMap=GetRandomMap()
	print("Map Chosen: "..NextMap.Name)
	task.wait()
end

Output:

10:32:50.200  No Maps in GameMaps - Refreshing..  -  Server - Script:6
  10:32:50.201  Map Chosen: Map_04  -  Server - Script:19
  10:32:50.258  Map Chosen: Map_05  -  Server - Script:19
  10:32:50.570  Map Chosen: Map_03  -  Server - Script:19
  10:32:50.576  Map Chosen: Map_02  -  Server - Script:19
  10:32:50.607  Map Chosen: Map_01  -  Server - Script:19
  10:32:50.882  No Maps in GameMaps - Refreshing..  -  Server - Script:6
  10:32:50.882  Map Chosen: Map_04  -  Server - Script:19
  10:32:51.316  Map Chosen: Map_03  -  Server - Script:19
  10:32:51.356  Map Chosen: Map_01  -  Server - Script:19
  10:32:51.382  Map Chosen: Map_05  -  Server - Script:19
  10:32:51.402  Map Chosen: Map_02  -  Server - Script:19
  10:32:51.431  No Maps in GameMaps - Refreshing..  -  Server - Script:6
  10:32:51.431  Map Chosen: Map_05  -  Server - Script:19
  10:32:51.515  Map Chosen: Map_04  -  Server - Script:19
  10:32:51.537  Map Chosen: Map_03  -  Server - Script:19
  10:32:51.551  Map Chosen: Map_02  -  Server - Script:19
  10:32:51.566  Map Chosen: Map_01  -  Server - Script:19
  10:32:51.587  No Maps in GameMaps - Refreshing..  -  Server - Script:6
  10:32:51.587  Map Chosen: Map_05  -  Server - Script:19
  10:32:51.599  Map Chosen: Map_03  -  Server - Script:19
  10:32:51.616  Map Chosen: Map_01  -  Server - Script:19
  10:32:51.633  Map Chosen: Map_02  -  Server - Script:19
  10:32:51.650  Map Chosen: Map_04  -  Server - Script:19

For clarity, there are just 5 folders in the Maps folder in my test.

local Game = game
local ServerStorage = Game:GetService("ServerStorage")
local MapsFolder = ServerStorage.Maps --Maps folder.
local RandomObject = Random.new()

while true do --Infinite loop.
	local Maps = MapsFolder:GetChildren() --Array of maps.
	repeat --Repeat loop.
		local Map = table.remove(Maps, RandomObject:NextInteger(1, #Maps)) --Remove map from array and use it for a round.
		--Round code here.
	until #Maps == 0 --End loop when no more maps available.
end

Fairly trivial pseudo-code to help steer you in the right direction.

Sorry in advance for the late reply, I haven’t been able to get to my computer until now. (Also thanks to everyone who replied, I will test each code out to see if it fixes this problem)

Ok so the code works beautifully, only 1 problem. I want only one of the maps to spawn in for a round, and then the order continue 1 by 1 as each round ends. Currently they all spawn in at once, and it makes my game kinda laggy. Any suggestions?

By repeating choosing a new index in the table until its not equal to LastChosen makes it randomized 100% of the times

local MapTables = {
    [1] = Blah,
    [2] = Bleh,
}
local LastChosen

function ChooseNewMap() 
     local Map = math.random(1,#MapTables)
     repeat  Map = math.random(1,#MapTables) task.wait() until Map ~= LastChosen
     LastChosen = MapTables[Map]
     return MapTables[Map]
end

This may look like a complicated article but it’s visualizations and math are very effecient. it will prevent stalling like a lot of recommendations here may.

We will be twisting a shuffle implementation into drawing from a deck of cards. The in-place shuffle we use is called Fisher-Yates.

Pros:

  • No infinite loops (very fast!)
  • No extra arrays/tables
local maps = {
    "Big City",
    "Desert",
    "Jungle",
    "Soup Isle",
}
local usedIndecies = 0

local function get_random_map(): string
    -- all of our cards are "used" we reset the deck
	if usedIndecies >= #maps then
		usedIndecies = 0
	end

    -- we get how many cards are marked as "Used"
	local unusedEnd = #maps - usedIndecies

    -- we do not draw from the end, these are "Used" cards
    -- as cards are drawn we shrink how many are available
	local index = math.random(1, unusedEnd)

	local gotItem = maps[index]
    -- swap our selected item with our "used" end of deck 
	maps[index], maps[unusedEnd] = maps[unusedEnd], maps[index]
    -- next draw cannot select the card we just swapped
	usedIndecies += 1
	return gotItem
end

Well, if your using my code example then you would remove the for i=1, 20 do loop, that was only added to show the function in action during many calls.
You would just use local NextMap=GetRandomMap() once before each round starts and clone/move the chosen map to the play area.

1 Like

Add all your items on another table, when choosing map, remove it from the table, when the table is empty “refill” it

Thanks to everyone who replied! I’ve learned a ton from each one of you guys. Also thanks to Sunny_Bunny, the method works, I just had to rearrange my code a bid to fit it in properly. Appreciate the help guys

1 Like