'C# - Random Generator exclude last number from Array

I've just started my first little project in C# and with WinForms and I am stuck on this one feature for a few days now..

I have an Array with about 60 PictureBoxes in it and when i press a button, I want it to pick one random out of these, but not twice in a row.

I guess I am searching for something like that:

static Random rnd = new Random();
int lastPick;

if (checkBox1.Checked == true)
{
                int RandomPick = rnd.Next(pictureBoxArray.Length);
                lastPick = RandomPick;
                PictureBox picBox = pictureBoxArray[RandomPick **- lastPick**];
                picBox.BorderStyle = BorderStyle.FixedSingle;
}

I've also tried to create a List containing my last Pick and tried to use this, but it also didn't work and it gave me an Out of Range Exception.

static Random rnd = new Random();
int lastPick;
List<int> lastNumber = new List<int>(); 

if (checkBox1.Checked == true)
{

                int RandomPick = rnd.Next(pictureBoxArray.Length);
                lastPick = RandomPick;
                lastNumber.Add(lastPick);
                PictureBox picBox = pictureBoxArray[RandomPick - lastNumber.Count];
                picBox.BorderStyle = BorderStyle.FixedSingle;
}

Any help or tips to get into the right direction would be appreciated



Solution 1:[1]

I feel like you are overcomplicating a problem. You can simply store the latest index in a variable (like you are doing), and then generate a random number until it is something else than the one in the variable. Here's an example code snippet:

int lastPick;
while (true) {
  int randomPick = rnd.Next(length);
  if (randomPick != lastPick) {
    lastPick = randomPick;
    // Do things here.
    break; // This breaks the loop.
  }
  // If the previous if-statement was false, we ended
  // up with the same number, so this loop will run again
  // and try a new number
}

Solution 2:[2]

You are close, just pick randomly until the new pick is not the same as the previous one.

int lastPick = -1;
int randomPick = -1;

if (checkBox1.Checked == true)
{
    while (randomPick == lastPick)
    { 
        randomPick = rnd.Next(pictureBoxArray.Length); 
    }
    lastPick = randomPick;
    PictureBox picBox = pictureBoxArray[randomPick];
    picBox.BorderStyle = BorderStyle.FixedSingle;
}

Solution 3:[3]

Since the other answers use while loops, I wanted to present a way to do this without a while loop. Create a list of indices initialized to contain all possible indices into your array. This solution requires System.Linq.

Initialize your previous chosen index to -1.

int lastChosenIndex = -1;

Create a list of all possible indices into your array.

List<int> indicesList = Enumerable.Range(0, pictureBoxArray.Length).ToList();

Now when you want an index into your array, you get the index from the indices list.

var randomIndex = random.Next(indicesList.Count - 1);
var randomItem = pictureBoxArray[indicesList[randomIndex]];

We are going to remove this chosen index from the indices list so it cannot be chosen again. First we need to add back the previously removed index (if it is not -1), since it is now a valid selection.

if (lastChosenIndex > -1)
    // Use Add so the index into this list doesn't change position
    indicesList.Add(lastChosenIndex); 

lastChosenIndex = indicesList[randomIndex];

// by removing the index at this position, there is no way to choose it a second time
indicesList.RemoveAt(randomIndex);

The nice thing is, if you wanted to never show a duplicate, you can remove the last chosen index code and it will never show a duplicate. This is a bit long winded compared to the other answers, but wanted to show there is an alternative to using brute force with a while loop.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Copper
Solution 2 GuyVdN
Solution 3