Math.random confusion

So, I have a game where objects move down the screen from a randomly selected x location (there are 5 possible lanes). However, I want to make it so that the objects do not come down in three successive lanes. So if you look at the next function I tried to store the last 2 locations and then if the new location makes it so that there are 3 in a row then I tried to make it reselect the location using math.random, and then when the new location it should exit the while loop. My code does not work though, as when I test it there are still objects coming down in three successive lanes :


displayMode( FULLSCREEN)
supportedOrientations(CurrentOrientation)

function setup()
    dy=-5
    count=0
    loc={342, 433, 523, 608, 703}
    car={}
    car1={}
    table.insert(car, "Planet Cute:Character Boy")
    table.insert(car, "Planet Cute:Character Cat Girl")
    table.insert(car, "Planet Cute:Character Horn Girl")
    table.insert(car, "Planet Cute:Character Pink Girl")
    table.insert(car, "Planet Cute:Character Princess Girl")
    next()
end

function next()
    x = math.random(1,5)
    while (last == {1,2} and x==3) or (last=={2,3} and x==4) or (last =={3,2} and x ==1) or (last=={3,4} or x==5) or (last=={4,3} and x==2) or (last=={5,4} and x==3) do
        x = math.random(1,5)  
    end
    if #last == 2 then
        for i=#last,1,-1 do
            table.remove(last,i)
        end
    end
    s = math.random(1,5)
    table.insert(car1,vec3(s,x,HEIGHT))
    table.insert(last, x)
end

function draw()
    background(52, 172, 22, 255)
    for a,b in pairs(car1) do
        sprite(car[b.x],loc[b.y],b.z)
        b.z=b.z+dy
        if b.z<0 then
            table.remove(car1,a)
            return
        end
    end
    count=count+1
    if count>50 then
        next()
        count=0
    end
end

Your problem stems from the fact that you cannot compare different tables with the == operator, even if they have the same contents:

last = {1,2}

print( last == {1,2} ) -- this is false

This is because even though they have the same contents, these are two different tables.

A better algorithm (and more efficient) might be to compute the “valid lanes” each time. Then randomly select a valid lane to spawn on from the list.

So for example, your list of valid lanes starts at {1,2,3,4,5} and if you have a situation where you spawn on 1,2 then you make sure your valid lanes only contains {1,2,4,5}.

Here’s a small example that only spawns at empty places:

function setup()
    dots = {0,0,0,0,0}
    
    parameter.action("Add", add)
    parameter.action("Reset", function() restart() end)
    
    h = HEIGHT/(#dots+1)
end

function draw()
    background(45)
    stroke(255); strokeWidth(5)
    fill(255)
    for i,v in ipairs(dots) do
        ellipse(WIDTH/2, i*h, 5+20*v)
    end
end

function add()
    local list = {}     -- The list that will contain empty indices
    
    -- This loop saves empty indices to 'list'
    for i,v in ipairs(dots) do
        if v == 0 then table.insert(list, i) end
    end
    
    -- 'index' is picked randomly from list
    local index = list[math.random(1, #list)]
    print("index: ", index)
    
    -- Change the index'th dot
    dots[index] = 1
end

However, another way of doing this is by keeping the list of empty indices updated every time an enemy spawns or disappears. That way, the program doesn’t have to run a for loop every time.

@RedCoder1 Here is an example that might work for you. Pass it 1 value and it will return a random value from 1,5 that’s not equal to the original value. Pass it 2 values and it will return a random value from 1,5 that is not equal to the original 2, and will not have 3 values in a row.
I added some code to show a combination of 1 or 2 values passed. The values in [ ] are the ones passed. The other value is what is returned. Just call next with 1 or 2 values.


function setup()
    -- passing 1 number
    for x=1,5 do
        print("[ "..x.." ]  ",next(x))
    end
    print()
    -- passing 2 numbers
    for x=1,5 do
        for y=1,5 do
            print("[ "..x..","..y.." ]  ",next(x,y))
        end
    end
end

function draw()
    background(40, 40, 50)
end

function next(p1,p2)
    if p2==nil or p1==p2 then
        p2=0
    end
    while 1 do
        n=math.random(5)
        if n~=p1 and n~=p2 then
            if math.abs(p1-n)~=1 and math.abs(p2-n)~=1 then
                return n                    
            end 
            if math.abs(p1-p2)~=1 and math.abs(p1-n)~=1 then
                return n
            end
            if math.abs(p1-p2)~=1 and math.abs(p2-n)~=1 then
                return n
            end
        end
    end
end

Untested

local a,b = -1,-1
function next()
    local i
    if b == a + 1 then
        i = math.random(1,4)
        if i > b then
            i = i + 1
        end
    elseif b == a -1 then
        i = math.random(2,5)
        if i < b then
            i = i - 1
        end
    else
        i = math.random(1,5)
    end
    a,b = b,i
    return i
end

Thank you all who replied my game now works as intended =)