Prevent spawning 3 objects in row

Hello!
I have following .blend:
Cookie Lover Packed
Where Spawner.py spawns obstacles randomly. Unfourtunately, sometimes there spawn 3 tall obstacles in row(each side by side to other) and player is just unable to pass. How do I solve this?
The screen of problem:
http://s30.postimg.org/j19gzdoe7/The_Bad_Problem.png

if own[‘Spawned’]!=“Empty”:----and-----------------own[‘Spawned’]==“Empty”
random--------------------------------/

if own[‘Spawned’]==“Empty”----------python


import bge
cont = bge.logic.getCurrentController()
own = cont.owner


if type(own['Spawned']) is str:
    own['Spawned'] = int(bge.logic.getRandomFloat()*2)
    if own['Spawned']==0:
        added=bge.logic.getCurrentScene().addObject('Object1',own,0)
    if own['Spawned']==1:
         added=bge.logic.getCurrentScene().addObject('Object2',own,0)
    if own['Spawned']==2:
         added=bge.logic.getCurrentScene().addObject('Object3',own,0)

Well, can you give me more details on that? I still don’t get it surely:(

You could keep a counter of how many times your random element chooses a ‘tall block’.

Before you generate your objects, have a variable hold the number of times ‘Obstacle2’ is spawned.


tall_blocks = 0

on line 56 of spawner.py, make it look something like:


if sp['spawn'] == 2 and tall_blocks >= 3:
    if random.randint(0,1) == 0:
        sp['spawn'] -= 1
    else:
        sp['spawn'] += 1
block = self.scene.addObject("Obstacle" + str(sp["spawn"]), sp, 0)
if block.name == 'Obstacle2':    tall_blocks += 1

It is ugly and hackish, but might do the trick. It’s basically checking for the unique case where you have 3 tall blocks, and forces one of them to be something else (either Obstacle1 or Obstacle3, neither of which are impassible).
I would suggest re-writing your random generator, but I understand you are doing this for a jam, and might not have the time to do so :slight_smile:

You’re right. I will try counting tall_blocks but will the property reset to zero each frame(which is what I need)?

check this out

Attachments

randomDropper.blend (494 KB)

Add ‘tall_blocks=0’ as the first line in your spawn() method, and tall_blocks will reset to 0 as soon as that method is called, yes.

@BPR, your example is a bit different case than mine.

@Nines, OK! Gotta try, I hope it works…

Do you want to prevent to add three objects at once?
or
Do you want to prevent blocking the way?

I want to prevent spawning 3 objects with name “Obstacle2” at once so that they can’t make unpassable obstacle.

:smiley:

so it would be fine when there are two objects (which makes an unpassable obstacle)?

I think you are missing a pre-check that tells you if the chosen position is ok or not.

Unpassable obstacle is made only when 3 objects are in a row. I don’t know how to pre-check the position.

I looked at your file, and now I understand your problem. The above statement is still valid: " would be fine when there are two objects ?"

Your problem is that you generate three values. This means you always block the way. It is just luck that it is not blocked.

I suggest you hold your head 90° to the floor and look at the problem again.

Maybe you see … you can simply create two (unpassable) obstacles. The third is always passable.

All you need to decide is what obstacle to use (you already decided if it is passable or not).

Alternative you can decide if none, one or two obstacles are unpassable. Then you decide which one of them is what.

Oh, do you mean choosing random for each of theese spawners seperately?

Like:


passable = random.randint(0,2)
passables = [0, 1, 3, 4, 5]

if passable == 0:
    spawner1["spawn"] = random.choice(passables)
    spawner2["spawn"] = random.randint(0, 5)
...

yes and no

your requirement adds a relation between the chosen objects.

You can’t accept all combinations. To avoid fail-throws (which costs time without knowing when you get a valid one) you better throw it in a way that invalid combinations will never happen.

In your case you do multiple throws each throw has different purpose, but a relation to the previous throw. It gives you a lot of control how your map is generated.

Throw 1 (number of blockers)
decide how many un-passable obstacles to generate (0…2) which gives you the number of passable obstacles too.

Throw 2a (type of blockers)
for each un-passable obstacle throw what type of obstacle to add

Throw 2b (type of passable)
for each passable obstacle throw what type of object to add

Your example would match 2b

Remark:

This method allows you any number of lanes. As each lane is treated the same, I suggest to loop over the lanes rather than hardcode each of it.

Indeed you better throw 1) before looping over the lanes :D.

Could you do a simple demo, please? I just can’t imageine what you said in action. I understand it well, but I am not sure how to do it in script.

Here is a sample code.

rowBuilder.py


import bge
import random

PASSABLES = [" A", " B", " C"]
BLOCKERS = ["1 ", "2 ", "3 ", "4 "]

ROWSIZE = 3

#--- BGE callables
def setupLevel():
    'To get the same level with the same levelNumber'
    
    # Do not forget to check the sensors!
     
    levelNumber = getLevelNumber()
    random.seed(levelNumber)
    
    # It is just a demonstration ... so we simply print it
    print("start level",levelNumber)

def generateRow():
    # Do not forget to check the sensors!
    rowSize = getRowSize()
    
    # Throw 1
    numberOfBlockers = random.randrange(rowSize)
    numberOfPassables = rowSize-numberOfBlockers
    
    # Throw 2a and 2b
    blockers = pickItemsFrom(BLOCKERS, numberOfBlockers)
    passables = pickItemsFrom(PASSABLES, numberOfPassables)
    
    # Throw 3
    row = createRow(blockers, passables)
    
    createRowObjects(row)

#--- Internals
def getLevelNumber():
    return getProperty('level', 1)
        
def getRowSize():
    return getProperty('lanes', ROWSIZE)

def getProperty(propertyName, defaultValue):
    return bge.logic.getCurrentController().owner.get(
        propertyName, defaultValue)
        
def pickItemsFrom(availables, size):
    'Returns a list with size time randomly picked items from availables.'
    return [pickItemFrom(availables) for index in range(size)]
            
def pickItemFrom(availables):
    'Returns a single uniformed pick from available'
    return random.choice(availables)

def createRow(blockers, passables):
    'Creates a row and unifromly randomizes the order.'
    row = blockers + passables
    random.shuffle(row)
    return row

def createRowObjects(row):
    'Adds Objects according to the items of row'
    # as this is a demo it simply prints them
    # I guess you call addObject somewhere
    output = ""
    for objectType in row:
        output += " | " + objectType
    print(output)

It has two entry points:

A) (optional) Level setup:
rowBuilder.setupLevel

It ensures for a given Level number you get the exact same obstacle sequence. If you do not call it … you get a new sequence with each run. This will also happen on level 0.

Btw. it does not needs to be called by the same object as B), But B) needs to be called after A)

B) that is what you are looking for - generate a row with x lanes:
rowBuilder.generateRow

I hope it is somehow understandable. I added comments that you can see when to throw the dice.
The snippets always throws uniform random numbers. It means everything has the same likelihood. If you want a different likelihood you can adjust #Throw 1, pickItemFrom() and createRow().

E.g. you might want an higher likelihood on 2 blockers in higher levels. And/or an higher likelihood for blockers with higher index on higher level.

You do not need to use strings as BLOCKERS, PASSABLES. You can choose whatever you like. Even game objects would be possible. I suggest to use object names. That makes it easier to use addObject() in createRowObjects().

Attention: the above sample does not perform any sensor checks. It is up to you to ensure they run at the time you want them to run (e.g. avoid running when the sensors turn “not positive”).

Here is a demo output:


Blender Game Engine Started
start level 3
 |  C |  A |  C
 |  B | 1  | 1
 |  C |  B |  C
 |  C |  A | 2
 | 1  | 1  |  A
 | 1  |  B |  B
 | 4  | 4  |  C
 |  A |  A |  B
 |  B |  C |  B
Blender Game Engine Finished

Blender Game Engine Started
start level 4
 |  B |  C |  A
 |  A |  A |  A
 |  A |  C |  C
 |  A |  B |  A
 | 2  |  B |  A
 |  C |  C |  B
 |  B |  B |  A
 |  C |  B |  C
 | 4  |  B |  C
Blender Game Engine Finished

I hope it helps

PS:
I just checked - Module random stores the seed by itself. That means if you generate a random number with that module at another Python call, you will influence the sequence of the level.