anchor.diff:  
   
   implementation
  -uses KM_HandSpectator, KM_ResWares, KM_Hand, KM_UnitsCollection;
  +uses KM_HandSpectator, KM_ResWares, KM_ResHouses, KM_Hand, KM_UnitsCollection;
  +
  +
  +
  +
  +{ TKMRunnerGA_Common }
  +procedure TKMRunnerGA_Common.SetUp;
  +var
  +  I,K: Integer;
  +  Pop: TGAPopulation;
  +begin
  +  inherited;
  +  fResults.ValueCount := 1;
  +  // Do something before simulation
  +  InitGAParameters();
  +  fResults.TimesCount := Ceil(10*60 * f_GA_SIMULATION_TIME_IN_MIN);
  +  OldPopulation := nil;
  +  NewPopulation := TGAPopulation.Create(f_GA_POPULATION_CNT, f_GA_GENE_CNT, True);
  +  fAlgorithm := TGAAlgorithm.Create;
  +  //fFitnessCalc := TGAFitnessCalc.Create;
  +  DefaultValues();
  +  if not PARALLEL_RUN then
  +  begin
  +    fLog := TKMLog.Create(ExeDir + '\Utils\Runner\LOG_GA.log');
  +    fLogPar := TKMLog.Create(ExeDir + '\Utils\Runner\LOG_GA_PAR.log');
  +  end
  +  else
  +  begin
  +
  +    fResults.TimesCount := Ceil(10*60 * SimSetup.SimTimeInMin);
  +    Pop := IOData.Population;
  +    f_GA_MAPS_CNT := IOData.MapCnt;
  +    f_GA_POPULATION_CNT := Pop.Count;
  +    f_GA_GENE_CNT := Pop.Individual[0].Count;
  +    if (Pop <> nil) then
  +    begin
  +      NewPopulation.Free;
  +      NewPopulation := TGAPopulation.Create(Pop.Count, Pop.Individual[0].Count, True);
  +      for I := 0 to NewPopulation.Count - 1 do
  +        for K := 0 to NewPopulation.Individual[I].Count - 1 do
  +          NewPopulation.Individual[I].Gene[K] := Pop.Individual[I].Gene[K];
  +    end;
  +
  +  end;
  +end;
  +
  +
  +procedure TKMRunnerGA_Common.TearDown;
  +var
  +  I: Integer;
  +begin
  +  if PARALLEL_RUN then
  +  begin
  +    for I := 0 to OldPopulation.Count - 1 do
  +      IOData.Population.Individual[I].Fitness := OldPopulation.Individual[I].Fitness;
  +  end
  +  else
  +  begin
  +    fLog.Free;
  +    fLogPar.Free;
  +  end;
  +  // Do something after simulation
  +  OldPopulation.Free;
  +  NewPopulation.Free;
  +  fAlgorithm.Free;
  +  //fFitnessCalc.Free;
  +  inherited;
  +end;
  +
  +
  +procedure TKMRunnerGA_Common.DefaultValues();
  +var
  +  I,K: Integer;
  +  Idv: TGAIndividual;
  +begin
  +  for I := 0 to NewPopulation.Count - 1 do
  +  begin
  +    Idv := NewPopulation[I];
  +    for K := 0 to Idv.Count - 1 do
  +      Idv.Gene[K] := Random;
  +  end;
  +end;
  +
  +
  +procedure TKMRunnerGA_Common.InitGAParameters();
  +begin
  +  f_GA_SIMULATION_TIME_IN_MIN    := 60;
  +  f_GA_POPULATION_CNT            := 40;
  +  f_GA_INDIVIDUALS_IN_TOURNAMENT := 3;
  +  f_GA_GENE_CNT                  := 5;
  +  f_GA_MAPS_CNT                  := 10;
  +  f_GA_START_MUTATION            := 0.1;
  +  f_GA_FINAL_MUTATION            := 0.05;
  +  f_GA_CROSSOVER_COEF            := 0.95;
  +end;
  +
  +
  +procedure TKMRunnerGA_Common.SimulateMap(aRun, aIdx, Seed: Integer; aSinglePLMapName: String; aSaveGame: Boolean = False);
  +begin
  +  gGameApp.NewSingleMap(ExtractFilePath(ParamStr(0)) + '..\..\Maps\' + aSinglePLMapName + '\' + aSinglePLMapName + '.dat', 'GA');
  +
  +  gMySpectator.Hand.FogOfWar.RevealEverything;
  +  gGameApp.Game.GamePlayInterface.Viewport.PanTo(KMPointF(0, 60), 0);
  +  gGameApp.Game.GamePlayInterface.Viewport.Zoom := 0.25;
  +  //SetKaMSeed(Max(1,Seed));
  +  try
  +    SimulateGame;
  +  except
  +    SimulateMap(aRun + 1, aIdx, Round(Random()*10000)+1, aSinglePLMapName);
  +  end;
  +  if aSaveGame then
  +    gGameApp.Game.Save('GA Test #' + IntToStr(aRun) + 'n' + IntToStr(aIdx), Now);
  +end;
  +
  +
  +// Small helping function
  +function TKMRunnerGA_Common.Incr(var Idx: Integer): Integer;
  +begin
  +  Result := Idx;
  +  Inc(Idx);
  +end;
  +
  +
  +function TKMRunnerGA_Common.CostFunction(): Single;
  +const
  +  PL = 1;
  +  HOUSE_WEIGHT = 1;
  +  WEAPONS_WEIGHT = 1;
  +  CITIZENS_LOST = 10;
  +  IRON_SOLDIER = 20;
  +  WOOD_SOLDIER = 10;
  +  MILITIA_SOLDIER = 3;
  +  COMPLETE_HOUSE = 10;
  +var
  +  I: Integer;
  +  IronArmy, WoodArmy, Militia, Output: Single;
  +begin
  +  with gHands[PL].Stats do
  +  begin
  +    Output := + GetHouseQty(htAny) * HOUSE_WEIGHT
  +              + GetWeaponsProduced * WEAPONS_WEIGHT
  +              - GetCitizensLost * CITIZENS_LOST;
  +    IronArmy := Min( GetWaresProduced(wt_MetalArmor),
  +                       GetWaresProduced(wt_Hallebard)
  +                     + GetWaresProduced(wt_Arbalet)
  +                     + Min(GetWaresProduced(wt_Sword), GetWaresProduced(wt_MetalShield))
  +                   );
  +    WoodArmy := Min( GetWaresProduced(wt_Armor),
  +                       GetWaresProduced(wt_Bow)
  +                     + GetWaresProduced(wt_Pike)
  +                     + Min(GetWaresProduced(wt_Shield), GetWaresProduced(wt_Axe))
  +                   );
  +    Militia := Min( Max(0,WoodArmy - GetWaresProduced(wt_Armor)), GetWaresProduced(wt_Axe));
  +    Output := Output
  +              + IronArmy * IRON_SOLDIER
  +              + WoodArmy * WOOD_SOLDIER
  +              + Militia * MILITIA_SOLDIER;
  +  end;
  +
  +  for I := 0 to gHands[PL].Houses.Count - 1 do
  +    Output := Output + Byte(gHands[PL].Houses[I].IsComplete) * COMPLETE_HOUSE;
  +
  +  Result := Output;
  +end;
  +
  +
  +procedure TKMRunnerGA_Common.Execute(aRun: Integer);
  +const
  +  MIN_SCORE = - 1000000;
  +var
  +  I, MapNum, Idx: Integer;
  +  Score, BestScore, MUTATION: Single;
  +  Idv: TGAIndividual;
  +begin
  +  // Run 1 generation of GA
  +  MUTATION := aRun / (fResults.ChartsCount * 1.0);
  +  MUTATION := Abs(f_GA_FINAL_MUTATION + (f_GA_START_MUTATION - f_GA_FINAL_MUTATION) * (1 - MUTATION));
  +  fAlgorithm.Mutation := MUTATION;
  +  fAlgorithm.CrossoverCoef := f_GA_CROSSOVER_COEF;
  +  fAlgorithm.IndividualsInTournament := f_GA_INDIVIDUALS_IN_TOURNAMENT; // This may be also changed during simulation
  +
  +  // Evolve population in next run (used because of parallel run)
  +  if (OldPopulation <> nil) then
  +  begin
  +    fAlgorithm.EvolvePopulation(OldPopulation, NewPopulation);
  +    OldPopulation.Free;
  +  end;
  +
  +  OldPopulation := NewPopulation;
  +  for MapNum := 1 to f_GA_MAPS_CNT do
  +  begin
  +    for I := 0 to f_GA_POPULATION_CNT - 1 do
  +    begin
  +      SetParameters(OldPopulation[I], False);
  +      SimulateMap(aRun, I, aRun, 'GA_S1_' + IntToStr(MapNum));// Name of maps is GA_1, GA_2 ...
  +      Score := CostFunction();
  +      OldPopulation[I].Fitness := OldPopulation[I].Fitness + Score;
  +    end;
  +  end;
  +  NewPopulation := TGAPopulation.Create(f_GA_POPULATION_CNT, f_GA_GENE_CNT, False);
  +
  +  // Save best score + parameters of best individual
  +  Idx := 0;
  +  Idv := OldPopulation.GetFittest(Idx);
  +  fResults.Value[aRun, 0] := Round(Idv.Fitness);
  +  if not PARALLEL_RUN then
  +    fLog.AddTime('GA: ' + IntToStr(aRun) + '. run. Best score: ' + FloatToStr(Idv.Fitness) + '. Index of the best individual: ' + IntToStr(Idx));
  +
  +  // Check history and find the most fitness individual
  +  BestScore := MIN_SCORE;
  +  for I := 0 to aRun - 1 do
  +    if (fResults.Value[I, 0] > BestScore) then
  +      BestScore := fResults.Value[I, 0];
  +  // If is the individual from the latest generation the best then log parameters
  +  if (BestScore < Idv.Fitness) then
  +    SetParameters(Idv, not PARALLEL_RUN);
  +
  +  // Stop simulation
  +  gGameApp.StopGame(gr_Silent);
  +end;
  +
  +
  +
  +{ TKMRunnerGA_TestManager }
  +procedure TKMRunnerGA_TestManager.Execute(aRun: Integer);
  +var
  +  I,K: Integer;
  +  Fitness: Single;
  +begin
  +  OldPopulation := NewPopulation;
  +  for I := 0 to NewPopulation.Count - 1 do
  +  begin
  +    Fitness := 0;
  +    for K := 0 to NewPopulation.Individual[I].Count - 1 do
  +      Fitness := Fitness + NewPopulation.Individual[I].Gene[K];
  +    NewPopulation.Individual[I].Fitness := Fitness;
  +  end;
  +  NewPopulation := nil;
  +
  +  //Sleep(1000); // Debug sleep
  +
  +  // Stop simulation
  +  gGameApp.StopGame(gr_Silent);
  +end;
  +
  +
  +
  +{ TKMRunnerGA_CityBuilder }
  +procedure TKMRunnerGA_CityBuilder.InitGAParameters();
  +begin
  +  f_GA_SIMULATION_TIME_IN_MIN    := 65;
  +  f_GA_POPULATION_CNT            := 40;
  +  f_GA_INDIVIDUALS_IN_TOURNAMENT := 3;
  +  f_GA_GENE_CNT                  := 13;
  +  f_GA_MAPS_CNT                  := 20;
  +  f_GA_START_MUTATION            := 0.2;
  +  f_GA_FINAL_MUTATION            := 0.01;
  +  f_GA_CROSSOVER_COEF            := 1;
  +end;
  +
  +procedure TKMRunnerGA_CityBuilder.SetParameters(aIdv: TGAIndividual; aLogIt: Boolean = False);
  +var
  +  I: Integer;
  +begin
  +  I := 0;
  +
  +  GA_BUILDER_BuildHouse_RoadMaxWork     := Max(1, aIdv.Gene[Incr(I)] * 15);
  +  GA_BUILDER_BuildHouse_FieldMaxWork    := Max(1, aIdv.Gene[Incr(I)] * 10);
  +  GA_BUILDER_BuildHouse_RTPMaxWork      := Max(1, aIdv.Gene[Incr(I)] * 20);
  +  GA_BUILDER_CreateShortcuts_MaxWork    := Max(1, aIdv.Gene[Incr(I)] * 15);
  +  GA_BUILDER_ChHTB_FractionCoef         := Max(1, aIdv.Gene[Incr(I)] * 80);
  +  GA_BUILDER_ChHTB_TrunkFactor          := Max(1, aIdv.Gene[Incr(I)] * 10);
  +  GA_BUILDER_ChHTB_TrunkBalance         := Max(1, aIdv.Gene[Incr(I)] * 15);
  +  GA_BUILDER_ChHTB_AllWorkerCoef        := Max(1, aIdv.Gene[Incr(I)] * 15);
  +  GA_BUILDER_ChHTB_FreeWorkerCoef       := Max(1, aIdv.Gene[Incr(I)] * 15);
  +  GA_BUILDER_TRUNK_SHORTAGE             := Max(1, aIdv.Gene[Incr(I)] * 20);
  +  GA_BUILDER_STONE_SHORTAGE             := Max(1, aIdv.Gene[Incr(I)] * 20);
  +  GA_BUILDER_WOOD_SHORTAGE              := Max(1, aIdv.Gene[Incr(I)] * 20);
  +  GA_BUILDER_GOLD_SHORTAGE              := Max(1, aIdv.Gene[Incr(I)] * 40);
  +
  +  if aLogIt then
  +  begin
  +    fLogPar.AddTime('GA_BUILDER_BuildHouse_RoadMaxWork     : Single = ' + FloatToStr( GA_BUILDER_BuildHouse_RoadMaxWork    ) + ';');
  +    fLogPar.AddTime('GA_BUILDER_BuildHouse_FieldMaxWork    : Single = ' + FloatToStr( GA_BUILDER_BuildHouse_FieldMaxWork   ) + ';');
  +    fLogPar.AddTime('GA_BUILDER_BuildHouse_RTPMaxWork      : Single = ' + FloatToStr( GA_BUILDER_BuildHouse_RTPMaxWork     ) + ';');
  +    fLogPar.AddTime('GA_BUILDER_CreateShortcuts_MaxWork    : Single = ' + FloatToStr( GA_BUILDER_CreateShortcuts_MaxWork   ) + ';');
  +    fLogPar.AddTime('GA_BUILDER_ChHTB_FractionCoef         : Single = ' + FloatToStr( GA_BUILDER_ChHTB_FractionCoef        ) + ';');
  +    fLogPar.AddTime('GA_BUILDER_ChHTB_TrunkFactor          : Single = ' + FloatToStr( GA_BUILDER_ChHTB_TrunkFactor         ) + ';');
  +    fLogPar.AddTime('GA_BUILDER_ChHTB_TrunkBalance         : Single = ' + FloatToStr( GA_BUILDER_ChHTB_TrunkBalance        ) + ';');
  +    fLogPar.AddTime('GA_BUILDER_ChHTB_AllWorkerCoef        : Single = ' + FloatToStr( GA_BUILDER_ChHTB_AllWorkerCoef       ) + ';');
  +    fLogPar.AddTime('GA_BUILDER_ChHTB_FreeWorkerCoef       : Single = ' + FloatToStr( GA_BUILDER_ChHTB_FreeWorkerCoef      ) + ';');
  +    fLogPar.AddTime('GA_BUILDER_TRUNK_SHORTAGE             : Single = ' + FloatToStr( GA_BUILDER_TRUNK_SHORTAGE            ) + ';');
  +    fLogPar.AddTime('GA_BUILDER_STONE_SHORTAGE             : Single = ' + FloatToStr( GA_BUILDER_STONE_SHORTAGE            ) + ';');
  +    fLogPar.AddTime('GA_BUILDER_WOOD_SHORTAGE              : Single = ' + FloatToStr( GA_BUILDER_WOOD_SHORTAGE             ) + ';');
  +    fLogPar.AddTime('GA_BUILDER_GOLD_SHORTAGE              : Single = ' + FloatToStr( GA_BUILDER_GOLD_SHORTAGE             ) + ';');
  +  end;
  +end;
  +
  +
  +
  +
  +{ TKMRunnerGA_CityPlannerForest }
  +procedure TKMRunnerGA_CityPlannerForest.InitGAParameters();
  +begin
  +  f_GA_SIMULATION_TIME_IN_MIN    := 60;
  +  f_GA_POPULATION_CNT            := 40;
  +  f_GA_INDIVIDUALS_IN_TOURNAMENT := 3;
  +  f_GA_GENE_CNT                  := 7;
  +  f_GA_MAPS_CNT                  := 10;
  +  f_GA_START_MUTATION            := 0.1;
  +  f_GA_FINAL_MUTATION            := 0.005;
  +  f_GA_CROSSOVER_COEF            := 0.95;
  +end;
  +
  +
  +procedure TKMRunnerGA_CityPlannerForest.SetParameters(aIdv: TGAIndividual; aLogIt: Boolean = False);
  +var
  +  I: Integer;
  +begin
  +  I := 0;
  +  // Please do NOT break this order and style use alt + shift + click if you want select / edit multiple colums at once!!!
  +  GA_PLANNER_PlaceWoodcutter_DistFromForest         := Max(1, aIdv.Gene[Incr(I)] * 100);
  +  GA_PLANNER_FindPlaceForWoodcutter_DistCrit        := Max(1, aIdv.Gene[Incr(I)] * 10);
  +  GA_PLANNER_FindPlaceForWoodcutter_TreeCnt         := Max(1, aIdv.Gene[Incr(I)] * 100);
  +  GA_PLANNER_FindPlaceForWoodcutter_Influence       := Max(1, aIdv.Gene[Incr(I)] * 15);
  +
  +  if aLogIt then
  +  begin
  +    fLogPar.AddTime('GA_PLANNER_PlaceWoodcutter_DistFromForest         : Single = ' + FloatToStr( GA_PLANNER_PlaceWoodcutter_DistFromForest         ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FindPlaceForWoodcutter_DistCrit        : Single = ' + FloatToStr( GA_PLANNER_FindPlaceForWoodcutter_DistCrit        ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FindPlaceForWoodcutter_TreeCnt         : Single = ' + FloatToStr( GA_PLANNER_FindPlaceForWoodcutter_TreeCnt         ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FindPlaceForWoodcutter_Influence       : Single = ' + FloatToStr( GA_PLANNER_FindPlaceForWoodcutter_Influence       ) + ';');
  +  end;
  +end;
  +
  +
  +{ TKMRunnerGA_CityRoadPlanner }
  +procedure TKMRunnerGA_CityRoadPlanner.InitGAParameters();
  +begin
  +  f_GA_SIMULATION_TIME_IN_MIN    := 70;
  +  f_GA_POPULATION_CNT            := 40;
  +  f_GA_INDIVIDUALS_IN_TOURNAMENT := 3;
  +  f_GA_GENE_CNT                  := 12;
  +  f_GA_MAPS_CNT                  := 9;
  +  f_GA_START_MUTATION            := 0.1;
  +  f_GA_FINAL_MUTATION            := 0.05;
  +  f_GA_CROSSOVER_COEF            := 0.95;
  +end;
  +
  +
  +procedure TKMRunnerGA_CityRoadPlanner.SetParameters(aIdv: TGAIndividual; aLogIt: Boolean = False);
  +var
  +  I: Integer;
  +begin
  +  I := 0;
  +  GA_PATHFINDING_BasePrice     := Round( Max(0, aIdv.Gene[Incr(I)] * 10) );
  +  GA_PATHFINDING_HouseOutside  := Round( Max(0, aIdv.Gene[Incr(I)] * 10) );
  +  GA_PATHFINDING_Field         := Round( Max(0, aIdv.Gene[Incr(I)] * 10) );
  +  GA_PATHFINDING_NoBuildArea   := Round( Max(0, aIdv.Gene[Incr(I)] * 10) );
  +  GA_PATHFINDING_Forest        := Round( Max(0, aIdv.Gene[Incr(I)] * 10) );
  +  GA_PATHFINDING_OtherCase     := Round( Max(0, aIdv.Gene[Incr(I)] * 10) );
  +
  +  GA_SHORTCUTS_BasePrice       := Round( Max(0, aIdv.Gene[Incr(I)] * 10) );
  +  GA_SHORTCUTS_HouseOutside    := Round( Max(0, aIdv.Gene[Incr(I)] * 10) );
  +  GA_SHORTCUTS_Field           := Round( Max(0, aIdv.Gene[Incr(I)] * 10) );
  +  GA_SHORTCUTS_NoBuildArea     := Round( Max(0, aIdv.Gene[Incr(I)] * 10) );
  +  GA_SHORTCUTS_Forest          := Round( Max(0, aIdv.Gene[Incr(I)] * 10) );
  +  GA_SHORTCUTS_OtherCase       := Round( Max(0, aIdv.Gene[Incr(I)] * 10) );
  +
  +  if aLogIt then
  +  begin
  +    fLogPar.AddTime('GA_PATHFINDING_BasePrice    : Word = ' + IntToStr( GA_PATHFINDING_BasePrice    ) + ';');
  +    fLogPar.AddTime('GA_PATHFINDING_HouseOutside : Word = ' + IntToStr( GA_PATHFINDING_HouseOutside ) + ';');
  +    fLogPar.AddTime('GA_PATHFINDING_Field        : Word = ' + IntToStr( GA_PATHFINDING_Field        ) + ';');
  +    fLogPar.AddTime('GA_PATHFINDING_NoBuildArea  : Word = ' + IntToStr( GA_PATHFINDING_NoBuildArea  ) + ';');
  +    fLogPar.AddTime('GA_PATHFINDING_Forest       : Word = ' + IntToStr( GA_PATHFINDING_Forest       ) + ';');
  +    fLogPar.AddTime('GA_PATHFINDING_OtherCase    : Word = ' + IntToStr( GA_PATHFINDING_OtherCase    ) + ';');
  +    fLogPar.AddTime('GA_SHORTCUTS_BasePrice      : Word = ' + IntToStr( GA_SHORTCUTS_BasePrice      ) + ';');
  +    fLogPar.AddTime('GA_SHORTCUTS_HouseOutside   : Word = ' + IntToStr( GA_SHORTCUTS_HouseOutside   ) + ';');
  +    fLogPar.AddTime('GA_SHORTCUTS_Field          : Word = ' + IntToStr( GA_SHORTCUTS_Field          ) + ';');
  +    fLogPar.AddTime('GA_SHORTCUTS_NoBuildArea    : Word = ' + IntToStr( GA_SHORTCUTS_NoBuildArea    ) + ';');
  +    fLogPar.AddTime('GA_SHORTCUTS_Forest         : Word = ' + IntToStr( GA_SHORTCUTS_Forest         ) + ';');
  +    fLogPar.AddTime('GA_SHORTCUTS_OtherCase      : Word = ' + IntToStr( GA_SHORTCUTS_OtherCase      ) + ';');
  +  end;
  +end;
  +
  +
  +{ TKMRunnerGA_CityPlanner }
  +procedure TKMRunnerGA_CityPlanner.InitGAParameters();
  +begin
  +  f_GA_SIMULATION_TIME_IN_MIN    := 60;
  +  f_GA_POPULATION_CNT            := 1;//50;
  +  f_GA_INDIVIDUALS_IN_TOURNAMENT := 4;
  +  f_GA_GENE_CNT                  := 19;
  +  f_GA_MAPS_CNT                  := 1;//9;
  +  f_GA_START_MUTATION            := 0.2;
  +  f_GA_FINAL_MUTATION            := 0.01;
  +  f_GA_CROSSOVER_COEF            := 1;
  +end;
  +
  +
  +procedure TKMRunnerGA_CityPlanner.SetParameters(aIdv: TGAIndividual; aLogIt: Boolean = False);
  +var
  +  I: Integer;
  +begin
  +  I := 0;
  +  //GA_PLANNER := True;
  +  // House build
  +  // Please do NOT break this order and style use alt + shift + click if you want select / edit multiple colums at once!!!
  +  GA_PLANNER_ObstaclesInHousePlan_Tree              := Max(1, aIdv.Gene[Incr(I)] * 100 + 75);
  +  GA_PLANNER_ObstaclesInHousePlan_Road              := Max(1, aIdv.Gene[Incr(I)] * 100 + 75);
  +  GA_PLANNER_FieldCrit_MissingFields                := Max(1, aIdv.Gene[Incr(I)] * 100 + 75);
  +  GA_PLANNER_FieldCrit_FarmPosition                 := Max(1, aIdv.Gene[Incr(I)] * 80);
  +  GA_PLANNER_SnapCrit_SnapToHouse                   := Max(1, aIdv.Gene[Incr(I)] * 50);
  +  GA_PLANNER_SnapCrit_SnapToFields                  := Max(1, aIdv.Gene[Incr(I)] * 50);
  +  GA_PLANNER_SnapCrit_SnapToRoads                   := Max(1, aIdv.Gene[Incr(I)] * 100 + 50);
  +  GA_PLANNER_SnapCrit_ClearEntrance                 := Max(1, aIdv.Gene[Incr(I)] * 50 + 50);
  +  GA_PLANNER_FindPlaceForHouse_CloseWorker          := Max(1, aIdv.Gene[Incr(I)] * 100);
  +  GA_PLANNER_FindPlaceForHouse_SnapCrit             := Max(1, aIdv.Gene[Incr(I)] * 50);
  +  GA_PLANNER_FindPlaceForHouse_DistCrit             := Max(1, aIdv.Gene[Incr(I)] * 50);
  +  GA_PLANNER_FindPlaceForHouse_CityCenter           := Max(1, aIdv.Gene[Incr(I)] * 75);
  +  GA_PLANNER_FindPlaceForHouse_EvalArea             := Max(1, aIdv.Gene[Incr(I)] * 100 + 50);
  +  GA_PLANNER_PlaceWoodcutter_DistFromForest         := Max(1, aIdv.Gene[Incr(I)] * 100);
  +  GA_PLANNER_FindPlaceForWoodcutter_TreeCnt         := Max(1, aIdv.Gene[Incr(I)] * 100 + 50);
  +  GA_PLANNER_FindPlaceForWoodcutter_PolyRoute       := Max(1, aIdv.Gene[Incr(I)] * 10);
  +  GA_PLANNER_FindPlaceForWoodcutter_EvalArea        := Max(1, aIdv.Gene[Incr(I)] * 30);
  +  GA_PLANNER_FindPlaceForWoodcutter_ExistForest     := Max(1, aIdv.Gene[Incr(I)] * 100 + 100);
  +  GA_PLANNER_FindPlaceForWoodcutter_DistCrit        := Max(1, aIdv.Gene[Incr(I)] * 20);
  +
  +  if aLogIt then
  +  begin
  +    fLogPar.AddTime('GA_PLANNER_ObstaclesInHousePlan_Tree                : Single = ' + FloatToStr( GA_PLANNER_ObstaclesInHousePlan_Tree              ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_ObstaclesInHousePlan_Road                : Single = ' + FloatToStr( GA_PLANNER_ObstaclesInHousePlan_Road              ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FieldCrit_MissingFields                  : Single = ' + FloatToStr( GA_PLANNER_FieldCrit_MissingFields                ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FieldCrit_FarmPosition                   : Single = ' + FloatToStr( GA_PLANNER_FieldCrit_FarmPosition                 ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_SnapCrit_SnapToHouse                     : Single = ' + FloatToStr( GA_PLANNER_SnapCrit_SnapToHouse                   ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_SnapCrit_SnapToFields                    : Single = ' + FloatToStr( GA_PLANNER_SnapCrit_SnapToFields                  ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_SnapCrit_SnapToRoads                     : Single = ' + FloatToStr( GA_PLANNER_SnapCrit_SnapToRoads                   ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_SnapCrit_ClearEntrance                   : Single = ' + FloatToStr( GA_PLANNER_SnapCrit_ClearEntrance                 ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FindPlaceForHouse_CloseWorker            : Single = ' + FloatToStr( GA_PLANNER_FindPlaceForHouse_CloseWorker          ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FindPlaceForHouse_SnapCrit               : Single = ' + FloatToStr( GA_PLANNER_FindPlaceForHouse_SnapCrit             ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FindPlaceForHouse_DistCrit               : Single = ' + FloatToStr( GA_PLANNER_FindPlaceForHouse_DistCrit             ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FindPlaceForHouse_CityCenter             : Single = ' + FloatToStr( GA_PLANNER_FindPlaceForHouse_CityCenter           ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FindPlaceForHouse_EvalArea               : Single = ' + FloatToStr( GA_PLANNER_FindPlaceForHouse_EvalArea             ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_PlaceWoodcutter_DistFromForest           : Single = ' + FloatToStr( GA_PLANNER_PlaceWoodcutter_DistFromForest         ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FindPlaceForWoodcutter_TreeCnt           : Single = ' + FloatToStr( GA_PLANNER_FindPlaceForWoodcutter_TreeCnt         ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FindPlaceForWoodcutter_PolyRoute         : Single = ' + FloatToStr( GA_PLANNER_FindPlaceForWoodcutter_PolyRoute       ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FindPlaceForWoodcutter_EvalArea          : Single = ' + FloatToStr( GA_PLANNER_FindPlaceForWoodcutter_EvalArea        ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FindPlaceForWoodcutter_ExistForest       : Single = ' + FloatToStr( GA_PLANNER_FindPlaceForWoodcutter_ExistForest     ) + ';');
  +    fLogPar.AddTime('GA_PLANNER_FindPlaceForWoodcutter_DistCrit          : Single = ' + FloatToStr( GA_PLANNER_FindPlaceForWoodcutter_DistCrit        ) + ';');
  +  end;
  +end;
  +
  +
  +{ TKMRunnerGA_CityPredictor }
  +procedure TKMRunnerGA_CityPredictor.InitGAParameters();
  +begin
  +  f_GA_SIMULATION_TIME_IN_MIN    := 10;
  +  f_GA_POPULATION_CNT            := 2;
  +  f_GA_INDIVIDUALS_IN_TOURNAMENT := 2;
  +  f_GA_GENE_CNT                  := 5;
  +  f_GA_MAPS_CNT                  := 9;
  +  f_GA_START_MUTATION            := 0.2;
  +  f_GA_FINAL_MUTATION            := 0.01;
  +  f_GA_CROSSOVER_COEF            := 1;
  +end;
  +
  +
  +procedure TKMRunnerGA_CityPredictor.SetParameters(aIdv: TGAIndividual; aLogIt: Boolean = False);
  +var
  +  I: Integer;
  +begin
  +  I := 0;
  +  GA_PREDICTOR_CityInitialization_Space        := aIdv.Gene[Incr(I)] * 1 / 100.0;
  +  GA_PREDICTOR_CityInitialization_Fertility    := aIdv.Gene[Incr(I)] * 1 / 100.0;
  +  GA_PREDICTOR_CityInitialization_Worker       := aIdv.Gene[Incr(I)] * 1 / 10.0;
  +  GA_MANAGER_CheckUnitCount_SerfCoef           := aIdv.Gene[Incr(I)];
  +  GA_MANAGER_CheckUnitCount_SerfLimit          := Max(1, aIdv.Gene[Incr(I)] * 10);
  +
  +  if aLogIt then
  +  begin
  +    fLogPar.AddTime('GA_PREDICTOR_CityInitialization_Space          : Single = ' + FloatToStr( GA_PREDICTOR_CityInitialization_Space     ) + ';');
  +    fLogPar.AddTime('GA_PREDICTOR_CityInitialization_Fertility      : Single = ' + FloatToStr( GA_PREDICTOR_CityInitialization_Fertility ) + ';');
  +    fLogPar.AddTime('GA_PREDICTOR_CityInitialization_Worker         : Single = ' + FloatToStr( GA_PREDICTOR_CityInitialization_Worker    ) + ';');
  +    fLogPar.AddTime('GA_MANAGER_CheckUnitCount_SerfCoef             : Single = ' + FloatToStr( GA_MANAGER_CheckUnitCount_SerfCoef        ) + ';');
  +    fLogPar.AddTime('GA_MANAGER_CheckUnitCount_SerfLimit            : Single = ' + FloatToStr( GA_MANAGER_CheckUnitCount_SerfLimit       ) + ';');
  +  end;
  +end;
  +
  +{
  +function TKMRunnerGA_CityPlanner.CostFunction(): Single;
  +const
  +  MAX_BID = 1000000;
  +var
  +  HMA: THouseMappingArray;
  +  PlannedHouses: TPlannedHousesArray;
  +  Loc: TKMPoint;
  +
  +  function IsCompletedRoad(aPoint: TKMPoint): Boolean; inline;
  +  begin
  +    Result := gTerrain.TileIsWalkableRoad(aPoint);
  +  end;
  +  function IsCompletedField(aPoint: TKMPoint): Boolean; inline;
  +  begin
  +    Result := gTerrain.TileIsCornField(aPoint);
  +  end;
  +  function IsCompletedWine(aPoint: TKMPoint): Boolean; inline;
  +  begin
  +    Result := gTerrain.TileIsWineField(aPoint);
  +  end;
  +
  +  function ClosestDistance(aHT: THouseType): Single;
  +  const
  +    MAX_DIST = 1000;
  +  var
  +    I: Integer;
  +    Output, Bid: Single;
  +    HT: THouseType;
  +  begin
  +    Output := MAX_DIST;
  +    for HT in HOUSE_DEPENDENCE[aHT] do
  +      for I := 0 to PlannedHouses[HT].Count - 1 do
  +      begin
  +        Bid := KMDistanceAbs(Loc, PlannedHouses[HT].Plans[I].Loc);
  +        if (Bid < Output) then
  +          Output := Bid;
  +      end;
  +    if (Output = MAX_DIST) then
  +      Output := 0;
  +    Result := Output;
  +  end;
  +
  +  function AllDistances(aHT: THouseType): Single;
  +  var
  +    I: Integer;
  +    HT: THouseType;
  +  begin
  +    Result := 0;
  +    for HT in HOUSE_DEPENDENCE[aHT] do
  +      for I := 0 to PlannedHouses[HT].Count - 1 do
  +        Result := Result + KMDistanceAbs(Loc, PlannedHouses[HT].Plans[I].Loc);
  +  end;
  +
  +  function SnapCrit(aHT: THouseType): Single;
  +  const
  +    SNAP_TO_HOUSE = 5;
  +    SNAP_TO_ROAD = 4;
  +    SNAP_TO_FIELD = 2;
  +  var
  +    I,Dist: Integer;
  +    Point: TKMPoint;
  +    Output: Single;
  +    Dir: TDirection;
  +  begin
  +    Output := 0;
  +    // Snap to road / field / wine / house / edge of map / mountains
  +    Dist := 1;
  +    for Dir := Low(HMA[aHT].Surroundings[Dist]) to High(HMA[aHT].Surroundings[Dist]) do
  +      for I := Low(HMA[aHT].Surroundings[Dist,Dir]) to High(HMA[aHT].Surroundings[Dist,Dir]) do
  +      begin
  +        Point := KMPointAdd(Loc, HMA[aHT].Surroundings[Dist,Dir,I]);
  +        Output := Output
  +                  + Byte(not (tpBuild in gTerrain.Land[Point.Y,Point.X].Passability)) * SNAP_TO_HOUSE
  +                  + Byte(IsCompletedRoad(Point)) * SNAP_TO_ROAD
  +                  + Byte(IsCompletedField(Point) OR IsCompletedWine(Point)) * SNAP_TO_FIELD;
  +      end;
  +    Result := Output;
  +  end;
  +
  +  function WoodcutCrit(aIdx: Integer): Single;
  +  const
  +    SCAN_RAD = 5;
  +    PLAN_PENALIZATION = 4;
  +    POTENTIAL_TREE_PRICE = 3;
  +    TREE_PRICE = 5;
  +    EDGE_RAD = 8;
  +    EDGE_PRICE = 2;
  +  var
  +    X,Y,minL,maxL: Integer;
  +    FLoc: TKMPoint;
  +    Output: Single;
  +  begin
  +    FLoc := PlannedHouses[htWoodcutters].Plans[aIdx].SpecPoint;
  +    Output := + 4*Abs(KMDistanceAbs(FLoc, PlannedHouses[htWoodcutters].Plans[aIdx].Loc)-2); // Place woocutter close to cutting point
  +    Output := Output - ClosestDistance(htStore); // We want wodcutter near city
  +    for Y := Max(1,FLoc.Y-SCAN_RAD) to Min(gTerrain.MapY,FLoc.Y+SCAN_RAD) do
  +    for X := Max(1,FLoc.X-SCAN_RAD) to Min(gTerrain.MapX,FLoc.X+SCAN_RAD) do
  +    begin
  +      if not (tpBuild in gTerrain.Land[Y,X].Passability) then // No plans around forest
  +        Output := Output - PLAN_PENALIZATION;
  +      if gTerrain.ObjectIsChopableTree(X, Y) then // Trees in forest
  +        Output := Output + TREE_PRICE;
  +      if gTerrain.TileGoodForTree(X, Y) then
  +        Output := Output + POTENTIAL_TREE_PRICE;
  +    end;
  +    minL := FLoc.Y-EDGE_RAD;
  +    maxL := FLoc.Y+EDGE_RAD;
  +    for X := Max(1,FLoc.X-EDGE_RAD) to Min(gTerrain.MapX-1,FLoc.X+EDGE_RAD) do
  +    begin
  +      if (minL < 1) OR not gRes.Tileset.TileIsRoadable( gTerrain.Land[minL,X].Terrain ) then
  +        Output := Output + EDGE_PRICE;
  +      if (maxL > gTerrain.MapY-1) OR not gRes.Tileset.TileIsRoadable( gTerrain.Land[maxL,X].Terrain ) then
  +        Output := Output + EDGE_PRICE;
  +    end;
  +    // Snap forest to edge
  +    minL := FLoc.X-EDGE_RAD;
  +    maxL := FLoc.X+EDGE_RAD;
  +    for Y := Max(1,FLoc.Y-EDGE_RAD) to Min(gTerrain.MapY-1,FLoc.Y+EDGE_RAD) do
  +    begin
  +      if (minL < 1) OR not gRes.Tileset.TileIsRoadable( gTerrain.Land[Y,minL].Terrain ) then
  +        Output := Output + EDGE_PRICE;
  +      if (maxL > gTerrain.MapX-1) OR not gRes.Tileset.TileIsRoadable( gTerrain.Land[Y,maxL].Terrain ) then
  +        Output := Output + EDGE_PRICE;
  +    end;
  +    Result := Output;
  +  end;
  +
  +const
  +  PL = 1;
  +  WEAPONS_WEIGHT = 50;
  +  CITIZENS_LOST = 25;
  +var
  +  K: Integer;
  +  Output: Single;
  +  HT: THouseType;
  +begin
  +  Output := 0.0;
  +  HMA := gAIFields.Eye.HousesMapping;
  +  PlannedHouses := gHands.Hands[PL].AI.CityManagement.Builder.Planner.PlannedHouses;
  +
  +  for HT := HOUSE_MIN to HOUSE_MAX do
  +    for K := 0 to PlannedHouses[HT].Count - 1 do
  +    begin
  +      Loc := PlannedHouses[HT].Plans[K].Loc;
  +      Output := Output + SnapCrit(HT) + AllDistances(HT);
  +    end;
  +
  +  Output := Output
  +            + gHands[PL].Stats.GetWeaponsProduced * WEAPONS_WEIGHT
  +            - gHands[PL].Stats.GetCitizensLost * CITIZENS_LOST;
  +  Result := Output;
  +end;
  +//}
  +
  +
  +
   
   
   procedure TKMRunnerStone.SetUp;
  
positive.code: end;
  
  initialization
    RegisterRunner(TKMRunnerStone);
    RegisterRunner(TKMRunnerFight95);
    RegisterRunner(TKMRunnerAIBuild);
  
negative.code:   inherited;
    if gLog = nil then
      gLog := TKMLog.Create(ExeDir + 'Utils\Runner\Runner_Log.log');
  
    fResults.ValueCount := 6;
  //  fResults.TimesCount := 60*60*10;
    fResults.TimesCount := 10;
    HTotal := 0;
    HAver := 0;
    WTotal := 0;
  