Skip to content

Commit e6489dc

Browse files
authored
Merge pull request #1739 from wichern/use-random-in-aijh
Make AIJH Actions Reproducible by using the user selectable Random Seed
2 parents 5a24dac + 0bf8a73 commit e6489dc

7 files changed

Lines changed: 178 additions & 118 deletions

File tree

extras/ai-battle/main.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "QuickStartGame.h"
88
#include "RTTR_Version.h"
99
#include "RttrConfig.h"
10+
#include "ai/random.h"
1011
#include "files.h"
1112
#include "random/Random.h"
1213
#include "s25util/System.h"
@@ -30,6 +31,7 @@ int main(int argc, char** argv)
3031
boost::optional<std::string> replay_path;
3132
boost::optional<std::string> savegame_path;
3233
unsigned random_init = static_cast<unsigned>(std::chrono::high_resolution_clock::now().time_since_epoch().count());
34+
unsigned random_ai_init = random_init;
3335

3436
po::options_description desc("Allowed options");
3537
// clang-format off
@@ -41,6 +43,7 @@ int main(int argc, char** argv)
4143
("replay", po::value(&replay_path),"Filename to write replay to (optional)")
4244
("save", po::value(&savegame_path),"Filename to write savegame to (optional)")
4345
("random_init", po::value(&random_init),"Seed value for the random number generator (optional)")
46+
("random_ai_init", po::value(&random_ai_init),"Seed value for the AI random number generator (optional)")
4447
("maxGF", po::value<unsigned>()->default_value(std::numeric_limits<unsigned>::max()),"Maximum number of game frames to run (optional)")
4548
("version", "Show version information and exit")
4649
;
@@ -85,10 +88,12 @@ int main(int argc, char** argv)
8588
bnw::cout << argv[i] << " ";
8689
bnw::cout << std::endl;
8790
bnw::cout << "random_init: " << random_init << std::endl;
91+
bnw::cout << "random_ai_init: " << random_ai_init << std::endl;
8892
bnw::cout << std::endl;
8993

9094
RTTRCONFIG.Init();
9195
RANDOM.Init(random_init);
96+
AI::getRandomGenerator().seed(random_ai_init);
9297

9398
const bfs::path mapPath = RTTRCONFIG.ExpandPath(options["map"].as<std::string>());
9499
const std::vector<AI::Info> ais = ParseAIOptions(options["ai"].as<std::vector<std::string>>());

libs/common/include/helpers/random.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ T randomValue(RandomT& rng, T min = std::numeric_limits<T>::min(), T max = std::
2424
return static_cast<T>(distr(rng));
2525
}
2626

27+
/// Return a true in `chance` out of `total` cases:
28+
/// randomChance() ... return true|false with 50% chance each
29+
/// randomChance(15) ... return true in 1/15 of the cases
30+
/// randomChance(20, 5) ... return true in 5 out of 20 cases, i.e. a probability of 25%. Sames as randomChance(4, 1)
31+
template<typename RandomT>
32+
bool randomChance(RandomT& rng, unsigned total = 2u, unsigned chance = 1u)
33+
{
34+
RTTR_Assert(total > 0u);
35+
return (chance >= total) || randomValue(rng, 1u, total) <= chance;
36+
}
37+
2738
/// Return a random enumerator from the given enum
2839
template<typename T, typename RandomT>
2940
T randomEnum(RandomT& rng)

libs/s25main/ai/aijh/AIConstruction.cpp

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org)
1+
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
22
//
33
// SPDX-License-Identifier: GPL-2.0-or-later
44

@@ -10,6 +10,7 @@
1010
#include "addons/const_addons.h"
1111
#include "ai/AIInterface.h"
1212
#include "ai/aijh/AIPlayerJH.h"
13+
#include "ai/random.h"
1314
#include "buildings/noBuildingSite.h"
1415
#include "buildings/nobBaseMilitary.h"
1516
#include "buildings/nobBaseWarehouse.h"
@@ -465,20 +466,21 @@ helpers::OptionalEnum<BuildingType> AIConstruction::ChooseMilitaryBuilding(const
465466
const BuildingType biggestBld = GetBiggestAllowedMilBuilding().value();
466467

467468
const Inventory& inventory = aii.GetInventory();
468-
if(((rand() % 3) == 0 || inventory.people[Job::Private] < 15)
469+
if((inventory.people[Job::Private] < 15 || AI::randomChance(3))
469470
&& (inventory.goods[GoodType::Stones] > 6 || bldPlanner.GetNumBuildings(BuildingType::Quarry) > 0))
470471
bld = BuildingType::Guardhouse;
471-
if(aijh.getAIInterface().isHarborPosClose(pt, 19) && rand() % 10 != 0 && aijh.ggs.isEnabled(AddonId::SEA_ATTACK))
472+
if(aijh.getAIInterface().isHarborPosClose(pt, 19) && !AI::randomChance(10)
473+
&& aijh.ggs.isEnabled(AddonId::SEA_ATTACK))
472474
{
473475
if(aii.CanBuildBuildingtype(BuildingType::Watchtower))
474476
return BuildingType::Watchtower;
475477
return GetBiggestAllowedMilBuilding();
476478
}
477479
if(biggestBld == BuildingType::Watchtower || biggestBld == BuildingType::Fortress)
478480
{
479-
if(aijh.UpdateUpgradeBuilding() < 0 && bldPlanner.GetNumBuildingSites(biggestBld) < 1
481+
if(!aijh.UpdateUpgradeBuilding() && bldPlanner.GetNumBuildingSites(biggestBld) < 1
480482
&& (inventory.goods[GoodType::Stones] > 20 || bldPlanner.GetNumBuildings(BuildingType::Quarry) > 0)
481-
&& rand() % 10 != 0)
483+
&& !AI::randomChance(10))
482484
{
483485
return biggestBld;
484486
}
@@ -488,23 +490,22 @@ helpers::OptionalEnum<BuildingType> AIConstruction::ChooseMilitaryBuilding(const
488490
sortedMilitaryBlds military = aii.gwb.LookForMilitaryBuildings(pt, 3);
489491
for(const nobBaseMilitary* milBld : military)
490492
{
491-
unsigned distance = aii.gwb.CalcDistance(milBld->GetPos(), pt);
493+
const unsigned distance = aii.gwb.CalcDistance(milBld->GetPos(), pt);
492494

493-
// Prüfen ob Feind in der Nähe
495+
// Check for close enemy
494496
if(milBld->GetPlayer() != playerId && distance < 35)
495497
{
496-
int randmil = rand();
497-
bool buildCatapult = randmil % 8 == 0 && aii.CanBuildCatapult()
498+
bool buildCatapult = AI::randomChance(8) && aii.CanBuildCatapult()
498499
&& bldPlanner.GetNumAdditionalBuildingsWanted(BuildingType::Catapult) > 0;
499-
// another catapult within "min" radius? ->dont build here!
500-
const unsigned min = 16;
501-
if(buildCatapult && aii.gwb.CalcDistance(pt, aii.GetStorehouses().front()->GetPos()) < min)
500+
// another catapult within this radius? ->dont build here!
501+
constexpr unsigned minCatapultDist = 16;
502+
if(buildCatapult && aii.gwb.CalcDistance(pt, aii.GetStorehouses().front()->GetPos()) < minCatapultDist)
502503
buildCatapult = false;
503504
if(buildCatapult)
504505
{
505506
for(const nobUsual* catapult : aii.GetBuildings(BuildingType::Catapult))
506507
{
507-
if(aii.gwb.CalcDistance(pt, catapult->GetPos()) < min)
508+
if(aii.gwb.CalcDistance(pt, catapult->GetPos()) < minCatapultDist)
508509
{
509510
buildCatapult = false;
510511
break;
@@ -515,7 +516,8 @@ helpers::OptionalEnum<BuildingType> AIConstruction::ChooseMilitaryBuilding(const
515516
{
516517
for(const noBuildingSite* bldSite : aii.GetBuildingSites())
517518
{
518-
if(bldSite->GetBuildingType() == bld && aii.gwb.CalcDistance(pt, bldSite->GetPos()) < min)
519+
if(bldSite->GetBuildingType() == bld
520+
&& aii.gwb.CalcDistance(pt, bldSite->GetPos()) < minCatapultDist)
519521
{
520522
buildCatapult = false;
521523
break;
@@ -526,19 +528,21 @@ helpers::OptionalEnum<BuildingType> AIConstruction::ChooseMilitaryBuilding(const
526528
bld = BuildingType::Catapult;
527529
else
528530
{
529-
if(randmil % 2 == 0 && aii.CanBuildBuildingtype(BuildingType::Watchtower))
530-
bld = BuildingType::Watchtower;
531-
else
532-
bld = biggestBld;
533-
}
534-
// slim chance for a guardhouse instead of tower or fortress so we can expand towards an enemy even if there
535-
// are no big building spots in that direction
536-
if(randmil % 10 == 0)
537-
{
538-
if(aii.CanBuildBuildingtype(BuildingType::Guardhouse))
539-
bld = BuildingType::Guardhouse;
540-
else
541-
bld = GetSmallestAllowedMilBuilding();
531+
// slim chance for a guardhouse instead of tower or fortress so we can expand towards an enemy even if
532+
// there are no big building spots in that direction
533+
if(AI::randomChance(10))
534+
{
535+
if(aii.CanBuildBuildingtype(BuildingType::Guardhouse))
536+
bld = BuildingType::Guardhouse;
537+
else
538+
bld = GetSmallestAllowedMilBuilding();
539+
} else
540+
{
541+
if(aii.CanBuildBuildingtype(BuildingType::Watchtower) && AI::randomChance())
542+
bld = BuildingType::Watchtower;
543+
else
544+
bld = biggestBld;
545+
}
542546
}
543547
break;
544548
}

0 commit comments

Comments
 (0)