Debugging vs. Robust coding techniques

Game development vs. Application development

The team is getting features in like a well oiled machine.  The new research screen just totally rocks. Kudos to Mudflap and Mormegil for getting that stuff humming.  BoogieBack and the Elf Girl (doncha just love these handles) made good progress for getting the new star base system in and the new freighters in.

Our newest team member, Jesse (no cool handle yet), has started on the ship battles.  We want the ship battles to be really impressive and we want ships to show damage.  Ever see Star Trek II/III where the Enterprise has phaser burns and such? We want that kind of stuff.

The big debate this week is something that's kind of an on-going thing.  I've been writing games now for 12 years. That's games on store shelves.  I'm not a great programmer as I've mentioned many times before.  But my coding style does lend itself to easy debugging and easy readability.  I.e. I can go back and look at my code from 4 months ago and know what I was doing and in a pinch, put it through the debugger with all the important stuff just a click or two away in a debugger watch. 

But I wouldn't program an application the same way as I program a game.  Most of our business is creating applications.  And when it comes to applications, robust coding techniques take the lead.  A well designed application still needs readable code, but the odds of going in and tossing out features or algorithms in a typical application on a regular basis are not as high as they are in gaming.

In a strategy game, debugging becomes critical because debugging isn't about finding bugs.  Debugging is about fine-tuning game-play.  The last couple months of development usually mean, for me, spending 90% of that time in the debugger tweaking playability.  And that means having structures and objects and classes that lend themselves to being easy to view in the debugger.  To me, CString is the devil.  STL is a pain.  I don't like iterators. Which means I really don't like container classes.  I understand the need for those things. I just find them a pain to deal with when I'm debugging.

The last few months of Galactic Civilizations II will mostly be about tweaking the AI.  Why did the AI build this kind of ship? What did the AI put together a fleet with those kinds of ships in them? Why did the AI not build a particular structure on that particular planet? Where in the heck is that AI ship giong? Why are players running out of money? Why does the morale on a planet fall so low after a certain point is reached? Why did the AI not build a certain type of star base in a certain sector? Why did the computer player declare war on the human player when he did? Why didn't the AI recognize the threat posted by the human player? Why did the AI offer help to that player despite being morally opposite of him?  Why did the AI choose to build that ship on that particular planet? How did the AI see my starship when it was over here in the corner? Why did the AI put their spending ratios and spend rate at that level? Why did the AI send their freighter there?  How much money per turn is a particular trade route established between AI player 1 and AI player 2 costing?  How many shield modules does that particular AI ship have on it? How much time is it taking the AI to research a typical technology when on Average intelligence? How much difference is there between different AI intelligence levels?

The questions go on and on.  And those questions create many subsequent questions.  The answer isn't to put a bunch of debug print statements in the code, that would take forever.  The answer, as a practical matter, is to try to make it such that you can always look at a particular ship, computer player, planet, colony, improvement, whatever and with a few clicks be able to see the numerical value of it.  And moreover, to be able to code it in such a way that it doesn't take long.

That means things like Arrays and pointers. Not special array types, but good old fashioned pClassStarShip pShipsInFleet[MAX_NUM_SHIPS_IN_FLEET]; type stuff.  This is the kind of thing that drives seasoned developers crazy because it's so prone to causing bugs later on.  One slip-up in a for loop or something and suddenly you're getting unexplained crashes that can take weeks to debug. 

The way I've tended to deal with that is to break down my stuff into lots of small functions.  So things that could potentially introduce some overrun don't happen.  I also don't allocate memory on the fly.  In 12 years of game development, none of my code has ever used a Malloc.  And I've only recently (mainly because I'm had to adapt to other people's code) had to create instances of things on the fly and destroy them when I was done.  My code has traditionally only created new things at the start of a game and deleted them at the end of a game.  A single function for creating the stuff and another to wipe it out.  Everything else is created (and stays) so I'm not messing with memory leaks, memory overrruns, etc. 

And those people who have played our games know that they're traditionally quite solid.  I don't need a standard template library or a CString or other "stability via compiler" type stuff.   My way isn't better as any general rule.  But I believe it is better for game development on a game with limited scope (I wouldn't recommend this method for a development team of 20 people, it still requires pretty disciplined coding).

So that was our debate this week.  Things are going well though.  Beta 3 is going to rock.

38,444 views 23 replies
Reply #2 Top
Yea, As a C coder, C# .NET has been a pleasure to use. the VS IDE is getting quite nice too.
Reply #3 Top
What games have you been making with .NET?
Reply #4 Top
Ive been doing smallish games, just to learn more about C#. Im new to the whole OO world, so Im falling victum to trying to do C# like I would a C program, which is wrong. But Managed DirectX has been nice. I think there a battle over the speen of .NET, but I haven't seed it with 100's of sprites moving around (using Direct3D). Of course, I am not trying to do Quake or some other intensive app.
Reply #5 Top
I'm not a great programmer as I've mentioned many times before. But my coding style does lend itself to easy debugging and easy readability. I.e. I can go back and look at my code from 4 months ago and know what I was doing and in a pinch, put it through the debugger with all the important stuff just a click or two away in a debugger watch.

If your code can be debugged and understood by someone else, then you're probably a good programmer. If you couldn't do what you're saying, then you'd be a poor developper.

Allocating everything at the start and deleting all at the end of the program (or the loading of a level in games with levels) is sound, and I've seen it advocated elsewhere. The only problem with memory leaks is applications where control of the life cycle of objects is hard to know.
Iterators are useful when you want to hide stuff from others. In a game, you may not need to use them, but if you realise you need better performances like changing a list to a (hash) set, then having used an abstraction through an iterator helps. But then the syntax of the STL and C++ templates in general makes it really ugly in my opinion (I prefer java iterators for instance).
As for using array[FIXED_SIZE], it's not really a problem. The only problem with arrays is when they are not of fixed size. And a good debugger can evaluate methods, so you should be able to call MyStructure->GetElement(index) inside the debugger, which is about as fast as getting MyStruct[i].
I wonder how you manage to handle big galaxy sizes though: If one can't play big galaxies because of low specs system, you don't have the same array sizes for a small galaxies as for a big one so everything is not coded at compile time. You initialize memory on reading the settings and then don't change them instead?

I code mostly applications (it's my job), mostly in infrastructure layers though I did some UI. And that's BIG applications (hundredS of coders). I also code a game as a hobby and I tend to code the same way as I code my app (except for testing which I do differently as I lack an automated test engine for a hobby). I don't know exactly what should be different between applications and games coding. How would you qualify the difference? Size? Layers of code? Time you keep debugging/supporting the app/game?
Reply #6 Top
LDiCesare , in a game you have to be able to quickly see and change any value when you're tweaking it for playability. In that sense a game isn't as well defined as a normal app, so you have different needs when you code.
Reply #7 Top
Changing values and tweaking is what resource/text files and scripts are for. I don't really see much difference here. And in the app I work on at my job, I spend lots of time changing a boolean here, a float there, when I debug, in order to simulate a problem or revert to a stage of the scenario I would otherwise have to replay everything.from scratch (including maybe loading a model that can take 1 hour to load).
Reply #8 Top
Changing the built in types from C are not the problem.  It's debugging the classes that are the problem.  If Brad need to step through the code where the AI designs its ships, an array of the current ship designs would be much easier to debug in Visual Studio.net 2003 than a container class from the STL.  Visual Studio.net 2005 is supposed to have a lot better debugging tools, particularly for the STL, but it won't be released until November and that's a little long to put off debugging.
Reply #9 Top
So that's essentially a debugger problem. A simple thing like dbx allows to evaluate methods so seeing and dumping state of objects in the debugger is easy. I have a certain dislike for MS debuggers. They have nice features (set next statement can be a boon) but are not consistent in allowing one to call methods from the debugger (it sometimes work) and crash far too often. But then, I use a very old version of MSDev at work...
Reply #10 Top
As a software architect I find technologies like STL and libraries like Boost (www.boost.org) indispensible. I also think it's a bad idea to eschew those technologies just because Microsoft's debugger hasn't caught up to them yet. They allow you to write maintainable, readable, memory-leak-free code more quickly than if you had to implement your own linked list or btree every time you needed one. Also, it sounds a little odd to use a debugger for something other than strictly debugging (i.e., AI or game balance tweaking). It seems like it'd be a better idea to build instrumented code that allows you to log and extract data from the proper points in the game. Certainly STL containers are a lot easier to introspect and do crazy operations on than tStarsip* or something.

For tasks similar to this, i.e., monitoring the internal goings-on of a system from a standpoint of tweaking, rather than squasing bugs, I usually end up writing my own tools, and "leveraging" (I hate that word) stuff like scripting languages that have powerful C++ bindings (read: Python and the Boost Python Libraries).
Reply #11 Top
if you had to implement your own linked list or btree every time you needed one.

I think Brad's saying he uses simple structures like arrays rather than linked lists (which are just another kind of list, arrays do as well except for removal) or btrees or maps. I wonder how he deos without Sets. Iff he's got a limited number of them, he can use a bit on the contained structure, but that doesn't look very practical.
I also develop traces utilities to debug the ai rather than the debugger, but then Brad has shown with Galciv that he managed to produce an excellent ai with these "simple" tools. Note that I usually loathe traces, and add them on purpose only when there's a complex engine that I must monitor, and once it's fixed I remove the traces so they remain readable.
Reply #12 Top
I love these little articles, its make me realize that I am not the only person who goes batty while coding. Now to prove my battiness has anyone manage to fix a bug while they were sleeping? I woke up with the answer once at 3am and ran to the computer, my girlfriend though I was crazy, she quickly moved out never to be seen again

(the moving out part was a joke, needed a punchline, thanks ill be here all week)
Reply #13 Top

Successful game development is like playing a strategy game.  If I have 6 months budgeted towards AI development and a certain coding method takes say merely 10% more time to debug, then I've effectively lost a couple crucial weeks.  And so far, STL and the like look more like 30% time eaters when it comes to debuggin and at that point, you're talking 2 months lost.

Reply #14 Top
That's why I make my home project in java. Far easier to debug than C++. And the time not spent debugging can be spent optimising, so there's little or no performance loss even with a slower language.
Reply #15 Top
I suppose that I can understand where you are coming from on issues of the STL. However, I do differ with you on your opinions. For instance, using vector instead of hard arrays has been show to offer very little in the way of performance costs. It has the added benefits of bounds checking when in debug mode so that you can't have issues of stack or heap corruption that you might have with a raw array. Also, if you are using .net 2003, I don't understand what kind of debugging problems you could be having with the STL. I use .net 2003 and it debugs the STL just fine.

I suppose that whatever works for you works for you. As long as your code is easy to read, more power to you, but I would also advise that you remember the bit about how we "stand on the shoulders of giants." Learning to use the STL and other such APIs is one of the essential features of the adaptable programmer.
Reply #16 Top

James,

Unless you know something that we don't, in VS.Net 2003, you have to navigate through the odd hierarchy in the container classes to view the contents of the vector.  It's not as quick as just putting aShipsInFleet[0] in the watch window.  As I said earlier, Visual Studio .Net 2005 is supposed to have better tools for debugging with the STL.  I love the convenience of the STL, but I am looking forward to the new debugging tools in VS.Net 2005.

Reply #17 Top
vs2005 is really nice That completes this informative post.
Reply #18 Top
You might find these interesting:
http://www.codeproject.com/macro/vsedebugaddin.asp
and
http://www.flipcode.com/cgi-bin/fcarticles.cgi?show=63905
Reply #19 Top
Those links above illustrate my point. Debugging is a general software development problem. So are data structures. And so is delivering a product on a tight schedule. I find STL to be a huge time saver precisely because you don't *have* to debug STL containers...that's already been done for you.

As far as introspecting the contents of STL containers, there are people solveing that problem right now. It seems like a little bit of a cop out to say, "hey, my debugger displays a bunch of oblique info. when I look into an STL container so I ain't using it," when a quick web search can uncover utilities like those linked above.

I'll agree that if arrays are all you ever have to work with, then the win of STL is minimal, and may not be worth the extra debugging complexity. But arrays are also limited, especially for large or frequently modified data sets. Linear time insertion, linear time search (unless sorted, and then see insertion), etc. I find myself using std::maps and std::sets a whole lot, and those are both implemented on red-black trees -- try whipping out a bug free implementation of one of those in a respectable amount of time. SGI STL even throws in hash-table-based sets and maps...yet another data structure that isn't trivial to whip out any time you want it. And I might add, doesn't look pretty in a debugger even if you do.

A gentleman above mentioned Java...Java's development tools have already largely solved the problem that C++ tools are just beginning to solve, which is the ability to introspect complicated containers easily. I'd think an app like GalCiv, which has fairly modest computing requirements compared to, say, a first-person shooter, might be easier to develop on JavaGL. That sounds kinda cool actually...
Reply #20 Top
Just a point that you will never heard mentioned in College, or in any textbook about programming. Using a basic array and a simple search you can load the entire unabridged dictionary and search for words faster, than if you use any other method. I did not believe this but then again I met one of the Engineers that helped build Intel, his name is Elliot, and he proved it by writing the code and loading the entire vocabulary in an array and using a simple sort. Then showing me evey other possible search method. I was rather amazed because I thought he was pulling my leg. I have never since underestimated the practical application of a simple sort and an array.

Luke
Reply #21 Top
I once tested the array vs Hashtable performance in java. A size 7 array was of equal speed to a (synchronised) hashtable without using dichotomy. To be faster, you'd have to use dichotomy search, which means you're effectively coding a structure on top of an array. Although a hashtable may become less efficient due to memory requirements, I doubt a B-tree would be less efficient than a straight array.
Reply #22 Top
If its a sorted array then you can do a binary search on it anyway, you dont need to turn it into a tree. Under C you could use the bsearch function on the array. I find arrays nice fast and simple for things like searching, just inserting elements into the middle of them or requiring a resize of the array can cause the performance hits.
Reply #23 Top
n uvem, thanks for those link. Those look like they might come in handy.