Fix an infinite loop in hints and better comment hint distribution (#4080)

This commit is contained in:
Pepper0ni 2024-05-07 18:48:00 +01:00 committed by GitHub
parent 8e520e490c
commit 980b1f1a6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -531,44 +531,40 @@ int32_t getRandomWeight(int32_t totalWeight){
} }
static void DistributeHints(std::vector<uint8_t>& selected, size_t stoneCount, std::vector<HintDistributionSetting> distTable, uint8_t junkWieght, bool addFixed = true){ static void DistributeHints(std::vector<uint8_t>& selected, size_t stoneCount, std::vector<HintDistributionSetting> 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){ for (HintDistributionSetting setting: distTable){ //Gather the wieghts of each distribution and, if it's the first pass, apply fixed hints
totalWeight += setting.weight; totalWeight += setting.weight; //Note that PlaceHints will set weights of distributions to zero if it can't place anything from them
if (addFixed){ if (addFixed){
selected[setting.type] += setting.fixed; selected[setting.type] += setting.fixed;
stoneCount -= setting.fixed * setting.copies; stoneCount -= setting.fixed * setting.copies;
} }
} }
int32_t currentWeight = getRandomWeight(totalWeight); int32_t currentWeight = getRandomWeight(totalWeight); //Initialise with the first random weight from 1 to the total.
while(stoneCount > 0 && totalWeight > 0){ 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++){ for (uint8_t distribution = 0; distribution < distTable.size(); distribution++){
currentWeight -= distTable[distribution].weight; currentWeight -= distTable[distribution].weight; //go over each distribution, subtracting the weight each time. Once we reach zero or less,
if (currentWeight <= 0){ if (currentWeight <= 0){ //tell the system to make 1 of that hint, unless not enough stones remain
if (distTable[distribution].copies == 0) { if (stoneCount >= distTable[distribution].copies && distTable[distribution].copies > 0){
// This should only happen if we ran out of locations to place hints of a certain distribution earlier. Skip selected[distribution] += 1; //if we have enough stones, and copies are not zero, assign 1 to this hint type, remove the stones, and break
// to the next distribution.
break;
}
if (stoneCount >= distTable[distribution].copies){
selected[distribution] += 1;
stoneCount -= distTable[distribution].copies; stoneCount -= distTable[distribution].copies;
break; break; //This leaves the whole for loop
} else { } 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; totalWeight -= distTable[distribution].weight; //Unlike PlaceHint, distTable is passed by value here, making this temporary
distTable[distribution].weight = 0; distTable[distribution].weight = 0; //this is so we can still roll this hint type if more stones free up later
break; break;
} }
} }
} }
//if there's still weight, then it's junk //if there's still weight then it's junk, as the leftover weight is junkWeight
if (currentWeight > 0){ 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; selected[selected.size()-1] += 1;
stoneCount -= 1; stoneCount -= 1;
} }
currentWeight = getRandomWeight(totalWeight); 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){ if (stoneCount > 0){
selected[selected.size()-1] += stoneCount; selected[selected.size()-1] += stoneCount;
} }
@ -586,10 +582,11 @@ uint8_t PlaceHints(std::vector<uint8_t>& selectedHints,
RandomizerCheck hintedLocation = RC_UNKNOWN_CHECK; RandomizerCheck hintedLocation = RC_UNKNOWN_CHECK;
hintedLocation = CreateRandomHint(hintTypePool, distribution.copies, distribution.type, distribution.name); 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; uint8_t hintsToRemove = (selectedHints[curSlot] - numHint) * distribution.copies;
selectedHints[curSlot] = 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; distTable[curSlot].copies = 0;//and prevent future distribution from choosing this slot
distTable[curSlot].weight = 0;
return hintsToRemove; return hintsToRemove;
} }
if(Rando::StaticData::GetLocation(hintedLocation)->IsDungeon()){ if(Rando::StaticData::GetLocation(hintedLocation)->IsDungeon()){
@ -661,9 +658,8 @@ void CreateStoneHints() {
while(totalStones != 0){ while(totalStones != 0){
totalStones = PlaceHints(selectedHints, distTable); totalStones = PlaceHints(selectedHints, distTable);
while (totalStones != 0){ if (totalStones != 0){
DistributeHints(selectedHints, totalStones, distTable, hintSetting.junkWeight, false); DistributeHints(selectedHints, totalStones, distTable, hintSetting.junkWeight, false);
totalStones = PlaceHints(selectedHints, distTable);
} }
} }