I have a class with the following members:
X
Y
Width
Height
One can create a rectangle with these parameters.
Now my problem is I have a list of this class, List<MyClass>.
I need to compare each object of the list with all the remaining objects in such a way that if the currentObject.Location(X, Y) falls in the rectangle(X, Y, Width, Height) of the other object, I need to delete the other object from the list.
I implemented it with for loops.
But the major problem is: performance.
My minimum list count is 300000.
Is there any procedure to improve the performance for this task uisng any of the .Net versions including LINQ?
`public class RectBase
{
private int _rectId;
private PointF _rectLocation;
private SizeF _rectSize;
public RectBase()
{
_rectId = -911;
_rectLocation = new PointF(0, 0);
_rectSize = new SizeF(0, 0);
}
public RectBase(int id, PointF loc, SizeF size)
{
_rectId = id;
_rectLocation = loc;
_rectSize = size;
}
public bool IsIntersected(RectBase otherRectObject)
{
RectangleF currentRect = new RectangleF(_rectLocation, _rectSize);
if (currentRect.Contains(otherRectObject.RectLocation))
return true;
else
return false;
}
public int RectId
{
get { return _rectId; }
set { _rectId = value; }
}
public PointF RectLocation
{
get { return _rectLocation; }
set { _rectLocation = value; }
}
public SizeF RectSize
{
get { return _rectSize; }
set { _rectSize = value; }
}
}
public class RectProcessor
{
List<RectBase> _rectList;
int maxCount = 300000;
public RectProcessor()
{
_rectList = new List<RectBase>();
FillList();
}
private void FillList()
{
// Adding the items to the list with dummy values
for (int i = 0; i < maxCount; i++)
{
int id = i+1;
PointF loc = new PointF(id, id);
SizeF sz = new SizeF(id, id);
RectBase obj = new RectBase(id, loc, sz);
_rectList.Add(obj);
}
}
private void RemoveIntersectedObjects()
{
List<RectBase> filteredList = new List<RectBase>();
bool isIntersected = false;
for (int i = 0; i < maxCount; i++)
{
for (int j = 0; j < maxCount; j++)
{
if (_rectList[i].IsIntersected(_rectList[j]))
{
isIntersected = true;
break;
}
}
if (!isIntersected)
{
filteredList.Add(_rectList[i]);
}
isIntersected = false;
}
}
}
`
The problem isn't eliminating for loops, at least in the way that you're thinking of it. Rewriting this in LINQ is just going to hide the for loops but they'll still be there. And that's the fundamental problem. Your algorithm, as written, is O(n^2) and that's why you see a ridiculous explosion in time as you go from 20,000 elements to 300,000 elements. You're doing 400,000,000 comparisons in the first case, and 90,000,000,000 in the second case and it will continue to grow like O(n^2).
So, the question you really want to ask is: is there an algorithm with time complexity better than O(n^2) for this problem?
Frankly, I don't know the answer to that question. I suspect that the answer is no: you can't know if a point is contained in some rectangle without comparing it to all the rectangles, and you have to inspect all the points. But maybe there's a clever way to do it such as computing the convex hull of all the rectangles and use that somehow?
This problem is an example of the field of computational geometry.
Related
using UnityEngine;
using UnityEditor;
public class SearchableWindow : EditorWindow
{
string searchString = "";
[MenuItem("Tools/Searching")]
private static void Searching()
{
const int width = 340;
const int height = 420;
var x = (Screen.currentResolution.width - width) / 2;
var y = (Screen.currentResolution.height - height) / 2;
GetWindow<SearchableWindow>().position = new Rect(x, y, width, height);
}
void OnGUI()
{
GUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.FlexibleSpace();
searchString = GUILayout.TextField(searchString, EditorStyles.toolbarTextField);
GUILayout.EndHorizontal();
var items = Selection.gameObjects;
// Do comparison here. For example
for (int i = 0; i < items.Length; i++)
{
if (items[i].name.Contains(searchString))
{
GUILayout.Label(items[i].name);
}
}
}
}
Before it was working fine but now everything is very slow and also I don't see the TextField and when selecting a GameObject in the Hierarchy it's taking almost 5 seconds to show it in the Editor Window.
And before it was all fast and showing the whole Hierarchy in the Editor Window.
Now it's empty:
I took the answer from this question:
Searchable
OnGUI is only called while the mouse is (moved/clicked) over the according Window -> it is not taking almost 5 seconds but until you move your mouse over the window again.
In order to solve that you could implement EditorWindow.OnSelectionChange and force a EditorWindow.Repaint so your window is refreshed everytime the Selection changes.
private void OnSelectionChange()
{
Repaint();
}
The second issue is produced as follows:
GUILayout.TextField together with GUILayout.FlexibleSpace if not defined different uses automatically the width of the inserted text -> Since you have an empty text at start the width is almost 0 ... you actually can see your TextField very thin there and it gets bigger as you fill it:
Using the drawers from the EditorGUILayout instead solves this:
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.FlexibleSpace();
searchString = EditorGUILayout.TextField(searchString, EditorStyles.toolbarTextField);
EditorGUILayout.EndHorizontal();
var items = Selection.gameObjects;
// Do comparison here. For example
foreach (var selectedObject in selected)
{
if (selectedObject .name.Contains(searchString))
{
EditorGUILayout.LabelField(selectedObject.name);
}
}
Tip If you replace
EditorGUILayout.LabelField(items[i].name);
with
if (GUILayout.Button(selectedObject.name, EditorStyles.label))
{
EditorGUIUtility.PingObject(selectedObject);
}
you can click on the name and "ping" the according GameObject in the hierachy!
Update since asked in the comments
If you want to include all children of the selection recursive you could do something like (there might be better ways but that's what I came up with within 10 minutes)
private static IEnumerable<GameObject> GetChildrenRecursive(GameObject root)
{
var output = new List<GameObject>();
//add the root object itself
output.Add(root);
// iterate over direct children
foreach (Transform child in root.transform)
{
// add the child itslef
output.Add(child.gameObject);
// Recursion here: Get all subchilds of this child
var childsOfchild = GetChildrenRecursive(child.gameObject);
output.AddRange(childsOfchild);
}
return output;
}
private static IEnumerable<GameObject> GetChildrenRecursive(IEnumerable<GameObject> rootObjects)
{
var output = new List<GameObject>();
foreach (var root in rootObjects)
{
output.AddRange(GetChildrenRecursive(root));
}
// remove duplicates
return output.Distinct().ToList();
}
and using
var selected = GetChildrenRecursive(Selection.gameObjects);
Another Update
Since this might not be very efficient you probably should move it to Searching and than only refresh the selected value also in OnSelectionChange like
public class SearchableWindow : EditorWindow
{
private string searchString = "";
private List<GameObject> selected;
[MenuItem("Tools/Searching")]
private static void Searching()
{
const int width = 340;
const int height = 420;
var x = (Screen.currentResolution.width - width) / 2;
var y = (Screen.currentResolution.height - height) / 2;
var window = GetWindow<SearchableWindow>();
window.position = new Rect(x, y, width, height);
window.selected = GetChildrenRecursive(Selection.gameObjects).ToList();
}
private void OnSelectionChange()
{
selected = GetChildrenRecursive(Selection.gameObjects).ToList();
Repaint();
}
private void OnGUI()
{
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.FlexibleSpace();
searchString = EditorGUILayout.TextField(searchString, EditorStyles.toolbarTextField);
EditorGUILayout.EndHorizontal();
// only as fallback
if (selected == null)
{
selected = GetChildrenRecursive(Selection.gameObjects).ToList();
}
// Do comparison here. For example
foreach (var selectedObject in selected)
{
if (selectedObject.name.Contains(searchString))
{
if (GUILayout.Button(selectedObject.name, EditorStyles.label))
{
EditorGUIUtility.PingObject(selectedObject);
}
}
}
}
private static IEnumerable<GameObject> GetChildrenRecursive(GameObject root)
{
var output = new List<GameObject>();
//add the root object itself
output.Add(root);
// iterate over direct children
foreach (Transform child in root.transform)
{
// add the children themselves
output.Add(child.gameObject);
var childsOfchild = GetChildrenRecursive(child.gameObject);
output.AddRange(childsOfchild);
}
return output;
}
private static IEnumerable<GameObject> GetChildrenRecursive(IEnumerable<GameObject> rootObjects)
{
var output = new List<GameObject>();
foreach (var root in rootObjects)
{
output.AddRange(GetChildrenRecursive(root));
}
// remove any duplicates that would e.g. appear if you select a parent and its child
return output.Distinct();
}
}
I am currently making a tile-based game in c#, but every time I draw the tiles it uses a lot of CPU, and as the tiles get bigger(if i make the game full screen) it consumes even more.
This is my Tile class:
public class Tiles
{
//PRIVATE :
//variabiles
private int XPosition, YPosition;
private Image Texture;
private bool Colidable;
private int SizeW = 32;
private int SizeH = 32;
private Resizer resizer = new Resizer();
//methods
//PUBLIC :
//variabiles
//methods
//CONSTRUCTOR
public Tiles(int _x,int _y,Image _i, int _sW = 32, int _sH = 32, bool _c = false)
{
XPosition = _x;//set position X
YPosition = _y;//set position Y
SizeW = _sW;
SizeH = _sH;
Texture = resizer.ResizeImage(_i, SizeW, SizeH) ;// set texture
Colidable = _c;//set if the tile is colidable,default : false
resizer = null;
}
//DRAW METHOD
//gets graphics object to draw on, adn draws at the position of the tile
public void Draw(Graphics _g)
{
_g.DrawImage(this.Texture, this.XPosition, this.YPosition);
}
//GET PRIVATE MEBERS
//returns if the tile is colidable
public bool getColidable()
{
return this.Colidable;
}
}
and this is how I draw the tiles:
private void DrawMap(Graphics _g)
{
//CALLS THE DRAW METHOD OF EACH TILE
for (int i = 0; i < MAP_WIDTH; i++)
{
for (int j = 0; j < MAP_HEIGHT; j++)
{
Tile[i, j].Draw(_g);
}
}
}
bool TilesUpdate = false;
private void _Window_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.Black);
if (isGameRunning)
{
DrawMap(e.Graphics);
}
else
{
FullRezolutionBtn.Draw(e.Graphics);
BigRezolutionBtn.Draw(e.Graphics);
NormalRezolutionBtn.Draw(e.Graphics);
}
}
private void Update_Tick(object sender, EventArgs e)
{
Invalidate();
}
I want to mention that the map is 20 x 20 tiles and it's cosuming around 50% of the cpu when it's fullscreen.
As I mentioned in the comments, the direction should be to do less painting. One way is to invalidate and paint portions of the drawing canvas only when something related to that portion changes. Windows itself does such optimization for controls/windows.
Here is an example. Look how Gadget class invalidates it's rectangle when some property changes. Then during the paint, only rectangles that intersect with e.ClipRectange are drawn. This highly reduces the number of the drawing operations.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Samples
{
class Gadget
{
public readonly Control Canvas;
public Gadget(Control canvas) { Canvas = canvas; }
private Rectangle bounds;
public Rectangle Bounds
{
get { return bounds; }
set
{
if (bounds == value) return;
// NOTE: Invalidate both old and new rectangle
Invalidate();
bounds = value;
Invalidate();
}
}
private Color color;
public Color Color
{
get { return color; }
set
{
if (color == value) return;
color = value;
Invalidate();
}
}
public void Invalidate()
{
Canvas.Invalidate(bounds);
}
public void Draw(Graphics g)
{
using (var brush = new SolidBrush(color))
g.FillRectangle(brush, bounds);
}
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form { WindowState = FormWindowState.Maximized };
int rows = 9, cols = 9;
var gadgets = new Gadget[rows, cols];
var rg = new Random();
Color[] colors = { Color.Yellow, Color.Blue, Color.Red, Color.Green, Color.Magenta };
int size = 64;
var canvas = form;
for (int r = 0, y = 8; r < rows; r++, y += size)
for (int c = 0, x = 8; c < cols; c++, x += size)
gadgets[r, c] = new Gadget(canvas) { Color = colors[rg.Next(colors.Length)], Bounds = new Rectangle(x, y, size, size) };
int paintCount = 0, drawCount = 0;
canvas.Paint += (sender, e) =>
{
paintCount++;
for (int r = 0; r < rows; r++)
{
for (int c = 0; c < cols; c++)
{
if (e.ClipRectangle.IntersectsWith(gadgets[r, c].Bounds))
{
gadgets[r, c].Draw(e.Graphics);
drawCount++;
}
}
}
form.Text = $"Paint:{paintCount} Draw:{drawCount} of {(long)paintCount * rows * cols}";
};
var timer = new Timer { Interval = 100 };
timer.Tick += (sender, e) =>
{
gadgets[rg.Next(rows), rg.Next(cols)].Color = colors[rg.Next(colors.Length)];
};
timer.Start();
Application.Run(form);
}
}
}
Not sure how your resizer class works. i think there is problem when you re-size every image every time.
Texture = resizer.ResizeImage(_i, SizeW, SizeH) ;// set texture
i would replace above line like this
Texture = _i;// set texture but do not resize image now
at the same time update the Draw Function of Tile like below.
public void Draw(Graphics _g)
{
//now specify the location and size of the image.
_g.DrawImage(Texture , new Rectangle(this.XPosition, this.YPosition, SizeW, SizeH));
}
hopefully it should improve the performance.
if it flicker then you can use Double Buffer
I've got this class fish and I just want to initialize my form with 8 types of fish positioning it in the form. When I run my code the eight fishes never appear. In some case one, two, or three. But when I debug the code the eight fishes appear.
I really don't know what is happening. Hope you could help me to find an answer.
class Fish : Aquaticanimal
{
private int x;
public int X
{
get { return x; }
set { x = value; }
}
private int y;
public int Y
{
get { return y; }
set { y = value; }
}
public Fish(Bitmap fish,Form form)
{
quantity++;
aquaticAnimal.BackColor = Color.Transparent;
aquaticAnimal.Image = fish;
aquaticAnimal.SizeMode = PictureBoxSizeMode.StretchImage;
positioning(form);
}
private void positioning(Form form)
{
Random rnd = new Random();
int FormWidth= Convert.ToInt32( form.Size.Width.ToString());
int FormHeight = Convert.ToInt32(form.Size.Height.ToString());
aquaticAnimal.Parent=form;
aquaticAnimal.Top = rnd.Next(200, FormHeight - 100);
aquaticAnimal.Left = rnd.Next(0, FormWidth - 100);
this.x = aquaticAnimal.Top;
this.y = aquaticAnimal.Left;
}
}
private void Form1_Load(object sender, EventArgs e)
{
Bitmap[] images = new Bitmap[8];
images[0] = Pecera.Properties.Resources.Nemo;
images[1] = Pecera.Properties.Resources.Pez_hembra;
images[2] = Pecera.Properties.Resources.Pez_macho;
images[3] = Pecera.Properties.Resources.tiburon_adulto;
images[4] = Pecera.Properties.Resources.Tiburon_bebe_hembra;
images[5] = Pecera.Properties.Resources.tiburon_macho;
images[6] = Pecera.Properties.Resources.Dorys;
images[7] = Pecera.Properties.Resources.tiburon_hembra;
Fish[] fish = new Fish[8];
for (int x = 0; x < 8; x++)
{
fish[x] = new Fish(images[x], this);
}
}
This is a case of creating a new instance of Random extremely quickly, resulting in not-so-random values. Your fishes are all overlapping each other, because the "random" values are the same.
You'll have to refactor your code in such a way that you only use a single instance of Random.
Here's my suggestion, changing as little of your code as possible:
class Fish : Aquaticanimal
{
...
...
private static Random rnd = new Random();
private void positioning(Form form)
{
int FormWidth= Convert.ToInt32( form.Size.Width.ToString());
int FormHeight = Convert.ToInt32(form.Size.Height.ToString());
aquaticAnimal.Parent=form;
aquaticAnimal.Top = rnd.Next(200, FormHeight - 100);
aquaticAnimal.Left = rnd.Next(0, FormWidth - 100);
this.x = aquaticAnimal.Top;
this.y = aquaticAnimal.Left;
}
}
FWIW, your Fish class really shouldn't be responsible for positioning itself on the Form.
I am creating a level designer for my game, with tiles you can draw on a User Control. To keep track of the tiles, I have made a int[]:
public int[,] Tiles = new int[35, 35];
Because the editing screen is 560px by 560px, 35 16x16 blocks. Tiles[x, y].Id will give the id of the tile.
Id will be the id of the tile. Whenever a tile is placed, It is recorded under a void:
public void Draw(int x, int y, int bid)
{
//draw code here
Program.form.Tiles[x, y].Id = bid;
}
And whenever you click, the void is called:
//In the MouseClick void:
Draw(e.X / 16, e.Y / 16, 1);
0 is an available block id, but using 1 for example.
Whenever I go to save it, (I'm saving to .txt files) It should write down the id of tile:
for (int x = 0; x < 35; x++)
{
for (int y = 0; y < 35; y++)
{
//Write code here
MessageBox.Show(Tiles[x, y].Id.ToString());
}
}
I just have it display the Id in a message box to see, and it always gives out '0', even when you edit on the certain x, y tile with a blockid that is definatley not 0.
Does anyone know why this is?
Thanks.
By the way, in the 'Program' class, above the STAThread thing, i put:
public Form1 form;
and then:
form = new Form1();
Application.Run(form);
This is because Tile is a struct - a value type. Therefore,
Program.form.Tiles[x, y]
gives you a copy. You assign Id of that copy, while the original Tile inside the array remains unchanged. This example illustrates why one should be extremely careful when dealing with value types inside arrays or collections.
You can fix this problem by changing the Tile to class. Note that unlike arrays of value types (structs) that always have a default value, elements of an array of reference objects (classes) need to be initialized manually to avoid the "Object reference not set to an instance of an object." errors:
for (int x = 0; x < 35; x++) {
for (int y = 0; y < 35; y++) {
Tiles[x, y] = new Tile();
}
}
In your code you are re-creating your form which re-creates your Tiles object. Do not re-create your form. Use it once:
public void Draw(int x, int y, int bid)
{
//If they are in the same form then Program.form is not needed:
Tiles[x, y].Id = bid;
}
Or reach your object like this:
Form1 frm = (Form1)Application.OpenForms[0];
frm.Tiles[x, y].Id = bid;
This should work pretty good, except for the horrible flickering. If I where designing this I would use a PictureBox and hook on its Paint() event.
public partial class Form1 : Form
{
Random rnd=new Random();
int[,] id;
public Form1()
{
InitializeComponent();
}
void Place(int x, int y, int bid)
{
int i=x/16, j=y/16;
id[i, j]=bid;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
id=new int[36, 36];
}
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
int bid=rnd.Next(KnownColor.White-KnownColor.AliceBlue);
Place(e.X, e.Y, bid);
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
for(int i=0; i<36; i++)
{
for(int j=0; j<36; j++)
{
int x=16*i, y=16*j;
Color color=Color.FromKnownColor(KnownColor.AliceBlue+id[i, j]);
using(Brush brush = new SolidBrush(color))
{
e.Graphics.FillRectangle(brush, x, y, 16, 16);
}
e.Graphics.DrawRectangle(Pens.Black, x, y, 16, 16);
}
}
}
public string SaveToFile()
{
string[] cols=new string[36];
for(int i=0; i<16; i++)
{
string[] rows=new string[36];
for(int j=0; j<36; j++)
{
rows[j]=id[i, j].ToString();
}
cols[i]=string.Join(",", rows);
}
return string.Join(";", cols);
}
public void ReadFromFile(string ids)
{
string[] cols=ids.Split(';');
for(int i=0; i<cols.Length; i++)
{
string[] rows=cols[i].Split(',');
for(int j=0; j<rows.Length; j++)
{
int temp_id=0;
int.TryParse(rows[j], out temp_id);
id[i, j]=temp_id;
}
}
}
}
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.