Bug or feature?

Bug or feature?

  • Comments 23

Writing about randomness reminded me of an interesting bug in the first commercial game I ever released. Extreme G was a futuristic racer for the Nintendo 64. Each vehicle had a limited number of turbo boosts, which increased your speed as long as you drove cleanly, but cut out as soon as you clipped the edge of the track. It was impossible to corner cleanly at turbo speed, so the best tactic was to save your boosts for the longest straight section, while trying to knock boosting opponents off the track.

Ash (the lead programmer) wrote some AI code that decided when the computer players should use their turbo. They would boost when they reached a straight piece of track, and also (for variety) at occasional randomly chosen intervals.

Shortly after the game came out, we read a review that went something like:

"We especially loved the aggressive AI, which does everything in its power to stop you overtaking. If you pass a computer player, they will fight back by firing their turbo, even midway through a turn where that is sure to cause a massive pileup. Perhaps not the best winning strategy, but an awesome f-you attitude!"

"Hah!", quoth Ash, "what a foolish reviewer! That's not how it works. I bet the random number generator just happened to trigger its turbo at the same time he was overtaking, and he read too much into that. My actual AI code is nowhere near so subtle."

But then he went back to look at this actual AI code :-)

A universal challenge of racing game design is how to keep the pack of vehicles close together. If they become too widely separated, the player can be left racing on what appears to be an empty track, with the computer players out of sight in front or behind them, which is no fun at all. In Extreme G, this problem was exacerbated by two things:

  • For performance reasons, we could not afford as many computer vehicles as we ideally wanted, so the pack was overly small in the first place.
  • Weapons and turbo boosts made the gameplay less predictable than in a pure racer, tending to spread out the vehicles.

To help keep the pack together, Ash biased the turbo AI based on race position. When a computer player was in front of the human they would rarely use their turbo, but if they were far behind they would fire it in an attempt to catch up.

What he meant to write was:

    if (computerPlayer is behind human)
    {
        ushort difference = human.TrackPos - computerPlayer.TrackPos;

        if (difference > catchUpThreshold)
            FireTurboBoost();
    }

The TrackPos field held a 16 bit counter of how far around the track each vehicle was, normalized so that 0 represented the starting line, 32768 was halfway around, and 65535 was a full lap nearly back to the start. This format was convenient because the rounding rules of integer arithmetic automatically handled the modulo operations necessary when comparing positions from different laps, or from either side of the start line.

Thanks to an order of processing bug, what actually happened was:

    if (computerPlayer is behind human)
    {
        ushort difference = human.TrackPos - computerPlayer.TrackPosFromPreviousFrame;

        if (difference > catchUpThreshold)
            FireTurboBoost();
    }

This worked as intended, except when you overtook an AI vehicle:

  • Computer was in front of human
  • Now human is in front, so this code kicks in
  • Because it erroneously uses the computer position from the previous frame, the position difference comes out negative
  • Because the type is unsigned, this is interpreted as a large positive integer
  • It looks like the computer is a long way behind, so the turbo is triggered

Oops!

But the reviewer was absolutely right. Although accidental, the resulting behavior was good gameplay. We left this bug unchanged in the sequel.

  • I still have the original Extreme G and have not placed much attention to this behavior until now. That aggressive behavior actually happens! If you have to have a bug, it may as well be like this one.

    Did you work on all of the Extreme G games? I started with XGRA and worked my way backwards to see how it progressed. I love this series.

  • Great story, and brought back good memories of Extreme G!  Narrative fallacy has filled in the gaps in many older games for me, I'm sure - but it's amusing to see that the assumption of the reviewer was actually correct.

  • Please reconsider your web site's layout.  My screen is at 1024 by 768 and your code still got cut off (and there wasn't even a horizontal scroll bar).  

  • For those who don't understand how the negative value comes in, I think it has to do with the size of the types being used. A unsigned short = 16 bits = It can hold values up to 65,536. Once you exceed that number, it overflows, and the value turns negative. :)

  • Actually you are close, but no cigar.

    1. 16 bit unsigned can take on values from 0 to 65,535, e.g. hex 0000 - FFFF.  Add one to 0xFFFF and you wind up with 0x0000, not 65536.

    2. An unsigned cannot go negative by definition.

    3. A signed short of 16 bits can range from -32768 to +32767, 0x1000 to 0x7FFF since the high order bit is the sign bit.

    My arithmetic is rusty -- I'm 67 so forgive a bit and correct me if I have erred.

  • now I dont know how to code, at all, but I think I can see how the negative may come in? from my simple perspective that is.

    -Check Human position = 1499,

    check computer position 1500 = ahead

    -Check human position = 1510

    Check computer position = 1509

    report human = ahead

    -human ahead reported

    computer checks last frame human position

    vs computer position for distance ahead

    previous frame

    human = 1507

    computer = 1508

    but

    human = ahead

    human position - computer position (1507 - 1508) = -1 or ahead one lap coming up behind computer

    boost to catch up

    like I said I have no actual knowlage on the subject, but thats just my take on it by the info given, or my wild guess if you would

  • @R.A.

    The post actually says it only uses the computer's previous position, not the human's.

    ushort difference = human.TrackPos - computerPlayer.TrackPosFromPreviousFrame;

    Since at the point where this code is executed, human.TrackPos is greater than computerPlayer.TrackPos and computerPlayer.TrackPosFromPreviousFrame I don't see how it could be "negative".

    Using your example and the code above we'd have something like:

    1510 - 1508 = 2.

    The only way I can see the negative "feature" happening, as has been mentioned already above, is if it was indeed the human's previous frame position being used, as opposed to the computer's.

  • I worked on XG3 and XGRA and I can say that the AI wasn't that much different on those titles :)

Page 2 of 2 (23 items) 12
Leave a Comment
  • Please add 7 and 7 and type the answer here:
  • Post