BGE python: how to randomly pick an object from a list without picking it again

greets fellow blenderheads.
Context: I have a bunch of objs in a hidden layer. I would like to add a random object to the scene every time the player clicks the cone object. The only constraint is that once an object has been selected, it should not be eligible for selection again, as it has already been added. See attached blend file with the example setup.

I tried using a while loop (see below) but it didn’t work.


#var declarations
hidden_objects = [obj for obj in scene.objectsInactive if 'img' in obj.name]
random.shuffle(hidden_objects)
random_image = random.choice(hidden_objects)
drawn_objects = []
while random_image not in drawn_objects:
         drawn_objects.append(random_image)     
scene.addObject(random_image)

Then I considered the current implementation (see blend file) which involves picking an object from a list and then removing the object from the list. Which kinda works but not in the way intended (i.e. more than a single click is required).

Strangely enough, testing both these code snippets in Python works as expected. What’s happening? Any suggestions are welcome.

Attachments

sampleFile.blend (676 KB)

Take it from the list. That is all you need to do.

The design is simple: select objects only from a the selected objects. As an already selected object is not selected anymore it should not be available as selectable object after selection.

According to that design your code is not correct. The list of available objects “hidden_objects” does not just contain selectable objects. Either you remove them before selecting or you do not add them at all.


try:
    selected_objects = owner["selected objects"] # just an example
except KeyError:
    selected_objects = []
    owner["selected objects"] = selected_objects
    
selectable_objects = [obj for obj in scene.objectsInactive 
                        if 'img' in obj.name and
                        obj not in selected_objects]

selected_object = random.choice(selectable_objects)
selected_objects.append(selected_object)

added_object = scene.addObject(selected_object)


hidden_objects = [obj for obj in scene.objectsInactive if 'img' in obj.name]

you run this every time you click the Cone ,and this rebuild the list prob. not what you want.

i made you code work here.


def run(cont):
    scene = bge.logic.getCurrentScene()
    own = cont.owner
    obj = scene.objects 
    mo = cont.sensors["mo"]
    hit = mo.hitObject
    click = cont.sensors["click"]
    value = own["value"]
    
    #create a list of hidden objs    
    if "hidden_objects" not in own:
        own["hidden_objects"] = [obj for obj in scene.objectsInactive if 'img' in obj.name]
    
    if len(own["hidden_objects"] )>0:
        if mo.positive and click.positive:
            if hit == scene.objects["Cone"]:
                random.shuffle(own["hidden_objects"]) 
                print("initial object list: ", own["hidden_objects"])
                item = random.choice(own["hidden_objects"]) #pick item 
                print("item picked: ", item) 
                own["hidden_objects"].remove(item) #remove item 
                print("new object list: ", own["hidden_objects"])
                scene.addObject(item)
        


Many thanx folks, got it to work using Monster’s idea.
I knew how to bypass the limitation of the list being recreated from scratch with every mouse click.

Here’s the final code for reference.


#import stuff
import bge
import random
from random import shuffle

#list initialization
def get_images(cont):
    scene = bge.logic.getCurrentScene
    own = cont.owner
    if not "image_items" in own.getPropertyNames():
        own['image_items'] = []
    return (own['image_items'])

#main funct
def run(cont):
    scene = bge.logic.getCurrentScene()
    own = cont.owner
    obj = scene.objects 
    mo = cont.sensors["mo"]
    hit = mo.hitObject
    click = cont.sensors["click"]
    value = own["value"]
    
    if mo.positive and click.positive:
        if hit == scene.objects["Cone"]:
            try: 
                selected_objects = get_images(cont) #list initialization
                print("selected objects: ", selected_objects)
                selectable_objects = [obj for obj in scene.objectsInactive \
                if 'img' in obj.name and obj not in selected_objects]
                print("selectable", selectable_objects)
                selected_object = random.choice(selectable_objects)
                print("item selected: ", selected_object)
                selected_objects.append(selected_object)
                print("selected objects AGAIN", selected_objects)
                scene.addObject(selected_object)
                print("added object 2 scene!")
            except IndexError:
                pass

Haven’t tested this yet but looks like an elegant solution! :slight_smile:
I like the idea of using a hidden_objects var in the object instead of calling a function to initialize the list so that it’s not overwritten with every mouse click.