private UpdateType GetUpdateType()
        {
            var    config           = ConfigurationManager.SettlementConfiguration[CurrentEpoch];
            double probNewRoad      = config.ProbNewRoad;
            double probNewBuildings = config.ProbNewBuildings;

            if (EpochSpecific.IncreaseProbabilityOfAddingBuildings(Settlement, CurrentEpoch))
            {
                probNewRoad      = 0.2;
                probNewBuildings = 0.5;
            }

            UpdateType updateType;

            switch (RandomProvider.NextDouble())
            {
            case double d when d < probNewRoad:
            {
                updateType = UpdateType.NewRoads;
                break;
            }

            case double d when d < probNewRoad + probNewBuildings:
            {
                updateType = UpdateType.NewBuildings;
                break;
            }

            default:
                updateType = UpdateType.NewTypes;
                break;
            }

            return(updateType);
        }
        public SettlementUpdate CreateNewSettlementUpdate(
            UpdateType updateType,
            Epoch epoch,
            int iteration,
            int maxIterations)
        {
            var roadTypeSetUp = new RoadTypeSetUp()
            {
                Epoch            = epoch,
                SettlementCenter = SettlementCenter,
                AvgDistanceToSettlementCenter =
                    (int)Roads.Average(r => r.Center.DistanceTo(SettlementCenter))
            };
            SettlementUpdate settlementUpdate = new SettlementUpdate(updateType);

            switch (updateType)
            {
            case UpdateType.NewRoads:
            {
                var genes = this.Roads.ToList();

                if (RandomProvider.NextDouble() < 0.5)         //in order to make it more probable for roads closer to center to be selected
                {
                    var numberOfGenesToInclude = (int)(0.2 * genes.Count) <= 1 ? 1 : (int)(0.2 * genes.Count);
                    genes = genes.OrderBy(g => g.Center.DistanceTo(this.SettlementCenter))
                            .Take(2 * numberOfGenesToInclude)
                            .ToList();
                }
                var roadToAttach = genes[RandomProvider.Next(genes.Count)];
                var road         = this.CreateNewRoad(roadToAttach);
                if (!CanAddRoad(road))
                {
                    return(settlementUpdate);
                }

                var possiblePlaces =
                    road.GetPossibleBuildingPositions(new PossibleBuildingPositions(this.Roads, Fields));

                var buildingsToAdd = RandomProvider.Next(1,
                                                         possiblePlaces.Count > MaxBuildingsToAddPerIteration / 2
                                ? MaxBuildingsToAddPerIteration / 2
                                : possiblePlaces.Count);

                for (int i = 0; i < buildingsToAdd; i++)
                {
                    var building = Building.GetRandom(epoch);
                    building.Position = possiblePlaces[RandomProvider.Next(possiblePlaces.Count)];
                    building.Road     = road;
                    building.Material = EpochSpecific.GetMaterialForBuilding(epoch);
                    road.AddBuilding(building);
                }

                road.SetUpRoadType(roadTypeSetUp);
                settlementUpdate.NewRoads.Add(road);
                return(settlementUpdate);
            }

            case UpdateType.NewBuildings:
            {
                var roadWithoutBuildings = this.Roads.FirstOrDefault(g => !g.Buildings.Any());

                var roadsToAttachCount = this.Roads.Count * 0.3 < 10 ? 10 : (int)(this.Roads.Count * 0.3);
                var roadsToAttach      = this.Roads
                                         .OrderByDescending(g => 2 * g.Length - g.Buildings.Count)
                                         .Take(roadsToAttachCount)
                                         .ToArray();

                var roadToAttach = roadWithoutBuildings ?? roadsToAttach[RandomProvider.Next(roadsToAttach.Count())];
                var copy         = roadToAttach.Copy();

                var possiblePlaces =
                    copy.GetPossibleBuildingPositions(new PossibleBuildingPositions(this.Roads, Fields));
                if (!possiblePlaces.Any())
                {
                    return(settlementUpdate);
                }

                var buildingsToAdd = RandomProvider.Next(1,
                                                         possiblePlaces.Count > MaxBuildingsToAddPerIteration
                                ? MaxBuildingsToAddPerIteration
                                : possiblePlaces.Count);

                double coef = (3 * iteration / (maxIterations * 0.1));
                buildingsToAdd = (int)(coef * buildingsToAdd);

                for (int i = 0; i < buildingsToAdd; i++)
                {
                    var building = Building.GetRandom(epoch);
                    building.Position = possiblePlaces[RandomProvider.Next(possiblePlaces.Count)];
                    building.Road     = roadToAttach;
                    building.Material = EpochSpecific.GetMaterialForBuilding(epoch);
                    copy.AddBuilding(building);
                    settlementUpdate.NewBuildings.Add(building);
                }
                return(settlementUpdate);
            }

            default:
                for (int i = 0; i < BuildingsPerUpdate; i++)
                {
                    var roadsWithBuildings = this.Roads.Where(g => g.Buildings.Any()).ToList();
                    var road     = roadsWithBuildings[RandomProvider.Next(roadsWithBuildings.Count)];
                    var building = road.Buildings[RandomProvider.Next(road.Buildings.Count)];

                    if (!(building is Residence) && Math.Abs(building.Fitness.Value) > 0.1)
                    {
                        continue;                                                                        //don't update rare buildings with positive fitness
                    }
                    var newBuilding = Building.GetRandom(epoch);
                    newBuilding.Position  = building.Position;
                    newBuilding.Direction = building.Direction;
                    newBuilding.Road      = building.Road;
                    newBuilding.Material  = EpochSpecific.GetMaterialForBuilding(epoch);

                    if (building.GetType() != newBuilding.GetType() || building.Material != newBuilding.Material)
                    {
                        settlementUpdate.UpdatedBuildings.Add((building, newBuilding));
                    }
                }

                for (int i = 0; i < RoadsPerUpdate; i++)
                {
                    var unpavedRoads = this.Roads.Where(g => g.Type == RoadType.Unpaved).ToList();
                    if (!unpavedRoads.Any())
                    {
                        break;
                    }
                    var oldRoad = unpavedRoads[RandomProvider.Next(unpavedRoads.Count)];
                    var newRoad = oldRoad.Copy();
                    newRoad.SetUpRoadType(roadTypeSetUp);
                    if (!oldRoad.Type.Equals(newRoad.Type))
                    {
                        settlementUpdate.UpdatedRoads.Add((oldRoad, newRoad));
                    }
                }
                return(settlementUpdate);
            }
        }
        public void NewIteration()
        {
            var updateType = GetUpdateType();

            var structures = Enumerable.Range(1, 100).ToList()
                             .Select(s => Settlement.CreateNewSettlementUpdate(
                                         updateType, CurrentEpoch, Iteration, 1000))
                             .ToList();

            var settlementUpdate = GetBestStructures(structures);

            var roadTypeSetUp = new RoadTypeSetUp()
            {
                Epoch            = CurrentEpoch,
                SettlementCenter = Settlement.SettlementCenter,
                AvgDistanceToSettlementCenter =
                    (int)Settlement.Roads.Average(r => r.Center.DistanceTo(Settlement.SettlementCenter))
            };

            LastSettlementUpdate = settlementUpdate;

            switch (updateType)
            {
            case UpdateType.NewRoads:
            {
                settlementUpdate.NewRoads
                .ForEach(r =>
                    {
                        r.SetUpRoadType(roadTypeSetUp);
                        Settlement.AddRoad(r);
                    });
                break;
            }

            case UpdateType.NewBuildings:
                settlementUpdate.NewBuildings
                .ForEach(b => { Settlement.AddBuildingToRoad(b.Road, b); });
                break;

            case UpdateType.NewTypes:
                settlementUpdate.UpdatedBuildings.ForEach(update =>
                {
                    Settlement.RemoveBuildingFromRoad(update.oldBuilding.Road, update.oldBuilding);
                    Settlement.AddBuildingToRoad(update.newBuilding.Road, update.newBuilding);
                });
                settlementUpdate.UpdatedRoads.ForEach(update =>
                {
                    update.oldRoad.SetRoadType(update.newRoad.Type);     //for only types
                });
                break;
            }

            this.Settlement.Buildings.ForEach(b =>
            {
                b.Age++;
                if (b.Age % 100 == 0)
                {
                    var old    = (b as ICopyable <IBuilding>).Copy();
                    b.Material = EpochSpecific.GetMaterialForBuilding(CurrentEpoch);
                    if (old.Material != b.Material)
                    {
                        settlementUpdate.UpdatedBuildings.Add((old, b));
                    }
                }
            });

            this.Settlement.Roads.ForEach(r =>
            {
                r.Age++;
                if (r.Age % 100 == 0)
                {
                    var old = (r as ICopyable <IRoad>).Copy();
                    r.SetUpRoadType(roadTypeSetUp);
                    if (old.Type != r.Type)
                    {
                        settlementUpdate.UpdatedRoads.Add((old, r));
                    }
                }
            });

            if (RandomProvider.NextDouble() < FloodMutationProbability)
            {
                settlementUpdate.FloodMutationResult = Settlement.InvokeFloodMutation();
            }

            if (RandomProvider.NextDouble() < EarthquakeMutationProbability)
            {
                settlementUpdate.EarthquakeMutationResult = Settlement.InvokeEarthquakeMutation();
            }

            if (RandomProvider.NextDouble() < FireMutationProbability)
            {
                settlementUpdate.FireMutationResult = Settlement.InvokeFireMutation();
            }

            if (EpochSpecific.CanEnterNextEpoch(Settlement, CurrentEpoch))
            {
                switch (CurrentEpoch)
                {
                case Epoch.First:
                    CurrentEpoch = Epoch.Second;
                    break;

                case Epoch.Second:
                    CurrentEpoch = Epoch.Third;
                    break;

                case Epoch.Third:
                    //TODO
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }

            if (Iteration % 100 == 0)
            {
                Settlement.UpdateSettlementCenter();
            }

            Iteration++;
        }