Index was outside of the bounds of the array [duplicate] - c#

This question already has answers here:
What is an IndexOutOfRangeException / ArgumentOutOfRangeException and how do I fix it?
(5 answers)
Closed 7 years ago.
Unhandled Exception: System.IndexOutOfRangeException: Index was outside of the bounds of the array (at the first if statements)
static int arrayRows = 20;
static int arrayCols = 20;
public void printBoard()
{
int neighbours;
for (y = 0; y < arrayCols; y++)
for (x = 0; x < arrayRows; x++)
{
neighbours = 0;
// Count number of neighbours surrounding live cell
if (parentGen[x-1, y-1] == 1) // top left
neighbours++;
if (parentGen[x-1, y] == 1) // left
neighbours++;
if (parentGen[x-1, y+1] == 1) // bottom left
neighbours++;
if (parentGen[x, y-1] == 1) // middle top
neighbours++;
if (parentGen[x, y+1] == 1) // middle bottom
neighbours++;
if (parentGen[x+1, y-1] == 1) // top right
neighbours++;
if (parentGen[x+1, y] == 1) // right
neighbours++;
if (parentGen[x+1, y+1] == 1) // bottom right
neighbours++;
}
}
The only thing I can think of is that my program is checking coordinates of < 0? How do I go about fixing this?

Your first coordinates are parentGen[-1, -1], this will always throw the exception.
You need to check if the cell you're on has any neighbors to the left, right, top, or bottom. For example, x = 0 has no neighbors to the left and y = 20 has no neighbors to the bottom. You may wish to break this out to other functions, such as HasNeighborsLeft(int x), etc.
edit: sample code
if(x > 0 && y > 0 && parentGen[x - 1, y - 1] == 1)
{
neighbors++;
}
You can factor this out to it's own functions though, and you can wrap this kind of logic around all of the checks that involve x - 1 for example.

You need boundary condition checks on both x and y at top and bottom of their range. You cannot legally index the entire array using +1 and -1 offsets. Break up your check into boundary condition cases where x == 0, x == arrayRows-1 (by not checking the invalid relative offsets here), and then check cases where x+1 and x-1 are always valid in an else. Similarly with y.

You're array goes from 0->21. As well, you're testing values at [-1, -1] and [22, 22]You can fix it by chainging your for statement(s) to
for (int x = 1; x <= arrayCols - 1; x++)
for (int y = 1; y <= arrayRows - 1; y++)
In addition, problems with loops are almost always caused by a small number of cases that you can always check for:
Your for statement a) starts at a lower bound than the array, or b) finishes at a higher one
a) for (int x = -1;
b) for (int x = 0; x <= array.Length
Your code in the loop accesses values outside of your indexer range
for (int x = 0...
array[x-1, ...
Your collection isn't initialized
In this case, your problem 2.

The problem is that you are looking at the previous and next values (-1 and +1) which will obviously go outside the array bounds at either ends of the array.
There are a few options solving this:
Create a bigger array with a dummy 'border' around the edge which you don't use for your board but allows you to use code very similar to that which you have now (with your -1 and +1 previous and next cell logic). (Imagine a chess board that is 10x10 where you are not allowed to play in the outermost squares).
Scatter loads of 'if' statements to check if you're at the first or last item in the array and thus avoid making any array accesses that are invalid.
Create a function to retrieve an item at a particular cell and put conditional logic in this function for dealing with the array bounds.
Personally I would go with the last option, build yourself a function which gets the state of the specified cell, checks that the indices are valid and returns a default value if they are not. For example:
private const int EMPTY_CELL = 0;
private const int INVALID_CELL = EMPTY_CELL; // for now the same, but gives scope to handle separately
private int GetCellState(int row, int column)
{
if (row < 0) return INVALID_CELL;
if (column < 0) return INVALID_CELL;
if (row >= arrayRows) return INVALID_CELL;
if (column >= arrayColumns) return INVALID_CELL;
return parentGen[row, column];
}
It is then simply a matter of swapping your direct accesses to parentGen with calls to the function.

you could start by creating a sequence of only the valid indices and then iterate the combinations of those:
static int arrayRows = 20;
static int arrayCols = 20;
public void printBoard()
{
var sequences = from row in Enumerable.Range(0, arrayRows)
from column in Enumerable.Range(0, arrayCols)
select new
{
Rows = (from xs in new[] { row - 1, row, row + 1 }
where xs >= 0 && xs < 20
select xs),
Columns = (from ys in new[] { column - 1, column, column + 1 }
where ys >= 0 && ys < 20
select ys)
};
//now that we have a sequence with all the needed (valid) indices
//iterate through the combinations of those
var neighbours = (from seq in sequences
from row in seq.Rows
from column in seq.Columns
where row != column && parentGen[row, column] == 1
select 1).Count();
}

Related

How to solve an index out of bounds issue, when you have to take into consideration both neighbors of an array's element?

This thing that I'm writing should do the following: get as an input a number, then, that many kids' names, and that many grades. Then, assign each kid a number of coins, so that if their grade is bigger, than their neighbor's, they get more coins and vice versa. What I wrote is this:
string input = Console.ReadLine();
int n = Convert.ToInt32(input);
int i = 0;
string[] names = new string[n];
for (i = 0; i < n; i++)
{
names[i] = Console.ReadLine();
}
string[] gradeText = new string[n];
int[] grades = new int[n];
for (i = 0; i < n; i++)
{
gradeText[i] = Console.ReadLine();
grades[i] = Convert.ToInt32(gradeText[i]);
}
int[] minCoins = { 1, 2, 3 };
int[] coinArray = new int[n];
for (i = 1; i < n - 2; i++)
{
if (grades[0] > grades[1])
{
coinArray[0] = 3;
}
else
{
coinArray[0] = 1;
}
if (grades[i] > grades[i + 1] && grades[i] > grades[i - 1])
{
coinArray[i] = 3;
}
if (grades[i] > grades[i + 1] || grades[i] > grades[i - 1])
{
coinArray[i] = 2;
}
if (grades[i] < grades[i + 1] && grades[i] < grades[i - 1])
{
coinArray[i] = 1;
}
if (grades[n - 1] > grades[n - 2])
{
coinArray[n - 1] = 3;
}
else
{ coinArray[n - 1] = 1; }
}
for (i = 0; i < n; i++)
{
Console.WriteLine(names[i] + " " + coinArray[i]);
}
I know my loop is hella messed up, but any tips on how to fix it would be kindly appreciated!
Others here have already suggested how to deal with index out of bounds issues. So this is slight different approach to solving your problem.
It is very easy to see this as one problem and then try to resolve it all in one place but that isn't always the best solution.
Your for loop is trying to do quite a lot. Each iteration could have many checks to make. In addition you are making checks you have previously made.
Do I have a neighbour to the left.
Do I have a neighbour to the right.
Did I get a better grade than both neighbours.
Did I get a better grade than one neighbour.
Did I lose to both neighbours.
My advice would be to break this down into two separate tasks.
1, To calculate how many neighbours each person got a higher grade than.
string[] names = new string[]{"John", "Paul", "Ringo", "George"};
int[] grades = new[] {3, 4, 3,2};
int[] winnersandloser = new int[4];
for (int i = 1; i < grades.Length; i++) //note starting at position 1 so I dont need to handle index out of bounds inside the for loop
{
if (grades[i] > grades[i - 1])
{
winnersandloser[i]++;
}
else
{
winnersandloser[i - 1]++;
}
}
In the above code you should have an array with these values: {0,2,1,0}
0 = you lost to both neighbours
1 = you beat one neighbour
2 = well done you beat both neighbours
Then using this winnersandlosers array you can calculate how many coins to give each person. I'll leave that for you to do.
Update
If you require different behaviour for the first and last in the list of people you need to add the logic to your code for allocating coins.
An array gives each value and index value, starting from 0. So 0 points to the first value in you array. George is the 4th entry in the array but as the array index starts with 0 the index value is 3, you also get this from arrayname.Length - 1
So now when looping through the array to allocate coins we can add a check for the first and last positions in the array.
//allocating coins
for (int i = 0; i < winnersandloser.Length; i++)
{
if (i == 0 || i == winnersandloser.Length - 1)
{
//allocating rules for first and last
}
else
{
//allocating rules for everyone else
}
}
One common way to approach this kind of issue is to oversize your array by as many elements as you need to look ahead/look behind. Place you real elements in the "middle"1 of this array and suitable dummy values into the elements at the start/end that don't correspond to real entries. You pick the dummy values such that the comparisons work out how you need them to (e.g. often you'll put int.MinValue in the dummy elements at the start and int.MaxValue in the dummy elements at the end).
You then just enumerate the real elements in the array, but all of your computed look aheads/look behinds still correspond to valid indexes in the array.
1Sometimes you'll have uneven look ahead/look behind requirements so it may not be the true middle. E.g. say you need to be able to look behind one element and ahead 3 elements, and you want to process 20 elements. You then create an array containing 24 entries, put dummy values at index 0, 21, 22 and 23, and populate your real elements into indexes 1 - 20.

go through matrix and get the lowest sum only going from left to right and from up to down only

I'm stuck with a college project and I wonder if you can help me have a hint on how to do this, I have to do it on c#.
Using an 80x80 matrix I have to go through it only from left to right and from up to down so I can find the path that gives me the lowest number when sum all the values from top left corner to bottom right corner.
As an example on this case the numbers that should be picked up are:
131,201,96,342,746,422,121,37,331 = 2427 the lowest number
It does not matter how many times you move to the right or down but what matters is to get the lowest number.
This is an interesting project in that it illustrates an important technique called dynamic programming: a solution to the entire problem can be constructed from a solution to a smaller sub-problem with a simple computation step.
Start with a recursive solution that wouldn't work for large matrix:
// m is the matrix
// R (uppercase) is the number of rows; C is the number of columns
// r (lowercase) and c are starting row/column
int minSum(int[,] m, int R, int C, int r, int c) {
int res;
if (r == R-1 && c == C-1) {
// Bottom-right corner - one answer
res = m[r,c];
} else if (r == R-1) {
// Bottom row - go right
res = m[r,c] + minSum(m, R, C, r, c+1);
} else if (c == C-1) {
// Rightmost column - go down
res = m[r,c] + minSum(m, R, C, r+1, c);
} else {
// In the middle - try going right, then try going down
int goRight = m[r,c] + minSum(m, R, C, r, c+1);
int goDown = m[r,c] + minSum(m, R, C, r+1, c);
res = Math.Min(goRight, goDown);
}
return res;
}
This will work for a 10×10 matrix, but it would take too long for a 80×80 matrix. However, it provides a template for a working solution: if you add a separate matrix of results you obtained at earlier steps, you would transform it into a faster solution:
// m is the matrix
// R (uppercase) is the number of rows; C is the number of columns
// known is the matrix of solutions you already know
// r (lowercase) and c are starting row/column
int minSum(int[,] m, int R, int C, int?[,] known, int r, int c) {
if (known[r,c].HasValue) {
return known[r,c];
}
int res;
... // Computation of the result goes here
known[r,c] = res;
return res;
}
This particular technique of implementing dynamic programming solutions is called memoization.
First step is always analysis, in particular to try to figure out the scale of the problem.
Ok assuming you can only ever step down or to the right, you will have 79 steps down and 79 steps to the right. 158 steps total of the form 011100101001 (1=move right, 0=move down) etc. Note that the solution space is not as much as 2^158 since not all binary numbers are possible... you must have exactly 79 downs and 79 rights. From combinatorics, this limits the number of possible correct answers to 158!/79!79!, which evaluates to still a very large number, something like 10^46.
You should realize is that this is quite large to brute-force, which methodology otherwise should definitely be a consideration for you if the project does not specifically rule it out, since it invariably makes the algorithm simpler (e.g. by simply iterating all the solution possibilities). I imagine the question has been designed this way in order to require you to use an algorithm that does not brute-force the correct answer.
The way to solve this problem without iterating the whole solution space is to realize that the best path to the lower right corner is the better of the two best paths to the squares immediately to the left of, and above, the lower right corner, and the best path to those is the best path to the next diagonal (numbers 524, 121, 111 in your diagram), and so on.
What you need to do is to treat each cell as a node in a graph and implement shortest path algorithm.
Dijkstra algorithm is one of them. You can find more information here https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
It is really simple, because You can divide the problem into solved and unsolved part and move items from unsolved into solved one by one. Start on top left and move through all "/" diagonals towards bottom right.
int size = 5;
int[,] matrix = new int[,] {
{131,673,234,103,18},
{201,96,342,965,150},
{630,803,746,422,111},
{537,699,497,121,956},
{805,732,524,37,331}
};
//Random rand = new Random();
//for (int y = 0; y < size; ++y)
//{
// for (int x = 0; x < size; ++x)
// {
// matrix[y, x] = rand.Next(10);
// }
//}
int[,] distance = new int[size, size];
distance[0, 0] = matrix[0, 0];
for (int i = 1; i < size * 2 - 1; ++i)
{
int y = Math.Min(i, size - 1);
int x = i - y;
while (x < size && y >= 0)
{
distance[y, x] = Math.Min(
x > 0 ? distance[y, x - 1] + matrix[y, x] : int.MaxValue,
y > 0 ? distance[y - 1, x] + matrix[y, x] : int.MaxValue);
x++;
y--;
}
}
for (int y = 0; y < size; ++y)
{
for (int x = 0; x < size; ++x)
{
Console.Write(matrix[y, x].ToString().PadLeft(5, ' '));
}
Console.WriteLine();
}
Console.WriteLine();
for (int y = 0; y < size; ++y)
{
for (int x = 0; x < size; ++x)
{
Console.Write(distance[y, x].ToString().PadLeft(5, ' '));
}
Console.WriteLine();
}
Console.WriteLine();

Sequentially removing items in a 2D array on a position's row and column, starting from the center

I'm having another problem in my Bejeweled clone. I want to make Star Gems act like they do in Bejeweled 3, meaning they destroy gems outward from the star gem(the center). So, say the star gem was at (4, 4) in a 10x10 2D array; it would destroy the positions (3, 4), (5, 4), (4, 3) and (4, 5) first, then, say, 10 frames later, destroy (2, 4), (6, 4), (4, 2), and (4, 6), and so on.
Right now I have the StarDestruction() method storing the position of the star gem to a couple of Board-scope variables, and the positions to destroy in a List<Gem>, like so:
Board.starPosX = i;
Board.starPosY = j;
for (int x = 0; x < gems.GetLength(0); x++)
{
moveTimer = 0;
int k = x;
int m = x;
int q = x;
int n = x;
if (i - k < 0) k = 0;
if (i + m > gems.GetLength(0) - 1) m = 0;
if (j - q < 0) q = 0;
if (j + n > gems.GetLength(1) - 1) n = 0;
gemQ.Add(gems[i - k, j]);
gemQ.Add(gems[i + m, j]);
gemQ.Add(gems[i, j - q]);
gemQ.Add(gems[i, j + n]);
}
where gemQ is the List<Gem> and gems is the 2D Gem array.
This is how I currently destroy the gems, in Update():
foreach (Gem g in gemQ)
{
if (timer2 % 12 == 0)
g.KillGem(gems[starPosX, starPosY]);
}
where timer2 is the timer for destroying the gems.
I have a bit simpler code for the original gem destroying, but it didn't seem to work any differently than this version. Here's the simpler code:
for (int x = 0; x < gems.GetLength(0); x++)
{
if (x != i)
{
gems[x, j].KillGem(gems[i, j]);
}
if (x != j)
{
gems[i, x].KillGem(gems[i, j]);
}
}
Any ideas?
Complete edit of my reply, based on our conversation in the comments.
I understand now that:
You want the star gem to destroy all other gems in the same column and same row as the star gem.
You want four gems to be destroyed at a time, with a delay between each four.
The explosion should move outward from the star gem, i.e. destroying the closest gems first.
Your foreach uses the time like this:
Timer % 12 == 0
At the time that is true for one gem, its true for all of them typically. You don't want to stall between destructions either, otherwise the destruction won't get rendered or the game will visibly lag.
The second issue is that even if you did space out the destruction of the gems, you'll likely find that the destruction occurs in a spiral, instead of four at a time.
With these points in mind, you'll need to do this instead:
// The initial destroy gem code
var gemsToDestroy = new List<Gem>();
for (int x = 0; x < gems.GetLength(0); x++)
{
if (x != i)
{
gemsToDestroy.add(gems[x, j]);
}
if (x != j)
{
gemsToDestroy.add(gems[i, x]);
}
}
// You can change your for loop above to achieve this directly, but this is the idea
// We are putting them in order of closest first.
gemsToDestroy = gemsToDestroy.OrderBy(o => o.DistanceFromStar).ToList();
// Your periodic UPDATE code - This is pseudo code but should convey the general idea
// I've been very lazy with my use of LINQ here, you should refactor this solution to
// to remove as many iterations of the list as possible.
if (gemsToDestroy.Any() && timer.Ready)
{
var closestDistance = gemsToDestroy[0].DistanceFromStar;
foreach (var gem in gemsToDestroy.Where(gem => gem.DistanceFromStar == closestDistance))
{
gem.Destroy();
}
// Again you can do this without LINQ, point is I've removed the now destroyed gems from the list
gemsToDestroy = gemsToDestroy.Where(gem => gem.DistanceFromStar != closestDistance).ToList();
timer.Reset(); // So that we wait X time before destroying the next set
}
Don't forget to prevent player input while there are items in the gemsToDestroy list and also to stop the game timer while destroying, so that the player isn't penalised time for playing well.

C# Printing a border around a 2D Array

I have a multidimensional array that I'm using as a box, and I have code that generates a border around it, like this:
#######
# #
# #
# #
# #
#######
However what I don't understand is that I can have either a 0 or a 1 in the "j == ProcArea.GetUpperBound(...)" part and it works successfully without any errors or unexpected output.
int[,] ProcArea = new int[rows, columns];
//Generate border
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
if (i == 0 || j == 0 || i == ProcArea.GetUpperBound(0) || j == ProcArea.GetUpperBound(1))
{
ProcArea[i, j] = 2;
}
}
}
Why does this work, and what is the correct value I should be using?
Thanks
If the number of rows and columns are the same, then GetUpperBound(0) and GetUpperBound(1) are going to return the same value.
Arrays you create in C# (unless you call Array.CreateInstance directly) are always 0-based. So GetUpperBound(0) will always return rows - 1, and GetUpperBound(1) will always return columns - 1.
So the code will "work" regardless of which upper bound you check, although I think you'll find that if rows != columns, then using GetUpperBound(0) will create a different sized box than GetUpperBound(1).
By the way, an alternate way of making your border would be:
var maxRow = ProcArea.GetUpperBound(0);
var maxCol = ProcArea.GetUpperBound(1);
// do top and bottom
for (int col = 0; col <= maxCol; ++col)
{
ProcArea[0, col] = 2;
ProcArea[maxRow, col] = 2;
}
// do left and right
for (int row = 0; row <= maxRow; ++row)
{
ProcArea[row, 0] = 2;
ProcArea[row, maxCol] = 2;
}
It's slightly more code, true, but you don't waste time checking indexes unnecessarily. Won't make a difference with small arrays, of course.
Check the documentation http://msdn.microsoft.com/en-us/library/system.array.getupperbound.aspx. Your array has 2 dimensions (rows and columns).
ProcArea.GetUpperBound(0) is equivalent to rows - 1
ProcArea.GetUpperBound(1) is equivalent to columns - 1

How to create Linked Array List in C#

I need to create an Array with Linked list capacities.
Basically, I need a static index based list (like array), but with the possibility to get next and previous field (and easily loop back and forward through list, like with linked list).
Note: Array is 2 dimensional. I use a custom class as array values. So I can set previous and next property for each instance.
Is there a built in C# collection for this? If not, any suggestions on how to create a very simple version of this? (I already have a version of this, consisting of 2 methods. One that loops forward to set the previous field, and one to loop backwards that set the next field, but it's still to messy).
Thanks in advance
EDIT:
The problem is my use of 2dimensional array. If loop through my array:
for (byte x = 0; x < Grid.GetLength(0); x++)
{
for (byte y = 0; y < Grid.GetLength(1); y++) /
{
//At certain point, I need to get the previous field. I can do:
if (y != 0)
{
y -= 2; //-2 because I will y++ in for. Already getting messy
}
else
{
//What if y == 0? Then I can't do y--. I should get max y and do x-- to get previous element:
y = (byte)(Grid.GetLength(1) - 1); //to get max value y
x--;
}
}
}
There is a built-in LinkedList<T> class.
But from your description why wouldn't an array work? It's static, and index-based, and you can easily get the next and previous element by incrementing / decrementing the index. It's hard to see exactly what you need from your code, but I'd like to point out that you can easily enumerate over a multi-dimensional array with:
var arry = new int[2,3];
foreach(var item in arry)
{
...
}
So you might be able to combine this with a Stack<T> structure (push items on the stack and pop them off to get the previous).
Alternatively, you can turn the array into a LinkedList directly.
var list = new LinkedList(arry.Cast<int>()); // flattens array
Or to preserve the indexes from the original array and still loop through the values as a linked list use:
var list = new LinkedList(arry.Cast<int>.Select((item, i) => new
{
Item = item,
Index1 = i % arry.GetLength(1),
Index2 = i / arry.GetLength(0)
}));
var node = list.First;
while(node.Next != null)
{
Console.WriteLine("Value # {1}, {2}: {0}", node.Value.Item, node.Value.Index1, node.Value.Index2);
// on some condition move to previous node
if (...)
{
node = node.Previous;
}
else
{
node = node.Next;
}
}
No, you don't. Instead of abandoning traditional arrays in lieu of "smart linked node arrays" which is what it seems like you're heading towards, try just adding a couple variables in your loop body:
byte x_len = Grid.GetLength(0);
byte y_len = Grid.GetLength(1);
byte prev_x, next_x, prev_y, next_y;
for (byte x = 0; x < x_len; ++x)
{
prev_x = x == 0? x_len - 1 : x - 1;
next_x = x == x_len - 1? 0 : x + 1;
for (byte y = 0; y < y_len; ++y)
{
prev_y = y == 0? y_len - 1 : y - 1;
next_y = y == y_len - 1? 0 : y + 1;
// here, you have access to the next and previous
// in both directions, satisfying your requirements
// without confusing your loop variables.
}
}

Categories

Resources