How do I store references to elements of a 2D array? - c#

I'm checking each element of a 2d array for specific conditionals.
If the element is true I want to check it's direct neighboring adjacent elements.
If the neighboring elements are false I want to keep track of them, lets call these "validElements".
Once all elements have been checked I want to pick one of the validElements at random and make it true, then clear that collection of validElements and repeat this process an arbitrary amount of times.
I know how to check each element and their neighbors for the conditionals but i'm not sure the proper way to keep track of the references to elements that are considered "validElements". How should I keep track of this data?
public bool [,] Grid = new bool [3,3];
// lets make the middle element true so we have a starting point.
Grid[Grid.GetLength(0) / 2, Grid.GetLength(1) / 2] = true;
for (int row = 0; row < Grid.GetLength(0); row++)
{
for (int col = 0; col < Grid.GetLength(1); col++)
{
if (Grid[row, col])
{
// if neighboring cells are false keep track of them somehow
}
}
}

One of the most basic solutions would be to create a list of coordinates where you'll store all the Xs and Ys where you found a valid element. So, before your
for (int row = 0; row < Grid.GetLength(0); row++)
add a
var validElements = new List<(int x, int y)>();
and instead of your
// if neighboring cells are false keep track of them somehow
do
if (/*some condition*/)
{
validElements.Add((row, col));
}
Next, once the loop is done, you need to pick a random valid element and set it to true. You can do this by
var random = new Random();
var randValidElement = validElements[random.Next(validElements.Count - 1)];
// above line is assuming you have at least one valid element.
// You should do a check if validElements.Count == 0 then do something
Grid[randValidElement.x, randValidElement.y] = true; // this is the final step

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

Check against two separate array values and grabbing the smallest one to insert into a new array the CW new array values

I am working with Arrays and conditionals statements, little lost right now and was hoping for some input.
So, I created two Arrays
int[] one = new int[] {
4160414, 6610574, 2864453, 9352227, -4750937, -3132620, 2208017,
-2226227, -8415856, -9834062, -3401569, 7581671, 8068562, 7520435,
-9277044, -7821114, -3095212, 966785, 6873349, -8441152, -7015683,
-6588326, -282013, 4051534, 9930123, -3093234 };
int[] two = new int[] {
1099626, 6083415, 8083888, -8210392, 2665304, -8710738, -8708241,
8859200, -1255323, 5604634, 2921294, -7260228, 7261646, 1137004,
5805162, 4883369, 8789460, 9769240, 319012, -7877588, -1573772,
5192333, 1185446, 1302131, 4217472, -3471445};
My next step what i was thinking is i am going to have to loop through each array
for (int i = 0; i < one.Length; i++)
{
int xValue = one[i];
for (int j = 0; j < two.Length; j++)
{
int yValue = two[j];
}
}
Now that i have the index of each Array i need to check wether the index of xValue is less than the index of yValue
if (xValue < yValue)
{
// dO SOMETHING HERE
}
if (yValue < xValue)
{
// Do Something HERE
}
Where i am getting confused at, is with C# from my understanding you can not push new values into an Array, it needs to be a new instance of the array and copy?
So i tried doing
if (xValue < yValue)
{
Array.Copy(one, x, 13);
}
if (yValue < xValue)
{
Array.Copy(two, x, 13)
}
Both Arrays have 26 values, so a new array of 13 would need to be created to insert the checked value, but Array.Copy seems to not be working getting an array out of bounds check lower bounds.
I'm just confused on checking the values of both arrays at their index, then grabbing the smallest value of the checked values then taking that small value and inserting it into a new array, then use a foreach-loop to iterate over it and print the values to the console. FacePalm
You can use LINQ's Zip to achieve this:
int[] smallest = one.Zip(two, (o, t) => Math.Min(o,t)).ToArray();
Essentially, Zip will provide both items to the lambda expression, allowing you to combine them how you see fit. In this case, we just choose the minimum and return it.
Try it online
Basically, you need to define the size of the new array when you declare it. Make it the same size as one. Then add the smallest item from one or two on each iteration by comparing the items in each array at index i.
int[] smallest = new int[one.Length];
for (int i = 0; i < one.Length; i++)
{
if (one[i] < two[i])
{
smallest[i] = one[i];
}
else
{
smallest[i] = two[i];
}
}

C# Loop to scan top,bottom,left,right elements of 2d Array

I am trying to complete a game over condition for a peg solitaire game i am writing.
I have all of the movements and pieces removing as they should.
Each piece is held within a 2d array as an Ellipse UI Ellement and when a piece is taken it is replaced with a border Ui Element.
I have started to use a method adapted from a Stackoverflow post that scans the 8 adjacent squares around the array element.
public static IEnumerable<T> AdjacentElements<T>(T[,] arr, int row, int column)
{
int rows = arr.GetLength(0);
int columns = arr.GetLength(1);
for (int j = row - 1; j <= row + 1; j++)
for (int i = column - 1; i <= column + 1; i++)
if (i >= 0 && j >= 0 && i < columns && j < rows && !(j == row && i == column))
yield return arr[j, i];
}
}
The method called once a piece is taken.
var results = AdjacentElements(grid, 3, 3);
foreach (var result in results)
Debug.WriteLine(result);
When it encounters an ellipse it should check the squares directly above,below,left and right of the Ellipse, at the moment is all 8 squares, i only need four (top, bottom, left and right).
I am using grid reference 3,3 to test at the moment and it is printing out as expected but for all 8 squares.
If any of the four squares in turn encounter and ellipse the next square in a straight line should be a Border in order to be a possible move.
For example:
Circle 1 is the Ellipse being checked.
The circles below,left and right are ignored.
The Cirle 2 is chosen as Square 3 is empty.
This would produce a valid move so the game would continue.
If no valid move is found the game will end.
I not sure how to proceed with this, I was thinking of putting the method call inside a nested for loop to go over every element but I'm thinking it would be very inefficient.
var results = AdjacentElements(grid, i, j);
foreach (var result in results)
//condition here
I don't think i truly understand what you want to do. However, yes you could do nested loops. but sometimes its just easier to poke at it
Given some array x, y
var x = 23;
var y = 3;
Exmaple
var checks = List<Point>();
checks.Add(new Point(x+1,y));
checks.Add(new Point(x-1,y));
checks.Add(new Point(x,y+1));
checks.Add(new Point(x,y-1));
foreach(var check in checks)
//If Bounds check
//do what you need to do

Issues with line-clearing algorithm for Tetris game?

I'm working on a text-based Tetris game and running into some issues with clearing lines. I have my DeleteRow() method which deletes the given row by working upwards from the given row, overwriting each row's data with the data of the row above it. This seems to work:
/**
* Deletes row r from the array of landed Tetromino blocks.
* #param r The row to delete from the landed array.
**/
private void DeleteRow(int row) {
for(int r = row; r > 0; r--) {
for(int c = 0; c < Board.WIDTH; c++) {
// Overwrite the value of this column from the row above.
still[r, c] = still[(r - 1), c];
}
}
}
Where "still" is the 2D array defined as such.
private int[,] still;
And initialized here:
public Board() {
still = new int[Board.HEIGHT, Board.WIDTH];
}
But in my CheckRows() method, which is what calls DeleteRow(), I seem to be having an issue where it will clear the first row that's passed to it, but subsequent rows are either ignored or it will delete the wrong row:
/**
* Checks each row to see if they are full, and if so, deletes the row and adds to score.
* #param score A long representing the current score, passed as reference.
**/
public void CheckRows(ref long score) {
List<int> rowsToClear = new List<int>();
for(int r = 0; r < Board.HEIGHT; r++) {
bool zero = false;
for(int c = 0; c < Board.WIDTH; c++) {
if(still[r, c] == 0) {
zero = true;
break;
}
}
if(!zero) rowsToClear.Add(r);
}
// Delete all the rows that did not contain zeros.
if(rowsToClear.Count > 0) {
// Add to the score depending on the number of rows cleared.
score += (new int[4] { 40, 100, 300, 1200 })[rowsToClear.Count - 1];
// Delete each row in the list and then increment all other row indices to account for
// the fact that the rows above this one are being copied to this one.
for(int i = (rowsToClear.Count - 1); i >= 0; i--) {
DeleteRow(rowsToClear[i]);
rowsToClear.ForEach(x => x++);
}
}
}
I have to assume this has something to do with the line following the call to DeleteRow, where I increment the row numbers of the other rows that need to be cleared to account for the fact that I'm shifting each row downward to delete the row.
I have noticed though that if those rows are not deleted in the first CheckRows() call, they will be in the next iteration of the main game loop.
Is there some flaw in the logic I'm using? This is my first time making a Tetris game. I'm doing this to familiarize myself with C#, and I'm just not sure what the issue is here. Can someone else spot what's wrong?
I have to assume this has something to do with the line following the call to DeleteRow, where I increment the row numbers of the other rows that need to be cleared to account for the fact that I'm shifting each row downward to delete the row.
This is the line you are speaking of:
rowsToClear.ForEach(x => x++);
That line of code does absolutely nothing: The values will not be incremented.
You can perform an action on the element passed to the delegate in ForEach but you cannot replace it with a new value. Here is the documentation from MSDN:
Modifying the underlying collection in the body of the Action delegate is not supported and causes undefined behavior.
That will not work and neither will this:
foreach (var thisNum in nums)
{
thisNum++; // <== will not compile
}
This will increment the list:
for (int i = nums.Count - 1; i >= 0; i--)
{
nums[i]++;
}
<== Try Me ==>
After rearranging my CheckRows method around, I realized that the solution was that I needed to delete the rows in top to bottom order, as opposed to bottom to top.

Random selection for a variable set with a guarantee of every item at least once

I am working on a game in c# but that detail is not really neccessary to solve my problem.
At I high level here is what I want:
I have a set that could have any number of items in it.
I want to randomly select 10 items from that set.
If the set has less than 10 items in then I expect to select the same
item more than once.
I want to ensure every item is selected at least once.
What would be the algorithm for this?
Sorry I'm not sure if this is an appropriate place to ask, but I've got no pen and paper to hand and I can't quite get my head round what's needed so appreciate the help.
In addition I might also want to add weights to the items to
increase/decrease chance of selection, so if you are able to
incorporate that into your answer that would be fab.
Finally thought I should mention that my set is actually a List<string>, which might be relevent if you prefer to give a full answer rather than psuedo code.
This is what I use to randomize an array. It takes an integer array and randomly sorts that list a certain amount of times determined by the random number (r).
private int[] randomizeArray(int[] i)
{
int L = i.Length - 1;
int c = 0;
int r = random.Next(L);
int prev = 0;
int curr = 0;
int temp;
while (c < r)
{
curr = random.Next(0, L);
if (curr != prev)
{
temp = i[prev];
i[prev] = i[curr];
i[curr] = temp;
c++;
}
}
return i;
}
If you look for effective code, my answer isnt it. In theory, create some collection you can remove from that will mirror your set. Then select random member of the object from it ...and remove, this will garantee items wont repeat(if possible).
Random rnd = new Random();
List<int> indexes = new List<int>(items.Count);
for (int i = 0; i < items.Count; i++)
indexes.Add(i);
List<string> selectedItems = new List<string>(10);
int tmp;
for(int i = 0; i < 10; i++)
{
tmp = rnd.Next(1,10000); //something big
if(indexes.Count > 0)
{
selectedItems.Add(yourItems[indexes[tmp%indexes.Count]]);
indexes.RemoveAt(tmp%indexes.Count);
}
else
selectedItems.Add(yourItems[rnd.Next(0,9)]); //you ran out of unique items
}
where items is your list and yourItems is list of selected items, you dont need to store them if you want process them right away
Perhaps shuffle the collection and pick elements from the front until you have the required amount.
Once you've gone through all the elements, you should perhaps shuffle it again, so you don't just repeat the same sequence.
The basic algorithm for shuffling: (in pseudo-code)
for i from n − 1 downto 1 do
j ← random integer with 0 ≤ j ≤ i
exchange a[j] and a[i]
With the above algorithm (or a minor variation), it's possible to just shuffle until you reach the required number of elements, no need to shuffle the whole thing.

Categories