Recent bug safari

During the recent Ludum Dare I came across a number of somewhat hilarious bugs :) I also recently polished off the last few remaining in NDK Black Dog, so once I’ve done the tutorial, win and lose screens it’ll be ready to ship!

Debugging is often seen as a chore by programmers, but there’s something to be said for the thrill of spying, stalking, hunting down and killing a bug that’s been bothering you for ages. It requires a very no-nonsense, sceptical mindset that filters out any superstitious assumed causes in favour of finding the true root-cause empirically.

Let me be clear about this: true debugging means finding the root of the problem and fixing it, not stopping the situation in which it has been reported from occurring. The latter is what I did for Supersoldat and is just plain lazy. What’s more, it doesn’t work: like a paint-bubble, when squashed in one place it will simply move somewhere else.

Anyway, here are some of the more amusing bugs I’ve encountered of late:

————————————— Surprise Cat Secks! —————————————

I should mention before we begin that the “post-compo” extension of the game is now pretty-much finished, and I’ve added a game-page for the project with information and links to various external sites of interest :)

Recent additions include cats seeking out mates, footstep-sounds and -artwork (including bloody footprints when you walk on cats or blood), improved menus, a “tweet this” button, death by old-age and a new weapon that heals cats and makes them ready and willing to mate.

1. Vanishing Cats

Symptom: When given an initial direction to move in, all the cats disappear. This does not occur if they’re initially immobile and only start moving when attacked.

Cause: The current_time variable is initialised at 0, meaning that the first (and only the first) update’s delta-time corresponds to the number of milliseconds since January the 1st 1970. This causes all the cats to fly off the screen at incredible speeds!

Side note: Using delta-time is extremely useful. You can, for instance, put the game into slow-motion or fast-forward. I used this a few times to test population stability (birth/death rate) to ensure that all the cats wouldn’t die-out without your intervention. Be careful though: set the multiplier too high and objects will move through each-other rather than colliding!

2. NaN Cats

Symptoms:  One or more cats become invulnerable to all attack. When these cats breed, they pass on their invulnerability to their partners until all cats are invulnerable.

Cause: A typo when deducting a constant value Kitten.REPRODUCE_COST (I wrote Kitten.REPODUCE_COST) from the cat’s hit-points counter during reproduction results in hit-points being set to NaN. NaN cats are, needless to say, invulnerable to all damage ;)

Side Note: This is something I dislike about javascript: throw an exception, don’t evaluate non-existent fields as NaN! It’s a real pain in the asp.

3. Asbestos kittens

Symptoms: Kittens are immune to fire, ice and poison after-effects until fully grown: they don’t take damage and the fire/ice/poison affecting them does not dissipate.

Cause: Since kittens do not regenerate, and since poison/fire/ice damage was lumped in together with regeneration code, it is only natural that kittens don’t take damage from these debuffs! I just had to move some code around a little to fix this.

4. Bouncing ice cats o’ death

Symptoms: Now that fire/ice effects are shared upon collision, cats very sometimes become so cold that they bounce around the screen, killing all other cats until none are left. Wait, what!?

Cause: When cold, cat speed is set to speed = (cold/MAX_COLD)*MAX_SPEED. I forgot to cap the cold value when cold is shared between cats, meaning that we can have cold > MAX_COLD and thus cats with speed > MAX_SPEED.

Side note: After encountering this error I added a “Bank” class with mutators that assure that the stored value is always below a certain maximum and above a certain minimum (0 by default). This is analogous to the “ResourceElement” in Black Dog’s arrogance engine.

————————————— NDK Black Dog —————————————

I should mention that I’ve also run NDK Black Dog through a valgrind recently, to check for memory leaks, and have plugged all those that occur in my code. That said tinyxml and SDL, especially SDL_mixer, are setting off many warnings I seemingly can’t do anything about on my end. Even minimal code seems to come up with dozens of warnings:

[cpp]int main() {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
SDL_Quit();
return 0;
}[/cpp]
I might put my code through a profiler too before shipping but I’m not sure if that’s really necessary: memory-leaks are unacceptable, but if it runs on my shoddy old hardware I think speed-optimisation is optional.

5. Vacuum-terrain

Symptom: The player-character is very rarely sucked backwards into the terrain when falling onto it, resulting in an instantaneous and unmerited death.

Cause: Collision code has the player-character move it backwards (towards the left) when colliding with a wall. In the rare event that the collision is to the left of the player, this means the player is moved towards the collision and not away from it, resulting in a vicious circle!

6. Phantom objects (part 1)

Symptom: Coming back to the code-base after the Dare, I notice falling objects (such as feathers, orbs and Cerberus imps) have stopped appearing.

Cause: Objects do appear but are deleted immediately due to out-of-bounds events. Gaeel had pointed out a problem in my Guillaume’s collision code my implementation of Guillaume‘s collision algorithm during the Ludum Dare. In fixing the problem I introduced a new one: I put my sign-check the wrong way round when testing the sign of a dot-product, meaning that collision events were generated only when there wasn’t a collision. Oops.

Side note: This is why you shouldn’t (re)write system-critical code when fatigued!

7. Phantom objects (part 2)

Symptom: This was actually I know bug before the Dare. After a while falling objects stop appearing, but only on Android. It makes the game impossible to win since orbs are needed to progress. Maybe Android is refusing to initialise them due to memory overuse?

Cause: Nope, that’s just superstition! When I switched from using the device resolution for collisions to using a virtual, fixed resolution (that of my PC), I forgot to update the level’s boundary-box, which is still based on the hardware resolution. This means that while the world has a width of 640, the boundary on my phone is only about 400 wide. Hence objects instantiated to the right-hand side of the screen are immediately deleted because they are seen to be out-of-bounds!

———————————————————————————————————————

I’m reaching the stage where my code does what it’s meant to first try most of the time, and when it does play up I immediately have an idea of what the problem is. Sometimes though, as we’ve seen, it’s the wrong idea. This shows that staying humble and adhering to the empirical method is very important!

I’m itching to make an RTS game: I’ve been playing around with Ogre3D and jMonkeyEngine recently, and I’m preparing to compare/contrast the two. I may end up using HTML 5 for prototyping though: it’s just so easy to use :)

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>