Compare commits

...
Sign in to create a new pull request.

23 commits

Author SHA1 Message Date
Eric S. Raymond
d388877c1b Implement -d option. 2023-03-29 16:40:57 -04:00
Eric S. Raymond
286556f885 Make Z a synonym for NOTHI. 2023-03-23 11:28:53 -04:00
Eric S. Raymond
ffa9332ee3 Avoid noise diffs around logging of seed command. 2023-03-23 11:28:53 -04:00
Eric S. Raymond
bed5fb747b Don't loop back on resume file read failure...
...it's inconvenient for testing.
2023-03-23 11:28:53 -04:00
Eric S. Raymond
0a63b5e2d6 Trim resume file names as required. 2023-03-23 11:28:53 -04:00
Eric S. Raymond
c3bb0dae75 Quiet down database compilation. Observe RNG stability.
At this commit, we can tell that the seeded-RNG nehavopr of this 430
branch is identical to that of master because the axebear log - which
includes randomization of dwarf spawning and the reservoir word -
yields the same results in both versions.
2023-03-23 11:28:53 -04:00
Eric S. Raymond
184e981be3 Fix things so seed doesn't cost clock time. 2023-03-23 11:28:53 -04:00
Eric S. Raymond
b80d1779e6 Fix dropped stitch in last commit. 2023-03-23 11:28:53 -04:00
Eric S. Raymond
220cf2c58b Make it possible to pass options to advent from within regression-test loads. 2023-03-23 11:28:53 -04:00
Jason S. Ninneman
c41dd35268 Ensure the ZZZZ magic word is reproducible.
This happens by making the SEED command also regenerate the magic word.
2023-03-23 11:28:53 -04:00
Eric S. Raymond
d39325f963 Fix bug that led to comments not being ignored. 2023-03-23 11:28:53 -04:00
Eric S. Raymond
85cd6b0bd5 Repeatable seeding is working. 2023-03-23 11:28:53 -04:00
Eric S. Raymond
f286c3a327 Implement fallback handler that looks at the raw command buffer. 2023-03-23 11:28:53 -04:00
Jason S. Ninneman
74dc437a7e Stop command-logging from non-stdin sources. 2023-03-23 11:28:53 -04:00
Eric S. Raymond
2fdd509f32 Once again, take srand()/random() out of the initialization chain.
They have exactly the wrong kind of randomness for this job - not
returning consistent sequences across different platforms or C library
versions, and because pseodorandom not really better than sampling
the clock.
2023-03-23 11:28:53 -04:00
Eric S. Raymond
54afefba94 Re-enable skipping of #-led comments. 2023-03-23 11:28:53 -04:00
Jason S. Ninneman
060601da2f Remove a bad use of tv_nsec. 2023-03-23 11:28:53 -04:00
Jason S. Ninneman
5598b7a178 Add seedable PRNG using an adaptation the original LCG algorithm. 2023-03-23 11:28:53 -04:00
Eric S. Raymond
97b00dfb14 Make output from replays easier to interpret by adding prompts. 2023-03-23 11:28:53 -04:00
Eric S. Raymond
63efff14f5 Echo commands to stdout when replaying...
...makes check loads full transcripts abd more readable.
2023-03-23 11:28:53 -04:00
Eric S. Raymond
a416d78a58 Input source is parametrized all the way down.
This means that, potentially, do_command() could be called on any text file
pointer and the right thing would happen.
2023-03-23 11:28:53 -04:00
Eric S. Raymond
baa508a683 Begin factoring out the command interpreter. 2023-03-23 11:28:53 -04:00
Eric S. Raymond
12f909bcc9 Start advent430 branch for correctness testing.
The purpose of this branch is to create a version of the game from
before the bug fixes, refactoring, and logic changes.  We want this so
we can run it against our 100% coverage test suite and see all changes
in behavior.

This branch is forked from the point where the prompt and the oldstyle
option were added.  At this point there had been only two logic
changes:

1. Do initialization of the LCG with gettimeofday(). Note that
this change will not affectt regression testing, since the
initialization done in this way will nbe overridden in the
logs by seed commands.

2. Refactor the input routines to a normal Unixy organization.
This is required for the -l option to work.

This commit just builds the binary at advent430 where it
won't collide with the production version.
2023-03-23 11:28:53 -04:00
19 changed files with 2775 additions and 93 deletions

View file

@ -4,10 +4,10 @@ OBJS=main.o init.o actions1.o actions2.o score.o misc.o
SOURCES=$(OBJS:.o=.c) COPYING NEWS README TODO advent.text control
.c.o:
gcc -O $(DBX) -c $<
gcc -g $(DBX) -c $<
advent: $(OBJS)
gcc -O $(DBX) -o advent $(OBJS)
advent430: $(OBJS)
gcc -g $(DBX) -o advent430 $(OBJS)
main.o: misc.h funcs.h
@ -22,7 +22,10 @@ score.o: misc.h main.h share.h
misc.o: misc.h main.h
clean:
rm -f *.o advent advent.html advent.6
rm -f *.o advent.html advent.6
realclean: clean
rm -f adventure.data advent430
# Requires asciidoc and xsltproc/docbook stylesheets.
.asc.6:

3
TODO
View file

@ -1,7 +1,6 @@
= Open Adventure TODO =
* Use a real pseudorandom-number generator with a seed rather than just
time-sampling.
* Update the command parser to accept a PRNG seed value.
* Add command logging and command log replay. Note that the replay log
needs to begin with the random-number seed.

View file

@ -14,7 +14,7 @@
/* Analyse a verb. Remember what it was, go back for object if second word
* unless verb is "say", which snarfs arbitrary second word. */
int action(long STARTAT) {
int action(FILE *input, long STARTAT) {
switch(STARTAT) {
case 4000: goto L4000;
case 4090: goto L4090;
@ -269,7 +269,7 @@ L9094: DROP(JADE,LOC);
/* Attack also moved into separate module. */
L9120: return(attack());
L9120: return(attack(input));
/* Pour. If no object, or object is bottle, assume contents of bottle.
* special tests for pouring water or oil on plant or rusty door. */
@ -349,11 +349,11 @@ L9160: if(OBJ != LAMP)SPK=76;
/* Throw moved into separate module. */
L9170: return(throw());
L9170: return(throw(input));
/* Quit. Intransitive only. Verify intent and exit if that's what he wants. */
L8180: if(YES(22,54,54)) score(1);
L8180: if(YES(input,22,54,54)) score(1);
return(2012);
/* Find. Might be carrying it, or it might be here. Else give caveat. */
@ -446,7 +446,7 @@ L9270: if(DARK(0)) goto L5190;
PSPEAK(OBJ,OBJTXT[OBJ]+PROP[OBJ]);
return(2012);
L9275: CLSHNT=YES(192,193,54);
L9275: CLSHNT=YES(input,192,193,54);
return(2012);
/* Break. Only works for mirror in repository and, of course, the vase. */
@ -475,7 +475,7 @@ L9290: if(OBJ != DWARF || !CLOSED) return(2011);
L8300: SPK=201;
RSPEAK(260);
if(!YES(200,54,54)) return(2012);
if(!YES(input,200,54,54)) return(2012);
SAVED=SAVED+5;
KK= -1;
@ -525,7 +525,7 @@ L8305: DATIME(I,K);
L8310: KK=1;
if(LOC == 1 && ABB[1] == 1) goto L8305;
RSPEAK(268);
if(!YES(200,54,54)) return(2012);
if(!YES(input,200,54,54)) return(2012);
goto L8305;
L8312: SETPRM(1,K/10,MOD(K,10));

View file

@ -131,7 +131,7 @@ L9028: PROP[VASE]=2;
* objects fall into two categories: enemies (snake, dwarf, etc.) and others
* (bird, clam, machine). Ambiguous if 2 enemies, or no enemies but 2 others. */
int attack() {
int attack(FILE *input) {
I=ATDWRF(LOC);
if(OBJ != 0) goto L9124;
if(I > 0)OBJ=DWARF;
@ -176,7 +176,7 @@ L9126: if(OBJ == 0)SPK=44;
RSPEAK(49);
VERB=0;
OBJ=0;
GETIN(WD1,WD1X,WD2,WD2X);
GETIN(input,WD1,WD1X,WD2,WD2X);
if(WD1 != MAKEWD(25) && WD1 != MAKEWD(250519)) return(2607);
PSPEAK(DRAGON,3);
PROP[DRAGON]=1;
@ -214,7 +214,7 @@ L9129: /*etc*/ ;
* and if dwarf is present then one might be killed. (Only way to do so!)
* Axe also special for dragon, bear, and troll. Treasures special for troll. */
int throw() {
int throw(FILE *cmdin) {
if(TOTING(ROD2) && OBJ == ROD && !TOTING(ROD))OBJ=ROD2;
if(!TOTING(OBJ)) return(2011);
if(OBJ >= 50 && OBJ <= MAXTRS && AT(TROLL)) goto L9178;
@ -230,10 +230,10 @@ int throw() {
if(AT(OGRE)) goto L9175;
if(HERE(BEAR) && PROP[BEAR] == 0) goto L9176;
OBJ=0;
return(attack());
return(attack(cmdin));
L9172: SPK=48;
if(RAN(7) < DFLAG) goto L9175;
if(randrange(7) < DFLAG) goto L9175;
DSEEN[I]=false;
DLOC[I]=0;
SPK=47;

View file

@ -1292,6 +1292,7 @@
2004 UNLOC
2004 OPEN
2005 NOTHI
2005 Z
2006 LOCK
2006 CLOSE
2007 LIGHT

View file

@ -23,7 +23,7 @@
#define CNDBIT(L,N) (TSTBIT(COND[L],N))
#define FORCED(LOC) (COND[LOC] == 2)
#define DARK(DUMMY) ((!CNDBIT(LOC,0)) && (PROP[LAMP] == 0 || !HERE(LAMP)))
#define PCT(N) (RAN(100) < (N))
#define PCT(N) (randrange(100) < (N))
#define GSTONE(OBJ) ((OBJ) == EMRALD || (OBJ) == RUBY || (OBJ) == AMBER || (OBJ) == SAPPH)
#define FOREST(LOC) ((LOC) >= 145 && (LOC) <= 166)
#define VOCWRD(LETTRS,SECT) (VOCAB(MAKEWD(LETTRS),SECT))
@ -37,7 +37,7 @@
#define OUTSID(LOC) ((LOC) <= 8 || FOREST(LOC) || (LOC) == PLAC[SAPPH] || (LOC) == 180 || (LOC) == 182)
#define INDEEP(LOC) ((LOC) >= 15 && !OUTSID(LOC) && (LOC) != 179)
extern int carry(void), discard(bool), attack(void), throw(void), feed(void), fill(void);
extern int carry(void), discard(bool), attack(FILE *), throw(FILE *), feed(void), fill(void);
void score(long);

6
init.c
View file

@ -180,7 +180,7 @@ void initialise(void) {
}
static int raw_init(void) {
printf("Couldn't find adventure.data, using adventure.text...\n");
//printf("Couldn't find adventure.data, using adventure.text...\n");
FILE *OPENED=fopen("adventure.text","r" /* NOT binary */);
if(!OPENED){printf("Can't read adventure.text!\n"); exit(0);}
@ -634,7 +634,7 @@ L1993: SETPRM(1,LINUSE,LINSIZ);
SETPRM(15,CLSSES,CLSMAX);
SETPRM(17,HNTMAX,HNTSIZ);
SETPRM(19,TRNVLS,TRNSIZ);
RSPEAK(267);
//RSPEAK(267);
TYPE0();
}
@ -661,7 +661,7 @@ static bool quick_init(void) {
}
static void quick_save(void) {
printf("Writing adventure.data...\n");
//printf("Writing adventure.data...\n");
f = fopen("adventure.data",WRITE_MODE);
if(f == NULL){printf("Can't open file!\n"); return;}
init_reading = false;

98
main.c
View file

@ -16,7 +16,7 @@ long ABB[186], ATAB[331], ATLOC[186], BLKLIN = true, DFLAG,
KTAB[331], *LINES, LINK[201], LNLENG, LNPOSN,
PARMS[26], PLACE[101], PTEXT[101], RTEXT[278],
SETUP = 0, TABSIZ = 330;
signed char INLINE[LINESIZE+1], MAP1[129], MAP2[129];
signed char rawbuf[LINESIZE], INLINE[LINESIZE+1], MAP1[129], MAP2[129];
long ABBNUM, ACTSPK[36], AMBER, ATTACK, AXE, BACK, BATTER, BEAR, BIRD, BLOOD, BONUS,
BOTTLE, CAGE, CAVE, CAVITY, CHAIN, CHASM, CHEST, CHLOC, CHLOC2,
@ -42,15 +42,19 @@ long ABBNUM, ACTSPK[36], AMBER, ATTACK, AXE, BACK, BATTER, BEAR, BIRD, BLOOD, BO
WZDARK = false, ZZWORD;
FILE *logfp;
bool oldstyle = false;
int debug;
lcg_state lcgstate;
extern void initialise();
extern void score(long);
extern int action(long);
extern int action(FILE *, long);
/*
* MAIN PROGRAM
*/
static void do_command(FILE *);
int main(int argc, char *argv[]) {
int ch;
@ -64,8 +68,11 @@ int main(int argc, char *argv[]) {
/* Options. */
while ((ch = getopt(argc, argv, "l:o")) != EOF) {
while ((ch = getopt(argc, argv, "dl:o")) != EOF) {
switch (ch) {
case 'd':
debug += 1;
break;
case 'l':
logfp = fopen(optarg, "w+");
if (logfp == NULL)
@ -91,6 +98,14 @@ int main(int argc, char *argv[]) {
#include "funcs.h"
/* Initialize our LCG PRNG with parameters tested against Knuth vol. 2. by the original authors */
lcgstate.a = 1093;
lcgstate.c = 221587;
lcgstate.m = 1048576;
long seedval = (long)time(NULL);
set_seed(seedval);
/* Read the database if we have not yet done so */
LINES = (long *)calloc(LINSIZ+1,sizeof(long));
@ -114,14 +129,39 @@ int main(int argc, char *argv[]) {
/* Start-up, dwarf stuff */
L1: SETUP= -1;
I=RAN(-1);
I=0;
ZZWORD=RNDVOC(3,0)+MESH*2;
NOVICE=YES(65,1,0);
NOVICE=YES(stdin, 65,1,0);
NEWLOC=1;
LOC=1;
LIMIT=330;
if(NOVICE)LIMIT=1000;
if (logfp)
fprintf(logfp, "seed %ld\n", seedval);
for (;;) {
do_command(stdin);
}
}
static bool fallback_handler(signed char *buf)
/* fallback handler for commands not handled by FORTRANish parser */
{
long sv;
if (sscanf(buf, "seed %ld", &sv) == 1) {
set_seed(sv);
printf("\nSeed set to %ld\n", sv);
// autogenerated, so don't charge user time for it.
--TURNS;
// here we reconfigure any global game state that uses random numbers
ZZWORD=RNDVOC(3,0)+MESH*2;
return true;
}
return false;
}
static void do_command(FILE *cmdin) {
/* Can't leave cave once it's closing (except by main office). */
L2: if(!OUTSID(NEWLOC) || NEWLOC == 0 || !CLOSNG) goto L71;
@ -166,7 +206,7 @@ L6000: if(DFLAG != 1) goto L6010;
if(!INDEEP(LOC) || (PCT(95) && (!CNDBIT(LOC,4) || PCT(85)))) goto L2000;
DFLAG=2;
for (I=1; I<=2; I++) {
J=1+RAN(5);
J=1+randrange(5);
if(PCT(50))DLOC[J]=0;
} /* end loop */
for (I=1; I<=5; I++) {
@ -204,7 +244,7 @@ L6014: KK=KK+1;
{long x = KK-1; if(TRAVEL[x] >= 0) goto L6012;}
L6016: TK[J]=ODLOC[I];
if(J >= 2)J=J-1;
J=1+RAN(J);
J=1+randrange(J);
ODLOC[I]=DLOC[I];
DLOC[I]=TK[J];
DSEEN[I]=(DSEEN[I] && INDEEP(LOC)) || (DLOC[I] == LOC || ODLOC[I] == LOC);
@ -257,7 +297,7 @@ L6027: DTOTAL=DTOTAL+1;
if(ODLOC[I] != DLOC[I]) goto L6030;
ATTACK=ATTACK+1;
if(KNFLOC >= 0)KNFLOC=LOC;
if(RAN(1000) < 95*(DFLAG-2))STICK=STICK+1;
if(randrange(1000) < 95*(DFLAG-2))STICK=STICK+1;
L6030: /*etc*/ ;
} /* end loop */
@ -371,8 +411,8 @@ L2603: if(!CLOSED) goto L2605;
} /* end loop */
L2605: WZDARK=DARK(0);
if(KNFLOC > 0 && KNFLOC != LOC)KNFLOC=0;
I=RAN(1);
GETIN(WD1,WD1X,WD2,WD2X);
I=0;
GETIN(cmdin, WD1,WD1X,WD2,WD2X);
/* Every input, check "FOOBAR" flag. If zero, nothing's going on. If pos,
* make neg. If neg, he skipped a word, so make it zero. */
@ -431,6 +471,8 @@ L2800: WD1=WD2;
/* Gee, I don't understand. */
L3000: SETPRM(1,WD1,WD1X);
if (fallback_handler(rawbuf))
return;
RSPEAK(254);
goto L2600;
@ -440,8 +482,8 @@ L4000: I=4000; goto Laction;
L4090: I=4090; goto Laction;
L5000: I=5000;
Laction:
switch (action(I)) {
case 2: goto L2;
switch (action(cmdin, I)) {
case 2: return;
case 8: goto L8;
case 2000: goto L2000;
case 2009: goto L2009;
@ -477,7 +519,7 @@ L8000: SETPRM(1,WD1,WD1X);
L8: KK=KEY[LOC];
NEWLOC=LOC;
if(KK == 0)BUG(26);
if(K == NUL) goto L2;
if(K == NUL) return;
if(K == BACK) goto L20;
if(K == LOOK) goto L30;
if(K == CAVE) goto L40;
@ -508,11 +550,11 @@ L13: if(NEWLOC <= 100) goto L14;
L14: if(NEWLOC != 0 && !PCT(NEWLOC)) goto L12;
L16: NEWLOC=MOD(LL,1000);
if(NEWLOC <= 300) goto L2;
if(NEWLOC <= 300) return;
if(NEWLOC <= 500) goto L30000;
RSPEAK(NEWLOC-500);
NEWLOC=LOC;
goto L2;
return;
/* Special motions come here. Labelling convention: statement numbers NNNXX
* (XX=00-99) are used for special case number NNN (NNN=301-500). */
@ -527,10 +569,10 @@ L30000: NEWLOC=NEWLOC-300;
* be used for actual motion, but can be spotted by "go back". */
L30100: NEWLOC=99+100-LOC;
if(HOLDNG == 0 || (HOLDNG == 1 && TOTING(EMRALD))) goto L2;
if(HOLDNG == 0 || (HOLDNG == 1 && TOTING(EMRALD))) return;
NEWLOC=LOC;
RSPEAK(117);
goto L2;
return;
/* Travel 302. Plover transport. Drop the emerald (only use special travel if
* toting it), so he's forced to use the plover-passage to get it out. Having
@ -554,11 +596,11 @@ L30300: if(PROP[TROLL] != 1) goto L30310;
MOVE(TROLL+100,FIXD[TROLL]);
JUGGLE(CHASM);
NEWLOC=LOC;
goto L2;
return;
L30310: NEWLOC=PLAC[TROLL]+FIXD[TROLL]-LOC;
if(PROP[TROLL] == 0)PROP[TROLL]=1;
if(!TOTING(BEAR)) goto L2;
if(!TOTING(BEAR)) return;
RSPEAK(162);
PROP[CHASM]=1;
PROP[TROLL]=2;
@ -582,7 +624,7 @@ L20: K=OLDLOC;
if(CNDBIT(LOC,4))K2=274;
if(K2 == 0) goto L21;
RSPEAK(K2);
goto L2;
return;
L21: LL=MOD((IABS(TRAVEL[KK])/1000),1000);
if(LL == K) goto L25;
@ -596,7 +638,7 @@ L22: if(TRAVEL[KK] < 0) goto L23;
L23: KK=K2;
if(KK != 0) goto L25;
RSPEAK(140);
goto L2;
return;
L25: K=MOD(IABS(TRAVEL[KK]),1000);
KK=KEY[LOC];
@ -609,14 +651,14 @@ L30: if(DETAIL < 3)RSPEAK(15);
DETAIL=DETAIL+1;
WZDARK=false;
ABB[LOC]=0;
goto L2;
return;
/* Cave. Different messages depending on whether above ground. */
L40: K=58;
if(OUTSID(LOC) && LOC != 8)K=57;
RSPEAK(K);
goto L2;
return;
/* Non-applicable motion. Various messages depending on word given. */
@ -629,7 +671,7 @@ L50: SPK=12;
if(K == 62 || K == 65)SPK=42;
if(K == 17)SPK=80;
RSPEAK(SPK);
goto L2;
return;
@ -661,7 +703,7 @@ L90: RSPEAK(23);
L99: if(CLOSNG) goto L95;
NUMDIE=NUMDIE+1;
if(!YES(79+NUMDIE*2,80+NUMDIE*2,54)) score(0);
if(!YES(cmdin,79+NUMDIE*2,80+NUMDIE*2,54)) score(0);
if(NUMDIE == MAXDIE) score(0);
PLACE[WATER]=0;
PLACE[OIL]=0;
@ -704,10 +746,10 @@ L40000: switch (HINT-1) { case 0: goto L40100; case 1: goto L40200; case 2: g
BUG(27);
L40010: HINTLC[HINT]=0;
if(!YES(HINTS[HINT][3],0,54)) goto L2602;
if(!YES(cmdin,HINTS[HINT][3],0,54)) goto L2602;
SETPRM(1,HINTS[HINT][2],HINTS[HINT][2]);
RSPEAK(261);
HINTED[HINT]=YES(175,HINTS[HINT][4],54);
HINTED[HINT]=YES(cmdin,175,HINTS[HINT][4],54);
if(HINTED[HINT] && LIMIT > 30)LIMIT=LIMIT+30*HINTS[HINT][2];
L40020: HINTLC[HINT]=0;
L40030: goto L2602;
@ -847,7 +889,7 @@ L11000: PROP[BOTTLE]=PUT(BOTTLE,115,1);
RSPEAK(132);
CLOSED=true;
goto L2;
return;
/* Another way we can force an end to things is by having the lamp give out.
* When it gets close, we come here to warn him. We go to 12000 if the lamp

9
main.h
View file

@ -2,9 +2,16 @@
#define LINESIZE 100
typedef struct lcg_state
{
unsigned long a, c, m, x;
} lcg_state;
extern long ABB[], ATAB[], ATLOC[], BLKLIN, DFLAG, DLOC[], FIXED[], HOLDNG,
KTAB[], *LINES, LINK[], LNLENG, LNPOSN,
PARMS[], PLACE[], PTEXT[], RTEXT[], TABSIZ;
extern signed char INLINE[LINESIZE+1], MAP1[], MAP2[];
extern signed char rawbuf[LINESIZE], INLINE[LINESIZE+1], MAP1[], MAP2[];
extern FILE *logfp;
extern bool oldstyle;
extern int debug;
extern lcg_state lcgstate;

77
misc.c
View file

@ -1,6 +1,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "misc.h"
#include "funcs.h"
@ -172,7 +173,7 @@ void fSETPRM(long FIRST, long P1, long P2) {
#define WORD1X (*wORD1X)
#define WORD2 (*wORD2)
#define WORD2X (*wORD2X)
void fGETIN(long *wORD1, long *wORD1X, long *wORD2, long *wORD2X) {
void fGETIN(FILE *input, long *wORD1, long *wORD1X, long *wORD2, long *wORD2X) {
long JUNK;
/* Get a command from the adventurer. Snarf out the first word, pad it with
@ -183,8 +184,8 @@ long JUNK;
L10: if(BLKLIN)TYPE0();
MAPLIN(stdin);
if(feof(stdin)) score(1);
MAPLIN(input);
if(input == stdin && feof(stdin)) score(1);
WORD1=GETTXT(true,true,true,0);
if(BLKLIN && WORD1 < 0) goto L10;
WORD1X=GETTXT(false,true,true,0);
@ -205,9 +206,9 @@ L22: JUNK=GETTXT(false,true,true,0);
#undef WORD1X
#undef WORD2
#undef WORD2X
#define GETIN(WORD1,WORD1X,WORD2,WORD2X) fGETIN(&WORD1,&WORD1X,&WORD2,&WORD2X)
#define GETIN(SRC,WORD1,WORD1X,WORD2,WORD2X) fGETIN(SRC,&WORD1,&WORD1X,&WORD2,&WORD2X)
#undef YES
long fYES(long X, long Y, long Z) {
long fYES(FILE *input, long X, long Y, long Z) {
long YES, REPLY, JUNK1, JUNK2, JUNK3;
@ -215,7 +216,7 @@ long YES, REPLY, JUNK1, JUNK2, JUNK3;
* if no, print Z and return false. */
L1: RSPEAK(X);
GETIN(REPLY,JUNK1,JUNK2,JUNK3);
GETIN(input, REPLY,JUNK1,JUNK2,JUNK3);
if(REPLY == MAKEWD(250519) || REPLY == MAKEWD(25)) goto L10;
if(REPLY == MAKEWD(1415) || REPLY == MAKEWD(14)) goto L20;
RSPEAK(185);
@ -722,7 +723,8 @@ L2: ATDWRF=I;
/* Utility routines (SETBIT, TSTBIT, RAN, RNDVOC, BUG) */
/* Utility routines (SETBIT, TSTBIT, set_seed, get_next_lcg_value,
* randrange, RNDVOC, BUG) */
#undef SETBIT
long fSETBIT(long BIT) {
@ -756,32 +758,31 @@ long TSTBIT;
#define TSTBIT(MASK,BIT) fTSTBIT(MASK,BIT)
#undef RAN
long fRAN(long RANGE) {
static long D, R = 0, RAN, T;
#undef RNDVOC
/* Since the ran function in LIB40 seems to be a real lose, we'll use one of
* our own. It's been run through many of the tests in Knuth vol. 2 and
* seems to be quite reliable. RAN returns a value uniformly selected
* between 0 and range-1. */
D=1;
if(R != 0 && RANGE >= 0) goto L1;
DATIME(D,T);
R=MOD(T+5,1048576L);
D=1000+MOD(D,1000);
L1: for (T=1; T<=D; T++) {
R=MOD(R*1093L+221587L,1048576L);
} /* end loop */
RAN=(RANGE*R)/1048576;
return(RAN);
void set_seed(long seedval)
{
lcgstate.x = (unsigned long) seedval % lcgstate.m;
}
unsigned long get_next_lcg_value(void)
{
/* Return the LCG's current value, and then iterate it. */
unsigned long old_x = lcgstate.x;
lcgstate.x = (lcgstate.a * lcgstate.x + lcgstate.c) % lcgstate.m;
if (debug) {
printf("# random %lu\n", old_x);
}
return(old_x);
}
long randrange(long range)
{
/* Return a random integer from [0, range). */
long result = range * get_next_lcg_value() / lcgstate.m;
return(result);
}
#define RAN(RANGE) fRAN(RANGE)
#undef RNDVOC
long fRNDVOC(long CHAR, long FORCE) {
long DIV, I, J, RNDVOC;
@ -794,7 +795,7 @@ long DIV, I, J, RNDVOC;
RNDVOC=FORCE;
if(RNDVOC != 0) goto L3;
for (I=1; I<=5; I++) {
J=11+RAN(26);
J=11+randrange(26);
if(I == 2)J=CHAR;
RNDVOC=RNDVOC*64+J;
} /* end loop */
@ -885,15 +886,21 @@ long I, VAL;
if(MAP2[1] == 0)MPINIT();
if (!oldstyle && isatty(0))
if (!oldstyle && OPENED == stdin)
fputs("> ", stdout);
IGNORE(fgets(INLINE+1,sizeof(INLINE)-1,OPENED));
do {
IGNORE(fgets(rawbuf,sizeof(rawbuf)-1,OPENED));
} while
(!feof(OPENED) && rawbuf[0] == '#');
if (feof(OPENED)) {
if (logfp)
fclose(logfp);
} else {
if (logfp)
IGNORE(fputs(INLINE+1, logfp));
if (logfp && OPENED == stdin)
IGNORE(fputs(rawbuf, logfp));
else if (!isatty(0))
IGNORE(fputs(rawbuf, stdout));
strcpy(INLINE+1, rawbuf);
LNLENG=0;
for (I=1; I<=sizeof(INLINE) && INLINE[I]!=0; I++) {
VAL=INLINE[I]+1;
@ -990,8 +997,10 @@ L10: fclose(F);
L20: printf("\nFile name: ");
IGNORE(fgets(NAME, sizeof(NAME), stdin));
if (NAME[strlen(NAME)-1] == '\n')
NAME[strlen(NAME)-1] = '\0';
F=fopen(NAME,(IN ? READ_MODE : WRITE_MODE));
if(F == NULL) {printf("Can't open file, try again.\n"); goto L20;}
if(F == NULL) {printf("Can't open file, try again.\n"); /* goto L20; */}
return;
L30: if(IN)IGNORE(fread(ARR,sizeof(long),250,F));

13
misc.h
View file

@ -13,10 +13,10 @@ extern void fRSPEAK(long);
#define RSPEAK(I) fRSPEAK(I)
extern void fSETPRM(long,long,long);
#define SETPRM(FIRST,P1,P2) fSETPRM(FIRST,P1,P2)
extern void fGETIN(long*,long*,long*,long*);
#define GETIN(WORD1,WORD1X,WORD2,WORD2X) fGETIN(&WORD1,&WORD1X,&WORD2,&WORD2X)
extern long fYES(long,long,long);
#define YES(X,Y,Z) fYES(X,Y,Z)
extern void fGETIN(FILE *,long*,long*,long*,long*);
#define GETIN(input,WORD1,WORD1X,WORD2,WORD2X) fGETIN(input,&WORD1,&WORD1X,&WORD2,&WORD2X)
extern long fYES(FILE *,long,long,long);
#define YES(input,X,Y,Z) fYES(input,X,Y,Z)
extern long fGETNUM(FILE *);
#define GETNUM(K) fGETNUM(K)
extern long fGETTXT(long,long,long,long);
@ -55,8 +55,6 @@ extern long fSETBIT(long);
#define SETBIT(BIT) fSETBIT(BIT)
extern long fTSTBIT(long,long);
#define TSTBIT(MASK,BIT) fTSTBIT(MASK,BIT)
extern long fRAN(long);
#define RAN(RANGE) fRAN(RANGE)
extern long fRNDVOC(long,long);
#define RNDVOC(CHAR,FORCE) fRNDVOC(CHAR,FORCE)
extern void fBUG(long);
@ -74,3 +72,6 @@ extern long fIABS(long);
#define IABS(N) fIABS(N)
extern long fMOD(long,long);
#define MOD(N,M) fMOD(N,M)
extern void set_seed(long);
extern unsigned long get_next_lcg_value(void);
extern long randrange(long);

46
tests/Makefile Normal file
View file

@ -0,0 +1,46 @@
# Test-suite makefile for reposurgeon
# Use absolute path so tests that change working directory still use
# scripts from parent directory. Note that using $PWD seems to fail
# here under Gitlab's CI environment.
PATH := $(realpath ..):$(realpath .):${PATH}
# Defeat annoying behavior under Mac OS X - builtin echo doesn't do -n
ECHO := /bin/echo
all: regress
@echo "=== No diff output is good news."
.SUFFIXES: .chk
clean:
rm -fr *~ adventure.text
# Show summary lines for all tests.
testlist:
@grep '^##' *.log
listcheck:
@for f in *.log; do \
if ( head -3 $$f | grep -q '^ *##' ); then :; else echo "$$f needs a description"; fi; \
done
# General regression testing of commands and output; look at the *.log and
# corresponding *.chk files to see which tests this runs.
TESTLOADS := $(shell ls -1 *.log | sed '/.log/s///')
buildregress:
@for file in $(TESTLOADS); do \
echo "Remaking $${file}.chk"; \
OPTS=`sed -n /#options:/s///p <$${file}.log`; \
advent $$OPTS <$${file}.log >$${file}.chk 2>&1 || exit 1; \
done
regress:
@for file in $(TESTLOADS); do \
$(ECHO) -n " $${file} "; grep '##' $${file}.log || echo ' ## (no description)'; \
OPTS=`sed -n /#options:/s///p <$${file}.log`; \
if advent $$OPTS < $${file}.log >/tmp/regress$$$$ 2>&1; \
then diff --text -u $${file}.chk /tmp/regress$$$$ || exit 1; \
else echo "*** Nonzero return status on $${file}!"; exit 1; fi \
done
@rm -f /tmp/regress$$$$
# end

140
tests/death-jump.chk Normal file
View file

@ -0,0 +1,140 @@
Welcome to Adventure!! Would you like instructions?
> n
You are standing at the end of a road before a small brick building.
Around you is a forest. A small stream flows out of the building and
down a gully.
> seed 1495774850
Seed set to 1495774850
You're in front of building.
> in
You are inside a building, a well house for a large spring.
There are some keys on the ground here.
There is a shiny brass lamp nearby.
There is food here.
There is a bottle of water here.
> take keys
OK
> take lamp
OK
> out
You're in front of building.
> s
You are in a valley in the forest beside a stream tumbling along a
rocky bed.
> s
At your feet all the water of the stream splashes into a 2-inch slit
in the rock. Downstream the streambed is bare rock.
> s
You are in a 20-foot depression floored with bare dirt. Set into the
dirt is a strong steel grate mounted in concrete. A dry streambed
leads into the depression.
The grate is locked.
> open grate
The grate is now unlocked.
> d
You are in a small chamber beneath a 3x3 steel grate to the surface.
A low crawl over cobbles leads inward to the west.
The grate is open.
> w
You are crawling over cobbles in a low passage. There is a dim light
at the east end of the passage.
There is a small wicker cage discarded nearby.
> w
It is now pitch dark. If you proceed you will likely fall into a pit.
> light lamp
Your lamp is now on.
You are in a debris room filled with stuff washed in from the surface.
A low wide passage with cobbles becomes plugged with mud and debris
here, but an awkward canyon leads upward and west. In the mud someone
has scrawled, "MAGIC WORD XYZZY".
A three foot black rod with a rusty star on an end lies nearby.
> w
You are in an awkward sloping east/west canyon.
> w
You are in a splendid chamber thirty feet high. The walls are frozen
rivers of orange stone. An awkward canyon and a good passage exit
from east and west sides of the chamber.
A cheerful little bird is sitting here singing.
> w
At your feet is a small pit breathing traces of white mist. An east
passage ends here except for a small crack leading on.
Rough stone steps lead down the pit.
> down
You are at one end of a vast hall stretching forward out of sight to
the west. There are openings to either side. Nearby, a wide stone
staircase leads downward. The hall is filled with wisps of white mist
swaying to and fro almost as if alive. A cold wind blows up the
staircase. There is a passage at the top of a dome behind you.
Rough stone steps lead up the dome.
> w
You are on the east bank of a fissure slicing clear across the hall.
The mist is quite thick here, and the fissure is too wide to jump.
> jump
You didn't make it.
Oh dear, you seem to have gotten yourself killed. I might be able to
help you out, but I've never really done this before. Do you want me
to try to reincarnate you?
> n
OK
You scored 51 out of a possible 430, using 18 turns.
Your score qualifies you as a novice class adventurer.
To achieve the next higher rating, you need 70 more points.

416
tests/dwarf.chk Normal file
View file

@ -0,0 +1,416 @@
Welcome to Adventure!! Would you like instructions?
> n
You are standing at the end of a road before a small brick building.
Around you is a forest. A small stream flows out of the building and
down a gully.
<<<<<<< HEAD
> seed 1495763690
Seed set to 1495763690
You're in front of building.
> seed 1495752222
Seed set to 1495752222
=======
> seed 1494912171
Seed set to 1494912171
>>>>>>> f9eca41 (Ensure the ZZZZ magic word is reproducible.)
You're in front of building.
> in
You are inside a building, a well house for a large spring.
There are some keys on the ground here.
There is a shiny brass lamp nearby.
There is food here.
There is a bottle of water here.
> take keys
OK
> take lamp
OK
> out
You're in front of building.
> down
You are in a valley in the forest beside a stream tumbling along a
rocky bed.
> s
At your feet all the water of the stream splashes into a 2-inch slit
in the rock. Downstream the streambed is bare rock.
> s
You are in a 20-foot depression floored with bare dirt. Set into the
dirt is a strong steel grate mounted in concrete. A dry streambed
leads into the depression.
The grate is locked.
> open grate
The grate is now unlocked.
> down
You are in a small chamber beneath a 3x3 steel grate to the surface.
A low crawl over cobbles leads inward to the west.
The grate is open.
> west
You are crawling over cobbles in a low passage. There is a dim light
at the east end of the passage.
There is a small wicker cage discarded nearby.
> take cage
OK
> west
It is now pitch dark. If you proceed you will likely fall into a pit.
> light lamp
Your lamp is now on.
You are in a debris room filled with stuff washed in from the surface.
A low wide passage with cobbles becomes plugged with mud and debris
here, but an awkward canyon leads upward and west. In the mud someone
has scrawled, "MAGIC WORD XYZZY".
A three foot black rod with a rusty star on an end lies nearby.
> take rod
OK
> xyzzy
>>Foof!<<
You're inside building.
There is food here.
There is a bottle of water here.
> xyzzy
>>Foof!<<
You're in debris room.
> west
You are in an awkward sloping east/west canyon.
> drop rod
OK
> west
You are in a splendid chamber thirty feet high. The walls are frozen
rivers of orange stone. An awkward canyon and a good passage exit
from east and west sides of the chamber.
A cheerful little bird is sitting here singing.
> take bird
OK
> east
You are in an awkward sloping east/west canyon.
A three foot black rod with a rusty star on an end lies nearby.
> take rod
OK
> west
You're in bird chamber.
> west
At your feet is a small pit breathing traces of white mist. An east
passage ends here except for a small crack leading on.
Rough stone steps lead down the pit.
> down
You are at one end of a vast hall stretching forward out of sight to
the west. There are openings to either side. Nearby, a wide stone
staircase leads downward. The hall is filled with wisps of white mist
swaying to and fro almost as if alive. A cold wind blows up the
staircase. There is a passage at the top of a dome behind you.
Rough stone steps lead up the dome.
> south
This is a low room with a crude note on the wall. The note says,
"You won't get it up the steps".
There is a large sparkling nugget of gold here!
> take gold
OK
> n
You're in Hall of Mists.
> n
You are in the Hall of the Mountain King, with passages off in all
directions.
A huge green fierce snake bars the way!
> drop bird
The little bird attacks the green snake, and in an astounding flurry
drives the snake away.
> west
You are in the west side chamber of the Hall of the Mountain King.
A passage continues west and up here.
There are many coins here!
> take coins
OK
> e
You're in Hall of Mt King.
A cheerful little bird is sitting here singing.
> s
A little dwarf just walked around a corner, saw you, threw a little
axe at you which missed, cursed, and ran away.
You are in the south side chamber.
There is a little axe here.
There is precious jewelry here!
> take jewelry
OK
> n
There is a threatening little dwarf in the room with you!
One sharp nasty knife is thrown at you!
It misses!
You're in Hall of Mt King.
A cheerful little bird is sitting here singing.
> n
There is a threatening little dwarf in the room with you!
You are in a low n/s passage at a hole in the floor. The hole goes
down to an e/w passage.
There are bars of silver here!
> take silver
You can't carry anything more. You'll have to drop something first.
> drop cage
OK
> take silver
OK
> n
There is a threatening little dwarf in the room with you!
You are in a large room, with a passage to the south, a passage to the
west, and a wall of broken rock to the east. There is a large "Y2" on
a rock in the room's center.
> plugh
>>Foof!<<
You're inside building.
There is food here.
There is a bottle of water here.
> inven
You are currently holding the following:
Set of keys
Brass lantern
Black rod
Large gold nugget
Bars of silver
Precious jewelry
Rare coins
> drop jewelry
OK
> drop gold
OK
> drop silver
OK
> inven
You are currently holding the following:
Set of keys
Brass lantern
Black rod
Rare coins
> drop keys
OK
> plugh
>>Foof!<<
There are 2 threatening little dwarves in the room with you.
One sharp nasty knife is thrown at you!
It misses!
You're at "Y2".
> take knife
The dwarves' knives vanish as they strike the walls of the cave.
> throw axe
I see no axe here.
> s
There are 2 threatening little dwarves in the room with you.
You're in n/s passage above e/w passage.
There is a small wicker cage discarded nearby.
> s
There are 2 threatening little dwarves in the room with you.
You're in Hall of Mt King.
A cheerful little bird is sitting here singing.
> up
There are 2 threatening little dwarves in the room with you.
You're in Hall of Mists.
Rough stone steps lead up the dome.
> w
There are 2 threatening little dwarves in the room with you.
You are on the east bank of a fissure slicing clear across the hall.
The mist is quite thick here, and the fissure is too wide to jump.
> wave rod
A crystal bridge now spans the fissure.
> w
There are 2 threatening little dwarves in the room with you.
You are on the west side of the fissure in the Hall of Mists.
There are diamonds here!
A crystal bridge now spans the fissure.
> take diamonds
OK
<<<<<<< HEAD
>
You scored 97 out of a possible 430, using 60 turns.
=======
> e
A little dwarf with a big knife blocks your way.
There are 2 threatening little dwarves in the room with you.
2 of them throw knives at you!
One of them gets you!
Oh dear, you seem to have gotten yourself killed. I might be able to
help you out, but I've never really done this before. Do you want me
to try to reincarnate you?
> n
OK
You scored 81 out of a possible 430, using 55 turns.
>>>>>>> 6a6670e (Fix things so seed doesn't cost clock time.)
Your score qualifies you as a novice class adventurer.
To achieve the next higher rating, you need 24 more points.

59
tests/dwarf.log Normal file
View file

@ -0,0 +1,59 @@
## Death by dwarf.
n
seed 1494912171
in
take keys
take lamp
out
down
s
s
open grate
down
west
take cage
west
light lamp
take rod
xyzzy
xyzzy
west
drop rod
west
take bird
east
take rod
west
west
down
south
take gold
n
n
drop bird
west
take coins
e
s
drop cage
take jewelry
take axe
n
n
n
plugh
inven
drop jewelry
drop gold
inven
drop keys
plugh
s
s
up
w
wave rod
w
take diamonds
e
n

23
tests/oldstyle.chk Normal file
View file

@ -0,0 +1,23 @@
Initialising...
Welcome to Adventure!! Would you like instructions?
n
You are standing at the end of a road before a small brick building.
Around you is a forest. A small stream flows out of the building and
down a gully.
quit
Do you really want to quit now?
yes
OK
You scored 32 out of a possible 430, using 1 turn.
You are obviously a rank amateur. Better luck next time.
To achieve the next higher rating, you need 14 more points.

5
tests/oldstyle.log Normal file
View file

@ -0,0 +1,5 @@
## Simple quit
#options: -o
n
quit
yes

1909
tests/pirate.chk Normal file

File diff suppressed because it is too large Load diff

22
tests/quit.chk Normal file
View file

@ -0,0 +1,22 @@
Welcome to Adventure!! Would you like instructions?
> n
You are standing at the end of a road before a small brick building.
Around you is a forest. A small stream flows out of the building and
down a gully.
> quit
Do you really want to quit now?
> yes
OK
You scored 32 out of a possible 430, using 1 turn.
You are obviously a rank amateur. Better luck next time.
To achieve the next higher rating, you need 14 more points.