Software Developer Tracks Down Code for a Beloved 41-Year-Old Text Adventure
<i>&quot;More than 40 years later, after five years of searching, the original Fortran code for that game has been re-discovered and resurrected, thanks to the diligent efforts of an amateur game preservationist who had fond memories of playing the game when he was 11. It’s a story of dedication and perseverance, but most of all how far one can go with the fondness for a minor piece of tech culture.&quot;</i><p>What&#x27;s the big deal about this? All he had to do was to ask me! ;-) I still have a copy of Adventure&#x27;s source code on my present laptop, it&#x27;s been there for years (it&#x27;s been moved from machine to machine over the years along with other source code and data).  At one time I used to use it as test code—it was reasonably long and didn&#x27;t usually crash.<p>I cannot understand why anyone would have had trouble trying to find it, for at one time in the early&#x2F;mid 1980s just about every Fortran programmer had a copy of it (he can&#x27;t have tried very hard, at one time its source was even included in those free give-away disks that were stuck to the covers of computer magazines).<p>Adventure&#x27;s source file is <i>advent.for</i>, it&#x27;s written in Fortran and this version is 81,857 bytes long. Incidentally, there are several versions of the source and they differ from one another very slightly in the game&#x27;s messages, etc., and also there were minor tweaks for the different Fortran compilers. Somewhere in my archives I have about a half dozen or so of these different versions on a floppy disk (but I&#x27;ve not seen it in years). <i>(If I recall, the reason for me still having this version on my PC is that it was the one that compiled on the Fortran that was on my Godbout&#x2F;CompuPro CP&#x2F;M 8&#x2F;16 S-100 system.)</i><p>One of the fun tricks of having the source was to compile it with special messages embedded for specific people that one knew (unbeknownst to them of course). We arranged it so that after playing for, say, some hours players would stumble across messages specifically intended for them—much to their amazement (as most didn&#x27;t even know what a compiler was let alone know that I had one to tweak the game).<p><i>Fortran people, except for the bit at the end, sorry about the wrap (it would take up too much listing space otherwise). &#x27;Tis a shame I can&#x27;t list it in full.<p>For those who aren&#x27;t literate in Fortran, comment lines begins with &#x27;C&#x27;. (Programmers who learned on punched Hollerith cards always had plenty of &#x27;blank&#x27; comment cards to insert to separate&#x2F;tidy up their source).</i><p>Here&#x27;s the intro:<p>C  ADVENTURES<p>C  CURRENT LIMITS:
C      9650 WORDS OF MESSAGE TEXT (LINES, LINSIZ).
C 750 TRAVEL OPTIONS (TRAVEL, TRVSIZ).
C 300 VOCABULARY WORDS (KTAB, ATAB, TABSIZ).
C 150 LOCATIONS (LTEXT, STEXT, KEY, COND, ABB, ATLOC, LOCSIZ).
C 100 OBJECTS (PLAC, PLACE, FIXD, FIXED, LINK (TWICE), PTEXT, PROP).
C  35 &quot;ACTION&quot; VERBS (ACTSPK, VRBSIZ).
C 205 RANDOM MESSAGES (RTEXT, RTXSIZ).
C  12 DIFFERENT PLAYER CLASSIFICATIONS (CTEXT, CVAL, CLSMAX).
C  20 HINTS, LESS 3 (HINTLC, HINTED, HINTS, HNTSIZ).
C  35 MAGIC MESSAGES (MTEXT, MAGSIZ).
C  THERE ARE ALSO LIMITS WHICH CANNOT BE EXCEEDED DUE TO THE STRUCTURE OF
C  THE DATABASE.  (E.G., THE VOCABULARY USES N&#x2F;1000 TO DETERMINE WORD TYPE,
C  SO THERE CAN&#x27;T BE MORE THAN 1000 WORDS.)  THESE UPPER LIMITS ARE:
C 1000 NON-SYNONYMOUS VOCABULARY WORDS
C 300 LOCATIONS
C 100 OBJECTS<p>IMPLICIT INTEGER(A-Z)
 LOGICAL DSEEN,BLKLIN,HINTED,YES,START<p>COMMON &#x2F;TXTCOM&#x2F; RTEXT,LINES
 COMMON &#x2F;BLKCOM&#x2F; BLKLIN
 COMMON &#x2F;VOCCOM&#x2F; KTAB,ATAB,TABSIZ
 COMMON &#x2F;PLACOM&#x2F; ATLOC,LINK,PLACE,FIXED,HOLDNG
 COMMON &#x2F;MTXCOM&#x2F; MTEXT
 COMMON &#x2F;PTXCOM&#x2F; PTEXT
 COMMON &#x2F;ABBCOM&#x2F; ABB
 COMMON &#x2F;WIZCOM&#x2F; WKDAY,WKEND,HOLID,HBEGIN,HEND,HNAME,
 1 SHORT,MAGIC,MAGNM,LATNCY,SAVED,SAVET,SETUP<p>DIMENSION LINES(9650)
 DIMENSION TRAVEL(750)
 DIMENSION KTAB(300),ATAB(300)
 DIMENSION LTEXT(150),STEXT(150),KEY(150),COND(150),ABB(150),
 1 ATLOC(150)
 DIMENSION PLAC(100),PLACE(100),FIXD(100),FIXED(100),LINK(200),
 1 PTEXT(100),PROP(100)
 DIMENSION ACTSPK(35)
 DIMENSION RTEXT(205)
 DIMENSION CTEXT(12),CVAL(12)
 DIMENSION HINTLC(20),HINTED(20),HINTS(20,4)
 DIMENSION MTEXT(35)
 DIMENSION TK(20),DSEEN(6),DLOC(6),ODLOC(6),HNAME(4)<p>C
C AVOID MAKING THE COMPILER WORRY ABOUT MODIFYING THE DO INDEX
C
 INTEGER IDONDX
 C  STATEMENT FUNCTIONS
C
C
C  TOTING(OBJ) = TRUE IF THE OBJ IS BEING CARRIED
C  HERE(OBJ) = TRUE IF THE OBJ IS AT &quot;LOC&quot; (OR IS BEING CARRIED)
C  AT(OBJ) = TRUE IF ON EITHER SIDE OF TWO-PLACED OBJECT
C  LIQ(DUMMY) = OBJECT NUMBER OF LIQUID IN BOTTLE
C  LIQLOC(LOC) = OBJECT NUMBER OF LIQUID (IF ANY) AT LOC
C  BITSET(L,N) = TRUE IF COND(L) HAS BIT N SET (BIT 0 IS UNITS BIT)
C  FORCED(LOC) = TRUE IF LOC MOVES WITHOUT ASKING FOR INPUT (COND=2)
C  DARK(DUMMY) = TRUE IF LOCATION &quot;LOC&quot; IS DARK
C  PCT(N)       = TRUE N% OF THE TIME (N INTEGER FROM 0 TO 100)
C
C  WZDARK SAYS WHETHER THE LOC HE&#x27;S LEAVING WAS DARK
C  LMWARN SAYS WHETHER HE&#x27;S BEEN WARNED ABOUT LAMP GOING DIM
C  CLOSNG SAYS WHETHER ITS CLOSING TIME YET
C  PANIC SAYS WHETHER HE&#x27;S FOUND OUT HE&#x27;S TRAPPED IN THE CAVE
C  CLOSED SAYS WHETHER WE&#x27;RE ALL THE WAY CLOSED
C  GAVEUP SAYS WHETHER HE EXITED VIA &quot;QUIT&quot;
C  SCORNG INDICATES TO THE SCORE ROUTINE WHETHER WE&#x27;RE DOING A &quot;SCORE&quot; COMMAND
C  DEMO IS TRUE IF THIS IS A PRIME-TIME DEMONSTRATION GAME
C  YEA IS RANDOM YES&#x2F;NO REPLY<p>LOGICAL TOTING,HERE,AT,BITSET,DARK,WZDARK,LMWARN,CLOSNG,PANIC,
 1 CLOSED,GAVEUP,SCORNG,DEMO,YEA<p>TOTING(OBJ)=PLACE(OBJ).EQ.-1
 HERE(OBJ)=PLACE(OBJ).EQ.LOC.OR.TOTING(OBJ)
 AT(OBJ)=PLACE(OBJ).EQ.LOC.OR.FIXED(OBJ).EQ.LOC
 LIQ2(PBOTL)=(1-PBOTL)*WATER+(PBOTL&#x2F;2)*(WATER+OIL)
 LIQ(DUMMY)=LIQ2(MAX0(PROP(BOTTLE),-1-PROP(BOTTLE)))
 LIQLOC(LOC)=LIQ2((MOD(COND(LOC)&#x2F;2*2,8)-5)*MOD(COND(LOC)&#x2F;4,2)+1)
 BITSET(L,N)=(COND(L).AND.SHIFT(1,N)).NE.0
 FORCED(LOC)=COND(LOC).EQ.2
 DARK(DUMMY)=MOD(COND(LOC),2).EQ.0.AND.(PROP(LAMP).EQ.0.OR.
 1 .NOT.HERE(LAMP))
 PCT(N)=RAN(100).LT.N
C
 DATA LINSIZ&#x2F;9650&#x2F;,TRVSIZ&#x2F;750&#x2F;,TABSIZ&#x2F;300&#x2F;,LOCSIZ&#x2F;150&#x2F;,
 1  VRBSIZ&#x2F;35&#x2F;,RTXSIZ&#x2F;205&#x2F;,CLSMAX&#x2F;12&#x2F;,HNTSIZ&#x2F;20&#x2F;,MAGSIZ&#x2F;35&#x2F;
 DATA SETUP&#x2F;0&#x2F;,BLKLIN&#x2F;.TRUE.&#x2F;
 C  DESCRIPTION OF THE DATABASE FORMAT
C
C
C  THE DATA FILE CONTAINS SEVERAL SECTIONS.  EACH BEGINS WITH A LINE CONTAINING
C  A NUMBER IDENTIFYING THE SECTION, AND ENDS WITH A LINE CONTAINING &quot;-1&quot;.<p>C &lt;...&gt;<p>*<p><i>Here&#x27;s the end of the listing (many of the subroutines are listed here but I&#x27;ve had to omit all but the last bit):</i><p>*<p>&lt;...&gt;<p>INTEGER FUNCTION RAN(RANGE)<p>C  SINCE THE RAN FUNCTION IN LIB40 SEEMS TO BE A REAL LOSE, WE&#x27;LL USE ONE OF
C  OUR OWN.  IT&#x27;S BEEN RUN THROUGH MANY OF THE TESTS IN KNUTH VOL. 2 AND
C  SEEMS TO BE QUITE RELIABLE.  RAN RETURNS A VALUE UNIFORMLY SELECTED
C  BETWEEN 0 AND RANGE-1.  NOTE RESEMBLANCE TO ALG USED IN WIZARD.<p>IMPLICIT INTEGER(A-Z)
 DATA R&#x2F;0&#x2F;<p>D=1
 IF(R.NE.0)GOTO 1
 CALL DATIME(D,T)
 R=18*T+5
 D=1000+MOD(D,1000)
1 DO 2 T=1,D
2 R=MOD(R*1021,1048576)
 RAN=(RANGE*R)&#x2F;1048576
 RETURN
 END<p>SUBROUTINE DATIME(D,T)<p>C  RETURN THE DATE AND TIME IN D AND T.  D IS NUMBER OF DAYS SINCE 01-JAN-77,
C  T IS MINUTES PAST MIDNIGHT.  THIS IS HARDER THAN IT SOUNDS, BECAUSE THE
C  FINAGLED DEC FUNCTIONS RETURN THE VALUES ONLY AS ASCII STRINGS!<p>IMPLICIT INTEGER(A-Z)
 DIMENSION DAT(2),MONTHS(12),HATH(12)
 DATA MONTHS&#x2F;&#x27;-JAN-&#x27;,&#x27;-FEB-&#x27;,&#x27;-MAR-&#x27;,&#x27;-APR-&#x27;,&#x27;-MAY-&#x27;,&#x27;-JUN-&#x27;,
 1 &#x27;-JUL-&#x27;,&#x27;-AUG-&#x27;,&#x27;-SEP-&#x27;,&#x27;-OCT-&#x27;,&#x27;-NOV-&#x27;,&#x27;-DEC-&#x27;&#x2F;
 DATA HATH&#x2F;31,28,31,30,31,30,31,31,30,31,30,31&#x2F;<p>C  FUNCTION I2 TAKES 2-DIGIT ASCII AND YIELDS DECIMAL VALUE.<p>I2(X)=(SHIFT(X,-29).AND.15)*10+(SHIFT(X,-22).AND.15)<p>CALL DATE(DAT)
 CALL TIME(TIM)<p>YEAR=I2(SHIFT(DAT(2),14))-77
 D=I2(DAT(1))-1
 X=((SHIFT(DAT(1),14).OR.SHIFT(DAT(2),-21)).AND..NOT.&quot;1004020001)
 1 .OR.&#x27;-@@@-&#x27;
C  ABOVE FUNNY EXPRESSION GUARANTEES (A) UPPER-CASE, AND (B) BOTTOM BIT OKAY.
 DO 1 MON=1,12
 IF(X.EQ.MONTHS(MON))GOTO 2
1 D=D+HATH(MON)
 CALL BUG(28)<p>2 D=D+YEAR*365+YEAR&#x2F;4
 IF(MOD(YEAR,4).EQ.3.AND.MON.GT.2)D=D+1
 T=I2(TIM)*60+I2(SHIFT(TIM,21))
 RETURN
 END<p>SUBROUTINE CIAO<p>C  EXITS, AFTER ISSUING REMINDER TO SAVE NEW CORE IMAGE.  USED WHEN SUSPENDING
C  AND WHEN CREATING NEW VERSION VIA MAGIC MODE.  ON SOME SYSTEMS, THE CORE
C  IMAGE IS LOST ONCE THE PROGRAM EXITS.  IF SO, SET K=31 INSTEAD OF 32.<p>IMPLICIT INTEGER(A-Z)
 DATA K&#x2F;32&#x2F;<p>CALL MSPEAK(K)
 IF(K.EQ.31)CALL GETIN(A,B,C,D)
 STOP
 END<p>SUBROUTINE BUG(NUM)
 IMPLICIT INTEGER(A-Z)<p>C  THE FOLLOWING CONDITIONS ARE CURRENTLY CONSIDERED FATAL BUGS.  NUMBERS &lt; 20<p>C  ARE DETECTED WHILE READING THE DATABASE; THE OTHERS OCCUR AT &quot;RUN TIME&quot;.<p>C 0 MESSAGE LINE &gt; 70 CHARACTERS
C 1 NULL LINE IN MESSAGE<p>C 2 TOO MANY WORDS OF MESSAGES
C 3 TOO MANY TRAVEL OPTIONS<p>C 4 TOO MANY VOCABULARY WORDS<p>C 5 REQUIRED VOCABULARY WORD NOT FOUND
C 6 TOO MANY RTEXT OR MTEXT MESSAGES<p>C 7 TOO MANY HINTS<p>C 8 LOCATION HAS COND BIT BEING SET TWICE<p>C 9 INVALID SECTION NUMBER IN DATABASE<p>C 20 SPECIAL TRAVEL (500&gt;L&gt;300) EXCEEDS GOTO LIST<p>C 21 RAN OFF END OF VOCABULARY TABLE<p>C 22 VOCABULARY TYPE (N&#x2F;1000) NOT BETWEEN 0 AND 3<p>C 23 INTRANSITIVE ACTION VERB EXCEEDS GOTO LIST<p>C 24 TRANSITIVE ACTION VERB EXCEEDS GOTO LIST<p>C 25 CONDITIONAL TRAVEL ENTRY WITH NO ALTERNATIVE<p>C 26 LOCATION HAS NO TRAVEL ENTRIES<p>C 27 HINT NUMBER EXCEEDS GOTO LIST<p>C 28 INVALID MONTH RETURNED BY DATE FUNCTION<p>TYPE 1, NUM<p>1 FORMAT (&#x27; Fatal error, see source code for interpretation.&#x27;&#x2F;<p>1 &#x27; Probable cause: erroneous info in database.&#x27;&#x2F;<p>2 &#x27; Error code =&#x27;,I2&#x2F;)<p>STOP<p>END