Balancing the Equations

Balancing large mathematical systems is one of the killer problems in game design. Every game has plenty of interacting subsystems; all too often some unforeseen interplay between two such subsystems can result in psychotic behavior from the system as a whole.

Let's use an imaginary first-person shooter as an example. Suppose that this shooter offers the player a reward for killing Goony-Woonies by equipping each Goony-Woony with one ammo clip, which the player gains by stripping each dead Goony-Woony. Of course, Goony-Woonies are dangerous critters; the player sustains an average of two health points worth of damage every time he fights a Goony-Woony. Fangybirds, by contrast, can be killed only by shooting them with a gun. They carry medicine packets worth five health points. If the cost of shooting down a Fangybird is less than one ammo clip, then the player has a simple strategy: Kill a Goony-Woony, use the clip obtained to shoot down a Fangybird, use the medicine from the Fangybird to heal the injuries inflicted by the Goony-Woony, and you're ahead in both ammo and health.

No game designer would make so glaring a blunder but that's only because there are only two monsters here to worry about. What if there were a hundred different monsters in the game? How can the designer get such a large system operating with any confidence that it won't have some hidden combination that reduces the game to nonsense?

LESSON 75

Eschew sensitive functions like exponentials or hyperbolics.

This is no hypothetical problem. The first edition of Civilization suffered from precisely this strategy. Somebody discovered a "lock on victory" in the form of a simple strategy, called "The Mongol Strategy," that always won the game. That strategy took advantage of a slight weakness in the balancing of the system of equations at work in the innards of the game. Now, Sid Meier is one of the great masters of complex simulation design; if this problem could nip Sid, you can be sure that it will masticate a mere mortal like you.

Herewith, then, some lessons for balancing large systems.

NOTE

Warning! The following material is highly mathematical! If you don't care for mathematics, skip this section!

Consider the following formula:

Apples = Oranges2

If the value of Oranges increases by 1%, the value of Apples will increase by 2%. To appreciate the danger imposed by such a formula, think in terms of "excursions." Imagine that your system has settled down to a nice stable configuration, and then one component, say Oranges, wiggles by 1%. That triggers an excursion. At its first step, the excursion is only 1% in magnitude, but on the second step, it has doubled in size. If you have many more of these sensitive functions in your system, the excursion can rapidly grow to outrageous levels.

LESSON 76

Dampen excursions with shock-absorbing functions.

LESSON 77

Buffer divisors with additive elements.

This doesn't mean that all of the functions in your system must be lily-livered wimps. You can have volatile functions; you just have to watch them carefully, as they will surely be the source of most of your headaches.

Just as exponentials magnify excursions, inverse exponentials dampen excursions. The simplest, safest shock absorber you can add to your system is a square root function. Let's apply the idea with a modification of our previous example:

Apples = Oranges1/2

For this formula, a 1% excursion in Oranges yields a 0.5% excursion in Apples. If your system is hyperactive, jumping around wildly, toss a few square roots in there to calm it down. If you need lots of dampening, use a log function I guarantee that a log function will take the life out of the most rambunctious system. Just be sparing in its use; the log function is a dinosaur-strength tranquilizer for any system of equations.

Everybody knows that it's dangerous to permit the possibility of division by zero:

Apples = Oranges÷Pears

If the value of Pears just happens to reach zero, you get a divide by zero error and your program blows sky high. Most people simply avoid division in any case where the divisor could conceivably equal zero. But there is a way to use division without risking catastrophe. Just add a buffer term to the divisor, like so:

Apples = Oranges÷(Pears + 1)

LESSON 78

Stick to the basic four functions.

LESSON 79

Don't use Boolean-modulated multi-part functions.

If you are absolutely certain that the value of Pears can never go below zero, then this modification will protect you from divide-by-zero blues.

You don't need to use lots of snazzy functions. Sure, you can impress your boss with that clever Gaussian function or a Legendre polynomial, but remember: You've got to eat everything you put on your plate. If you stuff some high-falutin' function into your system of equations, you'll have to tune and balance it later. Tuning and balancing requires a feel for the way that functions behave do you have a feel for Legendre polynomials? You'd be surprised just how much richness you can get out of the four basic arithmetic operators (addition, subtraction, multiplication, and division). If you work with them long enough, you'll develop a feel for how they work together, and you'll find it easy to tune a misbehaving system of equations until it purrs. Top them up with squares to punch up some factors and square roots to tone down others, and you can handle just about anything. Shift a factor with addition or subtraction; scale it with multiplication or division. Combine shifting and scaling in differing degrees to get just about any effect you want.

Some designers like to set up functions like this:

 IF (x > 23)       Y = A1 * x + B1 ELSE       Y = A2 * x + B2 

This is very dangerous! You have to make absolutely certain that the function itself is continuous across the boundary you have created. That in itself is not difficult, but often designers fail to ensure continuity of the first derivative of the function, leading to sudden changes in the behavior of the system as a whole. The user experiences these as hiccups; he's making slow, steady changes to the system and suddenly it jumps.

LESSON 80

Use only one mathematical operation per line of code.

LESSON 81

Don't commit "formula grabbing."

Here's the wrong way to write a big messy formula:

Apples = 43xOranges + 19xPears 27xKumquats

Here's the right way:

Intermediate#1 = 43xOranges

Intermediate#2 = 19xPears

Intermediate#3 = 27xKumquats

Apples = Intermediate#1 + Intermediate#2 Intermediate#3

The latter way makes it easy to check those intermediate values during your tuning process. Sure, this latter approach is slightly less efficient than the former approach; why, it probably wastes several dozen billionths of a second of processing time! What many programmers don't realize is that programming efficiency is often at odds with debugging efficiency. When you're poring over the system, trying to find out why it's misbehaving, you need to rapidly check out every possibility. If your problem is that the value of Pears has gone bonkers, you might not see the problem if that value is buried inside a big long formula. But if you use the latter approach and just check every left-hand value, the answer will pop right out at you. Do not underestimate the difficulty of tuning a big system of equations; use every ploy available to simplify that task.

Back in my days as a physics teacher, I had to help students past a nasty habit: formula grabbing. It arises when the student doesn't really understand the physics of the situation and desperately grabs a formula from the textbook and stuffs values into it, trying to get some answer, ANY answer. Some people never quite shuck the habit, reaching for a formula out of a book and then ensconcing themselves in a false sense of correctness because they got their formula out of a book. Most of the time, the formula you get out of the book is scientifically accurate but completely inappropriate to the situation you face. If you have to turn to a book to get the formula, then you don't understand the fundamentals of the system well enough to tune that formula later. You're better off using a simplistic approximation that you do understand.

LESSON 82

Assert maximum and minimum allowable values for each variable.

For every single variable that the user can experience, determine the range of acceptable values. State those values in the variable declarations in the program and then use assert statements to enforce their proper behavior. Most delinquent systems start with one variable going out of range, and then the rest of the system gets corrupted.



Chris Crawford on Game Design
Chris Crawford on Game Design
ISBN: 0131460994
EAN: 2147483647
Year: 2006
Pages: 248

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net