This post is about an idle experience that I released recently called “Happy”. If you haven’t played it yet I suggest you do so now before I spoil it for you. The following post is about math (and random numbers) but it should be approachable for everyone…
The Setup
Happy is a metaphor about as delicate as a sledgehammer, in part because I am not a writer and also because I like things being finished. The experience asks the question “How long are you willing to wait to be truly happy?” In short the answer is “Never long enough.” Aside from the experience being cruel and never letting you get to 100% happiness without some user intervention the experience throws up “events” at you that if you do not respond to drop your happiness level down by a random amount. This is basically an anti-idle protection, if you’re unable to check out “Happy” you can see the event in the image below.
The Problem
I’m not a mathemagician but I wanted a graph that felt nice to watch increase but was also deceptively slow. Not feeling confident to do this myself I hit up a good friend and certified mathemagician @MrJohnRowe. I sent him the following picture saying that in addition to my graph tending toward a limit I also wanted to be able to change the slope. For each missed event I wanted to change how fast the graph grew to subtly slow down player progress so the changing slope was fairly important.
He helpfully came back with the following equation…
My experience was already complete pending the “proper” equation so I swapped in this new one, prematurely patted myself on the back and got ready to release it to the world.
A very quick play-through saw a pretty significant problem, players weren’t losing any happiness! They HAD to lose happiness or my blunt metaphor would start to become concave, a proverbial metaphor “innie.”(I’m drawing a pretty long string here but I assume the more pointed the metaphor the better).
I poured over the code for an hour, which was a long time given it did only one thing. It generated a new random x value essentially having the player lose happiness by altering their elapsed idle time. So why wasn’t it working?
After some head scratching and looking at the equation above on Desmos I quickly came to an epiphany. There are only a small range of valid x values for which a significant change to y occurs, in this case the very steep bit of my graph near the start. If a player had been idling for a long time that flat section of the graph would make up for the majority of the x values to choose from and most of them would make a negligible impact on the experience. Of course, random being random, I must have got a lot of these values during testing. Thus the problem…
Random is terrible.
There’s a lot of writing about why relying on random feels bad so I don’t want to harp on it too much but this is a fantastic concrete use case where a trivial implementation of some (fairly basic) math very quickly led to bad results.
The Solve
What we really want is a new value on the y axis (a.k.a happiness)! Now that’s all well and good but I only have my equation in the form of f(x). So when I generate a new random y value how do I set my x value (elapsed time) appropriately? An hour with Google, some YouTube videos and Khan Academy and I was pretty refreshed on my high school level algebra and was able to solve the equation inthe form of f(y) instead of f(x). The result is below.
By using random over y axis values, being my current happiness level down to zero respectively, I was able to have a much stronger effect on the user. Mostly because I was always picking a value somewhere on the steep part of my slope.
You could argue that in this instance my implementation of random is still naive. And I would give you that, I still just call random over a given range. But I call it over the correct range and that is really important. Knowing your limits when calculating random values has a huge effect on how people perceive the fairness of your experience and their willingness to continue to engage with it.
Conclusion
If you are going to use random then do so sparingly and test it often to see how it feels, where possible pick an algorithm that works for you instead of just relying on your languages in-built Random class (like I did). If you’re new to using Random and want to know how it might effect your experiences, or games, I suggest you look into resources that might help you better understand how random numbers can work for you and against you. I’d suggest looking into Weighted Random Numbers, and the idea of Pre-Luck and Post-Luck (or Pre-Random and Post-Random).