I have created a tic tac toe program that works in that it allows a player to select a move in a 2d array by selecting the cords, then the computer makes a move.
The program works, but the computer's move is sequential rather then random and will always select the next available space in the array. Now that I have the game working, I want to improve the program into three levels of difficulty:
Easy = Randomly generated move by computer
Medium = Randomly generated move by computer that checks to block player and complete line
Hard = Selection of optimal move every time through recursion
How I can get the computer to randomly select a set of cords in my array?
(My current basic for loop for computers move)
static void Computermove(char[,] gamegrid, char fin)
{
Console.WriteLine("\nComputer's turn");
Console.ReadKey();
int x = 0;
int y = 0;
for (x = 0; x < 2; x++)
{
for (y = 0; y < 2; y++)
if (gamegrid[x, y] == ' ') break;
if (gamegrid[x, y] == ' ') break;
}
if (x * y == 9)
{
fin = 'X';
}
else
gamegrid[x, y] = 'O';
} // End Computermove
Create a new instance of the Random class and use the Next() method to create the two random numbers representing the coordinates.
Here's the code:
var r = new Random();
var randomX = r.Next(3);
var randomY = r.Next(3);
Update:
Here's how the method could look like:
static void Computermove(char[,] gamegrid, char fin)
{
if (!ContainsEmptyCells(gamegrid))
{
// table is full; probably nobody won
return;
}
bool goodRandomCoordinateFound = false;
int row = 0, column = 0;
var random = new Random();
while (!goodRandomCoordinateFound)
{
row = random.Next(3);
column = random.Next(3);
if(gamegrid[row, column] == ' ')
goodRandomCoordinateFound = true;
}
gamegrid[row, column] = 'O';
}
I found the problem that was causing the StackOverflowException. It is a pretty subtle one, but the conclusion is that when retrying to generate random coordinates, the same instance of Random should be used, instead of creating a new one.
This is because Random does not really generate truly random numbers. When a new Random() instance is created, it is initialized with a seed value that is based on the current time.
If you create multiple instances of Random with the same seed value, they will create the same random numbers stream.
In our example, in case we needed new random coordinates to be generated, a new instance of Random was created without specifying a seed, so the seed was using the current time. Due to the fact that the random instances were created extremely quickly, the seed value was the same, therefore the random values were the same, causing infinite recursion.
I've rewritten the method to reuse the random instance, which causes subsequent calls to Next(3) to yield other values than the ones we currently have.
Since the computer can't chose all fields, you need a 2-Step process
count the number of free fields (=:N)
create a random number in the range 0..N-1
use this number to chose a field
Keep a list of free coordinates, whenever a coordinate is picked by either player or AI remove it from the list. Randomize based on the number of items in the list, so if there are 5 entries in the list generate a random number between 1 and 5, pick the coordinate out of the list.
Or simpler keep track of the number of free coordinates, example you have 5 free coordinates generate a number 1-5 and just iterate over the board to get to the 5th position
Related
I'm working on a football league fixture project on C# Console Application.
I'm trying to choose random teams from the array which contains the teams which plays at their home and away.
When I'm trying to generate 9 random numbers, only 8 numbers are generated and 0 are not, so the code can't break the for loop.
I suppose that the problem is that the if statement does not allow to generate the same number and int array's elements' default value is 0.
Here is the code and the output:
C# Code Output
int randomHome; //Random number genetator for choosing a random iteration value from home array which containss the teams which plays at their home
int randomAway; //Random number genetator for choosing a random iteration value from awayarray which containss the teams which plays at away
Random randomNum = new Random();
int[] randomHomeNumArray = new int[home.Length]; //array will hold the randomHome values and home array is the array which is holding the team's iteration values which plays at their home
int[] randomAwayNumArray = new int[away.Length]; //array will hold the randomAway values and away array is the array which is holding the team's iteration values which plays at away
for (int homeArrayCounter = 0; homeArrayCounter < randomHomeNumArray.Length; homeArrayCounter++)
{
randomHome = randomNum.Next(home.Length)
if (!randomHomeNumArray.Contains(randomHome) )
{
randomHomeNumArray[homeArrayCounter] = randomHome; //It will hold the randomHome values
Console.WriteLine(homeArrayCounter + ". iterasyon in Home " + randomHomeNumArray[homeArrayCounter]);
}
else
{
homeArrayCounter--;
}
}
Console.WriteLine("\n\n");
for (int awayArrayCounter = 0; awayArrayCounter < randomAwayNumArray.Length; awayArrayCounter++)
{
randomAway = randomNum.Next(randomAwayNumArray.Length);
if (!randomAwayNumArray.Contains(randomAway))
{
randomAwayNumArray[awayArrayCounter] = randomAway; //It holds the random valures from away array which contains the teams which plays at away
Console.WriteLine(awayArrayCounter + ". iterasyon in Away " + randomAwayNumArray[awayArrayCounter]);
}
else
{
awayArrayCounter--;
}
}
When you initalize an array, it has the value 0 by default for each index. When you are using the random number, it always skips 0 because it already exists.
You can try like this:-
for(int i= 0; i<randomHomeNumArray.Length; i++){
randomHomeNumArray[i] = -1;
}
for (int homeArrayCounter = 0; homeArrayCounter < randomHomeNumArray.Length; homeArrayCounter++)
{
do{
randomHome = randomNum.Next(home.Length);
} while(!randomHomeNumArray.Contains(randomHome));
randomHomeNumArray[homeArrayCounter] = randomHome; //It will hold the randomHome values
Console.WriteLine(homeArrayCounter + ". iterasyon in Home " + randomHomeNumArray[homeArrayCounter]);
}
It appears you're trying to just randomize arrays.
Try this instead:
Random randomNum = new Random();
int[] randomHomeNumArray = Enumerable.Range(0, home.Length).OrderBy(_ => randomNum.Next()).ToArray();
int[] randomAwayNumArray = Enumerable.Range(0, away.Length).OrderBy(_ => randomNum.Next()).ToArray();
That's it. Done.
Your problem is the default initialization of your arrays:
int[] randomHomeNumArray = new int[home.Length];
This creates an array filled with 0s, because 0 is the default value for int.
So your if condition
if (!randomHomeNumArray.Contains(randomHome))
is always false for 0 because 0 is already contained in the array.
You may initialize your arrays instead like this:
int[] randomHomeNumArray = Enumerable.Repeat(-1, home.Length).ToArray();
So you fill it with -1 instead of 0 and your if condition works.
Because int is not a null-able data type, by default, an int[] gets initialized with zeroes. So even if you think it's an empty array, it's actually an array with all elements set to zero.
To rectify the problem, you can consider using an int?[] (null-able int array) instead. Or, you can initialize the array with either a negative integer or some integer greater than the maximum inclusive upper bound. Better yet, to achieve what you want, in a better way, use the solution provided by #Enigmativity, and mark his answer accepted, if it helped.
I am having trouble finding of a way to go about creating an equation inside my for loop that will generate a number of objects in a List collectibleList depending on the level that the character is on. As it is now, my list only creates one collectible per level. This, I'm guessing, is because of the i < currentLevel bound. But I don't know what kind of bound I should use or how to implement i in an equation so that more collectibles can be added to my list depending on the currentLevel.
// Set up each level the player encounters
public void NextLevel()
{
collectibleList.Clear();
currentLevel++;
timer = 10;
player.LevelScore = 0;
player.Position = new Rectangle(GraphicsDevice.Viewport.Width/2, GraphicsDevice.Viewport.Height/2, player.Position.Width, player.Position.Height);
// Random number generator that will help generate a random position of the collectible sprite
Random rng = new Random();
for (int i = 0; i < currentLevel; i++)
{
Collectible collectible = new Collectible(rng.Next(0, GraphicsDevice.Viewport.Width), rng.Next(0, GraphicsDevice.Viewport.Height), 70, 91, true);
collectible.ObjectSprite = collectibleSprite;
collectibleList.Add(collectible);
}
}
Since you are iterating based on currentLevel (one at a time) you will get single collectible entry per level in the list.
What you need is another factor to determine how many collectibles to add per iteration - then you can place inner loop within the main loop to generate as many collectibles as you want per level.
i.e 2 collectibles for level 1, 5 collectibles for level 2 etc.
You are looking for a good increasing integer function, right? There are pretty many options to choose from.
f(n) = n, f(n) = 2*n + 1, f(n) = n +⌊log(n)⌋, etc.
The bigger n is the bigger f(n) will be.
Another way to get the number for a given n is just to add a random number to f(n - 1).
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).
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.
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.