Skip to content

Commit 1d43ffc

Browse files
committed
Adjust AI random calls to previous distributions
1 parent 487f8f6 commit 1d43ffc

4 files changed

Lines changed: 81 additions & 84 deletions

File tree

libs/s25main/ai/aijh/AIConstruction.cpp

Lines changed: 30 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

@@ -466,20 +466,21 @@ helpers::OptionalEnum<BuildingType> AIConstruction::ChooseMilitaryBuilding(const
466466
const BuildingType biggestBld = GetBiggestAllowedMilBuilding().value();
467467

468468
const Inventory& inventory = aii.GetInventory();
469-
if((inventory.people[Job::Private] < 15 || AI::random(3))
469+
if((inventory.people[Job::Private] < 15 || AI::randomChance(3))
470470
&& (inventory.goods[GoodType::Stones] > 6 || bldPlanner.GetNumBuildings(BuildingType::Quarry) > 0))
471471
bld = BuildingType::Guardhouse;
472-
if(aijh.getAIInterface().isHarborPosClose(pt, 19) && !AI::random(9) && aijh.ggs.isEnabled(AddonId::SEA_ATTACK))
472+
if(aijh.getAIInterface().isHarborPosClose(pt, 19) && !AI::randomChance(10)
473+
&& aijh.ggs.isEnabled(AddonId::SEA_ATTACK))
473474
{
474475
if(aii.CanBuildBuildingtype(BuildingType::Watchtower))
475476
return BuildingType::Watchtower;
476477
return GetBiggestAllowedMilBuilding();
477478
}
478479
if(biggestBld == BuildingType::Watchtower || biggestBld == BuildingType::Fortress)
479480
{
480-
if(aijh.UpdateUpgradeBuilding() < 0 && bldPlanner.GetNumBuildingSites(biggestBld) < 1
481+
if(!aijh.UpdateUpgradeBuilding() && bldPlanner.GetNumBuildingSites(biggestBld) < 1
481482
&& (inventory.goods[GoodType::Stones] > 20 || bldPlanner.GetNumBuildings(BuildingType::Quarry) > 0)
482-
&& !AI::random(9u))
483+
&& !AI::randomChance(10))
483484
{
484485
return biggestBld;
485486
}
@@ -489,23 +490,22 @@ helpers::OptionalEnum<BuildingType> AIConstruction::ChooseMilitaryBuilding(const
489490
sortedMilitaryBlds military = aii.gwb.LookForMilitaryBuildings(pt, 3);
490491
for(const nobBaseMilitary* milBld : military)
491492
{
492-
unsigned distance = aii.gwb.CalcDistance(milBld->GetPos(), pt);
493+
const unsigned distance = aii.gwb.CalcDistance(milBld->GetPos(), pt);
493494

494-
// Prüfen ob Feind in der Nähe
495+
// Check for close enemy
495496
if(milBld->GetPlayer() != playerId && distance < 35)
496497
{
497-
const auto randmil = AI::randomValue(0, std::numeric_limits<int>::max());
498-
bool buildCatapult = randmil % 8 == 0 && aii.CanBuildCatapult()
498+
bool buildCatapult = AI::randomChance(8) && aii.CanBuildCatapult()
499499
&& bldPlanner.GetNumAdditionalBuildingsWanted(BuildingType::Catapult) > 0;
500-
// another catapult within "min" radius? ->dont build here!
501-
const unsigned min = 16;
502-
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)
503503
buildCatapult = false;
504504
if(buildCatapult)
505505
{
506506
for(const nobUsual* catapult : aii.GetBuildings(BuildingType::Catapult))
507507
{
508-
if(aii.gwb.CalcDistance(pt, catapult->GetPos()) < min)
508+
if(aii.gwb.CalcDistance(pt, catapult->GetPos()) < minCatapultDist)
509509
{
510510
buildCatapult = false;
511511
break;
@@ -516,7 +516,8 @@ helpers::OptionalEnum<BuildingType> AIConstruction::ChooseMilitaryBuilding(const
516516
{
517517
for(const noBuildingSite* bldSite : aii.GetBuildingSites())
518518
{
519-
if(bldSite->GetBuildingType() == bld && aii.gwb.CalcDistance(pt, bldSite->GetPos()) < min)
519+
if(bldSite->GetBuildingType() == bld
520+
&& aii.gwb.CalcDistance(pt, bldSite->GetPos()) < minCatapultDist)
520521
{
521522
buildCatapult = false;
522523
break;
@@ -527,19 +528,21 @@ helpers::OptionalEnum<BuildingType> AIConstruction::ChooseMilitaryBuilding(const
527528
bld = BuildingType::Catapult;
528529
else
529530
{
530-
if(randmil % 2 == 0 && aii.CanBuildBuildingtype(BuildingType::Watchtower))
531-
bld = BuildingType::Watchtower;
532-
else
533-
bld = biggestBld;
534-
}
535-
// slim chance for a guardhouse instead of tower or fortress so we can expand towards an enemy even if there
536-
// are no big building spots in that direction
537-
if(randmil % 10 == 0)
538-
{
539-
if(aii.CanBuildBuildingtype(BuildingType::Guardhouse))
540-
bld = BuildingType::Guardhouse;
541-
else
542-
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+
}
543546
}
544547
break;
545548
}

libs/s25main/ai/aijh/AIPlayerJH.cpp

Lines changed: 35 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2005 - 2021 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

@@ -363,10 +363,8 @@ void AIPlayerJH::PlanNewBuildings(const unsigned gf)
363363
const std::list<nobMilitary*>& militaryBuildings = aii.GetMilitaryBuildings();
364364
if(militaryBuildings.empty())
365365
return;
366-
const int randomMiliBld = static_cast<int>(AI::randomIndex(militaryBuildings));
367-
auto it2 = militaryBuildings.begin();
368-
std::advance(it2, randomMiliBld);
369-
MapPoint bldPos = (*it2)->GetPos();
366+
const auto* randomMiliBld = AI::randomElement(militaryBuildings);
367+
MapPoint bldPos = randomMiliBld->GetPos();
370368
UpdateNodesAround(bldPos, 15);
371369
// resource gathering buildings only around military; processing only close to warehouses
372370
for(unsigned i = 0; i < numResGatherBlds; i++)
@@ -377,7 +375,7 @@ void AIPlayerJH::PlanNewBuildings(const unsigned gf)
377375
}
378376
}
379377
AddMilitaryBuildJob(bldPos);
380-
if((*it2)->IsUseless() && (*it2)->IsDemolitionAllowed() && randomMiliBld != UpdateUpgradeBuilding())
378+
if(randomMiliBld->IsUseless() && randomMiliBld->IsDemolitionAllowed() && randomMiliBld != UpdateUpgradeBuilding())
381379
{
382380
aii.DestroyBuilding(bldPos);
383381
}
@@ -401,28 +399,24 @@ unsigned AIPlayerJH::GetNumJobs() const
401399
return eventManager.GetEventNum() + construction->GetBuildJobNum() + construction->GetConnectJobNum();
402400
}
403401

404-
/// returns the warehouse closest to the upgradebuilding or if it cant find a way the first warehouse and if there is no
405-
/// warehouse left null
402+
/// returns the warehouse closest to the upgrade building or if it cant find a way the first warehouse and if there is
403+
/// no warehouse left null
406404
nobBaseWarehouse* AIPlayerJH::GetUpgradeBuildingWarehouse()
407405
{
408406
const std::list<nobBaseWarehouse*>& storehouses = aii.GetStorehouses();
409407
if(storehouses.empty())
410408
return nullptr;
411-
nobBaseWarehouse* wh = storehouses.front();
412-
int uub = UpdateUpgradeBuilding();
409+
const auto* upgradeBld = UpdateUpgradeBuilding();
413410

414-
if(uub >= 0
415-
&& storehouses.size() > 1) // upgradebuilding exists and more than 1 warehouse -> find warehouse closest to the
416-
// upgradebuilding - gather stuff there and deactivate gathering in the previous one
411+
if(upgradeBld) // upgrade building exists -> find warehouse closest to the upgrade building,
412+
// gather stuff there and deactivate gathering in the previous one
417413
{
418-
auto upgradeBldIt = aii.GetMilitaryBuildings().begin();
419-
std::advance(upgradeBldIt, uub);
420414
// which warehouse is closest to the upgrade building? -> train troops there and block max ranks
421-
wh = aii.FindWarehouse(**upgradeBldIt, FW::NoCondition(), false, false);
422-
if(!wh)
423-
wh = storehouses.front();
415+
auto* wh = aii.FindWarehouse(*upgradeBld, FW::NoCondition(), false, false);
416+
if(wh)
417+
return wh;
424418
}
425-
return wh;
419+
return storehouses.front();
426420
}
427421

428422
void AIPlayerJH::AddMilitaryBuildJob(MapPoint pt)
@@ -1207,10 +1201,8 @@ void AIPlayerJH::HandleExpedition(const noShip* ship)
12071201
aii.FoundColony(ship);
12081202
else
12091203
{
1210-
const unsigned offset = AI::randomValue(0u, helpers::MaxEnumValue_v<ShipDirection> - 1u);
1211-
for(auto dir : helpers::EnumRange<ShipDirection>{})
1204+
for(auto dir : helpers::enumRange(AI::randomEnum<ShipDirection>()))
12121205
{
1213-
dir = ShipDirection((rttr::enum_cast(dir) + offset) % helpers::MaxEnumValue_v<ShipDirection>);
12141206
if(aii.IsExplorationDirectionPossible(ship->GetPos(), ship->GetCurrentHarbor(), dir))
12151207
{
12161208
aii.TravelToNextSpot(dir, ship);
@@ -1252,7 +1244,7 @@ void AIPlayerJH::HandleTreeChopped(const MapPoint pt)
12521244

12531245
UpdateNodesAround(pt, 3);
12541246

1255-
if(AI::random())
1247+
if(AI::randomChance())
12561248
AddMilitaryBuildJob(pt);
12571249
else // if (random % 12 == 0)
12581250
AddBuildJob(BuildingType::Woodcutter, pt);
@@ -1392,21 +1384,19 @@ void AIPlayerJH::HandleLostLand(const MapPoint pt)
13921384
void AIPlayerJH::MilUpgradeOptim()
13931385
{
13941386
// do we have a upgrade building?
1395-
int upb = UpdateUpgradeBuilding();
1396-
int count = 0;
1387+
const auto* upgradeBld = UpdateUpgradeBuilding();
1388+
unsigned count = 0;
13971389
const std::list<nobMilitary*>& militaryBuildings = aii.GetMilitaryBuildings();
13981390
for(const nobMilitary* milBld : militaryBuildings)
13991391
{
1400-
if(count != upb) // not upgrade building
1392+
if(milBld != upgradeBld) // not upgrade building
14011393
{
1402-
if(upb >= 0) // we do have an upgrade building
1394+
if(upgradeBld) // we do have an upgrade building
14031395
{
14041396
if(!milBld->IsGoldDisabled()) // deactivate gold for all other buildings
1405-
{
14061397
aii.SetCoinsAllowed(milBld->GetPos(), false);
1407-
}
14081398
if(milBld->GetFrontierDistance() == FrontierDistance::Far
1409-
&& (((unsigned)count + GetNumPlannedConnectedInlandMilitaryBlds())
1399+
&& (count + GetNumPlannedConnectedInlandMilitaryBlds()
14101400
< militaryBuildings.size())) // send out troops until 1 private is left, then cancel road
14111401
{
14121402
if(milBld->GetNumTroops() > 1) // more than 1 soldier remaining? -> send out order
@@ -1415,7 +1405,7 @@ void AIPlayerJH::MilUpgradeOptim()
14151405
for(unsigned rank = 1; rank < NUM_SOLDIER_RANKS; ++rank)
14161406
aii.SetTroopLimit(milBld->GetPos(), rank, 0);
14171407

1418-
// TODO: Currently the ai still manages soldiers by disconnecting roads, if in the future it
1408+
// TODO: Currently the AI still manages soldiers by disconnecting roads, if in the future it
14191409
// uses only SetTroopLimit then this can be removed
14201410
for(unsigned rank = 0; rank < NUM_SOLDIER_RANKS; ++rank)
14211411
aii.SetTroopLimit(milBld->GetPos(), rank, milBld->GetMaxTroopsCt());
@@ -1534,7 +1524,7 @@ void AIPlayerJH::TryToAttack()
15341524
// We handle the current building with a probability of limit/numMilBlds
15351525
// -> For twice the number of blds as the limit we will most likely skip every 2nd building
15361526
// This way we check roughly (at most) limit buildings but avoid any preference for one building over an other
1537-
if(!AI::random(numMilBlds, limit))
1527+
if(!AI::randomChance(numMilBlds, limit))
15381528
continue;
15391529

15401530
if(milBld->GetFrontierDistance() == FrontierDistance::Far) // inland building? -> skip it
@@ -1696,15 +1686,15 @@ void AIPlayerJH::TrySeaAttack()
16961686
}
16971687
}
16981688
}
1699-
// add all military buildings around still valid harborspots (unused or used by ally)
1689+
// add all military buildings around still valid harbor spots (unused or used by ally)
17001690
unsigned limit = 15;
17011691
unsigned skip = 0;
17021692
if(searcharoundharborspots.size() > 15)
1703-
skip = AI::randomValue(0u, static_cast<unsigned>(searcharoundharborspots.size() / 15u)) * 15u;
1693+
skip = AI::randomValue(1u, static_cast<unsigned>(searcharoundharborspots.size() / 15u)) * 15u - 1;
17041694
for(unsigned i = skip; i < searcharoundharborspots.size() && limit > 0; i++)
17051695
{
17061696
limit--;
1707-
// now add all military buildings around the harborspot to our list of potential targets
1697+
// now add all military buildings around the harbor spot to our list of potential targets
17081698
sortedMilitaryBlds buildings = gwb.LookForMilitaryBuildings(gwb.GetHarborPoint(searcharoundharborspots[i]), 2);
17091699
for(const nobBaseMilitary* milBld : buildings)
17101700
{
@@ -2041,15 +2031,15 @@ void AIPlayerJH::InitStoreAndMilitarylists()
20412031
{
20422032
SetFarmedNodes(charburner->GetPos(), true);
20432033
}
2044-
// find the upgradebuilding
2034+
// find the upgrade building
20452035
UpdateUpgradeBuilding();
20462036
}
2047-
int AIPlayerJH::UpdateUpgradeBuilding()
2037+
2038+
const nobMilitary* AIPlayerJH::UpdateUpgradeBuilding()
20482039
{
20492040
std::vector<const nobMilitary*> backup;
20502041
if(!aii.GetStorehouses().empty())
20512042
{
2052-
unsigned count = 0;
20532043
for(const nobMilitary* milBld : aii.GetMilitaryBuildings())
20542044
{
20552045
// inland building, tower or fortress
@@ -2059,14 +2049,11 @@ int AIPlayerJH::UpdateUpgradeBuilding()
20592049
{
20602050
if(construction->IsConnectedToRoadSystem(milBld->GetFlag()))
20612051
{
2062-
// LOG.write(("UpdateUpgradeBuilding at %i,%i for player %i (listslot %i) \n",itObj->GetX(),
2063-
// itObj->GetY(), playerId, count);
20642052
UpgradeBldPos = milBld->GetPos();
2065-
return count;
2053+
return milBld;
20662054
}
20672055
backup.push_back(milBld);
20682056
}
2069-
count++;
20702057
}
20712058
}
20722059
// no valid upgrade building yet - try to reconnect correctly flagged buildings
@@ -2075,7 +2062,7 @@ int AIPlayerJH::UpdateUpgradeBuilding()
20752062
construction->AddConnectFlagJob(milBld->GetFlag());
20762063
}
20772064
UpgradeBldPos = MapPoint::Invalid();
2078-
return -1;
2065+
return nullptr;
20792066
}
20802067
// set default start values for the ai for distribution & military settings
20812068
void AIPlayerJH::InitDistribution()
@@ -2440,7 +2427,7 @@ void AIPlayerJH::AdjustSettings()
24402427
milSettings[2] = 4;
24412428
milSettings[3] = 5;
24422429
// interior 0bar full if we have an upgrade building and gold(or produce gold) else 1 soldier each
2443-
milSettings[4] = UpdateUpgradeBuilding() >= 0
2430+
milSettings[4] = UpdateUpgradeBuilding()
24442431
&& (inventory[GoodType::Coins] > 0
24452432
|| (inventory[GoodType::Gold] > 0 && inventory[GoodType::Coal] > 0
24462433
&& !aii.GetBuildings(BuildingType::Mint).empty())) ?
@@ -2467,17 +2454,17 @@ unsigned AIPlayerJH::CalcMilSettings()
24672454

24682455
// now add up all counts of soldiers that are fixed in use and those that depend on whatever we have as a result
24692456
const unsigned numShouldStayConnected = GetNumPlannedConnectedInlandMilitaryBlds();
2470-
int count = 0;
2457+
unsigned count = 0;
24712458
unsigned soldierInUseFixed = 0;
2472-
const int uun = UpdateUpgradeBuilding();
2459+
const auto* upgradeBld = UpdateUpgradeBuilding();
24732460
const std::list<nobMilitary*>& militaryBuildings = aii.GetMilitaryBuildings();
24742461
for(const nobMilitary* milBld : militaryBuildings)
24752462
{
24762463
if(milBld->GetFrontierDistance() == FrontierDistance::Near
24772464
|| milBld->GetFrontierDistance() == FrontierDistance::Harbor
24782465
|| (milBld->GetFrontierDistance() == FrontierDistance::Far
2479-
&& (militaryBuildings.size() < (unsigned)count + numShouldStayConnected
2480-
|| count == uun))) // front or connected interior
2466+
&& (count + numShouldStayConnected > militaryBuildings.size()
2467+
|| milBld == upgradeBld))) // front or connected interior
24812468
{
24822469
soldierInUseFixed += milBld->CalcRequiredNumTroops(FrontierDistance::Mid, 8);
24832470
} else if(milBld->GetFrontierDistance() == FrontierDistance::Mid) // 1 bar (inland)

libs/s25main/ai/aijh/AIPlayerJH.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2005 - 2021 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

@@ -75,8 +75,8 @@ class AIPlayerJH final : public AIPlayer
7575
}
7676
/// returns the percentage*100 of possible normal building places
7777
unsigned BQsurroundcheck(MapPoint pt, unsigned range, bool includeexisting, unsigned limit = 0);
78-
/// returns list entry of the building the ai uses for troop upgrades
79-
int UpdateUpgradeBuilding();
78+
/// returns the building the AI uses for troop upgrades
79+
const nobMilitary* UpdateUpgradeBuilding();
8080
/// returns amount of good/people stored in warehouses right now
8181
unsigned AmountInStorage(GoodType good) const;
8282
unsigned AmountInStorage(Job job) const;

libs/s25main/ai/random.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ namespace AI {
1111

1212
std::minstd_rand& getRandomGenerator();
1313

14-
// Return a random value (min and max are included)
14+
/// Return a random value (min and max are included)
1515
template<typename T>
1616
T randomValue(T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>::max())
1717
{
1818
return helpers::randomValue(getRandomGenerator(), min, max);
1919
}
2020

21-
// Return a random bool:
22-
// random() ... will return true|false with 50% chance each
23-
// random(15) ... will return true in 1/15 of the cases
24-
// random(20, 5) ... will return true in 5 out of 20 cases, i.e. a probability of 25%. Sames as random(4, 1)
25-
inline bool random(unsigned total = 2u, unsigned chance = 1u)
21+
/// Return a true in `chance` out of `total` cases:
22+
/// randomChance() ... return true|false with 50% chance each
23+
/// randomChance(15) ... return true in 1/15 of the cases
24+
/// randomChance(20, 5) ... return true in 5 out of 20 cases, i.e. a probability of 25%. Sames as randomChance(4, 1)
25+
inline bool randomChance(unsigned total = 2u, unsigned chance = 1u)
2626
{
2727
RTTR_Assert(total > 0u);
2828
return (chance >= total) || randomValue(1u, total) <= chance;
@@ -41,4 +41,11 @@ auto randomElement(const ContainerT& container)
4141
return helpers::getRandomElement(getRandomGenerator(), container);
4242
}
4343

44+
/// Return random enumerator
45+
template<typename Enum>
46+
auto randomEnum()
47+
{
48+
return helpers::randomEnum<Enum>(getRandomGenerator());
49+
}
50+
4451
} // namespace AI

0 commit comments

Comments
 (0)