I'm taking a class that uses C# and our first assignment is to implement Conway's game of Life. We must do this by reading in a text file formatted something like this:
*
*
***
***
We then have to display the next 10 generations on the screen.
I have the file read into a string array, then I copy it to another array. I then go through it character by character and change the copied array to match what the next generation should be. My problem is that the code I have to count the live neighbors isn't working and I can't figure out why. I displayed the number of live neighbors for each cell on the screen and about half of them are wrong. I know the error is occurring with cells on the edge of the "board," but I can't figure out how to fix it.
Now, I don't want the whole thing written for me, that'd be a bit pointless. I just can't figure out where my logic is off. Any help would be appreciated. Also, I'm aware that my code is pretty poor, overall. This was just the only way I could figure it out. Sorry.
class Program
{
static void Main(string[] args)
{
//gets file name from command arguments
//checks to make sure file exists, exits if file does not exist
if (!File.Exists(Environment.GetCommandLineArgs()[1])) { System.Environment.Exit(1); }
//gets file name from command arguments then reads file into array of strings
string[] gen0 = File.ReadAllLines(Environment.GetCommandLineArgs()[1]);
string[] gen1 = gen0;
char alive = '*';
char dead = ' ';
//displays first generation
foreach (string s in gen0)
{
Console.WriteLine(s);
}
Console.WriteLine("=====================================");
//counts live neighbors of a cell
int count = 0;
for (int i = 0; i < gen0.Length; i++)
{
count = 0;
for (int j = 0; j < gen0[i].Length; j++)
{
//check top left neighbor
if (i > 0 && j > 0 && j < gen0[i-1].Length )
{
if (gen0[i - 1][j - 1] == alive) { count++; }
}
//check above neighbor
if (i > 0 && j < gen0[i-1].Length)
{
if (gen0[i - 1][j] == alive) { count++; }
}
//check top right neighbor
if (i > 0 && j + 1 < gen0[i - 1].Length)
{
if (gen0[i - 1][j + 1] == alive) { count++; }
}
//check left neighbor
if (j > 0)
{
if (gen0[i][j - 1] == alive) { count++; }
}
//check right neighbor
if (j + 1 < gen0[i].Length)
{
if (gen0[i][j + 1] == alive) { count++; }
}
//check bottom left neighbor
if (i + 1 < gen0.Length && j > 0 && j < gen0[i+1].Length)
{
if (gen0[i + 1][j - 1] == alive) { count++; }
}
//check below neighbor
if (i + 1 < gen0.Length && j < gen0[i+1].Length)
{
if (gen0[i + 1][j] == alive) { count++; }
}
//check bottom right neighbor
if (i + 1 < gen0.Length && j + 1 < gen0[i].Length && j + 1 < gen0[i+1].Length)
{
if (gen0[i + 1][j + 1] == alive) { count++; }
}
//Console.WriteLine(count);
//kills cells
if (count < 2 || count > 3)
{
gen1[i] = gen1[i].Remove(j, 1);
gen1[i] = gen1[i].Insert(j, dead.ToString());
}
//births cells
if (count == 3)
{
gen1[i] = gen1[i].Remove(j, 1);
gen1[i] = gen1[i].Insert(j, alive.ToString());
}
}
}
foreach (string s in gen1)
{
Console.WriteLine(s);
}
}
}
Your problem is very simple - you're resetting count in the wrong place. Put it inside the loop and it will (probably) work.
Regarding the rest of the code, if you want to make it significantly easier to follow just give your game area a one-element border.
You'll need to pad the file you read in (blank line above and below, blank characters to the left and right), and alter your loops to:
for (int i = 1; i < gen0.Length - 1; i++)
and
for (int j = 1; j < gen0[i].Length - 1; j++)
but your central count calculation can then be reduced down to a single calculation:
count = (gen0[i - 1][j - 1] == alive) ? 1 : 0 +
(gen0[i - 1][j] == alive) ? 1 : 0 +
... etc ...
which should make for cleaner code and ensure that any other errors you may make are significantly easier to spot.
So the first bug I see is that you don't actually copy the board for the next iteration.
gen1 = gen0;
The code above only assigns the gen1 reference to the same object as gen0. So when you modify gen1, you actually modify gen0 as well... causing inconsistencies in latter iterations. Try this:
gen1 = (string[])gen0.Clone();
Second bug is that int count = 0 should be in the second loop instead of first:
for (int i = 0; i< gen0.Length; i++)
{
// not here
// int count = 0
for (int j = 0; j < gen0[i].Length; j++)
{
// here
int count = 0
...
}
...
}
This way you reset the count for every cell instead of every row.
Related
I am building a minesweeper game in C#, and one of my functions keeps crashing the program. The erorr message is Process is terminated due to StackOverflowException. I tried to find where is the problem in my recursion, but I didn't find it. The problem only happen if Openk is called, so I am pretty sure the problem is there. I tried to find if there is an infinite recursion, but didn't find it. What could be the problem?
public void Openk(int row, int col)
{
if (sqrs[row, col].IsBlank() == true)
{
for (int i = -1; i < 2; i++)
{
for (int j = -1; j < 2; j++)
{
if (IsValid(row - i, col - j))
{
if (sqrs[row - i, col - j].IsMine() == false)
sqrs[row - i, col - j].Open();
Openk(row - i, col - j);
}
}
}
}
}
public bool IsValid(int row, int col)
{
if (row >= n || row <= 0 || col >= n || col <= 0)
return false;
return true;
}
Your problem is that you keep looping and executing Openk on the same middle square each time. Maybe it is because of the unguarded if statement. It goes like this in your code.
You pass the selected coordinates of a square, say [5,5].
Then you loop a row before 5 (4), row 5, and a row after 5 (6).
For each row, your loop a square before the selected column 5 (4), column 5, and and the next column of 5 (6).
In your code, only when you reach the middle square (i = 0, j = 0), you execute the checking of square if it is a mine, and if it is not mine, you pass the selected coordinates after subtracting with zeros (i = 0, j = 0), which effectively loops the same spot again!
.
public void Openk(int row, int col)
{
if (sqrs[row, col].IsBlank() == true)
{
for (int i = -1; i < 2; i++)
{
for (int j = -1; j < 2; j++)
{
// Only execute the following code when checking the middle square, where i = 0, and j = 0.
if (i == 0 && j == 0) // Notice the braces I added, the code has the same effect if you don't type them.
{
// This if statement has not effect.
if (IsValid(row - i, col - j)) // It is like if (true)
{
if (sqrs[row-i, col-j].IsMine() == false)
sqrs[row - i, col - j].Open();
Openk(row - i, col - j); // row - 0 = row, col - 0 = col, your looping infinitely here.
}
}
}
}
}
}
Its been bugging me for hours because it is always returning 0 at numbers[i] and I cant figure out the problem. code worked for a different program but I had to change it so it could have a custom array size and that's when everything went wrong.
any help would be great.
Thanks in advance.
int[] numbers = new int[Convert.ToInt16(TxtArray.Text)];
int j = 0;
for (j = numbers.Length; j >= 0; j--)
{
int i = 0;
for (i = 0; i <= j - 1; i++)
{
string NumbersInput = Microsoft.VisualBasic.Interaction.InputBox("Enter Numbers to be sorted",
"Numbers Input", "", -1, -1);
numbers[i] = Convert.ToInt16(NumbersInput);
//returns 0 in if statement
if (numbers[i] < numbers[i + 1])
{
int intTemp = 0;
intTemp = numbers[i];
numbers[i] = numbers[i + 1];
numbers[i + 1] = intTemp;
}
}
}
for (int i = 0; i < numbers.Length; i++)
{
LstNumbers.Items.Add(numbers[i]);
}
private void button1_Click(object sender, EventArgs e)
{
int sizeOfArrayInt = Convert.ToInt32(arraySize.Text);
int[] array = new int[sizeOfArrayInt];
string numbers = arrayValues.Text;
string[] numbersSplit = numbers.Split(',');
int count = 0;
foreach (string character in numbersSplit)
{
int value;
bool parse = Int32.TryParse(character, out value);
if (value != null)
{
array[count] = value;
}
count++;
}
array = this.SortArray(array);
foreach (int item in array)
{
this.listBox.Items.Add(item);
}
}
private int[] SortArray(int[] arrayToSort)
{
//int[] sortedArray = new int[arrayToSort.Length];
int count = arrayToSort.Length;
for (int j = count; j >= 0; j--)
{
int i = 0;
for (i = 0; i <= j - 2; i++)
{
if (arrayToSort[i] < arrayToSort[i + 1])
{
int intTemp = 0;
intTemp = arrayToSort[i];
arrayToSort[i] = arrayToSort[i + 1];
arrayToSort[i + 1] = intTemp;
}
}
}
return arrayToSort;
}
strong text
This I got to work as a Windows Form and the output displays in the list box as each array item or individual i iteration over the array. Of course there is no error checking. Hope that helps.
Setting aside the strangeness of how you are working with text boxes, your problem with throwing an exception would happen even without them because it lies here, in your inner loop:
for (i = 0; i <= j - 1; i++)
Suppose that numbers.Length == 2. This means that j == 2. So on the first time through the outer loop, you hit the inner loop with these conditions. The first time through, i == 0. You get to the if statement:
if (numbers[i] < numbers[i + 1])
numbers[0] exists, and numbers[1] exists, so this iteration goes through fine and i is incremented.
Now i == 1. Now the loop checks its boundary condition. i <= j - 1 == true, so the loop continues. Now when you hit that if statement, it tries to access numbers[i + 1], i.e., numbers[2], which does not exist, throwing an IndexOutOfRangeException.
Edit: Came back and realized that I left out the solution (to the exception, anyway). For the bubble sort to work, your inner loop's boundary condition should be i <= j - 2, because j's initial value is == numbers.Length, which is not zero-based, while array indexes are.
Second Edit: Note that just using a List won't actually solve this problem. You have to use the right boundary condition. Trying to access list[list.Count()] will throw an ArgumentOutOfRangeException. Just because a List will dynamically resize doesn't mean that it will somehow let you access items that don't exist. You should always take time to check your boundary conditions, no matter what data structure you use.
There is an image which only has vertical lines and horizontal lines. But there are some lines that are the same or they are close to each other, but they should be combined to only one line. Also I wrote loop to add line in a new list, the speed is slow. So I wonder if there are some efficient way for me to combine these adjacent lines to one line.
The following is the code:
for (int i = 0; i < RecoverLine_list.Count; i++) {
for (int j = 0; j < RecoverLine_list.Count; j++) {
for (int m = 0; m < RecoverLine_list.Count; m++) {
if (RecoverLine_list[i] != RecoverLine_list[j]
&& RecoverLine_list[i] != RecoverLine_list[m]
&& RecoverLine_list[j] != RecoverLine_list[m]) {
if (RecoverLine_list[i].orientation == 0
&& RecoverLine_list[j].orientation == 0
&& RecoverLine_list[m].orientation == 0) {
if (Math.Abs(RecoverLine_list[i].P1.Y - RecoverLine_list[j].P1.Y) < 3
&& Math.Abs(RecoverLine_list[i].P1.Y - RecoverLine_list[m].P1.Y) < 3
&& Math.Abs(RecoverLine_list[j].P1.Y - RecoverLine_list[m].P1.Y) < 3) {
// define RecoverLine_list[i] as grid line
GridLine_list.Add(RecoverLine_list[i]);
}
}
}
}
}
}
(REWRITTEN) So, assuming you want to somehow filter horizontal and vertical lines which are 'similar' and want both the code and good condition for what 'similar' should be, then here are my thoughts:
First split horizontal and vertical lines:
List<Line> vert = new List<Line>();
List<Line> horiz = new List<Line>();
foreach(Line ln in RecoverLine_list)
if(ln.orientation == 0) horiz.Add(ln);
else vert.Add(ln);
horiz.Sort(x, y => x.P1.Y.CompareTo(y.P1.Y));
vert.Sort(x, y => x.P1.X.CompareTo(y.P1.X));
or something like that (not exactly sure what your orientation is, I am just guessing including the coding of sorting using lambdas).
Then you can search the lines in one pass extracting good-ones:
List<Line> filtered = new List<Line>();
for(int i = 0; i < horiz.Count; i++) {
filtered.Add(ln); // always add, we can skip next:
if(i+1 >= horiz.Count) break;
if(Math.Abs(horiz[i].P1.Y - horiz[i+1].P1.Y) <= 3)
&& Math.Abs(horiz[i].P1.X - horiz[i+1].P1.X) <= 3)
&& Math.Abs(horiz[i].P2.X - horiz[i+1].P2.X) <= 3))
i++; // skip this line for being similar
}
but that is not final solution, because we can have lines close to each other in one coordinate but not in the other. So we need to add inner loop:
for(int i = 0; i < horiz.Count-1; i++) {
for(int j = i+1; j < horiz.Count; j++) {
if((horiz[j].P1.Y - horiz[i].P1.Y) > 3)
break; // this one is too far, all next will be
if(Math.Abs(horiz[i].P1.Y - horiz[j].P1.Y) <= 3)
&& Math.Abs(horiz[i].P1.X - horiz[j].P1.X) <= 3)
&& Math.Abs(horiz[i].P2.X - horiz[j].P2.X) <= 3))
horiz.RemoveAt(j--); // skip this line for being similar
}}
Got the idea? Same for vertical lines.
if (Int32.Parse(strTotals) == 0 && nTotalCount != 0)
{
nTotalCount = 0;
for (int j = 0; j < 66; j++)
{
if (GameHistoryPicBox1[j].InvokeRequired)
{
GameHistoryPicBox1[j].BeginInvoke(new MethodInvoker(() =>
{
if ((j + j / 6) % 2 == 0)
GameHistoryPicBox1[j].Image = Properties.Resources.al1; // Line2
else
GameHistoryPicBox1[j].Image = Properties.Resources.al2; // Line4
}));
}
else
{
if ((j + j / 6) % 2 == 0)
GameHistoryPicBox1[j].Image = Properties.Resources.al1;
else
GameHistoryPicBox1[j].Image = Properties.Resources.al2;
}
}
}
I have been checking nTotalCount value by using thread.
If nTotalCount is zero, then I must clean all game picture box image.
So I was implement above code.
Unfortunately, I got the error:
An unhandled exception of type 'System.IndexOutOfRangeException'
on Line 2 and 4.
And the j value was 66.
Is it possible that j value could be 66?
This is because of how closures work. Your lambda expression that you're creating and passing to the MethodInvoker references the j variable by reference. Thus when this piece of code is being executed (which can be almost any time, as it's asynchronous) the j variable can have any value from 0 to 66. And it can be 66 after the loop has finished.
A quick fix is to make a copy of j:
int index = j;
GameHistoryPicBox1[index].BeginInvoke(new MethodInvoker(() =>
{
if ((index + index / 6) % 2 == 0)
GameHistoryPicBox1[index].Image = Properties.Resources.al1; // Line2
else
GameHistoryPicBox1[index].Image = Properties.Resources.al2; // Line4
}));
You can read more about this here.
The variable j is being passed into the closure, and because the call is asynchronous, it is actually executed at some point after the loop completes. You cannot be sure what the value of j will be when the delegate is executed.
Try passing the value of j in as a parameter to the delegate, like this:
GameHistoryPicBox1[j].BeginInvoke(new Action<int>((x) =>
{
if ((x + x / 6) % 2 == 0)
GameHistoryPicBox1[x].Image = Properties.Resources.al1;
else
GameHistoryPicBox1[x].Image = Properties.Resources.al2;
}), j);
You've been bitten by the loop-variable-in-a-closure-bug.
Instead of
for (int j = 0; j < 66; j++)
{
//blahblahblah
}
write
for (int jj = 0; jj < 66; jj++)
{
int j = jj;
//blahblahblah
}
I have the below code. I create a list (tried with array as well) with one million and one members. All of them has the value 0 by default. It should work like a multidimensional array, but it's not necessary as the first 'column' has to be numbers from 1-1.000.000. To make life easier I made 1.000.001 members to avoid using the 0th place.
At a certain point I have to use a member of this list addressed by a variable: list[n], and to avoid issues this only happens if n < 1.000.000. As there are members from 0-1.000.000 I think it's OK, but still my program crashes after a while with error code ArgumentOutOfRangeException.
What am I missing here?
int highestCount = 0;
int highestNum = 0;
List<int> list = new List<int>();
for(int j = 0; j <= 1000001; j++)
{
list.Add(0);
}
for (int i = 2; i < 1000000; i++)
{
int count = 0;
int number = i;
do
{
if (i % 2 == 0)
{
number = number / 2;
if (number < 1000000)
{
if (list[number] != 0)
{
count += list[number];
break;
}
else
{
count++;
}
}
else { count++; };
}
else
{
number = (number * 3) + 1;
if (number < 1000000)
{
if (list[number] != 0) //program dies here
{
count += list[number];
break;
}
else
{
count++;
}
}
else { count++; };
}
} while (number > 1);
list[i] = count;
if (count > highestCount)
{
highestCount = count;
highestNum = i;
}
}
MessageBox.Show(highestNum.ToString());
I think it's just a problem with your logic.
When i == 3 (on the second run), it will go into the do-while block.
Since i % 2 != 0 at that point, it will hit the else.
The number is multiplied by 3 and you add 1.
It is less than 1000000, but....
if(list[number] != 0)
never evaluates to true and you are stuck in an infinite do-while loop and it never breaks out because A) number will always be greater than 1 (satisfying the while condition); and B) you set every element of the array to zero in the code above this loop (thus never satisfying the above if condition):
for(int j = 0; j <= 1000001; j++)
{
list.Add(0);
}