Sunday, 18 May 2008

Language Wars

These last few days, I've been using some some C and C++ code from other Roguelikes as a source of inspiration for Kharne. Years ago, I used to work professionally in both C and C++, and to say I didn't miss them at all would not be inaccurate. However, looking at the Angband source code, I am experiencing not a small degree of deja-vu, and surprisingly, not all of it is bad.

Thers is no doubt that certainly C is still the most popular language for Roguelike development, and nowadays I would contend this is due to two main reasons: the strong cross-platform possibilities that it offers, and also the established momentum that such an exiting large codebase of already written (and finished!) Roguelikes can offer.

There's no doubt that C does have somethings right - I had forgotten how the increment operator was such a thing of beauty. Let's take this amusingly commented extract from the Angband source, specifically in generate.c:

/* Mega-Hack -- Paranoia -- prevent infinite loops */
if (main_loop_count++ > 2000) break;

The Object Pascal equivalent of this is much more laborious:

/* Mega-Hack -- Paranoia -- prevent infinite loops */
inc(loop_count); if (loop_count > 2000) then break;

Double the code, and nowhere near as elegant.

Parameter passing into subroutines is also not as elegant in Object Pascal as it as in C. There is a function used in tunnel building in Angband to find the correct direction between two points. It is defined and used as:

void correct_dir(int *rdir, int *cdir, int y1, int x1, int y2, int x2)

correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);

Now consider the Object Pascal equivalent:

procedure correct_dir(var rdir: Integer; var cdir: Integer; y1, x1, y2, x2: Integer); begin ... end; correct_dir(row_dir, col_dir, row1, col1, row2, col2);

In Object Pascal, I really miss the fact that you know straight away from the calling line which parameters are modifiable or not.

Even the use of { and } seems to be suddenly a lot less irritating than begin and end are.

This is not to say that I've become a sudden convert to Brian W. Kernighan's famous criticisms of Pascal or that I wish to rewrite Kharne from scratch in C/C++. Far from it. There are many things in C and C++ that are simply atrocious, and anyone proposing those features for a modern day language would be, and ought to be, shot. The oft-quoted string handling, for example. The ridiculous fragility of operations involving memory. And, dare I say it, pointers. And pretty much all of Kernighan's criticisms of Pascal are no longer valid when it comes to Object Pascal (though it is, I guess, slightly unfair of me to compare an effectively ancient language like C to a comparatively modern one like Object Pascal). For example, Object Pascal supports casting using the as operator, as whilst you cant quite turn apples into oranges as it sometimes seems you can do with C, as is functional enough.

Now, I think that I perhaps unfairly dissed C and C++ in the past, and with the incorporation of the standard template libary, C++ is certainly a fine development language, but I am still of the opinion that Object Pascal is better. As for C however, would argue that it is unsuitable for modern development - unless there are compelling reasons to do so - it lacks features that modern languages have by default (even C++ has some of these) - exception handling, many useful data types, function overloading and so on. But my biggest complaint about C is that it lets you shoot yourself in the foot too easily.

There are two caveats to this opinion though. The first one is that often opinions on programming languages are a function of familiarity. The second is that what is important with a programming language is how you use it to get things done. This may seem an ironic statement considering I've not released anything yet, but I find the combination of Object Pascal and Delphi allows me to implement things extremely quickly. If you're developing a roguelike, you may yourself have a different method of getting things done, but for me, I'll be sticking with Object Pascal and Delphi, although giving a richly deserved nod to C++ at least.

In other news, I've implemented a simple cave algorithm, with the following results:

Although the levels it generates are somewhat annoying to traverse, they do induce the claustrophobic and careful game play I want the Elemental Planes levels to feature (well, they will do when I add monsters!). All three types of dungeon mentioned in my last post are now implemented, although there's still a lot of refactoring to be done with the code.


Anonymous said...

Hey Dave, you forgot the repeat ... until construct! Using that, the loop construct becomes much nicer.

//My calculations

until (loop_count > 2000);

Much nicer, and easier to comprehend in a rapid skim of the code.

And, unless I'm mistaken, you can rewrite your function call into "shorthand". You only need change that when you change types, either variable type or reference type.

procedure correct_dir(var rdir, cdir: Integer; y1, x1, y2, x2: Integer);

Seeing that I program in Object Pascal (Delphi and FPC, and Chrome) and PHP, I've used the curly brackets myself. In all honesty I find that writing begin/end to be expedient, and not to be a problem because I needn't hold shift and move my right hand to hit {}. Fast as that is, once you've got a pinky on shift, it's just got more rhythm to write "begin ... end;" :D

If you utilize Interfaces, then you can use as to make an apple into an orange -- so long as the apple implements the orange interface.

I like the results of your new cave algorithm; it looks rather organic, yet orderly.

Anonymous said...

Nice cave algorithm. :)

For the begin .. end question: A while ago I read an pro-Pascal article which tried to emphasize that begin .. end is much clearer than { } .

Personally, I am also familiar with { } because I use it in PHP rather much. It's okay.

However, I agree that i++ instead inc(i) would be a nice addition to Pascal. It's faster to write.

Dave said...

The problem with using repeat..until in this specific routine is that the 2000 is a worst-case scenario - usually the routine finishes long before it gets to 2000 iterations.

As for interfaces...I tend not to use them unless absolutely necessary ;-)

Anonymous said...

Dave, I don't see why that makes repeat until problematic. If it exits before then, then fine. Exit/Break out of it as you will. Unless the routine calls itself recursively, then repeat until should suit it fine. ;-)

Interfaces are better in FPC, as you can define them as you wish, without implied descendence from IUnknown and its members.

Dave said...

Unless the routine calls itself recursively, then repeat until should suit it fine. ;-)

Haha, yes, you guessed it, the routine does call itself repeatedly.