Browse Source

Introduce temporary function to advance the global rng state and discard results

pull/6094/head
ephphatha 3 years ago committed by Anders Jenbo
parent
commit
3bbb80d849
  1. 8
      Source/engine/random.cpp
  2. 10
      Source/engine/random.hpp
  3. 31
      Source/items.cpp
  4. 12
      Source/levels/drlg_l1.cpp
  5. 2
      Source/levels/drlg_l3.cpp
  6. 4
      Source/levels/drlg_l4.cpp
  7. 2
      Source/levels/themes.cpp
  8. 2
      Source/objects.cpp
  9. 4
      test/random_test.cpp

8
Source/engine/random.cpp

@ -29,6 +29,14 @@ uint32_t GetLCGEngineState()
return sglGameSeed;
}
void DiscardRandomValues(unsigned count)
{
while (count != 0) {
GenerateSeed();
count--;
}
}
uint32_t GenerateSeed()
{
sglGameSeed = (RndMult * sglGameSeed) + RndInc;

10
Source/engine/random.hpp

@ -29,6 +29,14 @@ void SetRndSeed(uint32_t seed);
*/
uint32_t GetLCGEngineState();
/**
* @brief Advance the global RandomNumberEngine state by the specified number of rounds
*
* Only used to maintain vanilla compatibility until logic requiring reproducable random number generation is isolated.
* @param count How many values to discard
*/
void DiscardRandomValues(unsigned count);
/**
* @brief Advances the global RandomNumberEngine state and returns the new value
*/
@ -46,7 +54,7 @@ uint32_t GenerateSeed();
*
* @return A random number in the range [0,2^31) or -2^31
*/
int32_t AdvanceRndSeed();
[[nodiscard]] int32_t AdvanceRndSeed();
/**
* @brief Generates a random integer less than the given limit using the vanilla RNG

31
Source/items.cpp

@ -1464,7 +1464,7 @@ _unique_items CheckUnique(Item &item, int lvl, int uper, bool recreate)
if (numu == 0)
return UITEM_INVALID;
AdvanceRndSeed();
DiscardRandomValues(1);
uint8_t itemData = 0;
while (numu > 0) {
if (uok[itemData])
@ -2156,7 +2156,7 @@ void RecreateWitchItem(const Player &player, Item &item, _item_indexes idx, int
GetItemAttrs(item, idx, lvl);
} else if (gbIsHellfire && idx >= 114 && idx <= 117) {
SetRndSeed(iseed);
AdvanceRndSeed();
DiscardRandomValues(1);
GetItemAttrs(item, idx, lvl);
} else {
SetRndSeed(iseed);
@ -2299,18 +2299,15 @@ std::string GetTranslatedItemNameMagical(const Item &item, bool hellfireItem, bo
int minlvl;
int maxlvl;
if ((item._iCreateInfo & CF_SMITHPREMIUM) != 0) {
AdvanceRndSeed(); // RndVendorItem
AdvanceRndSeed(); // GetItemAttrs
DiscardRandomValues(2); // RndVendorItem and GetItemAttrs
minlvl = lvl / 2;
maxlvl = lvl;
} else if ((item._iCreateInfo & CF_BOY) != 0) {
AdvanceRndSeed(); // RndVendorItem
AdvanceRndSeed(); // GetItemAttrs
DiscardRandomValues(2); // RndVendorItem and GetItemAttrs
minlvl = lvl;
maxlvl = lvl * 2;
} else if ((item._iCreateInfo & CF_WITCH) != 0) {
AdvanceRndSeed(); // RndVendorItem
AdvanceRndSeed(); // GetItemAttrs
DiscardRandomValues(2); // RndVendorItem and GetItemAttrs
int iblvl = -1;
if (GenerateRnd(100) <= 5)
iblvl = 2 * lvl;
@ -2319,11 +2316,11 @@ std::string GetTranslatedItemNameMagical(const Item &item, bool hellfireItem, bo
minlvl = iblvl / 2;
maxlvl = iblvl;
} else {
AdvanceRndSeed(); // GetItemAttrs
DiscardRandomValues(1); // GetItemAttrs
int iblvl = GetItemBLevel(lvl, item._iMiscId, onlygood, item._iCreateInfo & CF_UPER15);
minlvl = iblvl / 2;
maxlvl = iblvl;
AdvanceRndSeed(); // CheckUnique
DiscardRandomValues(1); // CheckUnique
}
if (minlvl > 25)
@ -2357,8 +2354,7 @@ std::string GetTranslatedItemNameMagical(const Item &item, bool hellfireItem, bo
else if (!hellfireItem && FlipCoin(4)) {
affixItemType = AffixItemType::Staff;
} else {
AdvanceRndSeed(); // Spell
AdvanceRndSeed(); // Charges
DiscardRandomValues(2); // Spell and Charges
int preidx = GetStaffPrefixId(maxlvl, onlygood, hellfireItem);
if (preidx == -1 || item._iSpell == SpellID::Null) {
@ -2394,14 +2390,13 @@ std::string GetTranslatedItemNameMagical(const Item &item, bool hellfireItem, bo
[&pPrefix](const PLStruct &prefix) {
pPrefix = &prefix;
// GenerateRnd(prefix.power.param2 - prefix.power.param2 + 1)
AdvanceRndSeed();
DiscardRandomValues(1);
switch (pPrefix->power.type) {
case IPL_TOHIT_DAMP:
AdvanceRndSeed();
AdvanceRndSeed();
DiscardRandomValues(2);
break;
case IPL_TOHIT_DAMP_CURSE:
AdvanceRndSeed();
DiscardRandomValues(1);
break;
default:
break;
@ -2503,7 +2498,7 @@ void InitItems()
}
if (!setlevel) {
AdvanceRndSeed(); /* unused */
DiscardRandomValues(1);
if (Quests[Q_ROCK].IsAvailable())
SpawnRock();
if (Quests[Q_ANVIL].IsAvailable())
@ -4251,7 +4246,7 @@ void SpawnWitch(int lvl)
if (lvl >= AllItemsList[bookType].iMinMLvl) {
item._iSeed = AdvanceRndSeed();
SetRndSeed(item._iSeed);
AdvanceRndSeed();
DiscardRandomValues(1);
GetItemAttrs(item, bookType, lvl);
item._iCreateInfo = lvl | CF_WITCH;
item._iIdentified = true;

12
Source/levels/drlg_l1.cpp

@ -701,42 +701,42 @@ void AddWall()
continue;
if (dungeon[i][j] == Corner) {
AdvanceRndSeed();
DiscardRandomValues(1);
int maxX = HorizontalWallOk({ i, j });
if (maxX != -1) {
HorizontalWall({ i, j }, HWall, maxX);
}
}
if (dungeon[i][j] == Corner) {
AdvanceRndSeed();
DiscardRandomValues(1);
int maxY = VerticalWallOk({ i, j });
if (maxY != -1) {
VerticalWall({ i, j }, VWall, maxY);
}
}
if (dungeon[i][j] == VWallEnd) {
AdvanceRndSeed();
DiscardRandomValues(1);
int maxX = HorizontalWallOk({ i, j });
if (maxX != -1) {
HorizontalWall({ i, j }, DWall, maxX);
}
}
if (dungeon[i][j] == HWallEnd) {
AdvanceRndSeed();
DiscardRandomValues(1);
int maxY = VerticalWallOk({ i, j });
if (maxY != -1) {
VerticalWall({ i, j }, DWall, maxY);
}
}
if (dungeon[i][j] == HWall) {
AdvanceRndSeed();
DiscardRandomValues(1);
int maxX = HorizontalWallOk({ i, j });
if (maxX != -1) {
HorizontalWall({ i, j }, HWall, maxX);
}
}
if (dungeon[i][j] == VWall) {
AdvanceRndSeed();
DiscardRandomValues(1);
int maxY = VerticalWallOk({ i, j });
if (maxY != -1) {
VerticalWall({ i, j }, VWall, maxY);

2
Source/levels/drlg_l3.cpp

@ -1683,7 +1683,7 @@ void Fence()
for (WorldTileCoord j = 1; j < DMAXY; j++) { // BUGFIX: Change '0' to '1' (fixed)
for (WorldTileCoord i = 1; i < DMAXX; i++) { // BUGFIX: Change '0' to '1' (fixed)
// note the comma operator is used here to advance the RNG state
if (dungeon[i][j] == 7 && (AdvanceRndSeed(), !IsNearThemeRoom({ i, j }))) {
if (dungeon[i][j] == 7 && (DiscardRandomValues(1), !IsNearThemeRoom({ i, j }))) {
if (FlipCoin()) {
int y1 = j;
// BUGFIX: Check `y1 >= 0` first (fixed)

4
Source/levels/drlg_l4.cpp

@ -446,7 +446,7 @@ void AddWall()
}
for (auto d : { 10, 12, 13, 15, 16, 21, 22 }) {
if (d == dungeon[i][j]) {
AdvanceRndSeed();
DiscardRandomValues(1);
int x = HorizontalWallOk(i, j);
if (x != -1) {
HorizontalWall(i, j, x);
@ -455,7 +455,7 @@ void AddWall()
}
for (auto d : { 8, 9, 11, 14, 15, 16, 21, 23 }) {
if (d == dungeon[i][j]) {
AdvanceRndSeed();
DiscardRandomValues(1);
int y = VerticalWallOk(i, j);
if (y != -1) {
VerticalWall(i, j, y);

2
Source/levels/themes.cpp

@ -514,7 +514,7 @@ void Theme_Treasure(int t)
int treasrnd[4] = { 4, 9, 7, 10 };
int monstrnd[4] = { 6, 8, 3, 7 };
AdvanceRndSeed();
DiscardRandomValues(1);
for (int yp = 0; yp < MAXDUNY; yp++) {
for (int xp = 0; xp < MAXDUNX; xp++) {
if (dTransVal[xp][yp] == themes[t].ttval && IsTileNotSolid({ xp, yp })) {

2
Source/objects.cpp

@ -3869,7 +3869,7 @@ void InitObjects()
if (currlevel == 16) {
AddDiabObjs();
} else {
AdvanceRndSeed();
DiscardRandomValues(1);
if (currlevel == 9 && !UseMultiplayerQuests())
AddSlainHero();
if (Quests[Q_MUSHROOM].IsAvailable())

4
test/random_test.cpp

@ -25,8 +25,8 @@ TEST(RandomTest, RandomEngineParams)
// C++11 defines the default seed for a LCG engine as 1. The ten thousandth value is commonly used for sanity checking
// a sequence, so as we've had one round since state 1 we need to discard another 9998 values to get to the 10000th state.
// To make off by one errors more visible test the 9999th value as well as 10000th
for (auto i = 2; i <= 9998; i++)
GenerateSeed();
DiscardRandomValues(9997);
uint32_t expectedState = 3495122800U;
EXPECT_EQ(GenerateSeed(), expectedState) << "Wrong engine state after 9999 invocations";
expectedState = 3007658545U;

Loading…
Cancel
Save