Developing Randomness in our Game: Dynamic Scaling and Relative Probability

Introduction to Randomness in Games

A brief introduction to randomness in games comes from looking at games without it. Chess is the most common example of this where every move can theoretically be calculated and optimized based on the opponent’s actions. This means that the game itself can be deterministic, as in who wins or loses is known before the game is actually played. Fortunately Chess is complex enough that humans struggle to fully enact this, but that’s why computers are often better than humans, because they can compute all the possible outcomes of each move and pick their move based on the sequence that allows them to win regardless of their opponent’s actions. Tic-Tac-Toe is a much more trivial version of this where if both players are playing optimally then the game always ends in a draw.

Situations where the end is known before the game starts tend to not be fun and people tend not to play them for long. This is why randomness is important. Even the slightest bit of randomness ensures that the outcome of a given game isn’t deterministic and therefore everyone involved has a chance at achieving the desired outcome regardless of skill level. This chance is beneficial in both winning and losing because it ensures that everyone has a chance to win, but also ensures that there’s risk of losing so that even skilled players have a reason to play and make an effort to win the game.

Randomness in Our Game

Our game is a tower defense where the player is fighting against an AI. Since it’s a single player game, there isn’t so much a focus on leveling the playing field as we generally want the player to win in this scenario or to at least feel like they had a fighting chance. In this case the major concern is that the player will figure out a strategy that will allow them to effectively ‘solve’ the game. This could potentially be a tower that is way stronger than the others, or a particularly effective combination that when replicated always defeats the enemy AI. Once they’ve found this particular strategy then the rest of the game becomes boring because it’s either not stimulating enough to force them to think or it becomes so trivial that they don’t even really need to make an effort to win the game. Our solution to this issue was to introduce randomness in the form of tower availability.

Our game will have a shop with a limited inventory where the player must purchase their towers. The current plan is to have it cycle on every wave and bring in a new inventory of options to purchase. This forces players to utilize the tools they have available to them through the shop, meaning that very rarely will they ever have the same set of options in a given level or even wave. While they can still have favorites that they prioritize through this system, it removes their ability to repeat the same strategy over and over, forcing them to think critically at every stage of gameplay and ensuring that repeated attempts at a given level will produce different results. The randomness also adds an extra avenue for strategic planning in that players will have to consider whether or not it’s worth it to buy a potentially worse tower now, or hope they can survive another wave and purchase a better one when the shop resets.

Designing the Random Inventory System

When I set out to build the shop inventory system I had two main goals in mind: dynamic scaling, where we can add new towers over time and the system would automatically adjust, and relative rarity where we could simply specify a value and the difference in that value from the other towers would determine how rare it is overall.

The dynamic scaling problem was probably the most important goal I set out to achieve as I knew we had an undefined number of towers and we would gradually add more over time. I knew it would be a nightmare to manage balancing the rarity over time, so I wanted to develop a system that simply required adding a tower type to a catalog and giving it some value for rarity and everything else would just work.

I ended up making the ‘catalog’ an array of structs containing references to the towers and their rarity. All we need for the shop to handle a new tower is to make an entry in this table

Relative rarity was the way to make an autonomous balancing system work so that we could specify how frequently a tower should spawn relative to any other towers we include. This system would gradually adjust the spawn chance of each tower over time, but would maintain the same relative frequency for each tower. For an example of how this works in the existing system we can look at a tower with 20 rarity and 100 rarity (with higher numbers being more common). In this case the 100 rarity tower would appear 5 times more frequently than a 20 rarity one (100/20 = 5) and in this case the 20 rarity tower would have 20/120 odds of appearing. If we added another 100 rarity tower then obviously the odds of the 20 rarity tower would drop significantly (20/220), however so does the rarity for the first 100 tower. The end result is that the ratio between the two spawn rates is equal in (20/120)/(100/120) = (20/220)/(100/220). What this means is that while overall spawn rates will drop across the board when we add new towers, their spawn frequency relative to each other will remain the same. With this system in place we can specify that we want a new tower to spawn five times more often than an existing baseline one simply by giving it a rarity that is five times higher than the existing tower.

This is the process by which a single tower for the shop inventory is chosen. This can be performed any number of times depending on how big we want the shop to be.

With the two major components in place, we have a rarity and selection system that will dynamically accommodate any number of towers we decide to add over the course of the project. This will enable us to worry less about managing the generation process and focus more on developing cool towers for players to use in our game.