public IEnumerable <EnvironmentalWorld> Ticks(EnvironmentalWorld world)
        {
            yield return(world);

            var nextGrid = Task.Run(() => world.Data.Grid.Cells.AsParallel().Select((row, outerInd) =>
                                                                                    row.AsParallel().Select((_, innerInd) =>
                                                                                                            world.CellCalculator.CalculateCell(
                                                                                                                world.Data.Grid.Cells[outerInd][innerInd],
                                                                                                                world.NeighbourFinder.FindNeighbours(world.Data.Grid, outerInd, innerInd),
                                                                                                                world.Data)).ToArray()).ToArray());

            var herbivoreDensity = Task.Run(() =>
                                            (double)world.Data.Grid.Cells.SelectMany(row => row).Where(c => c.IsAlive).Count(c => c.Diet == DietaryRestriction.Herbivore)
                                            / (world.Data.Grid.Cells.Count * world.Data.Grid.Cells.Count));

            var nextSeason      = Task.Run(() => world.SeasonCalculator.GetSeasonOfNextTick(world.Data.Season));
            var nextTemperature = Task.Run(() => world.SeasonCalculator.CalculateTemperature(world.Data.Season));

            var data = new EnvironmentalWorldData
            {
                Generation       = world.Data.Generation + 1,
                Grid             = new EnvironmentalCellGrid(nextGrid.Result),
                HerbivoreDensity = new Density(herbivoreDensity.Result),
                Season           = nextSeason.Result,
                Temperature      = nextTemperature.Result
            };

            var nextWorld = new EnvironmentalWorldBuilder(world).With(w => w.Data, data).Create();

            foreach (var tick in Ticks(nextWorld))
            {
                yield return(tick);
            }
        }
        public void Ticks_OneAlive_100Generations_AllDead()
        {
            // arrange
            var worldGenerator = new EnvironmentalWorldGenerator();
            var cellGrid       = new EnvironmentalCellGrid
                                 (
                new[]
            {
                new[] { Dead(), Dead(), Dead() },
                new[] { Dead(), Alive(), Dead() },
                new[] { Dead(), Dead(), Dead() }
            }
                                 );
            var world = new EnvironmentalWorldBuilder().With(w => w.Data, new EnvironmentalWorldData {
                Grid = cellGrid
            }).Create();
            var expected = new EnvironmentalCellGrid(EnumerablePrelude.Repeat(() => EnumerablePrelude.Repeat(Dead, 3).ToArray(), 3).ToArray());

            // act
            var result = worldGenerator.Ticks(world).Skip(100).First().Data.Grid;

            // assert
            Assert.AreEqual(GridToString(expected), GridToString(result));
        }
        public void Ticks_WithOscillating_100Generations_IsSame()
        {
            // arrange
            var worldGenerator = new EnvironmentalWorldGenerator();
            var cellGrid       = new EnvironmentalCellGrid
                                 (
                new[]
            {
                new[] { Dead(), Dead(), Dead(), Dead() },
                new[] { Dead(), Alive(), Alive(), Dead() },
                new[] { Dead(), Alive(), Alive(), Dead() },
                new[] { Dead(), Dead(), Dead(), Dead() }
            }
                                 );
            var world = new EnvironmentalWorldBuilder().With(w => w.Data, new EnvironmentalWorldData {
                Grid = cellGrid
            }).Create();

            // act
            var result = worldGenerator.Ticks(world).Skip(100).First().Data.Grid;

            // assert
            Assert.AreEqual(GridToString(cellGrid), GridToString(result));
        }