How to delete multiple lines in console mode C# - c#

I'm coding a game and I am struggling in deleting my stickman before I draw another one.
I could just use Console.Clear() but it clears the whole console and I only need to delete the previous stickman.
I'm trying to use:
private static string path = #"c:..\..\characters\";
private string file;
private int xpast = 0;
private int ypast = 0;
private int LinhasSeparacao;
public Graphics()
{
}
public Graphics(string file)
{
this.file = file;
StreamReader sr = new StreamReader(path + file);
LinhasSeparacao = 0;
do
{
LinhasSeparacao++;
}
while (sr.ReadLine() != "separar");
sr.Close();
}
public void Draw(int x, int y,bool forma = true)
{
string[] persona = File.ReadAllLines(path + file);
LimparAnterior(forma,persona,x,y);
//Console.Clear();
if (forma)
{
for (int i = 0; i < LinhasSeparacao - 1; i++)
{
Console.SetCursorPosition(x, y + i);
Console.Write(persona[i]);
}
}
else
{
int j= 0;
for (int i = LinhasSeparacao; i <persona.Length-1; i++)
{
Console.SetCursorPosition(x, y + j);
Console.Write(persona[i]);
j++;
}
}
xpast = x;
ypast = y;
}
private void LimparAnterior(bool forma,string[] persona, int xlive, int ylive)
{
int i = 0;
for (i = 0 ; i < LinhasSeparacao; i++)
{
Console.SetCursorPosition(xpast > 0? xpast -1:xpast,ypast + i);
Console.Write(" ",persona[i].Length);
}
}
This is my class to draw the new character, it needs x and y coordinates. I use a file to the drawing and put all the line into a array called persona. I'm drawing after someone press the arrows to make the little guy move.
If you need more information, say something. Here is the link to github:
https://github.com/digaso/Wizardoft

The code isn't necessarily your problem, if you look at the hero.txt file you'll notice that there is no space to the right of the hero's sword...
_A_
0
/|\/
/ \
separar
_A_
0
\/|\X
/ \
separar
See where I put the 'X' character? When you hero is facing right there is a sword there. But when you move him left there is no space to overwrite the cell where the sword was.
You either need to alter your hero.txt file so that it has spaces in strategic locations or update your drawing code so that it erases the area where your stickman was, then update his position and draw in the new position.
Since this seems to be a casual learning experience this is fine but if you do decide to get more adventurous you'll want to use a library specifically designed for this kind of thing or redesign your code so that it "composes" the scene by drawing each character to a buffer and then taking the finished buffer to the physical screen. Have fun!

Related

C# Find Winner for Connect 4 using Interface

I'm currently working on an assignment to create a game of Connect 4 on a standard 7x6 grid using Visual Studio forms. I've searched on here already, but couldn't find a question that uses the same method of checking for a win that I am supposed to. I already have the architecture of the game completed; the board is fully functional, the pieces drop correctly, and already placed pieces can't be overwritten. However the catch of the assignment is that checking for a winner needs to implement an interface. This is the interface that's required:
interface Winner
{
//find winner in the specified direction starting from (c,r)
bool straightup(int c, int r);
bool straightdown(int c, int r);
bool left(int c, int r);
bool right(int c, int r);
bool diagleftup(int c, int r);
bool diagrightdown(int c, int r);
bool diagleftdown(int c, int r);
bool diagrightup(int c, int r);
//return true if there is winner
bool winner();
}
Here's the code for the board if it's needed:
bool isWinner, xTurn; //Keeps track if there is a winner and whose turn it is
int xWins, oWins; //Keeps track of each players' wins
//Keeps track of which row the next piece in the column will drop
int col1Drop, col2Drop, col3Drop, col4Drop, col5Drop, col6Drop, col7Drop;
Button[,] places; //Array of buttons on the board
public Form1()
{
InitializeComponent();
isWinner = false;
xTurn = true;
xWins = 0;
oWins = 0;
col1Drop = 0; col2Drop = 0; col3Drop = 0; col4Drop = 0; col5Drop = 0; col6Drop = 0; col7Drop = 0;
currentTurn.Text = "X";
winRecords.Text = "Record:" + Environment.NewLine + Environment.NewLine + "X Wins - 0"+ Environment.NewLine + "O Wins - 0";
//Initializes the array of buttons on the board, starting from left to right, bottom to top
places = new Button[,]{ { col1row1, col1row2, col1row3, col1row4, col1row5, col1row6 }, { col2row1, col2row2, col2row3, col2row4, col2row5, col2row6 }, { col3row1, col3row2, col3row3, col3row4, col3row5, col3row6 }, { col4row1, col4row2, col4row3, col4row4, col4row5, col4row6 }, { col5row1, col5row2, col5row3, col5row4, col5row5, col5row6 }, { col6row1, col6row2, col6row3, col6row4, col6row5, col6row6 }, { col7row1, col7row2, col7row3, col7row4, col7row5, col7row6 } };
}
private void col1row6_Click(object sender, EventArgs e) //Runs when top button on column is clicked, all other buttons in column are disabled
{
if(xTurn) //Plays if its X's turn
{
if (col1Drop < 6) //Places piece as long as there's still a spot available
{
places[0, col1Drop].BackgroundImage = GUI_Connect4.Properties.Resources.X;
places[0, col1Drop].BackgroundImageLayout = ImageLayout.Stretch; //Fills the next available row in the column
xTurn = false; //Changes it to next player's turn
currentTurn.Text = "O";
col1Drop++; //Moves to next avaiable column
}
}
else //Plays if it's O's turn
{
if (col1Drop < 6)
{
places[0, col1Drop].BackgroundImage = GUI_Connect4.Properties.Resources.O;
places[0, col1Drop].BackgroundImageLayout = ImageLayout.Stretch;
xTurn = true;
currentTurn.Text = "X";
col1Drop++;
}
}
} //This code is the same for each column, with values changed accordingly
How would I go about checking for a win, and where should the interface be implemented? I'm assuming it should just be implemented in a separate checkWinner method that runs whenever a piece is played.

c# Windows Forms adding pictureBoxes to an Array

I need to add PictureBox's (pictureBox11 to pictureBox30) to an array.
So instead of adding PictureBox's like this:
PictureBox[] Coins = new PictureBox[20];
Coins[0] = pictureBox11;
...
Coins[19] = pictureBox30;
I wrote a for cycle like this (DOESN'T WORK) :
for (int i = 11; i < 31; i++)
{
for (int j = 0; j < Coins.Length; j++)
{
Coins[j] = (PictureBox)Controls.Find(
"pictureBox" + i.ToString(), true)[0];
}
}
There might be a small stupid mistake somewhere because I use the same cycle for another thing and it works, idk, maybe I'm just blind and cant see the mistake.
Maybe it is relevant, so I will include the code I am assigning for the array elements:
for (int i = 0; i < Coins.Length; i++)
{
if (player.Bounds.IntersectsWith(Coins[i].Bounds))
{
Coins[i].Visible = false;
}
}
EVERYTHING works fine if I add them as shown in first code, but it is not very practical.
Why isn't the second code (the for cycle) I wrote working for me?
Is there a better way to add multiple pictureboxes to an array?
I am guessing you are making this more complicated than it has to be. To try and understand better, I assume that when you say I need to add pictureboxes (pictureBox11 to pictureBox30) to an array. that you mean there are 30+ PictureBoxs on your form and each PictureBox uses a naming convention such that each is named “pictureBoxX” where “X” is 1,2,3…30,31. Then you want to get a (consecutive?) group of “PictureBoxes” on the form to make invisible. I hope this is correct.
To simply make the picture boxes invisible, I do not think an array is needed. Simply loop through the picture boxes and make it invisible if the name matches a string of the form “pictureBoxX “. I used IndexsAreValid method to validate the start and end indexes. It is also used in the code for an array implementation below this code.
Make PictureBoxes invisible without an array
private void SetPictureBoxesInvisible(int start, int end) {
int size = -1;
string targetString = "";
if (IndexsAreValid(start, end, out size)) {
for (int i = start; i < end + 1; i++) {
try {
targetString = "pictureBox" + i;
PictureBox target = (PictureBox)Controls.Find(targetString, true)[0];
if (target != null) {
target.Visible = false;
}
}
catch (IndexOutOfRangeException e) {
return;
}
}
}
}
If you must have a PictureBox array returned, then the code below should work.
First, to get an array of PictureBoxs as you want you need an array to store them. But first you need to know how big to make it. From your posted code it appears that you want to get picture boxes 11-30 and put them in an array. So we can get the size from these numbers… i.e. 30-11=19 +1 = 20. That’s about all you need. Simply create the array and loop through all the picture boxes and grab pictureBox11-pictureBox30. When done we can use this array to make these `PictureBoxes” invisible.
I created a method IsValidPic similar to a tryParse to validate if the given index (1,2,3..30) is valid. If it is out of range, I simply ignore that value. This gives you the ability to grab an individual picture box in case the desired picture boxes are not contiguous. I used a few buttons to test the methods.
Hope this helps.
private PictureBox[] GetPictureBoxes(int start, int end) {
int size = - 1;
if (IndexsAreValid(start, end, out size)) {
PictureBox curPic = null;
PictureBox[] allPics = new PictureBox[size];
int index = 0;
for (int i = start; i <= end; i++) {
if (IsValidPic(i, out curPic)) {
allPics[index] = curPic;
index++;
}
}
return allPics;
}
else {
return new PictureBox[0];
}
}
private Boolean IndexsAreValid(int start, int end, out int size) {
if (start < 1 || end < 1) {
size = -1;
return false;
}
if (start > end) {
size = -1;
return false;
}
size = end - start + 1;
return true;
}
private Boolean IsValidPic(int index, out PictureBox picture) {
string targetName = "pictureBox" + index;
try {
PictureBox target = (PictureBox)Controls.Find(targetName, true)[0];
if (target != null) {
picture = target;
return true;
}
picture = null;
return false;
}
catch (IndexOutOfRangeException e) {
picture = null;
return false;
}
}
private void ResetAll() {
foreach (PictureBox pb in this.Controls.OfType<PictureBox>()) {
pb.Visible = true;
}
}
private void button1_Click(object sender, EventArgs e) {
TurnInvisible(2, 3);
}
private void button3_Click(object sender, EventArgs e) {
TurnInvisible(11, 30);
}
private void button4_Click(object sender, EventArgs e) {
TurnInvisible(1,7);
}
private void TurnInvisible(int start, int end) {
PictureBox[] pictureBoxesToChange = GetPictureBoxes(start, end);
foreach (PictureBox pb in pictureBoxesToChange) {
if (pb != null)
pb.Visible = false;
}
}
private void button2_Click(object sender, EventArgs e) {
ResetAll();
}

Interchange two PictureBoxes

I'm trying to make the game 2048 in C#, but I have a problem when I move the tiles.
Here is where I keep the tiles:
picture = new PictureBox[4, 4] {
{pic1,pic2,pic3,pic4},
{pic5,pic6,pic7,pic8},
{pic9,pic10,pic11,pic12},
{pic13,pic14,pic15,pic16} };
And when I press 'W' I move it up :
public void Up()
{
for (int i = 1; i < picture.GetLength(0); i++)
for (int j = 0; j < picture.GetLength(1); j++)
if(picture[i,j].Image!=null && picture[i-1,j].Image==null)
{
picture[i - 1, j].Image = picture[i, j].Image;
picture[i, j].Image = null;
Up();
}
}
but that code only interchanges images from picturebox not the picturebox itself. How can I interchange the picturebox with image and all its property ?
There is no switching functionality in C#, so you will need a temporary helper variable.
It is up to you to decide what you want to switch:
the pictureBoxes referenced in your array
just their locations
their images
or any combination..
Here is an example of a simple helper function that exchanges the references and their Locations:
void SwitchControls(PictureBox pb1, PictureBox pb2)
{
PictureBox temp = pb1;
Point tempLoc = pb1.Location;
pb1 = pb2;
pb2 = temp;
pb1.Location = pb2.Location;
pb2.Location = tempLoc ;
}
You would call it for example like this:
SwitchControls(picture[i - 1, j], picture[i, j]);
To exchange the Images you would write the function as
void SwitchImages(PictureBox pb1, PictureBox pb2)
{
Image temp = pb1.Image;
pb1.Image = pb2.Image;
pb2.Image = temp.Image;
}
etc..

How can I instantiate large number of buttons in Windows Forms?

I'm developing a theatre reservation software. I'm using Windows Forms, the seats is represented by a 2-dimensioned array. And I draw the buttons as following:
public void DrawSeats()
{
// pnl_seats is a Panel
pnl_seats.Controls.Clear();
// Here I store all Buttons instance, to later add all buttons in one call (AddRange) to the Panel
var btns = new List<Control>();
// Suspend layout to avoid undesired Redraw/Refresh
this.SuspendLayout();
for (int y = 0; y < _seatZone.VerticalSize; y++)
{
for (int x = 0; x < _seatZone.HorizontalSize; x++)
{
// Check if this seat exists
if (IsException(x, y))
continue;
// Construct the button with desired properties. SeatSize is a common value for every button
var btn = new Button
{
Width = SeatSize,
Height = SeatSize,
Left = (x * SeatSize),
Top = (y * SeatSize),
Text = y + "" + x,
Tag = y + ";" + x, // When the button clicks, the purpose of this is to remember which seat this button is.
Font = new Font(new FontFamily("Microsoft Sans Serif"), 6.5f)
};
// Check if it is already reserved
if (ExistsReservation(x, y))
btn.Enabled = false;
else
btn.Click += btn_seat_Click; // Add click event
btns.Add(btn);
}
}
// As said before, add all buttons in one call
pnl_seats.Controls.AddRange(btns.ToArray());
// Resume the layout
this.ResumeLayout();
}
But already with a seat zone of 20 by 20 (400 buttons), it spent almost 1 minute to draw it, and in debug I checked that the lack of performance, is the instantiation of the buttons.
There is a way to make it faster? Perhaps disable all events during the instatiation or another lightweight Control that has the Click event too?
UPDATE:
lbl was a test, the correct is btn, sorry.
UPDATE 2:
Here is the IsException and ExistsReservations methods:
private bool IsException(int x, int y)
{
for (var k = 0; k < _seatsExceptions.GetLength(0); k++)
if (_seatsExceptions[k, 0] == x && _seatsExceptions[k, 1] == y)
return true;
return false;
}
private bool ExistsReservation(int x, int y)
{
for (var k = 0; k < _seatsReservations.GetLength(0); k++)
if (_seatsReservations[k, 0] == x && _seatsReservations[k, 1] == y)
return true;
return false;
}
Suppose that you change your arrays for reservations and exclusions to
public List<string> _seatsExceptions = new List<string>();
public List<string> _seatsReservations = new List<string>();
you add your exclusions and reservations in the list with something like
_seatsExceptions.Add("1;10");
_seatsExceptions.Add("4;19");
_seatsReservations.Add("2;5");
_seatsReservations.Add("5;5");
your checks for exclusions and reservations could be changed to
bool IsException(int x, int y)
{
string key = x.ToString() + ";" + y.ToString();
return _seatsExceptions.Contains(key);
}
bool ExistsReservation(int x, int y)
{
string key = x.ToString() + ";" + y.ToString();
return _seatsReservations.Contains(key);
}
of course I don't know if you are able to make this change or not in your program. However consider to change the search on your array sooner or later.
EDIT I have made some tests, and while a virtual grid of 20x20 buttons works acceptably well (31 millisecs 0.775ms on average), a bigger one slows down noticeably. At 200x50 the timing jumps to 10 seconds (1,0675 on average). So perhaps a different approach is needed. A bound DataGridView could be a simpler solution and will be relatively easy to handle.
I also won't use such a myriad of controls to implement such a thing. Instead you should maybe create your own UserControl, which will paint all the seats as images and reacts on a click event.
To make it a little easier for you i created such a simple UserControl, that will draw all the seats and reacts on a mouse click for changing of the state. Here it is:
public enum SeatState
{
Empty,
Selected,
Full
}
public partial class Seats : UserControl
{
private int _Columns;
private int _Rows;
private List<List<SeatState>> _SeatStates;
public Seats()
{
InitializeComponent();
this.DoubleBuffered = true;
_SeatStates = new List<List<SeatState>>();
_Rows = 40;
_Columns = 40;
ReDimSeatStates();
MouseUp += OnMouseUp;
Paint += OnPaint;
Resize += OnResize;
}
public int Columns
{
get { return _Columns; }
set
{
_Columns = Math.Min(1, value);
ReDimSeatStates();
}
}
public int Rows
{
get { return _Rows; }
set
{
_Rows = Math.Min(1, value);
ReDimSeatStates();
}
}
private Image GetPictureForSeat(int row, int column)
{
var seatState = _SeatStates[row][column];
switch (seatState)
{
case SeatState.Empty:
return Properties.Resources.emptySeat;
case SeatState.Selected:
return Properties.Resources.choosenSeat;
default:
case SeatState.Full:
return Properties.Resources.fullSeat;
}
}
private void OnMouseUp(object sender, MouseEventArgs e)
{
var heightPerSeat = Height / (float)Rows;
var widthPerSeat = Width / (float)Columns;
var row = (int)(e.X / widthPerSeat);
var column = (int)(e.Y / heightPerSeat);
var seatState = _SeatStates[row][column];
switch (seatState)
{
case SeatState.Empty:
_SeatStates[row][column] = SeatState.Selected;
break;
case SeatState.Selected:
_SeatStates[row][column] = SeatState.Empty;
break;
}
Invalidate();
}
private void OnPaint(object sender, PaintEventArgs e)
{
var heightPerSeat = Height / (float)Rows;
var widthPerSeat = Width / (float)Columns;
e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
for (int row = 0; row < Rows; row++)
{
for (int column = 0; column < Columns; column++)
{
var seatImage = GetPictureForSeat(row, column);
e.Graphics.DrawImage(seatImage, row * widthPerSeat, column * heightPerSeat, widthPerSeat, heightPerSeat);
}
}
}
private void OnResize(object sender, System.EventArgs e)
{
Invalidate();
}
private void ReDimSeatStates()
{
while (_SeatStates.Count < Rows)
_SeatStates.Add(new List<SeatState>());
if (_SeatStates.First().Count < Columns)
foreach (var columnList in _SeatStates)
while (columnList.Count < Columns)
columnList.Add(SeatState.Empty);
while (_SeatStates.Count > Rows)
_SeatStates.RemoveAt(_SeatStates.Count - 1);
if (_SeatStates.First().Count > Columns)
foreach (var columnList in _SeatStates)
while (columnList.Count > Columns)
columnList.RemoveAt(columnList.Count - 1);
}
}
This will currently draw forty rows and columns (so there are 800 seats) and you can click on each seat to change its state.
Here are the images i used:
EmtpySeat:
ChoosenSeat:
FullSeat:
If you anchor this control and resize it or you click on a seat to change its state there can be some minor lacking for the repainting if you further increase the number of rows or columns, but that is still somewhere far below one second. If this still hurts you, you have to improve the paint method and maybe check the ClipRectangle property of the paint event and only paint the really needed parts, but that's another story.
Rather than using actual button controls, just draw the image of the seats then when the user clicks on a seat translate the mouse X,Y coordinates to determine which seat was clicked. This will be more efficient. Of course, the drawback is that you have to write the method to translate x,y coordinates to a seat, but that really isn't that difficult.
EDIT; it has been pointed out to me this will not work in Windows Forms!
Well, you are Sequentially working through it.
if one iteration costs 1 sec, the full process will take 400*1 in time.
Perhaps you should try and make a collection of your objects, and process it 'parallel'.
try the .Net framework (4 and above) 'parallel foreach' method:
http://msdn.microsoft.com/en-s/library/system.threading.tasks.parallel.foreach(v=vs.110).aspx
edit: so, if you have a list buttonnames, you can say
buttonNames.ForEach(x=>CreateButton(x));
while your CreateButton() method is as following:
private Button CreateButton(string nameOfButton) { Button b = new
Button(); b.Text = nameOfButton; //do whatever you want...
return b; }

Conway's Game of Life not updating correctly [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I wrote a quick implementation of Conway's Game of Life, but it ran awfully slow mostly because my method of checking for neighbouring cells involved looping through the entire grid of cells again, now I've changed my method of checking for neighbouring cells but unfortunately it's not updating correctly anymore, it seems to work fine except that it doesn't create nearly as many new cells as it should.
Now I have spent a good few hours manually debugging the code, going through it with breakpoints and trying to compare the values and calls, but it SEEMS as if my GetNeighbours() method is working, so I concede to you, I can't figure out what's wrong on my own. I've submitted the code below for help.
EDIT: Some of you have pointed out that I can't copy my Grid.cells array the way I am doing it. I've changed it to use Array.Copy() instead but unfortunately it still doesn't work completely. I can't figure it out but it still doesn't seem to create new cells in all cases where it should.
MainForm.cs
public partial class MainFom : Form
{
Grid formGrid;
CancellationTokenSource tokenSrc = new CancellationTokenSource();
public MainFom()
{
InitializeComponent();
}
private void MainFom_Load(object sender, EventArgs e)
{
formGrid = new Grid();
for (int i = 0; i < 50; i++)
{
int xCoord = 10 * i + 12;
Controls.Add(new Label()
{
AutoSize = true,
Text = i.ToString(),
Location = new Point(xCoord, 0),
Font = new Font(Font.FontFamily, 6)
});
for (int s = 0; s < 50; s++)
{
int yCoord = 10 * s + 12;
Controls.Add(new Label()
{
AutoSize = true,
Text = s.ToString(),
Location = new Point(0, yCoord),
Font = new Font(Font.FontFamily, 6)
});
}
}
}
private void MainFom_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(formGrid.toBitmap(), 0, 0);
e.Graphics.Dispose();
}
private void startBtn_Click(object sender, EventArgs e)
{
Task tempTask = Task.Factory.StartNew(
(x) =>
{
while (!tokenSrc.IsCancellationRequested)
{
formGrid.UpdateGrid();
Graphics graphics = this.CreateGraphics();
graphics.Clear(this.BackColor);
graphics.DrawImage(formGrid.toBitmap(), 0, 0);
graphics.Dispose();
}
}, tokenSrc);
startBtn.Hide();
Button stopBtn = new Button() { Text = "Stop", Location = startBtn.Location, Size = startBtn.Size };
this.Controls.Add(stopBtn);
stopBtn.Click += new EventHandler(
(x, y) =>
{
tokenSrc.Cancel();
stopBtn.Hide();
startBtn.Show();
tempTask.Wait();
tokenSrc = new CancellationTokenSource();
});
}
}
Grid.cs
class Grid
{
#region Properties/Fields
const int MAX_CELLS = 50;
Random RNG = new Random();
Cell[,] cells;
int generations = new int();
#endregion
public Grid()
{
cells = new Cell[MAX_CELLS, MAX_CELLS];
for (int x = 0; x < MAX_CELLS; x++)
{
int xCoord = 10 * x + 12;
for (int y = 0; y < MAX_CELLS; y++)
{
int yCoord = 10 * y + 12;
Point point = new Point(xCoord, yCoord);
if (RNG.Next(100) < 20) {
cells[x, y] = new Cell(point, true); }
else {
cells[x, y] = new Cell(point, false);
}
}
}
}
public void UpdateGrid()
{
Cell[,] copy = cells;
for (int x = 0; x < MAX_CELLS; x++)
{
for (int y = 0; y < MAX_CELLS; y++)
{
int neighboursCtr = GetNeighbours(x, y);
//Rule 1: Any live cell with fewer than two live neighbours dies, as if caused by under-population.
if (cells[x, y].IsAlive && neighboursCtr < 2)
{
copy[x, y].Kill();
}
//Rule 2: Any live cell with more than three live neighbours dies, as if by overcrowding.
if (cells[x, y].IsAlive && neighboursCtr > 3)
{
copy[x, y].Kill();
}
//Rule 3: Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
if (!cells[x, y].IsAlive && neighboursCtr == 3)
{
copy[x, y].Alive();
}
}
}
cells = copy;
generations++;
}
public Bitmap toBitmap()
{
Bitmap gridBmp = new Bitmap(1000, 1000); // TODO: Find optimal size for bmp
Size cellSize = new Size(10, 10);
using (Graphics gfxObj = Graphics.FromImage(gridBmp))
{
// Draw grid here and Dispose() on Pen, gfxObj is implicitly disposed
Pen myPen = new Pen(Color.LightGray);
SolidBrush myBrush = new SolidBrush(Color.Black);
for (int x = 0; x < MAX_CELLS; x++)
{
for (int y = 0; y < MAX_CELLS; y++)
{
if (!cells[x, y].IsAlive)
{
gfxObj.DrawRectangle(myPen, new Rectangle(cells[x, y].point, cellSize));
} else
{
gfxObj.FillRectangle(myBrush, new Rectangle(cells[x, y].point, cellSize));
}
}
}
myPen.Dispose();
myBrush.Dispose();
}
return gridBmp;
}
private int GetNeighbours(int column, int row)
{
int neighbours = new int();
int[] starts = new int[] { Math.Max(0 ,column - 1), Math.Max(0, row - 1) };
int[] ends = new int[] { Math.Min(49, column + 1), Math.Min(49, row + 1) };
double colAndRow = column + row/10;
for (int x = starts[0]; x < ends[0]+1; x++)
{
for (int y = starts[1]; y < ends[1]+1; y++)
{
double xAndY = x + y/10;
if (cells[x, y].IsAlive && xAndY != colAndRow)
{
neighbours++;
}
}
}
return neighbours;
}
}
Cell.cs
struct Cell
{
public bool IsAlive { get; private set; }
public readonly Point point;
public Cell(Point point, bool isAlive) : this()
{
this.point = point;
IsAlive = isAlive;
}
public void Alive()
{
IsAlive = true;
}
public void Kill()
{
IsAlive = false;
}
}
The problem is in your UpdateGrid() method. You're simply assigning the reference for your original array to a new variable:
Cell[,] copy = cells;
But this is still the same object; in particular, there's no difference between calling copy[x, y].Kill() and cells[x, y].Kill(). So you're modifying your state during calculations, this affects your code's logic.
Make a copy of the original using Array.Copy and it should work correctly (there doesn't seem to be anything else wrong with your algorithm).
Arrays are reference types, which means
Cell[,] copy = cells;
doesn't what you probably intent to do. It's not a copy of the source array, so it will manipulate this whilst analyzing the neighbors which will lead to wrong results.
Use Array.Copy.
There are a lot of improvements that can be done.
Take a look at Optimizing Conway's 'Game of Life' and Hashlife
You can start by using LockBits to work faster with the bitmap.
You can use parallel programming to improve the loops: Save time with parallel FOR loop
You can also improve the algorithm avoiding the whole matrix scan each time, and instead maintain a list of the alive cells, and only step thru these cells and it's neighbors.
I've implemented such algorithm in the following C# Game of Life Code.

Categories