Perfecting Perfection in Isleward
Preface
This post is about Isleward. If you know what that is already, continue ahead! If not: Isleward is an open-source roguelike MMO that you can play in your browser. It has a small but friendly playerbase. Due to the open nature of the client, the community has developed many client-side addons which are userscripts that extend or modify client functionality, such as calculating and displaying extra info about items, which usually have randomly generated stats. As you might be able to guess, this post is about one such calculation. I maintain an addon called Waddon, which will be referenced later. Everything else should be pretty straightforward. Enjoy!
Striving for Perfection
In Isleward, items roll stats, and those stats roll values between a set minimum and maximum in approximately1 a normal distribution. This roll is then rounded to an integer, and if it’s 0, it’s set to 1. Most stats have a minimum roll of 1, but some have higher minimums. Also, the same stat can be rolled multiple times, but all the rolled values are stored together as a single number. We’ll see shortly that this turns out to be mildly annoying.
Given an item’s stat values, we might want to calculate how good its random rolls were in order to easily compare it with other items. To do this, we want to turn each stat’s absolute value, like 7 strength, into a normalized value from 0 to 1, which we might also display as a percentage. We’ll call this value the stat roll’s “perfection”.
If all stats had a minimum of 0, perfection could easily be calculated as roll/max
. Let’s pretend our stat has a max of 10. Then a roll of 10 is 100% perfect, a roll of 6 is 60% perfect, a roll of 0 is 0% perfect, and so on. Unfortunately, stats aren’t allowed to be 0, so every stat has a minimum of at least 1. Because of this, the simple method (as we’ll call it) isn’t entirely correct anymore. A roll of 1 should be 0%, since a roll of 0 is impossible. Even worse, balance changes in the v0.16 update increased the minimum rolls of a bunch of stats. Crit mult now rolls between 9 and 18 at max level. A minimum roll of 9 will have 50% perfection, and a mean roll of 13.5 will have 75% perfection. On average, your rolls will seem more perfect than they really are.
Instead, let’s get smart. Let’s shift the ranges down, and make sure the minimum roll is 0%, and the maximum roll stays 100%. We can achieve this by calculating perfection as (roll-min)/(max-min)
. Now 9 crit mult is 0%, 13.5 is 50%, and 18 is 100%. Yay! We solved our problems. Waddon has been displaying stat perfection in tooltips using this formula for years now.
But wait. If you play Isleward, you know we’re here to talk about stacked rolls, where we rolled the same stat more than once. Let’s look at some examples and see if everything still works. Suppose our stat rolls between 5 and 10, and we get the minimum roll twice, 5+5. The rolls are stored together, so the player only sees a roll of 10 and a perfection of 100%. It could just be a single max roll. But you’d notice that your item which should have 5 rolls only has 4 different stats, so one stat must have rolled low twice, resulting in a total within the single roll range. What if we got the maximum roll twice? We roll 10+10=20, and then calculate a perfection of… 300%? That’s misleading! We rolled 100%+100%, so we would expect to see 200%. And that’s why we’re here. The recent balance changes are what prompted me to revisit this, because higher minimums make the problems much more obvious.
This could have been avoided if each stat roll was stored separately, instead of the totals. Then each roll’s perfection could be displayed individually. I opened an issue in September 2021 asking about this, and a feature request for displaying roll perfection was first opened on June 25, 2017. I commented on the issue way back in November 2021, after working on the feature in Waddon, explaining pretty much the same thing as I just did above. I’ve also talked to Waf directly about it, but at this point it would be very annoying to change.
The Enemy of Good
So what can we do about it right now? Maybe we should just go back to the simple method. A minimum roll of 9 crit mult is back to 50% instead of 0%, so we’ll need to be aware that perfection skews high for single rolls of stats with high minimums, but at least two maximum rolls is 18+18=36=200% like we expected. It seems like most people are using the simple method for the idea of “item tier” which recently appeared in the community. As far as I can tell, an item’s tier is the sum of perfections of useful stats on an item. So a tier 2.5 item has 2.5 max rolls of useful stats. Which stats are considered useful depends on the player, and some players also weight certain stats lower, so it’s not entirely standard, but it seems like a useful approximation.
A few people have also suggested a hybrid approach, where we default to the smart method, but switch to the simple method when the value is above the single-roll max. This method combines the correctness of the smart method for single rolls while avoiding misleading results for stacked rolls, and is continuous (there’s no jump in perfection when the value slightly exceeds the maximum) which is good. Values below the minimum are a little strange (either negative with the smart method, or discontinuous at the minimum with the simple method), but unlikely to actually occur2, so we won’t worry about them.
In general though, it still doesn’t feel entirely correct to me. For example, consider a level 20 item with 40 crit mult, which rolls from 8-16 at level 20. We get 400% with the smart method and 250% with the simple or hybrid method. 400% is too high, since 4 max rolls should be 64. But if we interpret 250% as two max rolls and a 50% roll, we would expect 16+16+12=44. Again, since the rolls aren’t stored separately, we can’t be fully correct, and our intuitions about how perfections work aren’t really correct either. But it’s a useful tool, and the hybrid formula might be closer to what users expect which is what really matters. As of Waddon v3.4.11 (released 2025-02-19), stat perfection in tooltips now uses the hybrid method. I’m waiting to hear from users whether it’s better or not.
Fated Reroll
These calculations are also necessary for the newly added Fated Reroll mechanic. You can view the code at the time of writing here. Performing a Fated Reroll chooses a random stat on an item and rerolls it, potentially rolling the new stat multiple times if the removed stat was already stacked. To do this, it calculates the perfection of the removed stat using the smart method, so note that stacked values might have higher perfections than we would expect. Then a chain of conditions involving the perfection and random chance determine how many rolls of the new stat to add:
- If the perfection is greater than 440%, there is a 50% chance to receive 5 rolls.
- Otherwise, if the perfection is greater than 320%, there is a 65% chance to receive 4 rolls.
- Otherwise, if the perfection is greater than or equal to 160%, you receive 3 rolls.
- Otherwise, if the perfection is greater than or equal to 90%, you receive 2 rolls.
- Finally, there is a base 5% chance to receive 2 rolls, otherwise you receive 1 roll.
These values are somewhat arbitrary, but were chosen for balance reasons. The initial implementation of Fated Reroll worked differently and it was too easy to get very high stacked rolls. On average though, this version of Fated Reroll favors rolls with high minimum values, which includes crit stats after the v0.16 balance changes. Consider items with double stacked crit mult, which rolls from 9 to 18 at max level. The average double stacked roll is 13.5+13.5=27, or 200% perfection using the smart method. That means you’ll receive 3 rolls. But consider items with double stacked main stats instead, which roll from 1 to 13 at max level. The average double stacked roll is then 7+7=14, or ~108% perfection using the smart method, which only gets 2 rolls. Since Fated Reroll uses the smart method, removing stacked stats that have high minimums tends to result in higher perfections and thus produces more rolls. Make use of that while you can I guess.
Conclusion
Hopefully this will be the last time I write an explanation of this topic3. If you play Isleward, go forth and calculate your perfections perfectly (enough). If you don’t play Isleward, I hope it was still interesting for you. Thanks to Satan and Fatey from the Isleward Discord for their suggestions, and to everyone else who was confused by Waddon’s old formula. If you use Waddon, please update to v3.4.11+ to get the improved formula. If I missed anything here, or if you have a suggestion for a better formula, you can find me in the Isleward Discord or contact me via email. Thanks for reading!
Footnotes
-
For most purposes, “normal distribution” evokes the right idea, but let’s go through exactly how it works. If you want to verify for yourself, the relevant code is in
src/server/misc/random.js
. Most of the stat generators use thenorm
function from this file, except implicit stats and fishing rod stats which useexpNorm
instead.norm
takes a min and max and returns a random number between them. It starts by calling the minifiedRandom.normal
function, which uses the Box–Muller Transform to generate normally-distributed random numbers, in this case, with mean 0 and standard deviation 0.35. I’m not great at probability/statistics, but this calculator tells me that 99.572% of the returned values will fall between -1 and 1, andnorm
clamps4 everything outside that range to be in [-1, 1]. Then this range is mapped to the provided min and max. So the shape of the distribution is close to the standard normal distribution, but with the clamping and scaling and shifting, it’s technically not. ↩ -
Except when balance changes increase the minimum rolls, and old items which generated with the old stat ranges are left with values outside the current ranges. Which is happening right now with the v0.16 changes. ↩
-
Unless I make some sort of interactive visualization… ↩
-
While writing this post, I went on a side quest learning about probability distributions to try to understand the distribution of stacked rolls (I don’t have much to say for now, but I will say that the general intuition around min/max rolls is not entirely correct, since the ranges before rounding usually involve decimals, so max rolls for certain stats are harder to get than others). By clamping the random variable, we no longer have a normal distribution, but we can still write the probability density function as a combination of three components. On one blog, I saw this idea referred to as “clipping”. I also saw it referred to elsewhere as “censoring”. Neither of these terms seem very common though. Also,
expNorm
mentioned above is close to being a folded normal distribution. ↩