How to randomize instances without repeating the previous one?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By tehgaming

Hi all. Sorry if this was asked before.

I am trying to instance mob scenes from arrays randomly but even with randomize() and randi, they can some times still instance the same one many, many times in a row.

Is there a way to truly randomise instances without it repeating the previous var and without removing it from the array?

This is an example of my own attempt with the shuffle() method but it didn’t work. Any advice would be greatly appreciated!

# One of my many mob arrays.
var spawnable_a = [mobs_a3, mobs_a4, mobs_a5, mobs_a6, mobs_a7, mobs_a8]
var spawnable_a_full = []

func _ready():
	randomize()
	spawnable_a_full = spawnable_a.duplicate()
	spawnable_a.shuffle()

func _on_Timer_A_timeout():
	if spawnable_a.empty():
		spawnable_a_full = spawnable_a.duplicate()
		spawnable_a.shuffle()
	
	if score <= 19:
		var spawn = spawnable_a[randi()%spawnable_a.size()].instance()
		call_deferred("add_child", spawn)
		$Timer_A.start()
:bust_in_silhouette: Reply From: Wakatta

Firstly

  1. Randi is better than shuffle and no need to do both
  2. No need to duplicate array
  3. Error in code at line 11 (result is alway false)
  4. Error on line 18 posible infinite loop / not sure if intended?
  5. Fear of removing from original array or tampering with it

Process

  1. Move last_spawned to the front/back of the list
  2. Randomize by ignoring last_spawned position in the list

Code sniplet

# One of my many mob arrays.
var spawnable_a = [mobs_a3, mobs_a4, mobs_a5, mobs_a6, mobs_a7, mobs_a8]

func _ready():
    randomize()
    $Timer_A.start()

func _on_Timer_A_timeout():
    if score <= 19:
        var albert_simmons = spawnable_a[randi() % spawnable_a.size() -1]
        spawnable_a.erase(albert_simmons) #remove
        spawnable_a.push_back(albert_simmons) #put back
        
        var spawn = albert_simmons.instance()
        call_deferred("add_child", spawn)

Additional reading on randomization
How would i go about picking a random number?

Thanks for your reply. I get that Albert Simmons reference xD

I tried your codes but unfortunately I still get repeated spawns, some times even from the very first two spawns :frowning:

Btw, line 18 is intended. It stop looping once the score is no longer <= 19. Might this be the reason why it didn’t work?

Update:
I think the .push_back() is causing the Vars to get repicked again albeit randomly and therefore still retain the same random chance of getting repeated immediately.

Is there a way to prevent this?

I went with the .empty() and .duplicate() with .erase(). This works after I corrected the error you pointed out but the only problem is that the Vars won’t get picked again until the Array is emptied and refilled.

tehgaming | 2021-11-17 04:42

Yes push_back basically reshuffles the array and makes the variant available for selecion again, but is it that you want only one instance of every entity in the array to be spawned?

If so then.

# One of my many mob arrays.
var spawnable_a = [mobs_a3, mobs_a4, mobs_a5, mobs_a6, mobs_a7, mobs_a8]
var nth = spawnable_a.size()

func _ready():
    randomize()

func _on_Timer_A_timeout():
    if score <= 19:
        var albert_simmons = spawnable_a[randi() % nth]
        if nth > 0:
            nth -= 1

        var spawn = albert_simmons.instance()
        call_deferred("add_child", spawn)
        $Timer_A.start()

Otherwise

# observe this line and make sure you have the -1
randi() % spawnable_a.size() -1

Wakatta | 2021-11-18 01:11

Apologies for not being clearer with my question.

I hope to get only one of the variants in the array to be selected on every _timeout (which is on loop till a certain score) but ideally never repeatedly selected back to back with possibility of it being selected again not too long later. E.g.;

mobsa3
mobsa6
mobsa5

This is also okay;
mobsa3
mobsa6
mobsa3

This is not okay (which can still happen with your codes above);
mobsa3
mobsa6
mobsa6

I hope you get what I mean :slight_smile:

tehgaming | 2021-11-18 05:31

So it appears the fault in my code lies in the way randi() works.
To resolve remove the math by encapsulating it with ()

var albert_simmons = spawnable_a[randi() % (spawnable_a.size() -1)]

Wakatta | 2021-11-20 07:08