Assigning Non-Repeating Numbers Inside A Range

I am trying to fill an array of ints with a length of 6 the digits from 1 to 6 randomly, with no repetitions; something that is proving to be a lot more difficult than I once thought.

With my latest attempt, shown below, I was hoping to at least provide a different number (within the range) for each array item, this, unfortunately, turned out to be false; despite the fact that it is being performed in a for loop, and the Random instance is being created inside that loop, the same number is assigned to every item in the array.

public int[] digits = new int[6];
    public float invokeSpeed = 30.0f;

    void Start()
    {
        InvokeRepeating("ScrambleDigits", 0f, invokeSpeed);
    }

    void ScrambleDigits()
    {
        for (int i=0; i < digits.Length; i++)
        {
            System.Random r = new System.Random();
            int rInt = r.Next(1, 6);
            digits[i] = rInt;
        }
    }

Any ideas on how I can achieve what I am trying to?

Take a look at my example here where I just create a list and populate it with numbers and then shuffle it with linq. Once shuffled, you just iterate through it and it will be a random set of numbers. You can reshuffle it to randomize them again if you want.

There was also another post, but I’d have to dig around to find it.

create the array, fill with digits, shuffle

int[] arr = new int[6];
for(int i = 0; i < arr.Length; i++) arr[i] = i + 1;

//shuffle
var r = new System.Random();
System.Array.Sort(arr, (a,b) => { return r.Next(2) * 2 - 1; });

r.Next(2) will randomly select 0 or 1. Multiply by 2 and -1 makes that either -1 or 1. In a comparison -1 means less than, and 1 means greater than (0 is equal, I ignore equal, hence the *2-1)… so you’re saying a and b are randomly compared to one another resulting in a shuffle.

Thanks for posting.

Unfortunately, in the example provided (Post #4), rnd.Next() is used as if it were part of orderby, something the compiler picked up on:

‘List’ does not contain a definition for ‘OrderBy’ and no extension method ‘OrderBy’ accepting a first argument of type ‘List’ could be found

Also, it is very bad practice to use var in C#, as the compiler has to run through the script once to work out what type it is meant to be.

you need to be using ‘System.Linq’.

Put it at the top of the script.

using System.Linq;

It’s perfectly fine. Big whoop if the compiler has to do that at compile time… the nanosecond isn’t going to matter much and it doesn’t impact runtime.

Thanks for the tip, and yes I know using var isn’t technically bad for performance, I was just taught never to use it.

This is working ok, but any ideas on how to limit the array size to 6 (and have it update those instead of adding new ones each time)?

The only thing I can think of is sticking a reset above the whole thing, but I’m sure there must be a better way, but I don’t really touch Linq, or Lists for that matter too much.

private List<int> digitsPre;
    public List<int> digits;
    public float invokeSpeed = 30.0f;
    public int arraySize = 6;

    void Start()
    {
        digitsPre = new List<int>();
        InvokeRepeating("ScrambleDigits", 0f, invokeSpeed);
    }

    void ScrambleDigits()
    {
        for(int i=1; i < (arraySize+1); i++)
        {
            digitsPre.Add(i);
        }
        System.Random rnd = new System.Random();
        digits = (from item in digitsPre orderby rnd.Next() select item).ToList<int>();
    }

This is not true. The compiler runs a whole bunch of passes and it needs to type-check everything anyway, so there’s almost no cost to using var. It’s even potentially cheaper because the compiler knows it doesn’t have to type check the initial declaration like it would if you declare a type.
https://blogs.msdn.microsoft.com/ericlippert/2010/02/04/how-many-passes/

If you want to limit it to 6 but then randomize it, you want to split your code up. Have something that initializes the list and then have your randomize called by your invokerepeating.

You could always clear your list at the start of your scramble method, but that would be unnecessary unless you just always wanted the list to be 1-6 at the start before being scrambled.
In which case, digitsPre.Clear(); will clear it. But you should just be able to set your list up once and then repeatedly scramble it as you see fit.

Did you read my first post where I demonstrated shuffling an array with no lists, and no linq.

Also… you don’t touch Lists? You should get over that one… Lists are SUPER useful. You can get away with out linq just fine, but Lists… like srsly, those are super useful.

1 Like

Got this working eventually.

I can now randomly generate an x-length code, scramble it’s order, and run through that order every x seconds.

For anyone currently (or in the future) interested:

using UnityEngine;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;

public class DigitPatternDoors : MonoBehaviour
{
    private List<int> digitsPre;
    public List<int> digits;
    public float invokeInterval = 30.0f;
    public int arraySize = 6;
    [Space(5)]
    public string currentPattern;
    [Space(10)]
    public float cycleInterval = 30.0f;
    public bool doorsActive;
    public int doorIncrement;

    public List<bool> doors;

    void Start()
    {
        doorIncrement = -1;
        digitsPre = new List<int>();
        ScrambleDigits();
        InvokeRepeating("RunDoors", 0f, cycleInterval);
        //InvokeRepeating("ScrambleDigits", 0f, invokeSpeed);
    }

    void RunDoors()
    {
        if(doorsActive)
        {
            CycleDoors();
        }
    }

    void ScrambleDigits()
    {
        digitsPre = new List<int>();

        for (int i = 1; i < (arraySize + 1); i++)
        {
            digitsPre.Add(i);
        }
        System.Random rnd = new System.Random();
        digits = (from item in digitsPre orderby rnd.Next() select item).ToList<int>();

        StringBuilder sb = new StringBuilder();
        int counter = 0;
        foreach (int digit in digits)
        {
            counter++;
            sb.Append(digit.ToString());
            if (counter < digits.Count)
            {
                sb.Append(" ");
            }
        }
        currentPattern = sb.ToString();
    }

    void CycleDoors()
    {
        doors = new List<bool>();

        for(int i=0; i < (arraySize); i++)
        {
            doors.Add(false);
        }

        doorIncrement++;

        if (doorIncrement >= digits.Count)
        {
            doorIncrement = 0;
        }

        doors[digits[doorIncrement]-1] = true;
    }
}