GameOfLife/ViewModel/PetriDish.cs (149 lines of code) (raw):
using System;
namespace GameOfLife.ViewModel
{
public class PetriDish : IDisposable
{
private readonly Cell[,] currentCells;
private Cell[,] nextGenerationCells;
private readonly int height;
private readonly int width;
private readonly ITimer timer;
public PetriDish(int width, int height, ITimer timer)
{
this.width = width;
this.height = height;
this.timer = timer;
timer.Tick += UpdateCellsState;
currentCells = new Cell[width, height];
nextGenerationCells = new Cell[width, height];
for (var i = 0; i < width; i++)
{
for (var j = 0; j < height; j++)
{
currentCells[i, j] = new Cell(0, false);
nextGenerationCells[i, j] = new Cell(0, false);
}
}
Clear();
}
public Cell[,] Cells
{
get { return currentCells; }
}
public void Clear()
{
for (var i = 0; i < width; i++)
{
for (var j = 0; j < height; j++)
{
currentCells[i, j].IsAlive = false;
currentCells[i, j].Age = 0;
}
}
RaiseUpdated();
}
public void GenerateInitialState()
{
var random = new Random((int) (DateTime.UtcNow - DateTime.Today).TotalMilliseconds);
for (var i = 0; i < width; i++)
{
for (var j = 0; j < height; j++)
{
currentCells[i, j].IsAlive = random.NextDouble() > 0.8;
}
}
RaiseUpdated();
}
private void UpdateCellsState()
{
nextGenerationCells = new Cell[width, height]; // comment it to fix PetriDishTest.DontRecreateArrays
for (var i = 0; i < width; i++)
{
for (var j = 0; j < height; j++)
{
nextGenerationCells[i, j] = GetNextGenerationCellUnoptimized(i, j);
// nextGenerationCells[i, j] = GetNextGenerationCell(i, j); // change to fix PetriDishTest.AlgorithmTraffic and PetriDishTest.WholeRunTraffic
}
}
for (var i = 0; i < width; i++)
{
for (var j = 0; j < height; j++)
{
currentCells[i, j].IsAlive = nextGenerationCells[i, j].IsAlive;
currentCells[i, j].Age = nextGenerationCells[i, j].Age;
}
}
RaiseUpdated();
}
public void PerformOneStep()
{
UpdateCellsState();
}
public event EventHandler Updated;
private Cell GetNextGenerationCellUnoptimized(int i, int j) // UNOPTIMIZED
{
var cell = currentCells[i, j];
var isAlive = cell.IsAlive;
var neighborsCount = CountNeighbors(i, j);
if (isAlive && neighborsCount < 2)
return new Cell(0, false);
if (isAlive && (neighborsCount == 2 || neighborsCount == 3))
return new Cell(cell.Age + 1, true);
if (isAlive && neighborsCount > 3)
return new Cell(0, false);
if (!isAlive && neighborsCount == 3)
return new Cell(0, true);
return new Cell(0, false);
}
private Cell GetNextGenerationCell(int i, int j) // OPTIMIZED
{
var currentCell = currentCells[i, j];
var isAlive = currentCell.IsAlive;
var neighborsCount = CountNeighbors(i, j);
var cell = nextGenerationCells[i, j];
if (!isAlive)
{
if (neighborsCount == 3)
{
cell.IsAlive = true;
cell.Age = 0;
}
}
else if (neighborsCount == 2 || neighborsCount == 3)
{
cell.IsAlive = true;
cell.Age = currentCell.Age + 1;
}
else
{
cell.IsAlive = false;
cell.Age = 0;
}
return cell;
}
private int CountNeighbors(int i, int j)
{
var count = 0;
if (i != width - 1 && currentCells[i + 1, j].IsAlive) count++;
if (i != width - 1 && j != height - 1 && currentCells[i + 1, j + 1].IsAlive) count++;
if (j != height - 1 && currentCells[i, j + 1].IsAlive) count++;
if (i != 0 && j != height - 1 && currentCells[i - 1, j + 1].IsAlive) count++;
if (i != 0 && currentCells[i - 1, j].IsAlive) count++;
if (i != 0 && j != 0 && currentCells[i - 1, j - 1].IsAlive) count++;
if (j != 0 && currentCells[i, j - 1].IsAlive) count++;
if (i != width - 1 && j != 0 && currentCells[i + 1, j - 1].IsAlive) count++;
return count;
}
private void RaiseUpdated()
{
var handler = Updated;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
public void Dispose()
{
timer.Tick -= UpdateCellsState;
}
}
}