math.Random(4) runs out

Hi,

So I have a variable which is obstacles. There are 4 obstacles that are generated at random using

obstacle=math.random(4)

It works and so generates randomly any combination of 1-4 but it also runs out.

In other words it creates say obstacles in the order of 3,4,1,2 and then stops working.

Is it because that is what the (4) is doing and I need another number in there, or is it because it actually creates a 0 as well and I haven’t defined that?

If so how do I define that the math to do as random is between 1 and 4?

Thanks,
Major

@Majormorgan - try this

obstacle=math.random(1,4)

Thanks @Ignatz. I think it wasn’t what I thought after all, but what I know now is I need to reset the position of the objects once they pass the screen so that when they are called again by the math they appear from where they are.

I’ve still implemented the (1,4) so it means there is only objects 1-4 anyway. I just have to work out how to remove the object once its passed the screen and reset the xpostion.

Thanks,
Major

Ok so that’s all working now.

the interesting challange it to randomize without using the same object again, so if the current object is 1, then 1 should be left off the list somehow. Hmm… Takes a bit of thinking…

@Majormorgan - why not use a table, eg

objects={{1,math.random()},{2,math.random()},{3,math.random()},{4,math.random()}}
table.sort(objects,function(a,b) return a[2]<b[2] end)

so you have 4 mini-tables, the second item is just a fractional random number. We sort the mini tables based on these random numbers, which jumbles the numbers 1-4. Then your first object number is objects[1][1], your second one is objects[2][1], etc

or you can delete them when you’ve used them, like this

--get next object
O = objects[1][1] --take first object number in list
table.remove(objects,1) --remove first object from list
--repeat the same code to get the next object

@Majormorgan You need the Fisher-Yates algorithm (see this write-up).

function KnuthShuffle(n,odd)
    local l
    local o = 0
    local p = {}
    for k = 1,n do
        p[k] = k
    end
    for k = 1,n-1 do
        l = math.random(k,n)
        if l ~= k then
            p[k],p[l] = p[l],p[k]
            o = 1 - o
        end
    end
    if not odd and o == 1 then
        p[1],p[2] = p[2],p[1]
    end
    return p
end

This produces a random permutation of the numbers 1 to n. The odd parameter is to allow all permutations rather than just even permutations so you probably want to set that to true.

@Andrew_Stacey - what’s wrong with mine? It’s way shorter!

@Ignatz In terms of lines of code, perhaps, but not in terms of what actually happens. Doing the table.sort involves some sort of sort (I think we established once that it was a quicksort) which is more expensive than is needed for this.

@Andrew_Stacey - if you had 10,000 items, yes, but 4?

I think there is value in keeping the solution as easy as possible in situations like this. But I’m quibbling. All help is good help. :slight_smile:

@Ignatz Yeah, but the problem is that people don’t read the label and do use these little hacks on data sets of 10,000.

Thank you @Ignatz and @Andrew_Stacey

I think I actually do need to create a table version as per Ignatz’ example just because my code currently is using one math formula and switches the object on stage as opposed to generates one and attach it to the stage then removes later when it’s off screen.

Saying that tables like this do give me a headache but when I understand it I’m sure it will be cool.

I’ll let you know how I get on. Wish me luck!

I may be misunderstanding, but I have made some simple games like comet dodgers, and when I set the x position to change I will do something like setting, in your case obstacle, in an if then statement (If obstacle == nil then obstacle = math.random(1,4) ) Then when the asteroid is off the screen I set obstacle to nil again, so it will then give it another random x position.

Hi, so what I have are currently 4 obstacles. I’ll add up to four more later.

The screen travels at a constant speed and you avoid the obstacles. Currently I do a math.random(1,4) to pick one of the four obstacles and then it has to wait for them to come off the screen before I generate a new obstacle.

What I need to be able to do is let an obstacle get halfway across and then generate the next one.

Then once the obstacle is off of the screen then it can be deleted or put back into the obstacle pool to be called upon.

have you considered just having 4-8 different math.random for the 4-8 different obstacles? Sometimes the simplest way is the best way. If you need to generate more obstacles this wouldn’t work, but it should be easy for just 4. I would try copying and pasting then replacing data to save time.

@Majormorgan Is this something like what you’re after. Set up for 8 sprites.


displayMode(FULLSCREEN)

function setup()
    img1=readImage("Planet Cute:Character Boy")
    img2=readImage("Planet Cute:Character Cat Girl")
    img3=readImage("Planet Cute:Character Horn Girl")
    img4=readImage("Planet Cute:Character Pink Girl")
    img5=readImage("Planet Cute:Character Princess Girl")
    img6=readImage("Planet Cute:Enemy Bug")
    img7=readImage("Planet Cute:Gem Blue")
    img8=readImage("Planet Cute:Gem Green")
    img={img1,img2,img3,img4,img5,img6,img7,img8}   -- table of sprites
    -- screen objects x,y,z   z=0 off screen 1=left side  2=right side
    obj={vec3(0,0,0),vec3(0,0,0),vec3(0,0,0),vec3(0,0,0),
         vec3(0,0,0),vec3(0,0,0),vec3(0,0,0),vec3(0,0,0)}
    next()
end

function draw()
    background(40, 40, 50)
    for a,b in pairs(obj) do
        if b.z>0 then   -- draw sprite on screen
            sprite(img[a],b.x,b.y)  -- draw sprite
            b.x=b.x-5   -- move sprite to the left
        end
        if b.x<=WIDTH/2 and b.z==2 then -- on right side of screen
            b.z=1   -- 1 = now on left half of screen
            next()  -- get next sprite not on screen
        end 
        if b.x<-40 then -- off left side of screen
            b.x=0   -- reset everything to 0
            b.y=0
            b.z=0
        end
    end 
end

function next()
    local t={}  -- create table
    for z=1,#obj do -- loop for number of objects (8)
        if obj[z].z==0 then -- object not currently on screen
            table.insert(t,z)   -- put number in table
        end
    end
    local r=t[math.random(1,#t)]  -- random number from table
    obj[r].x=WIDTH+20   -- starting x position
    obj[r].y=math.random(50,HEIGHT-50)  -- starting y position
    obj[r].z=2  -- 2 = on right half of screen
end

@dave1707 That’s pretty inefficient. It can potentially loop for a long time.

Hi all. Thanks for the ideas and code examples. The first iteration of the game has gone off now for approval. In the next update I’ll address the frequency of the obstacles I generate with some of these ideas.

Thanks for the insights !

@Dave1707 thank you for your coding ideas too. I’ve got to rethink how I’m going to do it as there are classes for each object (there’s six now) and I will add more and more. Thanks all for your help

@Andrew_Stacey What do you consider a long time. There are only 2 sprites on the screen at any one time. That means there are 6 sprites waiting to choose from. In landscape mode, a new sprite is picked about every 2 seconds at the speed they’re going. So depending on the randomness of math.random, there’s only 2 numbers out of 8 that will come up as being in use and it will have to try to pick another number. I don’t consider milliseconds or less a long time every 2 seconds. From what I think the game will be doing, it isn’t a time critical program that needs every CPU cycle it can get.

@dave1707 Let me rephrase my objection then: that solution doesn’t scale well. As I said to Ignatz above, there’s a tendency (which I share myself) to simply cut-and-paste code without looking at how applicable it is to the new circumstances. Your code is fine in the situation you describe, but wouldn’t work in other similar situations.