Non repeating random number in c# for Mine sweeper Game - c#

i am developing a mine sweeper game in c# of dimension (8 x 8).The difficulty levels increase/decrease the number of mines on the grid.
I use a random class (with min,max set;) to generate a random cell number.The problem i am facing is ,the random object keeps repeating the same number.I tried to resolve this issue by maintaining a local list where i store the generated unique random numbers.The next time i call Next(), i would check it against the local list ,to see if its already present.If the number is already present i would keep calling Next() until i get a new number which is unique and not present in the list.But this doesnt look in itself a good solution as sometimes it takes painful amount of time to generate a new list.
Any suggestions on this please

Even if you use the same random number generator, repeating values are possible.
One way to avoid this would be to generate a list of possible values and using the random number generated to access a value in this list (using as indice) and reducing this list, as you find places to put mines to.
For 8 X 8 example, you have 64 possible places
List<int> possibleValues = new List<int>();
for (int i = 0; i < 64; i++)
{
possibleValues[i] = i;
}
List<int> result = new List<int>();
Random r = new Random();
int numberOfMines = 50; //say you want to put 50 mines there
for (int i = 0; i < numberOfMines; i++)
{
int indice = r.Next(possibleValues.Count);
int value = possibleValues[indice];
possibleValues.Remove(value);
result.Add(value);
}

It looks like you want a shuffle based on a fixed number of cells (8,8), e.g. a Fisher-Yates shuffle. This would guarantee that any coordinate only appears exactly once (as opposed to repeatedly using Random.Next() where you can draw the same number many times), and the order of appearance of coordinates is randomized.
Initialize an array that contains all the coordinates of your cells, shuffle the array and maintain an index of the next "random" cell, return the cell at the offset of the index and increase the index.

First calculate the number of mines, and empty fields.
Random rand=new Random();
int mines=GetMinesFromDifficulty(...);
int empty=TotalFields-mines;
Then for each field:
for(int y=0;y<height;y++)
for(int x=0;y<height;y++)
{
if(random.Next(mines+empty) < mines))
{
field[x,y]=Mine;
mines--;
}
else
{
field[x,y]=Empty;
empty--;
}
}

Instead of picking slots where the mines should be, loop through the slots and calculate the probability that there should be a mine there. The implementation for this becomes very simple, as you just need a single loop:
bool[] mines = new bool[64];
int cnt = 12;
Random rnd = new Random();
for (int i = 0; i < mines.Length; i++) {
if (rnd.Next(mines.Length - i) < cnt) {
mines[i] = true;
cnt--;
}
}
(Room for improvement: You can exit out of the loop when cnt reaches zero, if you don't need to initialise all slots.)

If your grid is 8x8, and you want to randomly choose an unused cell instead of pulling random numbers until you hit an unused one, then keep track of the number of unused cells. Say 8 have been used, leaving 55 unused. Then generate a random number between 0 and 54. You would then have to count through, and find the nth empty cell.

It would probably be easier to think of the problem in a more linear way. Instead of say a 2D array... Squares[8][8] think of it as a single dimension array Squares[64].
At this point you generate a number between 0-63 for your random mine placement. If say the value is 10 you could store for later to offset subsequent numbers. You can reduce your range now from 0-62, if you pulled out the value 16 you would want to add +1 for each value you'd already pulled out underneath it (so actually use 17 in this case, but square 10 has been excluded from our set).

Without seeing any code it's kind of hard to get the gist of things, but from what I can tell you have the following:
A multi-dimensional array [8][8] for the grid layout of the game, you're now trying to randomly place mines?
You'll need to keep one instance of Random for generating the numbers, else you will get the same number over and over again. Something like this
private readonly Random randomNumber = new Random();
for(int i = 0; i < 10; i++)
{
Console.WriteLine(this.randomNumber.Next(1, 10));
}
This will then generate 10 random numbers, each one different.

Related

I need some help to complete this code of non-duplicate random numbers

I need some help to do my homework. I should write non-duplicate random numbers. I'm able to show random numbers but I don't know about non-duplicate.
Here's my code:
Random r = new Random();
for (int i = 0; i < 40; i++)
{
int temp = r.Next(0, 100);
Console.WriteLine(temp);
}
What do I need to do to generate non-duplicate number?
Note that this answer only deals with (relatively) small, pre-determined sets.
The reason the other (simple) solution is inefficient is this: you want to generate 100 random numbers between 0 and 99. You get to the point where you have generated 90 random numbers, and just need 10 more.
The problem is that you're still generating numbers between 0 and 99 every time, except now your chance of finding a number that hasn't already been generated is 1 in 10. So 9 of every 10 numbers you generate has already been added to the list.
Once you get down to just needing 1 number, your chance of generating the remaining 1 that hasn't already been generated is 1 in 100. So for every 100 numbers you generate, only 1 of them will be the last possible number.
I'm sure this is simplifying things given that the Random class is pseudo-random (i.e. it's an algorithm that appears random), but this does explain your situation and why the other answer will be slower.
An improved solution would be this:
// Add all of the numbers 0 to 100 to a list
var availableNumbers = new List<int>();
for (int i = 0; i < 100; ++i)
{
availableNumbers.Add(i);
}
Random random = new Random();
for (int i = 0; i < 40; ++i)
{
// Choose a random position in the available numbers list
var idx = random.Next(0, availableNumbers.Count);
// Print the number from this position in the list
Console.WriteLine(availableNumbers[idx]);
// Remove the item at this position
availableNumbers.RemoveAt(idx);
}
Because we start with a list of all available numbers, we are able to choose numbers from it at random. Removing items from the available numbers list means that they are not available to be chosen a second time. We no longer have to try many times to find an unused number, as removing them when we select them ensures that all of the numbers in the available numbers list are always only unused numbers.
You may use a HashSet to store the numbers and make sure there are no duplicates. Here's an example:
HashSet<int> numbers = new HashSet<int>();
Random r = new Random();
for (int i = 0; i < 40; i++)
{
int temp;
do
{
temp = r.Next(0, 100);
} while (numbers.Add(temp) == false); // If the `.Add()` method returns false,
// that means the number already exists.
// So, we try to generate another number.
Console.WriteLine(temp);
}

Efficient way to pair a limited number of random elements from two separate collections

I have two lists (lista and listb), each containing an unknown number of points (two ints in a struct).
I want to create a new list containing unique random pairings from lista and listb. So an example entry might be [12,14] where 12 is an index for lista and 14 is an index for listb.
I also want to set a maximum number of pairings when calling this function. So instead of pairing every element in lista with every element in listb, I could limit it to 200 random pairings as an example.
My first attempt at this was to simply generate every possible pairing. Shuffle that list and knock off any elements past my max. This solution isn't nearly efficient enough.
My next attempt was to make an array per original list of every possible index, shuffle those separately, and then just iterate over them both until I had the max number of pairings (or all of them). This has several problems I'm not certain how to solve. One of which, lista could have 10 million elements for all I know. Creating a new array of 10 million elements (the indices list) and shuffling that when my max pairs might only be 200? Seems silly to go that far.
I've considered just choosing random elements from both lista/listb and seeing if I've already paired them before adding them to the new list. This is also quite a silly option as a lot of time can be spent picking duplicate pairings over and over.
So, what's a good option here or is there one? I don't want to iterate over every possible combination, pairings need to be unique, removing options from a list is quite slow due to the array re-sizing when they are quite large, distribution needs to be pretty uniform in the selection process for each list, etc.
Thanks for any and all help.
Edit - I meant the unique aspect regarding the pairs themselves. So element 10 in lista could be used over and over as long as the element in listb is different each time. The only catch there is I don't want to limit lista and listb right off as I need fairly even distribution across both lists for every pairing.
To avoid duplicates completely, you could try doing a sparse Fisher-Yates shuffle.
Create a Dictionary<int, int> dict that will map "indices in the Fisher-Yates array that do not hold their own index" to "the value at that index".
For the nth item, pick a random number x from n (inclusive) to "size of ListA * size of ListB" (exclusive)
dict[x] ?? x is your selected item.
Store dict[n] ?? n in dict[x].
Map the selected item back to a pair of indices (divide by size of ListA for the ListB index, modulus by the size of ListA for the ListA index).
A math or statistics buff might give you a formula for evaluating this but I just wrote some test code.
The code simply picks random pairs, and every time it sees a duplicate it tries again. Then for each such "pick a random pair until unique" cycle it counts how many retries it did and tracks this. Then finally this is summed up into a global array to track the relative frequency of these things.
Here's the results after about 1 minute of execution:
84382319 81 0 0 0 0 0 0 0 0
The numbers mean this:
Out of 421912 cycles [(84382319+81)/200]:
81 duplicates were found but retrying did not find a duplicate (3rd number and up is 0)
84382319 unique pairs could be found on the first try without duplicates
So, obviously this will start to rise if you increase the number of pairs you want generated or lower the numbers to choose wrong, but I'm not sure this will pose a problem in practice.
Here's the LINQPad program I used:
static Random R = new Random();
void Main()
{
var a = 10000;
var b = 10000;
var n = 200;
int[] counts = new int[10];
var dc = new DumpContainer().Dump();
while (true)
{
var once = Test(a, b, n);
for (int i = 0; i < once.Length; i++)
counts[i] += once[i];
dc.Content = Util.HorizontalRun(true, counts);
}
}
public static int[] Test(int a, int b, int n)
{
var seen = new HashSet<Tuple<int, int>>();
var result = new int[10];
for (int index = 0; index < n; index++)
{
int tries = 0;
while (true)
{
var av = R.Next(a);
var bv = R.Next(a);
var t = Tuple.Create(av, bv);
if (seen.Contains(t))
tries++;
else
{
seen.Add(t);
break;
}
}
result[tries]++;
}
return result;
}

Mine Sweeper: Improve mine random locate algorithm?

Here is my code to create a grid with randomly generate mines. Problem is the mines is so diffuse, so when I count the mines for non-mine cells, it mainly have value 1, 2 and nearly don't have value 4, 5 , 6, 7. How to improve this algorithm?
Assume that number of columns, rows and mines are constant.
var r = new Random();
int columns, rows, TotalMine;
int[,] grid = new int[columns, rows];
int MineCount = 0;
int X = 0;
int Y = 0;
// Add Mines (This is so simple, it cause the problem)
while (MineCount++ < TotalMine)
{
do
{
X = r.Next(columns);
Y = r.Next(rows);
}
while (grid[X, Y] == -1);
grid[X, Y] = -1; // -1 = have mine
}
Your algorithm is perfectly fine and will create randomly spread mines (assuming the RNG is good enough).
One way improving I could imagine would be using a Game of Life algorithm to remove extremes, for examle cluttered fields where one field is surrounded by 7 or 8 mines.
Just iterate over all fields and count the surrounding mines (i.e. calculate the fields' numbers). If it's 7 or 8, remove one random mine next to it.
As an alternative, you could use perlin noise to create "clouds" and then only place mines randomly in areas where you've got at least a given "density". This way you can rather easily create bigger areas with nothing in between them.
You could also mix both ideas a bit:
Create a table or board as big as your playing field, with values being randomly distributed (true or false).
Do several iterations with a Game of Life algorithm to create some pattern (you should end up with "islands" or random structures).
Place your mines only within areas that are set to false (or true - whatever you choose).

Randomly select a specific quantity of indices from an array?

I have an array of boolean values and need to randomly select a specific quantity of indices for values which are true.
What is the most efficient way to generate the array of indices?
For instance,
BitArray mask = GenerateSomeMask(length: 100000);
int[] randomIndices = RandomIndicesForTrue(mask, quantity: 10);
In this case the length of randomIndices would be 10.
There's a faster way to do this that requires only a single scan of the list.
Consider picking a line at random from a text file when you don't know how many lines are in the file, and the file is too large to fit in memory. The obvious solution is to read the file once to count the lines, pick a random number in the range of 0 to Count-1, and then read the file again up to the chosen line number. That works, but requires you to read the file twice.
A faster solution is to read the first line and save it as the selected line. You replace the selected line with the next line with probability 1/2. When you read the third line, you replace with probability 1/3, etc. When you've read the entire file, you have selected a line at random, and every line had equal probability of being selected. The code looks something like this:
string selectedLine = null;
int numLines = 0;
Random rnd = new Random();
foreach (var line in File.ReadLines(filename))
{
++numLines;
double prob = 1.0/numLines;
if (rnd.Next() >= prob)
selectedLine = line;
}
Now, what if you want to select 2 lines? You select the first two. Then, as each line is read the probability that it will replace one of the two lines is 2/n, where n is the number of lines already read. If you determine that you need to replace a line, you randomly select the line to be replaced. You can follow that same basic idea to select any number of lines at random. For example:
string[] selectedLines = new int[M];
int numLines = 0;
Random rnd = new Random();
foreach (var line in File.ReadLines(filename))
{
++numLines;
if (numLines <= M)
{
selectedLines[numLines-1] = line;
}
else
{
double prob = (double)M/numLines;
if (rnd.Next() >= prob)
{
int ix = rnd.Next(M);
selectedLines[ix] = line;
}
}
}
You can apply that to your BitArray quite easily:
int[] selected = new int[quantity];
int num = 0; // number of True items seen
Random rnd = new Random();
for (int i = 0; i < items.Length; ++i)
{
if (items[i])
{
++num;
if (num <= quantity)
{
selected[num-1] = i;
}
else
{
double prob = (double)quantity/num;
if (rnd.Next() > prob)
{
int ix = rnd.Next(quantity);
selected[ix] = i;
}
}
}
}
You'll need some special case code at the end to handle the case where there aren't quantity set bits in the array, but you'll need that with any solution.
This makes a single pass over the BitArray, and the only extra memory it uses is for the list of selected indexes. I'd be surprised if it wasn't significantly faster than the LINQ version.
Note that I used the probability calculation to illustrate the math. You can change the inner loop code in the first example to:
if (rnd.Next(numLines+1) == numLines)
{
selectedLine = line;
}
++numLines;
You can make a similar change to the other examples. That does the same thing as the probability calculation, and should execute a little faster because it eliminates a floating point divide for each item.
There are two families of approaches you can use: deterministic and non-deterministic. The first one involves finding all the eligible elements in the collection and then picking N at random; the second involves randomly reaching into the collection until you have found N eligible items.
Since the size of your collection is not negligible at 100K and you only want to pick a few out of those, at first sight non-deterministic sounds like it should be considered because it can give very good results in practice. However, since there is no guarantee that N true values even exist in the collection, going non-deterministic could put your program into an infinite loop (less catastrophically, it could just take a very long time to produce results).
Therefore I am going to suggest going for a deterministic approach, even though you are going to pay for the guarantees you need through the nose with resource usage. In particular, the operation will involve in-place sorting of an auxiliary collection; this will practically undo the nice space savings you got by using BitArray.
Theory aside, let's get to work. The standard way to handle this is:
Filter all eligible indices into an auxiliary collection.
Randomly shuffle the collection with Fisher-Yates (there's a convenient implementation on StackOverflow).
Pick the N first items of the shuffled collection. If there are less than N then your input cannot satisfy your requirements.
Translated into LINQ:
var results = mask
.Select((i, f) => Tuple.Create) // project into index/bool pairs
.Where(t => t.Item2) // keep only those where bool == true
.Select(t => t.Item1) // extract indices
.ToList() // prerequisite for next step
.Shuffle() // Fisher-Yates
.Take(quantity) // pick N
.ToArray(); // into an int[]
if (results.Length < quantity)
{
// not enough true values in input
}
If you have 10 indices to choose from, you could generate a random number from 0 to 2^10 - 1, and use that as you mask.

How to generate a non-growing (int) random array c#?

I'm trying to make a Integer (int) array with random numbers NOT growing.
For example: 3 10 5 9 20
But NOT: 3 5 9 10 20 (because they just grow)
I'm using Random class with this code (but I always get a growing list like in the second example):
int[] array1 = new int[5];
Random random_istance = new Random();
for (int i=0;i<5;i++)
{
array1[i] = random.Next(0,999999);
}
I also tried with a code like (I know it is horrible programming) :
int[] array1 = new int[5];
Random random_istance = new Random();
for (int i=0;i<5;i++)
{
random = new Random(x-y*z); // re-instantation
array1[i] = random.Next(0,999999); // x,y and z are variable defined outside
}
(*) My final goal is to get an array of random int between 0 and 999999 but some are to not to be in a sequence (because later I'm going to apply an algorithm to order the array and would not make sense to order a already-ordered array).
Moreover I have to create ANOTHER array with elements just DECREASING (so one random array , and one decreasing array).
Any idea how to salve at least first problem (*)?
Thanks in advance for any help.
One way to ensure that your array is not sorted from low to high is by ordering it randomly when you detect its ordered based on the value, something like:
// while the array is sorted
var sortedCopy = array1.ToList();
sortedCopy.Sort();
while (array1.SequenceEqual(sortedCopy))
{
Array.Sort(array1, new Comparison<int>((left, right) => random.Next(-1, 1)));
}
You can shuffle array after generating:
for (int i = 0; i < array1.Length; i++)
{
array1 = array1.OrderBy(c => random_istance.Next()).ToArray();
}
Well, I don't know if this would be a good solution, but you may try this:
Create random array with desired size.
Sort this array ascending.
Swap random elements i times, where i is a random number > 0.
To get decreasing numbers, create random array and just sort it descending.
Edit Of course it is possible, that you will end up with unchanged sequention if i is even. I think it is enough to swap elements i times, where i is greater than 0 and odd.

Categories

Resources