|
//
// PetriDish.cs
//
// -------------------------------------------------------------------
// History:
// 2008-03-02 Kwak Original File
// -------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
namespace GameOfLife.Core
{
/// <summary>
/// A container for cells
/// </summary>
public class PetriDish
{
#region Constants
private const int DefaultAgeLimit = 40;
private const int DefaultInitialChanceOfLife = 3; // 1 in 3
#endregion
#region Fields
private List<Cell> _cells; // used for queries
#endregion
#region Public Properties
/// <summary>
/// Gets or sets the age limit.
/// </summary>
/// <value>The age limit.</value>
/// <remarks>No cell is allowed to be over this age</remarks>
public int AgeLimit
{
get;
set;
}
/// <summary>
/// Gets the height.
/// </summary>
/// <value>The height.</value>
public int Height
{
get;
private set;
}
/// <summary>
/// Gets the number of living cells.
/// </summary>
/// <value>The number of living cells.</value>
public int NumberOfLivingCells
{
get
{
var numberOfLivingCells =
from c in _cells
where c.Age > 0
select c;
return numberOfLivingCells.Count();
}
}
/// <summary>
/// Gets the width.
/// </summary>
/// <value>The width.</value>
public int Width
{
get;
private set;
}
#endregion
#region Indexers
public Cell this[int row, int col]
{
get
{
Cell result = null;
if (row >= 0 && col >= 0)
{
if (row < Height && col < Width)
{
int position = row * Width + col;
result = _cells.ElementAtOrDefault(position);
}
}
return result;
}
}
#endregion
#region Initialization
/// <summary>
/// Initializes a new instance of the <see cref="Grid"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
public PetriDish(int width, int height)
{
Width = width;
Height = height;
AgeLimit = DefaultAgeLimit;
_cells = new List<Cell>(Height * Width);
Enumerable.Range(0, Height * Width).Each(InitializeCells);
_cells.Each(PopulateNeighbors);
}
/// <summary>
/// Initializes the cells.
/// </summary>
/// <param name="index">The index.</param>
private void InitializeCells(int index)
{
Cell c = new Cell(index);
_cells.Add(c);
}
private void PopulateNeighbors(Cell c)
{
int row = c.Position / Width;
int col = c.Position % Width;
List<Cell> neighbors = new List<Cell>
{
this[row - 1, col - 1],
this[row - 1, col],
this[row - 1, col + 1],
this[row, col -1],
// this[row, col], -- hey that's the Cell c!
this[row, col + 1],
this[row + 1, col - 1],
this[row + 1, col],
this[row+1, col + 1]
};
c.Neighbors = neighbors.FindAll(e => e != null);
}
#endregion // initialization
#region Public Methods
/// <summary>
/// Calculates the next generation on the grid
/// </summary>
public void NextGeneration()
{
// calculate what the next generation will look like
ApplyRulesToLivingCells();
ApplyRulesToNonLivingCells();
// make it so
UpdateCells();
}
/// <summary>
/// Resets the current cell grid using the default random seed.
/// </summary>
public void Reset()
{
Reset(DefaultInitialChanceOfLife);
}
/// <summary>
/// Resets the current cell grid using the specified random seed.
/// </summary>
/// <param name="chance">The chance a cell will be alive in the first generation</param>
public void Reset(int chance)
{
// Kill them all
_cells.Each(c => c.TakeLife());
// Randomly pick a new bunch
Random rnd = new Random();
var pregnantCells =
from c in _cells
where rnd.Next(0, chance) == 0
select c;
pregnantCells.Each(c => c.GiveLife());
}
#endregion // Public Methods
#region The Rules of Life
/// <summary>
/// Applies the rules to living cells.
/// </summary>
private void ApplyRulesToLivingCells()
{
// a living cell can either die or surive
// a non-living cell can only be born
// spawn rule: a cell dying of old age spawns.
var livingCells =
from c in _cells
where c.Age > 0
select c;
var cellsToKill =
from c in livingCells
where
c.NumberOfLivingNeighbors <= 1 || // loneliness
c.NumberOfLivingNeighbors >= 4 // over population
select c;
var cellsToKeepAlive =
from c in livingCells
where
c.NumberOfLivingNeighbors == 2 ||
c.NumberOfLivingNeighbors == 3
select c;
var dyingOfOldAge =
from c in cellsToKeepAlive
where
c.Age >= AgeLimit
select c;
// remove the old-age cells
cellsToKeepAlive = cellsToKeepAlive.Except(dyingOfOldAge);
// old age cells spawn by making
// any non-living neighbors alive
var spawnedCells =
from old in dyingOfOldAge
from n in old.Neighbors
where
n.Age == 0
select n;
cellsToKill.Each(c => c.LivesInNextGeneration = false);
dyingOfOldAge.Each(c => c.LivesInNextGeneration = false);
cellsToKeepAlive.Each(c => c.LivesInNextGeneration = true);
spawnedCells.Each(c => c.LivesInNextGeneration = true);
}
/// <summary>
/// Applies the rules to non living cells.
/// </summary>
private void ApplyRulesToNonLivingCells()
{
// a non-living cell can only be born
// a living cell can either die or surive
var notLivingCells =
from c in _cells
where c.Age == 0
select c;
var pregnantCells =
from c in notLivingCells
where c.NumberOfLivingNeighbors == 3
select c;
pregnantCells.Each(c => c.LivesInNextGeneration = true);
}
/// <summary>
/// Update cells effects the age of the cells in the dish
/// </summary>
private void UpdateCells()
{
// giveth life
var livingCells =
from c in _cells
where c.LivesInNextGeneration
select c;
livingCells.Each(c => c.GiveLife());
// taketh it away
var dyingCells =
from c in _cells
where !c.LivesInNextGeneration
select c;
dyingCells.Each(c => c.TakeLife());
}
#endregion // The Rules of Life
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
Jeff Kwak (a.k.a., FutureTurnip) is an open minded software developer/architect on top of the .net stack. He's coded in everything from 8-bit assembly language to C#. Currently, he's into big architecture, organizing software teams/process, emerging web technologies (like Silverlight and MS MVC), languages, ... all things software and related to software.