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);
            }
        }
        private double CalculateFitness(SettlementUpdate model)
        {
            double fitness = 0;

            switch (model.UpdateType)
            {
            case UpdateType.NewRoads:
                foreach (var road in model.NewRoads)
                {
                    fitness += 1;
                    var roads = new List <IRoad>(Settlement.Roads)
                    {
                        road
                    };
                    road.Buildings.ForEach(b => b.SetFitness(new BuildingRule()
                    {
                        Fields           = Settlement.Fields,
                        Roads            = roads,
                        BuildingRoad     = road,
                        SettlementCenter = Settlement.SettlementCenter
                    }));
                    fitness += road.Buildings.Sum(b => b.Fitness.Value);
                }
                break;

            case UpdateType.NewBuildings:
                foreach (var building in model.NewBuildings)
                {
                    var roads = new List <IRoad>(Settlement.Roads);
                    building.SetFitness(new BuildingRule()
                    {
                        Fields           = Settlement.Fields,
                        Roads            = roads,
                        BuildingRoad     = building.Road,
                        SettlementCenter = Settlement.SettlementCenter
                    });
                    fitness += building.Fitness.Value;
                }
                break;

            case UpdateType.NewTypes:
                foreach (var(oldBuilding, newBuilding) in model.UpdatedBuildings)
                {
                    var roads = new List <IRoad>(Settlement.Roads);
                    oldBuilding.SetFitness(new BuildingRule()
                    {
                        Fields           = Settlement.Fields,
                        Roads            = roads,
                        BuildingRoad     = oldBuilding.Road,
                        SettlementCenter = Settlement.SettlementCenter
                    });
                    newBuilding.SetFitness(new BuildingRule()
                    {
                        Fields           = Settlement.Fields,
                        Roads            = roads,
                        BuildingRoad     = newBuilding.Road,
                        SettlementCenter = Settlement.SettlementCenter
                    });
                    fitness += newBuilding.Fitness.Value - oldBuilding.Fitness.Value;
                }

                break;
            }

            return(fitness);
        }