Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make AIJH Actions Reproducible by Using the Games Random Seed #1739

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions extras/ai-battle/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "QuickStartGame.h"
#include "RTTR_Version.h"
#include "RttrConfig.h"
#include "ai/random.h"
#include "files.h"
#include "random/Random.h"
#include "s25util/System.h"
Expand All @@ -30,6 +31,7 @@ int main(int argc, char** argv)
boost::optional<std::string> replay_path;
boost::optional<std::string> savegame_path;
unsigned random_init = static_cast<unsigned>(std::chrono::high_resolution_clock::now().time_since_epoch().count());
unsigned random_ai_init = random_init;

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

RTTRCONFIG.Init();
RANDOM.Init(random_init);
AI::getRandomGenerator().seed(random_ai_init);

const bfs::path mapPath = RTTRCONFIG.ExpandPath(options["map"].as<std::string>());
const std::vector<AI::Info> ais = ParseAIOptions(options["ai"].as<std::vector<std::string>>());
Expand Down
12 changes: 7 additions & 5 deletions libs/s25main/ai/aijh/AIConstruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "addons/const_addons.h"
#include "ai/AIInterface.h"
#include "ai/aijh/AIPlayerJH.h"
#include "ai/random.h"
#include "buildings/noBuildingSite.h"
#include "buildings/nobBaseMilitary.h"
#include "buildings/nobBaseWarehouse.h"
Expand Down Expand Up @@ -465,10 +466,12 @@ helpers::OptionalEnum<BuildingType> AIConstruction::ChooseMilitaryBuilding(const
const BuildingType biggestBld = GetBiggestAllowedMilBuilding().value();

const Inventory& inventory = aii.GetInventory();
if(((rand() % 3) == 0 || inventory.people[Job::Private] < 15)
uint8_t playerId = aii.GetPlayerId();
if((AI::randomValue<int>(0, 2) == 0 || inventory.people[Job::Private] < 15)
&& (inventory.goods[GoodType::Stones] > 6 || bldPlanner.GetNumBuildings(BuildingType::Quarry) > 0))
bld = BuildingType::Guardhouse;
if(aijh.getAIInterface().isHarborPosClose(pt, 19) && rand() % 10 != 0 && aijh.ggs.isEnabled(AddonId::SEA_ATTACK))
if(aijh.getAIInterface().isHarborPosClose(pt, 19) && AI::randomValue<int>(0, 9) != 0
&& aijh.ggs.isEnabled(AddonId::SEA_ATTACK))
{
if(aii.CanBuildBuildingtype(BuildingType::Watchtower))
return BuildingType::Watchtower;
Expand All @@ -478,13 +481,12 @@ helpers::OptionalEnum<BuildingType> AIConstruction::ChooseMilitaryBuilding(const
{
if(aijh.UpdateUpgradeBuilding() < 0 && bldPlanner.GetNumBuildingSites(biggestBld) < 1
&& (inventory.goods[GoodType::Stones] > 20 || bldPlanner.GetNumBuildings(BuildingType::Quarry) > 0)
&& rand() % 10 != 0)
&& AI::randomValue<int>(0, 9) != 0)
{
return biggestBld;
}
}

uint8_t playerId = aii.GetPlayerId();
sortedMilitaryBlds military = aii.gwb.LookForMilitaryBuildings(pt, 3);
for(const nobBaseMilitary* milBld : military)
{
Expand All @@ -493,7 +495,7 @@ helpers::OptionalEnum<BuildingType> AIConstruction::ChooseMilitaryBuilding(const
// Prüfen ob Feind in der Nähe
if(milBld->GetPlayer() != playerId && distance < 35)
{
int randmil = rand();
int randmil = AI::randomValue<int>(0, std::numeric_limits<int>::max());
bool buildCatapult = randmil % 8 == 0 && aii.CanBuildCatapult()
&& bldPlanner.GetNumAdditionalBuildingsWanted(BuildingType::Catapult) > 0;
// another catapult within "min" radius? ->dont build here!
Expand Down
19 changes: 9 additions & 10 deletions libs/s25main/ai/aijh/AIPlayerJH.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "RttrForeachPt.h"
#include "addons/const_addons.h"
#include "ai/AIEvents.h"
#include "ai/random.h"
#include "boost/filesystem/fstream.hpp"
#include "buildings/noBuildingSite.h"
#include "buildings/nobHarborBuilding.h"
Expand Down Expand Up @@ -39,7 +40,6 @@
#include <algorithm>
#include <array>
#include <memory>
#include <random>
#include <stdexcept>
#include <type_traits>

Expand Down Expand Up @@ -344,7 +344,7 @@ void AIPlayerJH::PlanNewBuildings(const unsigned gf)
DistributeGoodsByBlocking(GoodType::Boards, 30);
DistributeGoodsByBlocking(GoodType::Stones, 50);
// go to the picked random warehouse and try to build around it
int randomStore = rand() % (storehouses.size());
int randomStore = AI::randomValue<int>(0, storehouses.size() - 1);
auto it = storehouses.begin();
std::advance(it, randomStore);
const MapPoint whPos = (*it)->GetPos();
Expand All @@ -365,7 +365,7 @@ void AIPlayerJH::PlanNewBuildings(const unsigned gf)
const std::list<nobMilitary*>& militaryBuildings = aii.GetMilitaryBuildings();
if(militaryBuildings.empty())
return;
int randomMiliBld = rand() % militaryBuildings.size();
int randomMiliBld = AI::randomValue<int>(0, militaryBuildings.size() - 1);
auto it2 = militaryBuildings.begin();
std::advance(it2, randomMiliBld);
MapPoint bldPos = (*it2)->GetPos();
Expand Down Expand Up @@ -1209,7 +1209,7 @@ void AIPlayerJH::HandleExpedition(const noShip* ship)
aii.FoundColony(ship);
else
{
const unsigned offset = rand() % helpers::MaxEnumValue_v<ShipDirection>;
const unsigned offset = AI::randomValue<unsigned>(0, helpers::MaxEnumValue_v<ShipDirection> - 1);
for(auto dir : helpers::EnumRange<ShipDirection>{})
{
dir = ShipDirection((rttr::enum_cast(dir) + offset) % helpers::MaxEnumValue_v<ShipDirection>);
Expand Down Expand Up @@ -1254,9 +1254,7 @@ void AIPlayerJH::HandleTreeChopped(const MapPoint pt)

UpdateNodesAround(pt, 3);

int random = rand();

if(random % 2 == 0)
if(AI::randomValue<int>(0, 1) == 0)
AddMilitaryBuildJob(pt);
else // if (random % 12 == 0)
AddBuildJob(BuildingType::Woodcutter, pt);
Expand Down Expand Up @@ -1538,7 +1536,7 @@ void AIPlayerJH::TryToAttack()
// We skip the current building with a probability of limit/numMilBlds
// -> For twice the number of blds as the limit we will most likely skip every 2nd building
// This way we check roughly (at most) limit buildings but avoid any preference for one building over an other
if(rand() % numMilBlds > limit)
if(AI::randomValue<unsigned>(0, numMilBlds - 1) > limit)
continue;

if(milBld->GetFrontierDistance() == FrontierDistance::Far) // inland building? -> skip it
Expand Down Expand Up @@ -1571,7 +1569,7 @@ void AIPlayerJH::TryToAttack()

// shuffle everything but headquarters and harbors without any troops in them
std::shuffle(potentialTargets.begin() + hq_or_harbor_without_soldiers, potentialTargets.end(),
std::mt19937(std::random_device()()));
std::mt19937(AI::randomValue<unsigned>(0, 2047)));

// check for each potential attacking target the number of available attacking soldiers
for(const nobBaseMilitary* target : potentialTargets)
Expand Down Expand Up @@ -1704,7 +1702,8 @@ void AIPlayerJH::TrySeaAttack()
unsigned limit = 15;
unsigned skip = 0;
if(searcharoundharborspots.size() > 15)
skip = std::max<int>(rand() % (searcharoundharborspots.size() / 15 + 1) * 15, 1) - 1;
skip =
std::max<int>(AI::randomValue<int>(0, static_cast<int>(searcharoundharborspots.size() / 15) * 15), 1) - 1;
for(unsigned i = skip; i < searcharoundharborspots.size() && limit > 0; i++)
{
limit--;
Expand Down
16 changes: 16 additions & 0 deletions libs/s25main/ai/random.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (C) 2005 - 2025 Settlers Freaks (sf-team at siedler25.org)
//
// SPDX-License-Identifier: GPL-2.0-or-later

#include "ai/random.h"

namespace AI {

std::minstd_rand& getRandomGenerator()
{
std::random_device rnd_dev;
static std::minstd_rand rng(rnd_dev());
return rng;
}

} // namespace AI
20 changes: 20 additions & 0 deletions libs/s25main/ai/random.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (C) 2005 - 2025 Settlers Freaks (sf-team at siedler25.org)
//
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "helpers/random.h"
#include <random>

namespace AI {

std::minstd_rand& getRandomGenerator();

template<typename T>
T randomValue(T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>::max())
{
return helpers::randomValue(getRandomGenerator(), min, max);
}

} // namespace AI
Loading