I am attempting to make Conway's Game of Life in C# with XAML. The window allows the user to specify the number of rows and columns of my 2D array of cells using a slider. When my Uniform Grid is a perfect square (10x10, 20x20, or even 16x16), the simulations work without a problem. However, when the user attempts to specify a rectangular uniform grid (13x14, 15x26, 24x14), the cells are thrown of by the difference (i.e. in a 33x27 grid, difference = 6, so the cell goes appropriately up, but is thrown off (left/right) by the difference). I have narrowed down that this only happens on the x-axis; the cells are never thrown off on the y-axis.
THE QUESTION: Why is my array throwing off my x axis? Is there something wrong with it?
As far as I can tell, everything should work fine. I set up a log to check the dimensions of my 2D arrays and my uniform grid. I'm not sure what is wrong, and I have been staring and debugging for literally DAYS. I'm at my wits end. Please help, I hope there is something that I am simply not catching.
Code Legend:
unigridOfCells is a Uniform Grid in XAML.
slideWidth/slideHeight are sliders.
Also, I am using a converter from my resource which converts my isAlive property to a SolidColorBrush.
private Cell[,] cells;
private Cell[,] nextGenCells;
private int codeColumn, codeRow, difference, secondDiff;
public MainWindow()
{
InitializeComponent();
unigridOfCells.Height = 500;
unigridOfCells.Width = 500;
setCellsOnGrid(10, 10);
}
//Sets all the cells on the grid, as well as setting the number of columns and rows to be reset for all arrays in the application
public void setCellsOnGrid(int column, int row)
{
unigridOfCells.Rows = row;
unigridOfCells.Columns = column;
codeColumn = column;
codeRow = row;
time = new Timer(3000);
cells = new Cell[codeColumn, codeRow];
nextGenCells = new Cell[codeColumn, codeRow];
for (int i = 0; i < codeColumn; i++)
{
for (int j = 0; j < codeRow; j++)
{
cells[i, j] = new Cell();
Rectangle block = new Rectangle();
block.Height = 10;
block.Width = 10;
block.DataContext = cells[i, j];
block.MouseLeftButtonDown += cells[i, j].ParentClicked;
//block.MouseLeftButtonDown += blockSpace;
Binding b = new Binding();
b.Source = cells[i, j];
b.Path = new PropertyPath("isAlive");
b.Converter = (BoolColorConverter)Application.Current.FindResource("cellLifeSwitch");
block.SetBinding(Rectangle.FillProperty, b);
unigridOfCells.Children.Add(block);
}
}
}
public void blockSpace(object sender, MouseButtonEventArgs e)
{
int spot = 0;
int pick = 0;
for (int i = 0; i < codeColumn; i++)
{
for (int j = 0; j < codeRow; j++)
{
spot = unigridOfCells.Children.IndexOf((Rectangle)sender);
}
}
MessageBox.Show("" + spot + " : " + pick);
}
//Updates the cells. This is where the rules are applied and the isAlive property is changed (if it is).
public void updateCells()
{
for (int n = 0; n < codeColumn; n++)
{
for (int m = 0; m < codeRow; m++)
{
nextGenCells[n, m] = new Cell();
bool living = cells[n, m].isAlive;
int count = GetLivingNeighbors(n, m);
bool result = false;
if (living && count < 2)
{
result = false;
}
if (living && (count == 2 || count == 3))
{
result = true;
}
if (living && count > 3)
{
result = false;
}
if (!living && count == 3)
{
result = true;
}
nextGenCells[n, m].isAlive = result;
}
}
setNextGenCells();
}
//Resets all the cells in a time step
public void setNextGenCells()
{
for (int f = 0; f < codeColumn; f++)
{
for (int k = 0; k < codeRow; k++)
{
cells[f, k].isAlive = nextGenCells[f, k].isAlive;
}
}
}
//Checks adjacent cells to the cell in the position that was passed in
public int GetLivingNeighbors(int x, int y)
{
int count = 0;
// Check cell on the right.
if (x != codeColumn - 1)
if (cells[x + 1, y].isAlive)
count++;
// Check cell on the bottom right.
if (x != codeColumn - 1 && y != codeRow - 1)
if (cells[x + 1, y + 1].isAlive)
count++;
// Check cell on the bottom.
if (y != codeRow - 1)
if (cells[x, y + 1].isAlive)
count++;
// Check cell on the bottom left.
if (x != 0 && y != codeRow - 1)
if (cells[x - 1, y + 1].isAlive)
count++;
// Check cell on the left.
if (x != 0)
if (cells[x - 1, y].isAlive)
count++;
// Check cell on the top left.
if (x != 0 && y != 0)
if (cells[x - 1, y - 1].isAlive)
count++;
// Check cell on the top.
if (y != 0)
if (cells[x, y - 1].isAlive)
count++;
// Check cell on the top right.
if (x != codeColumn - 1 && y != 0)
if (cells[x + 1, y - 1].isAlive)
count++;
return count;
}
//Fires when the next generation button is clicked. Simply makes the board go through the algorithm
private void nextGenerationClick(object sender, RoutedEventArgs e)
{
updateCells();
}
//Fired when the "Reset Grid" button is pressed, resets EVERYTHING with the new values from the sliders
private void resetGrid(object sender, RoutedEventArgs e)
{
MessageBox.Show("First Slide (width) value: " + slideWidth.Value + "\nSecond Slide (length) value: " + slideHeight.Value + "\nDifference: " + (codeColumn - codeRow) + "\nColumns: " + unigridOfCells.Columns + " \nRows: " + unigridOfCells.Rows + "\nChildren count: " + unigridOfCells.Children.Count + " \nLengths: "
+ "\n\tOf 1D of cells: " + cells.GetLength(0) + "\n\tOf 1D of nextGenCells: " + nextGenCells.GetLength(0) + "\n\tUniform Grid Columns: " + unigridOfCells.Columns + " \nWidths: "
+ "\n\tOf 2D of cells: " + cells.GetLength(1) + "\n\tOf 2D of nextGenCells: " + nextGenCells.GetLength(1) + "\n\tUniform Grid Rows: " + unigridOfCells.Rows);
unigridOfCells.Children.Clear();
setCellsOnGrid((int)slideWidth.Value, (int)slideHeight.Value);
}
The problem is that the order in which you create cells differs from the order in which the UniformGrid arranges its children.
In your setCellsOnGrid method, you create cells top-to-bottom, then left-to-right, whereas the UniformGrid arranges its children in the order left-to-right then top-to-bottom.
For a square grid, the cells in the first column of your grid are drawn in the first row of the UniformGrid, and similarly for other columns and rows. You end up with the grid being reflected in the line x = y. However, for a non-square grid, the length of a row does not equal the length of a column and so the grid is completely out of place.
For example, with a 3 × 3 grid, your loop runs in the following order:
1 4 7
2 5 8
3 6 9
However, the controls are added to the UniformGrid in the following order (assuming you haven't set FlowDirection="Right"):
1 2 3
4 5 6
7 8 9
For, for a 3 × 4 grid, your loop runs in the order
1 5 9
2 6 10
3 7 11
4 8 12
but the controls are added to the UniformGrid in the order
1 2 3
4 5 6
7 8 9
10 11 12
This means that cells that adjacent in your cells array might not be drawn as adjacent in the UniformGrid, and vice versa.
Fortunately, the fix is simple: switch the order of the i and j loops in setCellsOnGrid. Make the j loop the outer loop and the i loop the inner loop.
Incidentally, your blockSpace method doesn't appear to use the i and j loop variables - it just calls the same method codeColumn * codeRow times. Is this intentional?
Related
I need to write a program that asks the user for a number from 2 to 12, and then displays a multiplication table with answers in the format below:
User enters: 6
Output:
2 3 4 5 6
2 4 6 8 10 12
3 6 9 12 15 18
4 8 12 16 20 24
5 10 15 20 25 30
6 12 18 24 30 36
I cannot get, neither the top horizontal row (2, 3, 4, 5, 6), nor the left column (2, 3, 4, 5, 6) to display on the WPF application screen, and I also need to get the vertical tab right, so all the numbers are equally spaced horizontally and vertically. My current code looks like this:
int inputNumber;
public MainWindow()
{
InitializeComponent();
btnYes.IsEnabled = false;
}
private void calculateTable_Click(object sender, RoutedEventArgs e)
{
inputNumber = int.Parse(number.Text);
if (inputNumber < 2 || inputNumber > 12)
{
MessageBox.Show("Please enter values between 2 and 12");
number.Clear();
return;
}
for (int i = 2; i <= inputNumber; i++)
{
for (int j = 2; j <= inputNumber; j++)
{
displayValues.Text += (i * j).ToString() + '\t';
}
displayValues.Text += '\n';
}
calculateTable.IsEnabled = false;
btnYes.IsEnabled = true;
}
private void btnYes_Click(object sender, RoutedEventArgs e)
{
number.Clear();
number.Focus();
displayValues.Text = String.Empty;
btnYes.IsEnabled = false;
calculateTable.IsEnabled = true;
}
private void btnNo_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
Simplest and meaningful approach :
Use UniformGrid as <UniformGrid x:Name="UG" Columns="10"/>, code :
int from = 2, to = 15;
for (int i = from; i <= to; ++i)
{
for (int j = 1; j <= 10; ++j)
{
TextBlock tb = new TextBlock() { Text = (i * j).ToString() };
UG.Children.Add(tb);
}
}
There are other approaches :
Create a StackPanel with Orientation = Horizontal for every table. Use TextBlock for every digit.
Create a Grid with 10 columns. Your Columns are fixed, but Rows vary. So, you can create needed Rows, and show every digit in relevant Column. Use RowDefinition, Grid.SetRow() and Grid.SetColumn() .
Use a DataGrid and make it ReadOnly. Create your list, and simple assign it as DataGrid.ItemsSource. You can format the DataGrid to hide various lines etc.
The answer I need to get from my program should be in the following format:
Multiplication Tables from 2 to 12
I'm getting the left most column (2 to 8) and the results, but not the the top row (2 to 8) and the vertical spacing between the numbers. All numbers have to be evenly spaced/distributed in the grid.
One of your problems is that you forgot to print out the first number of the ì -loop iteration. For the linebreak you can also use Environment.NewLine
I used here a TextBox for the display.
Try this:
int inputNumber = 6;
for (int i = 2; i <= inputNumber; i++)
{
textBox.Text += i.ToString() + '\t';
for (int j = 2; j <= inputNumber; j++)
{
textBox.Text += (i * j).ToString() + '\t';
}
textBox.Text += Environment.NewLine;
}
The result looks the following:
And for inputNumber = 12 like this:
hope this helps.
EDIT:
Ok after you edit I understand you a little better.
To get the first line you need only to include an enumeration from 2 to the inputNumber with a leading whitespace " ". For the vertical alignment you could cheat a little and take 3 NewLines, that would be spacially the same as a tab.
here is an example code:
int inputNumber = 6;
textBox.Text += " \t" + String.Join("\t", Enumerable.Range(2, inputNumber-1)) + new string('\n', 3);
for (int i = 2; i <= inputNumber; i++)
{
textBox.Text += i.ToString() + '\t';
for (int j = 2; j <= inputNumber; j++)
{
textBox.Text += (i * j).ToString() + '\t';
}
textBox.Text += new string('\n', 3);
}
The result should look now like this:
I'm quite new to C# and I made this which creates a 5x5 grid which increases each number in the grid by 1 so the grid counts up.
public static void Main () {
int width = 5;
int height = 5;
int gridNumber = 0;
int[,] grid = new int[height, width];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (grid [x,y] + (gridNumber +1) < 10){
Console.Write (grid [x,y] + (gridNumber + 1) + " | " );
}
else if (grid [x,y] + (gridNumber +1) == 10) {
Console.Write (grid [x,y] + (gridNumber + 1) + " |" );
}
else {
Console.Write (grid [x,y] + (gridNumber + 1) + " | " );
}
gridNumber++;
}
Console.WriteLine ();
}
Console.ReadKey ();
}
My problem is whenever I change the grid dimensions (changing either int from a 5 to anything else), I get an error saying Index was outside the bounds of the array.
int width = 5;
int height = 5;
Any ideas on how to fix this?
Change
int[,] grid = new int[height, width];
to
int[,] grid = new int[width, height];
I have a one-dimensional array, and I need to run operations on it based on the adjacents cells of every cell.
For instance:
To run the operations on the first cell, I'll need to access the last cell and the second.
The second cell, I'll need to access the first cell and the third cell.
The last cell, I'll need to access the first cell and the one before the last cell.
My code so far is:
public static int[] firstRule(int[] numberArray)
{
for (int i = 0; i < numberArray.Length; i++)
{
if (numberArray[numberArray.Length - 1 - i] == numberArray[i] + 1
&& numberArray[i + 1] == numberArray[i] + 1)
{
numberArray[i] = numberArray[i] - 1;
}
}
return numberArray;
}
But the problem with my approach is that it would only work for the first cell, as it would take the last cell and the second cell correctly, but after that, everything would fall apart. I don't like posting without a lot of code but I have no clue how to follow this up.
I am trying to achieve the following:
Those values are numbers ranging from 0 to 3. If both the cell before and the cell after is the same number, I want to change it to x + 1
For instance: suppose I have 1 0 1 2 2. I would want to change 0 to 1. As the neighbor cells are both 0.
Just keep it simple and use variables to calculate the left and right cell indices. Inside your for loop you can do this...
var leftCell = i - 1;
if (leftCell < 0)
{
leftCell = numberArray.Length - 1; // Wrap around to the end...
}
var rightCell = i + 1;
if (rightCell > numberArray.Length - 1)
{
rightCell = 0; // Wrap back around to the beginning...
}
// Now you can update your original code to use these computed indices...
if (numberArray[leftCell] == numberArray[i] + 1
&& numberArray[rightCell] == numberArray[i] + 1)
{
numberArray[i] = numberArray[i] - 1;
}
Try this out:
var len = numberArray.Length;
for (int i = 0; i < len; i++)
{
var leftIndex = (i - 1 + len) % len;
var rightIndex = (i + 1) % len;
// do your stuff with numberArray[leftIndex] and numberArray[rightIndex]
}
% is mod operator. % len allows you to stay in range 0..len-1, so you can walk through array as if it has become 'cyclic'
From your comments.
Those values are numbers ranging from 0 to 3. If both the cell before and the cell after is the same number, I want to change it to x + 1
For instance: suppose I have 1 0 1 2 2. I would want to change 0 to 1. As the neighbor cells are both 0.
I would create a new array, populate it with the values of the existing array and then change the values of the new array according to the results of the value in the existing array.
Edit as Op is getting wrong values
I suspect you may not be copying the array correctly instead:
Existing Array array // The array you are passing in as parameter.
Declare a new empty array:
int[] newArray;
int size = array.length;
for(int i =1; i<size-1;i++){
if(array[i-1]==array[i+1])){
newArray[i]=array[i]+1;
}
else{
newArray[i]=array[i];
}
}
if(array[size-1]==array[0]){
newArray[size]= array[size]+1;
}
else{
newArray[i]=array[i];
}
if(array [size]==array[1]){
newArray[0]= array[0]+1;
}
else{
newArray[i]=array[i];
}
if there is a limit to the number and it reverts to zero after 2, then just do a simple if test for that.
int[] arr = new int[] { 1, 2, 3, 4, 5 };
var triples = arr.Select((n, i) =>
{
if (i == 0)
return Tuple.Create(arr[arr.Length - 1], arr[0], arr[1]);
else if (i == arr.Length - 1)
return Tuple.Create(arr[i - 1], arr[i], arr[0]);
else
return Tuple.Create(arr[i - 1], arr[i], arr[i + 1]);
});
foreach (var triple in triples)
{
Console.WriteLine(triple.Item1 + " " + triple.Item2 + " " + triple.Item3);
}
public static void firstRule(int[] numberArray)
{
for (int i = 0; i < numberArray.Length; i++)
{
int? prevElement = i == 0
? numberArray[numberArray.Length-1]
: numberArray[i - 1];
int? nextElement = i == numberArray.Length -1
? numberArray[0]
: numberArray[i + 1];
Console.WriteLine(
String.Format("Prev: {0}; Current: {1}; Next: {2}",
prevElement,
numberArray[i],
nextElement)
);
}
}
And then calling firstRule(new int[]{ 1, 2, 3 }); prints:
Prev: 3; Current: 1; Next: 2
Prev: 1; Current: 2; Next: 3
Prev: 2; Current: 3; Next: 1
OPTION 1
assign regardless
public static int[] firstRule(int[] numberArray)
{
int left,right;
for (int i = 0, max = numberArray.Length - 1; i <= max; i++)
{
left = (i == 0) ? max : i - 1;
right = (i == max) ? 0 : i + 1;
numberArray[i] = (numberArray[left] == numberArray[right]) ? numberArray[i] + 1 : numberArray[i]; //always peforms an assignment;
}
return numberArray;
}
OPTION 2
conditionally assign
public static int[] secondRule(int[] numberArray)
{
int left,right;
for (int i = 0, max = numberArray.Length - 1; i <= max; i++)
{
left = (i == 0) ? max : i - 1;
right = (i == max) ? 0 : i + 1;
if (numberArray[left] == numberArray[right])
{
numberArray[i]++;
}
}
return numberArray;
}
OPTION 3
left and right are only used 1 time in each iteration.. so why bother assigning them to a variable???...
public static int[] thirdRule(int[] numberArray)
{
for (int i = 0, max = numberArray.Length - 1; i <= max; i++)
{
if (numberArray[(i == 0) ? max : i - 1] == numberArray[(i == max) ? 0 : i + 1])
{
numberArray[i]++; // what happens if numberArray[i] is 3, should it become 4 or 0?
}
}
return numberArray;
}
OPTION 4 (UNSAFE)
unsafe - fixed - pointers
public static int[] fourthRule(int[] numberArray)
{
unsafe {
int* pointer, right, left;
for (int i = 0, max = numberArray.Length - 1; i <= max; i++)
{
fixed (int* p1 = &numberArray[0], p2 = &numberArray[i], p3 = &numberArray[max])
{
pointer = p2;
if (i == 0)
{
left = p3;
right = pointer;
right++;
}
else if (i == max)
{
left = pointer;
left--;
right = p1;
}
else
{
left = pointer;
left--;
right = pointer;
right++;
}
if (*right == *left) {
*pointer = *pointer + 1;
}
}
}
}
return numberArray;
}
Recently came up against this myself and found this to be a solid method.
`
int length = numberArray.Length;
for (int i = 0; i < length; ++i)
{
int iMinus = (((i - 1) % length) + length) % length;
int iPlus = (((i + 1) % length) + length) % length;
}`
Something like this should work. It determines the appropriate cells for the operation in each loop and executes the operation. You didn't state what that operation was so you need to fill in the DoYourOperation method.
public static int[] processArray(int[] numberArray)
{
for (int i= 0; i < numberArray.Length; i++)
{
int firstCell;
int secondCell;
//Check if first cell
if(i == 0)
{
firstCell = numberArray[numberArray.length-1]; //Last cell
secondCell = numberArray[i++]; //Next cell
}
//Check if last cell
else if(i == numberArray.Length - 1)
{
firstCell = numberArray[i--]; //Cell before last one
secondCell = numberArray[0]; //First cell
}
else
{
firstCell = numberArray[i--];
secondCell = numberArray[i++];
}
DoYourOperation(firstCell, secondCell);
}
}
I am having trouble displaying data. My problem is that the first series i establish starts from "0" on y, but the second series starts at the y value from the series before it. What do i need to adjust in my code to allow all subsequent series after the first to start at y = 0?
Code.
private void BuildGraph(machine_data[] array)
{
int series_cnt = 1;
chart1.Series.Clear();
chart2.Series.Clear();
for (int x = 0; x < array.Count(); x++)
{
chart1.Series.Add(array[x].name + array[x].Digital_Location);
chart2.Series.Add(array[x].name + array[x].Digital_Location);
int numpoints = array[x].Multi_Datapoints.Count();
for (Int32 i = 0; i < numpoints; i++)
{
/***************************/
chart1.Series[array[x].name + array[x].Digital_Location].ChartType = SeriesChartType.Area;
chart1.Series[array[x].name + array[x].Digital_Location].Points.AddXY(array[x].Multi_Datapoints[i].dt, array[x].Multi_Datapoints[i].state);
/***************************/
chart2.Series[array[x].name + array[x].Digital_Location].ChartType = SeriesChartType.StackedColumn;
// Set up the charting location of the Series
if (array[x].name == "Preci_4")
chart2.Series[array[x].name + array[x].Digital_Location].Points.AddXY(0, array[x].count_1s);
else
chart2.Series[array[x].name + array[x].Digital_Location].Points.AddXY(1, array[x].count_1s);
// Set up the color of the series' according to which DIO they correspond to.
if (array[x].Digital_Location == "DIO0")
chart2.Series[array[x].name + array[x].Digital_Location].Color = Color.Green;
else
chart2.Series[array[x].name + array[x].Digital_Location].Color = Color.Red;
/*chart2.AlignDataPointsByAxisLabel();
return chart2;*/
}
series_cnt++;
}
I've figured it out - I was making 4 separate series. I need only 2 series and to vary the x position. When I make 2 series I am then able to start from 0.
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
}
}
}
}