Browse Source

Implemented persistent random seed for hero pool

Ivan Savenko 2 years ago
parent
commit
539c508870
4 changed files with 32 additions and 13 deletions
  1. 5 0
      lib/CRandomGenerator.cpp
  2. 3 0
      lib/CRandomGenerator.h
  3. 16 11
      server/HeroPoolProcessor.cpp
  4. 8 2
      server/HeroPoolProcessor.h

+ 5 - 0
lib/CRandomGenerator.cpp

@@ -20,6 +20,11 @@ CRandomGenerator::CRandomGenerator()
 	resetSeed();
 }
 
+CRandomGenerator::CRandomGenerator(int seed)
+{
+	setSeed(seed);
+}
+
 void CRandomGenerator::setSeed(int seed)
 {
 	rand.seed(seed);

+ 3 - 0
lib/CRandomGenerator.h

@@ -30,6 +30,9 @@ public:
 	/// current thread ID.
 	CRandomGenerator();
 
+	/// Seeds the generator with provided initial seed
+	explicit CRandomGenerator(int seed);
+
 	void setSeed(int seed);
 
 	/// Resets the seed to the product of the current time in milliseconds and the

+ 16 - 11
server/HeroPoolProcessor.cpp

@@ -68,7 +68,7 @@ void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHe
 	sah.slotID = static_cast<int>(slot);
 
 	//first hero - native if possible, second hero -> any other class
-	CGHeroInstance *h = pickHeroFor(needNativeHero, color, gameHandler->getPlayerSettings(color)->castle, gameHandler->getRandomGenerator(), nullptr);
+	CGHeroInstance *h = pickHeroFor(needNativeHero, color);
 
 	if (h)
 	{
@@ -76,7 +76,7 @@ void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHe
 
 		if (giveArmy)
 		{
-			h->initArmy(gameHandler->getRandomGenerator(), &sah.army);
+			h->initArmy(getRandomGenerator(color), &sah.army);
 		}
 		else
 		{
@@ -212,7 +212,7 @@ std::set<CGHeroInstance *> HeroPoolProcessor::findAvailableHeroesFor(const Playe
 	return result;
 }
 
-const CHeroClass * HeroPoolProcessor::pickClassFor(bool isNative, const PlayerColor & player, const FactionID & factionID, CRandomGenerator & rand) const
+const CHeroClass * HeroPoolProcessor::pickClassFor(bool isNative, const PlayerColor & player)
 {
 	if(player >= PlayerColor::PLAYER_LIMIT)
 	{
@@ -220,6 +220,7 @@ const CHeroClass * HeroPoolProcessor::pickClassFor(bool isNative, const PlayerCo
 		return nullptr;
 	}
 
+	FactionID factionID = gameHandler->getPlayerSettings(player)->castle;
 	const auto & hpool = gameHandler->gameState()->hpool;
 	const auto & currentTavern = hpool->getHeroesFor(player);
 
@@ -257,7 +258,7 @@ const CHeroClass * HeroPoolProcessor::pickClassFor(bool isNative, const PlayerCo
 	for(const auto & heroClass : possibleClasses)
 		totalWeight += heroClass->selectionProbability.at(factionID);
 
-	int roll = rand.nextInt(totalWeight - 1);
+	int roll = getRandomGenerator(player).nextInt(totalWeight - 1);
 	for(const auto & heroClass : possibleClasses)
 	{
 		roll -= heroClass->selectionProbability.at(factionID);
@@ -268,13 +269,9 @@ const CHeroClass * HeroPoolProcessor::pickClassFor(bool isNative, const PlayerCo
 	return *possibleClasses.rbegin();
 }
 
-CGHeroInstance * HeroPoolProcessor::pickHeroFor(bool isNative,
-													 const PlayerColor & player,
-													 const FactionID & factionID,
-													 CRandomGenerator & rand,
-													 const CHeroClass * bannedClass) const
+CGHeroInstance * HeroPoolProcessor::pickHeroFor(bool isNative, const PlayerColor & player)
 {
-	const CHeroClass * heroClass = pickClassFor(isNative, player, factionID, rand);
+	const CHeroClass * heroClass = pickClassFor(isNative, player);
 
 	if(!heroClass)
 		return nullptr;
@@ -285,5 +282,13 @@ CGHeroInstance * HeroPoolProcessor::pickHeroFor(bool isNative,
 	if(possibleHeroes.empty())
 		return nullptr;
 
-	return *RandomGeneratorUtil::nextItem(possibleHeroes, rand);
+	return *RandomGeneratorUtil::nextItem(possibleHeroes, getRandomGenerator(player));
+}
+
+CRandomGenerator & HeroPoolProcessor::getRandomGenerator(const PlayerColor & player)
+{
+	if (playerSeed.count(player) == 0)
+		playerSeed.emplace(player, CRandomGenerator(gameHandler->getRandomGenerator().nextInt()));
+
+	return playerSeed.at(player);
 }

+ 8 - 2
server/HeroPoolProcessor.h

@@ -28,15 +28,20 @@ class HeroPoolProcessor : boost::noncopyable
 {
 	CGameHandler * gameHandler;
 
+	/// per-player random generators
+	std::map<PlayerColor, CRandomGenerator> playerSeed;
+
 	void clearHeroFromSlot(const PlayerColor & color, TavernHeroSlot slot);
 	void selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot, bool needNativeHero, bool giveStartingArmy);
 
 	std::set<const CHeroClass *> findAvailableClassesFor(const PlayerColor & player) const;
 	std::set<CGHeroInstance *> findAvailableHeroesFor(const PlayerColor & player, const CHeroClass * heroClass) const;
 
-	const CHeroClass * pickClassFor(bool isNative, const PlayerColor & player, const FactionID & faction, CRandomGenerator & rand) const;
+	const CHeroClass * pickClassFor(bool isNative, const PlayerColor & player);
+
+	CGHeroInstance * pickHeroFor(bool isNative, const PlayerColor & player);
 
-	CGHeroInstance * pickHeroFor(bool isNative, const PlayerColor & player, const FactionID & faction, CRandomGenerator & rand, const CHeroClass * bannedClass) const;
+	CRandomGenerator & getRandomGenerator(const PlayerColor & player);
 public:
 	HeroPoolProcessor();
 	HeroPoolProcessor(CGameHandler * gameHandler);
@@ -51,5 +56,6 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & gameHandler;
+		h & playerSeed;
 	}
 };