Hi all,
I have got a bit of a problem that I haven’t managed to solve in Lua so far.
I have a table, let’s say this one: items_table = {1, 2, 3, 4, 5}. Each time the player clicks, I would like to take a random number from this table. My problem: these numbers should not repeat but I cannot just remove the number from the table, I need the table intact. In python, I would append the number to another list, compare both lists and if the number is in the second list, pick another number. But there is no “is in” or “is not in” in Lua like there is in python.
How could I go about doing this in Lua? Any hint would be greatly appreciated.
Perhaps if the “other” container is a table (and not a list).
Then you can check if the return value is nil
local used = {}
-- v is the value from items_table
-- 1 is just some value to make it not nil
used[v] = 1
is_used = used[v] ~= nil
3 Likes
Thank you @Mathias_Westerdahl, I have tried something similar, but yours is a better idea. Will try out immediately.
2 Likes
Update: Here once lived slightly odd code - now removed. Nice, short and much better code see in my post further below.
4 Likes
This is an old thread - but the code I posted leaves wanting and I don’t want folks to use dodgy code.
Here is a valid version - I want to thank @ross.grams for it.
--[[
pick a random number from a table
produce a non-repeating number each time
and when restarting, do not pick the last one that was picked before
]]
local numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
local len_numbers = 10
local last_number
local function pick_random()
local random_index, random_number
if #numbers == 0 then
for i = 1, len_numbers do
table.insert(numbers, i)
end
end
repeat
random_index = math.random(#numbers)
random_number = numbers[random_index]
until random_number ~= last_number
table.remove(numbers, random_index)
last_number = random_number
return random_number
end
function init(self)
msg.post(".", "acquire_input_focus")
math.randomseed(os.time())
math.random()
end
function on_input(self, action_id, action)
if action_id == hash("touch") and action.pressed then
local number_picked = pick_random()
print(number_picked)
end
end
5 Likes
I decided to tackle the problem myself. The following approach requires a separate list of indices but avoids too many calls to table.remove. Not sure if it is more performant though.
local function shuffle(list)
local n = #list
for i = 1, n - 1 do
local k = math.random(i, n)
list[i], list[k] = list[k], list[i]
end
return list
end
function new_random_picker(list)
local n = #list
local index_list = {}
for i = 1, n do
index_list[i] = i
end
shuffle(index_list)
local pos = 0
return function()
pos = pos + 1
local index = index_list[pos]
if index then
return list[index]
else
local last = list[index_list[n]]
repeat
shuffle(index_list)
until last ~= list[index_list[1]]
pos = 1
return list[index_list[1]]
end
end
end
Could be used like this:
local numbers = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }
local random_picker = new_random_picker(numbers)
for i = 1, 20 do
print(random_picker())
end
2 Likes
Here is a variation using a closure:
local function setRandList()
local randList,one ={}
return function ()
if 0 == #randList then
local n = {1,2,3,4,5,6,7,8,9,10}
local nLen,nIndx = #n
for i = 1,nLen do
nIndx=math.random(nLen+1-i)
randList[i]=table.remove(n,nIndx)
end
if one and randList[#randList]==one then
r=math.random(#randList-1)
randList[r],randList[#randList]=randList[#randList],randList[r]
end
one = randList[1]
end
return table.remove(randList)
end
end
test=setRandList()
for i=1,31 do
print(test())
end
1 Like
@lzralbu and @iotaHum: thank you both very much for posting your versions to solve this problem. Something for me to study this afternoon!