I've been programming for about 4 months now, just trying to learn by myself. I've tried my way with coding the Game of Life here, would like some general feedback as well as some pointers on how I can speed it up because right now it seems to run incredibly slowly. Keep in mind I'm a newbie so I would like some easy ways to optimize it.
Both positive and negative feedback are very welcome.
Here's the complete code:
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();
}
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();
});
}
}
class Grid
{
#region Properties/Fields
const int MAX_CELLS_X = 41*2;//41;
const int MAX_CELLS_Y = 35*2;//35;
Random RNG = new Random();
CellCollection cells;
#endregion
public Grid()
{
//Initialize grid (both frontend and backend)
cells = new CellCollection();
for (int x = 0; x < MAX_CELLS_X; x++)
{
int XCord = 10 * (x + 1);
for (int y = 0; y < MAX_CELLS_Y; y++)
{
int YCord = 10 * (y + 1);
Point point = new Point(XCord, YCord);
if (RNG.Next(100) < 7)
{ // 10% chance of initial seed creating a live cell
cells.Add(new Cell(new Rectangle(point, new Size(10, 10)), point) { isAlive = true });
} else
{
cells.Add(new Cell(new Rectangle(point, new Size(10, 10)), point));
}
}
}
}
public void UpdateGrid()
{
//Create copy of cells since all changes must be done simultaneously
CellCollection copy = cells;
for (int i = 0; i < copy.Count; i++)
{
//Rule 1: Any live cell with fewer than two live neighbours dies, as if caused by under-population.
if (cells[i].isAlive && cells.GetNeighbours(cells[i]).Length < 2)
{
copy[i].Kill();
}
//Rule 2: Any live cell with more than three live neighbours dies, as if by overcrowding.
if (cells[i].isAlive && cells.GetNeighbours(cells[i]).Length > 3)
{
copy[i].Kill();
}
//Rule 3: Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
if (!cells[i].isAlive && cells.GetNeighbours(cells[i]).Length == 3)
{
cells[i].Alive();
}
}
// Now that all cells are changed we can copy those changes simulatenously by copying the copy back to the original
cells = copy;
}
public Bitmap toBitmap()
{
Bitmap gridBmp = new Bitmap(1000, 1000); // TODO: Find optimal size for bmp
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);
foreach (var cell in cells)
{
if (!cell.isAlive) {
gfxObj.DrawRectangle(myPen, cell.rect);
} else {
gfxObj.FillRectangle(myBrush, cell.rect);
}
}
myPen.Dispose();
}
return gridBmp;
}
}
class CellCollection : List<Cell>
{
public Cell[] GetNeighbours(Cell cell)
{
List<Cell> neighbours = new List<Cell>();
foreach (Cell entry in this)
{
//Top row
if(entry.point.Y.Equals(cell.point.Y - 10)) {
if (entry.point.X.Equals(cell.point.X-10) || entry.point.X.Equals(cell.point.X) || entry.point.X.Equals(cell.point.X+10))
{
if (entry.isAlive) {
neighbours.Add(entry);
}
}
}
// Middle row
if (entry.point.Y.Equals(cell.point.Y)) {
if (entry.point.X.Equals(cell.point.X - 10) || entry.point.X.Equals(cell.point.X + 10))
{
if (entry.isAlive) {
neighbours.Add(entry);
}
}
}
//Bottom row
if (entry.point.Y.Equals(cell.point.Y + 10))
{
if (entry.point.X.Equals(cell.point.X - 10) || entry.point.X.Equals(cell.point.X) || entry.point.X.Equals(cell.point.X + 10))
{
if (entry.isAlive) {
neighbours.Add(entry);
}
}
}
}
return neighbours.ToArray();
}
}
class Cell
{
public bool isAlive { get; set; }
public Rectangle rect { get; set; }
public readonly Point point;
public Cell(Rectangle rect, Point point)
{
this.rect = rect;
this.point = point;
}
public void Alive()
{
isAlive = true;
}
public void Kill()
{
isAlive = false;
}
}