Random.Range with no repetitions

I was having a problem with Random.Range, I needed a Random that didn’t repeat any number before all the numbers were chosen. Since there is no built in method that takes care of that, I created my own. Bellow is the script I created. Does anyone has a better solution?

using UnityEngine;
using System.Collections;

public class ArrayTest : MonoBehaviour
{
    private bool[] paramArray = new bool[15];

    // Use this for initialization
    void Start ()
    {
        ResetArray (ref paramArray);
    }
   
    // Update is called once per frame
    void Update ()
    {
       
    }

    public int GetRandom(ref bool[] refArray)
    {
        int randomLenght,arrayIndex;
        bool foundIndex = false;
        randomLenght = refArray.Length;
        arrayIndex = Random.Range (0, randomLenght);
        if (IsArrayFull (refArray))
        {
            ResetArray (ref refArray);
            refArray [arrayIndex] = true;
        } else
        {
            while (!foundIndex)
            {
                if (!refArray [arrayIndex])
                {
                    refArray [arrayIndex] = true;
                    foundIndex = true;
                } else
                {
                    if (arrayIndex == randomLenght - 1)
                    {
                        arrayIndex = 0;
                    } else
                    {
                        arrayIndex++;
                    }
                }
            }
        }
        return arrayIndex;

    }

    public void ResetArray( ref bool[] arrayToReset)
    {
        int arrayLenght,i;
        arrayLenght = arrayToReset.Length;
        for (i = 0; i < arrayLenght; i++)
        {
            arrayToReset [i] = false;
        }
    }

    public bool IsArrayFull( bool[] arrayToTest)
    {
        bool isFull = true;
        int arrayLenght, i;
        arrayLenght = arrayToTest.Length;
        for (i = 0; i < arrayLenght; i++)
        {
            if (arrayToTest [i] == false)
            {
                isFull = false;
            }
        }
        return isFull;
    }
}

The logic of it is that I have a boolean array where I store all used numbers, the index of that array can also be used for other things. For example, my game have 15 targets, and 5 types of targets. I have one array with the position of the targets (predefined) and another with the type of targets used. For each of this arrays, I have an equivalent boolean array.

An easier method might be to generate a list of sequential numbers from 0 to randomLength, shuffle it, and then when you need a random number just grab the next one from the list and remove it.

2 Likes

@KelsoMRK That sounds like I’ll use a lot less resources. But as I’m a beginner, how do shuffle the list? And how do I know the list is empty?

using System.Linq;
using UnityEngine;

public class UniqueRandom
{
    private readonly int[] numbers;
    private int index;

    public UniqueRandom(int min, int max)
    {
        numbers = Enumerable.Range(min, max)
                            .OrderBy(number => Random.value)
                            .ToArray();
        index = 0;
    }

    public int GetNext()
    {
        var result = numbers[index];
        index = (index + 1) % numbers.Length;
        return result;
    }
}

Usage:

var uniqueRandom = new UniqueRandom(0, 10);

for (int i = 0; i < 20; i++)
{
    Debug.Log(uniqueRandom.GetNext());
}
1 Like

Or even better:

using System;
using System.Linq;
using Random = UnityEngine.Random;

public class UniqueRandom
{
    private readonly int[] numbers;
    private int index;

    public UniqueRandom(int min, int max)
    {
        index = 0;
        numbers = Enumerable.Range(min, max).ToArray();
        Shuffle();
    }

    public int GetNext()
    {
        if (index == numbers.Length)
        {
            Shuffle();
            index = 0;
        }
        return numbers[index++];
    }

    private void Shuffle()
    {
        Array.Sort(numbers, (a, b) => Random.Range(0, 3) - 1);
    }
}

This way the collection of shuffled numbers will be reshuffled when all its values have already been used.

2 Likes

for those, who like lake of IF’s (like me :)):

private int randomNumber;
private int savedNumber;

private void Start()
    {
        savedNumber = Random.Range(0, 3);
    }
 
    void AnyMethod()
    {
        randomNumber = Random.Range(0, 3);

        if (randomNumber == savedNumber)
        {
            AnyMethod();
        }

        else if (randomNumber != savedNumber)
        {
            if (randomNumber == 0)
            {
                Debug.Log("0");
            }
            if (randomNumber == 1)
            {
                Debug.Log("1");             
            }
            else if (randomNumber == 2)
            {
                Debug.Log("2");           
            }
            savedNumber = randomNumber;
        }         
    }