using System; using System.Collections.Generic; using System.Linq; using GameOfLife.Entities; namespace GameOfLife { public abstract class LifeBase : ILife { public int Generation { get; protected set; } public int Population { get; protected set; } public abstract bool IsCellAlive(Cell cell); public virtual bool IsCellAlive(short x, short y) => IsCellAlive(new Cell(x,y)); public bool AllAlive(IEnumerable cells) => cells.All(IsCellAlive); public bool AllDead(IEnumerable cells) => cells.All(c => !IsCellAlive(c)); private IReadOnlyList> _neighborhoodField; public IReadOnlyList> NeighborhoodField => // cache values this expensive operation _neighborhoodField ?? (_neighborhoodField = Neighborhood.GetNeighborField(LivingCells).ToList()); private Pattern _livingCellPattern; public Pattern LivingCellPattern => _livingCellPattern ?? (_livingCellPattern = LivingCells.ToPattern()); public bool ToggleCell(Cell cell) { var alive = ToggleCell_Internal(cell); Population += alive ? 1 : -1; return alive; } protected abstract bool ToggleCell_Internal(Cell cell); public void IncrementGeneration() { _neighborhoodField = null; _livingCellPattern = null; Population = IncrementGeneration_Internal(); Generation++; } protected abstract int IncrementGeneration_Internal(); public abstract IEnumerable LivingCells { get; } public static readonly short[,] NeighborOffsets = { { -1, -1 }, { -1, 0 }, { -1, 1 }, { 0, -1 }, { 0, 1 }, { 1, -1 }, { 1, 0 }, { 1, 1 }, }; public byte GetNeighborCount(short x, short y) { byte numberOfNeighbors = 0; for (var i = 0; i < 8; i++) { numberOfNeighbors += IsCellAlive((short)(x + NeighborOffsets[i,0]), (short)(y + NeighborOffsets[i,1])) ? (byte)1 : (byte)0; } return numberOfNeighbors; } protected bool ShouldLive(short x, short y) { var numberOfNeighbors = GetNeighborCount(x, y); var isAlive = IsCellAlive(x, y); return LifeRule(isAlive, numberOfNeighbors); } protected bool LifeRule(bool isAlive, int neighborCount) { switch (isAlive) { case false when neighborCount == 3: case true when neighborCount == 2 || neighborCount == 3: return true; default: return false; } } } }