C# IndexOutOfRange Array Exception - c#

I'm trying to create a 2D char array to hold a grid of chars which will be used as a sort of 'map' for a 2D console game.
I am getting a:
IndexOutOfRange exception
..and cannot see why. I've stepped through the code in debug mode and still cannot see the issue.
It steps through the code fine until it hits X = 25 and Y = 1, the upper right boundary of my grid.
I have _gameWidth and _gameHeight created as follows, outside of main but still inside the class:
static int _gameWidth = 25;
static int _gameHeight = 15;
Following is the code that fails, when trying to generate and populate the grid. It fails at this point:
else if (x == _gameWidth && y == 1)
_grid[x, y] = '╕';
static void GenerateGrid()
{
for (int y = 1; y <= _gameHeight; y++)
{
for (int x = 1; x <= _gameWidth; x++)
{
if (x == 1 && y == 1)
_grid[x, y] = '╒';
else if (x == _gameWidth && y == _gameHeight)
_grid[x, y] = '╛';
else if (x == _gameWidth && y == 1)
_grid[x, y] = '╕';
else if (x == 1 && y == _gameHeight)
_grid[x, y] = '╘';
else if ((x != 1 && y == _gameHeight) || (x != _gameWidth && y == 1))
_grid[x, y] = '═';
else if ((x == 1 && y > 1 && y < _gameHeight) || (x == _gameWidth && y > 1 && y < _gameHeight))
_grid[x, y] = '│';
else
_grid[x, y] = 'x';
}
Console.WriteLine("");
}
}

Change
for (int i = 1; i <= gameHeight; i++)
to
for (int i = 0; i < gameHeight; i++)
and do the same for width.
EDIT:
This is because array indexes start at the number 0 and end with the length of the array minus 1.

This exception means that you have accessed an invalid index. From the way you have written the loop I can tell that you think that indexes go from 1 to the length of the array. Arrays are zero-based, though. Use the standard loop form:
for (int i = 0; i < length; i++)
Your loop starts at one. You can use the Visual Studio for loop template. Just type "for<tab><tab>".
Your program might benefit from the Code Review Stack Exchange site.

Related

recursive stackoverflow minesweeper c#

I am writing a game of minesweeper. Below is code for 3 methods in minesweeper. The first method is to check all the spaces around the button pressed and to count how many bombs are around it. The next method is to be called recursively, in order that if the user pressed a button with 0 buttons around it, it will open all of the squares that also indicate 0 squares around it. The third method is to check that it will be in bound the check. The empty space recursive call is getting me a stackoverflow error, what am I doing wrong?
Thanks!
private int GameLogicChecker(int x, int y)
{
int count = 0;
if (_grid[x, y] != -1)
{
if (x + 1 < SizeX)
{ //Right
if (_grid[x + 1, y] == -1)
count++;
}
if (x - 1 > 0)
{ //Left
if (_grid[x - 1, y] == -1)
count++;
}
if (y + 1 < SizeY)
{ //Upper
if (_grid[x, y + 1] == -1)
count++;
}
if (y - 1 > 0)
{ //Lower
if (_grid[x, y - 1] == -1)
count++;
}
if (x + 1 < SizeX && y + 1 < SizeY)
{ //Right-Upper
if (_grid[x + 1, y + 1] == -1)
count++;
}
if (x + 1 < SizeX && y - 1 > 0)
{ //Right-Lower
if (_grid[x + 1, y - 1] == -1)
count++;
}
if (x - 1 > 0 && y + 1 < SizeY)
{ //Left-Upper
if (_grid[x - 1, y + 1] == -1)
count++;
}
if (x - 1 > 0 && y - 1 > 0)
{ //Left-Lower
if (_grid[x - 1, y - 1] == -1)
count++;
}
}
return count;
}
void OpenEmptySpace(int x, int y)
{
for (var k = -1; k <= 1; k++)
{
for (var l = -1; l <= 1; l++)
{
if (CheckBounds(x + k, y + l) && GameLogicChecker(x + k, y + l) == 0)
{
_buttons[x + k, y + l].Text = "0";
OpenEmptySpace(x + k, y + l);
}
}
}
}
private bool CheckBounds(int x, int y)
{
return x >= 0 && x < SizeX && y >= 0 && y < SizeY;
}
For k = 0 and l = 0, you are calling yourself again and again and again...
Thanks to #BenVoigt for pointing out that two zeroes adjacent to each other will also lead to infinite recursion. So, in order to solve that one method is to create a boolean grid too and set a particular cell's value to true if it has been run through once. Assuming the grid is called Explored, I've added the condition for it in the code below.
If you insist on your current code, try changing the condition to:
if (CheckBounds(x + k, y + l)
&& GameLogicChecker(x + k, y + l) == 0
&& !(k == 0 && l == 0)
&& !Explored[x + k, y + l])
{
Explored[x + k, y + l] = true;
_buttons[x + k, y + l].Text = "0";
OpenEmptySpace(x + k, y + l);
}
Here is another answer for you, rewriting your methods one-by-one following better coding practices. Like in the other answer, a boolean grid called Explored[SizeX, SizeY] has been assumed.
1. GameLogicChecker()
private int GameLogicChecker(int x, int y)
{
if (_grid[x, y] == -1) return 0;
int count = 0;
if (x + 1 < SizeX && _grid[x + 1, y] == -1) //Right
{
count++;
}
if (x - 1 > 0 && _grid[x - 1, y] == -1) //Left
{
count++;
}
if (y + 1 < SizeY && _grid[x, y + 1] == -1) //Upper
{
count++;
}
if (y - 1 > 0 && _grid[x, y - 1] == -1) //Lower
{
count++;
}
if (x + 1 < SizeX && y + 1 < SizeY && _grid[x + 1, y + 1] == -1) //Right-Upper
{
count++;
}
if (x + 1 < SizeX && y - 1 > 0 && _grid[x + 1, y - 1] == -1) //Right-Lower
{
count++;
}
if (x - 1 > 0 && y + 1 < SizeY && _grid[x - 1, y + 1] == -1) //Left-Upper
{
count++;
}
if (x - 1 > 0 && y - 1 > 0 && _grid[x - 1, y - 1] == -1) //Left-Lower
{
count++;
}
return count;
}
What's better? Quicker returning from the method for special case. Reduced nesting in If(...) blocks.
2. OpenEmptySpace()
public/private void OpenEmptySpace(int x, int y)
{
for (var deltaX = -1; deltaX <= 1; deltaX += 2)
{
for (var deltaY = -1; deltaY <= 1; deltaY += 2)
{
var thisX = x + deltaX;
var thisY = y + deltaY;
if (OpeningNotNeeded(thisX, thisY))
{
continue;
}
Explored[thisX, thisY] = true;
_buttons[thisX, thisY].Text = "0";
OpenEmptySpace(thisX, thisY);
}
}
}
private bool OpeningNotNeeded(int x, int y)
{
return !CheckBounds(x, y)
|| GameLogicChecker(x, y) != 0
|| Explored[x, y];
}
What's better? Properly named indexing variables in both loops. Properly written condition (+= 2 instead of ++). Reduced nesting in If(...). Easier to read method call in the If(...) instead of three predicates. Useful temporary variables added which make it clear what x + k and y + l were in the code written earlier.
3. CheckBounds() is written fine.

Add selected elements to list using for loop

I'm trying to add the integers 6 from 30, except for 22 and 26, to a List using a for loop. But 22 and 26 are still being added. What did I do wrong?
List<int> lineNumbers = new List<int>();
for (int x = 6; x < 30; x++)
{
if ((x != 22) || (x != 26))
{
lineNumbers.Add(x);
}
}
The two integers are not added if the code is like below:
for (int x = 6; x < 30; x++)
{
if (x == 22 )
{
}
else if (x == 26)
{
}
else
{
lineNumbers.Add(x);
}
}
You need to replace the || with &&:
for (int x = 6; x < 30; x++)
{
if ((x != 22) && (x != 26))
{
lineNumbers.Add(x);
}
}
Or perhaps, to be more clear:
for (int x = 6; x < 30; x++)
{
if ((x == 22) || (x == 26))
continue;
lineNumbers.Add(x);
}
Alternatively, you could perform this as a one-liner using LINQ. This isn't necessarily faster or anything, so if your for loop is clearer to you, then keep it as-is.
lineNumbers.AddRange(Enumerable.Range(6, 24).Except(new[] { 22, 26 }));
Change your conditions. Use && instead of ||
if ((x != 22) && (x != 26))
Use && instead of || because you want to match between both conditions.
if ((x != 22) && (x != 26))
You can also do this using LINQ (not necessarily but I like LINQ :)
var lineNumbers = Enumerable.Range(6, 30).Except(new[] {22, 26}).ToList();
Replace if ((x != 22) || (x != 26)) to if ((x != 22) && (x != 26))
Or you can use continue like below :
for (int x = 6; x < 30; x++)
{
if ((x == 22) || (x == 26))
continue;
else
lineNumbers.Add(x);
}

Two Dimensional List Loop

I am looping through a List<List<Shape>> object and checking if the horizontally and vertically adjacent objects are the same:
for (int x = 0; x < grid.Columns.Count; x++)
{
for (int y = 0; y < grid.Columns[x].Count; y++)
{
if (y != grid.Columns[x].Count - 1)
{
if (grid.Columns[x][y].Column == grid.Columns[x][y + 1].Column)
{
if (!shapesToDestroy.Contains(grid.Columns[x][y]))
{
shapesToDestroy.Add(grid.Columns[x][y]);
}
if (!shapesToDestroy.Contains(grid.Columns[x][y + 1]))
{
shapesToDestroy.Add(grid.Columns[x][y + 1]);
}
}
}
if (x != grid.Columns.Count - 1)
{
if (grid.Columns[x][y].Column == grid.Columns[x + 1][y].Column)
{
if (!shapesToDestroy.Contains(grid.Columns[x][y]))
{
shapesToDestroy.Add(grid.Columns[x][y]);
}
if (!shapesToDestroy.Contains(grid.Columns[x + 1][y]))
{
shapesToDestroy.Add(grid.Columns[x + 1][y]);
}
}
}
}
}
However, I always seem to get a ArgumentOutOfRange on
if (grid.Columns[x][y].Column == grid.Columns[x][y + 1].Column)
and
if (grid.Columns[x][y].Column == grid.Columns[x + 1][y].Column)
Before indexing these, I am performing a check as you can see to make sure that I do not get a ArgumentOutOfRange, but here I am. When I look at the index which is being + 1, it appears to be well under the size of the collection.
Can anyone see the obvious mistake and where I am going horribly wrong?
UPDATE
I updated the code by changing the checks for X and Y to:
if (x < grid.Columns.Count - 1)
and
if (y < grid.Columns[x].Count - 1)
I still get the same error.
FYI - The size of the collection is always the same. The size is: X = 5 and Y = 10
if (x != grid.Columns.Count - 1)
should be
if (x < grid.Columns.Count - 1)
since y is iterated from 0 to the maximum index of the list the operation
grid.Columns[x][y + 1]
will attempt to access an element with a higher index than what is currently in the list. If you want to keep the code above you should change the loop to only iterate to
for (int x = 0; x < grid.Columns.Count-1; x++)
{
for (int y = 0; y < grid.Columns[x].Count-1; y++)
{
}
}
I tried running your code, and i think you could get the functionality that you are looking for stripping it down to:
for (int x = 0; x < grid.Count; x++)
{
for (int y = 0; y < grid[x].Count; y++)
{
if (grid[x].Count > y && grid[x][y] == grid[x][y + 1])
{
if (!shapesToDestroy.Contains(grid[x][y]))
{
shapesToDestroy.Add(grid[x][y]);
}
if (!shapesToDestroy.Contains(grid[x][y + 1]))
{
shapesToDestroy.Add(grid[x][y + 1]);
}
}
if (grid.Count > x && grid[x+1].Count > y && grid[x][y] == grid[x + 1][y])
{
if (!shapesToDestroy.Contains(grid[x][y]))
{
shapesToDestroy.Add(grid[x][y]);
}
if (!shapesToDestroy.Contains(grid[x + 1][y]))
{
shapesToDestroy.Add(grid[x + 1][y]);
}
}
}
}
EDIT
Could also remove the second if in both of the cases since you have already tested that they are equal, doesn't really matter though since it won't change the result. Would just improve performance a tiny bit
it seems that your if condition does not seems correct
Try this
if (y < grid.Columns[x].Count - 1)
instead of
if (y != grid.Columns[x].Count - 1)
Probably your arrays are jagged. And the individual subarrays do not have the same length.
That could be a reason for getting an ArgumentOutOfRange-Exception in:
if (grid.Columns[x][y].Column == grid.Columns[x + 1][y].Column)
Why not use a foreach loop? then you won't have problems with the array out of bounds? Unless you are going for speed. You can use and then nest them as above.
foreach (int key in values.Keys)
{
Console.WriteLine("{0} is assigned to key: {1}", values[key], key);
}

Game of Life - Dead

I was bored and had only 30 minutes of free time so I decided to have a crack at making the game of life. I followed that rules on wikipedia and it doesn't seem to be working correctly. Could someone please tell me what I would be doing wrong?
Here are the rules:
Any live cell with fewer than two live neighbours dies, as if caused by under-population.
Any live cell with two or three live neighbours lives on to the next generation.
Any live cell with more than three live neighbours dies, as if by overcrowding.
Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
public void PerformLogic()
{
if (in_game)
{
time_elapsed += rate;
if (time_elapsed > frame_rate)
{
time_elapsed = 0;
for (int x = 0; x < board_width; x++)
{
for (int y = 0; y < board_height; y++)
{
if (board[x, y] == alive)
{
int surrounding_cells = 0;
for (int x2 = -1; x2 <= 1; x2++)
{
for (int y2 = -1; y2 <= 1; y2++)
{
if (!(x2 + x <= -1 || y2 + y <= -1 || x + x2 >= board_width || y + y2 >= board_height))
{
if (board[x + x2, y + y2] == alive)
{
surrounding_cells++;
}
}
}
}
if (surrounding_cells < 2)
{
board[x, y] = dead;
}
if (surrounding_cells == 2 ||
surrounding_cells == 3)
{
board[x, y] = alive;
}
if (surrounding_cells > 3)
{
board[x, y] = dead;
}
}
else if (board[x, y] == dead)
{
int surrounding_cells = 0;
for (int x2 = -1; x2 <= 1; x2++)
{
for (int y2 = -1; y2 <= ; y2++)
{
if (!(x2 + x <= -1 || y2 + y <= -1 || x + x2 >= board_width || y + y2 >= board_height))
{
if (board[x + x2, y + y2] == alive)
{
surrounding_cells++;
}
}
}
}
if (surrounding_cells == 3)
{
board[x, y] = alive;
}
}
}
}
}
}
}
Any ideas?
I believe you are updating the board too early. The game of life should update the board after it finishes scanning the whole board, rather than while scanning.
E.g.:
if (surrounding_cells > 3)
{
board[x, y] = dead;
}
After this, for the cell next to it, this cell would be treated as dead.
Marc is right, too.
for (int x2 = -1; x2 <= 1; x2++)
{
for (int y2 = -1; y2 <= 1; y2++)
{
looks to me like you're including the central cell in this loop, so 9 instead of 8.
I'm not sure the nested for is the best option, but if you are using that, add:
if(x2 == 0 && y2 == 0) continue;
at the start of the inner loop (i.e. after the last line that I've posted above)

Select all adjacent values in multidimensional array

I'm building a bubble breaker-kinda game. My code uses two 2D arrays, one containing color indexes (1 - 6) to represent colored circles, and one indicating whether the circle has been selected (1 or 0). I can succesfully select a circle, the right value in the second array changes and this is reflected correctly on screen.
This is the method that selects one circle and four adjacent circles. I pass in the X and Y coordinates that the user has selected on the grid. I set that position to selected (from 0 to 1 in the SelectedCircles array. Check whether any of the sides has a circle with the same color, if so, change that circle to selected too.
private void SelectSurroundingCircles(int xPosition, int yPosition)
{
SelectedCircles[yPosition, xPosition] = 1;
int colorKey = Circles[yPosition, xPosition];
int increment = 1;
for (int i = 0; i < Nickles.Length; i++)
{
if (Circles[yPosition - increment, xPosition] == colorKey)
SelectedCircles[yPosition - increment, xPosition] = 1; // TOP
if (Circles[yPosition + increment, xPosition] == colorKey)
SelectedCircles[yPosition + increment, xPosition] = 1; // BOTTOM
if (Circles[yPosition, xPosition + increment] == colorKey)
SelectedCircles[yPosition, xPosition + increment] = 1; // RIGHT
if (Circles[yPosition, xPosition - increment] == colorKey)
SelectedCircles[yPosition, xPosition - increment] = 1; // LEFT
}
}
What I want to achieve is that all circles of the same color that are next to each other get selected. Basically you first look at the circles adjacent as above, look at their adjacent circles, and so on... I tried various other things but somehow I couldn't figure it out. Hopefully someone can help me, I must be overlooking something.
Thanks.
Not sure this fullfills your exact selecting logic but isn't recursion the solution:
if ( SelectedCircles[yPosition - increment, xPosition] != 1 && Circles[yPosition - increment, xPosition] == colorKey) {
SelectSurroundingCircles(xPosition, yPosition - increment)
}
//... same for other 3 directions
The extra check if the position isn't selected already is important to prevent endless recursion
Nevermind, I solved it myself. I checked against the array of selected circles, this worked.
for (int y = 0; y < SelectedCircles.GetLength(0); y++)
{
for (int x = 0; x < SelectedCircles.GetLength(1); x++)
{
if (SelectedCircles[y, x] == 1)
{
if (y - 1 >= 0 && SelectedCircles[y - 1, x] != 1 && Circles[y - 1, x] == colorKey)
SelectedCircles[y - 1, x] = 1; // TOP
if (y + 1 <= 9 && SelectedCircles[y + 1, x] != 1 && Circles[y + 1, x] == colorKey)
SelectedCircles[y + 1, x] = 1; // BOTTOM
if (x + 1 <= 9 && SelectedCircles[y, x + 1] != 1 && Circles[y, x + 1] == colorKey)
SelectedCircles[y, x + 1] = 1; // RIGHT
if (x - 1 >= 0 && SelectedCircles[y, x - 1] != 1 && Circles[y, x - 1] == colorKey)
SelectedCircles[y, x - 1] = 1; // LEFT
}
}
}
}

Categories