C# - Random Generator exclude last number from Array - c#

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

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
}

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;
}

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.

Related

How to activate 2 gameobjects randomly?

I have this code but looks like the random is activating the same gameobject. How can I be sure that not the same will be activated? For example, this code activate 2 gameobjects but sometimes just only one, because the random picked the same gameobjects twice.
for (int i = 0; i < 2; i++)
{
int index = Random.Range(0, EyeButtonArray.Length);
EyeButtonArray[index].SetActive(true);
}
You can shuffle your container randomly and pick the first two elements.
int wanted = 2;
int[] nums = System.Linq.Enumerable.Range(0, EyeButtonArray.Length).ToArray();
System.Random rnd = new System.Random();
nums = nums.OrderBy(x => rnd.Next()).ToArray();
for(int x = 0; x < 2; ++x)
EyeButtonArray[nums[x]].SetActive(true);
Another solution using a HashSet is to keep track of the elements you have picked and keep generating until you pick a unique one.
HashSet<int> RandomElementIdx = new HashSet<int>();
int picked = 0;
int wanted = 2;
while(picked < wanted)
{
int index = Random.Range(0, EyeButtonArray.Length);
if(RandomElementIdx.Contains(index))
continue;
RandomElementIdx.Add(index);
EyeButtonArray[index].SetActive(true);
++picked;
}
The above snippet is assuming your list is greater than or equal to wanted. If it is less, it will result in an infinite loop. It is also not exactly efficient as it will continually try to spawn new numbers until it randomly hits one not used.
It sounds like what you want is
You have a list/array of objects
You want to enable the next two random ones of them
(optionally - not sure on this) you want to disable the two current ones
You could simply "shuffle" your array and then enable the two first of the now shuffled items like e.g.
using System.Linq;
...
// OPTIONAL (but I thought makes sense)
// store the currently enabled objects to also be able to disable them later
// when you enable the next pair
private GameObject[] _currentEnabled;
public void EnableTwoRandom()
{
// OPTIONAL (but I thought makes sense)
// if exists first disable the last selected two buttons
if(_currentEnabled!=null)
{
foreach(var item in _currentEnabled)
{
item.SetActive(false);
}
}
// Pick two random buttons
// This first "shuffles" the buttons and then simply takes the
// first two random elements
_currentEnabled = EyeButtonArray.OrderBy(i => Random.value).Take(2).ToArray();
// enable the new two random buttons
foreach(var item in toEnable)
{
item.SetActive(true);
}
}
See
IEnumerable.OrderBy
Random.value
IEnumerable.Take
if additionally you want to be sure that the new selected buttons are also not equal to the previous ones you could add
// Pick two random buttons
if(_currentEnabled != null)
{
// if there already exists a previous selection
// then exclude this selection from the available options first
_currentEnabled = EyeButtonArray.Except(_currentEnabled).OrderBy(i => Random.value).Take(2).ToArray();
}
else
{
_currentEnabled = EyeButtonArray.OrderBy(i => Random.value).Take(2).ToArray();
}
See
IEnumerable.Except

how to create a random int[] [duplicate]

This question already has answers here:
Most efficient way to randomly "sort" (Shuffle) a list of integers in C#
(13 answers)
Closed 5 years ago.
I need to create a random array of int with certain parameters.
int[] test = new int[80];
Random random = new Random();
I need to assign 1's and 0's, randomly to the array. I know how to do that.
test[position] = Random.Next(0,2);//obviously with a for loop
But I need to have exactly 20 1's, but they need to be randomly positioned in the array. I don't know how to make sure that the 1's are randomly positioned, AND that there are exactly 20 1's. The rest of the positions in the array would be assigned 0.
I think you need to turn your thinking around.
Consider:
var cnt = 20;
while (cnt > 0) {
var r = Random.Next(0, 80);
if (test[r] != 1) {
test[r] = 1;
cnt--;
}
}
Expanding explanation based on comments from CodeCaster. Rather than generate a random value to place in the array, this code generates and index to set. Since C# automatically initializes the test array to 0 these values are already set. So all you need is to add your 1 values. The code generates a random index, tests it to see if it isn't 1, if so it sets the array element and decrements a count (cnt). Once count reaches zero the loop terminates.
This won't properly function if you need more values than 0 and 1 that is true. Of course the questions explicitly stated that these were the needed values.
"This causes horrible runtime performance". What!? Can you produce any prove of that? There is a chance that the index generated will collide with an existing entry. This chance increases as more 1's are added. Worst case is there is a 19/80 (~23%) chance of collision.
If you know you need exactly 20 of one value, a better way to go about this is to pre-populate the array with the required values and then shuffle it.
Something like this should work:
int[] array = new int[80];
for (int i = 0; i < 80; i++)
{
int val = 0;
if (i < 20)
{
val = 1;
}
array[i] = val;
}
Random rnd = new Random();
int[] shuffledArray = array.OrderBy(x => rnd.Next()).ToArray();
You can do
for (int i = 0; i < 20; i++)
{
var rand = random.Next(0,80);
if (test[rand] == 1)
{
i--;
continue;
}
test[rand] = 1;
}
Remaining are automatically zero.

dont want a random number to show twice

I am trying to save random numbers in a list, the same number can not come twice.
It´s a BINGO game where this method is used to display ex B12, then the user hits enter and a new number will show. This will keep on until the user writes q.
This works, BUT the number can show up twice...
static void bingo()
{
Random rnd =new Random();
List<int> check = new List<int>();
string choice = "";
while (choice != "Q")
{
int number = rnd.Next(1, 76);
while (!check.Contains(number))
{
kontroll.Add(number);
}
if (number <=15)
{
choice = Interaction.InputBox("B" + number);
choice = choice.ToUpper();
}
else if(number <= 30)
etc.
Something like this should work (if I'm reading your question correctly)
Enumerable.Range(1,76).OrderBy(n => rnd.NextDouble())
There are a couple of ways to do this:
Keep track of what numbers have been "called" - if a number is in the list, pick a different one
Remove numbers that have been called from the original list, then pick a new one at random each time.
Sort the list of possible values by a random number and just work though the list
The easiest way to accomplish this is to use a HashSet.
var usedNumbers = new HashSet<int>();
...
int number;
do {
number = rnd.Next(1, 76);
} while (usedNumbers.Contains(number));
usedNumbers.Add(number);

swaping strings inside of a list using c#

I have a list that contains file names like f1, f2, f3,...,f6. my
program needs to output a list with file names appearing in
random order such as f4,f1,f6,f2,f3,f5.
I want to swap or shift string correctly inside a list I have a list of
size 6 named fileName already containing 6 different file names I
am swapping file names inside of a list fileName as follows and fil
is also a string for remembering current string or file name.
temp =-1;
foreach (Control control in tableLayoutPanel1.Controls) // run loop untill every control inside tablelayoutpanel1.Controls is check or read
{
Button btns = control as Button; // btn sotre the
current button in table.contr
if (btns != null) // check btn got a button from
the panel then
{
temp++;
int randomNumber = 0;
randomNumber = theArray[temp]; //this pic the random number from 0 index then 1 so on theArray already contains random number from 0 to 5 without any repitition and also tested
fil = fileName[randomNumber]; //fil for holding string
fileName[temp] = fileName[randomNumber]; // at filname[0]
swap or shift filename[randomNumber] random are between 0 to
5 and without repitition
fileName[randomNumber] = fil; // this line isnt even necessary
but to be safe i wrot
btns.BackgroundImage = images[randomNumber]; // change
btn image to current random image
copyImages.Add(images[randomNumber]);
btns.BackgroundImage = null; // i purposely doing this :)
}
Using that code I am able to swap string but I cant swap them
correctly as it only run for 6 times so it should swap 6 strings
(each with different name) inside the list on 6 different location
inside the list fileName but its not happening some strings are
showing twice or thrice, hoping someone can point out what I am
doing wrong and theres no index out of range or exception error
I tested it for like hundred time please help thanks and any idea
suggestion or piece of code will be helpful and fil just storing the string at the location of fileName[temp] :) and temp just going from 0 to 5 in a loop
i dont want to shuffle them i just want to swap them according to given index which i am doing in my code but cant to it properly theArray already contains the suffle index i just want to assign the fileName[0] index to theArray[temp] i can send you my proj if you want to have a look just send me hi to my id which show in my profile
So given you have a List of length 6 and an int[6] containing 0 to five in random order
List<String> newList = newList<String>();
foreach(int position in theArray)
{
newList.Add(filename[pos]);
}
filename.Clear();
fileName.AddRange(newList);
would be one way.
or you could simply do filename = newList and not bother with the Clear and AddRange.
You stored the wrong String in your temporary variable.
fil = fileName[temp]; // index changed here
fileName[temp] = fileName[randomNumber];
fileName[randomNumber] = fil;
This is a simple way to shuffle elements in an array. Basicly you go through the array from one end to the middle, swapping each element with one selected at random. Since each swap operation results in two elements being randomly placed, when we reach the middle, the whole array is shuffled.
void Main()
{
var arr = new string[] {"a","b","c"};
Shuffle(arr);
// arr is now shuffled
}
public static void Shuffle<T>(T[] arr)
{
Random r = new Random();
for(int i = arr.Length; i > arr.Length / 2; i--)
{
Swap(arr, r.Next(i), i -1);
}
}
private static void Swap<T>(T[] arr, int first, int second)
{
T temp = arr[first];
arr[first] = arr[second];
arr[second] = temp;
}

Insert an underlying value into a non-existing index

I'm trying to solve a simple algorithm a specific way where it takes the current row and adds it to the top most row. I know there are plenty of ways to solve this but currently I have a text file that gets read line by line. Each line is converted to an sbyte (there's a certain reason why I am using sbyte but it's irrelevant to my post and I won't mention it here) and added to a list. From there, the line is reversed and added to another list. Here's the code I have for that first part:
List<List<sbyte>> largeNumbers = new List<List<sbyte>>();
List<string> total = new List<string>();
string bigIntFile = #"C:\Users\Justin\Documents\BigNumbers.txt";
string result;
StreamReader streamReader = new StreamReader(bigIntFile);
while ((result = streamReader.ReadLine()) != null)
{
List<sbyte> largeNumber = new List<sbyte>();
for (int i = 0; i < result.Length; i++)
{
sbyte singleConvertedDigit = Convert.ToSByte(result.Substring(i, 1));
largeNumber.Add(singleConvertedDigit);
}
largeNumber.Reverse();
largeNumbers.Add(largeNumber);
}
From there, I want to use an empty list that stores strings which I will be using later for adding my numbers. However, I want to be able to add numbers to this new list named "total". The numbers I'll be adding to it are not all the same length and because so, I need to check if an index exists at a certain location, if it does I'll be adding the value I'm looking at to the number that resides in that index, if not, I need to create that index and set it's value to 0. In trying to do so, I keep getting an IndexOutOfRange exception (obviously because that index doesn't exist). :
foreach (var largeNumber in largeNumbers)
{
int totalIndex = 0;
foreach (var digit in largeNumber)
{
if (total.Count == 0)
{
total[totalIndex] = digit.ToString(); //Index out of Range exception occurs here
}
else
{
total[totalIndex] = (Convert.ToSByte(total[totalIndex]) + digit).ToString();
}
totalIndex ++;
}
}
I'm just at a loss. Any Ideas on how to check if that index exists; if it does not create it and set it's underlying value equal to 0? This is just a fun exercise for me but I am hitting a brick wall with this lovely index portion. I've tried to use SingleOrDefault as well as ElementAtOrDefault but they don't seem to be working so hot for me. Thanks in advance!
Depending on if your result is have small number of missing elements (i.e. have more than 50% elements missing) consider simply adding 0 to the list till you reach neccessary index. You may use list of nullable items (i.e. List<int?>) instead of regular values (List<int>) if you care if item is missing or not.
Something like (non-compiled...) sample:
// List<long> list; int index; long value
if (index >= list.Count)
{
list.AddRange(Enumerable.Repeat(0, index-list.Count+1);
}
list[index] = value;
If you have significant number of missing elements use Dictionary (or SortedDictionary) with (index, value) pairs.
Dictionary<int, long> items;
if (items.ContainsKey(index))
{
items[key] = value;
}
else
{
items.Add(index, value);
}

Categories