Colossal Cave Adventure, that venerable classic, is back and better than ever! The page for downloads is here.
The game is fully playable. It would be astonishing if it were otherwise, since it has been stable since 1995. One minor cosmetic change a lot of people used to the non-mainline variants will appreciate is that there’s a command prompt.
The most substantial thing Jason Ninneman and I have done to the code itself is that the binary is now completely self-contained, with no need for it to refer to an external adventure.text file. That file is now compiled to C structures that are linked to the rest of the game at build time.
The other major thing we’ve done is that the distribution now includes a pretty comprehensive regression-test suite. We do coverage analysis to verify that it visits most of the code. This clears the way to do serious code cleanup (gotos and stale FORTRAN idioms must die!) while being sure we’re not breaking things,
We gratefully acknowledge the support of the game’s original authors, Don Woods and Will Crowther.
Well, that just sucked part of my life away :)
Many humble thanks for your work. This needed to be preserved.
The database.h file is missing from the dist. set. make complained about this returning an error.
>The database.h file is missing from the dist. set.
That file is not missing, it’s generated at build time. We’re you trying to make parallel?
I grabbed the tar.bz2 master and it built & executed just fine
I’m curious what motivated this. Will it no longer accept an external file at all, or does it just have a “default” one kept internally?
It seems to me that removing the external game “definition” (for lack of a better term) would make the game un-moddable and generally violate the Rule of Separation.
(I don’t know anything about the game and may be misunderstanding what said file actually does.)
>Will it no longer accept an external file at all, or does it just have a “default” one kept internally?
Neither. The adventure.text is file still interpreted, but it’s done once at build time rather than every time at startup. You can still mod the game by altering it, but now you need to recompile the source for the changes to take effect.
The old architecture made sense in a world where people passed around binaries and building from source was a high-overhead operation normally done only by the game’s developers (which is how things were in 1977). It had the disadvantage that the advent binary wasn’t standalone – it would fail if it couldn’t find adventure.text at startup time.
The way it works now is that adventure.text is processed at build time into two derived source files, database.[ch], which are then linked to the rest of the source. Besides removing the runtime dependency, this makes startup faster.
(I should say ‘un-moddable for the end user, i.e. without recompilation’. Admittedly that may be less of an issue with OSS)
Okay. I think I disagree with that call, but without digging into how the game works (and to what extent its design is even comparable to modern games, where reading all possible game data from external files at runtime is almost always the Right Thing) I don’t think I can defend my opinion.
That said, thanks for elaborating.
>(and to what extent its design is even comparable to modern games, where reading all possible game data from external files at runtime is almost always the Right Thing)
It isn’t very. The game code is rife with magic-number assumptions tied not just to the database schema but to the numeric IDs of individual items. The game engine is not really flexible enough to benefit from the amount of declarative specification you get in a modern game; the “database” is more of a desperate expedient to get around the lack of any real string types in archaic FORTRAN,
Ah, that makes a lot more sense then.
GOTOs and stale FORTRAN idioms – Oh dear, I think I’m having flashbacks.
>GOTOs and stale FORTRAN idioms – Oh dear, I think I’m having flashbacks.
Yeah, dude, stay away from the brown acid.
I never thought I’d have to perform a massive gotoectomy again. In 2017. But there it is.
It’s been fun watching the commits. Nice methodological refactoring.
Some things about the original code are funny. Like the globals I, J, K, L… I’ve never thought of globalizing re-usable for loop indices. It’s nice that I can actually mostly read actions.c now.
>Some things about the original code are funny. Like the globals I, J, K, L… I’ve never thought of globalizing re-usable for loop indices.
It’s because archaic FORTRAN had no locals.
Gotcha. Never learned FORTRAN.
I did learn COBOL in high school. My tearcher was convinced new-fangled language like C were only useful for OS’s, and serious business applications would always have to be written in COBOL and RPG III and the like, because they were easier to for accountants to audit.
@Aaron:
> serious business applications
I’m pretty sure that that phrase decomposes to its component words in a violently exothermic fashion. Those words just don’t want to be together in the same sentence.
I did an unofficial port from Fortran (DECSystem-10 source, I think) to a C-like language (Proprietary: Datapoint Advanced Systems Language – DASL) back in the early 1980s. After several iterations, I managed to get most of the in-line Fortran stuff moved to various functions and subroutines, but there was one pair of intermingled loops at the heart of the code that I could never properly untangle. Probably a good case study of how *not* to use GOTO.
>but there was one pair of intermingled loops at the heart of the code that I could never properly untangle.
Yeah, I think I’m staring at that one (er, or those two) now…
@Jon Brase:
Yeah… in a lot of ways, he was a smart dude, but he came exclusively from a business/MIS POV and a mainframe background, but he’d been out of the corporate world long enough to lose touch.
@ESR, I have a couple of as to style in your C code:
1) Any reason for preference of ++varname instead of varname++? Sure, there are differences when used in a statement, but I always defaulted to post rather than pre.
2) I noticed your infinite loops you seem to prefer for(;;). Any reason for this over while(1) or similar?
I’ve not written C professionally in years, and when I did, it was in conformance to a style at my employer that was a bit different, so there might be cultural norms and the like I’m ignorant of.
>1) Any reason for preference of ++varname instead of varname++? Sure, there are differences when used in a statement, but I always defaulted to post rather than pre.
A habit I’ve developed recently. I think it foregrounds the increment operation more than the postfix form does.
>2) I noticed your infinite loops you seem to prefer for(;;). Any reason for this over while(1) or similar?
Yes, a more concrete one. Some compilers don’t optimize out the constant evaluation in while (1) {}.
On top of the reasons esr gave:
1) It’s good to get into habit of using ++x rather than x++ if you ever plan to start coding in C++. If x is a complex object with overloaded operators, ++x tends to be more efficient since it doesn’t need to make a useless copy of the original value to return.
2) for(;;) is shorter and stands out more. You don’t need to spend your brain resources to confirm whether it’s while(1) or while(l). Yes, I’ve seen “while(l)” in production code, may it’s author burn in hell for the time I wasted trying to understand it.
An interesting somewhat related project
“Assembling the history of Unix” on LWN.net
https://lwn.net/Articles/725297/
https://lwn.net/SubscriberLink/725297/ea93bc6ac9f8ae29/
Thanks for this. I first played adventure when I was in junior high. at computer camp given by a nearly college. And I’ve always wished that I got to finish the game. Now I can. So big, big thanks.
On linux, in advent.h, I had to add #include stdarg.h at line 4 to avoid a compile failure at line 97 on the use of va_list.
On OSX, make will build it, but when run the binary immediately segfaults.
The dwarves throw knives that miss, yet I still die. Seems a little buggy.
>On linux, in advent.h, I had to add #include stdarg.h at line 4 to avoid a compile failure at line 97 on the use of va_list.
Odd. That makes sense, and I just pushed a patch including that header, but we didn’t need to do that under any of our Linuxes. Atrre you sing gcc ior clang>
As for your other problems, try building the repo tip version. It has a pretty serious testsuite.
On my system (Ubuntu 17.04, gcc 6.30, glibc 2.24), stdarg.h is ultimately included by stdio.h. The Makefile uses -std=c99 -D_DEFAULT_SOURCE in CFLAGS for this effect. My guess is he is on a pre-2.19 glibc which does not use _DEFAULT_SOURCE. Incidentally, -D_POSIX_C_SOURCE=200809L is sufficient to build cleanly (the only thing that breaks without it is the use of the “getline” function) now, and any _XOPEN_SOURCE is sufficient to also cause va_list to be defined by stdio.h.
Incidentally, I could swear this comment did not have these typos yesterday.
—-
I got several errors attempting to build: newdungeon.py uses “rule” rather than “data” around line 566, and is set up to build “actspk” instead of “actions” (the latter appears in the YAML and C source code). I get the impression that maybe this was modified and not committed? Also, throughout the C source code, a number of constants that are WORD_FOO in the header are simply FOO.
>I got several errors attempting to build: newdungeon.py
I pushed a bad commit last night – tests should have detected it, but I think we have a Makefile glitch that means newdungeon.py sometimes doesn’t fire when it should, so tests ran with and older (and correct) version of the generated C. Try re-cloning, and after that make sure you make clean before you build – I’ll try to track down the recipe glitch today.