From 980b1f1a6e5451459970be6974f6b9847abe9596 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Tue, 7 May 2024 18:48:00 +0100 Subject: [PATCH] Fix an infinite loop in hints and better comment hint distribution (#4080) --- .../Enhancements/randomizer/3drando/hints.cpp | 48 +++++++++---------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/hints.cpp b/soh/soh/Enhancements/randomizer/3drando/hints.cpp index 1c1edd62d..5973fdcee 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hints.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hints.cpp @@ -531,44 +531,40 @@ int32_t getRandomWeight(int32_t totalWeight){ } static void DistributeHints(std::vector& selected, size_t stoneCount, std::vector distTable, uint8_t junkWieght, bool addFixed = true){ - int32_t totalWeight = junkWieght; + int32_t totalWeight = junkWieght; //Start with our Junk Weight, the natural chance of a junk hint - for (HintDistributionSetting setting: distTable){ - totalWeight += setting.weight; + for (HintDistributionSetting setting: distTable){ //Gather the wieghts of each distribution and, if it's the first pass, apply fixed hints + totalWeight += setting.weight; //Note that PlaceHints will set weights of distributions to zero if it can't place anything from them if (addFixed){ selected[setting.type] += setting.fixed; stoneCount -= setting.fixed * setting.copies; } } - int32_t currentWeight = getRandomWeight(totalWeight); - while(stoneCount > 0 && totalWeight > 0){ + int32_t currentWeight = getRandomWeight(totalWeight); //Initialise with the first random weight from 1 to the total. + while(stoneCount > 0 && totalWeight > 0){//Loop until we run out of stones or have no TotalWeight. 0 totalWeight means junkWeight is 0 + //and that all weights have been 0'd out for another reason, and skips to placing all junk hints for (uint8_t distribution = 0; distribution < distTable.size(); distribution++){ - currentWeight -= distTable[distribution].weight; - if (currentWeight <= 0){ - if (distTable[distribution].copies == 0) { - // This should only happen if we ran out of locations to place hints of a certain distribution earlier. Skip - // to the next distribution. - break; - } - if (stoneCount >= distTable[distribution].copies){ - selected[distribution] += 1; + currentWeight -= distTable[distribution].weight; //go over each distribution, subtracting the weight each time. Once we reach zero or less, + if (currentWeight <= 0){ //tell the system to make 1 of that hint, unless not enough stones remain + if (stoneCount >= distTable[distribution].copies && distTable[distribution].copies > 0){ + selected[distribution] += 1; //if we have enough stones, and copies are not zero, assign 1 to this hint type, remove the stones, and break stoneCount -= distTable[distribution].copies; - break; - } else { - totalWeight -= distTable[distribution].weight; - distTable[distribution].weight = 0; + break; //This leaves the whole for loop + } else { //If we don't have the stones, or copies is 0 despite there being the wieght to trigger a hit, temporerally set wieght to zero + totalWeight -= distTable[distribution].weight; //Unlike PlaceHint, distTable is passed by value here, making this temporary + distTable[distribution].weight = 0; //this is so we can still roll this hint type if more stones free up later break; } } } - //if there's still weight, then it's junk - if (currentWeight > 0){ + //if there's still weight then it's junk, as the leftover weight is junkWeight + if (currentWeight > 0){ //zero TotalWeight breaks the while loop and hits the fallback, so skipping this is fine in that case selected[selected.size()-1] += 1; stoneCount -= 1; } currentWeight = getRandomWeight(totalWeight); } - //if stones are left, assign junk + //if stones are left, assign junk to every remaining stone as a fallback. if (stoneCount > 0){ selected[selected.size()-1] += stoneCount; } @@ -586,10 +582,11 @@ uint8_t PlaceHints(std::vector& selectedHints, RandomizerCheck hintedLocation = RC_UNKNOWN_CHECK; hintedLocation = CreateRandomHint(hintTypePool, distribution.copies, distribution.type, distribution.name); - if (hintedLocation == RC_UNKNOWN_CHECK){ //if hint failed to place + if (hintedLocation == RC_UNKNOWN_CHECK){ //if hint failed to place, remove all wieght and copies then return the number of stones to redistribute uint8_t hintsToRemove = (selectedHints[curSlot] - numHint) * distribution.copies; - selectedHints[curSlot] = 0; - distTable[curSlot].copies = 0; + selectedHints[curSlot] = 0; //as distTable is passed by refernce here, these changes stick for the rest of this seed generation + distTable[curSlot].copies = 0;//and prevent future distribution from choosing this slot + distTable[curSlot].weight = 0; return hintsToRemove; } if(Rando::StaticData::GetLocation(hintedLocation)->IsDungeon()){ @@ -661,9 +658,8 @@ void CreateStoneHints() { while(totalStones != 0){ totalStones = PlaceHints(selectedHints, distTable); - while (totalStones != 0){ + if (totalStones != 0){ DistributeHints(selectedHints, totalStones, distTable, hintSetting.junkWeight, false); - totalStones = PlaceHints(selectedHints, distTable); } }