diff --git a/.gitignore b/.gitignore index ddb549b..fcfc9f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: (C) Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause advent *.gcda *.gcno diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0d0ab9d..b8f4b91 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: (C) Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause stages: - ci-build - build diff --git a/.shipper b/.shipper index a34d7d6..5e5904c 100644 --- a/.shipper +++ b/.shipper @@ -1,3 +1,5 @@ +#SPDX-FileCopyrightText: (C) Eric S. Raymond +#SPDX-License-Identifier: BSD-2-Clause extralines="""

There is a code coverage analysis and a symbol coverage analysis

""" diff --git a/COPYING b/COPYING index c4da3e1..b1de571 100644 --- a/COPYING +++ b/COPYING @@ -1,7 +1,4 @@ - BSD LICENSE - -Copyright (c) 1977, 2005 by Will Crowther and Don Woods -Copyright (c) 2017 by Eric S. Raymond + BSD 2-Clause LICENSE Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/Dockerfile.ci b/Dockerfile.ci index 535ed65..b8eeef5 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -1,5 +1,7 @@ # This image is built by the Gitlab CI pipeline to be used in subsequent # pipeline steps. +# SPDX-FileCopyrightText: (C) Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause FROM ubuntu @@ -7,4 +9,4 @@ FROM ubuntu ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update -RUN apt-get install --yes --no-install-recommends make gcc-10 libedit-dev libasan6 libubsan1 python3 python3-yaml lcov asciidoc libxslt1.1 pkg-config docbook-xml xsltproc +RUN apt-get install --yes --no-install-recommends make gcc libedit-dev libasan6 libubsan1 python3 python3-yaml lcov asciidoctor libxslt1.1 pkg-config docbook-xml xsltproc diff --git a/INSTALL.adoc b/INSTALL.adoc index a51b8ac..fb93b7d 100644 --- a/INSTALL.adoc +++ b/INSTALL.adoc @@ -1,4 +1,6 @@ = Installing Open Adventure = +// SPDX-FileCopyrightText: (C) Eric S. Raymond +// SPDX-License-Identifier: CC-BY-4.0 Installation now requires Python3 due to a security issue with the YAML library. @@ -19,3 +21,7 @@ You can also use pip to install PyYAML: `pip3 install PyYAML`. 4. Optionally run a regression test on the code with `make check`. 5. Run `./advent` to play. + +6. If you want to buld the documentation you will need asciidoctor. + +7. Running the regression tests requires batchspell diff --git a/Makefile b/Makefile index 59f7e4e..e4e31cb 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,18 @@ # Makefile for the open-source release of adventure 2.5 -# To build with save/resume disabled, pass CFLAGS="-DADVENT_NOSAVE" +# SPDX-FileCopyrightText: (C) Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause -VERS=$(shell sed -n +// SPDX-License-Identifier: CC-BY-4.0 + +1.20: 2024-09-23:: + Make oldstyle correctly suppress line editing. + +1.19: 2024-06-27:: + Ensore that the KNIVES_VANISH message can't issue twice. + +1.18: 2024-02-15:: + Bring the manual page fully up to date. + +1.17: 2024-01-02:: + Saying Z'ZZZ at reservoir no longer causes the waters to part and crash. + +1.16: 2023-04-15:: + Savefiles now have an identifying magic cookie at the front. + Resume detects if a save has incompatible endianness. + +1.15: 2023-04-03:: + Commands in magic-word sequence now interrupt it, as in original. + Bug fix for bird not starting caged in endgame. + +1.14: 2023-03-09:: + Added -a option for BBS door systems. + -o reverts to the old message on some failed magic words. + Typo fixes and documentation polishing. 1.13: 2023-02-28:: Fixed slightly buggy emission of end-of-game messages on a win. 1.12: 2023-02-06:: - The bug and todo list has been cleared. + The bug and todo list has been cleared; project declared finished. Correctness has been systematically tested against the 1995 code. Typo fixes and documentation polishing. @@ -14,7 +41,7 @@ 1.10: 2022-04-06:: Fixed a bug that manifested after two "fly rug" commands - third one fails. - Fix some giltches in processing fee fie foe foo. + Fix some glitches in processing fee fie foe foo. Correct some object start states and reading-related glitches in the endgame. 1.9: 2020-08-27:: diff --git a/README.adoc b/README.adoc index 03f249d..6b25f7f 100644 --- a/README.adoc +++ b/README.adoc @@ -1,4 +1,6 @@ = README for Open Adventure = +// SPDX-FileCopyrightText: (C) Eric S. Raymond +// SPDX-License-Identifier: CC-BY-4.0 If you are reading this anywhere but at http://www.catb.org/~esr/open-adventure you can go there for tarball downloads and other resources. diff --git a/actions.c b/actions.c index a2105f8..469e92f 100644 --- a/actions.c +++ b/actions.c @@ -1,1613 +1,1659 @@ /* - * Actions for the duneon-running code. + * Actions for the dungeon-running code. * - * Copyright (c) 1977, 2005 by Will Crowther and Don Woods - * Copyright (c) 2017 by Eric S. Raymond - * SPDX-License-Identifier: BSD-2-clause + * SPDX-FileCopyrightText: (C) 1977, 2005 Will Crowther and Don Woods + * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include -#include -#include "advent.h" -#include "dungeon.h" #include +#include +#include +#include + +#include "advent.h" static phase_codes_t fill(verb_t, obj_t); -static phase_codes_t attack(command_t command) -/* Attack. Assume target if unambiguous. "Throw" also links here. - * Attackable objects fall into two categories: enemies (snake, - * dwarf, etc.) and others (bird, clam, machine). Ambiguous if 2 - * enemies, or no enemies but 2 others. */ -{ - verb_t verb = command.verb; - obj_t obj = command.obj; +static phase_codes_t attack(command_t command) { + /* Attack. Assume target if unambiguous. "Throw" also links here. + * Attackable objects fall into two categories: enemies (snake, + * dwarf, etc.) and others (bird, clam, machine). Ambiguous if 2 + * enemies, or no enemies but 2 others. */ + verb_t verb = command.verb; + obj_t obj = command.obj; - if (obj == INTRANSITIVE) { - int changes = 0; - if (atdwrf(game.loc) > 0) { - obj = DWARF; - ++changes; - } - if (HERE(SNAKE)) { - obj = SNAKE; - ++changes; - } - if (AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS) { - obj = DRAGON; - ++changes; - } - if (AT(TROLL)) { - obj = TROLL; - ++changes; - } - if (AT(OGRE)) { - obj = OGRE; - ++changes; - } - if (HERE(BEAR) && game.prop[BEAR] == UNTAMED_BEAR) { - obj = BEAR; - ++changes; - } - /* check for low-priority targets */ - if (obj == INTRANSITIVE) { - /* Can't attack bird or machine by throwing axe. */ - if (HERE(BIRD) && verb != THROW) { - obj = BIRD; - ++changes; - } - if (HERE(VEND) && verb != THROW) { - obj = VEND; - ++changes; - } - /* Clam and oyster both treated as clam for intransitive case; - * no harm done. */ - if (HERE(CLAM) || HERE(OYSTER)) { - obj = CLAM; - ++changes; - } - } - if (changes >= 2) - return GO_UNKNOWN; - } + if (obj == INTRANSITIVE) { + int changes = 0; + if (atdwrf(game.loc) > 0) { + obj = DWARF; + ++changes; + } + if (HERE(SNAKE)) { + obj = SNAKE; + ++changes; + } + if (AT(DRAGON) && game.objects[DRAGON].prop == DRAGON_BARS) { + obj = DRAGON; + ++changes; + } + if (AT(TROLL)) { + obj = TROLL; + ++changes; + } + if (AT(OGRE)) { + obj = OGRE; + ++changes; + } + if (HERE(BEAR) && game.objects[BEAR].prop == UNTAMED_BEAR) { + obj = BEAR; + ++changes; + } + /* check for low-priority targets */ + if (obj == INTRANSITIVE) { + /* Can't attack bird or machine by throwing axe. */ + if (HERE(BIRD) && verb != THROW) { + obj = BIRD; + ++changes; + } + if (HERE(VEND) && verb != THROW) { + obj = VEND; + ++changes; + } + /* Clam and oyster both treated as clam for intransitive + * case; no harm done. */ + if (HERE(CLAM) || HERE(OYSTER)) { + obj = CLAM; + ++changes; + } + } + if (changes >= 2) { + return GO_UNKNOWN; + } + } - if (obj == BIRD) { - if (game.closed) { - rspeak(UNHAPPY_BIRD); - } else { - DESTROY(BIRD); - rspeak(BIRD_DEAD); - } - return GO_CLEAROBJ; - } - if (obj == VEND) { - state_change(VEND, - game.prop[VEND] == VEND_BLOCKS ? VEND_UNBLOCKS : VEND_BLOCKS); + if (obj == BIRD) { + if (game.closed) { + rspeak(UNHAPPY_BIRD); + } else { + DESTROY(BIRD); + rspeak(BIRD_DEAD); + } + return GO_CLEAROBJ; + } + if (obj == VEND) { + state_change(VEND, game.objects[VEND].prop == VEND_BLOCKS + ? VEND_UNBLOCKS + : VEND_BLOCKS); - return GO_CLEAROBJ; - } + return GO_CLEAROBJ; + } - if (obj == BEAR) { - switch (game.prop[BEAR]) { - case UNTAMED_BEAR: - rspeak(BEAR_HANDS); - break; - case SITTING_BEAR: - rspeak(BEAR_CONFUSED); - break; - case CONTENTED_BEAR: - rspeak(BEAR_CONFUSED); - break; - case BEAR_DEAD: - rspeak(ALREADY_DEAD); - break; - } - return GO_CLEAROBJ; - } - if (obj == DRAGON && game.prop[DRAGON] == DRAGON_BARS) { - /* Fun stuff for dragon. If he insists on attacking it, win! - * Set game.prop to dead, move dragon to central loc (still - * fixed), move rug there (not fixed), and move him there, - * too. Then do a null motion to get new description. */ - rspeak(BARE_HANDS_QUERY); - if (!silent_yes_or_no()) { - speak(arbitrary_messages[NASTY_DRAGON]); - return GO_MOVE; - } - state_change(DRAGON, DRAGON_DEAD); - game.prop[RUG] = RUG_FLOOR; - /* Hardcoding LOC_SECRET5 as the dragon's death location is ugly. - * The way it was computed before was worse; it depended on the - * two dragon locations being LOC_SECRET4 and LOC_SECRET6 and - * LOC_SECRET5 being right between them. - */ - move(DRAGON + NOBJECTS, IS_FIXED); - move(RUG + NOBJECTS, IS_FREE); - move(DRAGON, LOC_SECRET5); - move(RUG, LOC_SECRET5); - drop(BLOOD, LOC_SECRET5); - for (obj_t i = 1; i <= NOBJECTS; i++) { - if (game.place[i] == objects[DRAGON].plac || - game.place[i] == objects[DRAGON].fixd) - move(i, LOC_SECRET5); - } - game.loc = LOC_SECRET5; - return GO_MOVE; - } + if (obj == BEAR) { + switch (game.objects[BEAR].prop) { + case UNTAMED_BEAR: + rspeak(BEAR_HANDS); + break; + case SITTING_BEAR: + rspeak(BEAR_CONFUSED); + break; + case CONTENTED_BEAR: + rspeak(BEAR_CONFUSED); + break; + case BEAR_DEAD: + rspeak(ALREADY_DEAD); + break; + } + return GO_CLEAROBJ; + } + if (obj == DRAGON && game.objects[DRAGON].prop == DRAGON_BARS) { + /* Fun stuff for dragon. If he insists on attacking it, win! + * Set game.prop to dead, move dragon to central loc (still + * fixed), move rug there (not fixed), and move him there, + * too. Then do a null motion to get new description. */ + rspeak(BARE_HANDS_QUERY); + if (!silent_yes_or_no()) { + speak(arbitrary_messages[NASTY_DRAGON]); + return GO_MOVE; + } + state_change(DRAGON, DRAGON_DEAD); + game.objects[RUG].prop = RUG_FLOOR; + /* Hardcoding LOC_SECRET5 as the dragon's death location is + * ugly. The way it was computed before was worse; it depended + * on the two dragon locations being LOC_SECRET4 and LOC_SECRET6 + * and LOC_SECRET5 being right between them. + */ + move(DRAGON + NOBJECTS, IS_FIXED); + move(RUG + NOBJECTS, IS_FREE); + move(DRAGON, LOC_SECRET5); + move(RUG, LOC_SECRET5); + drop(BLOOD, LOC_SECRET5); + for (obj_t i = 1; i <= NOBJECTS; i++) { + if (game.objects[i].place == objects[DRAGON].plac || + game.objects[i].place == objects[DRAGON].fixd) { + move(i, LOC_SECRET5); + } + } + game.loc = LOC_SECRET5; + return GO_MOVE; + } - if (obj == OGRE) { - rspeak(OGRE_DODGE); - if (atdwrf(game.loc) == 0) - return GO_CLEAROBJ; + if (obj == OGRE) { + rspeak(OGRE_DODGE); + if (atdwrf(game.loc) == 0) { + return GO_CLEAROBJ; + } + rspeak(KNIFE_THROWN); + DESTROY(OGRE); + int dwarves = 0; + for (int i = 1; i < PIRATE; i++) { + if (game.dwarves[i].loc == game.loc) { + ++dwarves; + game.dwarves[i].loc = LOC_LONGWEST; + game.dwarves[i].seen = false; + } + } + rspeak((dwarves > 1) ? OGRE_PANIC1 : OGRE_PANIC2); + return GO_CLEAROBJ; + } - rspeak(KNIFE_THROWN); - DESTROY(OGRE); - int dwarves = 0; - for (int i = 1; i < PIRATE; i++) { - if (game.dloc[i] == game.loc) { - ++dwarves; - game.dloc[i] = LOC_LONGWEST; - game.dseen[i] = false; - } - } - rspeak((dwarves > 1) ? - OGRE_PANIC1 : - OGRE_PANIC2); - return GO_CLEAROBJ; - } - - switch (obj) { - case INTRANSITIVE: - rspeak(NO_TARGET); - break; - case CLAM: - case OYSTER: - rspeak(SHELL_IMPERVIOUS); - break; - case SNAKE: - rspeak(SNAKE_WARNING); - break; - case DWARF: - if (game.closed) { - return GO_DWARFWAKE; - } - rspeak(BARE_HANDS_QUERY); - break; - case DRAGON: - rspeak(ALREADY_DEAD); - break; - case TROLL: - rspeak(ROCKY_TROLL); - break; - default: - speak(actions[verb].message); - } - return GO_CLEAROBJ; + switch (obj) { + case INTRANSITIVE: + rspeak(NO_TARGET); + break; + case CLAM: + case OYSTER: + rspeak(SHELL_IMPERVIOUS); + break; + case SNAKE: + rspeak(SNAKE_WARNING); + break; + case DWARF: + if (game.closed) { + return GO_DWARFWAKE; + } + rspeak(BARE_HANDS_QUERY); + break; + case DRAGON: + rspeak(ALREADY_DEAD); + break; + case TROLL: + rspeak(ROCKY_TROLL); + break; + default: + speak(actions[verb].message); + } + return GO_CLEAROBJ; } -static phase_codes_t bigwords(vocab_t id) -/* Only called on FEE FIE FOE FOO (AND FUM). Advance to next state if given - * in proper order. Look up foo in special section of vocab to determine which - * word we've got. Last word zips the eggs back to the giant room (unless - * already there). */ -{ - if ((game.foobar == WORD_EMPTY && id == FEE) || - (game.foobar == FEE && id == FIE) || - (game.foobar == FIE && id == FOE) || - (game.foobar == FOE && id == FOO) || - (game.foobar == FOE && id == FUM)) { - game.foobar = id; - if ((id != FOO) && (id != FUM)) { - rspeak(OK_MAN); - return GO_CLEAROBJ; - } - game.foobar = WORD_EMPTY; - if (game.place[EGGS] == objects[EGGS].plac || - (TOTING(EGGS) && game.loc == objects[EGGS].plac)) { - rspeak(NOTHING_HAPPENS); - return GO_CLEAROBJ; - } else if (id == FUM) { - goto fum; - } else { - /* Bring back troll if we steal the eggs back from him before - * crossing. */ - if (game.place[EGGS] == LOC_NOWHERE && game.place[TROLL] == LOC_NOWHERE && game.prop[TROLL] == TROLL_UNPAID) - game.prop[TROLL] = TROLL_PAIDONCE; - if (HERE(EGGS)) - pspeak(EGGS, look, true, EGGS_VANISHED); - else if (game.loc == objects[EGGS].plac) - pspeak(EGGS, look, true, EGGS_HERE); - else - pspeak(EGGS, look, true, EGGS_DONE); - move(EGGS, objects[EGGS].plac); +static phase_codes_t bigwords(vocab_t id) { + /* Only called on FEE FIE FOE FOO (AND FUM). Advance to next state if + * given in proper order. Look up foo in special section of vocab to + * determine which word we've got. Last word zips the eggs back to the + * giant room (unless already there). */ + int foobar = abs(game.foobar); - return GO_CLEAROBJ; - } - } else { -fum: - if (game.loc == LOC_GIANTROOM) { - rspeak(START_OVER); - } else { - /* This is new behavior in Open Adventure - sounds better when - * player isn't in the Giant Room. */ - rspeak(NOTHING_HAPPENS); - } - game.foobar = WORD_EMPTY; - return GO_CLEAROBJ; - } + /* Only FEE can start a magic-word sequence. */ + if ((foobar == WORD_EMPTY) && + (id == FIE || id == FOE || id == FOO || id == FUM)) { + rspeak(NOTHING_HAPPENS); + return GO_CLEAROBJ; + } + + if ((foobar == WORD_EMPTY && id == FEE) || + (foobar == FEE && id == FIE) || (foobar == FIE && id == FOE) || + (foobar == FOE && id == FOO)) { + game.foobar = id; + if (id != FOO) { + rspeak(OK_MAN); + return GO_CLEAROBJ; + } + game.foobar = WORD_EMPTY; + if (game.objects[EGGS].place == objects[EGGS].plac || + (TOTING(EGGS) && game.loc == objects[EGGS].plac)) { + rspeak(NOTHING_HAPPENS); + return GO_CLEAROBJ; + } else { + /* Bring back troll if we steal the eggs back from him + * before crossing. */ + if (game.objects[EGGS].place == LOC_NOWHERE && + game.objects[TROLL].place == LOC_NOWHERE && + game.objects[TROLL].prop == TROLL_UNPAID) { + game.objects[TROLL].prop = TROLL_PAIDONCE; + } + if (HERE(EGGS)) { + pspeak(EGGS, look, true, EGGS_VANISHED); + } else if (game.loc == objects[EGGS].plac) { + pspeak(EGGS, look, true, EGGS_HERE); + } else { + pspeak(EGGS, look, true, EGGS_DONE); + } + move(EGGS, objects[EGGS].plac); + + return GO_CLEAROBJ; + } + } else { + /* Magic-word sequence was started but is incorrect */ + if (settings.oldstyle || game.seenbigwords) { + rspeak(START_OVER); + } else { + rspeak(WELL_POINTLESS); + } + game.foobar = WORD_EMPTY; + return GO_CLEAROBJ; + } } -static void blast(void) -/* Blast. No effect unless you've got dynamite, which is a neat trick! */ -{ - if (game.prop[ROD2] == STATE_NOTFOUND || - !game.closed) - rspeak(REQUIRES_DYNAMITE); - else { - if (HERE(ROD2)) { - game.bonus = splatter; - rspeak(SPLATTER_MESSAGE); - } else if (game.loc == LOC_NE) { - game.bonus = defeat; - rspeak(DEFEAT_MESSAGE); - } else { - game.bonus = victory; - rspeak(VICTORY_MESSAGE); - } - terminate(endgame); - } +static void blast(void) { + /* Blast. No effect unless you've got dynamite, which is a neat trick! + */ + if (OBJECT_IS_NOTFOUND(ROD2) || !game.closed) { + rspeak(REQUIRES_DYNAMITE); + } else { + if (HERE(ROD2)) { + game.bonus = splatter; + rspeak(SPLATTER_MESSAGE); + } else if (game.loc == LOC_NE) { + game.bonus = defeat; + rspeak(DEFEAT_MESSAGE); + } else { + game.bonus = victory; + rspeak(VICTORY_MESSAGE); + } + terminate(endgame); + } } -static phase_codes_t vbreak(verb_t verb, obj_t obj) -/* Break. Only works for mirror in repository and, of course, the vase. */ -{ - switch (obj) { - case MIRROR: - if (game.closed) { - state_change(MIRROR, MIRROR_BROKEN); - return GO_DWARFWAKE; - } else { - rspeak(TOO_FAR); - break; - } - case VASE: - if (game.prop[VASE] == VASE_WHOLE) { - if (TOTING(VASE)) - drop(VASE, game.loc); - state_change(VASE, VASE_BROKEN); - game.fixed[VASE] = IS_FIXED; - break; - } - /* FALLTHRU */ - default: - speak(actions[verb].message); - } - return (GO_CLEAROBJ); +static phase_codes_t vbreak(verb_t verb, obj_t obj) { + /* Break. Only works for mirror in repository and, of course, the + * vase. */ + switch (obj) { + case MIRROR: + if (game.closed) { + state_change(MIRROR, MIRROR_BROKEN); + return GO_DWARFWAKE; + } else { + rspeak(TOO_FAR); + break; + } + case VASE: + if (game.objects[VASE].prop == VASE_WHOLE) { + if (TOTING(VASE)) { + drop(VASE, game.loc); + } + state_change(VASE, VASE_BROKEN); + game.objects[VASE].fixed = IS_FIXED; + break; + } + /* FALLTHRU */ + default: + speak(actions[verb].message); + } + return (GO_CLEAROBJ); } -static phase_codes_t brief(void) -/* Brief. Intransitive only. Suppress full descriptions after first time. */ -{ - game.abbnum = 10000; - game.detail = 3; - rspeak(BRIEF_CONFIRM); - return GO_CLEAROBJ; +static phase_codes_t brief(void) { + /* Brief. Intransitive only. Suppress full descriptions after first + * time. */ + game.abbnum = 10000; + game.detail = 3; + rspeak(BRIEF_CONFIRM); + return GO_CLEAROBJ; } -static phase_codes_t vcarry(verb_t verb, obj_t obj) -/* Carry an object. Special cases for bird and cage (if bird in cage, can't - * take one without the other). Liquids also special, since they depend on - * status of bottle. Also various side effects, etc. */ -{ - if (obj == INTRANSITIVE) { - /* Carry, no object given yet. OK if only one object present. */ - if (game.atloc[game.loc] == NO_OBJECT || - game.link[game.atloc[game.loc]] != 0 || - atdwrf(game.loc) > 0) - return GO_UNKNOWN; - obj = game.atloc[game.loc]; - } +static phase_codes_t vcarry(verb_t verb, obj_t obj) { + /* Carry an object. Special cases for bird and cage (if bird in cage, + * can't take one without the other). Liquids also special, since they + * depend on status of bottle. Also various side effects, etc. */ + if (obj == INTRANSITIVE) { + /* Carry, no object given yet. OK if only one object present. + */ + if (game.locs[game.loc].atloc == NO_OBJECT || + game.link[game.locs[game.loc].atloc] != 0 || + atdwrf(game.loc) > 0) { + return GO_UNKNOWN; + } + obj = game.locs[game.loc].atloc; + } - if (TOTING(obj)) { - speak(actions[verb].message); - return GO_CLEAROBJ; - } + if (TOTING(obj)) { + speak(actions[verb].message); + return GO_CLEAROBJ; + } - if (obj == MESSAG) { - rspeak(REMOVE_MESSAGE); - DESTROY(MESSAG); - return GO_CLEAROBJ; - } + if (obj == MESSAG) { + rspeak(REMOVE_MESSAGE); + DESTROY(MESSAG); + return GO_CLEAROBJ; + } - if (game.fixed[obj] != IS_FREE) { - switch (obj) { - case PLANT: - /* Next guard tests whether plant is tiny or stashed */ - rspeak(game.prop[PLANT] <= PLANT_THIRSTY ? DEEP_ROOTS : YOU_JOKING); - break; - case BEAR: - rspeak( game.prop[BEAR] == SITTING_BEAR ? BEAR_CHAINED : YOU_JOKING); - break; - case CHAIN: - rspeak( game.prop[BEAR] != UNTAMED_BEAR ? STILL_LOCKED : YOU_JOKING); - break; - case RUG: - rspeak(game.prop[RUG] == RUG_HOVER ? RUG_HOVERS : YOU_JOKING); - break; - case URN: - rspeak(URN_NOBUDGE); - break; - case CAVITY: - rspeak(DOUGHNUT_HOLES); - break; - case BLOOD: - rspeak(FEW_DROPS); - break; - case SIGN: - rspeak(HAND_PASSTHROUGH); - break; - default: - rspeak(YOU_JOKING); - } - return GO_CLEAROBJ; - } + if (game.objects[obj].fixed != IS_FREE) { + switch (obj) { + case PLANT: + rspeak((game.objects[PLANT].prop == PLANT_THIRSTY || + OBJECT_IS_STASHED(PLANT)) + ? DEEP_ROOTS + : YOU_JOKING); + break; + case BEAR: + rspeak(game.objects[BEAR].prop == SITTING_BEAR + ? BEAR_CHAINED + : YOU_JOKING); + break; + case CHAIN: + rspeak(game.objects[BEAR].prop != UNTAMED_BEAR + ? STILL_LOCKED + : YOU_JOKING); + break; + case RUG: + rspeak(game.objects[RUG].prop == RUG_HOVER + ? RUG_HOVERS + : YOU_JOKING); + break; + case URN: + rspeak(URN_NOBUDGE); + break; + case CAVITY: + rspeak(DOUGHNUT_HOLES); + break; + case BLOOD: + rspeak(FEW_DROPS); + break; + case SIGN: + rspeak(HAND_PASSTHROUGH); + break; + default: + rspeak(YOU_JOKING); + } + return GO_CLEAROBJ; + } - if (obj == WATER || - obj == OIL) { - if (!HERE(BOTTLE) || - LIQUID() != obj) { - if (!TOTING(BOTTLE)) { - rspeak(NO_CONTAINER); - return GO_CLEAROBJ; - } - if (game.prop[BOTTLE] == EMPTY_BOTTLE) { - return (fill(verb, BOTTLE)); - } else - rspeak(BOTTLE_FULL); - return GO_CLEAROBJ; - } - obj = BOTTLE; - } + if (obj == WATER || obj == OIL) { + if (!HERE(BOTTLE) || LIQUID() != obj) { + if (!TOTING(BOTTLE)) { + rspeak(NO_CONTAINER); + return GO_CLEAROBJ; + } + if (game.objects[BOTTLE].prop == EMPTY_BOTTLE) { + return (fill(verb, BOTTLE)); + } else { + rspeak(BOTTLE_FULL); + } + return GO_CLEAROBJ; + } + obj = BOTTLE; + } - if (game.holdng >= INVLIMIT) { - rspeak(CARRY_LIMIT); - return GO_CLEAROBJ; + if (game.holdng >= INVLIMIT) { + rspeak(CARRY_LIMIT); + return GO_CLEAROBJ; + } - } + if (obj == BIRD && game.objects[BIRD].prop != BIRD_CAGED && + !OBJECT_IS_STASHED(BIRD)) { + if (game.objects[BIRD].prop == BIRD_FOREST_UNCAGED) { + DESTROY(BIRD); + rspeak(BIRD_CRAP); + return GO_CLEAROBJ; + } + if (!TOTING(CAGE)) { + rspeak(CANNOT_CARRY); + return GO_CLEAROBJ; + } + if (TOTING(ROD)) { + rspeak(BIRD_EVADES); + return GO_CLEAROBJ; + } + game.objects[BIRD].prop = BIRD_CAGED; + } + if ((obj == BIRD || obj == CAGE) && + OBJECT_STATE_EQUALS(BIRD, BIRD_CAGED)) { + /* expression maps BIRD to CAGE and CAGE to BIRD */ + carry(BIRD + CAGE - obj, game.loc); + } - if (obj == BIRD && game.prop[BIRD] != BIRD_CAGED && STASHED(BIRD) != BIRD_CAGED) { - if (game.prop[BIRD] == BIRD_FOREST_UNCAGED) { - DESTROY(BIRD); - rspeak(BIRD_CRAP); - return GO_CLEAROBJ; - } - if (!TOTING(CAGE)) { - rspeak(CANNOT_CARRY); - return GO_CLEAROBJ; - } - if (TOTING(ROD)) { - rspeak(BIRD_EVADES); - return GO_CLEAROBJ; - } - game.prop[BIRD] = BIRD_CAGED; - } - if ((obj == BIRD || - obj == CAGE) && - (game.prop[BIRD] == BIRD_CAGED || STASHED(BIRD) == BIRD_CAGED)) { - /* expression maps BIRD to CAGE and CAGE to BIRD */ - carry(BIRD + CAGE - obj, game.loc); - } + carry(obj, game.loc); - carry(obj, game.loc); + if (obj == BOTTLE && LIQUID() != NO_OBJECT) { + game.objects[LIQUID()].place = CARRIED; + } - if (obj == BOTTLE && LIQUID() != NO_OBJECT) - game.place[LIQUID()] = CARRIED; - - if (GSTONE(obj) && game.prop[obj] != STATE_FOUND) { - game.prop[obj] = STATE_FOUND; - game.prop[CAVITY] = CAVITY_EMPTY; - } - rspeak(OK_MAN); - return GO_CLEAROBJ; + if (GSTONE(obj) && !OBJECT_IS_FOUND(obj)) { + OBJECT_SET_FOUND(obj); + game.objects[CAVITY].prop = CAVITY_EMPTY; + } + rspeak(OK_MAN); + return GO_CLEAROBJ; } -static int chain(verb_t verb) -/* Do something to the bear's chain */ -{ - if (verb != LOCK) { - if (game.prop[BEAR] == UNTAMED_BEAR) { - rspeak(BEAR_BLOCKS); - return GO_CLEAROBJ; - } - if (game.prop[CHAIN] == CHAIN_HEAP) { - rspeak(ALREADY_UNLOCKED); - return GO_CLEAROBJ; - } - game.prop[CHAIN] = CHAIN_HEAP; - game.fixed[CHAIN] = IS_FREE; - if (game.prop[BEAR] != BEAR_DEAD) - game.prop[BEAR] = CONTENTED_BEAR; +static int chain(verb_t verb) { + /* Do something to the bear's chain */ + if (verb != LOCK) { + if (game.objects[BEAR].prop == UNTAMED_BEAR) { + rspeak(BEAR_BLOCKS); + return GO_CLEAROBJ; + } + if (game.objects[CHAIN].prop == CHAIN_HEAP) { + rspeak(ALREADY_UNLOCKED); + return GO_CLEAROBJ; + } + game.objects[CHAIN].prop = CHAIN_HEAP; + game.objects[CHAIN].fixed = IS_FREE; + if (game.objects[BEAR].prop != BEAR_DEAD) { + game.objects[BEAR].prop = CONTENTED_BEAR; + } - switch (game.prop[BEAR]) { - // LCOV_EXCL_START - case BEAR_DEAD: - /* Can't be reached until the bear can die in some way other - * than a bridge collapse. Leave in in case this changes, but - * exclude from coverage testing. */ - game.fixed[BEAR] = IS_FIXED; - break; - // LCOV_EXCL_STOP - default: - game.fixed[BEAR] = IS_FREE; - } - rspeak(CHAIN_UNLOCKED); - return GO_CLEAROBJ; - } + switch (game.objects[BEAR].prop) { + // LCOV_EXCL_START + case BEAR_DEAD: + /* Can't be reached until the bear can die in some way + * other than a bridge collapse. Leave in in case this + * changes, but exclude from coverage testing. */ + game.objects[BEAR].fixed = IS_FIXED; + break; + // LCOV_EXCL_STOP + default: + game.objects[BEAR].fixed = IS_FREE; + } + rspeak(CHAIN_UNLOCKED); + return GO_CLEAROBJ; + } - if (game.prop[CHAIN] != CHAIN_HEAP) { - rspeak(ALREADY_LOCKED); - return GO_CLEAROBJ; - } - if (game.loc != objects[CHAIN].plac) { - rspeak(NO_LOCKSITE); - return GO_CLEAROBJ; - } + if (game.objects[CHAIN].prop != CHAIN_HEAP) { + rspeak(ALREADY_LOCKED); + return GO_CLEAROBJ; + } + if (game.loc != objects[CHAIN].plac) { + rspeak(NO_LOCKSITE); + return GO_CLEAROBJ; + } - game.prop[CHAIN] = CHAIN_FIXED; + game.objects[CHAIN].prop = CHAIN_FIXED; - if (TOTING(CHAIN)) - drop(CHAIN, game.loc); - game.fixed[CHAIN] = IS_FIXED; + if (TOTING(CHAIN)) { + drop(CHAIN, game.loc); + } + game.objects[CHAIN].fixed = IS_FIXED; - rspeak(CHAIN_LOCKED); - return GO_CLEAROBJ; + rspeak(CHAIN_LOCKED); + return GO_CLEAROBJ; } -static phase_codes_t discard(verb_t verb, obj_t obj) -/* Discard object. "Throw" also comes here for most objects. Special cases for - * bird (might attack snake or dragon) and cage (might contain bird) and vase. - * Drop coins at vending machine for extra batteries. */ -{ - if (obj == ROD && !TOTING(ROD) && TOTING(ROD2)) { - obj = ROD2; - } +static phase_codes_t discard(verb_t verb, obj_t obj) { + /* Discard object. "Throw" also comes here for most objects. Special + * cases for bird (might attack snake or dragon) and cage (might contain + * bird) and vase. Drop coins at vending machine for extra batteries. */ + if (obj == ROD && !TOTING(ROD) && TOTING(ROD2)) { + obj = ROD2; + } - if (!TOTING(obj)) { - speak(actions[verb].message); - return GO_CLEAROBJ; - } + if (!TOTING(obj)) { + speak(actions[verb].message); + return GO_CLEAROBJ; + } - if (GSTONE(obj) && AT(CAVITY) && game.prop[CAVITY] != CAVITY_FULL) { - rspeak(GEM_FITS); - game.prop[obj] = STATE_IN_CAVITY; - game.prop[CAVITY] = CAVITY_FULL; - if (HERE(RUG) && ((obj == EMERALD && game.prop[RUG] != RUG_HOVER) || - (obj == RUBY && game.prop[RUG] == RUG_HOVER))) { - if (obj == RUBY) - rspeak(RUG_SETTLES); - else if (TOTING(RUG)) - rspeak(RUG_WIGGLES); - else - rspeak(RUG_RISES); - if (!TOTING(RUG) || obj == RUBY) { - int k = (game.prop[RUG] == RUG_HOVER) ? RUG_FLOOR : RUG_HOVER; - game.prop[RUG] = k; - if (k == RUG_HOVER) - k = objects[SAPPH].plac; - move(RUG + NOBJECTS, k); - } - } - drop(obj, game.loc); - return GO_CLEAROBJ; - } + if (GSTONE(obj) && AT(CAVITY) && + game.objects[CAVITY].prop != CAVITY_FULL) { + rspeak(GEM_FITS); + game.objects[obj].prop = STATE_IN_CAVITY; + game.objects[CAVITY].prop = CAVITY_FULL; + if (HERE(RUG) && + ((obj == EMERALD && game.objects[RUG].prop != RUG_HOVER) || + (obj == RUBY && game.objects[RUG].prop == RUG_HOVER))) { + if (obj == RUBY) { + rspeak(RUG_SETTLES); + } else if (TOTING(RUG)) { + rspeak(RUG_WIGGLES); + } else { + rspeak(RUG_RISES); + } + if (!TOTING(RUG) || obj == RUBY) { + int k = (game.objects[RUG].prop == RUG_HOVER) + ? RUG_FLOOR + : RUG_HOVER; + game.objects[RUG].prop = k; + if (k == RUG_HOVER) { + k = objects[SAPPH].plac; + } + move(RUG + NOBJECTS, k); + } + } + drop(obj, game.loc); + return GO_CLEAROBJ; + } - if (obj == COINS && HERE(VEND)) { - DESTROY(COINS); - drop(BATTERY, game.loc); - pspeak(BATTERY, look, true, FRESH_BATTERIES); - return GO_CLEAROBJ; - } + if (obj == COINS && HERE(VEND)) { + DESTROY(COINS); + drop(BATTERY, game.loc); + pspeak(BATTERY, look, true, FRESH_BATTERIES); + return GO_CLEAROBJ; + } - if (LIQUID() == obj) - obj = BOTTLE; - if (obj == BOTTLE && LIQUID() != NO_OBJECT) { - game.place[LIQUID()] = LOC_NOWHERE; - } + if (LIQUID() == obj) { + obj = BOTTLE; + } + if (obj == BOTTLE && LIQUID() != NO_OBJECT) { + game.objects[LIQUID()].place = LOC_NOWHERE; + } - if (obj == BEAR && AT(TROLL)) { - state_change(TROLL, TROLL_GONE); - move(TROLL, LOC_NOWHERE); - move(TROLL + NOBJECTS, IS_FREE); - move(TROLL2, objects[TROLL].plac); - move(TROLL2 + NOBJECTS, objects[TROLL].fixd); - juggle(CHASM); - drop(obj, game.loc); - return GO_CLEAROBJ; - } + if (obj == BEAR && AT(TROLL)) { + state_change(TROLL, TROLL_GONE); + move(TROLL, LOC_NOWHERE); + move(TROLL + NOBJECTS, IS_FREE); + move(TROLL2, objects[TROLL].plac); + move(TROLL2 + NOBJECTS, objects[TROLL].fixd); + juggle(CHASM); + drop(obj, game.loc); + return GO_CLEAROBJ; + } - if (obj == VASE) { - if (game.loc != objects[PILLOW].plac) { - state_change(VASE, AT(PILLOW) - ? VASE_WHOLE - : VASE_DROPPED); - if (game.prop[VASE] != VASE_WHOLE) - game.fixed[VASE] = IS_FIXED; - drop(obj, game.loc); - return GO_CLEAROBJ; - } - } + if (obj == VASE) { + if (game.loc != objects[PILLOW].plac) { + state_change(VASE, + AT(PILLOW) ? VASE_WHOLE : VASE_DROPPED); + if (game.objects[VASE].prop != VASE_WHOLE) { + game.objects[VASE].fixed = IS_FIXED; + } + drop(obj, game.loc); + return GO_CLEAROBJ; + } + } - if (obj == CAGE && game.prop[BIRD] == BIRD_CAGED) { - drop(BIRD, game.loc); - } + if (obj == CAGE && game.objects[BIRD].prop == BIRD_CAGED) { + drop(BIRD, game.loc); + } - if (obj == BIRD) { - if (AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS) { - rspeak(BIRD_BURNT); - DESTROY(BIRD); - return GO_CLEAROBJ; - } - if (HERE(SNAKE)) { - rspeak(BIRD_ATTACKS); - if (game.closed) - return GO_DWARFWAKE; - DESTROY(SNAKE); - /* Set game.prop for use by travel options */ - game.prop[SNAKE] = SNAKE_CHASED; - } else - rspeak(OK_MAN); + if (obj == BIRD) { + if (AT(DRAGON) && game.objects[DRAGON].prop == DRAGON_BARS) { + rspeak(BIRD_BURNT); + DESTROY(BIRD); + return GO_CLEAROBJ; + } + if (HERE(SNAKE)) { + rspeak(BIRD_ATTACKS); + if (game.closed) { + return GO_DWARFWAKE; + } + DESTROY(SNAKE); + /* Set game.prop for use by travel options */ + game.objects[SNAKE].prop = SNAKE_CHASED; + } else { + rspeak(OK_MAN); + } - game.prop[BIRD] = FOREST(game.loc) ? BIRD_FOREST_UNCAGED : BIRD_UNCAGED; - drop(obj, game.loc); - return GO_CLEAROBJ; - } + game.objects[BIRD].prop = + FOREST(game.loc) ? BIRD_FOREST_UNCAGED : BIRD_UNCAGED; + drop(obj, game.loc); + return GO_CLEAROBJ; + } - rspeak(OK_MAN); - drop(obj, game.loc); - return GO_CLEAROBJ; + rspeak(OK_MAN); + drop(obj, game.loc); + return GO_CLEAROBJ; } -static phase_codes_t drink(verb_t verb, obj_t obj) -/* Drink. If no object, assume water and look for it here. If water is in - * the bottle, drink that, else must be at a water loc, so drink stream. */ -{ - if (obj == INTRANSITIVE && LIQLOC(game.loc) != WATER && - (LIQUID() != WATER || !HERE(BOTTLE))) { - return GO_UNKNOWN; - } +static phase_codes_t drink(verb_t verb, obj_t obj) { + /* Drink. If no object, assume water and look for it here. If water + * is in the bottle, drink that, else must be at a water loc, so drink + * stream. */ + if (obj == INTRANSITIVE && LIQLOC(game.loc) != WATER && + (LIQUID() != WATER || !HERE(BOTTLE))) { + return GO_UNKNOWN; + } - if (obj == BLOOD) { - DESTROY(BLOOD); - state_change(DRAGON, DRAGON_BLOODLESS); - game.blooded = true; - return GO_CLEAROBJ; - } + if (obj == BLOOD) { + DESTROY(BLOOD); + state_change(DRAGON, DRAGON_BLOODLESS); + game.blooded = true; + return GO_CLEAROBJ; + } - if (obj != INTRANSITIVE && obj != WATER) { - rspeak(RIDICULOUS_ATTEMPT); - return GO_CLEAROBJ; - } - if (LIQUID() == WATER && HERE(BOTTLE)) { - game.place[WATER] = LOC_NOWHERE; - state_change(BOTTLE, EMPTY_BOTTLE); - return GO_CLEAROBJ; - } + if (obj != INTRANSITIVE && obj != WATER) { + rspeak(RIDICULOUS_ATTEMPT); + return GO_CLEAROBJ; + } + if (LIQUID() == WATER && HERE(BOTTLE)) { + game.objects[WATER].place = LOC_NOWHERE; + state_change(BOTTLE, EMPTY_BOTTLE); + return GO_CLEAROBJ; + } - speak(actions[verb].message); - return GO_CLEAROBJ; + speak(actions[verb].message); + return GO_CLEAROBJ; } -static phase_codes_t eat(verb_t verb, obj_t obj) -/* Eat. Intransitive: assume food if present, else ask what. Transitive: food - * ok, some things lose appetite, rest are ridiculous. */ -{ - switch (obj) { - case INTRANSITIVE: - if (!HERE(FOOD)) - return GO_UNKNOWN; - /* FALLTHRU */ - case FOOD: - DESTROY(FOOD); - rspeak(THANKS_DELICIOUS); - break; - case BIRD: - case SNAKE: - case CLAM: - case OYSTER: - case DWARF: - case DRAGON: - case TROLL: - case BEAR: - case OGRE: - rspeak(LOST_APPETITE); - break; - default: - speak(actions[verb].message); - } - return GO_CLEAROBJ; +static phase_codes_t eat(verb_t verb, obj_t obj) { + /* Eat. Intransitive: assume food if present, else ask what. + * Transitive: food ok, some things lose appetite, rest are ridiculous. + */ + switch (obj) { + case INTRANSITIVE: + if (!HERE(FOOD)) { + return GO_UNKNOWN; + } + /* FALLTHRU */ + case FOOD: + DESTROY(FOOD); + rspeak(THANKS_DELICIOUS); + break; + case BIRD: + case SNAKE: + case CLAM: + case OYSTER: + case DWARF: + case DRAGON: + case TROLL: + case BEAR: + case OGRE: + rspeak(LOST_APPETITE); + break; + default: + speak(actions[verb].message); + } + return GO_CLEAROBJ; } -static phase_codes_t extinguish(verb_t verb, obj_t obj) -/* Extinguish. Lamp, urn, dragon/volcano (nice try). */ -{ - if (obj == INTRANSITIVE) { - if (HERE(LAMP) && game.prop[LAMP] == LAMP_BRIGHT) - obj = LAMP; - if (HERE(URN) && game.prop[URN] == URN_LIT) - obj = URN; - if (obj == INTRANSITIVE) - return GO_UNKNOWN; - } +static phase_codes_t extinguish(verb_t verb, obj_t obj) { + /* Extinguish. Lamp, urn, dragon/volcano (nice try). */ + if (obj == INTRANSITIVE) { + if (HERE(LAMP) && game.objects[LAMP].prop == LAMP_BRIGHT) { + obj = LAMP; + } + if (HERE(URN) && game.objects[URN].prop == URN_LIT) { + obj = URN; + } + if (obj == INTRANSITIVE) { + return GO_UNKNOWN; + } + } - switch (obj) { - case URN: - if (game.prop[URN] != URN_EMPTY) { - state_change(URN, URN_DARK); - } else { - pspeak(URN, change, true, URN_DARK); - } - break; - case LAMP: - state_change(LAMP, LAMP_DARK); - rspeak(DARK(game.loc) ? - PITCH_DARK : - NO_MESSAGE); - break; - case DRAGON: - case VOLCANO: - rspeak(BEYOND_POWER); - break; - default: - speak(actions[verb].message); - } - return GO_CLEAROBJ; + switch (obj) { + case URN: + if (game.objects[URN].prop != URN_EMPTY) { + state_change(URN, URN_DARK); + } else { + pspeak(URN, change, true, URN_DARK); + } + break; + case LAMP: + state_change(LAMP, LAMP_DARK); + rspeak(IS_DARK_HERE() ? PITCH_DARK : NO_MESSAGE); + break; + case DRAGON: + case VOLCANO: + rspeak(BEYOND_POWER); + break; + default: + speak(actions[verb].message); + } + return GO_CLEAROBJ; } -static phase_codes_t feed(verb_t verb, obj_t obj) -/* Feed. If bird, no seed. Snake, dragon, troll: quip. If dwarf, make him - * mad. Bear, special. */ -{ - switch (obj) { - case BIRD: - rspeak(BIRD_PINING); - break; - case DRAGON: - if (game.prop[DRAGON] != DRAGON_BARS) - rspeak(RIDICULOUS_ATTEMPT); - else - rspeak(NOTHING_EDIBLE); - break; - case SNAKE: - if (!game.closed && HERE(BIRD)) { - DESTROY(BIRD); - rspeak(BIRD_DEVOURED); - } else - rspeak(NOTHING_EDIBLE); - break; - case TROLL: - rspeak(TROLL_VICES); - break; - case DWARF: - if (HERE(FOOD)) { - game.dflag += 2; - rspeak(REALLY_MAD); - } else - speak(actions[verb].message); - break; - case BEAR: - if (game.prop[BEAR] == BEAR_DEAD) { - rspeak(RIDICULOUS_ATTEMPT); - break; - } - if (game.prop[BEAR] == UNTAMED_BEAR) { - if (HERE(FOOD)) { - DESTROY(FOOD); - game.fixed[AXE] = IS_FREE; - game.prop[AXE] = AXE_HERE; - state_change(BEAR, SITTING_BEAR); - } else - rspeak(NOTHING_EDIBLE); - break; - } - speak(actions[verb].message); - break; - case OGRE: - if (HERE(FOOD)) - rspeak(OGRE_FULL); - else - speak(actions[verb].message); - break; - default: - rspeak(AM_GAME); - } - return GO_CLEAROBJ; +static phase_codes_t feed(verb_t verb, obj_t obj) { + /* Feed. If bird, no seed. Snake, dragon, troll: quip. If dwarf, + * make him mad. Bear, special. */ + switch (obj) { + case BIRD: + rspeak(BIRD_PINING); + break; + case DRAGON: + if (game.objects[DRAGON].prop != DRAGON_BARS) { + rspeak(RIDICULOUS_ATTEMPT); + } else { + rspeak(NOTHING_EDIBLE); + } + break; + case SNAKE: + if (!game.closed && HERE(BIRD)) { + DESTROY(BIRD); + rspeak(BIRD_DEVOURED); + } else { + rspeak(NOTHING_EDIBLE); + } + break; + case TROLL: + rspeak(TROLL_VICES); + break; + case DWARF: + if (HERE(FOOD)) { + game.dflag += 2; + rspeak(REALLY_MAD); + } else { + speak(actions[verb].message); + } + break; + case BEAR: + if (game.objects[BEAR].prop == BEAR_DEAD) { + rspeak(RIDICULOUS_ATTEMPT); + break; + } + if (game.objects[BEAR].prop == UNTAMED_BEAR) { + if (HERE(FOOD)) { + DESTROY(FOOD); + game.objects[AXE].fixed = IS_FREE; + game.objects[AXE].prop = AXE_HERE; + state_change(BEAR, SITTING_BEAR); + } else { + rspeak(NOTHING_EDIBLE); + } + break; + } + speak(actions[verb].message); + break; + case OGRE: + if (HERE(FOOD)) { + rspeak(OGRE_FULL); + } else { + speak(actions[verb].message); + } + break; + default: + rspeak(AM_GAME); + } + return GO_CLEAROBJ; } -phase_codes_t fill(verb_t verb, obj_t obj) -/* Fill. Bottle or urn must be empty, and liquid available. (Vase - * is nasty.) */ -{ - if (obj == VASE) { - if (LIQLOC(game.loc) == NO_OBJECT) { - rspeak(FILL_INVALID); - return GO_CLEAROBJ; - } - if (!TOTING(VASE)) { - rspeak(ARENT_CARRYING); - return GO_CLEAROBJ; - } - rspeak(SHATTER_VASE); - game.prop[VASE] = VASE_BROKEN; - game.fixed[VASE] = IS_FIXED; - drop(VASE, game.loc); - return GO_CLEAROBJ; - } +phase_codes_t fill(verb_t verb, obj_t obj) { + /* Fill. Bottle or urn must be empty, and liquid available. (Vase + * is nasty.) */ + if (obj == VASE) { + if (LIQLOC(game.loc) == NO_OBJECT) { + rspeak(FILL_INVALID); + return GO_CLEAROBJ; + } + if (!TOTING(VASE)) { + rspeak(ARENT_CARRYING); + return GO_CLEAROBJ; + } + rspeak(SHATTER_VASE); + game.objects[VASE].prop = VASE_BROKEN; + game.objects[VASE].fixed = IS_FIXED; + drop(VASE, game.loc); + return GO_CLEAROBJ; + } - if (obj == URN) { - if (game.prop[URN] != URN_EMPTY) { - rspeak(FULL_URN); - return GO_CLEAROBJ; - } - if (!HERE(BOTTLE)) { - rspeak(FILL_INVALID); - return GO_CLEAROBJ; - } - int k = LIQUID(); - switch (k) { - case WATER: - game.prop[BOTTLE] = EMPTY_BOTTLE; - rspeak(WATER_URN); - break; - case OIL: - game.prop[URN] = URN_DARK; - game.prop[BOTTLE] = EMPTY_BOTTLE; - rspeak(OIL_URN); - break; - case NO_OBJECT: - default: - rspeak(FILL_INVALID); - return GO_CLEAROBJ; - } - game.place[k] = LOC_NOWHERE; - return GO_CLEAROBJ; - } - if (obj != INTRANSITIVE && obj != BOTTLE) { - speak(actions[verb].message); - return GO_CLEAROBJ; - } - if (obj == INTRANSITIVE && !HERE(BOTTLE)) - return GO_UNKNOWN; + if (obj == URN) { + if (game.objects[URN].prop != URN_EMPTY) { + rspeak(FULL_URN); + return GO_CLEAROBJ; + } + if (!HERE(BOTTLE)) { + rspeak(FILL_INVALID); + return GO_CLEAROBJ; + } + int k = LIQUID(); + switch (k) { + case WATER: + game.objects[BOTTLE].prop = EMPTY_BOTTLE; + rspeak(WATER_URN); + break; + case OIL: + game.objects[URN].prop = URN_DARK; + game.objects[BOTTLE].prop = EMPTY_BOTTLE; + rspeak(OIL_URN); + break; + case NO_OBJECT: + default: + rspeak(FILL_INVALID); + return GO_CLEAROBJ; + } + game.objects[k].place = LOC_NOWHERE; + return GO_CLEAROBJ; + } + if (obj != INTRANSITIVE && obj != BOTTLE) { + speak(actions[verb].message); + return GO_CLEAROBJ; + } + if (obj == INTRANSITIVE && !HERE(BOTTLE)) { + return GO_UNKNOWN; + } - if (HERE(URN) && game.prop[URN] != URN_EMPTY) { - rspeak(URN_NOPOUR); - return GO_CLEAROBJ; - } - if (LIQUID() != NO_OBJECT) { - rspeak(BOTTLE_FULL); - return GO_CLEAROBJ; - } - if (LIQLOC(game.loc) == NO_OBJECT) { - rspeak(NO_LIQUID); - return GO_CLEAROBJ; - } + if (HERE(URN) && game.objects[URN].prop != URN_EMPTY) { + rspeak(URN_NOPOUR); + return GO_CLEAROBJ; + } + if (LIQUID() != NO_OBJECT) { + rspeak(BOTTLE_FULL); + return GO_CLEAROBJ; + } + if (LIQLOC(game.loc) == NO_OBJECT) { + rspeak(NO_LIQUID); + return GO_CLEAROBJ; + } - state_change(BOTTLE, (LIQLOC(game.loc) == OIL) - ? OIL_BOTTLE - : WATER_BOTTLE); - if (TOTING(BOTTLE)) - game.place[LIQUID()] = CARRIED; - return GO_CLEAROBJ; + state_change(BOTTLE, + (LIQLOC(game.loc) == OIL) ? OIL_BOTTLE : WATER_BOTTLE); + if (TOTING(BOTTLE)) { + game.objects[LIQUID()].place = CARRIED; + } + return GO_CLEAROBJ; } -static phase_codes_t find(verb_t verb, obj_t obj) -/* Find. Might be carrying it, or it might be here. Else give caveat. */ -{ - if (TOTING(obj)) { - rspeak(ALREADY_CARRYING); - return GO_CLEAROBJ; - } +static phase_codes_t find(verb_t verb, obj_t obj) { + /* Find. Might be carrying it, or it might be here. Else give caveat. + */ + if (TOTING(obj)) { + rspeak(ALREADY_CARRYING); + return GO_CLEAROBJ; + } - if (game.closed) { - rspeak(NEEDED_NEARBY); - return GO_CLEAROBJ; - } + if (game.closed) { + rspeak(NEEDED_NEARBY); + return GO_CLEAROBJ; + } - if (AT(obj) || - (LIQUID() == obj && AT(BOTTLE)) || - obj == LIQLOC(game.loc) || - (obj == DWARF && atdwrf(game.loc) > 0)) { - rspeak(YOU_HAVEIT); - return GO_CLEAROBJ; - } + if (AT(obj) || (LIQUID() == obj && AT(BOTTLE)) || + obj == LIQLOC(game.loc) || (obj == DWARF && atdwrf(game.loc) > 0)) { + rspeak(YOU_HAVEIT); + return GO_CLEAROBJ; + } - - speak(actions[verb].message); - return GO_CLEAROBJ; + speak(actions[verb].message); + return GO_CLEAROBJ; } -static phase_codes_t fly(verb_t verb, obj_t obj) -/* Fly. Snide remarks unless hovering rug is here. */ -{ - if (obj == INTRANSITIVE) { - if (!HERE(RUG)) { - rspeak(FLAP_ARMS); - return GO_CLEAROBJ; - } - if (game.prop[RUG] != RUG_HOVER) { - rspeak(RUG_NOTHING2); - return GO_CLEAROBJ; - } - obj = RUG; - } +static phase_codes_t fly(verb_t verb, obj_t obj) { + /* Fly. Snide remarks unless hovering rug is here. */ + if (obj == INTRANSITIVE) { + if (!HERE(RUG)) { + rspeak(FLAP_ARMS); + return GO_CLEAROBJ; + } + if (game.objects[RUG].prop != RUG_HOVER) { + rspeak(RUG_NOTHING2); + return GO_CLEAROBJ; + } + obj = RUG; + } - if (obj != RUG) { - speak(actions[verb].message); - return GO_CLEAROBJ; - } - if (game.prop[RUG] != RUG_HOVER) { - rspeak(RUG_NOTHING1); - return GO_CLEAROBJ; - } + if (obj != RUG) { + speak(actions[verb].message); + return GO_CLEAROBJ; + } + if (game.objects[RUG].prop != RUG_HOVER) { + rspeak(RUG_NOTHING1); + return GO_CLEAROBJ; + } - if (game.loc == LOC_CLIFF) { - game.oldlc2 = game.oldloc; - game.oldloc = game.loc; - game.newloc = LOC_LEDGE; - rspeak(RUG_GOES); - } else if (game.loc == LOC_LEDGE) { - game.oldlc2 = game.oldloc; - game.oldloc = game.loc; - game.newloc = LOC_CLIFF; - rspeak(RUG_RETURNS); - } else { -// LCOV_EXCL_START - /* should never happen */ - rspeak(NOTHING_HAPPENS); -// LCOV_EXCL_STOP - } - return GO_TERMINATE; + if (game.loc == LOC_CLIFF) { + game.oldlc2 = game.oldloc; + game.oldloc = game.loc; + game.newloc = LOC_LEDGE; + rspeak(RUG_GOES); + } else if (game.loc == LOC_LEDGE) { + game.oldlc2 = game.oldloc; + game.oldloc = game.loc; + game.newloc = LOC_CLIFF; + rspeak(RUG_RETURNS); + } else { + // LCOV_EXCL_START + /* should never happen */ + rspeak(NOTHING_HAPPENS); + // LCOV_EXCL_STOP + } + return GO_TERMINATE; } -static phase_codes_t inven(void) -/* Inventory. If object, treat same as find. Else report on current burden. */ -{ - bool empty = true; - for (obj_t i = 1; i <= NOBJECTS; i++) { - if (i == BEAR || - !TOTING(i)) - continue; - if (empty) { - rspeak(NOW_HOLDING); - empty = false; - } - pspeak(i, touch, false, -1); - } - if (TOTING(BEAR)) - rspeak(TAME_BEAR); - if (empty) - rspeak(NO_CARRY); - return GO_CLEAROBJ; +static phase_codes_t inven(void) { + /* Inventory. If object, treat same as find. Else report on current + * burden. */ + bool empty = true; + for (obj_t i = 1; i <= NOBJECTS; i++) { + if (i == BEAR || !TOTING(i)) { + continue; + } + if (empty) { + rspeak(NOW_HOLDING); + empty = false; + } + pspeak(i, touch, false, -1); + } + if (TOTING(BEAR)) { + rspeak(TAME_BEAR); + } + if (empty) { + rspeak(NO_CARRY); + } + return GO_CLEAROBJ; } -static phase_codes_t light(verb_t verb, obj_t obj) -/* Light. Applicable only to lamp and urn. */ -{ - if (obj == INTRANSITIVE) { - int selects = 0; - if (HERE(LAMP) && game.prop[LAMP] == LAMP_DARK && game.limit >= 0) { - obj = LAMP; - selects++; - } - if (HERE(URN) && game.prop[URN] == URN_DARK) { - obj = URN; - selects++; - } - if (selects != 1) - return GO_UNKNOWN; - } +static phase_codes_t light(verb_t verb, obj_t obj) { + /* Light. Applicable only to lamp and urn. */ + if (obj == INTRANSITIVE) { + int selects = 0; + if (HERE(LAMP) && game.objects[LAMP].prop == LAMP_DARK && + game.limit >= 0) { + obj = LAMP; + selects++; + } + if (HERE(URN) && game.objects[URN].prop == URN_DARK) { + obj = URN; + selects++; + } + if (selects != 1) { + return GO_UNKNOWN; + } + } - switch (obj) { - case URN: - state_change(URN, game.prop[URN] == URN_EMPTY ? - URN_EMPTY : - URN_LIT); - break; - case LAMP: - if (game.limit < 0) { - rspeak(LAMP_OUT); - break; - } - state_change(LAMP, LAMP_BRIGHT); - if (game.wzdark) - return GO_TOP; - break; - default: - speak(actions[verb].message); - } - return GO_CLEAROBJ; + switch (obj) { + case URN: + state_change(URN, game.objects[URN].prop == URN_EMPTY + ? URN_EMPTY + : URN_LIT); + break; + case LAMP: + if (game.limit < 0) { + rspeak(LAMP_OUT); + break; + } + state_change(LAMP, LAMP_BRIGHT); + if (game.wzdark) { + return GO_TOP; + } + break; + default: + speak(actions[verb].message); + } + return GO_CLEAROBJ; } -static phase_codes_t listen(void) -/* Listen. Intransitive only. Print stuff based on object sound proprties. */ -{ - bool soundlatch = false; - vocab_t sound = locations[game.loc].sound; - if (sound != SILENT) { - rspeak(sound); - if (!locations[game.loc].loud) - rspeak(NO_MESSAGE); - soundlatch = true; - } - for (obj_t i = 1; i <= NOBJECTS; i++) { - if (!HERE(i) || - objects[i].sounds[0] == NULL || - game.prop[i] < 0) - continue; - int mi = game.prop[i]; - /* (ESR) Some unpleasant magic on object states here. Ideally - * we'd have liked the bird to be a normal object that we can - * use state_change() on; can't do it, because there are - * actually two different series of per-state birdsounds - * depending on whether player has drunk dragon's blood. */ - if (i == BIRD) - mi += 3 * game.blooded; - pspeak(i, hear, true, mi, game.zzword); - rspeak(NO_MESSAGE); - if (i == BIRD && mi == BIRD_ENDSTATE) - DESTROY(BIRD); - soundlatch = true; - } - if (!soundlatch) - rspeak(ALL_SILENT); - return GO_CLEAROBJ; +static phase_codes_t listen(void) { + /* Listen. Intransitive only. Print stuff based on object sound + * properties. */ + bool soundlatch = false; + vocab_t sound = locations[game.loc].sound; + if (sound != SILENT) { + rspeak(sound); + if (!locations[game.loc].loud) { + rspeak(NO_MESSAGE); + } + soundlatch = true; + } + for (obj_t i = 1; i <= NOBJECTS; i++) { + if (!HERE(i) || objects[i].sounds[0] == NULL || + OBJECT_IS_STASHED(i) || OBJECT_IS_NOTFOUND(i)) { + continue; + } + int mi = game.objects[i].prop; + /* (ESR) Some unpleasant magic on object states here. Ideally + * we'd have liked the bird to be a normal object that we can + * use state_change() on; can't do it, because there are + * actually two different series of per-state birdsounds + * depending on whether player has drunk dragon's blood. */ + if (i == BIRD) { + mi += 3 * game.blooded; + } + pspeak(i, hear, true, mi, game.zzword); + rspeak(NO_MESSAGE); + if (i == BIRD && mi == BIRD_ENDSTATE) { + DESTROY(BIRD); + } + soundlatch = true; + } + if (!soundlatch) { + rspeak(ALL_SILENT); + } + return GO_CLEAROBJ; } -static phase_codes_t lock(verb_t verb, obj_t obj) -/* Lock, unlock, no object given. Assume various things if present. */ -{ - if (obj == INTRANSITIVE) { - if (HERE(CLAM)) - obj = CLAM; - if (HERE(OYSTER)) - obj = OYSTER; - if (AT(DOOR)) - obj = DOOR; - if (AT(GRATE)) - obj = GRATE; - if (HERE(CHAIN)) - obj = CHAIN; - if (obj == INTRANSITIVE) { - rspeak(NOTHING_LOCKED); - return GO_CLEAROBJ; - } - } +static phase_codes_t lock(verb_t verb, obj_t obj) { + /* Lock, unlock, no object given. Assume various things if present. */ + if (obj == INTRANSITIVE) { + if (HERE(CLAM)) { + obj = CLAM; + } + if (HERE(OYSTER)) { + obj = OYSTER; + } + if (AT(DOOR)) { + obj = DOOR; + } + if (AT(GRATE)) { + obj = GRATE; + } + if (HERE(CHAIN)) { + obj = CHAIN; + } + if (obj == INTRANSITIVE) { + rspeak(NOTHING_LOCKED); + return GO_CLEAROBJ; + } + } - /* Lock, unlock object. Special stuff for opening clam/oyster - * and for chain. */ + /* Lock, unlock object. Special stuff for opening clam/oyster + * and for chain. */ - switch (obj) { - case CHAIN: - if (HERE(KEYS)) { - return chain(verb); - } else - rspeak(NO_KEYS); - break; - case GRATE: - if (HERE(KEYS)) { - if (game.closng) { - rspeak(EXIT_CLOSED); - if (!game.panic) - game.clock2 = PANICTIME; - game.panic = true; - } else { - state_change(GRATE, (verb == LOCK) ? - GRATE_CLOSED : - GRATE_OPEN); - } - } else - rspeak(NO_KEYS); - break; - case CLAM: - if (verb == LOCK) - rspeak(HUH_MAN); - else if (TOTING(CLAM)) - rspeak(DROP_CLAM); - else if (!TOTING(TRIDENT)) - rspeak(CLAM_OPENER); - else { - DESTROY(CLAM); - drop(OYSTER, game.loc); - drop(PEARL, LOC_CULDESAC); - rspeak(PEARL_FALLS); - } - break; - case OYSTER: - if (verb == LOCK) - rspeak(HUH_MAN); - else if (TOTING(OYSTER)) - rspeak(DROP_OYSTER); - else if (!TOTING(TRIDENT)) - rspeak(OYSTER_OPENER); - else - rspeak(OYSTER_OPENS); - break; - case DOOR: - rspeak((game.prop[DOOR] == DOOR_UNRUSTED) ? OK_MAN : RUSTY_DOOR); - break; - case CAGE: - rspeak( NO_LOCK); - break; - case KEYS: - rspeak(CANNOT_UNLOCK); - break; - default: - speak(actions[verb].message); - } + switch (obj) { + case CHAIN: + if (HERE(KEYS)) { + return chain(verb); + } else { + rspeak(NO_KEYS); + } + break; + case GRATE: + if (HERE(KEYS)) { + if (game.closng) { + rspeak(EXIT_CLOSED); + if (!game.panic) { + game.clock2 = PANICTIME; + } + game.panic = true; + } else { + state_change(GRATE, (verb == LOCK) + ? GRATE_CLOSED + : GRATE_OPEN); + } + } else { + rspeak(NO_KEYS); + } + break; + case CLAM: + if (verb == LOCK) { + rspeak(HUH_MAN); + } else if (TOTING(CLAM)) { + rspeak(DROP_CLAM); + } else if (!TOTING(TRIDENT)) { + rspeak(CLAM_OPENER); + } else { + DESTROY(CLAM); + drop(OYSTER, game.loc); + drop(PEARL, LOC_CULDESAC); + rspeak(PEARL_FALLS); + } + break; + case OYSTER: + if (verb == LOCK) { + rspeak(HUH_MAN); + } else if (TOTING(OYSTER)) { + rspeak(DROP_OYSTER); + } else if (!TOTING(TRIDENT)) { + rspeak(OYSTER_OPENER); + } else { + rspeak(OYSTER_OPENS); + } + break; + case DOOR: + rspeak((game.objects[DOOR].prop == DOOR_UNRUSTED) ? OK_MAN + : RUSTY_DOOR); + break; + case CAGE: + rspeak(NO_LOCK); + break; + case KEYS: + rspeak(CANNOT_UNLOCK); + break; + default: + speak(actions[verb].message); + } - return GO_CLEAROBJ; + return GO_CLEAROBJ; } -static phase_codes_t pour(verb_t verb, obj_t obj) -/* Pour. If no object, or object is bottle, assume contents of bottle. - * special tests for pouring water or oil on plant or rusty door. */ -{ - if (obj == BOTTLE || - obj == INTRANSITIVE) - obj = LIQUID(); - if (obj == NO_OBJECT) - return GO_UNKNOWN; - if (!TOTING(obj)) { - speak(actions[verb].message); - return GO_CLEAROBJ; - } +static phase_codes_t pour(verb_t verb, obj_t obj) { + /* Pour. If no object, or object is bottle, assume contents of bottle. + * special tests for pouring water or oil on plant or rusty door. */ + if (obj == BOTTLE || obj == INTRANSITIVE) { + obj = LIQUID(); + } + if (obj == NO_OBJECT) { + return GO_UNKNOWN; + } + if (!TOTING(obj)) { + speak(actions[verb].message); + return GO_CLEAROBJ; + } - if (obj != OIL && obj != WATER) { - rspeak(CANT_POUR); - return GO_CLEAROBJ; - } - if (HERE(URN) && game.prop[URN] == URN_EMPTY) - return fill(verb, URN); - game.prop[BOTTLE] = EMPTY_BOTTLE; - game.place[obj] = LOC_NOWHERE; - if (!(AT(PLANT) || - AT(DOOR))) { - rspeak(GROUND_WET); - return GO_CLEAROBJ; - } - if (!AT(DOOR)) { - if (obj == WATER) { - /* cycle through the three plant states */ - state_change(PLANT, MOD(game.prop[PLANT] + 1, 3)); - game.prop[PLANT2] = game.prop[PLANT]; - return GO_MOVE; - } else { - rspeak(SHAKING_LEAVES); - return GO_CLEAROBJ; - } - } else { - state_change(DOOR, (obj == OIL) ? - DOOR_UNRUSTED : - DOOR_RUSTED); - return GO_CLEAROBJ; - } + if (obj != OIL && obj != WATER) { + rspeak(CANT_POUR); + return GO_CLEAROBJ; + } + if (HERE(URN) && game.objects[URN].prop == URN_EMPTY) { + return fill(verb, URN); + } + game.objects[BOTTLE].prop = EMPTY_BOTTLE; + game.objects[obj].place = LOC_NOWHERE; + if (!(AT(PLANT) || AT(DOOR))) { + rspeak(GROUND_WET); + return GO_CLEAROBJ; + } + if (!AT(DOOR)) { + if (obj == WATER) { + /* cycle through the three plant states */ + state_change(PLANT, + MOD(game.objects[PLANT].prop + 1, 3)); + game.objects[PLANT2].prop = game.objects[PLANT].prop; + return GO_MOVE; + } else { + rspeak(SHAKING_LEAVES); + return GO_CLEAROBJ; + } + } else { + state_change(DOOR, (obj == OIL) ? DOOR_UNRUSTED : DOOR_RUSTED); + return GO_CLEAROBJ; + } } -static phase_codes_t quit(void) -/* Quit. Intransitive only. Verify intent and exit if that's what he wants. */ -{ - if (yes_or_no(arbitrary_messages[REALLY_QUIT], arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN])) - terminate(quitgame); - return GO_CLEAROBJ; +static phase_codes_t quit(void) { + /* Quit. Intransitive only. Verify intent and exit if that's what he + * wants. */ + if (yes_or_no(arbitrary_messages[REALLY_QUIT], + arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN])) { + terminate(quitgame); + } + return GO_CLEAROBJ; } static phase_codes_t read(command_t command) /* Read. Print stuff based on objtxt. Oyster (?) is special case. */ { - if (command.obj == INTRANSITIVE) { - command.obj = NO_OBJECT; - for (int i = 1; i <= NOBJECTS; i++) { - if (HERE(i) && objects[i].texts[0] != NULL && game.prop[i] >= 0) - command.obj = command.obj * NOBJECTS + i; - } - if (command.obj > NOBJECTS || - command.obj == NO_OBJECT || - DARK(game.loc)) - return GO_UNKNOWN; - } + if (command.obj == INTRANSITIVE) { + command.obj = NO_OBJECT; + for (int i = 1; i <= NOBJECTS; i++) { + if (HERE(i) && objects[i].texts[0] != NULL && + !OBJECT_IS_STASHED(i)) { + command.obj = command.obj * NOBJECTS + i; + } + } + if (command.obj > NOBJECTS || command.obj == NO_OBJECT || + IS_DARK_HERE()) { + return GO_UNKNOWN; + } + } - if (DARK(game.loc)) { - sspeak(NO_SEE, command.word[0].raw); - } else if (command.obj == OYSTER) { - if (!TOTING(OYSTER) || !game.closed) { - rspeak(DONT_UNDERSTAND); - } else if (!game.clshnt) { - game.clshnt = yes_or_no(arbitrary_messages[CLUE_QUERY], arbitrary_messages[WAYOUT_CLUE], arbitrary_messages[OK_MAN]); - } else { - pspeak(OYSTER, hear, true, 1); // Not really a sound, but oh well. - } - } else if (objects[command.obj].texts[0] == NULL || - game.prop[command.obj] == STATE_NOTFOUND) { - speak(actions[command.verb].message); - } else - pspeak(command.obj, study, true, game.prop[command.obj]); - return GO_CLEAROBJ; + if (IS_DARK_HERE()) { + sspeak(NO_SEE, command.word[0].raw); + } else if (command.obj == OYSTER) { + if (!TOTING(OYSTER) || !game.closed) { + rspeak(DONT_UNDERSTAND); + } else if (!game.clshnt) { + game.clshnt = yes_or_no(arbitrary_messages[CLUE_QUERY], + arbitrary_messages[WAYOUT_CLUE], + arbitrary_messages[OK_MAN]); + } else { + pspeak(OYSTER, hear, true, + 1); // Not really a sound, but oh well. + } + } else if (objects[command.obj].texts[0] == NULL || + OBJECT_IS_NOTFOUND(command.obj)) { + speak(actions[command.verb].message); + } else { + pspeak(command.obj, study, true, + game.objects[command.obj].prop); + } + return GO_CLEAROBJ; } -static phase_codes_t reservoir(void) -/* Z'ZZZ (word gets recomputed at startup; different each game). */ -{ - if (!AT(RESER) && game.loc != LOC_RESBOTTOM) { - rspeak(NOTHING_HAPPENS); - return GO_CLEAROBJ; - } else { - state_change(RESER, - game.prop[RESER] == WATERS_PARTED ? WATERS_UNPARTED : WATERS_PARTED); - if (AT(RESER)) - return GO_CLEAROBJ; - else { - game.oldlc2 = game.loc; - game.newloc = LOC_NOWHERE; - rspeak(NOT_BRIGHT); - return GO_TERMINATE; - } - } +static phase_codes_t reservoir(void) { + /* Z'ZZZ (word gets recomputed at startup; different each game). */ + if (!AT(RESER) && game.loc != LOC_RESBOTTOM) { + rspeak(NOTHING_HAPPENS); + return GO_CLEAROBJ; + } else { + state_change(RESER, game.objects[RESER].prop == WATERS_PARTED + ? WATERS_UNPARTED + : WATERS_PARTED); + if (AT(RESER)) { + return GO_CLEAROBJ; + } else { + game.oldlc2 = game.loc; + game.newloc = LOC_NOWHERE; + rspeak(NOT_BRIGHT); + return GO_TERMINATE; + } + } } -static phase_codes_t rub(verb_t verb, obj_t obj) -/* Rub. Yields various snide remarks except for lit urn. */ -{ - if (obj == URN && game.prop[URN] == URN_LIT) { - DESTROY(URN); - drop(AMBER, game.loc); - game.prop[AMBER] = AMBER_IN_ROCK; - --game.tally; - drop(CAVITY, game.loc); - rspeak(URN_GENIES); - } else if (obj != LAMP) { - rspeak(PECULIAR_NOTHING); - } else { - speak(actions[verb].message); - } - return GO_CLEAROBJ; +static phase_codes_t rub(verb_t verb, obj_t obj) { + /* Rub. Yields various snide remarks except for lit urn. */ + if (obj == URN && game.objects[URN].prop == URN_LIT) { + DESTROY(URN); + drop(AMBER, game.loc); + game.objects[AMBER].prop = AMBER_IN_ROCK; + --game.tally; + drop(CAVITY, game.loc); + rspeak(URN_GENIES); + } else if (obj != LAMP) { + rspeak(PECULIAR_NOTHING); + } else { + speak(actions[verb].message); + } + return GO_CLEAROBJ; } -static phase_codes_t say(command_t command) -/* Say. Echo WD2. Magic words override. */ -{ - if (command.word[1].type == MOTION && - (command.word[1].id == XYZZY || - command.word[1].id == PLUGH || - command.word[1].id == PLOVER)) { - return GO_WORD2; - } - if (command.word[1].type == ACTION && command.word[1].id == PART) - return reservoir(); +static phase_codes_t say(command_t command) { + /* Say. Echo WD2. Magic words override. */ + if (command.word[1].type == MOTION && + (command.word[1].id == XYZZY || command.word[1].id == PLUGH || + command.word[1].id == PLOVER)) { + return GO_WORD2; + } + if (command.word[1].type == ACTION && command.word[1].id == PART) { + return reservoir(); + } - if (command.word[1].type == ACTION && - (command.word[1].id == FEE || - command.word[1].id == FIE || - command.word[1].id == FOE || - command.word[1].id == FOO || - command.word[1].id == FUM || - command.word[1].id == PART)) { - return bigwords(command.word[1].id); - } - sspeak(OKEY_DOKEY, command.word[1].raw); - return GO_CLEAROBJ; + if (command.word[1].type == ACTION && + (command.word[1].id == FEE || command.word[1].id == FIE || + command.word[1].id == FOE || command.word[1].id == FOO || + command.word[1].id == FUM || command.word[1].id == PART)) { + return bigwords(command.word[1].id); + } + sspeak(OKEY_DOKEY, command.word[1].raw); + return GO_CLEAROBJ; } -static phase_codes_t throw_support(vocab_t spk) -{ - rspeak(spk); - drop(AXE, game.loc); - return GO_MOVE; +static phase_codes_t throw_support(vocab_t spk) { + rspeak(spk); + drop(AXE, game.loc); + return GO_MOVE; } -static phase_codes_t throwit(command_t command) -/* Throw. Same as discard unless axe. Then same as attack except - * ignore bird, 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. */ -{ - if (!TOTING(command.obj)) { - speak(actions[command.verb].message); - return GO_CLEAROBJ; - } - if (objects[command.obj].is_treasure && AT(TROLL)) { - /* Snarf a treasure for the troll. */ - drop(command.obj, LOC_NOWHERE); - move(TROLL, LOC_NOWHERE); - move(TROLL + NOBJECTS, IS_FREE); - drop(TROLL2, objects[TROLL].plac); - drop(TROLL2 + NOBJECTS, objects[TROLL].fixd); - juggle(CHASM); - rspeak(TROLL_SATISFIED); - return GO_CLEAROBJ; - } - if (command.obj == FOOD && HERE(BEAR)) { - /* But throwing food is another story. */ - command.obj = BEAR; - return (feed(command.verb, command.obj)); - } - if (command.obj != AXE) - return (discard(command.verb, command.obj)); - else { - if (atdwrf(game.loc) <= 0) { - if (AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS) - return throw_support(DRAGON_SCALES); - if (AT(TROLL)) - return throw_support(TROLL_RETURNS); - if (AT(OGRE)) - return throw_support(OGRE_DODGE); - if (HERE(BEAR) && game.prop[BEAR] == UNTAMED_BEAR) { - /* This'll teach him to throw the axe at the bear! */ - drop(AXE, game.loc); - game.fixed[AXE] = IS_FIXED; - juggle(BEAR); - state_change(AXE, AXE_LOST); - return GO_CLEAROBJ; - } - command.obj = INTRANSITIVE; - return (attack(command)); - } +static phase_codes_t throwit(command_t command) { + /* Throw. Same as discard unless axe. Then same as attack except + * ignore bird, 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. */ + if (!TOTING(command.obj)) { + speak(actions[command.verb].message); + return GO_CLEAROBJ; + } + if (objects[command.obj].is_treasure && AT(TROLL)) { + /* Snarf a treasure for the troll. */ + drop(command.obj, LOC_NOWHERE); + move(TROLL, LOC_NOWHERE); + move(TROLL + NOBJECTS, IS_FREE); + drop(TROLL2, objects[TROLL].plac); + drop(TROLL2 + NOBJECTS, objects[TROLL].fixd); + juggle(CHASM); + rspeak(TROLL_SATISFIED); + return GO_CLEAROBJ; + } + if (command.obj == FOOD && HERE(BEAR)) { + /* But throwing food is another story. */ + command.obj = BEAR; + return (feed(command.verb, command.obj)); + } + if (command.obj != AXE) { + return (discard(command.verb, command.obj)); + } else { + if (atdwrf(game.loc) <= 0) { + if (AT(DRAGON) && + game.objects[DRAGON].prop == DRAGON_BARS) { + return throw_support(DRAGON_SCALES); + } + if (AT(TROLL)) { + return throw_support(TROLL_RETURNS); + } + if (AT(OGRE)) { + return throw_support(OGRE_DODGE); + } + if (HERE(BEAR) && + game.objects[BEAR].prop == UNTAMED_BEAR) { + /* This'll teach him to throw the axe at the + * bear! */ + drop(AXE, game.loc); + game.objects[AXE].fixed = IS_FIXED; + juggle(BEAR); + state_change(AXE, AXE_LOST); + return GO_CLEAROBJ; + } + command.obj = INTRANSITIVE; + return (attack(command)); + } - if (randrange(NDWARVES + 1) < game.dflag) { - return throw_support(DWARF_DODGES); - } else { - int i = atdwrf(game.loc); - game.dseen[i] = false; - game.dloc[i] = LOC_NOWHERE; - return throw_support((++game.dkill == 1) ? - DWARF_SMOKE : - KILLED_DWARF); - } - } + if (randrange(NDWARVES + 1) < game.dflag) { + return throw_support(DWARF_DODGES); + } else { + int i = atdwrf(game.loc); + game.dwarves[i].seen = false; + game.dwarves[i].loc = LOC_NOWHERE; + return throw_support( + (++game.dkill == 1) ? DWARF_SMOKE : KILLED_DWARF); + } + } } -static phase_codes_t wake(verb_t verb, obj_t obj) -/* Wake. Only use is to disturb the dwarves. */ -{ - if (obj != DWARF || - !game.closed) { - speak(actions[verb].message); - return GO_CLEAROBJ; - } else { - rspeak(PROD_DWARF); - return GO_DWARFWAKE; - } +static phase_codes_t wake(verb_t verb, obj_t obj) { + /* Wake. Only use is to disturb the dwarves. */ + if (obj != DWARF || !game.closed) { + speak(actions[verb].message); + return GO_CLEAROBJ; + } else { + rspeak(PROD_DWARF); + return GO_DWARFWAKE; + } } -static phase_codes_t seed(verb_t verb, const char *arg) -/* Set seed */ -{ - int32_t seed = strtol(arg, NULL, 10); - speak(actions[verb].message, seed); - set_seed(seed); - --game.turns; - return GO_TOP; +static phase_codes_t seed(verb_t verb, const char *arg) { + /* Set seed */ + int32_t seed = strtol(arg, NULL, 10); + speak(actions[verb].message, seed); + set_seed(seed); + --game.turns; + return GO_TOP; } -static phase_codes_t waste(verb_t verb, turn_t turns) -/* Burn turns */ -{ - game.limit -= turns; - speak(actions[verb].message, (int)game.limit); - return GO_TOP; +static phase_codes_t waste(verb_t verb, turn_t turns) { + /* Burn turns */ + game.limit -= turns; + speak(actions[verb].message, (int)game.limit); + return GO_TOP; } -static phase_codes_t wave(verb_t verb, obj_t obj) -/* Wave. No effect unless waving rod at fissure or at bird. */ -{ - if (obj != ROD || - !TOTING(obj) || - (!HERE(BIRD) && - (game.closng || - !AT(FISSURE)))) { - speak(((!TOTING(obj)) && (obj != ROD || - !TOTING(ROD2))) ? - arbitrary_messages[ARENT_CARRYING] : - actions[verb].message); - return GO_CLEAROBJ; - } +static phase_codes_t wave(verb_t verb, obj_t obj) { + /* Wave. No effect unless waving rod at fissure or at bird. */ + if (obj != ROD || !TOTING(obj) || + (!HERE(BIRD) && (game.closng || !AT(FISSURE)))) { + speak(((!TOTING(obj)) && (obj != ROD || !TOTING(ROD2))) + ? arbitrary_messages[ARENT_CARRYING] + : actions[verb].message); + return GO_CLEAROBJ; + } - if (game.prop[BIRD] == BIRD_UNCAGED && game.loc == game.place[STEPS] && game.prop[JADE] == STATE_NOTFOUND) { - drop(JADE, game.loc); - game.prop[JADE] = STATE_FOUND; - --game.tally; - rspeak(NECKLACE_FLY); - return GO_CLEAROBJ; - } else { - if (game.closed) { - rspeak((game.prop[BIRD] == BIRD_CAGED) ? - CAGE_FLY : - FREE_FLY); - return GO_DWARFWAKE; - } - if (game.closng || - !AT(FISSURE)) { - rspeak((game.prop[BIRD] == BIRD_CAGED) ? - CAGE_FLY : - FREE_FLY); - return GO_CLEAROBJ; - } - if (HERE(BIRD)) - rspeak((game.prop[BIRD] == BIRD_CAGED) ? - CAGE_FLY : - FREE_FLY); + if (game.objects[BIRD].prop == BIRD_UNCAGED && + game.loc == game.objects[STEPS].place && OBJECT_IS_NOTFOUND(JADE)) { + drop(JADE, game.loc); + OBJECT_SET_FOUND(JADE); + --game.tally; + rspeak(NECKLACE_FLY); + return GO_CLEAROBJ; + } else { + if (game.closed) { + rspeak((game.objects[BIRD].prop == BIRD_CAGED) + ? CAGE_FLY + : FREE_FLY); + return GO_DWARFWAKE; + } + if (game.closng || !AT(FISSURE)) { + rspeak((game.objects[BIRD].prop == BIRD_CAGED) + ? CAGE_FLY + : FREE_FLY); + return GO_CLEAROBJ; + } + if (HERE(BIRD)) { + rspeak((game.objects[BIRD].prop == BIRD_CAGED) + ? CAGE_FLY + : FREE_FLY); + } - state_change(FISSURE, - game.prop[FISSURE] == BRIDGED ? UNBRIDGED : BRIDGED); - return GO_CLEAROBJ; - } + state_change(FISSURE, game.objects[FISSURE].prop == BRIDGED + ? UNBRIDGED + : BRIDGED); + return GO_CLEAROBJ; + } } -phase_codes_t action(command_t command) -/* Analyse a verb. Remember what it was, go back for object if second word - * unless verb is "say", which snarfs arbitrary second word. - */ -{ - /* Previously, actions that result in a message, but don't do anything - * further were called "specials". Now they're handled here as normal - * actions. If noaction is true, then we spit out the message and return */ - if (actions[command.verb].noaction) { - speak(actions[command.verb].message); - return GO_CLEAROBJ; - } +phase_codes_t action(command_t command) { + /* Analyse a verb. Remember what it was, go back for object if second + * word unless verb is "say", which snarfs arbitrary second word. + */ + /* Previously, actions that result in a message, but don't do anything + * further were called "specials". Now they're handled here as normal + * actions. If noaction is true, then we spit out the message and return + */ + if (actions[command.verb].noaction) { + speak(actions[command.verb].message); + return GO_CLEAROBJ; + } - if (command.part == unknown) { - /* Analyse an object word. See if the thing is here, whether - * we've got a verb yet, and so on. Object must be here - * unless verb is "find" or "invent(ory)" (and no new verb - * yet to be analysed). Water and oil are also funny, since - * they are never actually dropped at any location, but might - * be here inside the bottle or urn or as a feature of the - * location. */ - if (HERE(command.obj)) - /* FALL THROUGH */; - else if (command.obj == DWARF && atdwrf(game.loc) > 0) - /* FALL THROUGH */; - else if (!game.closed && ((LIQUID() == command.obj && HERE(BOTTLE)) || - command.obj == LIQLOC(game.loc))) - /* FALL THROUGH */; - else if (command.obj == OIL && HERE(URN) && game.prop[URN] != URN_EMPTY) { - command.obj = URN; - /* FALL THROUGH */; - } else if (command.obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != PLANT_THIRSTY) { - command.obj = PLANT2; - /* FALL THROUGH */; - } else if (command.obj == KNIFE && game.knfloc == game.loc) { - game.knfloc = -1; - rspeak(KNIVES_VANISH); - return GO_CLEAROBJ; - } else if (command.obj == ROD && HERE(ROD2)) { - command.obj = ROD2; - /* FALL THROUGH */; - } else if ((command.verb == FIND || - command.verb == INVENTORY) && (command.word[1].id == WORD_EMPTY || command.word[1].id == WORD_NOT_FOUND)) - /* FALL THROUGH */; - else { - sspeak(NO_SEE, command.word[0].raw); - return GO_CLEAROBJ; - } + if (command.part == unknown) { + /* Analyse an object word. See if the thing is here, whether + * we've got a verb yet, and so on. Object must be here + * unless verb is "find" or "invent(ory)" (and no new verb + * yet to be analysed). Water and oil are also funny, since + * they are never actually dropped at any location, but might + * be here inside the bottle or urn or as a feature of the + * location. */ + if (HERE(command.obj)) { + /* FALL THROUGH */; + } else if (command.obj == DWARF && atdwrf(game.loc) > 0) { + /* FALL THROUGH */; + } else if (!game.closed && + ((LIQUID() == command.obj && HERE(BOTTLE)) || + command.obj == LIQLOC(game.loc))) { + /* FALL THROUGH */; + } else if (command.obj == OIL && HERE(URN) && + game.objects[URN].prop != URN_EMPTY) { + command.obj = URN; + /* FALL THROUGH */; + } else if (command.obj == PLANT && AT(PLANT2) && + game.objects[PLANT2].prop != PLANT_THIRSTY) { + command.obj = PLANT2; + /* FALL THROUGH */; + } else if (command.obj == KNIFE && game.knfloc == game.loc) { + game.knfloc = -1; + rspeak(KNIVES_VANISH); + return GO_CLEAROBJ; + } else if (command.obj == ROD && HERE(ROD2)) { + command.obj = ROD2; + /* FALL THROUGH */; + } else if ((command.verb == FIND || + command.verb == INVENTORY) && + (command.word[1].id == WORD_EMPTY || + command.word[1].id == WORD_NOT_FOUND)) { + /* FALL THROUGH */; + } else { + sspeak(NO_SEE, command.word[0].raw); + return GO_CLEAROBJ; + } - if (command.verb != 0) - command.part = transitive; - } + if (command.verb != 0) { + command.part = transitive; + } + } - switch (command.part) { - case intransitive: - if (command.word[1].raw[0] != '\0' && command.verb != SAY) - return GO_WORD2; - if (command.verb == SAY) - /* KEYS is not special, anything not NO_OBJECT or INTRANSITIVE - * will do here. We're preventing interpretation as an intransitive - * verb when the word is unknown. */ - command.obj = command.word[1].raw[0] != '\0' ? KEYS : NO_OBJECT; - if (command.obj == NO_OBJECT || - command.obj == INTRANSITIVE) { - /* Analyse an intransitive verb (ie, no object given yet). */ - switch (command.verb) { - case CARRY: - return vcarry(command.verb, INTRANSITIVE); - case DROP: - return GO_UNKNOWN; - case SAY: - return GO_UNKNOWN; - case UNLOCK: - return lock(command.verb, INTRANSITIVE); - case NOTHING: { - rspeak(OK_MAN); - return (GO_CLEAROBJ); - } - case LOCK: - return lock(command.verb, INTRANSITIVE); - case LIGHT: - return light(command.verb, INTRANSITIVE); - case EXTINGUISH: - return extinguish(command.verb, INTRANSITIVE); - case WAVE: - return GO_UNKNOWN; - case TAME: - return GO_UNKNOWN; - case GO: { - speak(actions[command.verb].message); - return GO_CLEAROBJ; - } - case ATTACK: - command.obj = INTRANSITIVE; - return attack(command); - case POUR: - return pour(command.verb, INTRANSITIVE); - case EAT: - return eat(command.verb, INTRANSITIVE); - case DRINK: - return drink(command.verb, INTRANSITIVE); - case RUB: - return GO_UNKNOWN; - case THROW: - return GO_UNKNOWN; - case QUIT: - return quit(); - case FIND: - return GO_UNKNOWN; - case INVENTORY: - return inven(); - case FEED: - return GO_UNKNOWN; - case FILL: - return fill(command.verb, INTRANSITIVE); - case BLAST: - blast(); - return GO_CLEAROBJ; - case SCORE: - score(scoregame); - return GO_CLEAROBJ; - case FEE: - case FIE: - case FOE: - case FOO: - case FUM: - return bigwords(command.word[0].id); - case BRIEF: - return brief(); - case READ: - command.obj = INTRANSITIVE; - return read(command); - case BREAK: - return GO_UNKNOWN; - case WAKE: - return GO_UNKNOWN; - case SAVE: - return suspend(); - case RESUME: - return resume(); - case FLY: - return fly(command.verb, INTRANSITIVE); - case LISTEN: - return listen(); - case PART: - return reservoir(); - case SEED: - case WASTE: - rspeak(NUMERIC_REQUIRED); - return GO_TOP; - default: // LCOV_EXCL_LINE - BUG(INTRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE - } - } - /* FALLTHRU */ - case transitive: - /* Analyse a transitive verb. */ - switch (command.verb) { - case CARRY: - return vcarry(command.verb, command.obj); - case DROP: - return discard(command.verb, command.obj); - case SAY: - return say(command); - case UNLOCK: - return lock(command.verb, command.obj); - case NOTHING: { - rspeak(OK_MAN); - return (GO_CLEAROBJ); - } - case LOCK: - return lock(command.verb, command.obj); - case LIGHT: - return light(command.verb, command.obj); - case EXTINGUISH: - return extinguish(command.verb, command.obj); - case WAVE: - return wave(command.verb, command.obj); - case TAME: { - speak(actions[command.verb].message); - return GO_CLEAROBJ; - } - case GO: { - speak(actions[command.verb].message); - return GO_CLEAROBJ; - } - case ATTACK: - return attack(command); - case POUR: - return pour(command.verb, command.obj); - case EAT: - return eat(command.verb, command.obj); - case DRINK: - return drink(command.verb, command.obj); - case RUB: - return rub(command.verb, command.obj); - case THROW: - return throwit(command); - case QUIT: { - speak(actions[command.verb].message); - return GO_CLEAROBJ; - } - case FIND: - return find(command.verb, command.obj); - case INVENTORY: - return find(command.verb, command.obj); - case FEED: - return feed(command.verb, command.obj); - case FILL: - return fill(command.verb, command.obj); - case BLAST: - blast(); - return GO_CLEAROBJ; - case SCORE: { - speak(actions[command.verb].message); - return GO_CLEAROBJ; - } - case FEE: - case FIE: - case FOE: - case FOO: - case FUM: { - speak(actions[command.verb].message); - return GO_CLEAROBJ; - } - case BRIEF: { - speak(actions[command.verb].message); - return GO_CLEAROBJ; - } - case READ: - return read(command); - case BREAK: - return vbreak(command.verb, command.obj); - case WAKE: - return wake(command.verb, command.obj); - case SAVE: { - speak(actions[command.verb].message); - return GO_CLEAROBJ; - } - case RESUME: { - speak(actions[command.verb].message); - return GO_CLEAROBJ; - } - case FLY: - return fly(command.verb, command.obj); - case LISTEN: { - speak(actions[command.verb].message); - return GO_CLEAROBJ; - } - // LCOV_EXCL_START - // This case should never happen - here only as placeholder - case PART: - return reservoir(); - // LCOV_EXCL_STOP - case SEED: - return seed(command.verb, command.word[1].raw); - case WASTE: - return waste(command.verb, (turn_t)atol(command.word[1].raw)); - default: // LCOV_EXCL_LINE - BUG(TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE - } - case unknown: - /* Unknown verb, couldn't deduce object - might need hint */ - sspeak(WHAT_DO, command.word[0].raw); - return GO_CHECKHINT; - default: // LCOV_EXCL_LINE - BUG(SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN); // LCOV_EXCL_LINE - } + switch (command.part) { + case intransitive: + if (command.word[1].raw[0] != '\0' && command.verb != SAY) { + return GO_WORD2; + } + if (command.verb == SAY) { + /* KEYS is not special, anything not NO_OBJECT or + * INTRANSITIVE will do here. We're preventing + * interpretation as an intransitive verb when the word + * is unknown. */ + command.obj = + command.word[1].raw[0] != '\0' ? KEYS : NO_OBJECT; + } + if (command.obj == NO_OBJECT || command.obj == INTRANSITIVE) { + /* Analyse an intransitive verb (ie, no object given + * yet). */ + switch (command.verb) { + case CARRY: + return vcarry(command.verb, INTRANSITIVE); + case DROP: + return GO_UNKNOWN; + case SAY: + return GO_UNKNOWN; + case UNLOCK: + return lock(command.verb, INTRANSITIVE); + case NOTHING: { + rspeak(OK_MAN); + return (GO_CLEAROBJ); + } + case LOCK: + return lock(command.verb, INTRANSITIVE); + case LIGHT: + return light(command.verb, INTRANSITIVE); + case EXTINGUISH: + return extinguish(command.verb, INTRANSITIVE); + case WAVE: + return GO_UNKNOWN; + case TAME: + return GO_UNKNOWN; + case GO: { + speak(actions[command.verb].message); + return GO_CLEAROBJ; + } + case ATTACK: + command.obj = INTRANSITIVE; + return attack(command); + case POUR: + return pour(command.verb, INTRANSITIVE); + case EAT: + return eat(command.verb, INTRANSITIVE); + case DRINK: + return drink(command.verb, INTRANSITIVE); + case RUB: + return GO_UNKNOWN; + case THROW: + return GO_UNKNOWN; + case QUIT: + return quit(); + case FIND: + return GO_UNKNOWN; + case INVENTORY: + return inven(); + case FEED: + return GO_UNKNOWN; + case FILL: + return fill(command.verb, INTRANSITIVE); + case BLAST: + blast(); + return GO_CLEAROBJ; + case SCORE: + score(scoregame); + return GO_CLEAROBJ; + case FEE: + case FIE: + case FOE: + case FOO: + case FUM: + return bigwords(command.word[0].id); + case BRIEF: + return brief(); + case READ: + command.obj = INTRANSITIVE; + return read(command); + case BREAK: + return GO_UNKNOWN; + case WAKE: + return GO_UNKNOWN; + case SAVE: + return suspend(); + case RESUME: + return resume(); + case FLY: + return fly(command.verb, INTRANSITIVE); + case LISTEN: + return listen(); + case PART: + return reservoir(); + case SEED: + case WASTE: + rspeak(NUMERIC_REQUIRED); + return GO_TOP; + default: // LCOV_EXCL_LINE + BUG(INTRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE + } + } + /* FALLTHRU */ + case transitive: + /* Analyse a transitive verb. */ + switch (command.verb) { + case CARRY: + return vcarry(command.verb, command.obj); + case DROP: + return discard(command.verb, command.obj); + case SAY: + return say(command); + case UNLOCK: + return lock(command.verb, command.obj); + case NOTHING: { + rspeak(OK_MAN); + return (GO_CLEAROBJ); + } + case LOCK: + return lock(command.verb, command.obj); + case LIGHT: + return light(command.verb, command.obj); + case EXTINGUISH: + return extinguish(command.verb, command.obj); + case WAVE: + return wave(command.verb, command.obj); + case TAME: { + speak(actions[command.verb].message); + return GO_CLEAROBJ; + } + case GO: { + speak(actions[command.verb].message); + return GO_CLEAROBJ; + } + case ATTACK: + return attack(command); + case POUR: + return pour(command.verb, command.obj); + case EAT: + return eat(command.verb, command.obj); + case DRINK: + return drink(command.verb, command.obj); + case RUB: + return rub(command.verb, command.obj); + case THROW: + return throwit(command); + case QUIT: + speak(actions[command.verb].message); + return GO_CLEAROBJ; + case FIND: + return find(command.verb, command.obj); + case INVENTORY: + return find(command.verb, command.obj); + case FEED: + return feed(command.verb, command.obj); + case FILL: + return fill(command.verb, command.obj); + case BLAST: + blast(); + return GO_CLEAROBJ; + case SCORE: + speak(actions[command.verb].message); + return GO_CLEAROBJ; + case FEE: + case FIE: + case FOE: + case FOO: + case FUM: + speak(actions[command.verb].message); + return GO_CLEAROBJ; + case BRIEF: + speak(actions[command.verb].message); + return GO_CLEAROBJ; + case READ: + return read(command); + case BREAK: + return vbreak(command.verb, command.obj); + case WAKE: + return wake(command.verb, command.obj); + case SAVE: + speak(actions[command.verb].message); + return GO_CLEAROBJ; + case RESUME: + speak(actions[command.verb].message); + return GO_CLEAROBJ; + case FLY: + return fly(command.verb, command.obj); + case LISTEN: + speak(actions[command.verb].message); + return GO_CLEAROBJ; + // LCOV_EXCL_START + // This case should never happen - here only as placeholder + case PART: + return reservoir(); + // LCOV_EXCL_STOP + case SEED: + return seed(command.verb, command.word[1].raw); + case WASTE: + return waste(command.verb, + (turn_t)atol(command.word[1].raw)); + default: // LCOV_EXCL_LINE + BUG(TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE + } + case unknown: + /* Unknown verb, couldn't deduce object - might need hint */ + sspeak(WHAT_DO, command.word[0].raw); + return GO_CHECKHINT; + default: // LCOV_EXCL_LINE + BUG(SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN); // LCOV_EXCL_LINE + } } + +// end diff --git a/advent.adoc b/advent.adoc index fa69ddc..a064d1a 100644 --- a/advent.adoc +++ b/advent.adoc @@ -1,14 +1,19 @@ = advent(6) = :doctype: manpage +// SPDX-FileCopyrightText: (C) Eric S. Raymond +// SPDX-License-Identifier: CC-BY-4.0 + +// batchspell: add advent logfile savefile roleplaying Gillogly PDP Ctrl-D +// batchspell: add EOF autosave endianness wumpus zork nethack == NAME == advent - Colossal Cave Adventure == SYNOPSIS == -*advent* [-l logfile] [-o] [-r savefile] [script...] +*advent* [-l logfile] [-o] [-r savefile] [-a savefile] [script...] == DESCRIPTION == -The original Colossal Cave Adventure from 1976-77 was the origin of all +The original Colossal Cave Adventure from 1976-1977 was the origin of all later text adventures, dungeon-crawl (computer) games, and computer-hosted roleplaying games. @@ -18,19 +23,22 @@ adventure". To learn more about the changes since the 350-point original, type 'news' at the command prompt. There is an 'adventure' in the BSD games package that is a C port by -Jim Gillogly of the 1976 ancestor of this game. To avoid a name -collision, this game builds as 'advent', reflecting the fact that the -PDP-10 on which the game originally ran limited filenames to 6 characters. +Jim Gillogly of the 1977 version. To avoid a name collision, this game +builds as 'advent', reflecting the fact that the PDP-10 on which the +game originally ran limited filenames to 6 characters. This version is released as open source with the permission and encouragement of the original authors. -Unlike the original, this version supports use of your arrow keys to edit -your command line in place. Basic Emacs keystrokes are supported, and -your up/down arrows access a command history. +Unlike the original, this version has a command prompt and supports +use of your arrow keys to edit your command line in place. Basic +Emacs keystrokes are supported, and your up/down arrows access a +command history. -Otherwise, the "version" command is about the only way to tell you're not -running Don's original. +Some minor bugs and message typos have been fixed. Otherwise, the +"version" command is almost the only way to tell you're not running +Don's 1977 version until you get to the new cave sections added for +2.5. To exit the game, type Ctrl-D (EOF). @@ -40,21 +48,24 @@ There have been no gameplay changes. -l:: Log commands to specified file. --r:: Restore game from specified file +-r:: Restore game from specified save file --o:: Old-style. Restores original interface, no prompt or line editing. +-a:: Load from specified save file and autosave to it on exit or signal. + +-o:: Old-style. Reverts some minor cosmetic fixes in game + messages. Restores original interface, no prompt or line editing. Also ignores new-school one-letter commands l, x, g, z, i. Also case-smashes and truncates unrecognized text when echoed. Normally, game input is taken from standard input. If script file -arguments are given, input is taken fron them instead. A script file +arguments are given, input is taken from them instead. A script file argument of '-' is taken as a directive to read from standard input. == BUGS == -The binary save file format is fragile, dependent on your machine word -size and endianness, and unlikely to survive through version bumps. There -is a version check. +The binary save file format is fragile, dependent on your machine's +endianness, and unlikely to survive through version bumps. There are +version and endianness checks when attempting to restore from a save. The input parser was the first attempt *ever* at natural-language parsing in a game and has some known deficiencies. While later text diff --git a/advent.desktop b/advent.desktop index d41c896..1cc38f8 100644 --- a/advent.desktop +++ b/advent.desktop @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: (C) Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause [Desktop Entry] Type=Application Name=Open Adventure diff --git a/advent.h b/advent.h index a2c1eb0..fa0767b 100644 --- a/advent.h +++ b/advent.h @@ -1,8 +1,14 @@ +/* + * Dungeon types and macros. + * + * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include +#include #include #include -#include -#include -#include #include "dungeon.h" @@ -12,180 +18,231 @@ #define LCG_C 221587L #define LCG_M 1048576L -#define LINESIZE 1024 -#define TOKLEN 5 // # sigificant characters in a token */ -#define NDWARVES 6 // number of dwarves -#define PIRATE NDWARVES // must be NDWARVES-1 when zero-origin -#define DALTLC LOC_NUGGET // alternate dwarf location -#define INVLIMIT 7 // inventory limit (# of objects) -#define INTRANSITIVE -1 // illegal object number -#define GAMELIMIT 330 // base limit of turns -#define NOVICELIMIT 1000 // limit of turns for novice -#define WARNTIME 30 // late game starts at game.limit-this -#define FLASHTIME 50 // turns from first warning till blinding flash -#define PANICTIME 15 // time left after closing -#define BATTERYLIFE 2500 // turn limit increment from batteries -#define WORD_NOT_FOUND -1 // "Word not found" flag value for the vocab hash functions. -#define WORD_EMPTY 0 // "Word empty" flag value for the vocab hash functions -#define CARRIED -1 // Player is toting it -#define READ_MODE "rb" // b is not needed for POSIX but harmless -#define WRITE_MODE "wb" // b is not needed for POSIX but harmless +#define LINESIZE 1024 +#define TOKLEN 5 // # outputting characters in a token */ +#define PIRATE NDWARVES // must be NDWARVES-1 when zero-origin +#define DALTLC LOC_NUGGET // alternate dwarf location +#define INVLIMIT 7 // inventory limit (# of objects) +#define INTRANSITIVE -1 // illegal object number +#define GAMELIMIT 330 // base limit of turns +#define NOVICELIMIT 1000 // limit of turns for novice +#define WARNTIME 30 // late game starts at game.limit-this +#define FLASHTIME 50 // turns from first warning till blinding flash +#define PANICTIME 15 // time left after closing +#define BATTERYLIFE 2500 // turn limit increment from batteries +#define WORD_NOT_FOUND \ + -1 // "Word not found" flag value for the vocab hash functions. +#define WORD_EMPTY 0 // "Word empty" flag value for the vocab hash functions +#define PIT_KILL_PROB 35 // Percentage probability of dying from fall in pit. +#define CARRIED -1 // Player is toting it +#define READ_MODE "rb" // b is not needed for POSIX but harmless +#define WRITE_MODE "wb" // b is not needed for POSIX but harmless /* Special object-state values - integers > 0 are object-specific */ -#define STATE_NOTFOUND -1 // 'Not found" state of treasures */ -#define STATE_FOUND 0 // After discovered, before messed with -#define STATE_IN_CAVITY 1 // State value common to all gemstones +#define STATE_NOTFOUND -1 // 'Not found" state of treasures +#define STATE_FOUND 0 // After discovered, before messed with +#define STATE_IN_CAVITY 1 // State value common to all gemstones /* Special fixed object-state values - integers > 0 are location */ #define IS_FIXED -1 #define IS_FREE 0 -/* Map a state property value to a negative range, where the object cannot be - * picked up but the value can be recovered later. Avoid colliding with -1, - * which has its own meaning. */ -#define STASHED(obj) (-1 - game.prop[obj]) +/* (ESR) It is fitting that translation of the original ADVENT should + * have left us a maze of twisty little conditionals that resists all + * understanding. Setting and use of what is now the per-object state + * member (which used to be an array of its own) is our mystery. This + * state tangles together information about whether the object is a + * treasure, whether the player has seen it yet, and its activation + * state. + * + * Things we think we know: + * + * STATE_NOTFOUND is only set on treasures. Non-treasures start the + * game in STATE_FOUND. + * + * PROP_STASHIFY is supposed to map a state property value to a + * negative range, where the object cannot be picked up but the value + * can be recovered later. Various objects get this property when + * the cave starts to close. Only seems to be significant for the bird + * and readable objects, notably the clam/oyster - but the code around + * those tests is difficult to read. + * + * All tests of the prop member are done with either these macros or ==. + */ +#define OBJECT_IS_NOTFOUND(obj) (game.objects[obj].prop == STATE_NOTFOUND) +#define OBJECT_IS_FOUND(obj) (game.objects[obj].prop == STATE_FOUND) +#define OBJECT_SET_FOUND(obj) (game.objects[obj].prop = STATE_FOUND) +#define OBJECT_SET_NOT_FOUND(obj) (game.objects[obj].prop = STATE_NOTFOUND) +#define OBJECT_IS_NOTFOUND2(g, o) (g.objects[o].prop == STATE_NOTFOUND) +#define PROP_IS_INVALID(val) (val < -MAX_STATE - 1 || val > MAX_STATE) +#define PROP_STASHIFY(n) (-1 - (n)) +#define OBJECT_STASHIFY(obj, pval) game.objects[obj].prop = PROP_STASHIFY(pval) +#define OBJECT_IS_STASHED(obj) (game.objects[obj].prop < STATE_NOTFOUND) +#define OBJECT_STATE_EQUALS(obj, pval) \ + ((game.objects[obj].prop == pval) || \ + (game.objects[obj].prop == PROP_STASHIFY(pval))) -#define PROMPT "> " +#define PROMPT "> " /* - * DESTROY(N) = Get rid of an item by putting it in LOC_NOWHERE - * MOD(N,M) = Arithmetic modulus - * TOTING(OBJ) = true if the OBJ is being carried - * AT(OBJ) = true if on either side of two-placed object - * HERE(OBJ) = true if the OBJ is at "LOC" (or is being carried) - * CNDBIT(L,N) = true if COND(L) has bit n set (bit 0 is units bit) - * LIQUID() = object number of liquid in bottle - * LIQLOC(LOC) = object number of liquid (if any) at LOC - * FORCED(LOC) = true if LOC moves without asking for input (COND=2) - * DARK(LOC) = true if location "LOC" is dark - * PCT(N) = true N% of the time (N integer from 0 to 100) - * GSTONE(OBJ) = true if OBJ is a gemstone - * FOREST(LOC) = true if LOC is part of the forest - * OUTSID(LOC) = true if location not in the cave - * INSIDE(LOC) = true if location is in the cave or the building at the beginning of the game - * INDEEP(LOC) = true if location is in the Hall of Mists or deeper - * BUG(X) = report bug and exit + * DESTROY(N) = Get rid of an item by putting it in LOC_NOWHERE + * MOD(N,M) = Arithmetic modulus + * TOTING(OBJ) = true if the OBJ is being carried + * AT(OBJ) = true if on either side of two-placed object + * HERE(OBJ) = true if the OBJ is at "LOC" (or is being carried) + * CNDBIT(L,N) = true if COND(L) has bit n set (bit 0 is units bit) + * LIQUID() = object number of liquid in bottle + * LIQLOC(LOC) = object number of liquid (if any) at LOC + * FORCED(LOC) = true if LOC moves without asking for input (COND=2) + * IS_DARK_HERE() = true if location "LOC" is dark + * PCT(N) = true N% of the time (N integer from 0 to 100) + * GSTONE(OBJ) = true if OBJ is a gemstone + * FOREST(LOC) = true if LOC is part of the forest + * OUTSIDE(LOC) = true if location not in the cave + * INSIDE(LOC) = true if location is in the cave or the building at the + * beginning of the game + * INDEEP(LOC) = true if location is in the Hall of Mists or deeper + * BUG(X) = report bug and exit */ -#define DESTROY(N) move(N, LOC_NOWHERE) -#define MOD(N,M) ((N) % (M)) -#define TOTING(OBJ) (game.place[OBJ] == CARRIED) -#define AT(OBJ) (game.place[OBJ] == game.loc || game.fixed[OBJ] == game.loc) -#define HERE(OBJ) (AT(OBJ) || TOTING(OBJ)) -#define CNDBIT(L,N) (tstbit(conditions[L],N)) -#define LIQUID() (game.prop[BOTTLE] == WATER_BOTTLE? WATER : game.prop[BOTTLE] == OIL_BOTTLE ? OIL : NO_OBJECT ) -#define LIQLOC(LOC) (CNDBIT((LOC),COND_FLUID)? CNDBIT((LOC),COND_OILY) ? OIL : WATER : NO_OBJECT) -#define FORCED(LOC) CNDBIT(LOC, COND_FORCED) -#define DARK(DUMMY) (!CNDBIT(game.loc,COND_LIT) && (game.prop[LAMP] == LAMP_DARK || !HERE(LAMP))) -#define PCT(N) (randrange(100) < (N)) -#define GSTONE(OBJ) ((OBJ) == EMERALD || (OBJ) == RUBY || (OBJ) == AMBER || (OBJ) == SAPPH) -#define FOREST(LOC) CNDBIT(LOC, COND_FOREST) -#define OUTSID(LOC) (CNDBIT(LOC, COND_ABOVE) || FOREST(LOC)) -#define INSIDE(LOC) (!OUTSID(LOC) || LOC == LOC_BUILDING) -#define INDEEP(LOC) CNDBIT((LOC),COND_DEEP) -#define BUG(x) bug(x, #x) +#define DESTROY(N) move(N, LOC_NOWHERE) +#define MOD(N, M) ((N) % (M)) +#define TOTING(OBJ) (game.objects[OBJ].place == CARRIED) +#define AT(OBJ) \ + (game.objects[OBJ].place == game.loc || \ + game.objects[OBJ].fixed == game.loc) +#define HERE(OBJ) (AT(OBJ) || TOTING(OBJ)) +#define CNDBIT(L, N) (tstbit(conditions[L], N)) +#define LIQUID() \ + (game.objects[BOTTLE].prop == WATER_BOTTLE ? WATER \ + : game.objects[BOTTLE].prop == OIL_BOTTLE ? OIL \ + : NO_OBJECT) +#define LIQLOC(LOC) \ + (CNDBIT((LOC), COND_FLUID) ? CNDBIT((LOC), COND_OILY) ? OIL : WATER \ + : NO_OBJECT) +#define FORCED(LOC) CNDBIT(LOC, COND_FORCED) +#define IS_DARK_HERE() \ + (!CNDBIT(game.loc, COND_LIT) && \ + (game.objects[LAMP].prop == LAMP_DARK || !HERE(LAMP))) +#define PCT(N) (randrange(100) < (N)) +#define GSTONE(OBJ) \ + ((OBJ) == EMERALD || (OBJ) == RUBY || (OBJ) == AMBER || (OBJ) == SAPPH) +#define FOREST(LOC) CNDBIT(LOC, COND_FOREST) +#define OUTSIDE(LOC) (CNDBIT(LOC, COND_ABOVE) || FOREST(LOC)) +#define INSIDE(LOC) (!OUTSIDE(LOC) || LOC == LOC_BUILDING) +#define INDEEP(LOC) CNDBIT((LOC), COND_DEEP) +#define BUG(x) bug(x, #x) enum bugtype { - SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST, - VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3, - INTRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST, - TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST, - CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION, - LOCATION_HAS_NO_TRAVEL_ENTRIES, - HINT_NUMBER_EXCEEDS_GOTO_LIST, - SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN, - ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH, + SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST, + VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3, + INTRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST, + TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST, + CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION, + LOCATION_HAS_NO_TRAVEL_ENTRIES, + HINT_NUMBER_EXCEEDS_GOTO_LIST, + SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN, + ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH, }; -enum speaktype {touch, look, hear, study, change}; +enum speaktype { touch, look, hear, study, change }; -enum termination {endgame, quitgame, scoregame}; +enum termination { endgame, quitgame, scoregame }; -enum speechpart {unknown, intransitive, transitive}; +enum speechpart { unknown, intransitive, transitive }; -typedef enum {NO_WORD_TYPE, MOTION, OBJECT, ACTION, NUMERIC} word_type_t; +typedef enum { NO_WORD_TYPE, MOTION, OBJECT, ACTION, NUMERIC } word_type_t; -typedef enum scorebonus {none, splatter, defeat, victory} score_t; +typedef enum scorebonus { none, splatter, defeat, victory } score_t; /* Phase codes for action returns. * These were at one time FORTRAN line numbers. - * The values don't matter, but perturb their order at your peril. */ typedef enum { - GO_TERMINATE, - GO_MOVE, - GO_TOP, - GO_CLEAROBJ, - GO_CHECKHINT, - GO_WORD2, - GO_UNKNOWN, - GO_DWARFWAKE, + GO_TERMINATE, + GO_MOVE, + GO_TOP, + GO_CLEAROBJ, + GO_CHECKHINT, + GO_WORD2, + GO_UNKNOWN, + GO_DWARFWAKE, } phase_codes_t; -typedef int vocab_t; // index into a vocabulary array */ -typedef int verb_t; // index into an actions array */ -typedef int obj_t; // index into the object array */ -typedef int loc_t; // index into the locations array */ -typedef int turn_t; // turn counter or threshold */ +/* Use fixed-lwength types to make the save format moore portable */ +typedef int32_t vocab_t; // index into a vocabulary array */ +typedef int32_t verb_t; // index into an actions array */ +typedef int32_t obj_t; // index into the object array */ +typedef int32_t loc_t; // index into the locations array */ +typedef int32_t turn_t; // turn counter or threshold */ +typedef int32_t bool32_t; // turn counter or threshold */ struct game_t { - int32_t lcg_x; - int abbnum; // How often to print int descriptions - score_t bonus; // What kind of finishing bonus we are getting - loc_t chloc; // pirate chest location - loc_t chloc2; // pirate chest alternate location - turn_t clock1; // # turns from finding last treasure to close - turn_t clock2; // # turns from warning till blinding flash - bool clshnt; // has player read the clue in the endgame? - bool closed; // whether we're all the way closed - bool closng; // whether it's closing time yet - bool lmwarn; // has player been warned about lamp going dim? - bool novice; // asked for instructions at start-up? - bool panic; // has player found out he's trapped? - bool wzdark; // whether the loc he's leaving was dark - bool blooded; // has player drunk of dragon's blood? - int conds; // min value for cond[loc] if loc has any hints - int detail; // level of detail in descriptions + int32_t lcg_x; + int32_t abbnum; // How often to print int descriptions + score_t bonus; // What kind of finishing bonus we are getting + loc_t chloc; // pirate chest location + loc_t chloc2; // pirate chest alternate location + turn_t clock1; // # turns from finding last treasure to close + turn_t clock2; // # turns from warning till blinding flash + bool32_t clshnt; // has player read the clue in the endgame? + bool32_t closed; // whether we're all the way closed + bool32_t closng; // whether it's closing time yet + bool32_t lmwarn; // has player been warned about lamp going dim? + bool32_t novice; // asked for instructions at start-up? + bool32_t panic; // has player found out he's trapped? + bool32_t wzdark; // whether the loc he's leaving was dark + bool32_t blooded; // has player drunk of dragon's blood? + int32_t conds; // min value for cond[loc] if loc has any hints + int32_t detail; // level of detail in descriptions - /* dflag controls the level of activation of dwarves: - * 0 No dwarf stuff yet (wait until reaches Hall Of Mists) - * 1 Reached Hall Of Mists, but hasn't met first dwarf - * 2 Met first dwarf, others start moving, no knives thrown yet - * 3 A knife has been thrown (first set always misses) - * 3+ Dwarves are mad (increases their accuracy) */ - int dflag; + /* dflag controls the level of activation of dwarves: + * 0 No dwarf stuff yet (wait until reaches Hall Of Mists) + * 1 Reached Hall Of Mists, but hasn't met first dwarf + * 2 Met 1t dwarf, others start moving, no knives thrown yet + * 3 A knife has been thrown (first set always misses) 3+ + * Dwarves are mad (increases their accuracy) */ + int32_t dflag; - int dkill; // dwarves killed - int dtotal; // total dwarves (including pirate) in loc - int foobar; // progress in saying "FEE FIE FOE FOO". - int holdng; // number of objects being carried - int igo; // # uses of "go" instead of a direction - int iwest; // # times he's said "west" instead of "w" - int knfloc; // knife location; 0 if none, -1 after caveat - turn_t limit; // lifetime of lamp - loc_t loc; // where player is now - loc_t newloc; // where player is going - turn_t numdie; // number of times killed so far - loc_t oldloc; // where player was - loc_t oldlc2; // where player was two moves ago - obj_t oldobj; // last object player handled - int saved; // point penalty for saves - int tally; // count of treasures gained - int thresh; // current threshold for endgame scoring tier - turn_t trndex; // FIXME: not used, remove on next format bump - turn_t trnluz; // # points lost so far due to turns used - turn_t turns; // counts commands given (ignores yes/no) - char zzword[TOKLEN + 1]; // randomly generated magic word from bird - int abbrev[NLOCATIONS + 1]; // has location been seen? - int atloc[NLOCATIONS + 1]; // head of object linked list per location - int dseen[NDWARVES + 1]; // true if dwarf has seen him - loc_t dloc[NDWARVES + 1]; // location of dwarves, initially hard-wired in - loc_t odloc[NDWARVES + 1]; // prior loc of each dwarf, initially garbage - loc_t fixed[NOBJECTS + 1]; // fixed location of object (if not IS_FREE) - obj_t link[NOBJECTS * 2 + 1];// object-list links - loc_t place[NOBJECTS + 1]; // location of object - int hinted[NHINTS]; // hinted[i] = true iff hint i has been used. - int hintlc[NHINTS]; // hintlc[i] = how int at LOC with cond bit i - int prop[NOBJECTS + 1]; // object state array */ + int32_t dkill; // dwarves killed + int32_t dtotal; // total dwarves (including pirate) in loc + int32_t foobar; // progress in saying "FEE FIE FOE FOO". + int32_t holdng; // number of objects being carried + int32_t igo; // # uses of "go" instead of a direction + int32_t iwest; // # times he's said "west" instead of "w" + loc_t knfloc; // knife location; LOC_NOWERE if none, -1 after caveat + turn_t limit; // lifetime of lamp + loc_t loc; // where player is now + loc_t newloc; // where player is going + turn_t numdie; // number of times killed so far + loc_t oldloc; // where player was + loc_t oldlc2; // where player was two moves ago + obj_t oldobj; // last object player handled + int32_t saved; // point penalty for saves + int32_t tally; // count of treasures gained + int32_t thresh; // current threshold for endgame scoring tier + bool32_t seenbigwords; // have we red the graffiti in the Giant's Room? + turn_t trnluz; // # points lost so far due to turns used + turn_t turns; // counts commands given (ignores yes/no) + char zzword[TOKLEN + 1]; // randomly generated magic word from bird + struct { + int32_t abbrev; // has location been seen? + int32_t atloc; // head of object linked list per location + } locs[NLOCATIONS + 1]; + struct { + int32_t seen; // true if dwarf has seen him + loc_t loc; // location of dwarves, initially hard-wired in + loc_t oldloc; // prior loc of each dwarf, initially garbage + } dwarves[NDWARVES + 1]; + struct { + loc_t fixed; // fixed location of object (if not IS_FREE) + int32_t prop; // object state + loc_t place; // location of object + } objects[NOBJECTS + 1]; + struct { + bool32_t used; // hints[i].used = true iff hint i has been used. + int32_t lc; // hints[i].lc = show int at LOC with cond bit i + } hints[NHINTS]; + obj_t link[NOBJECTS * 2 + 1]; // object-list links }; /* @@ -193,47 +250,83 @@ struct game_t { * This data is not saved in a saved game. */ struct settings_t { - FILE *logfp; - bool oldstyle; - bool prompt; - char **argv; - int argc; - int optind; - FILE *scriptfp; + FILE *logfp; + bool oldstyle; + bool prompt; + char **argv; + int argc; + int optind; + FILE *scriptfp; + int debug; }; typedef struct { - char raw[LINESIZE]; - vocab_t id; - word_type_t type; + char raw[LINESIZE]; + vocab_t id; + word_type_t type; } command_word_t; -typedef enum {EMPTY, RAW, TOKENIZED, GIVEN, PREPROCESSED, PROCESSING, EXECUTED} command_state_t; +typedef enum { + EMPTY, + RAW, + TOKENIZED, + GIVEN, + PREPROCESSED, + PROCESSING, + EXECUTED +} command_state_t; typedef struct { - enum speechpart part; - command_word_t word[2]; - verb_t verb; - obj_t obj; - command_state_t state; + enum speechpart part; + command_word_t word[2]; + verb_t verb; + obj_t obj; + command_state_t state; } command_t; +/* + * Bump on save format change. + * + * Note: Verify that the tests run clean before bumping this, then rebuild the + * check files afterwards. Otherwise you will get a spurious failure due to the + * old version having been generated into a check file. + */ +#define SAVE_VERSION 31 + +/* + * Goes at start of file so saves can be identified by file(1) and the like. + */ +#define ADVENT_MAGIC "open-adventure\n" + +/* + * If you change the first three members, the resume function may not properly + * reject saves from older versions. Later members can change, but bump the + * version when you do that. + */ +struct save_t { + char magic[sizeof(ADVENT_MAGIC)]; + int32_t version; + int32_t canary; + struct game_t game; +}; + extern struct game_t game; +extern struct save_t save; extern struct settings_t settings; extern char *myreadline(const char *); extern bool get_command_input(command_t *); extern void clear_command(command_t *); -extern void speak(const char*, ...); +extern void speak(const char *, ...); extern void sspeak(int msg, ...); extern void pspeak(vocab_t, enum speaktype, bool, int, ...); extern void rspeak(vocab_t, ...); -extern void echo_input(FILE*, const char*, const char*); +extern void echo_input(FILE *, const char *, const char *); extern bool silent_yes_or_no(void); -extern bool yes_or_no(const char*, const char*, const char*); +extern bool yes_or_no(const char *, const char *, const char *); extern void juggle(obj_t); extern void move(obj_t, loc_t); -extern loc_t put(obj_t, int, int); +extern void put(obj_t, loc_t, int); extern void carry(obj_t, loc_t); extern void drop(obj_t, loc_t); extern int atdwrf(loc_t); @@ -243,7 +336,10 @@ extern void set_seed(int32_t); extern int32_t randrange(int32_t); extern int score(enum termination); extern void terminate(enum termination) __attribute__((noreturn)); -extern int savefile(FILE *, int32_t); +extern int savefile(FILE *); +#if defined ADVENT_AUTOSAVE +extern void autosave(void); +#endif extern int suspend(void); extern int resume(void); extern int restore(FILE *); @@ -251,8 +347,7 @@ extern int initialise(void); extern phase_codes_t action(command_t); extern void state_change(obj_t, int); extern bool is_valid(struct game_t); - -void bug(enum bugtype, const char *) __attribute__((__noreturn__)); +extern void bug(enum bugtype, const char *) __attribute__((__noreturn__)); /* represent an empty command word */ static const command_word_t empty_command_word = { diff --git a/advent.svg b/advent.svg index 8f50e5e..02a0e1b 100644 --- a/advent.svg +++ b/advent.svg @@ -2,30 +2,8 @@ diff --git a/adventure.yaml b/adventure.yaml index cb577bd..141ffb7 100644 --- a/adventure.yaml +++ b/adventure.yaml @@ -1,3 +1,10 @@ +# SPDX-FileCopyrightText: (C) Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +# +# batchspell: add XYZZY Twopit Bedquilt ne se sw nw dwarves dwarvish gouts +# batchspell: add Crowther add axe pinin Har har meself Hmmm Adventuredom +# batchspell: add Tsk gameplay bugfixes +# # This YAML file gets processed into a collection of data structures and # variable initializers describing Colossal Cave. It replaces an ad-hoc # text database shipped with Adventure versions up to 2.5. The format @@ -7,7 +14,7 @@ # We define a bunch of YAML structures: # # motions: Motion words, grouped into synonyms. The 'oldstyle' -# attribute, if false, means that single-letter synonyms should be +# attribute, if false, means that single-letter synonyms should not be # accepted in oldstyle mode; it defaults to true. # # actions: Action words, grouped into synonyms, and their corresponding @@ -24,7 +31,7 @@ # long: Long description, always shown on first encounter. # short: Short description. If none, use long description. # maptag: Tag for mapping, not used by the game itself. -# Only used if the "short" property in !!null. +# Only used if the "short" property is !!null. # conditions: A dictionary of attributes # LIT Light # OILY If FLUID flag is on: true for oil, false for water @@ -115,8 +122,6 @@ # %S = the letter 's' or nothing (if a previous %d value is exactly 1) # %V = substitute program version string # -# Copyright (c) 2017 by Eric S. Raymond -# SPDX-License-Identifier: BSD-2-clause # Motion names of the form MOT_* are not explicitly referenced in the # locations YAML, but usually get compiled into generated C. @@ -783,7 +788,7 @@ locations: !!omap You are in a low n/s passage at a hole in the floor. The hole goes down to an e/w passage. short: 'You''re in n/s passage above e/w passage.' - maptag: !!null + maptag: "Floor hole." conditions: {DEEP: true} travel: [ {verbs: [HALL, OUT, SOUTH], action: [goto, LOC_KINGHALL]}, @@ -1729,7 +1734,7 @@ locations: !!omap description: long: 'You are in a long sloping corridor with ragged sharp walls.' short: !!null - maptag: !!null + maptag: "Sloping corridor" conditions: {DEEP: true} travel: [ {verbs: [UPWAR, SHELL], action: [goto, LOC_SHELLROOM]}, @@ -2047,7 +2052,7 @@ locations: !!omap roar, so loud that the entire cave seems to be trembling. Another passage leads south, and a low crawl goes east. short: 'You''re at junction with warm walls.' - maptag: !!null + maptag: "Warm junction" conditions: {NOARRR: true, DEEP: true} sound: LOUD_ROAR travel: [ @@ -2107,7 +2112,7 @@ locations: !!omap ] - LOC_LIMESTONE: description: - long: + long: |- You are walking along a gently sloping north/south passage lined with oddly shaped limestone formations. short: 'You''re in limestone passage.' @@ -2755,7 +2760,7 @@ locations: !!omap You are on a small ledge at the top of a nearly vertical cliff. There is a low crawl leading off to the northeast. short: 'You''re at top of cliff.' - maptag: 'Cliftop' + maptag: 'Clifftop' conditions: {DEEP: true} travel: [ {verbs: [CLIMB, DOWN], action: [goto, LOC_CLIFFACE]}, @@ -2834,6 +2839,18 @@ locations: !!omap {verbs: [], action: [goto, LOC_Y2]}, ] +# Starting locations of dwarves. +# Sixth dwarf is special (the pirate). He always starts at his +# chest's eventual location inside the maze. +dwarflocs: [ + LOC_KINGHALL, + LOC_WESTBANK, + LOC_Y2, + LOC_ALIKE3, + LOC_COMPLEX, + LOC_MAZEEND12, +] + arbitrary_messages: !!omap - NO_MESSAGE: !!null - CAVE_NEARBY: |- @@ -3015,6 +3032,7 @@ arbitrary_messages: !!omap black smoke. - SHELL_IMPERVIOUS: 'The shell is very strong and is impervious to attack.' - START_OVER: 'What''s the matter, can''t you read? Now you''d best start over.' +- WELL_POINTLESS: 'Well, that was remarkably pointless!' - DRAGON_SCALES: 'The axe bounces harmlessly off the dragon''s thick scales.' - NASTY_DRAGON: 'The dragon looks rather nasty. You''d best not try to get by.' - BIRD_BURNT: |- @@ -3173,6 +3191,7 @@ arbitrary_messages: !!omap To achieve the next higher rating would be a neat trick! Congratulations!! - OFF_SCALE: 'You just went off my scale!!' +- SAVERESUME_DISABLED: 'Save and resume are disabled.' - RESUME_HELP: 'To resume your Adventure, start a new game and then say "RESUME".' # This message is not currently used #- TABLE_SPACE: |- @@ -3183,18 +3202,18 @@ arbitrary_messages: !!omap # %d of %d "random" messages %d of %d "class" messages # %d of %d hints %d of %d turn thresholds' - RESUME_ABANDON: 'To resume an earlier Adventure, you must abandon the current one.' +- BAD_SAVE: 'Oops, that does not look like a valid save file.' - VERSION_SKEW: |- I'm sorry, but that Adventure was begun using Version %d.%d of the save file format, and this program uses Version %d.%d. You must find an instance using that other version in order to resume that Adventure. -# This message is not currently used -#- SAVE_TAMPERING: |- -# A dark fog creeps in to surround you. From somewhere in the fog you -# hear a stern voice. "This Adventure has been tampered with! You have -# been dabbling in magic, knowing not the havoc you might cause thereby. -# Leave at once, before you do irrevocable harm!" The fog thickens, -# until at last you can see nothing at all. Your vision then clears, -# and you find yourself back in The Real World. +- SAVE_TAMPERING: |- + A dark fog creeps in to surround you. From somewhere in the fog you + hear a stern voice. "This Adventure has been tampered with! You have + been dabbling in magic, knowing not the havoc you might cause thereby. + Leave at once, before you do irrevocable harm!" The fog thickens, + until at last you can see nothing at all. Your vision then clears, + and you find yourself back in The Real World. - TWIST_TURN: |- Sorry, but the path twisted and turned so much that I can't figure out which way to go to get back. @@ -3227,10 +3246,10 @@ classes: message: 'All of Adventuredom gives tribute to you, Adventurer Grandmaster!' - threshold: 9999 message: |- - 'Adventuredom stands in awe -- you have now joined the ranks of the + Adventuredom stands in awe -- you have now joined the ranks of the W O R L D C H A M P I O N A D V E N T U R E R S ! It may interest you to know that the Dungeon-Master himself has, to - my knowledge, never achieved this threshold in fewer than 330 turns.' + my knowledge, never achieved this threshold in fewer than 330 turns. turn_thresholds: - threshold: 350 @@ -3250,7 +3269,9 @@ turn_thresholds: Good grief, don't you *EVER* give up? Do you realize you've spent over 2500 turns at this? That's another ten points off, a total of twenty points lost for taking so long. - + +# Objects names OBJ_* are not made visible by the map-graph generator. +# Don't change these casually. objects: !!omap - NO_OBJECT: inventory: !!null @@ -3415,7 +3436,7 @@ objects: !!omap - 'There are a few recent issues of "Spelunker Today" magazine here.' texts: - |- - I'm afraid the magazine is written in dwarvish. But pencilled on one + I'm afraid the magazine is written in dwarvish. But penciled on one cover you see, "Please leave the magazines at the construction site." - DWARF: words: ['dwarf', 'dwarv'] @@ -3923,11 +3944,13 @@ obituaries: 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? + # batchspell: add wr yes_response: |- All right. But don't blame me if something goes wr...... --- POOF!! --- You are engulfed in a cloud of orange smoke. Coughing and gasping, you emerge from the smoke and find.... + # batchspell: remove wr - query: |- You clumsy oaf, you've done it again! I don't know how long I can keep this up. Do you want me to try reincarnating you again? @@ -4144,7 +4167,7 @@ actions: !!omap message: |- Mist is a white vapor, usually water, seen from time to time in caverns. It can be found anywhere but is frequently a sign of a deep - pit leading down to water.' + pit leading down to water. words: ['mist'] noaction: true - FBOMB: @@ -4209,7 +4232,7 @@ actions: !!omap message: |- There is a puff of orange smoke; within it, fiery runes spell out: - \tOpen Adventure %V - http://www.catb.org/esr/open-adventure/ + Open Adventure %V - http://www.catb.org/esr/open-adventure/ words: ['versi'] noaction: true diff --git a/cheat.c b/cheat.c index 92500e4..4e94025 100644 --- a/cheat.c +++ b/cheat.c @@ -1,101 +1,96 @@ /* * 'cheat' is a tool for generating save game files to test states that ought * not happen. It leverages chunks of advent, mostly initialize() and - * savefile(), so we know we're always outputing save files that advent + * savefile(), so we know we're always outputting save files that advent * can import. * - * Copyright (c) 1977, 2005 by Will Crowther and Don Woods - * Copyright (c) 2017 by Eric S. Raymond - * SPDX-License-Identifier: BSD-2-clause + * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods + * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include -#include -#include -#include #include "advent.h" +#include +#include +#include +#include +#include -int main(int argc, char *argv[]) -{ - int ch; - char *savefilename = NULL; - int version = 0; - FILE *fp = NULL; +int main(int argc, char *argv[]) { + int ch; + char *savefilename = NULL; + FILE *fp = NULL; - // Initialize game variables - initialise(); + // Initialize game variables + initialise(); - /* we're generating a saved game, so saved once by default, - * unless overridden with command-line options below. - */ - game.saved = 1; + /* we're generating a saved game, so saved once by default, + * unless overridden with command-line options below. + */ + game.saved = 1; - /* Options. */ - const char* opts = "d:l:s:t:v:o:"; - const char* usage = "Usage: %s [-d numdie] [-s numsaves] [-v version] -o savefilename \n" - " -d number of deaths. Signed integer.\n" - " -l lifetime of lamp in turns. Signed integer.\n" - " -s number of saves. Signed integer.\n" - " -t number of turns. Signed integer.\n" - " -v version number of save format.\n" - " -o required. File name of save game to write.\n"; + /* Options. */ + const char *opts = "d:l:s:t:v:o:"; + const char *usage = + "Usage: %s [-d numdie] [-s numsaves] [-v version] -o savefilename " + "\n" + " -d number of deaths. Signed integer.\n" + " -l lifetime of lamp in turns. Signed integer.\n" + " -s number of saves. Signed integer.\n" + " -t number of turns. Signed integer.\n" + " -v version number of save format.\n" + " -o required. File name of save game to write.\n"; - while ((ch = getopt(argc, argv, opts)) != EOF) { - switch (ch) { - case 'd': - game.numdie = (turn_t)atoi(optarg); - printf("cheat: game.numdie = %d\n", game.numdie); - break; - case 'l': - game.limit = (turn_t)atoi(optarg); - printf("cheat: game.limit = %d\n", game.limit); - break; - case 's': - game.saved = (int)atoi(optarg); - printf("cheat: game.saved = %d\n", game.saved); - break; - case 't': - game.turns = (turn_t)atoi(optarg); - printf("cheat: game.turns = %d\n", game.turns); - break; - case 'v': - version = atoi(optarg); - printf("cheat: version = %d\n", version); - break; - case 'o': - savefilename = optarg; - break; - default: - fprintf(stderr, - usage, argv[0]); - exit(EXIT_FAILURE); - break; - } - } + while ((ch = getopt(argc, argv, opts)) != EOF) { + switch (ch) { + case 'd': + game.numdie = (turn_t)atoi(optarg); + printf("cheat: game.numdie = %d\n", game.numdie); + break; + case 'l': + game.limit = (turn_t)atoi(optarg); + printf("cheat: game.limit = %d\n", game.limit); + break; + case 's': + game.saved = (int)atoi(optarg); + printf("cheat: game.saved = %d\n", game.saved); + break; + case 't': + game.turns = (turn_t)atoi(optarg); + printf("cheat: game.turns = %d\n", game.turns); + break; + case 'v': + save.version = atoi(optarg); + printf("cheat: version = %d\n", save.version); + break; + case 'o': + savefilename = optarg; + break; + default: + fprintf(stderr, usage, argv[0]); + exit(EXIT_FAILURE); + break; + } + } - // Save filename required; the point of cheat is to generate save file - if (savefilename == NULL) { - fprintf(stderr, - usage, argv[0]); - fprintf(stderr, - "ERROR: filename required\n"); - exit(EXIT_FAILURE); - } + // Save filename required; the point of cheat is to generate save file + if (savefilename == NULL) { + fprintf(stderr, usage, argv[0]); + fprintf(stderr, "ERROR: filename required\n"); + exit(EXIT_FAILURE); + } - fp = fopen(savefilename, WRITE_MODE); - if (fp == NULL) { - fprintf(stderr, - "Can't open file %s. Exiting.\n", savefilename); - exit(EXIT_FAILURE); - } + fp = fopen(savefilename, WRITE_MODE); + if (fp == NULL) { + fprintf(stderr, "Can't open file %s. Exiting.\n", savefilename); + exit(EXIT_FAILURE); + } - savefile(fp, version); + savefile(fp); - fclose(fp); + fclose(fp); - printf("cheat: %s created.\n", savefilename); + printf("cheat: %s created.\n", savefilename); - return EXIT_SUCCESS; + return EXIT_SUCCESS; } // LCOV_EXCL_START @@ -104,12 +99,7 @@ int main(int argc, char *argv[]) * See the actually useful version of this in main.c */ -char *myreadline(const char *prompt) -{ - return readline(prompt); -} +char *myreadline(const char *prompt) { return readline(prompt); } // LCOV_EXCL_STOP /* end */ - - diff --git a/control b/control index 841e7ab..e47484d 100644 --- a/control +++ b/control @@ -5,11 +5,10 @@ Package: open-adventure Description: Colossal Cave Adventure, the 1995 430-point version. This is the last descendant of the original 1976 Colossal Cave Adventure - worked on by the original authors - Crowther & Woods. It has sometimes - been known as Adventure 2.5. The original PDP-10 name 'advent' is used - for the built program to avoid collision with the BSD Games version. - -XBS-Destinations: mailto:ubuntu-devel-discuss@lists.ubuntu.com + worked on by the original authors - Crowther & Woods; it is shipped with + their permission and encouragement. It has sometimes been known as + Adventure 2.5. The original PDP-10 name 'advent' is used for the + built program to avoid collision with the BSD Games version. Homepage: http://www.catb.org/~esr/open-adventure diff --git a/hints.adoc b/hints.adoc index 1d2b092..f7e25e1 100644 --- a/hints.adoc +++ b/hints.adoc @@ -1,4 +1,6 @@ = Non-spoiler hints = +// SPDX-FileCopyrightText: (C) Eric S. Raymond +// SPDX-License-Identifier: CC-BY-4.0 Say the words you see. They can have interesting effects. diff --git a/history.adoc b/history.adoc index 6f86fac..8ca5da7 100644 --- a/history.adoc +++ b/history.adoc @@ -1,5 +1,7 @@ = A brief history of Colossal Cave Adventure = by Eric S. Raymond +// SPDX-FileCopyrightText: (C) Eric S. Raymond +// SPDX-License-Identifier: CC-BY-4.0 Adventure is the fons et origo of all later dungeon-crawling computer games, the granddaddy of interactive fiction, and one of the hallowed @@ -163,9 +165,9 @@ even as primitive as Adventure's. - [[[SN]]] http://www.digitalhumanities.org/dhq/vol/1/2/000009/000009.html[Digital - Humanties Quarterly] + Humanities Quarterly] -- [[[DND]]] https://en.wikipedia.org/wiki/Dnd_(video_game)[dnd (ivdeo game)] +- [[[DND]]] https://en.wikipedia.org/wiki/Dnd_(video_game)[dnd (video game)] - [[[WUMPUS]]] https://en.wikipedia.org/wiki/Hunt_the_Wumpus[Hunt The Wumpus] diff --git a/init.c b/init.c index 9333df3..372e494 100644 --- a/init.c +++ b/init.c @@ -1,101 +1,96 @@ /* * Initialisation * - * Copyright (c) 1977, 2005 by Will Crowther and Don Woods - * Copyright (c) 2017 by Eric S. Raymond - * SPDX-License-Identifier: BSD-2-clause + * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods + * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include -#include +#include #include +#include +#include #include +#include #include "advent.h" -struct settings_t settings = { - .logfp = NULL, - .oldstyle = false, - .prompt = true -}; +struct settings_t settings = {.logfp = NULL, .oldstyle = false, .prompt = true}; struct game_t game = { - .dloc[1] = LOC_KINGHALL, - .dloc[2] = LOC_WESTBANK, - .dloc[3] = LOC_Y2, - .dloc[4] = LOC_ALIKE3, - .dloc[5] = LOC_COMPLEX, - - /* Sixth dwarf is special (the pirate). He always starts at his + /* Last dwarf is special (the pirate). He always starts at his * chest's eventual location inside the maze. This loc is saved * in chloc for ref. The dead end in the other maze has its * loc stored in chloc2. */ - .dloc[6] = LOC_MAZEEND12, - .chloc = LOC_MAZEEND12, - .chloc2 = LOC_DEADEND13, - .abbnum = 5, - .clock1 = WARNTIME, - .clock2 = FLASHTIME, - .newloc = LOC_START, - .loc = LOC_START, - .limit = GAMELIMIT, - .foobar = WORD_EMPTY, + .chloc = LOC_MAZEEND12, .chloc2 = LOC_DEADEND13, .abbnum = 5, + .clock1 = WARNTIME, .clock2 = FLASHTIME, .newloc = LOC_START, + .loc = LOC_START, .limit = GAMELIMIT, .foobar = WORD_EMPTY, }; -int initialise(void) -{ - if (settings.oldstyle) - printf("Initialising...\n"); +int initialise(void) { + if (settings.oldstyle) { + printf("Initialising...\n"); + } - srand(time(NULL)); - int seedval = (int)rand(); - set_seed(seedval); + srand(time(NULL)); + int seedval = (int)rand(); + set_seed(seedval); - for (int i = 1; i <= NOBJECTS; i++) { - game.place[i] = LOC_NOWHERE; - } + for (int i = 1; i <= NDWARVES; i++) { + game.dwarves[i].loc = dwarflocs[i - 1]; + } - for (int i = 1; i <= NLOCATIONS; i++) { - if (!(locations[i].description.big == 0 || - tkey[i] == 0)) { - int k = tkey[i]; - if (travel[k].motion == HERE) - conditions[i] |= (1 << COND_FORCED); - } - } + for (int i = 1; i <= NOBJECTS; i++) { + game.objects[i].place = LOC_NOWHERE; + } - /* Set up the game.atloc and game.link arrays. - * We'll use the DROP subroutine, which prefaces new objects on the - * lists. Since we want things in the other order, we'll run the - * loop backwards. If the object is in two locs, we drop it twice. - * Also, since two-placed objects are typically best described - * last, we'll drop them first. */ - for (int i = NOBJECTS; i >= 1; i--) { - if (objects[i].fixd > 0) { - drop(i + NOBJECTS, objects[i].fixd); - drop(i, objects[i].plac); - } - } + for (int i = 1; i <= NLOCATIONS; i++) { + if (!(locations[i].description.big == 0 || tkey[i] == 0)) { + int k = tkey[i]; + if (travel[k].motion == HERE) { + conditions[i] |= (1 << COND_FORCED); + } + } + } - for (int i = 1; i <= NOBJECTS; i++) { - int k = NOBJECTS + 1 - i; - game.fixed[k] = objects[k].fixd; - if (objects[k].plac != 0 && objects[k].fixd <= 0) - drop(k, objects[k].plac); - } + /* Set up the game.locs atloc and game.link arrays. + * We'll use the DROP subroutine, which prefaces new objects on the + * lists. Since we want things in the other order, we'll run the + * loop backwards. If the object is in two locs, we drop it twice. + * Also, since two-placed objects are typically best described + * last, we'll drop them first. */ + for (int i = NOBJECTS; i >= 1; i--) { + if (objects[i].fixd > 0) { + drop(i + NOBJECTS, objects[i].fixd); + drop(i, objects[i].plac); + } + } - /* Treasure props are initially -1, and are set to 0 the first time - * they are described. game.tally keeps track of how many are - * not yet found, so we know when to close the cave. */ - for (int treasure = 1; treasure <= NOBJECTS; treasure++) { - if (objects[treasure].is_treasure) { - if (objects[treasure].inventory != 0) - game.prop[treasure] = STATE_NOTFOUND; - game.tally = game.tally - game.prop[treasure]; - } - } - game.conds = setbit(11); + for (int i = 1; i <= NOBJECTS; i++) { + int k = NOBJECTS + 1 - i; + game.objects[k].fixed = objects[k].fixd; + if (objects[k].plac != 0 && objects[k].fixd <= 0) { + drop(k, objects[k].plac); + } + } - return seedval; + /* Treasure props are initially STATE_NOTFOUND, and are set to + * STATE_FOUND the first time they are described. game.tally + * keeps track of how many are not yet found, so we know when to + * close the cave. + * (ESR) Non-treasures are set to STATE_FOUND explicitly so we + * don't rely on the value of uninitialized storage. This is to + * make translation to future languages easier. */ + for (int object = 1; object <= NOBJECTS; object++) { + if (objects[object].is_treasure) { + ++game.tally; + if (objects[object].inventory != NULL) { + OBJECT_SET_NOT_FOUND(object); + } + } else { + OBJECT_SET_FOUND(object); + } + } + game.conds = setbit(COND_HBASE); + + return seedval; } diff --git a/main.c b/main.c index f74bd8f..835fc1b 100644 --- a/main.c +++ b/main.c @@ -1,490 +1,470 @@ /* - * Copyright (c) 1977, 2005 by Will Crowther and Don Woods - * Copyright (c) 2017 by Eric S. Raymond - * SPDX-License-Identifier: BSD-2-clause + * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods + * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include -#include +#include "advent.h" +#include +#include #include #include +#include +#include +#include #include -#include #include -#include -#include "advent.h" -#include "dungeon.h" -#define DIM(a) (sizeof(a)/sizeof(a[0])) +#define DIM(a) (sizeof(a) / sizeof(a[0])) + +#if defined ADVENT_AUTOSAVE +static FILE *autosave_fp; +void autosave(void) { + if (autosave_fp != NULL) { + rewind(autosave_fp); + savefile(autosave_fp); + fflush(autosave_fp); + } +} +#endif // LCOV_EXCL_START // exclude from coverage analysis because it requires interactivity to test -static void sig_handler(int signo) -{ - if (signo == SIGINT) { - if (settings.logfp != NULL) - fflush(settings.logfp); - } - exit(EXIT_FAILURE); +static void sig_handler(int signo) { + if (signo == SIGINT) { + if (settings.logfp != NULL) { + fflush(settings.logfp); + } + } + +#if defined ADVENT_AUTOSAVE + if (signo == SIGHUP || signo == SIGTERM) { + autosave(); + } +#endif + exit(EXIT_FAILURE); } // LCOV_EXCL_STOP -/* - * MAIN PROGRAM - * - * Adventure (rev 2: 20 treasures) - * History: Original idea & 5-treasure version (adventures) by Willie Crowther - * 15-treasure version (adventure) by Don Woods, April-June 1977 - * 20-treasure version (rev 2) by Don Woods, August 1978 - * Errata fixed: 78/12/25 - * Revived 2017 as Open Adventure. - */ +char *myreadline(const char *prompt) { + /* + * This function isn't required for gameplay, readline() straight + * up would suffice for that. It's where we interpret command-line + * logfiles for testing purposes. + */ + /* Normal case - no script arguments */ + if (settings.argc == 0) { + char *ln = readline(prompt); + if (ln == NULL) { + fputs(prompt, stdout); + } + return ln; + } -static bool do_command(void); -static bool do_move(void); + char *buf = malloc(LINESIZE + 1); + for (;;) { + if (settings.scriptfp == NULL || feof(settings.scriptfp)) { + if (settings.optind >= settings.argc) { + free(buf); + return NULL; + } -int main(int argc, char *argv[]) -{ - int ch; + char *next = settings.argv[settings.optind++]; - /* Options. */ + if (settings.scriptfp != NULL && + feof(settings.scriptfp)) { + fclose(settings.scriptfp); + } + if (strcmp(next, "-") == 0) { + settings.scriptfp = stdin; // LCOV_EXCL_LINE + } else { + settings.scriptfp = fopen(next, "r"); + } + } -#ifndef ADVENT_NOSAVE - const char* opts = "l:or:"; - const char* usage = "Usage: %s [-l logfilename] [-o] [-r restorefilename] [script...]\n"; - FILE *rfp = NULL; -#else - const char* opts = "l:o"; - const char* usage = "Usage: %s [-l logfilename] [-o] [script...]\n"; -#endif - while ((ch = getopt(argc, argv, opts)) != EOF) { - switch (ch) { - case 'l': - settings.logfp = fopen(optarg, "w"); - if (settings.logfp == NULL) - fprintf(stderr, - "advent: can't open logfile %s for write\n", - optarg); - signal(SIGINT, sig_handler); - break; - case 'o': - settings.oldstyle = true; - settings.prompt = false; - break; -#ifndef ADVENT_NOSAVE - case 'r': - rfp = fopen(optarg, "r"); - if (rfp == NULL) - fprintf(stderr, - "advent: can't open save file %s for read\n", - optarg); - break; -#endif - default: - fprintf(stderr, - usage, argv[0]); - fprintf(stderr, - " -l create a log file of your game named as specified'\n"); - fprintf(stderr, - " -o 'oldstyle' (no prompt, no command editing, displays 'Initialising...')\n"); -#ifndef ADVENT_NOSAVE - fprintf(stderr, - " -r restore from specified saved game file\n"); -#endif - exit(EXIT_FAILURE); - break; - } - } + if (isatty(fileno(settings.scriptfp)) && !settings.oldstyle) { + free(buf); // LCOV_EXCL_LINE + return readline(prompt); // LCOV_EXCL_LINE + } else { + char *ln = fgets(buf, LINESIZE, settings.scriptfp); + if (ln != NULL) { + fputs(prompt, stdout); + fputs(ln, stdout); + return ln; + } + } + } - /* copy inncation line part after switches */ - settings.argc = argc - optind; - settings.argv = argv + optind; - settings.optind = 0; - - /* Initialize game variables */ - int seedval = initialise(); - -#ifndef ADVENT_NOSAVE - if (!rfp) { - game.novice = yes_or_no(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]); - if (game.novice) - game.limit = NOVICELIMIT; - } else { - restore(rfp); - } -#else - game.novice = yes_or_no(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]); - if (game.novice) - game.limit = NOVICELIMIT; -#endif - - if (settings.logfp) - fprintf(settings.logfp, "seed %d\n", seedval); - - /* interpret commands until EOF or interrupt */ - for (;;) { - // if we're supposed to move, move - if (!do_move()) - continue; - - // get command - if (!do_command()) - break; - } - /* show score and exit */ - terminate(quitgame); -} - -char *myreadline(const char *prompt) -{ - /* - * This function isbn't required for gameplay, readline() straight - * up would suffice for tat. It's where we interpret command-line - * logfiles for testing purposes. - */ - /* Normal case - no script arguments */ - if (settings.argc == 0) - return readline(prompt); - - char *buf = malloc(LINESIZE + 1); - for (;;) { - if (settings.scriptfp == NULL || feof(settings.scriptfp)) { - if (settings.optind >= settings.argc) { - free(buf); - return NULL; - } - - char *next = settings.argv[settings.optind++]; - - if (settings.scriptfp != NULL && feof(settings.scriptfp)) - fclose(settings.scriptfp); - if (strcmp(next, "-") == 0) - settings.scriptfp = stdin; // LCOV_EXCL_LINE - else - settings.scriptfp = fopen(next, "r"); - } - - if (isatty(fileno(settings.scriptfp))) { - free(buf); // LCOV_EXCL_LINE - return readline(prompt); // LCOV_EXCL_LINE - } else { - char *ln = fgets(buf, LINESIZE, settings.scriptfp); - if (ln != NULL) { - fputs(PROMPT, stdout); - fputs(ln, stdout); - return ln; - } - } - } - - return NULL; + return NULL; } /* Check if this loc is eligible for any hints. If been here int * enough, display. Ignore "HINTS" < 4 (special stuff, see database * notes). */ -static void checkhints(void) -{ - if (conditions[game.loc] >= game.conds) { - for (int hint = 0; hint < NHINTS; hint++) { - if (game.hinted[hint]) - continue; - if (!CNDBIT(game.loc, hint + 1 + COND_HBASE)) - game.hintlc[hint] = -1; - ++game.hintlc[hint]; - /* Come here if he's been int enough at required loc(s) for some - * unused hint. */ - if (game.hintlc[hint] >= hints[hint].turns) { - int i; +static void checkhints(void) { + if (conditions[game.loc] >= game.conds) { + for (int hint = 0; hint < NHINTS; hint++) { + if (game.hints[hint].used) { + continue; + } + if (!CNDBIT(game.loc, hint + 1 + COND_HBASE)) { + game.hints[hint].lc = -1; + } + ++game.hints[hint].lc; + /* Come here if he's been int enough at required loc(s) + * for some unused hint. */ + if (game.hints[hint].lc >= hints[hint].turns) { + int i; - switch (hint) { - case 0: - /* cave */ - if (game.prop[GRATE] == GRATE_CLOSED && !HERE(KEYS)) - break; - game.hintlc[hint] = 0; - return; - case 1: /* bird */ - if (game.place[BIRD] == game.loc && TOTING(ROD) && game.oldobj == BIRD) - break; - return; - case 2: /* snake */ - if (HERE(SNAKE) && !HERE(BIRD)) - break; - game.hintlc[hint] = 0; - return; - case 3: /* maze */ - if (game.atloc[game.loc] == NO_OBJECT && - game.atloc[game.oldloc] == NO_OBJECT && - game.atloc[game.oldlc2] == NO_OBJECT && - game.holdng > 1) - break; - game.hintlc[hint] = 0; - return; - case 4: /* dark */ - if (game.prop[EMERALD] != STATE_NOTFOUND && game.prop[PYRAMID] == STATE_NOTFOUND) - break; - game.hintlc[hint] = 0; - return; - case 5: /* witt */ - break; - case 6: /* urn */ - if (game.dflag == 0) - break; - game.hintlc[hint] = 0; - return; - case 7: /* woods */ - if (game.atloc[game.loc] == NO_OBJECT && - game.atloc[game.oldloc] == NO_OBJECT && - game.atloc[game.oldlc2] == NO_OBJECT) - break; - return; - case 8: /* ogre */ - i = atdwrf(game.loc); - if (i < 0) { - game.hintlc[hint] = 0; - return; - } - if (HERE(OGRE) && i == 0) - break; - return; - case 9: /* jade */ - if (game.tally == 1 && game.prop[JADE] < 0) - break; - game.hintlc[hint] = 0; - return; - default: // LCOV_EXCL_LINE - // Should never happen - BUG(HINT_NUMBER_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE - } + switch (hint) { + case 0: + /* cave */ + if (game.objects[GRATE].prop == + GRATE_CLOSED && + !HERE(KEYS)) { + break; + } + game.hints[hint].lc = 0; + return; + case 1: /* bird */ + if (game.objects[BIRD].place == + game.loc && + TOTING(ROD) && + game.oldobj == BIRD) { + break; + } + return; + case 2: /* snake */ + if (HERE(SNAKE) && !HERE(BIRD)) { + break; + } + game.hints[hint].lc = 0; + return; + case 3: /* maze */ + if (game.locs[game.loc].atloc == + NO_OBJECT && + game.locs[game.oldloc].atloc == + NO_OBJECT && + game.locs[game.oldlc2].atloc == + NO_OBJECT && + game.holdng > 1) { + break; + } + game.hints[hint].lc = 0; + return; + case 4: /* dark */ + if (!OBJECT_IS_NOTFOUND(EMERALD) && + OBJECT_IS_NOTFOUND(PYRAMID)) { + break; + } + game.hints[hint].lc = 0; + return; + case 5: /* witt */ + break; + case 6: /* urn */ + if (game.dflag == 0) { + break; + } + game.hints[hint].lc = 0; + return; + case 7: /* woods */ + if (game.locs[game.loc].atloc == + NO_OBJECT && + game.locs[game.oldloc].atloc == + NO_OBJECT && + game.locs[game.oldlc2].atloc == + NO_OBJECT) { + break; + } + return; + case 8: /* ogre */ + i = atdwrf(game.loc); + if (i < 0) { + game.hints[hint].lc = 0; + return; + } + if (HERE(OGRE) && i == 0) { + break; + } + return; + case 9: /* jade */ + if (game.tally == 1 && + (OBJECT_IS_STASHED(JADE) || + OBJECT_IS_NOTFOUND(JADE))) { + break; + } + game.hints[hint].lc = 0; + return; + default: // LCOV_EXCL_LINE + // Should never happen + BUG(HINT_NUMBER_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE + } - /* Fall through to hint display */ - game.hintlc[hint] = 0; - if (!yes_or_no(hints[hint].question, arbitrary_messages[NO_MESSAGE], arbitrary_messages[OK_MAN])) - return; - rspeak(HINT_COST, hints[hint].penalty, hints[hint].penalty); - game.hinted[hint] = yes_or_no(arbitrary_messages[WANT_HINT], hints[hint].hint, arbitrary_messages[OK_MAN]); - if (game.hinted[hint] && game.limit > WARNTIME) - game.limit += WARNTIME * hints[hint].penalty; - } - } - } + /* Fall through to hint display */ + game.hints[hint].lc = 0; + if (!yes_or_no(hints[hint].question, + arbitrary_messages[NO_MESSAGE], + arbitrary_messages[OK_MAN])) { + return; + } + rspeak(HINT_COST, hints[hint].penalty, + hints[hint].penalty); + game.hints[hint].used = + yes_or_no(arbitrary_messages[WANT_HINT], + hints[hint].hint, + arbitrary_messages[OK_MAN]); + if (game.hints[hint].used && + game.limit > WARNTIME) { + game.limit += + WARNTIME * hints[hint].penalty; + } + } + } + } } -static bool spotted_by_pirate(int i) -{ - if (i != PIRATE) - return false; +static bool spotted_by_pirate(int i) { + if (i != PIRATE) { + return false; + } - /* The pirate's spotted him. Pirate leaves him alone once we've - * found chest. K counts if a treasure is here. If not, and - * tally=1 for an unseen chest, let the pirate be spotted. Note - * that game.place[CHEST] = LOC_NOWHERE might mean that he's thrown - * it to the troll, but in that case he's seen the chest - * (game.prop[CHEST] == STATE_FOUND). */ - if (game.loc == game.chloc || - game.prop[CHEST] != STATE_NOTFOUND) - return true; - int snarfed = 0; - bool movechest = false, robplayer = false; - for (int treasure = 1; treasure <= NOBJECTS; treasure++) { - if (!objects[treasure].is_treasure) - continue; - /* Pirate won't take pyramid from plover room or dark - * room (too easy!). */ - if (treasure == PYRAMID && (game.loc == objects[PYRAMID].plac || - game.loc == objects[EMERALD].plac)) { - continue; - } - if (TOTING(treasure) || - HERE(treasure)) - ++snarfed; - if (TOTING(treasure)) { - movechest = true; - robplayer = true; - } - } - /* Force chest placement before player finds last treasure */ - if (game.tally == 1 && snarfed == 0 && game.place[CHEST] == LOC_NOWHERE && HERE(LAMP) && game.prop[LAMP] == LAMP_BRIGHT) { - rspeak(PIRATE_SPOTTED); - movechest = true; - } - /* Do things in this order (chest move before robbery) so chest is listed - * last at the maze location. */ - if (movechest) { - move(CHEST, game.chloc); - move(MESSAG, game.chloc2); - game.dloc[PIRATE] = game.chloc; - game.odloc[PIRATE] = game.chloc; - game.dseen[PIRATE] = false; - } else { - /* You might get a hint of the pirate's presence even if the - * chest doesn't move... */ - if (game.odloc[PIRATE] != game.dloc[PIRATE] && PCT(20)) - rspeak(PIRATE_RUSTLES); - } - if (robplayer) { - rspeak(PIRATE_POUNCES); - for (int treasure = 1; treasure <= NOBJECTS; treasure++) { - if (!objects[treasure].is_treasure) - continue; - if (!(treasure == PYRAMID && (game.loc == objects[PYRAMID].plac || - game.loc == objects[EMERALD].plac))) { - if (AT(treasure) && game.fixed[treasure] == IS_FREE) - carry(treasure, game.loc); - if (TOTING(treasure)) - drop(treasure, game.chloc); - } - } - } + /* The pirate's spotted him. Pirate leaves him alone once we've + * found chest. K counts if a treasure is here. If not, and + * tally=1 for an unseen chest, let the pirate be spotted. Note + * that game.objexts,place[CHEST] = LOC_NOWHERE might mean that he's + * thrown it to the troll, but in that case he's seen the chest + * OBJECT_IS_FOUND(CHEST) == true. */ + if (game.loc == game.chloc || !OBJECT_IS_NOTFOUND(CHEST)) { + return true; + } + int snarfed = 0; + bool movechest = false, robplayer = false; + for (int treasure = 1; treasure <= NOBJECTS; treasure++) { + if (!objects[treasure].is_treasure) { + continue; + } + /* Pirate won't take pyramid from plover room or dark + * room (too easy!). */ + if (treasure == PYRAMID && + (game.loc == objects[PYRAMID].plac || + game.loc == objects[EMERALD].plac)) { + continue; + } + if (TOTING(treasure) || HERE(treasure)) { + ++snarfed; + } + if (TOTING(treasure)) { + movechest = true; + robplayer = true; + } + } + /* Force chest placement before player finds last treasure */ + if (game.tally == 1 && snarfed == 0 && + game.objects[CHEST].place == LOC_NOWHERE && HERE(LAMP) && + game.objects[LAMP].prop == LAMP_BRIGHT) { + rspeak(PIRATE_SPOTTED); + movechest = true; + } + /* Do things in this order (chest move before robbery) so chest is + * listed last at the maze location. */ + if (movechest) { + move(CHEST, game.chloc); + move(MESSAG, game.chloc2); + game.dwarves[PIRATE].loc = game.chloc; + game.dwarves[PIRATE].oldloc = game.chloc; + game.dwarves[PIRATE].seen = false; + } else { + /* You might get a hint of the pirate's presence even if the + * chest doesn't move... */ + if (game.dwarves[PIRATE].oldloc != game.dwarves[PIRATE].loc && + PCT(20)) { + rspeak(PIRATE_RUSTLES); + } + } + if (robplayer) { + rspeak(PIRATE_POUNCES); + for (int treasure = 1; treasure <= NOBJECTS; treasure++) { + if (!objects[treasure].is_treasure) { + continue; + } + if (!(treasure == PYRAMID && + (game.loc == objects[PYRAMID].plac || + game.loc == objects[EMERALD].plac))) { + if (AT(treasure) && + game.objects[treasure].fixed == IS_FREE) { + carry(treasure, game.loc); + } + if (TOTING(treasure)) { + drop(treasure, game.chloc); + } + } + } + } - return true; + return true; } -static bool dwarfmove(void) -/* Dwarves move. Return true if player survives, false if he dies. */ -{ - int kk, stick, attack; - loc_t tk[21]; +static bool dwarfmove(void) { + /* Dwarves move. Return true if player survives, false if he dies. */ + int kk, stick, attack; + loc_t tk[21]; - /* Dwarf stuff. See earlier comments for description of - * variables. Remember sixth dwarf is pirate and is thus - * very different except for motion rules. */ + /* Dwarf stuff. See earlier comments for description of + * variables. Remember sixth dwarf is pirate and is thus + * very different except for motion rules. */ - /* First off, don't let the dwarves follow him into a pit or a - * wall. Activate the whole mess the first time he gets as far - * as the Hall of Mists (what INDEEP() tests). If game.newloc - * is forbidden to pirate (in particular, if it's beyond the - * troll bridge), bypass dwarf stuff. That way pirate can't - * steal return toll, and dwarves can't meet the bear. Also - * means dwarves won't follow him into dead end in maze, but - * c'est la vie. They'll wait for him outside the dead end. */ - if (game.loc == LOC_NOWHERE || - FORCED(game.loc) || - CNDBIT(game.newloc, COND_NOARRR)) - return true; + /* First off, don't let the dwarves follow him into a pit or a + * wall. Activate the whole mess the first time he gets as far + * as the Hall of Mists (what INDEEP() tests). If game.newloc + * is forbidden to pirate (in particular, if it's beyond the + * troll bridge), bypass dwarf stuff. That way pirate can't + * steal return toll, and dwarves can't meet the bear. Also + * means dwarves won't follow him into dead end in maze, but + * c'est la vie. They'll wait for him outside the dead end. */ + if (game.loc == LOC_NOWHERE || FORCED(game.loc) || + CNDBIT(game.newloc, COND_NOARRR)) { + return true; + } - /* Dwarf activity level ratchets up */ - if (game.dflag == 0) { - if (INDEEP(game.loc)) - game.dflag = 1; - return true; - } + /* Dwarf activity level ratchets up */ + if (game.dflag == 0) { + if (INDEEP(game.loc)) { + game.dflag = 1; + } + return true; + } - /* When we encounter the first dwarf, we kill 0, 1, or 2 of - * the 5 dwarves. If any of the survivors is at game.loc, - * replace him with the alternate. */ - if (game.dflag == 1) { - if (!INDEEP(game.loc) || - (PCT(95) && (!CNDBIT(game.loc, COND_NOBACK) || - PCT(85)))) - return true; - game.dflag = 2; - for (int i = 1; i <= 2; i++) { - int j = 1 + randrange(NDWARVES - 1); - if (PCT(50)) - game.dloc[j] = 0; - } + /* When we encounter the first dwarf, we kill 0, 1, or 2 of + * the 5 dwarves. If any of the survivors is at game.loc, + * replace him with the alternate. */ + if (game.dflag == 1) { + if (!INDEEP(game.loc) || + (PCT(95) && (!CNDBIT(game.loc, COND_NOBACK) || PCT(85)))) { + return true; + } + game.dflag = 2; + for (int i = 1; i <= 2; i++) { + int j = 1 + randrange(NDWARVES - 1); + if (PCT(50)) { + game.dwarves[j].loc = 0; + } + } - /* Alternate initial loc for dwarf, in case one of them - * starts out on top of the adventurer. */ - for (int i = 1; i <= NDWARVES - 1; i++) { - if (game.dloc[i] == game.loc) - game.dloc[i] = DALTLC; // - game.odloc[i] = game.dloc[i]; - } - rspeak(DWARF_RAN); - drop(AXE, game.loc); - return true; - } + /* Alternate initial loc for dwarf, in case one of them + * starts out on top of the adventurer. */ + for (int i = 1; i <= NDWARVES - 1; i++) { + if (game.dwarves[i].loc == game.loc) { + game.dwarves[i].loc = DALTLC; + } + game.dwarves[i].oldloc = game.dwarves[i].loc; + } + rspeak(DWARF_RAN); + drop(AXE, game.loc); + return true; + } - /* Things are in full swing. Move each dwarf at random, - * except if he's seen us he sticks with us. Dwarves stay - * deep inside. If wandering at random, they don't back up - * unless there's no alternative. If they don't have to - * move, they attack. And, of course, dead dwarves don't do - * much of anything. */ - game.dtotal = 0; - attack = 0; - stick = 0; - for (int i = 1; i <= NDWARVES; i++) { - if (game.dloc[i] == 0) - continue; - /* Fill tk array with all the places this dwarf might go. */ - unsigned int j = 1; - kk = tkey[game.dloc[i]]; - if (kk != 0) - do { - enum desttype_t desttype = travel[kk].desttype; - game.newloc = travel[kk].destval; - /* Have we avoided a dwarf encounter? */ - if (desttype != dest_goto) - continue; - else if (!INDEEP(game.newloc)) - continue; - else if (game.newloc == game.odloc[i]) - continue; - else if (j > 1 && game.newloc == tk[j - 1]) - continue; - else if (j >= DIM(tk) - 1) - /* This can't actually happen. */ - continue; // LCOV_EXCL_LINE - else if (game.newloc == game.dloc[i]) - continue; - else if (FORCED(game.newloc)) - continue; - else if (i == PIRATE && CNDBIT(game.newloc, COND_NOARRR)) - continue; - else if (travel[kk].nodwarves) - continue; - tk[j++] = game.newloc; - } while - (!travel[kk++].stop); - tk[j] = game.odloc[i]; - if (j >= 2) - --j; - j = 1 + randrange(j); - game.odloc[i] = game.dloc[i]; - game.dloc[i] = tk[j]; - game.dseen[i] = (game.dseen[i] && INDEEP(game.loc)) || - (game.dloc[i] == game.loc || - game.odloc[i] == game.loc); - if (!game.dseen[i]) - continue; - game.dloc[i] = game.loc; - if (spotted_by_pirate(i)) - continue; - /* This threatening little dwarf is in the room with him! */ - ++game.dtotal; - if (game.odloc[i] == game.dloc[i]) { - ++attack; - if (game.knfloc >= 0) - game.knfloc = game.loc; - if (randrange(1000) < 95 * (game.dflag - 2)) - ++stick; - } - } + /* Things are in full swing. Move each dwarf at random, + * except if he's seen us he sticks with us. Dwarves stay + * deep inside. If wandering at random, they don't back up + * unless there's no alternative. If they don't have to + * move, they attack. And, of course, dead dwarves don't do + * much of anything. */ + game.dtotal = 0; + attack = 0; + stick = 0; + for (int i = 1; i <= NDWARVES; i++) { + if (game.dwarves[i].loc == 0) { + continue; + } + /* Fill tk array with all the places this dwarf might go. */ + unsigned int j = 1; + kk = tkey[game.dwarves[i].loc]; + if (kk != 0) { + do { + enum desttype_t desttype = travel[kk].desttype; + game.newloc = travel[kk].destval; + /* Have we avoided a dwarf encounter? */ + if (desttype != dest_goto) { + continue; + } else if (!INDEEP(game.newloc)) { + continue; + } else if (game.newloc == + game.dwarves[i].oldloc) { + continue; + } else if (j > 1 && game.newloc == tk[j - 1]) { + continue; + } else if (j >= DIM(tk) - 1) { + /* This can't actually happen. */ + continue; // LCOV_EXCL_LINE + } else if (game.newloc == game.dwarves[i].loc) { + continue; + } else if (FORCED(game.newloc)) { + continue; + } else if (i == PIRATE && + CNDBIT(game.newloc, COND_NOARRR)) { + continue; + } else if (travel[kk].nodwarves) { + continue; + } + tk[j++] = game.newloc; + } while (!travel[kk++].stop); + } + tk[j] = game.dwarves[i].oldloc; + if (j >= 2) { + --j; + } + j = 1 + randrange(j); + game.dwarves[i].oldloc = game.dwarves[i].loc; + game.dwarves[i].loc = tk[j]; + game.dwarves[i].seen = + (game.dwarves[i].seen && INDEEP(game.loc)) || + (game.dwarves[i].loc == game.loc || + game.dwarves[i].oldloc == game.loc); + if (!game.dwarves[i].seen) { + continue; + } + game.dwarves[i].loc = game.loc; + if (spotted_by_pirate(i)) { + continue; + } + /* This threatening little dwarf is in the room with him! */ + ++game.dtotal; + if (game.dwarves[i].oldloc == game.dwarves[i].loc) { + ++attack; + if (game.knfloc >= LOC_NOWHERE) { + game.knfloc = game.loc; + } + if (randrange(1000) < 95 * (game.dflag - 2)) { + ++stick; + } + } + } - /* Now we know what's happening. Let's tell the poor sucker about it. */ - if (game.dtotal == 0) - return true; - rspeak(game.dtotal == 1 ? DWARF_SINGLE : DWARF_PACK, game.dtotal); - if (attack == 0) - return true; - if (game.dflag == 2) - game.dflag = 3; - if (attack > 1) { - rspeak(THROWN_KNIVES, attack); - rspeak(stick > 1 ? MULTIPLE_HITS : (stick == 1 ? ONE_HIT : NONE_HIT), stick); - } else { - rspeak(KNIFE_THROWN); - rspeak(stick ? GETS_YOU : MISSES_YOU); - } - if (stick == 0) - return true; - game.oldlc2 = game.loc; - return false; + /* Now we know what's happening. Let's tell the poor sucker about it. + */ + if (game.dtotal == 0) { + return true; + } + rspeak(game.dtotal == 1 ? DWARF_SINGLE : DWARF_PACK, game.dtotal); + if (attack == 0) { + return true; + } + if (game.dflag == 2) { + game.dflag = 3; + } + if (attack > 1) { + rspeak(THROWN_KNIVES, attack); + rspeak(stick > 1 ? MULTIPLE_HITS + : (stick == 1 ? ONE_HIT : NONE_HIT), + stick); + } else { + rspeak(KNIFE_THROWN); + rspeak(stick ? GETS_YOU : MISSES_YOU); + } + if (stick == 0) { + return true; + } + game.oldlc2 = game.loc; + return false; } /* "You're dead, Jim." @@ -506,73 +486,77 @@ static bool dwarfmove(void) * building (and heaven help him if he tries to xyzzy back into the * cave without the lamp!). game.oldloc is zapped so he can't just * "retreat". */ -static void croak(void) -/* Okay, he's dead. Let's get on with it. */ -{ - const char* query = obituaries[game.numdie].query; - const char* yes_response = obituaries[game.numdie].yes_response; +static void croak(void) { + /* Okay, he's dead. Let's get on with it. */ + const char *query = obituaries[game.numdie].query; + const char *yes_response = obituaries[game.numdie].yes_response; - ++game.numdie; + ++game.numdie; - if (game.closng) { - /* He died during closing time. No resurrection. Tally up a - * death and exit. */ - rspeak(DEATH_CLOSING); - terminate(endgame); - } else if (!yes_or_no(query, yes_response, arbitrary_messages[OK_MAN]) - || game.numdie == NDEATHS) { - /* Player is asked if he wants to try again. If not, or if - * he's already used all of his lives, we end the game */ - terminate(endgame); - } else { - /* If player wishes to continue, we empty the liquids in the - * user's inventory, turn off the lamp, and drop all items - * where he died. */ - game.place[WATER] = game.place[OIL] = LOC_NOWHERE; - if (TOTING(LAMP)) - game.prop[LAMP] = LAMP_DARK; - for (int j = 1; j <= NOBJECTS; j++) { - int i = NOBJECTS + 1 - j; - if (TOTING(i)) { - /* Always leave lamp where it's accessible aboveground */ - drop(i, (i == LAMP) ? LOC_START : game.oldlc2); - } - } - game.oldloc = game.loc = game.newloc = LOC_BUILDING; - } + if (game.closng) { + /* He died during closing time. No resurrection. Tally up a + * death and exit. */ + rspeak(DEATH_CLOSING); + terminate(endgame); + } else if (!yes_or_no(query, yes_response, + arbitrary_messages[OK_MAN]) || + game.numdie == NDEATHS) { + /* Player is asked if he wants to try again. If not, or if + * he's already used all of his lives, we end the game */ + terminate(endgame); + } else { + /* If player wishes to continue, we empty the liquids in the + * user's inventory, turn off the lamp, and drop all items + * where he died. */ + game.objects[WATER].place = game.objects[OIL].place = + LOC_NOWHERE; + if (TOTING(LAMP)) { + game.objects[LAMP].prop = LAMP_DARK; + } + for (int j = 1; j <= NOBJECTS; j++) { + int i = NOBJECTS + 1 - j; + if (TOTING(i)) { + /* Always leave lamp where it's accessible + * aboveground */ + drop(i, (i == LAMP) ? LOC_START : game.oldlc2); + } + } + game.oldloc = game.loc = game.newloc = LOC_BUILDING; + } } -static void describe_location(void) -/* Describe the location to the user */ -{ - const char* msg = locations[game.loc].description.small; +static void describe_location(void) { + /* Describe the location to the user */ + const char *msg = locations[game.loc].description.small; - if (MOD(game.abbrev[game.loc], game.abbnum) == 0 || - msg == NO_MESSAGE) - msg = locations[game.loc].description.big; + if (MOD(game.locs[game.loc].abbrev, game.abbnum) == 0 || + msg == NO_MESSAGE) { + msg = locations[game.loc].description.big; + } - if (!FORCED(game.loc) && DARK(game.loc)) { - msg = arbitrary_messages[PITCH_DARK]; - } + if (!FORCED(game.loc) && IS_DARK_HERE()) { + msg = arbitrary_messages[PITCH_DARK]; + } - if (TOTING(BEAR)) - rspeak(TAME_BEAR); + if (TOTING(BEAR)) { + rspeak(TAME_BEAR); + } - speak(msg); + speak(msg); - if (game.loc == LOC_Y2 && PCT(25) && !game.closng) - rspeak(SAYS_PLUGH); + if (game.loc == LOC_Y2 && PCT(25) && !game.closng) { + rspeak(SAYS_PLUGH); + } } - -static bool traveleq(int a, int b) -/* Are two travel entries equal for purposes of skip after failed condition? */ -{ - return (travel[a].condtype == travel[b].condtype) - && (travel[a].condarg1 == travel[b].condarg1) - && (travel[a].condarg2 == travel[b].condarg2) - && (travel[a].desttype == travel[b].desttype) - && (travel[a].destval == travel[b].destval); +static bool traveleq(int a, int b) { + /* Are two travel entries equal for purposes of skip after failed + * condition? */ + return (travel[a].condtype == travel[b].condtype) && + (travel[a].condarg1 == travel[b].condarg1) && + (travel[a].condarg2 == travel[b].condarg2) && + (travel[a].desttype == travel[b].desttype) && + (travel[a].destval == travel[b].destval); } /* Given the current location in "game.loc", and a motion verb number in @@ -582,743 +566,1023 @@ static bool traveleq(int a, int b) * does, game.newloc will be limbo, and game.oldloc will be what killed * him, so we need game.oldlc2, which is the last place he was * safe.) */ -static void playermove(int motion) -{ - int scratchloc, travel_entry = tkey[game.loc]; - game.newloc = game.loc; - if (travel_entry == 0) - BUG(LOCATION_HAS_NO_TRAVEL_ENTRIES); // LCOV_EXCL_LINE - if (motion == NUL) - return; - else if (motion == BACK) { - /* Handle "go back". Look for verb which goes from game.loc to - * game.oldloc, or to game.oldlc2 If game.oldloc has forced-motion. - * te_tmp saves entry -> forced loc -> previous loc. */ - motion = game.oldloc; - if (FORCED(motion)) - motion = game.oldlc2; - game.oldlc2 = game.oldloc; - game.oldloc = game.loc; - if (CNDBIT(game.loc, COND_NOBACK)) { - rspeak(TWIST_TURN); - return; - } - if (motion == game.loc) { - rspeak(FORGOT_PATH); - return; - } +static void playermove(int motion) { + int scratchloc, travel_entry = tkey[game.loc]; + game.newloc = game.loc; + if (travel_entry == 0) { + BUG(LOCATION_HAS_NO_TRAVEL_ENTRIES); // LCOV_EXCL_LINE + } + if (motion == NUL) { + return; + } else if (motion == BACK) { + /* Handle "go back". Look for verb which goes from game.loc to + * game.oldloc, or to game.oldlc2 If game.oldloc has + * forced-motion. te_tmp saves entry -> forced loc -> previous + * loc. */ + motion = game.oldloc; + if (FORCED(motion)) { + motion = game.oldlc2; + } + game.oldlc2 = game.oldloc; + game.oldloc = game.loc; + if (CNDBIT(game.loc, COND_NOBACK)) { + rspeak(TWIST_TURN); + return; + } + if (motion == game.loc) { + rspeak(FORGOT_PATH); + return; + } - int te_tmp = 0; - for (;;) { - enum desttype_t desttype = travel[travel_entry].desttype; - scratchloc = travel[travel_entry].destval; - if (desttype != dest_goto || scratchloc != motion) { - if (desttype == dest_goto) { - if (FORCED(scratchloc) && travel[tkey[scratchloc]].destval == motion) - te_tmp = travel_entry; - } - if (!travel[travel_entry].stop) { - ++travel_entry; /* go to next travel entry for this location */ - continue; - } - /* we've reached the end of travel entries for game.loc */ - travel_entry = te_tmp; - if (travel_entry == 0) { - rspeak(NOT_CONNECTED); - return; - } - } + int te_tmp = 0; + for (;;) { + enum desttype_t desttype = + travel[travel_entry].desttype; + scratchloc = travel[travel_entry].destval; + if (desttype != dest_goto || scratchloc != motion) { + if (desttype == dest_goto) { + if (FORCED(scratchloc) && + travel[tkey[scratchloc]].destval == + motion) { + te_tmp = travel_entry; + } + } + if (!travel[travel_entry].stop) { + ++travel_entry; /* go to next travel + entry for this + location */ + continue; + } + /* we've reached the end of travel entries for + * game.loc */ + travel_entry = te_tmp; + if (travel_entry == 0) { + rspeak(NOT_CONNECTED); + return; + } + } - motion = travel[travel_entry].motion; - travel_entry = tkey[game.loc]; - break; /* fall through to ordinary travel */ - } - } else if (motion == LOOK) { - /* Look. Can't give more detail. Pretend it wasn't dark - * (though it may now be dark) so he won't fall into a - * pit while staring into the gloom. */ - if (game.detail < 3) - rspeak(NO_MORE_DETAIL); - ++game.detail; - game.wzdark = false; - game.abbrev[game.loc] = 0; - return; - } else if (motion == CAVE) { - /* Cave. Different messages depending on whether above ground. */ - rspeak((OUTSID(game.loc) && game.loc != LOC_GRATE) ? FOLLOW_STREAM : NEED_DETAIL); - return; - } else { - /* none of the specials */ - game.oldlc2 = game.oldloc; - game.oldloc = game.loc; - } + motion = travel[travel_entry].motion; + travel_entry = tkey[game.loc]; + break; /* fall through to ordinary travel */ + } + } else if (motion == LOOK) { + /* Look. Can't give more detail. Pretend it wasn't dark + * (though it may now be dark) so he won't fall into a + * pit while staring into the gloom. */ + if (game.detail < 3) { + rspeak(NO_MORE_DETAIL); + } + ++game.detail; + game.wzdark = false; + game.locs[game.loc].abbrev = 0; + return; + } else if (motion == CAVE) { + /* Cave. Different messages depending on whether above ground. + */ + rspeak((OUTSIDE(game.loc) && game.loc != LOC_GRATE) + ? FOLLOW_STREAM + : NEED_DETAIL); + return; + } else { + /* none of the specials */ + game.oldlc2 = game.oldloc; + game.oldloc = game.loc; + } - /* Look for a way to fulfil the motion verb passed in - travel_entry indexes - * the beginning of the motion entries for here (game.loc). */ - for (;;) { - if ((travel[travel_entry].motion == HERE) || - travel[travel_entry].motion == motion) - break; - if (travel[travel_entry].stop) { - /* Couldn't find an entry matching the motion word passed - * in. Various messages depending on word given. */ - switch (motion) { - case EAST: - case WEST: - case SOUTH: - case NORTH: - case NE: - case NW: - case SW: - case SE: - case UP: - case DOWN: - rspeak(BAD_DIRECTION); - break; - case FORWARD: - case LEFT: - case RIGHT: - rspeak(UNSURE_FACING); - break; - case OUTSIDE: - case INSIDE: - rspeak(NO_INOUT_HERE); - break; - case XYZZY: - case PLUGH: - rspeak(NOTHING_HAPPENS); - break; - case CRAWL: - rspeak(WHICH_WAY); - break; - default: - rspeak(CANT_APPLY); - } - return; - } - ++travel_entry; - } + /* Look for a way to fulfil the motion verb passed in - travel_entry + * indexes the beginning of the motion entries for here (game.loc). */ + for (;;) { + if ((travel[travel_entry].motion == HERE) || + travel[travel_entry].motion == motion) { + break; + } + if (travel[travel_entry].stop) { + /* Couldn't find an entry matching the motion word + * passed in. Various messages depending on word given. + */ + switch (motion) { + case EAST: + case WEST: + case SOUTH: + case NORTH: + case NE: + case NW: + case SW: + case SE: + case UP: + case DOWN: + rspeak(BAD_DIRECTION); + break; + case FORWARD: + case LEFT: + case RIGHT: + rspeak(UNSURE_FACING); + break; + case OUTSIDE: + case INSIDE: + rspeak(NO_INOUT_HERE); + break; + case XYZZY: + case PLUGH: + rspeak(NOTHING_HAPPENS); + break; + case CRAWL: + rspeak(WHICH_WAY); + break; + default: + rspeak(CANT_APPLY); + } + return; + } + ++travel_entry; + } - /* (ESR) We've found a destination that goes with the motion verb. - * Next we need to check any conditional(s) on this destination, and - * possibly on following entries. */ - do { - for (;;) { /* L12 loop */ - for (;;) { - enum condtype_t condtype = travel[travel_entry].condtype; - int condarg1 = travel[travel_entry].condarg1; - int condarg2 = travel[travel_entry].condarg2; - if (condtype < cond_not) { - /* YAML N and [pct N] conditionals */ - if (condtype == cond_goto || condtype == cond_pct) { - if (condarg1 == 0 || - PCT(condarg1)) - break; - /* else fall through */ - } - /* YAML [with OBJ] clause */ - else if (TOTING(condarg1) || - (condtype == cond_with && AT(condarg1))) - break; - /* else fall through to check [not OBJ STATE] */ - } else if (game.prop[condarg1] != condarg2) - break; + /* (ESR) We've found a destination that goes with the motion verb. + * Next we need to check any conditional(s) on this destination, and + * possibly on following entries. */ + do { + for (;;) { /* L12 loop */ + for (;;) { + enum condtype_t condtype = + travel[travel_entry].condtype; + int condarg1 = travel[travel_entry].condarg1; + int condarg2 = travel[travel_entry].condarg2; + if (condtype < cond_not) { + /* YAML N and [pct N] conditionals */ + if (condtype == cond_goto || + condtype == cond_pct) { + if (condarg1 == 0 || + PCT(condarg1)) { + break; + } + /* else fall through */ + } + /* YAML [with OBJ] clause */ + else if (TOTING(condarg1) || + (condtype == cond_with && + AT(condarg1))) { + break; + } + /* else fall through to check [not OBJ + * STATE] */ + } else if (game.objects[condarg1].prop != + condarg2) { + break; + } - /* We arrive here on conditional failure. - * Skip to next non-matching destination */ - int te_tmp = travel_entry; - do { - if (travel[te_tmp].stop) - BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE - ++te_tmp; - } while - (traveleq(travel_entry, te_tmp)); - travel_entry = te_tmp; - } + /* We arrive here on conditional failure. + * Skip to next non-matching destination */ + int te_tmp = travel_entry; + do { + if (travel[te_tmp].stop) { + BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE + } + ++te_tmp; + } while (traveleq(travel_entry, te_tmp)); + travel_entry = te_tmp; + } - /* Found an eligible rule, now execute it */ - enum desttype_t desttype = travel[travel_entry].desttype; - game.newloc = travel[travel_entry].destval; - if (desttype == dest_goto) - return; + /* Found an eligible rule, now execute it */ + enum desttype_t desttype = + travel[travel_entry].desttype; + game.newloc = travel[travel_entry].destval; + if (desttype == dest_goto) { + return; + } - if (desttype == dest_speak) { - /* Execute a speak rule */ - rspeak(game.newloc); - game.newloc = game.loc; - return; - } else { - switch (game.newloc) { - case 1: - /* Special travel 1. Plover-alcove passage. Can carry only - * emerald. Note: travel table must include "useless" - * entries going through passage, which can never be used - * for actual motion, but can be spotted by "go back". */ - game.newloc = (game.loc == LOC_PLOVER) - ? LOC_ALCOVE - : LOC_PLOVER; - if (game.holdng > 1 || - (game.holdng == 1 && !TOTING(EMERALD))) { - game.newloc = game.loc; - rspeak(MUST_DROP); - } - return; - case 2: - /* Special travel 2. 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 dropped it, go back and - * pretend he wasn't carrying it after all. */ - drop(EMERALD, game.loc); - { - int te_tmp = travel_entry; - do { - if (travel[te_tmp].stop) - BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE - ++te_tmp; - } while - (traveleq(travel_entry, te_tmp)); - travel_entry = te_tmp; - } - continue; /* goto L12 */ - case 3: - /* Special travel 3. Troll bridge. Must be done - * only as special motion so that dwarves won't - * wander across and encounter the bear. (They - * won't follow the player there because that - * region is forbidden to the pirate.) If - * game.prop[TROLL]=TROLL_PAIDONCE, he's crossed - * since paying, so step out and block him. - * (standard travel entries check for - * game.prop[TROLL]=TROLL_UNPAID.) Special stuff - * for bear. */ - if (game.prop[TROLL] == TROLL_PAIDONCE) { - pspeak(TROLL, look, true, TROLL_PAIDONCE); - game.prop[TROLL] = TROLL_UNPAID; - DESTROY(TROLL2); - move(TROLL2 + NOBJECTS, IS_FREE); - move(TROLL, objects[TROLL].plac); - move(TROLL + NOBJECTS, objects[TROLL].fixd); - juggle(CHASM); - game.newloc = game.loc; - return; - } else { - game.newloc = objects[TROLL].plac + objects[TROLL].fixd - game.loc; - if (game.prop[TROLL] == TROLL_UNPAID) - game.prop[TROLL] = TROLL_PAIDONCE; - if (!TOTING(BEAR)) - return; - state_change(CHASM, BRIDGE_WRECKED); - game.prop[TROLL] = TROLL_GONE; - drop(BEAR, game.newloc); - game.fixed[BEAR] = IS_FIXED; - game.prop[BEAR] = BEAR_DEAD; - game.oldlc2 = game.newloc; - croak(); - return; - } - default: // LCOV_EXCL_LINE - BUG(SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE - } - } - break; /* Leave L12 loop */ - } - } while - (false); + if (desttype == dest_speak) { + /* Execute a speak rule */ + rspeak(game.newloc); + game.newloc = game.loc; + return; + } else { + switch (game.newloc) { + case 1: + /* Special travel 1. Plover-alcove + * passage. Can carry only emerald. + * Note: travel table must include + * "useless" entries going through + * passage, which can never be used for + * actual motion, but can be spotted by + * "go back". */ + game.newloc = (game.loc == LOC_PLOVER) + ? LOC_ALCOVE + : LOC_PLOVER; + if (game.holdng > 1 || + (game.holdng == 1 && + !TOTING(EMERALD))) { + game.newloc = game.loc; + rspeak(MUST_DROP); + } + return; + case 2: + /* Special travel 2. 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 dropped it, go back and + * pretend he wasn't carrying it after + * all. */ + drop(EMERALD, game.loc); + { + int te_tmp = travel_entry; + do { + if (travel[te_tmp] + .stop) { + BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE + } + ++te_tmp; + } while (traveleq(travel_entry, + te_tmp)); + travel_entry = te_tmp; + } + continue; /* goto L12 */ + case 3: + /* Special travel 3. Troll bridge. Must + * be done only as special motion so + * that dwarves won't wander across and + * encounter the bear. (They won't + * follow the player there because that + * region is forbidden to the pirate.) + * If game.prop[TROLL]=TROLL_PAIDONCE, + * he's crossed since paying, so step + * out and block him. (standard travel + * entries check for + * game.prop[TROLL]=TROLL_UNPAID.) + * Special stuff for bear. */ + if (game.objects[TROLL].prop == + TROLL_PAIDONCE) { + pspeak(TROLL, look, true, + TROLL_PAIDONCE); + game.objects[TROLL].prop = + TROLL_UNPAID; + DESTROY(TROLL2); + move(TROLL2 + NOBJECTS, + IS_FREE); + move(TROLL, + objects[TROLL].plac); + move(TROLL + NOBJECTS, + objects[TROLL].fixd); + juggle(CHASM); + game.newloc = game.loc; + return; + } else { + game.newloc = + objects[TROLL].plac + + objects[TROLL].fixd - + game.loc; + if (game.objects[TROLL].prop == + TROLL_UNPAID) { + game.objects[TROLL] + .prop = + TROLL_PAIDONCE; + } + if (!TOTING(BEAR)) { + return; + } + state_change(CHASM, + BRIDGE_WRECKED); + game.objects[TROLL].prop = + TROLL_GONE; + drop(BEAR, game.newloc); + game.objects[BEAR].fixed = + IS_FIXED; + game.objects[BEAR].prop = + BEAR_DEAD; + game.oldlc2 = game.newloc; + croak(); + return; + } + default: // LCOV_EXCL_LINE + BUG(SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE + } + } + break; /* Leave L12 loop */ + } + } while (false); } -static void lampcheck(void) -/* Check game limit and lamp timers */ -{ - if (game.prop[LAMP] == LAMP_BRIGHT) - --game.limit; +static void lampcheck(void) { + /* Check game limit and lamp timers */ + if (game.objects[LAMP].prop == LAMP_BRIGHT) { + --game.limit; + } - /* 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. - * First following arm checks if the lamp and fresh batteries are - * here, in which case we replace the batteries and continue. - * Second is for other cases of lamp dying. Even after it goes - * out, he can explore outside for a while if desired. */ - if (game.limit <= WARNTIME) { - if (HERE(BATTERY) && game.prop[BATTERY] == FRESH_BATTERIES && HERE(LAMP)) { - rspeak(REPLACE_BATTERIES); - game.prop[BATTERY] = DEAD_BATTERIES; + /* 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. + * First following arm checks if the lamp and fresh batteries are + * here, in which case we replace the batteries and continue. + * Second is for other cases of lamp dying. Even after it goes + * out, he can explore outside for a while if desired. */ + if (game.limit <= WARNTIME) { + if (HERE(BATTERY) && + game.objects[BATTERY].prop == FRESH_BATTERIES && + HERE(LAMP)) { + rspeak(REPLACE_BATTERIES); + game.objects[BATTERY].prop = DEAD_BATTERIES; #ifdef __unused__ - /* This code from the original game seems to have been faulty. - * No tests ever passed the guard, and with the guard removed - * the game hangs when the lamp limit is reached. - */ - if (TOTING(BATTERY)) - drop(BATTERY, game.loc); + /* This code from the original game seems to have been + * faulty. No tests ever passed the guard, and with the + * guard removed the game hangs when the lamp limit is + * reached. + */ + if (TOTING(BATTERY)) { + drop(BATTERY, game.loc); + } #endif - game.limit += BATTERYLIFE; - game.lmwarn = false; - } else if (!game.lmwarn && HERE(LAMP)) { - game.lmwarn = true; - if (game.prop[BATTERY] == DEAD_BATTERIES) - rspeak(MISSING_BATTERIES); - else if (game.place[BATTERY] == LOC_NOWHERE) - rspeak(LAMP_DIM); - else - rspeak(GET_BATTERIES); - } - } - if (game.limit == 0) { - game.limit = -1; - game.prop[LAMP] = LAMP_DARK; - if (HERE(LAMP)) - rspeak(LAMP_OUT); - } + game.limit += BATTERYLIFE; + game.lmwarn = false; + } else if (!game.lmwarn && HERE(LAMP)) { + game.lmwarn = true; + if (game.objects[BATTERY].prop == DEAD_BATTERIES) { + rspeak(MISSING_BATTERIES); + } else if (game.objects[BATTERY].place == LOC_NOWHERE) { + rspeak(LAMP_DIM); + } else { + rspeak(GET_BATTERIES); + } + } + } + if (game.limit == 0) { + game.limit = -1; + game.objects[LAMP].prop = LAMP_DARK; + if (HERE(LAMP)) { + rspeak(LAMP_OUT); + } + } } -static bool closecheck(void) /* Handle the closing of the cave. The cave closes "clock1" turns * after the last treasure has been located (including the pirate's * chest, which may of course never show up). Note that the * treasures need not have been taken yet, just located. Hence * clock1 must be large enough to get out of the cave (it only ticks - * while inside the cave). When it hits zero, we branch to 10000 to - * start closing the cave, and then sit back and wait for him to try - * to get out. If he doesn't within clock2 turns, we close the cave; - * if he does try, we assume he panics, and give him a few additional - * turns to get frantic before we close. When clock2 hits zero, we - * transport him into the final puzzle. Note that the puzzle depends - * upon all sorts of random things. For instance, there must be no - * water or oil, since there are beanstalks which we don't want to be - * able to water, since the code can't handle it. Also, we can have - * no keys, since there is a grate (having moved the fixed object!) - * there separating him from all the treasures. Most of these - * problems arise from the use of negative prop numbers to suppress - * the object descriptions until he's actually moved the objects. */ -{ - /* If a turn threshold has been met, apply penalties and tell - * the player about it. */ - for (int i = 0; i < NTHRESHOLDS; ++i) { - if (game.turns == turn_thresholds[i].threshold + 1) { - game.trnluz += turn_thresholds[i].point_loss; - speak(turn_thresholds[i].message); - } - } + * while inside the cave). When it hits zero, we start closing the + * cave, and then sit back and wait for him to try to get out. If he + * doesn't within clock2 turns, we close the cave; if he does try, we + * assume he panics, and give him a few additional turns to get + * frantic before we close. When clock2 hits zero, we transport him + * into the final puzzle. Note that the puzzle depends upon all + * sorts of random things. For instance, there must be no water or + * oil, since there are beanstalks which we don't want to be able to + * water, since the code can't handle it. Also, we can have no keys, + * since there is a grate (having moved the fixed object!) there + * separating him from all the treasures. Most of these problems + * arise from the use of negative prop numbers to suppress the object + * descriptions until he's actually moved the objects. */ +static bool closecheck(void) { + /* If a turn threshold has been met, apply penalties and tell + * the player about it. */ + for (int i = 0; i < NTHRESHOLDS; ++i) { + if (game.turns == turn_thresholds[i].threshold + 1) { + game.trnluz += turn_thresholds[i].point_loss; + speak(turn_thresholds[i].message); + } + } - /* Don't tick game.clock1 unless well into cave (and not at Y2). */ - if (game.tally == 0 && INDEEP(game.loc) && game.loc != LOC_Y2) - --game.clock1; + /* Don't tick game.clock1 unless well into cave (and not at Y2). */ + if (game.tally == 0 && INDEEP(game.loc) && game.loc != LOC_Y2) { + --game.clock1; + } - /* When the first warning comes, we lock the grate, destroy - * the bridge, kill all the dwarves (and the pirate), remove - * the troll and bear (unless dead), and set "closng" to - * true. Leave the dragon; too much trouble to move it. - * from now until clock2 runs out, he cannot unlock the - * grate, move to any location outside the cave, or create - * the bridge. Nor can he be resurrected if he dies. Note - * that the snake is already gone, since he got to the - * treasure accessible only via the hall of the mountain - * king. Also, he's been in giant room (to get eggs), so we - * can refer to it. Also also, he's gotten the pearl, so we - * know the bivalve is an oyster. *And*, the dwarves must - * have been activated, since we've found chest. */ - if (game.clock1 == 0) { - game.prop[GRATE] = GRATE_CLOSED; - game.prop[FISSURE] = UNBRIDGED; - for (int i = 1; i <= NDWARVES; i++) { - game.dseen[i] = false; - game.dloc[i] = LOC_NOWHERE; - } - DESTROY(TROLL); - move(TROLL + NOBJECTS, IS_FREE); - move(TROLL2, objects[TROLL].plac); - move(TROLL2 + NOBJECTS, objects[TROLL].fixd); - juggle(CHASM); - if (game.prop[BEAR] != BEAR_DEAD) - DESTROY(BEAR); - game.prop[CHAIN] = CHAIN_HEAP; - game.fixed[CHAIN] = IS_FREE; - game.prop[AXE] = AXE_HERE; - game.fixed[AXE] = IS_FREE; - rspeak(CAVE_CLOSING); - game.clock1 = -1; - game.closng = true; - return game.closed; - } else if (game.clock1 < 0) - --game.clock2; - if (game.clock2 == 0) { - /* Once he's panicked, and clock2 has run out, we come here - * to set up the storage room. The room has two locs, - * hardwired as LOC_NE and LOC_SW. At the ne end, we - * place empty bottles, a nursery of plants, a bed of - * oysters, a pile of lamps, rods with stars, sleeping - * dwarves, and him. At the sw end we place grate over - * treasures, snake pit, covey of caged birds, more rods, and - * pillows. A mirror stretches across one wall. Many of the - * objects come from known locations and/or states (e.g. the - * snake is known to have been destroyed and needn't be - * carried away from its old "place"), making the various - * objects be handled differently. We also drop all other - * objects he might be carrying (lest he have some which - * could cause trouble, such as the keys). We describe the - * flash of light and trundle back. */ - game.prop[BOTTLE] = put(BOTTLE, LOC_NE, EMPTY_BOTTLE); - game.prop[PLANT] = put(PLANT, LOC_NE, PLANT_THIRSTY); - game.prop[OYSTER] = put(OYSTER, LOC_NE, STATE_FOUND); - game.prop[LAMP] = put(LAMP, LOC_NE, LAMP_DARK); - game.prop[ROD] = put(ROD, LOC_NE, STATE_FOUND); - game.prop[DWARF] = put(DWARF, LOC_NE, 0); - game.loc = LOC_NE; - game.oldloc = LOC_NE; - game.newloc = LOC_NE; - /* Leave the grate with normal (non-negative) property. - * Reuse sign. */ - put(GRATE, LOC_SW, 0); - put(SIGN, LOC_SW, 0); - game.prop[SIGN] = ENDGAME_SIGN; - game.prop[SNAKE] = put(SNAKE, LOC_SW, SNAKE_CHASED); - game.prop[BIRD] = put(BIRD, LOC_SW, BIRD_CAGED); - game.prop[CAGE] = put(CAGE, LOC_SW, STATE_FOUND); - game.prop[ROD2] = put(ROD2, LOC_SW, STATE_FOUND); - game.prop[PILLOW] = put(PILLOW, LOC_SW, STATE_FOUND); + /* When the first warning comes, we lock the grate, destroy + * the bridge, kill all the dwarves (and the pirate), remove + * the troll and bear (unless dead), and set "closng" to + * true. Leave the dragon; too much trouble to move it. + * from now until clock2 runs out, he cannot unlock the + * grate, move to any location outside the cave, or create + * the bridge. Nor can he be resurrected if he dies. Note + * that the snake is already gone, since he got to the + * treasure accessible only via the hall of the mountain + * king. Also, he's been in giant room (to get eggs), so we + * can refer to it. Also also, he's gotten the pearl, so we + * know the bivalve is an oyster. *And*, the dwarves must + * have been activated, since we've found chest. */ + if (game.clock1 == 0) { + game.objects[GRATE].prop = GRATE_CLOSED; + game.objects[FISSURE].prop = UNBRIDGED; + for (int i = 1; i <= NDWARVES; i++) { + game.dwarves[i].seen = false; + game.dwarves[i].loc = LOC_NOWHERE; + } + DESTROY(TROLL); + move(TROLL + NOBJECTS, IS_FREE); + move(TROLL2, objects[TROLL].plac); + move(TROLL2 + NOBJECTS, objects[TROLL].fixd); + juggle(CHASM); + if (game.objects[BEAR].prop != BEAR_DEAD) { + DESTROY(BEAR); + } + game.objects[CHAIN].prop = CHAIN_HEAP; + game.objects[CHAIN].fixed = IS_FREE; + game.objects[AXE].prop = AXE_HERE; + game.objects[AXE].fixed = IS_FREE; + rspeak(CAVE_CLOSING); + game.clock1 = -1; + game.closng = true; + return game.closed; + } else if (game.clock1 < 0) { + --game.clock2; + } + if (game.clock2 == 0) { + /* Once he's panicked, and clock2 has run out, we come here + * to set up the storage room. The room has two locs, + * hardwired as LOC_NE and LOC_SW. At the ne end, we + * place empty bottles, a nursery of plants, a bed of + * oysters, a pile of lamps, rods with stars, sleeping + * dwarves, and him. At the sw end we place grate over + * treasures, snake pit, covey of caged birds, more rods, and + * pillows. A mirror stretches across one wall. Many of the + * objects come from known locations and/or states (e.g. the + * snake is known to have been destroyed and needn't be + * carried away from its old "place"), making the various + * objects be handled differently. We also drop all other + * objects he might be carrying (lest he has some which + * could cause trouble, such as the keys). We describe the + * flash of light and trundle back. */ + put(BOTTLE, LOC_NE, EMPTY_BOTTLE); + put(PLANT, LOC_NE, PLANT_THIRSTY); + put(OYSTER, LOC_NE, STATE_FOUND); + put(LAMP, LOC_NE, LAMP_DARK); + put(ROD, LOC_NE, STATE_FOUND); + put(DWARF, LOC_NE, STATE_FOUND); + game.loc = LOC_NE; + game.oldloc = LOC_NE; + game.newloc = LOC_NE; + /* Leave the grate with normal (non-negative) property. + * Reuse sign. */ + move(GRATE, LOC_SW); + move(SIGN, LOC_SW); + game.objects[SIGN].prop = ENDGAME_SIGN; + put(SNAKE, LOC_SW, SNAKE_CHASED); + put(BIRD, LOC_SW, BIRD_CAGED); + put(CAGE, LOC_SW, STATE_FOUND); + put(ROD2, LOC_SW, STATE_FOUND); + put(PILLOW, LOC_SW, STATE_FOUND); - game.prop[MIRROR] = put(MIRROR, LOC_NE, STATE_FOUND); - game.fixed[MIRROR] = LOC_SW; + put(MIRROR, LOC_NE, STATE_FOUND); + game.objects[MIRROR].fixed = LOC_SW; - for (int i = 1; i <= NOBJECTS; i++) { - if (TOTING(i)) - DESTROY(i); - } + for (int i = 1; i <= NOBJECTS; i++) { + if (TOTING(i)) { + DESTROY(i); + } + } - rspeak(CAVE_CLOSED); - game.closed = true; - return game.closed; - } + rspeak(CAVE_CLOSED); + game.closed = true; + return game.closed; + } - lampcheck(); - return false; + lampcheck(); + return false; } -static void listobjects(void) -/* Print out descriptions of objects at this location. If - * not closing and property value is negative, tally off - * another treasure. Rug is special case; once seen, its - * game.prop is RUG_DRAGON (dragon on it) till dragon is killed. - * Similarly for chain; game.prop is initially CHAINING_BEAR (locked to - * bear). These hacks are because game.prop=0 is needed to - * get full score. */ -{ - if (!DARK(game.loc)) { - ++game.abbrev[game.loc]; - for (int i = game.atloc[game.loc]; i != 0; i = game.link[i]) { - obj_t obj = i; - if (obj > NOBJECTS) - obj = obj - NOBJECTS; - if (obj == STEPS && TOTING(NUGGET)) - continue; - if (game.prop[obj] < 0) { - if (game.closed) - continue; - game.prop[obj] = STATE_FOUND; - if (obj == RUG) - game.prop[RUG] = RUG_DRAGON; - if (obj == CHAIN) - game.prop[CHAIN] = CHAINING_BEAR; - --game.tally; - /* Note: There used to be a test here to see whether the - * player had blown it so badly that he could never ever see - * the remaining treasures, and if so the lamp was zapped to - * 35 turns. But the tests were too simple-minded; things - * like killing the bird before the snake was gone (can never - * see jewelry), and doing it "right" was hopeless. E.G., - * could cross troll bridge several times, using up all - * available treasures, breaking vase, using coins to buy - * batteries, etc., and eventually never be able to get - * across again. If bottle were left on far side, could then - * never get eggs or trident, and the effects propagate. So - * the whole thing was flushed. anyone who makes such a - * gross blunder isn't likely to find everything else anyway - * (so goes the rationalisation). */ - } - int kk = game.prop[obj]; - if (obj == STEPS) - kk = (game.loc == game.fixed[STEPS]) - ? STEPS_UP - : STEPS_DOWN; - pspeak(obj, look, true, kk); - } - } +static void listobjects(void) { + /* Print out descriptions of objects at this location. If + * not closing and property value is negative, tally off + * another treasure. Rug is special case; once seen, its + * game.prop is RUG_DRAGON (dragon on it) till dragon is killed. + * Similarly for chain; game.prop is initially CHAINING_BEAR (locked to + * bear). These hacks are because game.prop=0 is needed to + * get full score. */ + if (!IS_DARK_HERE()) { + ++game.locs[game.loc].abbrev; + for (int i = game.locs[game.loc].atloc; i != 0; + i = game.link[i]) { + obj_t obj = i; + if (obj > NOBJECTS) { + obj = obj - NOBJECTS; + } + if (obj == STEPS && TOTING(NUGGET)) { + continue; + } + /* (ESR) Warning: it looks like you could get away with + * running this code only on objects with the treasure + * property set. Nope. There is mystery here. + */ + if (OBJECT_IS_STASHED(i) || OBJECT_IS_NOTFOUND(obj)) { + if (game.closed) { + continue; + } + OBJECT_SET_FOUND(obj); + if (obj == RUG) { + game.objects[RUG].prop = RUG_DRAGON; + } + if (obj == CHAIN) { + game.objects[CHAIN].prop = + CHAINING_BEAR; + } + if (obj == EGGS) { + game.seenbigwords = true; + } + --game.tally; + /* Note: There used to be a test here to see + * whether the player had blown it so badly that + * he could never ever see the remaining + * treasures, and if so the lamp was zapped to + * 35 turns. But the tests were too + * simple-minded; things like killing the bird + * before the snake was gone (can never see + * jewelry), and doing it "right" was hopeless. + * E.G., could cross troll bridge several times, + * using up all available treasures, breaking + * vase, using coins to buy batteries, etc., and + * eventually never be able to get across again. + * If bottle were left on far side, could then + * never get eggs or trident, and the effects + * propagate. So the whole thing was flushed. + * anyone who makes such a gross blunder isn't + * likely to find everything else anyway (so + * goes the rationalisation). */ + } + int kk = game.objects[obj].prop; + if (obj == STEPS) { + kk = (game.loc == game.objects[STEPS].fixed) + ? STEPS_UP + : STEPS_DOWN; + } + pspeak(obj, look, true, kk); + } + } } -static bool preprocess_command(command_t *command) -/* Pre-processes a command input to see if we need to tease out a few specific cases: +/* Pre-processes a command input to see if we need to tease out a few specific + * cases: * - "enter water" or "enter stream": - * wierd specific case that gets the user wet, and then kicks us back to get another command + * weird specific case that gets the user wet, and then kicks us back to get + * another command * - : - * Irregular form of input, but should be allowed. We switch back to form for - * furtherprocessing. + * Irregular form of input, but should be allowed. We switch back to + * form for further processing. * - "grate": - * If in location with grate, we move to that grate. If we're in a number of other places, - * we move to the entrance. + * If in location with grate, we move to that grate. If we're in a number of + * other places, we move to the entrance. * - "water plant", "oil plant", "water door", "oil door": * Change to "pour water" or "pour oil" based on context * - "cage bird": * If bird is present, we change to "carry bird" * - * Returns true if pre-processing is complete, and we're ready to move to the primary command - * processing, false otherwise. */ -{ - if (command->word[0].type == MOTION && command->word[0].id == ENTER - && (command->word[1].id == STREAM || command->word[1].id == WATER)) { - if (LIQLOC(game.loc) == WATER) - rspeak(FEET_WET); - else - rspeak(WHERE_QUERY); - } else { - if (command->word[0].type == OBJECT) { - /* From OV to VO form */ - if (command->word[1].type == ACTION) { - command_word_t stage = command->word[0]; - command->word[0] = command->word[1]; - command->word[1] = stage; - } + * Returns true if pre-processing is complete, and we're ready to move to the + * primary command processing, false otherwise. */ +static bool preprocess_command(command_t *command) { + if (command->word[0].type == MOTION && command->word[0].id == ENTER && + (command->word[1].id == STREAM || command->word[1].id == WATER)) { + if (LIQLOC(game.loc) == WATER) { + rspeak(FEET_WET); + } else { + rspeak(WHERE_QUERY); + } + } else { + if (command->word[0].type == OBJECT) { + /* From OV to VO form */ + if (command->word[1].type == ACTION) { + command_word_t stage = command->word[0]; + command->word[0] = command->word[1]; + command->word[1] = stage; + } - if (command->word[0].id == GRATE) { - command->word[0].type = MOTION; - if (game.loc == LOC_START || - game.loc == LOC_VALLEY || - game.loc == LOC_SLIT) { - command->word[0].id = DEPRESSION; - } - if (game.loc == LOC_COBBLE || - game.loc == LOC_DEBRIS || - game.loc == LOC_AWKWARD || - game.loc == LOC_BIRDCHAMBER || - game.loc == LOC_PITTOP) { - command->word[0].id = ENTRANCE; - } - } - if ((command->word[0].id == WATER || command->word[0].id == OIL) && - (command->word[1].id == PLANT || command->word[1].id == DOOR)) { - if (AT(command->word[1].id)) { - command->word[1] = command->word[0]; - command->word[0].id = POUR; - command->word[0].type = ACTION; - strncpy(command->word[0].raw, "pour", LINESIZE - 1); - } - } - if (command->word[0].id == CAGE && command->word[1].id == BIRD && HERE(CAGE) && HERE(BIRD)) { - command->word[0].id = CARRY; - command->word[0].type = ACTION; - } - } + if (command->word[0].id == GRATE) { + command->word[0].type = MOTION; + if (game.loc == LOC_START || + game.loc == LOC_VALLEY || + game.loc == LOC_SLIT) { + command->word[0].id = DEPRESSION; + } + if (game.loc == LOC_COBBLE || + game.loc == LOC_DEBRIS || + game.loc == LOC_AWKWARD || + game.loc == LOC_BIRDCHAMBER || + game.loc == LOC_PITTOP) { + command->word[0].id = ENTRANCE; + } + } + if ((command->word[0].id == WATER || + command->word[0].id == OIL) && + (command->word[1].id == PLANT || + command->word[1].id == DOOR)) { + if (AT(command->word[1].id)) { + command->word[1] = command->word[0]; + command->word[0].id = POUR; + command->word[0].type = ACTION; + strncpy(command->word[0].raw, "pour", + LINESIZE - 1); + } + } + if (command->word[0].id == CAGE && + command->word[1].id == BIRD && HERE(CAGE) && + HERE(BIRD)) { + command->word[0].id = CARRY; + command->word[0].type = ACTION; + } + } - /* If no word type is given for the first word, we assume it's a motion. */ - if (command->word[0].type == NO_WORD_TYPE) - command->word[0].type = MOTION; + /* If no word type is given for the first word, we assume it's a + * motion. */ + if (command->word[0].type == NO_WORD_TYPE) { + command->word[0].type = MOTION; + } - command->state = PREPROCESSED; - return true; - } - return false; + command->state = PREPROCESSED; + return true; + } + return false; } -static bool do_move(void) -/* Actually execute the move to the new location and dwarf movement */ -{ - /* Can't leave cave once it's closing (except by main office). */ - if (OUTSID(game.newloc) && game.newloc != 0 && game.closng) { - rspeak(EXIT_CLOSED); - game.newloc = game.loc; - if (!game.panic) - game.clock2 = PANICTIME; - game.panic = true; - } +static bool do_move(void) { + /* Actually execute the move to the new location and dwarf movement */ + /* Can't leave cave once it's closing (except by main office). */ + if (OUTSIDE(game.newloc) && game.newloc != 0 && game.closng) { + rspeak(EXIT_CLOSED); + game.newloc = game.loc; + if (!game.panic) { + game.clock2 = PANICTIME; + } + game.panic = true; + } - /* See if a dwarf has seen him and has come from where he - * wants to go. If so, the dwarf's blocking his way. If - * coming from place forbidden to pirate (dwarves rooted in - * place) let him get out (and attacked). */ - if (game.newloc != game.loc && !FORCED(game.loc) && !CNDBIT(game.loc, COND_NOARRR)) { - for (size_t i = 1; i <= NDWARVES - 1; i++) { - if (game.odloc[i] == game.newloc && game.dseen[i]) { - game.newloc = game.loc; - rspeak(DWARF_BLOCK); - break; - } - } - } - game.loc = game.newloc; + /* See if a dwarf has seen him and has come from where he + * wants to go. If so, the dwarf's blocking his way. If + * coming from place forbidden to pirate (dwarves rooted in + * place) let him get out (and attacked). */ + if (game.newloc != game.loc && !FORCED(game.loc) && + !CNDBIT(game.loc, COND_NOARRR)) { + for (size_t i = 1; i <= NDWARVES - 1; i++) { + if (game.dwarves[i].oldloc == game.newloc && + game.dwarves[i].seen) { + game.newloc = game.loc; + rspeak(DWARF_BLOCK); + break; + } + } + } + game.loc = game.newloc; - if (!dwarfmove()) - croak(); + if (!dwarfmove()) { + croak(); + } - if (game.loc == LOC_NOWHERE) - croak(); + if (game.loc == LOC_NOWHERE) { + croak(); + } - /* The easiest way to get killed is to fall into a pit in - * pitch darkness. */ - if (!FORCED(game.loc) && DARK(game.loc) && game.wzdark && PCT(35)) { // FIXME: magic number - rspeak(PIT_FALL); - game.oldlc2 = game.loc; - croak(); - return false; - } + /* The easiest way to get killed is to fall into a pit in + * pitch darkness. */ + if (!FORCED(game.loc) && IS_DARK_HERE() && game.wzdark && + PCT(PIT_KILL_PROB)) { + rspeak(PIT_FALL); + game.oldlc2 = game.loc; + croak(); + return false; + } - return true; + return true; } -static bool do_command() -/* Get and execute a command */ -{ - static command_t command; - clear_command(&command); +static bool do_command(void) { + /* Get and execute a command */ + static command_t command; + clear_command(&command); - /* Describe the current location and (maybe) get next command. */ - while (command.state != EXECUTED) { - describe_location(); + /* Describe the current location and (maybe) get next command. */ + while (command.state != EXECUTED) { + describe_location(); - if (FORCED(game.loc)) { - playermove(HERE); - return true; - } + if (FORCED(game.loc)) { + playermove(HERE); + return true; + } - listobjects(); + listobjects(); - /* Command not yet given; keep getting commands from user - * until valid command is both given and executed. */ - clear_command(&command); - while (command.state <= GIVEN) { + /* Command not yet given; keep getting commands from user + * until valid command is both given and executed. */ + clear_command(&command); + while (command.state <= GIVEN) { - if (game.closed) { - /* If closing time, check for any objects being toted with - * game.prop < 0 and stash them. This way objects won't be - * described until they've been picked up and put down - * separate from their respective piles. */ - if (game.prop[OYSTER] < 0 && TOTING(OYSTER)) - pspeak(OYSTER, look, true, 1); - for (size_t i = 1; i <= NOBJECTS; i++) { - if (TOTING(i) && game.prop[i] < 0) - game.prop[i] = STASHED(i); - } - } + if (game.closed) { + /* If closing time, check for any stashed + * objects being toted and unstash them. This + * way objects won't be described until they've + * been picked up and put down separate from + * their respective piles. */ + if ((OBJECT_IS_NOTFOUND(OYSTER) || + OBJECT_IS_STASHED(OYSTER)) && + TOTING(OYSTER)) { + pspeak(OYSTER, look, true, 1); + } + for (size_t i = 1; i <= NOBJECTS; i++) { + if (TOTING(i) && + (OBJECT_IS_NOTFOUND(i) || + OBJECT_IS_STASHED(i))) { + OBJECT_STASHIFY( + i, game.objects[i].prop); + } + } + } - /* Check to see if the room is dark. If the knife is here, - * and it's dark, the knife permanently disappears */ - game.wzdark = DARK(game.loc); - if (game.knfloc != LOC_NOWHERE && game.knfloc != game.loc) - game.knfloc = LOC_NOWHERE; + /* Check to see if the room is dark. */ + game.wzdark = IS_DARK_HERE(); - /* Check some for hints, get input from user, increment - * turn, and pre-process commands. Keep going until - * pre-processing is done. */ - while ( command.state < PREPROCESSED ) { - checkhints(); + /* If the knife is not here it permanently disappears. + * Possibly this should fire if the knife is here but + * the room is dark? */ + if (game.knfloc > LOC_NOWHERE && + game.knfloc != game.loc) { + game.knfloc = LOC_NOWHERE; + } - /* Get command input from user */ - if (!get_command_input(&command)) - return false; + /* Check some for hints, get input from user, increment + * turn, and pre-process commands. Keep going until + * pre-processing is done. */ + while (command.state < PREPROCESSED) { + checkhints(); - ++game.turns; - preprocess_command(&command); - } + /* Get command input from user */ + if (!get_command_input(&command)) { + return false; + } - /* check if game is closed, and exit if it is */ - if (closecheck() ) - return true; + /* 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. + */ + game.foobar = (game.foobar > WORD_EMPTY) + ? -game.foobar + : WORD_EMPTY; - /* loop until all words in command are procesed */ - while (command.state == PREPROCESSED ) { - command.state = PROCESSING; + ++game.turns; + preprocess_command(&command); + } - if (command.word[0].id == WORD_NOT_FOUND) { - /* Gee, I don't understand. */ - sspeak(DONT_KNOW, command.word[0].raw); - clear_command(&command); - continue; - } + /* check if game is closed, and exit if it is */ + if (closecheck()) { + return true; + } - /* Give user hints of shortcuts */ - if (strncasecmp(command.word[0].raw, "west", sizeof("west")) == 0) { - if (++game.iwest == 10) - rspeak(W_IS_WEST); - } - if (strncasecmp(command.word[0].raw, "go", sizeof("go")) == 0 && command.word[1].id != WORD_EMPTY) { - if (++game.igo == 10) - rspeak(GO_UNNEEDED); - } + /* loop until all words in command are processed */ + while (command.state == PREPROCESSED) { + command.state = PROCESSING; - switch (command.word[0].type) { - case MOTION: - playermove(command.word[0].id); - command.state = EXECUTED; - continue; - case OBJECT: - command.part = unknown; - command.obj = command.word[0].id; - break; - case ACTION: - if (command.word[1].type == NUMERIC) - command.part = transitive; - else - command.part = intransitive; - command.verb = command.word[0].id; - break; - case NUMERIC: - if (!settings.oldstyle) { - sspeak(DONT_KNOW, command.word[0].raw); - clear_command(&command); - continue; - } - break;// LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - case NO_WORD_TYPE: // LCOV_EXCL_LINE - BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3); // LCOV_EXCL_LINE - } + if (command.word[0].id == WORD_NOT_FOUND) { + /* Gee, I don't understand. */ + sspeak(DONT_KNOW, command.word[0].raw); + clear_command(&command); + continue; + } - switch (action(command)) { - case GO_TERMINATE: - command.state = EXECUTED; - break; - case GO_MOVE: - playermove(NUL); - command.state = EXECUTED; - break; - case GO_WORD2: + /* Give user hints of shortcuts */ + if (strncasecmp(command.word[0].raw, "west", + sizeof("west")) == 0) { + if (++game.iwest == 10) { + rspeak(W_IS_WEST); + } + } + if (strncasecmp(command.word[0].raw, "go", + sizeof("go")) == 0 && + command.word[1].id != WORD_EMPTY) { + if (++game.igo == 10) { + rspeak(GO_UNNEEDED); + } + } + + switch (command.word[0].type) { + case MOTION: + playermove(command.word[0].id); + command.state = EXECUTED; + continue; + case OBJECT: + command.part = unknown; + command.obj = command.word[0].id; + break; + case ACTION: + if (command.word[1].type == NUMERIC) { + command.part = transitive; + } else { + command.part = intransitive; + } + command.verb = command.word[0].id; + break; + case NUMERIC: + if (!settings.oldstyle) { + sspeak(DONT_KNOW, + command.word[0].raw); + clear_command(&command); + continue; + } + break; // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + case NO_WORD_TYPE: // LCOV_EXCL_LINE + BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3); // LCOV_EXCL_LINE + } + + switch (action(command)) { + case GO_TERMINATE: + command.state = EXECUTED; + break; + case GO_MOVE: + playermove(NUL); + command.state = EXECUTED; + break; + case GO_WORD2: #ifdef GDEBUG - printf("Word shift\n"); + printf("Word shift\n"); #endif /* GDEBUG */ - /* Get second word for analysis. */ - command.word[0] = command.word[1]; - command.word[1] = empty_command_word; - command.state = PREPROCESSED; - break; - case GO_UNKNOWN: - /* Random intransitive verbs come here. Clear obj just in case - * (see attack()). */ - command.word[0].raw[0] = toupper(command.word[0].raw[0]); - sspeak(DO_WHAT, command.word[0].raw); - command.obj = NO_OBJECT; + /* Get second word for analysis. */ + command.word[0] = command.word[1]; + command.word[1] = empty_command_word; + command.state = PREPROCESSED; + break; + case GO_UNKNOWN: + /* Random intransitive verbs come here. + * Clear obj just in case (see + * attack()). */ + command.word[0].raw[0] = + toupper(command.word[0].raw[0]); + sspeak(DO_WHAT, command.word[0].raw); + command.obj = NO_OBJECT; - /* object cleared; we need to go back to the preprocessing step */ - command.state = GIVEN; - break; - case GO_CHECKHINT: // FIXME: re-name to be more contextual; this was previously a label - command.state = GIVEN; - break; - case GO_DWARFWAKE: - /* Oh dear, he's disturbed the dwarves. */ - rspeak(DWARVES_AWAKEN); - terminate(endgame); - case GO_CLEAROBJ: // FIXME: re-name to be more contextual; this was previously a label - clear_command(&command); - break; - case GO_TOP: // FIXME: re-name to be more contextual; this was previously a label - break; - default: // LCOV_EXCL_LINE - BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH); // LCOV_EXCL_LINE - } - } /* while command has not been fully processed */ - } /* while command is not yet given */ - } /* while command is not executed */ + /* object cleared; we need to go back to + * the preprocessing step */ + command.state = GIVEN; + break; + case GO_CHECKHINT: // FIXME: re-name to be more + // contextual; this was + // previously a label + command.state = GIVEN; + break; + case GO_DWARFWAKE: + /* Oh dear, he's disturbed the dwarves. + */ + rspeak(DWARVES_AWAKEN); + terminate(endgame); + case GO_CLEAROBJ: // FIXME: re-name to be more + // contextual; this was + // previously a label + clear_command(&command); + break; + case GO_TOP: // FIXME: re-name to be more + // contextual; this was previously + // a label + break; + default: // LCOV_EXCL_LINE + BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH); // LCOV_EXCL_LINE + } + } /* while command has not been fully processed */ + } /* while command is not yet given */ + } /* while command is not executed */ - /* command completely executed; we return true. */ - return true; + /* command completely executed; we return true. */ + return true; +} + +/* + * MAIN PROGRAM + * + * Adventure (rev 2: 20 treasures) + * History: Original idea & 5-treasure version (adventures) by Willie Crowther + * 15-treasure version (adventure) by Don Woods, April-June 1977 + * 20-treasure version (rev 2) by Don Woods, August 1978 + * Errata fixed: 78/12/25 + * Revived 2017 as Open Adventure. + */ + +int main(int argc, char *argv[]) { + int ch; + + /* Options. */ + +#if defined ADVENT_AUTOSAVE + const char *opts = "dl:oa:"; + const char *usage = + "Usage: %s [-l logfilename] [-o] [-a filename] [script...]\n"; + FILE *rfp = NULL; + const char *autosave_filename = NULL; +#elif !defined ADVENT_NOSAVE + const char *opts = "dl:or:"; + const char *usage = "Usage: %s [-l logfilename] [-o] [-r " + "restorefilename] [script...]\n"; + FILE *rfp = NULL; +#else + const char *opts = "dl:o"; + const char *usage = "Usage: %s [-l logfilename] [-o] [script...]\n"; +#endif + while ((ch = getopt(argc, argv, opts)) != EOF) { + switch (ch) { + case 'd': // LCOV_EXCL_LINE + settings.debug += 1; // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + case 'l': + settings.logfp = fopen(optarg, "w"); + if (settings.logfp == NULL) { + fprintf( + stderr, + "advent: can't open logfile %s for write\n", + optarg); + } + signal(SIGINT, sig_handler); + break; + case 'o': + settings.oldstyle = true; + settings.prompt = false; + break; +#ifdef ADVENT_AUTOSAVE + case 'a': + rfp = fopen(optarg, READ_MODE); + autosave_filename = optarg; + signal(SIGHUP, sig_handler); + signal(SIGTERM, sig_handler); + break; +#elif !defined ADVENT_NOSAVE + case 'r': + rfp = fopen(optarg, "r"); + if (rfp == NULL) { + fprintf(stderr, + "advent: can't open save file %s for " + "read\n", + optarg); + } + break; +#endif + default: + fprintf(stderr, usage, argv[0]); + fprintf(stderr, " -l create a log file of your " + "game named as specified'\n"); + fprintf(stderr, + " -o 'oldstyle' (no prompt, no command " + "editing, displays 'Initialising...')\n"); +#if defined ADVENT_AUTOSAVE + fprintf(stderr, " -a automatic save/restore " + "from specified saved game file\n"); +#elif !defined ADVENT_NOSAVE + fprintf(stderr, " -r restore from specified " + "saved game file\n"); +#endif + exit(EXIT_FAILURE); + break; + } + } + + /* copy invocation line part after switches */ + settings.argc = argc - optind; + settings.argv = argv + optind; + settings.optind = 0; + + /* Initialize game variables */ + int seedval = initialise(); + +#if !defined ADVENT_NOSAVE + if (!rfp) { + game.novice = yes_or_no(arbitrary_messages[WELCOME_YOU], + arbitrary_messages[CAVE_NEARBY], + arbitrary_messages[NO_MESSAGE]); + if (game.novice) { + game.limit = NOVICELIMIT; + } + } else { + restore(rfp); +#if defined ADVENT_AUTOSAVE + score(scoregame); +#endif + } +#if defined ADVENT_AUTOSAVE + if (autosave_filename != NULL) { + if ((autosave_fp = fopen(autosave_filename, WRITE_MODE)) == + NULL) { + perror(autosave_filename); + return EXIT_FAILURE; + } + autosave(); + } +#endif +#else + game.novice = yes_or_no(arbitrary_messages[WELCOME_YOU], + arbitrary_messages[CAVE_NEARBY], + arbitrary_messages[NO_MESSAGE]); + if (game.novice) { + game.limit = NOVICELIMIT; + } +#endif + + if (settings.logfp) { + fprintf(settings.logfp, "seed %d\n", seedval); + } + + /* interpret commands until EOF or interrupt */ + for (;;) { + // if we're supposed to move, move + if (!do_move()) { + continue; + } + + // get command + if (!do_command()) { + break; + } + } + /* show score and exit */ + terminate(quitgame); } /* end */ diff --git a/make_dungeon.py b/make_dungeon.py index 5eb7dee..8d7f719 100755 --- a/make_dungeon.py +++ b/make_dungeon.py @@ -1,4 +1,6 @@ #!/usr/bin/env python3 +# SPDX-FileCopyrightText: (C) Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause """ This is the open-adventure dungeon generator. It consumes a YAML description of the dungeon and outputs a dungeon.h and dungeon.c pair of C code files. @@ -6,12 +8,9 @@ the dungeon and outputs a dungeon.h and dungeon.c pair of C code files. The nontrivial part of this is the compilation of the YAML for movement rules to the travel array that's actually used by playermove(). - -Copyright (c) 2017 by Eric S. Raymond -SPDX-License-Identifier: BSD-2-clause """ -# pylint: disable=consider-using-f-string +# pylint: disable=consider-using-f-string,line-too-long,invalid-name,missing-function-docstring,too-many-branches,global-statement,multiple-imports,too-many-locals,too-many-statements,too-many-nested-blocks,no-else-return,raise-missing-from,redefined-outer-name import sys, yaml @@ -21,10 +20,11 @@ C_NAME = "dungeon.c" H_TEMPLATE_PATH = "templates/dungeon.h.tpl" C_TEMPLATE_PATH = "templates/dungeon.c.tpl" -DONOTEDIT_COMMENT = "/* Generated from adventure.yaml - do not hand-hack! */\n/* SPDX-License-Identifier: BSD-2-clause */\n\n" +DONOTEDIT_COMMENT = "/* Generated from adventure.yaml - do not hand-hack! */\n\n" statedefines = "" + def make_c_string(string): """Render a Python string into C string literal format.""" if string is None: @@ -36,14 +36,16 @@ def make_c_string(string): string = '"' + string + '"' return string + def get_refs(l): reflist = [x[0] for x in l] ref_str = "" for ref in reflist: ref_str += " {},\n".format(ref) - ref_str = ref_str[:-1] # trim trailing newline + ref_str = ref_str[:-1] # trim trailing newline return ref_str + def get_string_group(strings): template = """{{ .strs = {}, @@ -52,20 +54,24 @@ def get_string_group(strings): if strings == []: strs = "NULL" else: - strs = "(const char* []) {" + ", ".join([make_c_string(s) for s in strings]) + "}" + strs = ( + "(const char* []) {" + ", ".join([make_c_string(s) for s in strings]) + "}" + ) n = len(strings) sg_str = template.format(strs, n) return sg_str + def get_arbitrary_messages(arb): template = """ {}, """ arb_str = "" for item in arb: arb_str += template.format(make_c_string(item[1])) - arb_str = arb_str[:-1] # trim trailing newline + arb_str = arb_str[:-1] # trim trailing newline return arb_str + def get_class_messages(cls): template = """ {{ .threshold = {}, @@ -77,9 +83,10 @@ def get_class_messages(cls): threshold = item["threshold"] message = make_c_string(item["message"]) cls_str += template.format(threshold, message) - cls_str = cls_str[:-1] # trim trailing newline + cls_str = cls_str[:-1] # trim trailing newline return cls_str + def get_turn_thresholds(trn): template = """ {{ .threshold = {}, @@ -93,9 +100,10 @@ def get_turn_thresholds(trn): point_loss = item["point_loss"] message = make_c_string(item["message"]) trn_str += template.format(threshold, point_loss, message) - trn_str = trn_str[:-1] # trim trailing newline + trn_str = trn_str[:-1] # trim trailing newline return trn_str + def get_locations(loc): template = """ {{ // {}: {} .description = {{ @@ -113,9 +121,10 @@ def get_locations(loc): sound = item[1].get("sound", "SILENT") loud = "true" if item[1].get("loud") else "false" loc_str += template.format(i, item[0], short_d, long_d, sound, loud) - loc_str = loc_str[:-1] # trim trailing newline + loc_str = loc_str[:-1] # trim trailing newline return loc_str + def get_objects(obj): template = """ {{ // {}: {} .words = {}, @@ -137,6 +146,7 @@ def get_objects(obj): }}, }}, """ + max_state = 0 obj_str = "" for (i, item) in enumerate(obj): attr = item[1] @@ -154,12 +164,13 @@ def get_objects(obj): descriptions_str += " " * 12 + make_c_string(l_msg) + ",\n" for label in attr.get("states", []): labels.append(label) - descriptions_str = descriptions_str[:-1] # trim trailing newline + descriptions_str = descriptions_str[:-1] # trim trailing newline if labels: global statedefines statedefines += "/* States for %s */\n" % item[0] for (n, label) in enumerate(labels): statedefines += "#define %s\t%d\n" % (label, n) + max_state = max(max_state, n) statedefines += "\n" sounds_str = "" if attr.get("sounds") is None: @@ -167,21 +178,21 @@ def get_objects(obj): else: for l_msg in attr["sounds"]: sounds_str += " " * 12 + make_c_string(l_msg) + ",\n" - sounds_str = sounds_str[:-1] # trim trailing newline + sounds_str = sounds_str[:-1] # trim trailing newline texts_str = "" if attr.get("texts") is None: texts_str = " " * 12 + "NULL," else: for l_msg in attr["texts"]: texts_str += " " * 12 + make_c_string(l_msg) + ",\n" - texts_str = texts_str[:-1] # trim trailing newline + texts_str = texts_str[:-1] # trim trailing newline changes_str = "" if attr.get("changes") is None: changes_str = " " * 12 + "NULL," else: for l_msg in attr["changes"]: changes_str += " " * 12 + make_c_string(l_msg) + ",\n" - changes_str = changes_str[:-1] # trim trailing newline + changes_str = changes_str[:-1] # trim trailing newline locs = attr.get("locations", ["LOC_NOWHERE", "LOC_NOWHERE"]) immovable = attr.get("immovable", False) try: @@ -191,10 +202,24 @@ def get_objects(obj): sys.stderr.write("dungeon: unknown object location in %s\n" % locs) sys.exit(1) treasure = "true" if attr.get("treasure") else "false" - obj_str += template.format(i, item[0], words_str, i_msg, locs[0], locs[1], treasure, descriptions_str, sounds_str, texts_str, changes_str) - obj_str = obj_str[:-1] # trim trailing newline + obj_str += template.format( + i, + item[0], + words_str, + i_msg, + locs[0], + locs[1], + treasure, + descriptions_str, + sounds_str, + texts_str, + changes_str, + ) + obj_str = obj_str[:-1] # trim trailing newline + statedefines += "/* Maximum state value */\n#define MAX_STATE %d\n" % max_state return obj_str + def get_obituaries(obit): template = """ {{ .query = {}, @@ -206,9 +231,10 @@ def get_obituaries(obit): query = make_c_string(o["query"]) yes = make_c_string(o["yes_response"]) obit_str += template.format(query, yes) - obit_str = obit_str[:-1] # trim trailing newline + obit_str = obit_str[:-1] # trim trailing newline return obit_str + def get_hints(hnt): template = """ {{ .number = {}, @@ -227,9 +253,10 @@ def get_hints(hnt): question = make_c_string(item["question"]) hint = make_c_string(item["hint"]) hnt_str += template.format(number, penalty, turns, question, hint) - hnt_str = hnt_str[:-1] # trim trailing newline + hnt_str = hnt_str[:-1] # trim trailing newline return hnt_str + def get_condbits(locations): cnd_str = "" for (name, loc) in locations: @@ -240,7 +267,7 @@ def get_condbits(locations): if conditions[flag]: flaglist.append(flag) line = "|".join([("(1<500 message N-500 from section 6 is printed, - # and he stays wherever he is. + # If N<=300 it is the location to go to. + # If 300500 message N-500 from section 6 is printed, + # and he stays wherever he is. # Meanwhile, M specifies the conditions on the motion. - # If M=0 it's unconditional. - # If 0 +# SPDX-License-Identifier: BSD-2-Clause """\ -usage: make-graph.py [-a] -d] [-m] [-s] +usage: make_graph.py [-a] [-d] [-m] [-s] [-v] Make a DOT graph of Colossal Cave. -a = emit graph of entire dungeon --d = emit graoh of mazw all different +-d = emit graph of maze all different -f = emit graph of forest locations -m = emit graph of maze all alike -s = emit graph of non-forest surface locations -v = include internal symbols in room labels """ -# Copyright (c) 2017 by Eric S. Raymond -# SPDX-License-Identifier: BSD-2-clause -# pylint: disable=consider-using-f-string +# pylint: disable=consider-using-f-string,line-too-long,invalid-name,missing-function-docstring,multiple-imports,redefined-outer-name import sys, getopt, yaml + def allalike(loc): "Select out loci related to the Maze All Alike" return location_lookup[loc]["conditions"].get("ALLALIKE") + def alldifferent(loc): "Select out loci related to the Maze All Alike" return location_lookup[loc]["conditions"].get("ALLDIFFERENT") + def surface(loc): "Select out surface locations" return location_lookup[loc]["conditions"].get("ABOVE") + def forest(loc): return location_lookup[loc]["conditions"].get("FOREST") + def abbreviate(d): - m = {"NORTH":"N", "EAST":"E", "SOUTH":"S", "WEST":"W", "UPWAR":"U", "DOWN":"D"} + m = { + "NORTH": "N", + "EAST": "E", + "SOUTH": "S", + "WEST": "W", + "UPWAR": "U", + "DOWN": "D", + } return m.get(d, d) + def roomlabel(loc): "Generate a room label from the description, if possible" - loc_descriptions = location_lookup[loc]['description'] + loc_descriptions = location_lookup[loc]["description"] description = "" if debug: description = loc[4:] @@ -51,8 +64,12 @@ def roomlabel(loc): if short.startswith("You're "): short = short[7:] if short.startswith("You are "): - short = short[8 :] - if short.startswith("in ") or short.startswith("at ") or short.startswith("on "): + short = short[8:] + if ( + short.startswith("in ") + or short.startswith("at ") + or short.startswith("on ") + ): short = short[3:] if short.startswith("the "): short = short[4:] @@ -69,8 +86,9 @@ def roomlabel(loc): description += "\\n(" + ",".join(startlocs[loc]).lower() + ")" return description + # A forwarder is a location that you can't actually stop in - when you go there -# it ships some message (which is the point) then shifts you to a nexr location. +# it ships some message (which is the point) then shifts you to a next location. # A forwarder has a zero-length array of notion verbs in its travel section. # # Here is an example forwarder declaration: @@ -85,10 +103,12 @@ def roomlabel(loc): # {verbs: [], action: [goto, LOC_NOWHERE]}, # ] + def is_forwarder(loc): "Is a location a forwarder?" - travel = location_lookup[loc]['travel'] - return len(travel) == 1 and len(travel[0]['verbs']) == 0 + travel = location_lookup[loc]["travel"] + return len(travel) == 1 and len(travel[0]["verbs"]) == 0 + def forward(loc): "Chase a location through forwarding links." @@ -96,8 +116,9 @@ def forward(loc): loc = location_lookup[loc]["travel"][0]["action"][1] return loc + def reveal(objname): - "Should this object be revealed when mappinmg?" + "Should this object be revealed when mapping?" if "OBJ_" in objname: return False if objname == "VEND": @@ -105,8 +126,9 @@ def reveal(objname): obj = object_lookup[objname] return not obj.get("immovable") + if __name__ == "__main__": - with open("adventure.yaml", "r", encoding='ascii', errors='surrogateescape') as f: + with open("adventure.yaml", "r", encoding="ascii", errors="surrogateescape") as f: db = yaml.safe_load(f) location_lookup = dict(db["locations"]) @@ -121,17 +143,18 @@ if __name__ == "__main__": subset = allalike debug = False for (switch, val) in options: - if switch == '-a': + if switch == "-a": + # pylint: disable=unnecessary-lambda-assignment subset = lambda loc: True - elif switch == '-d': + elif switch == "-d": subset = alldifferent - elif switch == '-f': + elif switch == "-f": subset = forest - elif switch == '-m': + elif switch == "-m": subset = allalike - elif switch == '-s': + elif switch == "-s": subset = surface - elif switch == '-v': + elif switch == "-v": debug = True else: sys.stderr.write(__doc__) @@ -148,8 +171,8 @@ if __name__ == "__main__": startlocs[location] = [objname] # Compute reachability, using forwards. - # Dictionary ke6y is (from, to) iff its a valid link, - # value is correspoinding motion verbs. + # Dictionary key is (from, to) iff its a valid link, + # value is corresponding motion verbs. links = {} nodes = [] for (loc, attrs) in db["locations"]: @@ -170,7 +193,7 @@ if __name__ == "__main__": neighbors = set() for loc in nodes: for (f, t) in links: - if f == 'LOC_NOWHERE' or t == 'LOC_NOWHERE': + if f == "LOC_NOWHERE" or t == "LOC_NOWHERE": continue if (f == loc and subset(t)) or (t == loc and subset(f)): if loc not in neighbors: @@ -189,7 +212,7 @@ if __name__ == "__main__": # Draw arcs for (f, t) in links: arc = "%s -> %s" % (f[4:], t[4:]) - label=",".join(links[(f, t)]).lower() + label = ",".join(links[(f, t)]).lower() if len(label) > 0: arc += ' [label="%s"]' % label print(" " + arc) diff --git a/misc.c b/misc.c index 7365fbf..b28949e 100644 --- a/misc.c +++ b/misc.c @@ -1,738 +1,775 @@ /* - * I/O and support riutines. + * I/O and support routines. * - * Copyright (c) 1977, 2005 by Will Crowther and Don Woods - * Copyright (c) 2017 by Eric S. Raymond - * SPDX-License-Identifier: BSD-2-clause + * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods + * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include -#include -#include -#include -#include #include #include #include +#include +#include +#include +#include +#include +#include +#include #include "advent.h" #include "dungeon.h" -static void* xcalloc(size_t size) -{ - void* ptr = calloc(size, 1); - if (ptr == NULL) { - // LCOV_EXCL_START - // exclude from coverage analysis because we can't simulate an out of memory error in testing - fprintf(stderr, "Out of memory!\n"); - exit(EXIT_FAILURE); - // LCOV_EXCL_STOP - } - return (ptr); +static void *xcalloc(size_t size) { + void *ptr = calloc(size, 1); + if (ptr == NULL) { + // LCOV_EXCL_START + // exclude from coverage analysis because we can't simulate an + // out of memory error in testing + fprintf(stderr, "Out of memory!\n"); + exit(EXIT_FAILURE); + // LCOV_EXCL_STOP + } + return (ptr); } /* I/O routines (speak, pspeak, rspeak, sspeak, get_input, yes) */ -static void vspeak(const char* msg, bool blank, va_list ap) -/* Engine for various speak functions */ -{ - // Do nothing if we got a null pointer. - if (msg == NULL) - return; +static void vspeak(const char *msg, bool blank, va_list ap) { + /* Engine for various speak functions */ + // Do nothing if we got a null pointer. + if (msg == NULL) { + return; + } - // Do nothing if we got an empty string. - if (strlen(msg) == 0) - return; + // Do nothing if we got an empty string. + if (strlen(msg) == 0) { + return; + } - if (blank == true) - printf("\n"); + if (blank == true) { + printf("\n"); + } - int msglen = strlen(msg); + int msglen = strlen(msg); - // Rendered string - ssize_t size = 2000; /* msglen > 50 ? msglen*2 : 100; */ - char* rendered = xcalloc(size); - char* renderp = rendered; + // Rendered string + ssize_t size = 2000; /* msglen > 50 ? msglen*2 : 100; */ + char *rendered = xcalloc(size); + char *renderp = rendered; - // Handle format specifiers (including the custom %S) by - // adjusting the parameter accordingly, and replacing the - // specifier with %s. - bool pluralize = false; - for (int i = 0; i < msglen; i++) { - if (msg[i] != '%') { - /* Ugh. Least obtrusive way to deal with artifacts "on the floor" - * being dropped outside of both cave and building. */ - if (strncmp(msg + i, "floor", 5) == 0 && strchr(" .", msg[i + 5]) && !INSIDE(game.loc)) { - strcpy(renderp, "ground"); - renderp += 6; - i += 4; - size -= 5; - } else { - *renderp++ = msg[i]; - size--; - } - } else { - i++; - // Integer specifier. - if (msg[i] == 'd') { - int32_t arg = va_arg(ap, int32_t); - int ret = snprintf(renderp, size, "%" PRId32, arg); - if (ret < size) { - renderp += ret; - size -= ret; - } - pluralize = (arg != 1); - } + // Handle format specifiers (including the custom %S) by + // adjusting the parameter accordingly, and replacing the + // specifier with %s. + bool pluralize = false; + for (int i = 0; i < msglen; i++) { + if (msg[i] != '%') { + /* Ugh. Least obtrusive way to deal with artifacts "on + * the floor" being dropped outside of both cave and + * building. */ + if (strncmp(msg + i, "floor", 5) == 0 && + strchr(" .", msg[i + 5]) && !INSIDE(game.loc)) { + strcpy(renderp, "ground"); + renderp += 6; + i += 4; + size -= 5; + } else { + *renderp++ = msg[i]; + size--; + } + } else { + i++; + // Integer specifier. + if (msg[i] == 'd') { + int32_t arg = va_arg(ap, int32_t); + int ret = + snprintf(renderp, size, "%" PRId32, arg); + if (ret < size) { + renderp += ret; + size -= ret; + } + pluralize = (arg != 1); + } - // Unmodified string specifier. - if (msg[i] == 's') { - char *arg = va_arg(ap, char *); - strncat(renderp, arg, size - 1); - size_t len = strlen(renderp); - renderp += len; - size -= len; - } + // Unmodified string specifier. + if (msg[i] == 's') { + char *arg = va_arg(ap, char *); + strncat(renderp, arg, size - 1); + size_t len = strlen(renderp); + renderp += len; + size -= len; + } - // Singular/plural specifier. - if (msg[i] == 'S') { - // look at the *previous* numeric parameter - if (pluralize) { - *renderp++ = 's'; - size--; - } - } + // Singular/plural specifier. + if (msg[i] == 'S') { + // look at the *previous* numeric parameter + if (pluralize) { + *renderp++ = 's'; + size--; + } + } - // LCOV_EXCL_START - doesn't occur in test suite. - /* Version specifier */ - if (msg[i] == 'V') { - strcpy(renderp, VERSION); - size_t len = strlen(VERSION); - renderp += len; - size -= len; - } - // LCOV_EXCL_STOP - } - } - *renderp = 0; + // LCOV_EXCL_START - doesn't occur in test suite. + /* Version specifier */ + if (msg[i] == 'V') { + strcpy(renderp, VERSION); + size_t len = strlen(VERSION); + renderp += len; + size -= len; + } + // LCOV_EXCL_STOP + } + } + *renderp = 0; - // Print the message. - printf("%s\n", rendered); + // Print the message. + printf("%s\n", rendered); - free(rendered); + free(rendered); } -void speak(const char* msg, ...) -/* speak a specified string */ -{ - va_list ap; - va_start(ap, msg); - vspeak(msg, true, ap); - va_end(ap); +void speak(const char *msg, ...) { + /* speak a specified string */ + va_list ap; + va_start(ap, msg); + vspeak(msg, true, ap); + va_end(ap); } -void sspeak(const int msg, ...) -/* Speak a message from the arbitrary-messages list */ -{ - va_list ap; - va_start(ap, msg); - fputc('\n', stdout); - vprintf(arbitrary_messages[msg], ap); - fputc('\n', stdout); - va_end(ap); +void sspeak(const int msg, ...) { + /* Speak a message from the arbitrary-messages list */ + va_list ap; + va_start(ap, msg); + fputc('\n', stdout); + vprintf(arbitrary_messages[msg], ap); + fputc('\n', stdout); + va_end(ap); } -void pspeak(vocab_t msg, enum speaktype mode, bool blank, int skip, ...) -/* Find the skip+1st message from msg and print it. Modes are: - * feel = for inventory, what you can touch - * look = the full description for the state the object is in - * listen = the sound for the state the object is in - * study = text on the object. */ -{ - va_list ap; - va_start(ap, skip); - switch (mode) { - case touch: - vspeak(objects[msg].inventory, blank, ap); - break; - case look: - vspeak(objects[msg].descriptions[skip], blank, ap); - break; - case hear: - vspeak(objects[msg].sounds[skip], blank, ap); - break; - case study: - vspeak(objects[msg].texts[skip], blank, ap); - break; - case change: - vspeak(objects[msg].changes[skip], blank, ap); - break; - } - va_end(ap); +void pspeak(vocab_t msg, enum speaktype mode, bool blank, int skip, ...) { + /* Find the skip+1st message from msg and print it. Modes are: + * feel = for inventory, what you can touch + * look = the full description for the state the object is in + * listen = the sound for the state the object is in + * study = text on the object. */ + va_list ap; + va_start(ap, skip); + switch (mode) { + case touch: + vspeak(objects[msg].inventory, blank, ap); + break; + case look: + vspeak(objects[msg].descriptions[skip], blank, ap); + break; + case hear: + vspeak(objects[msg].sounds[skip], blank, ap); + break; + case study: + vspeak(objects[msg].texts[skip], blank, ap); + break; + case change: + vspeak(objects[msg].changes[skip], blank, ap); + break; + } + va_end(ap); } -void rspeak(vocab_t i, ...) -/* Print the i-th "random" message (section 6 of database). */ -{ - va_list ap; - va_start(ap, i); - vspeak(arbitrary_messages[i], true, ap); - va_end(ap); +void rspeak(vocab_t i, ...) { + /* Print the i-th "random" message (section 6 of database). */ + va_list ap; + va_start(ap, i); + vspeak(arbitrary_messages[i], true, ap); + va_end(ap); } -void echo_input(FILE* destination, const char* input_prompt, const char* input) -{ - size_t len = strlen(input_prompt) + strlen(input) + 1; - char* prompt_and_input = (char*) xcalloc(len); - strcpy(prompt_and_input, input_prompt); - strcat(prompt_and_input, input); - fprintf(destination, "%s\n", prompt_and_input); - free(prompt_and_input); +void echo_input(FILE *destination, const char *input_prompt, + const char *input) { + size_t len = strlen(input_prompt) + strlen(input) + 1; + char *prompt_and_input = (char *)xcalloc(len); + strcpy(prompt_and_input, input_prompt); + strcat(prompt_and_input, input); + fprintf(destination, "%s\n", prompt_and_input); + free(prompt_and_input); } -static int word_count(char* str) -{ - char delims[] = " \t"; - int count = 0; - int inblanks = true; +static int word_count(char *str) { + char delims[] = " \t"; + int count = 0; + int inblanks = true; - for (char *s = str; *s; s++) - if (inblanks) { - if (strchr(delims, *s) == 0) { - ++count; - inblanks = false; - } - } else { - if (strchr(delims, *s) != 0) { - inblanks = true; - } - } + for (char *s = str; *s; s++) { + if (inblanks) { + if (strchr(delims, *s) == 0) { + ++count; + inblanks = false; + } + } else { + if (strchr(delims, *s) != 0) { + inblanks = true; + } + } + } - return (count); + return (count); } -static char* get_input(void) -{ - // Set up the prompt - char input_prompt[] = PROMPT; - if (!settings.prompt) - input_prompt[0] = '\0'; +static char *get_input(void) { + // Set up the prompt + char input_prompt[] = PROMPT; + if (!settings.prompt) { + input_prompt[0] = '\0'; + } - // Print a blank line - printf("\n"); + // Print a blank line + printf("\n"); - char* input = ""; - for (;;) { - input = myreadline(input_prompt); + char *input; + for (;;) { + input = myreadline(input_prompt); - if (input == NULL) // Got EOF; return with it. - return (input); - if (input[0] == '#') { // Ignore comments. - free(input); - continue; - } - // We have a 'normal' line; leave the loop. - break; - } + if (input == NULL) { // Got EOF; return with it. + return (input); + } + if (input[0] == '#') { // Ignore comments. + free(input); + continue; + } + // We have a 'normal' line; leave the loop. + break; + } - // Strip trailing newlines from the input - input[strcspn(input, "\n")] = 0; + // Strip trailing newlines from the input + input[strcspn(input, "\n")] = 0; - add_history(input); + add_history(input); - if (!isatty(0)) - echo_input(stdout, input_prompt, input); + if (!isatty(0)) { + echo_input(stdout, input_prompt, input); + } - if (settings.logfp) - echo_input(settings.logfp, "", input); + if (settings.logfp) { + echo_input(settings.logfp, "", input); + } - return (input); + return (input); } -bool silent_yes_or_no(void) -{ - bool outcome = false; +bool silent_yes_or_no(void) { + bool outcome = false; - for (;;) { - char* reply = get_input(); - if (reply == NULL) { - // LCOV_EXCL_START - // Should be unreachable. Reply should never be NULL - free(reply); - exit(EXIT_SUCCESS); - // LCOV_EXCL_STOP - } - if (strlen(reply) == 0) { - free(reply); - rspeak(PLEASE_ANSWER); - continue; - } + for (;;) { + char *reply = get_input(); + if (reply == NULL) { + // LCOV_EXCL_START + // Should be unreachable. Reply should never be NULL + free(reply); + exit(EXIT_SUCCESS); + // LCOV_EXCL_STOP + } + if (strlen(reply) == 0) { + free(reply); + rspeak(PLEASE_ANSWER); + continue; + } - char* firstword = (char*) xcalloc(strlen(reply) + 1); - sscanf(reply, "%s", firstword); + char *firstword = (char *)xcalloc(strlen(reply) + 1); + sscanf(reply, "%s", firstword); - free(reply); + free(reply); - for (int i = 0; i < (int)strlen(firstword); ++i) - firstword[i] = tolower(firstword[i]); + for (int i = 0; i < (int)strlen(firstword); ++i) { + firstword[i] = tolower(firstword[i]); + } - int yes = strncmp("yes", firstword, sizeof("yes") - 1); - int y = strncmp("y", firstword, sizeof("y") - 1); - int no = strncmp("no", firstword, sizeof("no") - 1); - int n = strncmp("n", firstword, sizeof("n") - 1); + int yes = strncmp("yes", firstword, sizeof("yes") - 1); + int y = strncmp("y", firstword, sizeof("y") - 1); + int no = strncmp("no", firstword, sizeof("no") - 1); + int n = strncmp("n", firstword, sizeof("n") - 1); - free(firstword); + free(firstword); - if (yes == 0 || y == 0) { - outcome = true; - break; - } else if (no == 0 || n == 0) { - outcome = false; - break; - } else - rspeak(PLEASE_ANSWER); - } - return (outcome); + if (yes == 0 || y == 0) { + outcome = true; + break; + } else if (no == 0 || n == 0) { + outcome = false; + break; + } else { + rspeak(PLEASE_ANSWER); + } + } + return (outcome); } +bool yes_or_no(const char *question, const char *yes_response, + const char *no_response) { + /* Print message X, wait for yes/no answer. If yes, print Y and return + * true; if no, print Z and return false. */ + bool outcome = false; -bool yes_or_no(const char* question, const char* yes_response, const char* no_response) -/* Print message X, wait for yes/no answer. If yes, print Y and return true; - * if no, print Z and return false. */ -{ - bool outcome = false; + for (;;) { + speak(question); - for (;;) { - speak(question); + char *reply = get_input(); + if (reply == NULL) { + // LCOV_EXCL_START + // Should be unreachable. Reply should never be NULL + free(reply); + exit(EXIT_SUCCESS); + // LCOV_EXCL_STOP + } - char* reply = get_input(); - if (reply == NULL) { - // LCOV_EXCL_START - // Should be unreachable. Reply should never be NULL - free(reply); - exit(EXIT_SUCCESS); - // LCOV_EXCL_STOP - } + if (strlen(reply) == 0) { + free(reply); + rspeak(PLEASE_ANSWER); + continue; + } - if (strlen(reply) == 0) { - free(reply); - rspeak(PLEASE_ANSWER); - continue; - } + char *firstword = (char *)xcalloc(strlen(reply) + 1); + sscanf(reply, "%s", firstword); - char* firstword = (char*) xcalloc(strlen(reply) + 1); - sscanf(reply, "%s", firstword); + free(reply); - free(reply); + for (int i = 0; i < (int)strlen(firstword); ++i) { + firstword[i] = tolower(firstword[i]); + } - for (int i = 0; i < (int)strlen(firstword); ++i) - firstword[i] = tolower(firstword[i]); + int yes = strncmp("yes", firstword, sizeof("yes") - 1); + int y = strncmp("y", firstword, sizeof("y") - 1); + int no = strncmp("no", firstword, sizeof("no") - 1); + int n = strncmp("n", firstword, sizeof("n") - 1); - int yes = strncmp("yes", firstword, sizeof("yes") - 1); - int y = strncmp("y", firstword, sizeof("y") - 1); - int no = strncmp("no", firstword, sizeof("no") - 1); - int n = strncmp("n", firstword, sizeof("n") - 1); + free(firstword); - free(firstword); + if (yes == 0 || y == 0) { + speak(yes_response); + outcome = true; + break; + } else if (no == 0 || n == 0) { + speak(no_response); + outcome = false; + break; + } else { + rspeak(PLEASE_ANSWER); + } + } - if (yes == 0 || - y == 0) { - speak(yes_response); - outcome = true; - break; - } else if (no == 0 || - n == 0) { - speak(no_response); - outcome = false; - break; - } else - rspeak(PLEASE_ANSWER); - - } - - return (outcome); + return (outcome); } -/* Data structure routines */ +/* Data structure routines */ -static int get_motion_vocab_id(const char* word) -// Return the first motion number that has 'word' as one of its words. -{ - for (int i = 0; i < NMOTIONS; ++i) { - for (int j = 0; j < motions[i].words.n; ++j) { - if (strncasecmp(word, motions[i].words.strs[j], TOKLEN) == 0 && (strlen(word) > 1 || - strchr(ignore, word[0]) == NULL || - !settings.oldstyle)) - return (i); - } - } - // If execution reaches here, we didn't find the word. - return (WORD_NOT_FOUND); +static int get_motion_vocab_id(const char *word) { + // Return the first motion number that has 'word' as one of its words. + for (int i = 0; i < NMOTIONS; ++i) { + for (int j = 0; j < motions[i].words.n; ++j) { + if (strncasecmp(word, motions[i].words.strs[j], + TOKLEN) == 0 && + (strlen(word) > 1 || + strchr(ignore, word[0]) == NULL || + !settings.oldstyle)) { + return (i); + } + } + } + // If execution reaches here, we didn't find the word. + return (WORD_NOT_FOUND); } -static int get_object_vocab_id(const char* word) -// Return the first object number that has 'word' as one of its words. -{ - for (int i = 0; i < NOBJECTS + 1; ++i) { // FIXME: the + 1 should go when 1-indexing for objects is removed - for (int j = 0; j < objects[i].words.n; ++j) { - if (strncasecmp(word, objects[i].words.strs[j], TOKLEN) == 0) - return (i); - } - } - // If execution reaches here, we didn't find the word. - return (WORD_NOT_FOUND); +static int get_object_vocab_id(const char *word) { + // Return the first object number that has 'word' as one of its words. + for (int i = 0; i < NOBJECTS + 1; + ++i) { // FIXME: the + 1 should go when 1-indexing for objects is + // removed + for (int j = 0; j < objects[i].words.n; ++j) { + if (strncasecmp(word, objects[i].words.strs[j], + TOKLEN) == 0) { + return (i); + } + } + } + // If execution reaches here, we didn't find the word. + return (WORD_NOT_FOUND); } -static int get_action_vocab_id(const char* word) -// Return the first motion number that has 'word' as one of its words. -{ - for (int i = 0; i < NACTIONS; ++i) { - for (int j = 0; j < actions[i].words.n; ++j) { - if (strncasecmp(word, actions[i].words.strs[j], TOKLEN) == 0 && (strlen(word) > 1 || - strchr(ignore, word[0]) == NULL || - !settings.oldstyle)) - return (i); - } - } - // If execution reaches here, we didn't find the word. - return (WORD_NOT_FOUND); +static int get_action_vocab_id(const char *word) { + // Return the first motion number that has 'word' as one of its words. + for (int i = 0; i < NACTIONS; ++i) { + for (int j = 0; j < actions[i].words.n; ++j) { + if (strncasecmp(word, actions[i].words.strs[j], + TOKLEN) == 0 && + (strlen(word) > 1 || + strchr(ignore, word[0]) == NULL || + !settings.oldstyle)) { + return (i); + } + } + } + // If execution reaches here, we didn't find the word. + return (WORD_NOT_FOUND); } -static bool is_valid_int(const char *str) -/* Returns true if the string passed in is represents a valid integer, - * that could then be parsed by atoi() */ -{ - // Handle negative number - if (*str == '-') - ++str; +static bool is_valid_int(const char *str) { + /* Returns true if the string passed in is represents a valid integer, + * that could then be parsed by atoi() */ + // Handle negative number + if (*str == '-') { + ++str; + } - // Handle empty string or just "-". Should never reach this - // point, because this is only used with transitive verbs. - if (!*str) - return false; // LCOV_EXCL_LINE + // Handle empty string or just "-". Should never reach this + // point, because this is only used with transitive verbs. + if (!*str) { + return false; // LCOV_EXCL_LINE + } - // Check for non-digit chars in the rest of the stirng. - while (*str) { - if (!isdigit(*str)) - return false; - else - ++str; - } + // Check for non-digit chars in the rest of the string. + while (*str) { + if (!isdigit(*str)) { + return false; + } else { + ++str; + } + } - return true; + return true; } -static void get_vocab_metadata(const char* word, vocab_t* id, word_type_t* type) -{ - /* Check for an empty string */ - if (strncmp(word, "", sizeof("")) == 0) { - *id = WORD_EMPTY; - *type = NO_WORD_TYPE; - return; - } +static void get_vocab_metadata(const char *word, vocab_t *id, + word_type_t *type) { + /* Check for an empty string */ + if (strncmp(word, "", sizeof("")) == 0) { + *id = WORD_EMPTY; + *type = NO_WORD_TYPE; + return; + } - vocab_t ref_num; + vocab_t ref_num; - ref_num = get_motion_vocab_id(word); - if (ref_num != WORD_NOT_FOUND) { - *id = ref_num; - *type = MOTION; - return; - } + ref_num = get_motion_vocab_id(word); + // Second conjunct is because the magic-word placeholder is a bit + // special + if (ref_num != WORD_NOT_FOUND) { + *id = ref_num; + *type = MOTION; + return; + } - ref_num = get_object_vocab_id(word); - if (ref_num != WORD_NOT_FOUND) { - *id = ref_num; - *type = OBJECT; - return; - } + ref_num = get_object_vocab_id(word); + if (ref_num != WORD_NOT_FOUND) { + *id = ref_num; + *type = OBJECT; + return; + } - ref_num = get_action_vocab_id(word); - if (ref_num != WORD_NOT_FOUND) { - *id = ref_num; - *type = ACTION; - return; - } + ref_num = get_action_vocab_id(word); + if (ref_num != WORD_NOT_FOUND && ref_num != PART) { + *id = ref_num; + *type = ACTION; + return; + } - // Check for the reservoir magic word. - if (strcasecmp(word, game.zzword) == 0) { - *id = PART; - *type = ACTION; - return; - } + // Check for the reservoir magic word. + if (strcasecmp(word, game.zzword) == 0) { + *id = PART; + *type = ACTION; + return; + } - // Check words that are actually numbers. - if (is_valid_int(word)) { - *id = WORD_EMPTY; - *type = NUMERIC; - return; - } + // Check words that are actually numbers. + if (is_valid_int(word)) { + *id = WORD_EMPTY; + *type = NUMERIC; + return; + } - *id = WORD_NOT_FOUND; - *type = NO_WORD_TYPE; - return; + *id = WORD_NOT_FOUND; + *type = NO_WORD_TYPE; + return; } -static void tokenize(char* raw, command_t *cmd) -{ - /* - * Be caereful about modifing this. We do not want to nuke the - * the speech part or ID from the previous turn. - */ - memset(&cmd->word[0].raw, '\0', sizeof(cmd->word[0].raw)); - memset(&cmd->word[1].raw, '\0', sizeof(cmd->word[1].raw)); +static void tokenize(char *raw, command_t *cmd) { + /* + * Be careful about modifying this. We do not want to nuke the + * the speech part or ID from the previous turn. + */ + memset(&cmd->word[0].raw, '\0', sizeof(cmd->word[0].raw)); + memset(&cmd->word[1].raw, '\0', sizeof(cmd->word[1].raw)); - /* Bound prefix on the %s would be needed to prevent buffer - * overflow. but we shortstop this more simply by making each - * raw-input buffer as int as the entire input buffer. */ - sscanf(raw, "%s%s", cmd->word[0].raw, cmd->word[1].raw); + /* Bound prefix on the %s would be needed to prevent buffer + * overflow. We shortstop this more simply by making each + * raw-input buffer as long as the entire input buffer. */ + sscanf(raw, "%s%s", cmd->word[0].raw, cmd->word[1].raw); - /* (ESR) In oldstyle mode, simulate the uppercasing and truncating - * effect on raw tokens of packing them into sixbit characters, 5 - * to a 32-bit word. This is something the FORTRAN version did - * becuse archaic FORTRAN had no string types. Don Wood's - * mechanical translation of 2.5 to C retained the packing and - * thus this misfeature. - * - * It's philosophically questionable whether this is the right - * thing to do even in oldstyle mode. On one hand, the text - * mangling was not authorial intent, but a result of limitations - * in their tools. On the other, not simulating this misbehavior - * goes against the goal of making oldstyle as accurate as - * possible an emulation of the original UI. - */ - if (settings.oldstyle) { - cmd->word[0].raw[TOKLEN + TOKLEN] = cmd->word[1].raw[TOKLEN + TOKLEN] = '\0'; - for (size_t i = 0; i < strlen(cmd->word[0].raw); i++) - cmd->word[0].raw[i] = toupper(cmd->word[0].raw[i]); - for (size_t i = 0; i < strlen(cmd->word[1].raw); i++) - cmd->word[1].raw[i] = toupper(cmd->word[1].raw[i]); - } + /* (ESR) In oldstyle mode, simulate the uppercasing and truncating + * effect on raw tokens of packing them into sixbit characters, 5 + * to a 32-bit word. This is something the FORTRAN version did + * because archaic FORTRAN had no string types. Don Wood's + * mechanical translation of 2.5 to C retained the packing and + * thus this misfeature. + * + * It's philosophically questionable whether this is the right + * thing to do even in oldstyle mode. On one hand, the text + * mangling was not authorial intent, but a result of limitations + * in their tools. On the other, not simulating this misbehavior + * goes against the goal of making oldstyle as accurate as + * possible an emulation of the original UI. + * + * The definition of TRUNCLEN is dubious. It accurately reflects the + * FORTRAN, but it's possible that was a bug and the proper definition + * is (TOKLEN). + */ +#define TRUNCLEN (TOKLEN + TOKLEN) + if (settings.oldstyle) { + cmd->word[0].raw[TRUNCLEN] = cmd->word[1].raw[TRUNCLEN] = '\0'; + for (size_t i = 0; i < strlen(cmd->word[0].raw); i++) { + cmd->word[0].raw[i] = toupper(cmd->word[0].raw[i]); + } + for (size_t i = 0; i < strlen(cmd->word[1].raw); i++) { + cmd->word[1].raw[i] = toupper(cmd->word[1].raw[i]); + } + } - /* populate command with parsed vocabulary metadata */ - get_vocab_metadata(cmd->word[0].raw, &(cmd->word[0].id), &(cmd->word[0].type)); - get_vocab_metadata(cmd->word[1].raw, &(cmd->word[1].id), &(cmd->word[1].type)); - cmd->state = TOKENIZED; + /* populate command with parsed vocabulary metadata */ + get_vocab_metadata(cmd->word[0].raw, &(cmd->word[0].id), + &(cmd->word[0].type)); + get_vocab_metadata(cmd->word[1].raw, &(cmd->word[1].id), + &(cmd->word[1].type)); + cmd->state = TOKENIZED; } -bool get_command_input(command_t *command) -/* Get user input on stdin, parse and map to command */ -{ - char inputbuf[LINESIZE]; - char* input; +bool get_command_input(command_t *command) { + /* Get user input on stdin, parse and map to command */ + char inputbuf[LINESIZE]; + char *input; - for (;;) { - input = get_input(); - if (input == NULL) - return false; - if (word_count(input) > 2) { - rspeak(TWO_WORDS); - free(input); - continue; - } - if (strcmp(input, "") != 0) - break; - free(input); - } + for (;;) { + input = get_input(); + if (input == NULL) { + return false; + } + if (word_count(input) > 2) { + rspeak(TWO_WORDS); + free(input); + continue; + } + if (strcmp(input, "") != 0) { + break; + } + free(input); + } - strncpy(inputbuf, input, LINESIZE - 1); - free(input); + strncpy(inputbuf, input, LINESIZE - 1); + free(input); - tokenize(inputbuf, command); + tokenize(inputbuf, command); #ifdef GDEBUG - /* Needs to stay synced with enum word_type_t */ - const char *types[] = {"NO_WORD_TYPE", "MOTION", "OBJECT", "ACTION", "NUMERIC"}; - /* needs to stay synced with enum speechpart */ - const char *roles[] = {"unknown", "intransitive", "transitive"}; - printf("Command: role = %s type1 = %s, id1 = %d, type2 = %s, id2 = %d\n", - roles[command->part], - types[command->word[0].type], - command->word[0].id, - types[command->word[1].type], - command->word[1].id); + /* Needs to stay synced with enum word_type_t */ + const char *types[] = {"NO_WORD_TYPE", "MOTION", "OBJECT", "ACTION", + "NUMERIC"}; + /* needs to stay synced with enum speechpart */ + const char *roles[] = {"unknown", "intransitive", "transitive"}; + printf( + "Command: role = %s type1 = %s, id1 = %d, type2 = %s, id2 = %d\n", + roles[command->part], types[command->word[0].type], + command->word[0].id, types[command->word[1].type], + command->word[1].id); #endif - command->state = GIVEN; - return true; + command->state = GIVEN; + return true; } -void clear_command(command_t *cmd) -/* Resets the state of the command to empty */ -{ - cmd->verb = ACT_NULL; - cmd->part = unknown; - game.oldobj = cmd->obj; - cmd->obj = NO_OBJECT; - cmd->state = EMPTY; +void clear_command(command_t *cmd) { + /* Resets the state of the command to empty */ + cmd->verb = ACT_NULL; + cmd->part = unknown; + game.oldobj = cmd->obj; + cmd->obj = NO_OBJECT; + cmd->state = EMPTY; } -void juggle(obj_t object) -/* Juggle an object by picking it up and putting it down again, the purpose - * being to get the object to the front of the chain of things at its loc. */ -{ - loc_t i, j; +void juggle(obj_t object) { + /* Juggle an object by picking it up and putting it down again, the + * purpose being to get the object to the front of the chain of things + * at its loc. */ + loc_t i, j; - i = game.place[object]; - j = game.fixed[object]; - move(object, i); - move(object + NOBJECTS, j); + i = game.objects[object].place; + j = game.objects[object].fixed; + move(object, i); + move(object + NOBJECTS, j); } -void move(obj_t object, loc_t where) -/* Place any object anywhere by picking it up and dropping it. May - * already be toting, in which case the carry is a no-op. Mustn't - * pick up objects which are not at any loc, since carry wants to - * remove objects from game.atloc chains. */ -{ - loc_t from; +void move(obj_t object, loc_t where) { + /* Place any object anywhere by picking it up and dropping it. May + * already be toting, in which case the carry is a no-op. Mustn't + * pick up objects which are not at any loc, since carry wants to + * remove objects from game atloc chains. */ + loc_t from; - if (object > NOBJECTS) - from = game.fixed[object - NOBJECTS]; - else - from = game.place[object]; - /* (ESR) Used to check for !SPECIAL(from). I *think* that was wrong... */ - if (from != LOC_NOWHERE && from != CARRIED) - carry(object, from); - drop(object, where); + if (object > NOBJECTS) { + from = game.objects[object - NOBJECTS].fixed; + } else { + from = game.objects[object].place; + } + /* (ESR) Used to check for !SPECIAL(from). I *think* that was wrong... + */ + if (from != LOC_NOWHERE && from != CARRIED) { + carry(object, from); + } + drop(object, where); } -loc_t put(obj_t object, loc_t where, int pval) -/* put() is the same as move(), except it returns a value used to set up the - * negated game.prop values for the repository objects. */ -{ - move(object, where); - return STASHED(pval); +void put(obj_t object, loc_t where, int pval) { + /* put() is the same as move(), except the object is stashed and + * can no longer be picked up. */ + move(object, where); + OBJECT_STASHIFY(object, pval); +#ifdef OBJECT_SET_SEEN + OBJECT_SET_SEEN(object); +#endif } -void carry(obj_t object, loc_t where) -/* Start toting an object, removing it from the list of things at its former - * location. Incr holdng unless it was already being toted. If object>NOBJECTS - * (moving "fixed" second loc), don't change game.place or game.holdng. */ -{ - int temp; +void carry(obj_t object, loc_t where) { + /* Start toting an object, removing it from the list of things at its + * former location. Incr holdng unless it was already being toted. If + * object>NOBJECTS (moving "fixed" second loc), don't change game.place + * or game.holdng. */ + int temp; - if (object <= NOBJECTS) { - if (game.place[object] == CARRIED) - return; - game.place[object] = CARRIED; + if (object <= NOBJECTS) { + if (game.objects[object].place == CARRIED) { + return; + } + game.objects[object].place = CARRIED; - if (object != BIRD) - ++game.holdng; - } - if (game.atloc[where] == object) { - game.atloc[where] = game.link[object]; - return; - } - temp = game.atloc[where]; - while (game.link[temp] != object) { - temp = game.link[temp]; - } - game.link[temp] = game.link[object]; + /* + * Without this conditional your inventory is overcounted + * when you pick up the bird while it's caged. This fixes + * a cosmetic bug in the original. + * + * Possibly this check should be skipped whwn oldstyle is on. + */ + if (object != BIRD) { + ++game.holdng; + } + } + if (game.locs[where].atloc == object) { + game.locs[where].atloc = game.link[object]; + return; + } + temp = game.locs[where].atloc; + while (game.link[temp] != object) { + temp = game.link[temp]; + } + game.link[temp] = game.link[object]; } -void drop(obj_t object, loc_t where) -/* Place an object at a given loc, prefixing it onto the game.atloc list. Decr - * game.holdng if the object was being toted. No state change on the object. */ -{ - if (object > NOBJECTS) - game.fixed[object - NOBJECTS] = where; - else { - if (game.place[object] == CARRIED) - if (object != BIRD) - /* The bird has to be weightless. This ugly hack (and the - * corresponding code in the drop function) brought to you - * by the fact that when the bird is caged, we need to be able - * to either 'take bird' or 'take cage' and have the right thing - * happen. - */ - --game.holdng; - game.place[object] = where; - } - if (where == LOC_NOWHERE || - where == CARRIED) - return; - game.link[object] = game.atloc[where]; - game.atloc[where] = object; +void drop(obj_t object, loc_t where) { + /* Place an object at a given loc, prefixing it onto the game atloc + * list. Decr game.holdng if the object was being toted. No state + * change on the object. */ + if (object > NOBJECTS) { + game.objects[object - NOBJECTS].fixed = where; + } else { + if (game.objects[object].place == CARRIED) { + if (object != BIRD) { + /* The bird has to be weightless. This ugly + * hack (and the corresponding code in the carry + * function) brought to you by the fact that + * when the bird is caged, we need to be able to + * either 'take bird' or 'take cage' and have + * the right thing happen. + */ + --game.holdng; + } + } + game.objects[object].place = where; + } + if (where == LOC_NOWHERE || where == CARRIED) { + return; + } + game.link[object] = game.locs[where].atloc; + game.locs[where].atloc = object; } -int atdwrf(loc_t where) -/* Return the index of first dwarf at the given location, zero if no dwarf is - * there (or if dwarves not active yet), -1 if all dwarves are dead. Ignore - * the pirate (6th dwarf). */ -{ - int at; +int atdwrf(loc_t where) { + /* Return the index of first dwarf at the given location, zero if no + * dwarf is there (or if dwarves not active yet), -1 if all dwarves are + * dead. Ignore the pirate (6th dwarf). */ + int at; - at = 0; - if (game.dflag < 2) - return at; - at = -1; - for (int i = 1; i <= NDWARVES - 1; i++) { - if (game.dloc[i] == where) - return i; - if (game.dloc[i] != 0) - at = 0; - } - return at; + at = 0; + if (game.dflag < 2) { + return at; + } + at = -1; + for (int i = 1; i <= NDWARVES - 1; i++) { + if (game.dwarves[i].loc == where) { + return i; + } + if (game.dwarves[i].loc != 0) { + at = 0; + } + } + return at; } /* Utility routines (setbit, tstbit, set_seed, get_next_lcg_value, * randrange) */ -int setbit(int bit) -/* Returns 2**bit for use in constructing bit-masks. */ -{ - return (1L << bit); +int setbit(int bit) { + /* Returns 2**bit for use in constructing bit-masks. */ + return (1L << bit); } -bool tstbit(int mask, int bit) -/* Returns true if the specified bit is set in the mask. */ -{ - return (mask & (1 << bit)) != 0; +bool tstbit(int mask, int bit) { + /* Returns true if the specified bit is set in the mask. */ + return (mask & (1 << bit)) != 0; } -void set_seed(int32_t seedval) -/* Set the LCG seed */ -{ - game.lcg_x = seedval % LCG_M; - if (game.lcg_x < 0) { - game.lcg_x = LCG_M + game.lcg_x; - } - // once seed is set, we need to generate the Z`ZZZ word - for (int i = 0; i < 5; ++i) { - game.zzword[i] = 'A' + randrange(26); - } - game.zzword[1] = '\''; // force second char to apostrophe - game.zzword[5] = '\0'; +void set_seed(int32_t seedval) { + /* Set the LCG1 seed */ + game.lcg_x = seedval % LCG_M; + if (game.lcg_x < 0) { + game.lcg_x = LCG_M + game.lcg_x; + } + // once seed is set, we need to generate the Z`ZZZ word + for (int i = 0; i < 5; ++i) { + game.zzword[i] = 'A' + randrange(26); + } + game.zzword[1] = '\''; // force second char to apostrophe + game.zzword[5] = '\0'; } -static int32_t get_next_lcg_value(void) -/* Return the LCG's current value, and then iterate it. */ -{ - int32_t old_x = game.lcg_x; - game.lcg_x = (LCG_A * game.lcg_x + LCG_C) % LCG_M; - return old_x; +static int32_t get_next_lcg_value(void) { + /* Return the LCG's current value, and then iterate it. */ + int32_t old_x = game.lcg_x; + game.lcg_x = (LCG_A * game.lcg_x + LCG_C) % LCG_M; + if (settings.debug) { + printf("# random %d\n", old_x); // LCOV_EXCL_LINE + } + return old_x; } -int32_t randrange(int32_t range) -/* Return a random integer from [0, range). */ -{ - return range * get_next_lcg_value() / LCG_M; +int32_t randrange(int32_t range) { + /* Return a random integer from [0, range). */ + return range * get_next_lcg_value() / LCG_M; } // LCOV_EXCL_START -void bug(enum bugtype num, const char *error_string) -{ - fprintf(stderr, "Fatal error %d, %s.\n", num, error_string); - exit(EXIT_FAILURE); +void bug(enum bugtype num, const char *error_string) { + fprintf(stderr, "Fatal error %d, %s.\n", num, error_string); + exit(EXIT_FAILURE); } // LCOV_EXCL_STOP -void state_change(obj_t obj, int state) -/* Object must have a change-message list for this to be useful; only some do */ -{ - game.prop[obj] = state; - pspeak(obj, change, true, state); +void state_change(obj_t obj, int state) { + /* Object must have a change-message list for this to be useful; only + * some do */ + game.objects[obj].prop = state; + pspeak(obj, change, true, state); } /* end */ diff --git a/notes.adoc b/notes.adoc index 68ea03d..a247e49 100644 --- a/notes.adoc +++ b/notes.adoc @@ -1,5 +1,7 @@ = Open Adventure Maintainer's Notes = by Eric S. Raymond +// SPDX-FileCopyrightText: (C) Eric S. Raymond +// SPDX-License-Identifier: CC-BY-4.0 In which we explain what has been done to this code since Don Woods authorized us to ship it under an open-source license. There's a @@ -13,7 +15,9 @@ the game; Jason signed on early in the process to help. The assistance of Peje Nilsson in restructuring some particularly grotty gotos is gratefully acknowledged. Petr Voropaev contributed fuzz testing and code cleanups. Aaron Traas did a lot of painstaking work to improve -test coverage, and factored out the last handful of gotos. +test coverage, and factored out the last handful of gotos. Ryan +Sarson nudged us into fixing a longstanding minor bug in the +handling of incorrect magic-word sequences, == Nomenclature == @@ -41,6 +45,11 @@ form that is (a) readable, and (b) friendly to forward translation to future languages. It has already survived a move from FORTRAN to C; a future as a Python or Go translation seems possible, even probable. +Compatibility with the 2.5 source we found has been checked by +building a version patched minimally to support the seed command and +running it against the entire test suite, which has 100% code +coverage. + == Functional changes == Bug fixes: @@ -51,10 +60,13 @@ Bug fixes: * Oyster was readable after first gotten even when not carried. -* Behavior when saying the giant's magic words outside his room wasn't - quite correct - the game responded as though the player were in - the room ("...can't you read?"). The new message is "Nothing happens." - +* Response to an attempt to unlock the oyster while carrying it was incorrect. + +* Behavior when saying the giant's magic words before having seen them + wasn't quite correct - the game responded as though the player had + already read them ("...can't you read?"). The new message is "Well, + that was remarkably pointless!" The -o option reverts this change. + * Attempting to extinguish an unlit urn caused it to lose its oil. * "A crystal bridge now spans the fissure." (progressive present) was @@ -62,18 +74,16 @@ Bug fixes: bridge spans the fissure." (timeless present). * A few minor typos have been corrected: absence of capitalization on - "Swiss" and "Persian", inconsistent selling of "imbedded" vs. "embedded", - "eying" for "eyeing". "thresholds" for "threshholds". + "Swiss" and "Persian", inconsistent spelling of "imbedded" vs. "embedded", + "eying" for "eyeing", "thresholds" for "threshholds", "pencilled" + for "penciled". * Under odd circumstances (dropping rug or vase outdoors) the game could - say "floor" when it should say "ground" (or "dirt", or something). + formerly say "floor" when it should say "ground" (or "dirt", or + something). -Bugs (accidental changes that don't seem worth the effort to fix): - -* Commands that are not moves (e.g. "look" and "inven") can be used - during fee fie fo foo without breaking recognition of the sequence. - -* Bird starts uncaged in the endgame. +* The "knives vanish" message could formerly be emitted when "I see no + knife here." would be appropriate. Enhancements: @@ -98,6 +108,7 @@ that random events (dwarf & pirate appearances, the bird's magic word) will be reproducible. A "version" command has been added. This has no effect on gameplay. + The text displayed by the "news" command has been updated. A -l command-line option has been added. When this is given (with a @@ -121,11 +132,28 @@ FORTRAN-derived code that formerly implemented the save/restore functions; without C's fread(3)/fwrite() and structs it was necessarily pretty ugly by modern standards. Encryption and checksumming have been discarded - it's pointless to try -tamper-proofing saves when everyone has the source code. +tamper-proofing saves when everyone has the source code. However +the game still integrity-checks savefiles on resume, including an +abort if the endianness of the restoring machine does not match that of +the saving machine. There is a magic-cookie header on the saves so +in theory they could be identified by programs like file(1). + +Save and resume filenames are stripped of leading and trailing +whitespace before processing. A -r command-line option has been added. When it is given (with a file path argument) it is functionally equivalent to a RESTORE command. +An -a command-line option has been added (conditionally on +ADVENT_AUTOSAVE) for use in BBS door systems. When this option is +given, the game roads from the specified filename argument on startup +and saves to it on quit or a received signal. There is a new nmessage +to inform the user about this. + +The game can be built in a mode that entirely disables save/resume +(-DADVENT_NOSAVE). If the game had been built this way, a diagnostic is +emitted if you try to save or resume. + == Translation == The 2.5 code was a mechanical C translation of a FORTRAN original. @@ -162,6 +190,9 @@ afl (American Fuzzy Lop). We've found and fixed some crashers in our new code (which occasionally uses malloc(3)), but none as yet in Don's old code (which didn't). +After version 1.11, correctness was carefully checked against the +behavior of a binary from before the big refactoring. + The code falls short of being fully modern C in the following ways: @@ -192,7 +223,7 @@ messages with the objects that conceptually own them. We consider this project finished. All issues and TODOs have been cleared, behavior has been carefully checked against original ADVENT, no future demand for new features is expected, and the test suite has -100% code coverage. If the toolchain bit-rots out from under it, -we will fix that. +100% code coverage. If new bugs appear as the toolchain bit-rots out +from under underneath, we will fix those problems. // end diff --git a/saveresume.c b/saveresume.c index b5205d3..1c778ef 100644 --- a/saveresume.c +++ b/saveresume.c @@ -4,254 +4,264 @@ * (ESR) This replaces a bunch of particularly nasty FORTRAN-derived code; * see the history.adoc file in the source distribution for discussion. * - * Copyright (c) 1977, 2005 by Will Crowther and Don Woods - * Copyright (c) 2017 by Eric S. Raymond - * SPDX-License-Identifier: BSD-2-clause + * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods + * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include #include #include #include -#include #include "advent.h" -#include "dungeon.h" /* - * Bump on save format change. - * - * Note: Verify that the tests run clean before bumping this, then rebuild the check - * files afterwards. Otherwise you will get a spurious failure due to the old version - * having been generated into a check file. + * Use this to detect endianness mismatch. Can't be unchanged by byte-swapping. */ -#define VRSION 29 +#define ENDIAN_MAGIC 2317 -/* - * If you change the first three members, the resume function may not properly - * reject saves from older versions. Yes, this glues us to a hardware- - * dependent length of int. Later members can change, but bump the version - * when you do that. - */ -struct save_t { - int64_t savetime; - int32_t mode; /* not used, must be present for version detection */ - int32_t version; - struct game_t game; -}; struct save_t save; -#define IGNORE(r) do{if (r){}}while(0) +#define IGNORE(r) \ + do { \ + if (r) { \ + } \ + } while (0) -int savefile(FILE *fp, int32_t version) -/* Save game to file. No input or output from user. */ -{ - save.savetime = time(NULL); - save.mode = -1; - save.version = (version == 0) ? VRSION : version; - - save.game = game; - IGNORE(fwrite(&save, sizeof(struct save_t), 1, fp)); - return (0); +int savefile(FILE *fp) { + /* Save game to file. No input or output from user. */ + memcpy(&save.magic, ADVENT_MAGIC, sizeof(ADVENT_MAGIC)); + if (save.version == 0) { + save.version = SAVE_VERSION; + } + if (save.canary == 0) { + save.canary = ENDIAN_MAGIC; + } + save.game = game; + IGNORE(fwrite(&save, sizeof(struct save_t), 1, fp)); + return (0); } /* Suspend and resume */ -int suspend(void) -{ - /* Suspend. Offer to save things in a file, but charging - * some points (so can't win by using saved games to retry - * battles or to start over after learning zzword). - * If ADVENT_NOSAVE is defined, do nothing instead. */ -#ifdef ADVENT_NOSAVE - return GO_UNKNOWN; -#endif - FILE *fp = NULL; +static char *strip(char *name) { + // Trim leading whitespace + while (isspace((unsigned char)*name)) { + name++; // LCOV_EXCL_LINE + } + if (*name != '\0') { + // Trim trailing whitespace; + // might be left there by autocomplete + char *end = name + strlen(name) - 1; + while (end > name && isspace((unsigned char)*end)) { + end--; + } + // Write new null terminator character + end[1] = '\0'; + } - rspeak(SUSPEND_WARNING); - if (!yes_or_no(arbitrary_messages[THIS_ACCEPTABLE], arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN])) - return GO_CLEAROBJ; - game.saved = game.saved + 5; - - while (fp == NULL) { - char* name = myreadline("\nFile name: "); - if (name == NULL) - return GO_TOP; - fp = fopen(name, WRITE_MODE); - if (fp == NULL) - printf("Can't open file %s, try again.\n", name); - free(name); - } - - savefile(fp, VRSION); - fclose(fp); - rspeak(RESUME_HELP); - exit(EXIT_SUCCESS); + return name; } -int resume(void) -{ - /* Resume. Read a suspended game back from a file. - * If ADVENT_NOSAVE is defined, do nothing instead. */ +int suspend(void) { + /* Suspend. Offer to save things in a file, but charging + * some points (so can't win by using saved games to retry + * battles or to start over after learning zzword). + * If ADVENT_NOSAVE is defined, gripe instead. */ -#ifdef ADVENT_NOSAVE - return GO_UNKNOWN; +#if defined ADVENT_NOSAVE || defined ADVENT_AUTOSAVE + rspeak(SAVERESUME_DISABLED); + return GO_TOP; #endif - FILE *fp = NULL; + FILE *fp = NULL; - if (game.loc != 1 || - game.abbrev[1] != 1) { - rspeak(RESUME_ABANDON); - if (!yes_or_no(arbitrary_messages[THIS_ACCEPTABLE], arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN])) - return GO_CLEAROBJ; - } + rspeak(SUSPEND_WARNING); + if (!yes_or_no(arbitrary_messages[THIS_ACCEPTABLE], + arbitrary_messages[OK_MAN], + arbitrary_messages[OK_MAN])) { + return GO_CLEAROBJ; + } + game.saved = game.saved + 5; - while (fp == NULL) { - char* name = myreadline("\nFile name: "); - // Autocomplete can leave the input with an extra trailing space. - if (name != NULL && strlen(name) > 0 && name[strlen(name) - 1] == ' ') - name[strlen(name) - 1] = '\0'; - if (name == NULL) - return GO_TOP; - fp = fopen(name, READ_MODE); - if (fp == NULL) - printf("Can't open file %s, try again.\n", name); - free(name); - } + while (fp == NULL) { + char *name = myreadline("\nFile name: "); + if (name == NULL) { + return GO_TOP; + } + name = strip(name); + if (strlen(name) == 0) { + return GO_TOP; // LCOV_EXCL_LINE + } + fp = fopen(strip(name), WRITE_MODE); + if (fp == NULL) { + printf("Can't open file %s, try again.\n", name); + } + free(name); + } - return restore(fp); + savefile(fp); + fclose(fp); + rspeak(RESUME_HELP); + exit(EXIT_SUCCESS); } -int restore(FILE* fp) -{ - /* Read and restore game state from file, assuming - * sane initial state. - * If ADVENT_NOSAVE is defined, do nothing instead. */ -#ifdef ADVENT_NOSAVE - return GO_UNKNOWN; -#endif +int resume(void) { + /* Resume. Read a suspended game back from a file. + * If ADVENT_NOSAVE is defined, gripe instead. */ - IGNORE(fread(&save, sizeof(struct save_t), 1, fp)); - fclose(fp); - if (save.version != VRSION) { - rspeak(VERSION_SKEW, save.version / 10, MOD(save.version, 10), VRSION / 10, MOD(VRSION, 10)); - } else if (is_valid(save.game)) { - game = save.game; - } - return GO_TOP; +#if defined ADVENT_NOSAVE || defined ADVENT_AUTOSAVE + rspeak(SAVERESUME_DISABLED); + return GO_TOP; +#endif + FILE *fp = NULL; + + if (game.loc != LOC_START || game.locs[LOC_START].abbrev != 1) { + rspeak(RESUME_ABANDON); + if (!yes_or_no(arbitrary_messages[THIS_ACCEPTABLE], + arbitrary_messages[OK_MAN], + arbitrary_messages[OK_MAN])) { + return GO_CLEAROBJ; + } + } + + while (fp == NULL) { + char *name = myreadline("\nFile name: "); + if (name == NULL) { + return GO_TOP; + } + name = strip(name); + if (strlen(name) == 0) { + return GO_TOP; // LCOV_EXCL_LINE + } + fp = fopen(name, READ_MODE); + if (fp == NULL) { + printf("Can't open file %s, try again.\n", name); + } + free(name); + } + + return restore(fp); } -bool is_valid(struct game_t valgame) -{ - /* Save files can be roughly grouped into three groups: - * With valid, reaceable state, with valid, but unreachable - * state and with invaild state. We check that state is - * valid: no states are outside minimal or maximal value - */ +int restore(FILE *fp) { + /* Read and restore game state from file, assuming + * sane initial state. + * If ADVENT_NOSAVE is defined, gripe instead. */ +#ifdef ADVENT_NOSAVE + rspeak(SAVERESUME_DISABLED); + return GO_TOP; +#endif - /* Prevent division by zero */ - if (valgame.abbnum == 0) { - return false; // LCOV_EXCL_LINE - } + IGNORE(fread(&save, sizeof(struct save_t), 1, fp)); + fclose(fp); + if (memcmp(save.magic, ADVENT_MAGIC, sizeof(ADVENT_MAGIC)) != 0 || + save.canary != ENDIAN_MAGIC) { + rspeak(BAD_SAVE); + } else if (save.version != SAVE_VERSION) { + rspeak(VERSION_SKEW, save.version / 10, MOD(save.version, 10), + SAVE_VERSION / 10, MOD(SAVE_VERSION, 10)); + } else if (!is_valid(save.game)) { + rspeak(SAVE_TAMPERING); + exit(EXIT_SUCCESS); + } else { + game = save.game; + } + return GO_TOP; +} - /* Check for RNG overflow. Truncate */ - if (valgame.lcg_x >= LCG_M) { - valgame.lcg_x %= LCG_M; // LCOV_EXCL_LINE - } +bool is_valid(struct game_t valgame) { + /* Save files can be roughly grouped into three groups: + * With valid, reachable state, with valid, but unreachable + * state and with invalid state. We check that state is + * valid: no states are outside minimal or maximal value + */ - /* Check for RNG underflow. Transpose */ - if (valgame.lcg_x < LCG_M) { - valgame.lcg_x = LCG_M + (valgame.lcg_x % LCG_M); - } + /* Prevent division by zero */ + if (valgame.abbnum == 0) { + return false; // LCOV_EXCL_LINE + } - /* Bounds check for locations */ - if ( valgame.chloc < -1 || valgame.chloc > NLOCATIONS || - valgame.chloc2 < -1 || valgame.chloc2 > NLOCATIONS || - valgame.loc < 0 || valgame.loc > NLOCATIONS || - valgame.newloc < 0 || valgame.newloc > NLOCATIONS || - valgame.oldloc < 0 || valgame.oldloc > NLOCATIONS || - valgame.oldlc2 < 0 || valgame.oldlc2 > NLOCATIONS) { - return false; // LCOV_EXCL_LINE - } - /* Bounds check for location arrays */ - for (int i = 0; i <= NDWARVES; i++) { - if (valgame.dloc[i] < -1 || valgame.dloc[i] > NLOCATIONS || - valgame.odloc[i] < -1 || valgame.odloc[i] > NLOCATIONS) { - return false; // LCOV_EXCL_LINE - } - } + /* Check for RNG overflow. Truncate */ + if (valgame.lcg_x >= LCG_M) { + return false; + } - for (int i = 0; i <= NOBJECTS; i++) { - if (valgame.place[i] < -1 || valgame.place[i] > NLOCATIONS || - valgame.fixed[i] < -1 || valgame.fixed[i] > NLOCATIONS) { - return false; // LCOV_EXCL_LINE - } - } + /* Bounds check for locations */ + if (valgame.chloc < -1 || valgame.chloc > NLOCATIONS || + valgame.chloc2 < -1 || valgame.chloc2 > NLOCATIONS || + valgame.loc < 0 || valgame.loc > NLOCATIONS || valgame.newloc < 0 || + valgame.newloc > NLOCATIONS || valgame.oldloc < 0 || + valgame.oldloc > NLOCATIONS || valgame.oldlc2 < 0 || + valgame.oldlc2 > NLOCATIONS) { + return false; // LCOV_EXCL_LINE + } + /* Bounds check for location arrays */ + for (int i = 0; i <= NDWARVES; i++) { + if (valgame.dwarves[i].loc < -1 || + valgame.dwarves[i].loc > NLOCATIONS || + valgame.dwarves[i].oldloc < -1 || + valgame.dwarves[i].oldloc > NLOCATIONS) { + return false; // LCOV_EXCL_LINE + } + } - /* Bounds check for dwarves */ - if (valgame.dtotal < 0 || valgame.dtotal > NDWARVES || - valgame.dkill < 0 || valgame.dkill > NDWARVES) { - return false; // LCOV_EXCL_LINE - } + for (int i = 0; i <= NOBJECTS; i++) { + if (valgame.objects[i].place < -1 || + valgame.objects[i].place > NLOCATIONS || + valgame.objects[i].fixed < -1 || + valgame.objects[i].fixed > NLOCATIONS) { + return false; // LCOV_EXCL_LINE + } + } - /* Validate that we didn't die too many times in save */ - if (valgame.numdie >= NDEATHS) { - return false; // LCOV_EXCL_LINE - } + /* Bounds check for dwarves */ + if (valgame.dtotal < 0 || valgame.dtotal > NDWARVES || + valgame.dkill < 0 || valgame.dkill > NDWARVES) { + return false; // LCOV_EXCL_LINE + } - /* Recalculate tally, throw the towel if in disagreement */ - int temp_tally = 0; - for (int treasure = 1; treasure <= NOBJECTS; treasure++) { - if (objects[treasure].is_treasure) { - if (valgame.prop[treasure] == STATE_NOTFOUND) { - ++temp_tally; - } - } - } - if (temp_tally != valgame.tally) { - return false; // LCOV_EXCL_LINE - } + /* Validate that we didn't die too many times in save */ + if (valgame.numdie >= NDEATHS) { + return false; // LCOV_EXCL_LINE + } - /* Check that properties of objects aren't beyond expected */ - for (obj_t obj = 0; obj <= NOBJECTS; obj++) { - if (valgame.prop[obj] < STATE_NOTFOUND || valgame.prop[obj] > 1) { - switch (obj) { - case RUG: - case DRAGON: - case BIRD: - case BOTTLE: - case PLANT: - case PLANT2: - case TROLL: - case URN: - case EGGS: - case VASE: - case CHAIN: - if (valgame.prop[obj] == 2) // There are multiple different states, but it's convenient to clump them together - continue; - /* FALLTHRU */ - case BEAR: - if (valgame.prop[BEAR] == CONTENTED_BEAR || valgame.prop[BEAR] == BEAR_DEAD) - continue; - /* FALLTHRU */ - default: - return false; // LCOV_EXCL_LINE - } - } - } + /* Recalculate tally, throw the towel if in disagreement */ + int temp_tally = 0; + for (int treasure = 1; treasure <= NOBJECTS; treasure++) { + if (objects[treasure].is_treasure) { + if (OBJECT_IS_NOTFOUND2(valgame, treasure)) { + ++temp_tally; + } + } + } + if (temp_tally != valgame.tally) { + return false; // LCOV_EXCL_LINE + } - /* Check that values in linked lists for objects in locations are inside bounds */ - for (loc_t loc = LOC_NOWHERE; loc <= NLOCATIONS; loc++) { - if (valgame.atloc[loc] < NO_OBJECT || valgame.atloc[loc] > NOBJECTS * 2) { - return false; // LCOV_EXCL_LINE - } - } - for (obj_t obj = 0; obj <= NOBJECTS * 2; obj++ ) { - if (valgame.link[obj] < NO_OBJECT || valgame.link[obj] > NOBJECTS * 2) { - return false; // LCOV_EXCL_LINE - } - } + /* Check that properties of objects aren't beyond expected */ + for (obj_t obj = 0; obj <= NOBJECTS; obj++) { + if (PROP_IS_INVALID(valgame.objects[obj].prop)) { + return false; // LCOV_EXCL_LINE + } + } - return true; + /* Check that values in linked lists for objects in locations are inside + * bounds */ + for (loc_t loc = LOC_NOWHERE; loc <= NLOCATIONS; loc++) { + if (valgame.locs[loc].atloc < NO_OBJECT || + valgame.locs[loc].atloc > NOBJECTS * 2) { + return false; // LCOV_EXCL_LINE + } + } + for (obj_t obj = 0; obj <= NOBJECTS * 2; obj++) { + if (valgame.link[obj] < NO_OBJECT || + valgame.link[obj] > NOBJECTS * 2) { + return false; // LCOV_EXCL_LINE + } + } + + return true; } /* end */ diff --git a/score.c b/score.c index 7e6b28c..ad1c00a 100644 --- a/score.c +++ b/score.c @@ -1,142 +1,162 @@ /* * Scoring and wrap-up. * - * Copyright (c) 1977, 2005 by Will Crowther and Don Woods - * Copyright (c) 2017 by Eric S. Raymond - * SPDX-License-Identifier: BSD-2-clause + * SPDX-FileCopyrightText: (C) 1977, 2005 by Will Crowther and Don Woods + * SPDX-License-Identifier: BSD-2-Clause */ -#include #include "advent.h" #include "dungeon.h" +#include -static int mxscor; /* ugh..the price for having score() not exit. */ +static int mxscor; /* ugh..the price for having score() not exit. */ -int score(enum termination mode) -/* mode is 'scoregame' if scoring, 'quitgame' if quitting, 'endgame' if died - * or won */ -{ - int score = 0; +int score(enum termination mode) { + /* mode is 'scoregame' if scoring, 'quitgame' if quitting, 'endgame' if + * died or won */ + int score = 0; - /* The present scoring algorithm is as follows: - * Objective: Points: Present total possible: - * Getting well into cave 25 25 - * Each treasure < chest 12 60 - * Treasure chest itself 14 14 - * Each treasure > chest 16 224 - * Surviving (MAX-NUM)*10 30 - * Not quitting 4 4 - * Reaching "game.closng" 25 25 - * "Closed": Quit/Killed 10 - * Klutzed 25 - * Wrong way 30 - * Success 45 45 - * Came to Witt's End 1 1 - * Round out the total 2 2 - * TOTAL: 430 - * Points can also be deducted for using hints or too many turns, or for - * saving intermediate positions. */ + /* The present scoring algorithm is as follows: + * Objective: Points: Present total possible: + * Getting well into cave 25 25 + * Each treasure < chest 12 60 + * Treasure chest itself 14 14 + * Each treasure > chest 16 224 + * Surviving (MAX-NUM)*10 30 + * Not quitting 4 4 + * Reaching "game.closng" 25 25 + * "Closed": Quit/Killed 10 + * Klutzed 25 + * Wrong way 30 + * Success 45 45 + * Came to Witt's End 1 1 + * Round out the total 2 2 + * TOTAL: 430 + * Points can also be deducted for using hints or too many turns, or + * for saving intermediate positions. */ - /* First tally up the treasures. Must be in building and not broken. - * Give the poor guy 2 points just for finding each treasure. */ - mxscor = 0; - for (int i = 1; i <= NOBJECTS; i++) { - if (!objects[i].is_treasure) - continue; - if (objects[i].inventory != 0) { - int k = 12; - if (i == CHEST) - k = 14; - if (i > CHEST) - k = 16; - if (game.prop[i] > STATE_NOTFOUND) - score += 2; - if (game.place[i] == LOC_BUILDING && game.prop[i] == STATE_FOUND) - score += k - 2; - mxscor += k; - } - } + /* First tally up the treasures. Must be in building and not broken. + * Give the poor guy 2 points just for finding each treasure. */ + mxscor = 0; + for (int i = 1; i <= NOBJECTS; i++) { + if (!objects[i].is_treasure) { + continue; + } + if (objects[i].inventory != 0) { + int k = 12; + if (i == CHEST) { + k = 14; + } + if (i > CHEST) { + k = 16; + } + if (!OBJECT_IS_STASHED(i) && !OBJECT_IS_NOTFOUND(i)) { + score += 2; + } + if (game.objects[i].place == LOC_BUILDING && + OBJECT_IS_FOUND(i)) { + score += k - 2; + } + mxscor += k; + } + } - /* Now look at how he finished and how far he got. NDEATHS and - * game.numdie tell us how well he survived. game.dflag will tell us - * if he ever got suitably deep into the cave. game.closng still - * indicates whether he reached the endgame. And if he got as far as - * "cave closed" (indicated by "game.closed"), then bonus is zero for - * mundane exits or 133, 134, 135 if he blew it (so to speak). */ - score += (NDEATHS - game.numdie) * 10; - mxscor += NDEATHS * 10; - if (mode == endgame) - score += 4; - mxscor += 4; - if (game.dflag != 0) - score += 25; - mxscor += 25; - if (game.closng) - score += 25; - mxscor += 25; - if (game.closed) { - if (game.bonus == none) - score += 10; - if (game.bonus == splatter) - score += 25; - if (game.bonus == defeat) - score += 30; - if (game.bonus == victory) - score += 45; - } - mxscor += 45; + /* Now look at how he finished and how far he got. NDEATHS and + * game.numdie tell us how well he survived. game.dflag will tell us + * if he ever got suitably deep into the cave. game.closng still + * indicates whether he reached the endgame. And if he got as far as + * "cave closed" (indicated by "game.closed"), then bonus is zero for + * mundane exits or 133, 134, 135 if he blew it (so to speak). */ + score += (NDEATHS - game.numdie) * 10; + mxscor += NDEATHS * 10; + if (mode == endgame) { + score += 4; + } + mxscor += 4; + if (game.dflag != 0) { + score += 25; + } + mxscor += 25; + if (game.closng) { + score += 25; + } + mxscor += 25; + if (game.closed) { + if (game.bonus == none) { + score += 10; + } + if (game.bonus == splatter) { + score += 25; + } + if (game.bonus == defeat) { + score += 30; + } + if (game.bonus == victory) { + score += 45; + } + } + mxscor += 45; - /* Did he come to Witt's End as he should? */ - if (game.place[MAGAZINE] == LOC_WITTSEND) - score += 1; - mxscor += 1; + /* Did he come to Witt's End as he should? */ + if (game.objects[MAGAZINE].place == LOC_WITTSEND) { + score += 1; + } + mxscor += 1; - /* Round it off. */ - score += 2; - mxscor += 2; + /* Round it off. */ + score += 2; + mxscor += 2; - /* Deduct for hints/turns/saves. Hints < 4 are special; see database desc. */ - for (int i = 0; i < NHINTS; i++) { - if (game.hinted[i]) - score = score - hints[i].penalty; - } - if (game.novice) - score -= 5; - if (game.clshnt) - score -= 10; - score = score - game.trnluz - game.saved; + /* Deduct for hints/turns/saves. Hints < 4 are special; see database + * desc. */ + for (int i = 0; i < NHINTS; i++) { + if (game.hints[i].used) { + score = score - hints[i].penalty; + } + } + if (game.novice) { + score -= 5; + } + if (game.clshnt) { + score -= 10; + } + score = score - game.trnluz - game.saved; - /* Return to score command if that's where we came from. */ - if (mode == scoregame) { - rspeak(GARNERED_POINTS, score, mxscor, game.turns, game.turns); - } + /* Return to score command if that's where we came from. */ + if (mode == scoregame) { + rspeak(GARNERED_POINTS, score, mxscor, game.turns, game.turns); + } - return score; + return score; } -void terminate(enum termination mode) -/* End of game. Let's tell him all about it. */ -{ - int points = score(mode); +void terminate(enum termination mode) { + /* End of game. Let's tell him all about it. */ + int points = score(mode); +#if defined ADVENT_AUTOSAVE + autosave(); +#endif - if (points + game.trnluz + 1 >= mxscor && game.trnluz != 0) - rspeak(TOOK_LONG); - if (points + game.saved + 1 >= mxscor && game.saved != 0) - rspeak(WITHOUT_SUSPENDS); - rspeak(TOTAL_SCORE, points, mxscor, game.turns, game.turns); - for (int i = 1; i <= (int)NCLASSES; i++) { - if (classes[i].threshold >= points) { - speak(classes[i].message); - if (i < (int)NCLASSES) { - int nxt = classes[i].threshold + 1 - points; - rspeak(NEXT_HIGHER, nxt, nxt); - } else { - rspeak(NO_HIGHER); - } - exit(EXIT_SUCCESS); - } - } - rspeak(OFF_SCALE); - exit(EXIT_SUCCESS); + if (points + game.trnluz + 1 >= mxscor && game.trnluz != 0) { + rspeak(TOOK_LONG); + } + if (points + game.saved + 1 >= mxscor && game.saved != 0) { + rspeak(WITHOUT_SUSPENDS); + } + rspeak(TOTAL_SCORE, points, mxscor, game.turns, game.turns); + for (int i = 1; i <= (int)NCLASSES; i++) { + if (classes[i].threshold >= points) { + speak(classes[i].message); + if (i < (int)NCLASSES) { + int nxt = classes[i].threshold + 1 - points; + rspeak(NEXT_HIGHER, nxt, nxt); + } else { + rspeak(NO_HIGHER); + } + exit(EXIT_SUCCESS); + } + } + rspeak(OFF_SCALE); + exit(EXIT_SUCCESS); } /* end */ diff --git a/templates/coverage_dungeon.html.tpl b/templates/coverage_dungeon.html.tpl index 372c43f..e9f7f74 100644 --- a/templates/coverage_dungeon.html.tpl +++ b/templates/coverage_dungeon.html.tpl @@ -1,3 +1,7 @@ +<-- +SPDX-FileCopyrightText: Copyright Eric S. Raymond +SPDX-License-Identifier: BSD-2-Clause +--> diff --git a/templates/dungeon.c.tpl b/templates/dungeon.c.tpl index dbc28a6..03640d5 100644 --- a/templates/dungeon.c.tpl +++ b/templates/dungeon.c.tpl @@ -1,3 +1,8 @@ +/* +SPDX-FileCopyrightText: Copyright Eric S. Raymond +SPDX-License-Identifier: BSD-2-Clause +*/ + #include "{h_file}" const char* arbitrary_messages[] = {{ @@ -48,4 +53,7 @@ const travelop_t travel[] = {{ const char *ignore = "{ignore}"; -/* end */ \ No newline at end of file +/* Dwarf starting locations */ +const int dwarflocs[NDWARVES] = {{{dwarflocs}}}; + +/* end */ diff --git a/templates/dungeon.h.tpl b/templates/dungeon.h.tpl index e42ab07..cf637c8 100644 --- a/templates/dungeon.h.tpl +++ b/templates/dungeon.h.tpl @@ -1,3 +1,7 @@ +/* +SPDX-FileCopyrightText: Copyright Eric S. Raymond +SPDX-License-Identifier: BSD-2-Clause +*/ #ifndef DUNGEON_H #define DUNGEON_H @@ -31,6 +35,9 @@ #define COND_HOGRE 20 /* Trying to deal with ogre */ #define COND_HJADE 21 /* Found all treasures except jade */ +#define NDWARVES {ndwarflocs} // number of dwarves +extern const int dwarflocs[NDWARVES]; + typedef struct {{ const char** strs; const int n; diff --git a/tests/Makefile b/tests/Makefile index d188e70..997096a 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,4 +1,7 @@ -# Test-suite makefile for opeb-adventure +# Test-suite makefile for open-adventure + +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Use absolute path so tests that change working directory still use # scripts from parent directory. Note that using $PWD seems to fail @@ -29,14 +32,12 @@ TESTLOADS := $(shell ls -1 *.log | sed '/.log/s///' | sort) check: savecheck @make tap | tapview - @echo "=== No diff output is good news." @-advent -x 2>/dev/null || exit 0 # Get usage message into coverage tests - @-advent -l /dev/null /dev/null .SUFFIXES: .chk clean: - rm -fr *~ adventure.text *.adv scratch.tmp + rm -fr *~ *.adv scratch.tmp *.ochk advent430 adventure.data # Show summary lines for all tests. testlist: @@ -47,39 +48,50 @@ listcheck: done # Generate bogus savegames. -savegames: - @$(ECHO) "cheat: Generate save file with -900 deaths" +cheat_numdie.adv: @$(PARDIR)/cheat -d -900 -o cheat_numdie.adv > /tmp/cheat_numdie - @$(ECHO) "cheat: Generate save file with -1000 deaths" +cheat_numdie1000.adv: @$(PARDIR)/cheat -d -1000 -o cheat_numdie1000.adv > /tmp/cheat_numdie1000 - @$(ECHO) "cheat: Generate save file with version -1337" +cheat_savetamper.adv: + @$(PARDIR)/cheat -d 2000 -o cheat_savetamper.adv > /tmp/cheat_savetamper +resume_badversion.adv: @$(PARDIR)/cheat -v -1337 -o resume_badversion.adv > /tmp/cheat_badversion - @$(ECHO) "cheat: Generate save file 1000 saves" +thousand_saves.adv: @$(PARDIR)/cheat -s -1000 -o thousand_saves.adv > /tmp/cheat_1000saves - @$(ECHO) "cheat: Generate save file 1000 turns" - @$(PARDIR)/cheat -t -1000 -o thousand_saves.adv > /tmp/cheat_1000turns - @$(ECHO) "cheat: Generate save file 1000 turns" - @$(PARDIR)/cheat -l -1000 -o thousand_lamp.adv > /tmp/cheat_1000lamp - @rm -f /tmp/cheat* +thousand_turns.adv: + @$(PARDIR)/cheat -t -1000 -o thousand_turns.adv > /tmp/cheat_1000turns +thousand_limit.adv: + @$(PARDIR)/cheat -l -1000 -o thousand_limit.adv > /tmp/cheat_1000limit +SGAMES = cheat_numdie.adv cheat_numdie1000.adv cheat_savetamper.adv resume_badversion.adv \ + thousand_saves.adv thousand_turns.adv thousand_limit.adv # Force coverage of cheat edgecases -savecheck: savegames - @$(ECHO) "TEST cheat: Bogus option for save file generation" +scheck1: @$(PARDIR)/cheat -QqQ 2> /tmp/coverage_cheat_batopt | true - @$(ECHO) "TEST cheat: No save file specified" + @./outcheck.sh "cheat: bogus option for save file generation" +scheck2: @$(PARDIR)/cheat 2>/dev/null | true - @$(ECHO) "TEST cheat: Fail to save because we omit -o" + @./outcheck.sh "cheat: No save file specified" +scheck3: @$(PARDIR)/cheat -d 1 2> /tmp/coverage_cheat_nooutput | true - @$(ECHO) "TEST cheat: Fail to save to invalid path" + @./outcheck.sh "cheat: doesn't save because we omit -o" +scheck4: @$(PARDIR)/cheat -o / 2> /tmp/coverage_cheat_badoutput | true - @$(ECHO) "TEST advent: Start with invalid file with -r" - @advent -r /badfilename < pitfall.log > /tmp/coverage_advent_readfail 2>&1 || exit 1 - @$(ECHO) "TEST advent: Start with invalid file with -l" - @advent -l / < pitfall.log > /tmp/coverage_advent_logfail 2>&1 || exit 1 - @$(ECHO) "TEST advent: Test -r with valid input" - @advent -r thousand_saves.adv < pitfall.log > /tmp/coverage_advent_readfail 2>&1 || exit 1 - @rm -f /tmp/coverage* + @./outcheck.sh "cheat: doesn't save to invalid path" +scheck5: + @$(advent) -r /badfilename < pitfall.log > /tmp/coverage_advent_readfail 2>&1 || exit 1 + @./outcheck.sh "cheat: doesn't start with invalid file with -r" +scheck6: + @$(advent) -l / < pitfall.log > /tmp/coverage_advent_logfail 2>&1 || exit 1 + @./outcheck.sh "cheat: doesn't start with invalid file passed to -l" +scheck7: + @$(advent) -r thousand_saves.adv < pitfall.log > /tmp/coverage_advent_readfail 2>&1 || exit 1 + @./outcheck.sh "test -r with valid input" +SCHECKS = scheck1 scheck2 scheck3 scheck4 scheck5 scheck6 scheck7 +# Don't run this from here, you'll get cryptic warnings and no good result +# if the advent binary wasn't built with coverage flags. Do "make clean coverage" +# from the top-level directory. coverage: check lcov -t "advent" -o $(PARDIR)/advent.info -c -d $(PARDIR) --gcov-tool=$(GCOV) genhtml -o $(PARDIR)/coverage/ $(PARDIR)/advent.info @@ -93,22 +105,73 @@ buildchecks: savegames OPTS=`sed -n /#options:/s///p <$${file}.log`; \ advent $$OPTS <$${file}.log >$${file}.chk 2>&1 || exit 1; \ done; \ - echo "inven" | advent isofoo.log /dev/stdin >multifile.chk + echo "inven" | advent issue36.log /dev/stdin >multifile.chk; \ rm -f scratch.tmp RUN_TARGETS=$(TESTLOADS:%=run-regress-%) $(RUN_TARGETS): run-regress-%: %.log @(test=$(<:.log=); legend=$$(sed -n '/^## /s///p' <"$<" 2>/dev/null || echo "(no description)"); \ OPTS=`sed -n /#options:/s///p $<`; \ - $(advent) $$OPTS <$< | tapdiffer "$<: $${legend}" "$${test}.chk") + $(advent) $$OPTS <$< | tapdiffer "$${test}: $${legend}" "$${test}.chk") multifile-regress: - @(echo "inven" | advent isofoo.log /dev/stdin) | tapdiffer "multifile: multiple-file test" multifile.chk + @(echo "inven" | advent issue36.log /dev/stdin) | tapdiffer "multifile: multiple-file test" multifile.chk -TEST_TARGETS = $(RUN_TARGETS) multifile-regress +TEST_TARGETS = $(SCHECKS) $(RUN_TARGETS) multifile-regress -tap: count $(TEST_TARGETS) +tap: count $(SGAMES) $(TEST_TARGETS) + @rm -f scratch.tmp /tmp/coverage* /tmp/cheat* count: @echo 1..$(words $(TEST_TARGETS)) +# The following machinery tests the game against a binary made from +# the advent430 branch To use it, switch to that branch, build the +# binary, run it once to generate adventure.data, then switch back to +# master leaving advent430 and adventure.data in place (make clean +# does not remove them). +# +# make clean # Clean up object files, laving a bare source tree +# git checkout advent430 # Check out the advent430 branch +# make # Build the advent430 binary +# advent430 # Run it. Answer the novice question and quit +# make clean # Remove .o files +# git checkout master # Go back to master branch +# make # Rebuild advent. +# +# The diff file produced has corrected spellings in it. That's what oldfilter +# is for, to massage out the original spellings and avoid noise diffs. +# Diffs in amount of whitespace and trailing whitespace are ignored +# +# A magic comment of NOCOMPARE in a log file excludes it from this comparison. +# making it a skipped test in the TAP view. First use of this was to avoid a +# spurious mismatch on the news text. Other uses avoid spurious mismatches due +# to bug fixes. +# +# When adding more tests, bear in mind that any game that continues after a +# resurrection will need a NOCOMPARE. At some point in the forward port, +# resurrection was accidentally changed in a way that messed with the LCG chain. +# +# The *.chk files need not be up-to-date for this to work. +# +TAPFILTER=tapview +oldcompare: + @if [ -f ../advent430 ]; then cp ../advent430 ../adventure.data .; else echo "advent430 nonexistent"; exit 1; fi + @-(for x in *.log; do \ + stem=$${x%.log}; \ + legend=$$(sed -n '/^## /s///p' <$$x 2>/dev/null || echo "(no description)"); \ + if grep NOCOMPARE $$x >/dev/null; \ + then echo "not ok - $${stem}.ochk: $${legend} # SKIP"; \ + else \ + ./advent430 <$${stem}.log | oldfilter >$${stem}.ochk; \ + ../advent <$${stem}.log >$${stem}.log-new; \ + ./newfilter <$${stem}.log-new | tapdiffer -b "$${stem}: $${legend}" $${stem}.ochk; \ + fi; \ + done; \ + echo 1..$(words $(shell ls *.log))) | $(TAPFILTER) + @rm *.ochk *-new advent430 adventure.data + +# List all NOCOMPARE tests. +residuals: + @grep -n NOCOMPARE *.log + # end diff --git a/tests/README b/tests/README index 1118056..d84dfd4 100644 --- a/tests/README +++ b/tests/README @@ -17,9 +17,9 @@ To see summary lines from all tests, 'make testlist'. The summary lines are those led with ##; you should have one such descriptive line at the head of each file. -To run the tests, "make regress". +To run the tests, "make check". -To remake the check files, "make buildregress". +To remake the check files, "make buildchecks". == Composing tests == @@ -29,12 +29,8 @@ option giving a log path. Commands will be captured to that log. To re-use a command sequence from an existing log, run advent and paste it to the advent command log from the clipboard. -To see where we can use more tests, have a look at our test coverage -report: +To see where we can use more tests, "make coverage". -http://esr.gitlab.io/open-adventure/coverage/ - -Improvements in test coverage are always welcome. // end diff --git a/tests/axebear.chk b/tests/axebear.chk index 7cff028..ba465c2 100644 --- a/tests/axebear.chk +++ b/tests/axebear.chk @@ -1631,7 +1631,7 @@ There is a little axe lying beside the bear. The bear is locked to the wall with a golden chain! - +> You scored 251 out of a possible 430, using 271 turns. You have reached "Junior Master" status. diff --git a/tests/axebear.log b/tests/axebear.log index 8498b21..1788008 100644 --- a/tests/axebear.log +++ b/tests/axebear.log @@ -1,4 +1,6 @@ ## Observe axe after throwing at bear +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in diff --git a/tests/axeorama.chk b/tests/axeorama.chk index 741ac50..fbb1d7b 100644 --- a/tests/axeorama.chk +++ b/tests/axeorama.chk @@ -652,7 +652,7 @@ mist. A notice posted on the bridge reads, "Stop! Pay troll!" A burly troll stands by the bridge and insists you throw him a treasure before you may cross. - +> You scored 105 out of a possible 430, using 109 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/axeorama.log b/tests/axeorama.log index 7d2e273..a6def76 100644 --- a/tests/axeorama.log +++ b/tests/axeorama.log @@ -1,4 +1,6 @@ ## Test throwing axe at non-dwarves. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Added coverage of LOC_DEADCRAWL and CROSS_BRIDGE n seed 1838473132 diff --git a/tests/isofoo.chk b/tests/badmagic.chk similarity index 76% rename from tests/isofoo.chk rename to tests/badmagic.chk index 1c0cbaf..08c8ffe 100644 --- a/tests/isofoo.chk +++ b/tests/badmagic.chk @@ -7,11 +7,14 @@ 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. -> foo +> resume +Can't open file y, try again. -Nothing happens. +Oops, that does not look like a valid save file. +You're in front of building. +> You scored 32 out of a possible 430, using 1 turn. You are obviously a rank amateur. Better luck next time. diff --git a/tests/badmagic.log b/tests/badmagic.log new file mode 100644 index 0000000..6a1f056 --- /dev/null +++ b/tests/badmagic.log @@ -0,0 +1,8 @@ +## Resume from filename withoy the right magic at the front +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE advent430 doesn't have this test +n +resume +y +../main.o diff --git a/tests/barehands.chk b/tests/barehands.chk index 38b0c05..f94685f 100644 --- a/tests/barehands.chk +++ b/tests/barehands.chk @@ -307,7 +307,7 @@ The blood-specked body of a huge green dead dragon lies to one side. For crying out loud, the poor thing is already dead! - +> You scored 77 out of a possible 430, using 49 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/barehands.log b/tests/barehands.log index 72fc3de..cb5ad99 100644 --- a/tests/barehands.log +++ b/tests/barehands.log @@ -1,4 +1,7 @@ ## Get to dragon, refuse to use bare hands +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE Fails due uninteresting difference in whitespace process. # Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56 n seed 1635997320 diff --git a/tests/bigfail.chk b/tests/bigfail.chk index eebea66..df15f38 100644 --- a/tests/bigfail.chk +++ b/tests/bigfail.chk @@ -1800,746 +1800,12 @@ 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? -> yes - -All right. But don't blame me if something goes wr...... - --- POOF!! --- -You are engulfed in a cloud of orange smoke. Coughing and gasping, -you emerge from the smoke and find.... - -You're inside building. - -There is a richly-carved ebony statuette here! - -There is an emerald here the size of a plover's egg! - -There is a jewel-encrusted trident here! - -There is a platinum pyramid here, 8 inches on a side! - -There are diamonds here! - -There is an enormous ruby here! - -There are bars of silver here! - -There is a Persian rug spread out on the floor! - -There is a large sparkling nugget of gold here! - -A precious jade necklace has been dropped here! - -There is precious jewelry here! - -> inven - -You're not carrying anything. - -> out - -You're in front of building. - -There is a shiny brass lamp nearby. - -> take lamp +> n OK -> light lamp - -Your lamp is now on. - -> in - -You're inside building. - -There is a richly-carved ebony statuette here! - -There is an emerald here the size of a plover's egg! - -There is a jewel-encrusted trident here! - -There is a platinum pyramid here, 8 inches on a side! - -There are diamonds here! - -There is an enormous ruby here! - -There are bars of silver here! - -There is a Persian rug spread out on the floor! - -There is a large sparkling nugget of gold here! - -A precious jade necklace has been dropped here! - -There is precious jewelry here! - -> plugh - ->>Foof!<< - -You're at "Y2". - -> s - -You're in n/s passage above e/w passage. - -> d - -You're in dirty passage. - -> bedquilt - -You're in Bedquilt. - -> n - -You're in large low room. - -> sw - -You're in sloping corridor. - -> up - -You're on sw side of chasm. - -There is a little axe here. - -There are many coins here! - -Off to one side lies a glistening pearl! - -There is a golden chain lying in a heap on the floor! - -The wreckage of a bridge (and a dead bear) can be seen at the bottom -of the chasm. - -The troll is nowhere to be seen. - -> over - -There is no longer any way across the chasm. - -You're on sw side of chasm. - -There is a little axe here. - -There are many coins here! - -Off to one side lies a glistening pearl! - -There is a golden chain lying in a heap on the floor! - -The wreckage of a bridge (and a dead bear) can be seen at the bottom -of the chasm. - -The troll is nowhere to be seen. - -> feed bear - -Don't be ridiculous! - -> attack bear - -For crying out loud, the poor thing is already dead! - -> take coins - -OK - -> take axe - -OK - -> sw - -You're in sloping corridor. - -> d - -You're in large low room. - -> se - -You're in Oriental Room. - -The floor is littered with worthless shards of pottery. - -> se - -You're in Swiss Cheese Room. - -> ne - -You are in Bedquilt, a long east/west passage with holes everywhere. -To explore at random select north, south, up, or down. - -> e - -You're at complex junction. - -> up - -You're in dusty rock room. - -> e - -You're in dirty passage. - -> up - -You are in a low n/s passage at a hole in the floor. The hole goes -down to an e/w passage. - -> s - -You're in Hall of Mt King. - -> up - -You're in Hall of Mists. - -Rough stone steps lead up the dome. - -> w - -You're on east bank of fissure. - -A three foot black rod with a rusty star on an end lies nearby. - -A crystal bridge spans the fissure. - -> take rod - -OK - -> wave rod - -The crystal bridge has vanished! - -> across - -There is no way across the fissure. - -You're on east bank of fissure. - -> wave rod - -A crystal bridge now spans the fissure. - -> across - -You're on west bank of fissure. - -A crystal bridge spans the fissure. - -> w - -You're at west end of Hall of Mists. - -> w - -You're at east end of long hall. - -> w - -You're at west end of long hall. - -> s - -You are in a maze of twisty little passages, all different. - -> s - -You are in a maze of twisting little passages, all different. - -> sw - -You are in a twisting little maze of passages, all different. - -> se - -You are in a twisting maze of little passages, all different. - -> s - -You are in a little maze of twisting passages, all different. - -> s - -Dead end - -There is a massive vending machine here, swung back to reveal a -southward passage. - -> drop coins - -There are fresh batteries here. - -> look - -Sorry, but I am not allowed to give more detail. I will repeat the -long description of your location. - -Dead end - -There are fresh batteries here. - -There is a massive vending machine here, swung back to reveal a -southward passage. - -> n - -Your lamp is getting dim. I'm taking the liberty of replacing the -batteries. - -You are in a little maze of twisting passages, all different. - -> z - -OK - -> z - -Tsk! A wizard wouldn't have to take 350 turns. This is going to cost -you a couple of points. - -OK - -> z - -OK - -> z - -OK - -> s - -Dead end - -Some worn-out batteries have been discarded nearby. - -There is a massive vending machine here, swung back to reveal a -southward passage. - -> take batteries - -OK - -> n - -You are in a little maze of twisting passages, all different. - -> back - -Sorry, but the path twisted and turned so much that I can't figure -out which way to go to get back. - -You are in a little maze of twisting passages, all different. - -> n - -You are in a little maze of twisty passages, all different. - -> nw - -You are in a maze of twisty little passages, all different. - -> d - -You're at west end of long hall. - -> drink - -Drink what? - -> e - -You're at east end of long hall. - -> e - -You're at west end of Hall of Mists. - -> n - -You have crawled through a very low wide passage parallel to and north -of the Hall of Mists. - -You're on west bank of fissure. - -A crystal bridge spans the fissure. - -> w - -You're at west end of Hall of Mists. - -> w - -You're at east end of long hall. - -> n - -You are at a crossover of a high n/s passage and a low e/w one. - -> e - -You're in the west side chamber. - -> d - -There is no way to go that direction. - -You're in the west side chamber. - -> e - -You're in Hall of Mt King. - -> u - -There is a threatening little dwarf in the room with you! - -You're in Hall of Mists. - -Rough stone steps lead up the dome. - -> throw axe - -You killed a little dwarf. - -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. - -There is a little axe here. - -Rough stone steps lead up the dome. - -> d - -You're in Hall of Mt King. - -> n - -You're in n/s passage above e/w passage. - -> d - -You are in a dirty broken passage. To the east is a crawl. To the -west is a large passage. Above you is a hole to another passage. - -> e - -You are on the brink of a small clean climbable pit. A crawl leads -west. - -> d - -You are in the bottom of a small pit with a little stream, which -enters and exits through tiny slits. - -> listen - -The stream is gurgling placidly. - -> u - -You're at brink of small pit. - -> w - -You're in dirty passage. - -> bedquilt - -You're in Bedquilt. - -> z - -OK - -> z - -OK - -> z - -OK - -> n - -You have crawled around in some little holes and wound up back in the -main passage. - -You're in Bedquilt. - -> n - -You have crawled around in some little holes and wound up back in the -main passage. - -You're in Bedquilt. - -> n - -You have crawled around in some little holes and wound up back in the -main passage. - -You're in Bedquilt. - -> n - -You are in a secret canyon at a junction of three canyons, bearing -north, south, and se. The north one is as tall as the other two -combined. - -> w - -There is no way to go that direction. - -You're at junction of three secret canyons. - -> n - -You're at a low window overlooking a huge pit, which extends up out of -sight. A floor is indistinctly visible over 50 feet below. Traces of -white mist cover the floor of the pit, becoming thicker to the left. -Marks in the dust around the window would seem to indicate that -someone has been here recently. Directly across the pit from you and -25 feet away there is a similar window looking into a lighted room. A -shadowy figure can be seen there peering back at you. - -The shadowy figure seems to be trying to attract your attention. - -> w - -You're at junction of three secret canyons. - -> s - -You are in a secret n/s canyon above a sizable passage. - -> s - -A large stalactite extends from the roof and almost reaches the floor -below. You could climb down it, and jump from it to the floor, but -having done so you would be unable to reach it to climb back up. - -> n - -You are in a secret n/s canyon above a sizable passage. - -> s - -There are faint rustling noises from the darkness behind you. - -You're at top of stalactite. - -> n - -You are in a secret n/s canyon above a sizable passage. - -> n - -You're at junction of three secret canyons. - -> n - -You're at window on pit. - -The shadowy figure seems to be trying to attract your attention. - -> jump - -You are at the bottom of the pit with a broken neck. - -You clumsy oaf, you've done it again! I don't know how long I can -keep this up. Do you want me to try reincarnating you again? - -> y - -Okay, now where did I put my orange smoke?.... >POOF!< -Everything disappears in a dense cloud of orange smoke. - -You're inside building. - -There is a richly-carved ebony statuette here! - -There is an emerald here the size of a plover's egg! - -There is a jewel-encrusted trident here! - -There is a platinum pyramid here, 8 inches on a side! - -There are diamonds here! - -There is an enormous ruby here! - -There are bars of silver here! - -There is a Persian rug spread out on the floor! - -There is a large sparkling nugget of gold here! - -A precious jade necklace has been dropped here! - -There is precious jewelry here! - -> out - -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. - -There is a shiny brass lamp nearby. - -> take lamp - -OK - -> in - -You're inside building. - -There is a richly-carved ebony statuette here! - -There is an emerald here the size of a plover's egg! - -There is a jewel-encrusted trident here! - -There is a platinum pyramid here, 8 inches on a side! - -There are diamonds here! - -There is an enormous ruby here! - -There are bars of silver here! - -There is a Persian rug spread out on the floor! - -There is a large sparkling nugget of gold here! - -A precious jade necklace has been dropped here! - -There is precious jewelry here! - -> downstream - -The stream flows out through a pair of 1 foot diameter sewer pipes. -It would be advisable to use the exit. - -You are inside a building, a well house for a large spring. - -There is a richly-carved ebony statuette here! - -There is an emerald here the size of a plover's egg! - -There is a jewel-encrusted trident here! - -There is a platinum pyramid here, 8 inches on a side! - -There are diamonds here! - -There is an enormous ruby here! - -There are bars of silver here! - -There is a Persian rug spread out on the floor! - -There is a large sparkling nugget of gold here! - -A precious jade necklace has been dropped here! - -There is precious jewelry here! - -> plugh - ->>Foof!<< - -It is now pitch dark. If you proceed you will likely fall into a pit. - -> on lamp - -Your lamp is now on. - -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. - -> w - -You're at a low window overlooking a huge pit, which extends up out of -sight. A floor is indistinctly visible over 50 feet below. Traces of -white mist cover the floor of the pit, becoming thicker to the right. -Marks in the dust around the window would seem to indicate that -someone has been here recently. Directly across the pit from you and -25 feet away there is a similar window looking into a lighted room. A -shadowy figure can be seen there peering back at you. - -The shadowy figure seems to be trying to attract your attention. - -> e - -You're at "Y2". - -> s - -You're in n/s passage above e/w passage. - -> s - -You're in Hall of Mt King. - -> e - -You're in Hall of Mists. - -There is a little axe here. - -Rough stone steps lead up the dome. - -> take axe - -OK - -> listen - -The wind whistles coldly past your ears. - -> score - -You have garnered 207 out of a possible 430 points, using 413 turns. - -> inven - -You are currently holding the following: -Brass lantern -Dwarf's axe - -> waste 2443 - -Game limit is now 27 - -You're in Hall of Mists. - -Rough stone steps lead up the dome. - -> z - -Your lamp is getting dim, and you're out of spare batteries. You'd -best start wrapping this up. - -OK - - -You scored 207 out of a possible 430, using 416 turns. +You scored 223 out of a possible 430, using 303 turns. You may now consider yourself a "Seasoned Adventurer". -To achieve the next higher rating, you need 44 more points. +To achieve the next higher rating, you need 28 more points. diff --git a/tests/bigfail.log b/tests/bigfail.log index a839f1c..8de6118 100644 --- a/tests/bigfail.log +++ b/tests/bigfail.log @@ -1,4 +1,6 @@ ## Test many nonlethal failure conditions +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # See comments in this log n seed 1838473132 @@ -310,141 +312,5 @@ sw free bear take bear sw -yes -inven -out -take lamp -light lamp -in -plugh -s -d -bedquilt n -sw -up -over -feed bear -attack bear -# We'll need these when the game times out -take coins -take axe -# Back to fissure to vanish the bridge and then test OVER -sw -d -se -se -ne -e -up -e -up -s -up -w -take rod -wave rod -across -# Next, buy batteries but don't take them. -wave rod -across -w -w -w -s -s -sw -se -s -s -drop coins -look -n -z -z -z -z -# Battery warning happens here. -s -take batteries -# We now have 2500 more turns of life. Into the maze... -n -# Show that trying to back up in the maze fails -back -n -nw -d -# Out of maze. Drink where nothing is eligible. -drink -e -e -# PARALLEL1 coverage -n -# If we go to hall of mists we'll meet a killer dwarf with the drop on us -#e -#e -w -w -n -e -d -e -u -throw axe -d -n -d -# Coverage of LOC_SMALLPIT and LOC_SMALLPITBRINK -e -d -listen -u -w -# Coverage of LOC_THREEJUNCTION, LOC_WINDOW2, LOC_SECRET2, LOC_TOPSTALACTITE, -# LOC_NECKBROKE. Only accessible via stalactite from big maze or by random -# exit from Bedquilt. -bedquilt -z -z -z -n -n -n -n -# In secret canyon -w -n -w -s -# LOC_TOPSTALACTITE -s -n -s -n -n -n -jump -y -# Reincarnation, cover LOC_SEWER -out -take lamp -in -downstream -plugh -on lamp -# Cover WINDOW1 -w -e -# Retrieve axe -s -s -e -take axe -listen -# At Hall of Mists -score -inven -# Timewarp forward to test exhaustion of replacement batteries -waste 2443 -z -# MISSING_BATTERIES is uttered +quit diff --git a/tests/birdsnakewake.chk b/tests/birdsnakewake.chk index 68f99d4..361cbf7 100644 --- a/tests/birdsnakewake.chk +++ b/tests/birdsnakewake.chk @@ -2930,7 +2930,7 @@ OK > take bird -OK +You are already carrying it! > free bird diff --git a/tests/birdsnakewake.log b/tests/birdsnakewake.log index 0d64d15..ef1a6d5 100644 --- a/tests/birdsnakewake.log +++ b/tests/birdsnakewake.log @@ -1,4 +1,6 @@ ## Attempt to kill snake with bird in the endgame +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in diff --git a/tests/birdweight.chk b/tests/birdweight.chk index 0df99e6..24f0904 100644 --- a/tests/birdweight.chk +++ b/tests/birdweight.chk @@ -456,7 +456,7 @@ attack. He seems almost amused by your puny effort. OK - +> You scored 61 out of a possible 430, using 81 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/birdweight.log b/tests/birdweight.log index bc07eb6..5cfe3db 100644 --- a/tests/birdweight.log +++ b/tests/birdweight.log @@ -1,5 +1,8 @@ ## Verify that the bird is weightless in inventory +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Checks fix for GitLab issue #40 +#NOCOMPARE Bird was not weightless in cage in advent430 so this test is invalid. n #seed 687800971 seed 976729036 diff --git a/tests/boulder2.chk b/tests/boulder2.chk index cdd7dbb..87aee68 100644 --- a/tests/boulder2.chk +++ b/tests/boulder2.chk @@ -984,7 +984,7 @@ There is a huge beanstalk growing out of the west pit up to the hole. You are at the bottom of the eastern pit in the Twopit Room. There is a small pool of oil in one corner of the pit. -> g oil +> get oil Your bottle is now full of oil. @@ -1351,7 +1351,7 @@ You're in Giant Room. There is a large nest here, full of golden eggs! -> g +> get OK @@ -1479,7 +1479,7 @@ You're in Chamber of Boulders. There are rare spices here! - +> You scored 119 out of a possible 430, using 238 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/boulder2.log b/tests/boulder2.log index ad4acd4..dbaaf81 100644 --- a/tests/boulder2.log +++ b/tests/boulder2.log @@ -1,4 +1,6 @@ ## Coverage of LOC_BOULDERS2.short +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in @@ -156,7 +158,7 @@ u drop appen e d -g oil +get oil u w d @@ -220,7 +222,7 @@ w d climb w -g +get n take bottl n @@ -239,4 +241,4 @@ fork ne e out -e \ No newline at end of file +e diff --git a/tests/breakmirror.chk b/tests/breakmirror.chk index 2581648..faf0008 100644 --- a/tests/breakmirror.chk +++ b/tests/breakmirror.chk @@ -2934,14 +2934,14 @@ Huh? Huh? -> unlock oyster - -I advise you to put down the oyster before opening it. >WRENCH!< - > drop oyster OK +> unlock oyster + +You don't have anything strong enough to open the oyster. + > take oyster OK diff --git a/tests/breakmirror.log b/tests/breakmirror.log index 12b14e8..7cd9dcc 100644 --- a/tests/breakmirror.log +++ b/tests/breakmirror.log @@ -1,4 +1,6 @@ ## Break the mirror in endgame and die +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in @@ -467,14 +469,16 @@ e e cave e -# Everything to here is from endgame428 +# Everything to here is from endgame428, +# except we drop the oyster before trying +# to unlock it rather than after. attack take oyster find oyster lock lock oyster -unlock oyster drop oyster +unlock oyster take oyster read oyster y @@ -482,4 +486,3 @@ sw attack bird find bird break mirror - diff --git a/tests/carrybird.chk b/tests/carrybird.chk index 7f3b931..5b4b1de 100644 --- a/tests/carrybird.chk +++ b/tests/carrybird.chk @@ -51,10 +51,6 @@ has scrawled, "MAGIC WORD XYZZY". A three foot black rod with a rusty star on an end lies nearby. -> eat grate - -I see no grate here. - > w You are in an awkward sloping east/west canyon. @@ -75,8 +71,8 @@ You can catch the bird, but you cannot carry it. The little bird is now dead. Its body disappears. - -You scored 32 out of a possible 430, using 10 turns. +> +You scored 32 out of a possible 430, using 9 turns. You are obviously a rank amateur. Better luck next time. diff --git a/tests/carrybird.log b/tests/carrybird.log index 5f79548..d661145 100644 --- a/tests/carrybird.log +++ b/tests/carrybird.log @@ -1,4 +1,6 @@ ## Try to carry bird without cage, then kill bird +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1071883378 in @@ -6,9 +8,8 @@ take lamp rub lamp xyzzy on -eat grate w w take bird # test intransitive attack on bird -attack \ No newline at end of file +attack diff --git a/tests/carryfreebird.chk b/tests/carryfreebird.chk index dcaf64b..870ea4b 100644 --- a/tests/carryfreebird.chk +++ b/tests/carryfreebird.chk @@ -327,7 +327,7 @@ The bird eyes you suspiciously and flutters away. A moment later you feel something wet land on your head, but upon looking up you can see no sign of the culprit. - +> You scored 113 out of a possible 430, using 57 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/carryfreebird.log b/tests/carryfreebird.log index 0c81742..e0482f2 100644 --- a/tests/carryfreebird.log +++ b/tests/carryfreebird.log @@ -1,4 +1,6 @@ ## Try to carry the bird after freeing it instead of listening +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1495951709 attack diff --git a/tests/cheatresume.chk b/tests/cheatresume.chk index 5ca8d1b..a56a78b 100644 --- a/tests/cheatresume.chk +++ b/tests/cheatresume.chk @@ -13,15 +13,15 @@ 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. - +> Now let's see you do it without suspending in mid-Adventure. You scored 9031 out of a possible 430, using 0 turns. -'Adventuredom stands in awe -- you have now joined the ranks of the +Adventuredom stands in awe -- you have now joined the ranks of the W O R L D C H A M P I O N A D V E N T U R E R S ! It may interest you to know that the Dungeon-Master himself has, to -my knowledge, never achieved this threshold in fewer than 330 turns.' +my knowledge, never achieved this threshold in fewer than 330 turns. To achieve the next higher rating would be a neat trick! Congratulations!! diff --git a/tests/cheatresume.log b/tests/cheatresume.log index 45d8df2..5440c4f 100644 --- a/tests/cheatresume.log +++ b/tests/cheatresume.log @@ -1,4 +1,7 @@ ## Resume from absurd save file with numdie = -900 +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE Can't compare to advent430 due to version skew n resume cheat_numdie.adv diff --git a/tests/cheatresume2.chk b/tests/cheatresume2.chk index d698a26..af3648c 100644 --- a/tests/cheatresume2.chk +++ b/tests/cheatresume2.chk @@ -13,7 +13,7 @@ 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. - +> Now let's see you do it without suspending in mid-Adventure. You scored 10031 out of a possible 430, using 0 turns. diff --git a/tests/cheatresume2.log b/tests/cheatresume2.log index 614c692..b479726 100644 --- a/tests/cheatresume2.log +++ b/tests/cheatresume2.log @@ -1,5 +1,8 @@ ## Resume from absurd save file with numdie = -1000 +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # generating "off my scale" score threshold message +#NOCOMPARE Can't compare to advent430 due to version skew n resume cheat_numdie1000.adv diff --git a/tests/compare b/tests/compare new file mode 100755 index 0000000..32f60be --- /dev/null +++ b/tests/compare @@ -0,0 +1,4 @@ +#! /bin/sh +# Display diff for an individual test +test=$1 +../advent <${test}.log | ./tapdiffer "${test}" "${test}.chk" diff --git a/tests/coverage_dungeon.py b/tests/coverage_dungeon.py index bbab99b..d193b27 100755 --- a/tests/coverage_dungeon.py +++ b/tests/coverage_dungeon.py @@ -1,4 +1,6 @@ #!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause """ This is the open-adventure dungeon text coverage report generator. It consumes a YAML description of the dungeon and determines whether the @@ -7,12 +9,13 @@ various strings contained are present within the test check files. The default HTML output is appropriate for use with Gitlab CI. You can override it with a command-line argument. -The DANGLING list is for actions that should be considered always found -even if the checkfile search doesn't find them. Typically this will because -they emit a templated message that can't be regression-tested by equality. +The DANGLING lists are for actions and messages that should be +considered always found even if the checkfile search doesn't find them. +Typically this will because an action emit a templated message that +can't be regression-tested by equality. """ -# pylint: disable=consider-using-f-string +# pylint: disable=consider-using-f-string,line-too-long,invalid-name,missing-function-docstring,redefined-outer-name import os import sys @@ -23,44 +26,48 @@ TEST_DIR = "." YAML_PATH = "../adventure.yaml" HTML_TEMPLATE_PATH = "../templates/coverage_dungeon.html.tpl" DEFAULT_HTML_OUTPUT_PATH = "../coverage/adventure.yaml.html" -DANGLING = ["ACT_VERSION"] +DANGLING_ACTIONS = ["ACT_VERSION"] +DANGLING_MESSAGES = ["SAVERESUME_DISABLED"] -STDOUT_REPORT_CATEGORY = " {name:.<19}: {percent:5.1f}% covered ({covered} of {total})\n" +STDOUT_REPORT_CATEGORY = ( + " {name:.<19}: {percent:5.1f}% covered ({covered} of {total})\n" +) -HTML_SUMMARY_ROW = ''' +HTML_SUMMARY_ROW = """ {name}: {total} {covered} {percent:.1f}% -''' +""" -HTML_CATEGORY_SECTION = ''' +HTML_CATEGORY_SECTION = """ {rows}   -''' +""" -HTML_CATEGORY_HEADER = ''' +HTML_CATEGORY_HEADER = """ {label} {cells} -''' +""" HTML_CATEGORY_HEADER_CELL = '{}\n' HTML_CATEGORY_COVERAGE_CELL = ' \n' -HTML_CATEGORY_ROW = ''' +HTML_CATEGORY_ROW = """ {id} {cells} -''' +""" + def search(needle, haystack): # Search for needle in haystack, first escaping needle for regex, then @@ -71,16 +78,19 @@ def search(needle, haystack): # if needle is empty, assume we're going to find an empty string return True - needle_san = re.escape(needle) \ - .replace("\\n", "\n") \ - .replace("\\t", "\t") \ - .replace("%S", ".*") \ - .replace("%s", ".*") \ - .replace("%d", ".*") \ - .replace("%V", ".*") + needle_san = ( + re.escape(needle) + .replace("\\n", "\n") + .replace("\\t", "\t") + .replace("%S", ".*") + .replace("%s", ".*") + .replace("%d", ".*") + .replace("%V", ".*") + ) return re.search(needle_san, haystack) + def obj_coverage(objects, text, report): # objects have multiple descriptions based on state for _, objouter in enumerate(objects): @@ -89,19 +99,20 @@ def obj_coverage(objects, text, report): for j, desc in enumerate(obj["descriptions"]): name = "{}[{}]".format(obj_name, j) if name not in report["messages"]: - report["messages"][name] = {"covered" : False} + report["messages"][name] = {"covered": False} report["total"] += 1 if not report["messages"][name]["covered"] and search(desc, text): report["messages"][name]["covered"] = True report["covered"] += 1 + def loc_coverage(locations, text, report): # locations have a long and a short description, that each have to - # be checked seperately + # be checked separately for name, loc in locations: desc = loc["description"] if name not in report["messages"]: - report["messages"][name] = {"long" : False, "short": False} + report["messages"][name] = {"long": False, "short": False} report["total"] += 2 if not report["messages"][name]["long"] and search(desc["long"], text): report["messages"][name]["long"] = True @@ -110,6 +121,7 @@ def loc_coverage(locations, text, report): report["messages"][name]["short"] = True report["covered"] += 1 + def hint_coverage(obituaries, text, report): # hints have a "question" where the hint is offered, followed # by the actual hint if the player requests it @@ -117,7 +129,7 @@ def hint_coverage(obituaries, text, report): hint = hintouter["hint"] name = hint["name"] if name not in report["messages"]: - report["messages"][name] = {"question" : False, "hint": False} + report["messages"][name] = {"question": False, "hint": False} report["total"] += 2 if not report["messages"][name]["question"] and search(hint["question"], text): report["messages"][name]["question"] = True @@ -126,61 +138,72 @@ def hint_coverage(obituaries, text, report): report["messages"][name]["hint"] = True report["covered"] += 1 + def obit_coverage(obituaries, text, report): # obituaries have a "query" where it asks the player for a resurrection, # followed by a snarky comment if the player says yes for name, obit in enumerate(obituaries): if name not in report["messages"]: - report["messages"][name] = {"query" : False, "yes_response": False} + report["messages"][name] = {"query": False, "yes_response": False} report["total"] += 2 if not report["messages"][name]["query"] and search(obit["query"], text): report["messages"][name]["query"] = True report["covered"] += 1 - if not report["messages"][name]["yes_response"] and search(obit["yes_response"], text): + if not report["messages"][name]["yes_response"] and search( + obit["yes_response"], text + ): report["messages"][name]["yes_response"] = True report["covered"] += 1 + def threshold_coverage(classes, text, report): # works for class thresholds and turn threshold, which have a "message" # property for name, item in enumerate(classes): if name not in report["messages"]: - report["messages"][name] = {"covered" : "False"} + report["messages"][name] = {"covered": False} report["total"] += 1 if not report["messages"][name]["covered"] and search(item["message"], text): report["messages"][name]["covered"] = True report["covered"] += 1 + def arb_coverage(arb_msgs, text, report): for name, message in arb_msgs: if name not in report["messages"]: - report["messages"][name] = {"covered" : False} + report["messages"][name] = {"covered": False} report["total"] += 1 - if not report["messages"][name]["covered"] and search(message, text): + if not report["messages"][name]["covered"] and ( + search(message, text) or name in DANGLING_MESSAGES + ): report["messages"][name]["covered"] = True report["covered"] += 1 + def actions_coverage(items, text, report): # works for actions for name, item in items: if name not in report["messages"]: - report["messages"][name] = {"covered" : False} + report["messages"][name] = {"covered": False} report["total"] += 1 - if not report["messages"][name]["covered"] and (search(item["message"], text) or name in DANGLING): + if not report["messages"][name]["covered"] and ( + search(item["message"], text) or name in DANGLING_ACTIONS + ): report["messages"][name]["covered"] = True report["covered"] += 1 + def coverage_report(db, check_file_contents): - # Create report for each catagory, including total items, number of items + # Create report for each category, including total items, number of items # covered, and a list of the covered messages report = {} for name in db.keys(): # initialize each catagory report[name] = { - "name" : name, # convenience for string formatting - "total" : 0, - "covered" : 0, - "messages" : {} + "name": name, # convenience for string formatting + "total": 0, + "covered": 0, + "messages": {}, } # search for each message in every test check file @@ -196,20 +219,21 @@ def coverage_report(db, check_file_contents): return report + if __name__ == "__main__": # load DB try: - with open(YAML_PATH, "r", encoding='ascii', errors='surrogateescape') as f: + with open(YAML_PATH, "r", encoding="ascii", errors="surrogateescape") as f: db = yaml.safe_load(f) except IOError as e: - print('ERROR: could not load %s (%s)' % (YAML_PATH, e.strerror)) + print("ERROR: could not load %s (%s)" % (YAML_PATH, e.strerror)) sys.exit(-1) # get contents of all the check files check_file_contents = [] for filename in os.listdir(TEST_DIR): if filename.endswith(".chk"): - with open(filename, "r", encoding='ascii', errors='surrogateescape') as f: + with open(filename, "r", encoding="ascii", errors="surrogateescape") as f: check_file_contents.append(f.read()) # run coverage analysis report on dungeon database @@ -232,14 +256,20 @@ if __name__ == "__main__": colspan = 10 - len(cat_keys) for key in cat_keys: headers_html += HTML_CATEGORY_HEADER_CELL.format(key) - category_html = HTML_CATEGORY_HEADER.format(colspan=colspan, label=category["name"], cells=headers_html) + category_html = HTML_CATEGORY_HEADER.format( + colspan=colspan, label=category["name"], cells=headers_html + ) # render message coverage row for message_id, covered in cat_messages: category_html_row = "" for key, value in covered.items(): - category_html_row += HTML_CATEGORY_COVERAGE_CELL.format("uncovered" if not value else "covered") - category_html += HTML_CATEGORY_ROW.format(id=message_id,colspan=colspan, cells=category_html_row) + category_html_row += HTML_CATEGORY_COVERAGE_CELL.format( + "uncovered" if not value else "covered" + ) + category_html += HTML_CATEGORY_ROW.format( + id=message_id, colspan=colspan, cells=category_html_row + ) categories_html += HTML_CATEGORY_SECTION.format(id=name, rows=category_html) # render category summaries @@ -256,16 +286,22 @@ if __name__ == "__main__": # render HTML report try: - with open(HTML_TEMPLATE_PATH, "r", encoding='ascii', errors='surrogateescape') as f: + with open( + HTML_TEMPLATE_PATH, "r", encoding="ascii", errors="surrogateescape" + ) as f: # read in HTML template html_template = f.read() except IOError as e: - print('ERROR: reading HTML report template failed ({})'.format(e.strerror)) + print("ERROR: reading HTML report template failed ({})".format(e.strerror)) sys.exit(-1) # parse template with report and write it out try: - with open(html_output_path, "w", encoding='ascii', errors='surrogateescape') as f: - f.write(html_template.format(categories=categories_html, summary=summary_html)) + with open( + html_output_path, "w", encoding="ascii", errors="surrogateescape" + ) as f: + f.write( + html_template.format(categories=categories_html, summary=summary_html) + ) except IOError as e: - print('ERROR: writing HTML report failed ({})'.format(e.strerror)) + print("ERROR: writing HTML report failed ({})".format(e.strerror)) diff --git a/tests/death-jump.log b/tests/death-jump.log index 3c877f0..91e63f9 100644 --- a/tests/death-jump.log +++ b/tests/death-jump.log @@ -1,4 +1,6 @@ ## Jump into a pit and die, refuse reincarnation +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1495774850 in diff --git a/tests/defeat.log b/tests/defeat.log index c42430a..f3d6b08 100644 --- a/tests/defeat.log +++ b/tests/defeat.log @@ -1,4 +1,6 @@ ## Last-minute defeat, with lava. Also tests vase drop before pillow. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56 n seed 1838473132 diff --git a/tests/domefail.chk b/tests/domefail.chk index 000995b..961b8c5 100644 --- a/tests/domefail.chk +++ b/tests/domefail.chk @@ -166,7 +166,7 @@ The dome is unclimbable. You're in Hall of Mists. - +> You scored 63 out of a possible 430, using 24 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/domefail.log b/tests/domefail.log index df46939..d75352e 100644 --- a/tests/domefail.log +++ b/tests/domefail.log @@ -1,4 +1,6 @@ ## Take nugget and fail to climb to the dome +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in diff --git a/tests/dragon_secret5.chk b/tests/dragon_secret5.chk index 1d5e121..b469f27 100644 --- a/tests/dragon_secret5.chk +++ b/tests/dragon_secret5.chk @@ -246,7 +246,7 @@ There is a Persian rug spread out on the floor! The blood-specked body of a huge green dead dragon lies to one side. - +> You scored 65 out of a possible 430, using 32 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/dragon_secret5.log b/tests/dragon_secret5.log index 15f3668..61f01ac 100644 --- a/tests/dragon_secret5.log +++ b/tests/dragon_secret5.log @@ -1,4 +1,6 @@ ## Check that dead dragon actually moves its location (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 18084731 in diff --git a/tests/dropcagedbird.chk b/tests/dropcagedbird.chk index b5c64e6..5f382a3 100644 --- a/tests/dropcagedbird.chk +++ b/tests/dropcagedbird.chk @@ -150,7 +150,7 @@ A huge green fierce snake bars the way! There's nothing here it wants to eat (except perhaps you). - +> You scored 59 out of a possible 430, using 25 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/dropcagedbird.log b/tests/dropcagedbird.log index ba90580..d069f1a 100644 --- a/tests/dropcagedbird.log +++ b/tests/dropcagedbird.log @@ -1,4 +1,6 @@ ## Try to carry the bird after freeing it instead of listening +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1495951709 in @@ -25,4 +27,4 @@ take bird drop cage d d -feed snake \ No newline at end of file +feed snake diff --git a/tests/drown.log b/tests/drown.log index b5ebf27..75e89ba 100644 --- a/tests/drown.log +++ b/tests/drown.log @@ -1,4 +1,6 @@ ## Speak a magic word at an inopportune time and drown. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56 n seed 1838473132 diff --git a/tests/dwarf.log b/tests/dwarf.log index 3bb0d4b..7954a62 100644 --- a/tests/dwarf.log +++ b/tests/dwarf.log @@ -1,4 +1,6 @@ ## In which the dwarf kills you +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1494912171 in diff --git a/tests/dwarf_alternative.chk b/tests/dwarf_alternative.chk index 5fcaf06..1dae06b 100644 --- a/tests/dwarf_alternative.chk +++ b/tests/dwarf_alternative.chk @@ -58,3 +58,12 @@ 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 7 turns. + +Your score qualifies you as a novice class adventurer. + +To achieve the next higher rating, you need 70 more points. diff --git a/tests/dwarf_alternative.log b/tests/dwarf_alternative.log index 69a0619..ef0a881 100644 --- a/tests/dwarf_alternative.log +++ b/tests/dwarf_alternative.log @@ -1,4 +1,6 @@ ## Check that dwarf spawns in alternative location (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 383847 in @@ -8,3 +10,4 @@ w w d d +n diff --git a/tests/eggs_done.chk b/tests/eggs_done.chk index baa766f..4e85042 100644 --- a/tests/eggs_done.chk +++ b/tests/eggs_done.chk @@ -1167,7 +1167,7 @@ You're in Giant Room. There is a large nest here, full of golden eggs! -> g egg +> take egg OK @@ -1223,7 +1223,7 @@ Wicker cage Black rod Small bottle - +> You scored 77 out of a possible 430, using 190 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/eggs_done.log b/tests/eggs_done.log index 1d600f0..829c292 100644 --- a/tests/eggs_done.log +++ b/tests/eggs_done.log @@ -1,4 +1,6 @@ ## Be done with Giant Room and eggs (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in @@ -184,11 +186,11 @@ w d climb w -g egg +take egg n fee fie foe foo look -inven \ No newline at end of file +inven diff --git a/tests/eggs_vanish.chk b/tests/eggs_vanish.chk index 5f84bfc..256ac5e 100644 --- a/tests/eggs_vanish.chk +++ b/tests/eggs_vanish.chk @@ -399,7 +399,7 @@ the west wall is scrawled the inscription, "FEE FIE FOE FOO" [sic]. There is a large nest here, full of golden eggs! -> g +> take OK @@ -435,7 +435,7 @@ There is a large nest here, full of golden eggs! The passage here is blocked by a recent cave-in. - +> You scored 67 out of a possible 430, using 66 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/eggs_vanish.log b/tests/eggs_vanish.log index 452335d..3e9c799 100644 --- a/tests/eggs_vanish.log +++ b/tests/eggs_vanish.log @@ -1,4 +1,6 @@ ## Vanishing eggs in Giant Room (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in @@ -59,7 +61,7 @@ d water plant climb w -g +take n fee fie @@ -67,4 +69,4 @@ foe foo # go south, east to arrive at LOC_CAVEIN for coverage s -e \ No newline at end of file +e diff --git a/tests/endgame428.log b/tests/endgame428.log index 8df0238..9556677 100644 --- a/tests/endgame428.log +++ b/tests/endgame428.log @@ -1,4 +1,6 @@ ## 428-point walkthrough +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56 n seed 1838473132 diff --git a/tests/endobjects.chk b/tests/endobjects.chk index 1f06db5..42132d3 100644 --- a/tests/endobjects.chk +++ b/tests/endobjects.chk @@ -2435,11 +2435,12 @@ OK You are currently holding the following: Wicker cage +Little bird in cage Small bottle > get bird -OK +You are already carrying it! > inven diff --git a/tests/endobjects.log b/tests/endobjects.log index e532df0..978d457 100644 --- a/tests/endobjects.log +++ b/tests/endobjects.log @@ -1,5 +1,8 @@ ### Check that water is unavailable in endgame +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Addresses GitLab issue #55: in endgame, some object starting states are incorrect +#NOCOMPARE Bird was not weightless in cage in advent430, this test depends on that. no seed 11247848 no diff --git a/tests/fail_hint_maze.chk b/tests/fail_hint_maze.chk index 9973218..46b0aa7 100644 --- a/tests/fail_hint_maze.chk +++ b/tests/fail_hint_maze.chk @@ -436,7 +436,7 @@ OK OK - +> You scored 59 out of a possible 430, using 93 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/fail_hint_maze.log b/tests/fail_hint_maze.log index d458561..982d44b 100644 --- a/tests/fail_hint_maze.log +++ b/tests/fail_hint_maze.log @@ -1,4 +1,6 @@ ## Fail to get maze hint by being empty-handed (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 25508795 in diff --git a/tests/fail_hint_ogre.chk b/tests/fail_hint_ogre.chk index 71a9d72..4d300b7 100644 --- a/tests/fail_hint_ogre.chk +++ b/tests/fail_hint_ogre.chk @@ -1638,7 +1638,7 @@ OK OK - +> You scored 77 out of a possible 430, using 263 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/fail_hint_ogre.log b/tests/fail_hint_ogre.log index c26ecbb..3df4697 100644 --- a/tests/fail_hint_ogre.log +++ b/tests/fail_hint_ogre.log @@ -1,4 +1,7 @@ ## Qualify for ogre hint but fail due to dwarves dead (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE Fails due uninteresting difference in whitespace process. n seed 1838473132 in diff --git a/tests/fail_hint_ogre2.chk b/tests/fail_hint_ogre2.chk index 28b673e..3437b22 100644 --- a/tests/fail_hint_ogre2.chk +++ b/tests/fail_hint_ogre2.chk @@ -394,7 +394,7 @@ You are in a large chamber with passages to the west and north. A formidable ogre bars the northern exit. - +> You scored 63 out of a possible 430, using 56 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/fail_hint_ogre2.log b/tests/fail_hint_ogre2.log index 4961479..6e37f8e 100644 --- a/tests/fail_hint_ogre2.log +++ b/tests/fail_hint_ogre2.log @@ -1,4 +1,6 @@ ## Qualify for ogre hint but fail due to nearby dwarf (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 25508795 in diff --git a/tests/fail_hint_woods.chk b/tests/fail_hint_woods.chk index 990cf8e..ca5ac5e 100644 --- a/tests/fail_hint_woods.chk +++ b/tests/fail_hint_woods.chk @@ -109,7 +109,7 @@ OK OK - +> You scored 32 out of a possible 430, using 25 turns. You are obviously a rank amateur. Better luck next time. diff --git a/tests/fail_hint_woods.log b/tests/fail_hint_woods.log index 5c37435..21694b5 100644 --- a/tests/fail_hint_woods.log +++ b/tests/fail_hint_woods.log @@ -1,4 +1,6 @@ ## Fail getting wood hint by finding appendage (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n n z @@ -24,4 +26,4 @@ z z z z -z \ No newline at end of file +z diff --git a/tests/fillfail.chk b/tests/fillfail.chk index fd3d096..11f58eb 100644 --- a/tests/fillfail.chk +++ b/tests/fillfail.chk @@ -57,7 +57,7 @@ You can't fill that. Your bottle is already full. - +> You scored 32 out of a possible 430, using 10 turns. You are obviously a rank amateur. Better luck next time. diff --git a/tests/fillfail.log b/tests/fillfail.log index afb125f..1a9e12b 100644 --- a/tests/fillfail.log +++ b/tests/fillfail.log @@ -1,4 +1,6 @@ ## Attempt to fill lamp, attempt to fill bottle with no source +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n in carry lamp diff --git a/tests/fillvase.chk b/tests/fillvase.chk index 5d8c889..cbbeea4 100644 --- a/tests/fillvase.chk +++ b/tests/fillvase.chk @@ -1463,7 +1463,7 @@ A small velvet pillow lies on the ground. You can't be serious! - +> You scored 191 out of a possible 430, using 241 turns. You may now consider yourself a "Seasoned Adventurer". diff --git a/tests/fillvase.log b/tests/fillvase.log index 8414f14..2f9c76c 100644 --- a/tests/fillvase.log +++ b/tests/fillvase.log @@ -1,4 +1,6 @@ ## Fill the vase +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Arthur O'Dwyer writes: # # (4) Lastly, here's a test case for you! Go get the VASE; then get the diff --git a/tests/flyback.chk b/tests/flyback.chk index 2d8750e..04b658c 100644 --- a/tests/flyback.chk +++ b/tests/flyback.chk @@ -2039,8 +2039,6 @@ There is a Persian rug here, hovering in mid-air! A brilliant blue star sapphire is here! > - - You scored 257 out of a possible 430, using 337 turns. You have reached "Junior Master" status. diff --git a/tests/flyback.log b/tests/flyback.log index b849cc8..18c380c 100644 --- a/tests/flyback.log +++ b/tests/flyback.log @@ -1,4 +1,7 @@ -## Test fix for issue #51: rug flying is broken +## Test fix for issue 51: rug flying is broken +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE Behavior differs due to a bug fix. n seed 1838473132 in @@ -340,4 +343,3 @@ drop emerald fly rug fly rug fly rug - diff --git a/tests/footslip.chk b/tests/footslip.chk index ef2f595..a9f28d3 100644 --- a/tests/footslip.chk +++ b/tests/footslip.chk @@ -796,7 +796,7 @@ There is food here. > - +> You scored 61 out of a possible 430, using 121 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/footslip.log b/tests/footslip.log index ad02244..8b9edf2 100644 --- a/tests/footslip.log +++ b/tests/footslip.log @@ -1,4 +1,6 @@ ## Coverage of LOC_FOOTSLIP +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in diff --git a/tests/gemstates.chk b/tests/gemstates.chk index db58aa4..8739f93 100644 --- a/tests/gemstates.chk +++ b/tests/gemstates.chk @@ -2145,7 +2145,7 @@ There is a ruby resting in a small cavity in the rock! There is a Persian rug spread out on the floor! - +> You scored 271 out of a possible 430, using 365 turns. You have reached "Junior Master" status. diff --git a/tests/gemstates.log b/tests/gemstates.log index ea49416..a07d86f 100644 --- a/tests/gemstates.log +++ b/tests/gemstates.log @@ -1,4 +1,6 @@ ## Observe amber, ruby, sapphire after state change +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1635997320 in diff --git a/tests/goback.chk b/tests/goback.chk index 27d0cb8..fb6eb0f 100644 --- a/tests/goback.chk +++ b/tests/goback.chk @@ -2527,7 +2527,7 @@ best start wrapping this up. OK - +> You scored 207 out of a possible 430, using 413 turns. You may now consider yourself a "Seasoned Adventurer". diff --git a/tests/goback.log b/tests/goback.log index 2915836..83199f5 100644 --- a/tests/goback.log +++ b/tests/goback.log @@ -1,6 +1,9 @@ ## Test many nonlethal failure conditions +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # This variant elicits the prompt to go back for batteries # See comments in this log +#NOCOMPARE Relies on "waste" n seed 1838473132 in diff --git a/tests/hint_dark.chk b/tests/hint_dark.chk index cee6051..1a95bf8 100644 --- a/tests/hint_dark.chk +++ b/tests/hint_dark.chk @@ -150,7 +150,7 @@ There is a way to explore that region without having to worry about falling into a pit. None of the objects available is immediately useful in discovering the secret. - +> You scored 54 out of a possible 430, using 27 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/hint_dark.log b/tests/hint_dark.log index b522b90..78a3c79 100644 --- a/tests/hint_dark.log +++ b/tests/hint_dark.log @@ -1,4 +1,6 @@ ## Elicit hint for dealing with plugh room and darkness (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1495951709 in @@ -29,4 +31,4 @@ z z z y -y \ No newline at end of file +y diff --git a/tests/hint_grate.chk b/tests/hint_grate.chk index 652b639..fa7d80d 100644 --- a/tests/hint_grate.chk +++ b/tests/hint_grate.chk @@ -83,7 +83,7 @@ The grate is very solid and has a hardened steel lock. You cannot enter without a key, and there are no keys nearby. I would recommend looking elsewhere for the keys. - +> You scored 30 out of a possible 430, using 10 turns. You are obviously a rank amateur. Better luck next time. diff --git a/tests/hint_grate.log b/tests/hint_grate.log index e7ddd73..634caf3 100644 --- a/tests/hint_grate.log +++ b/tests/hint_grate.log @@ -1,4 +1,6 @@ ## Elicit hint for dealing with grate +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1495774850 in diff --git a/tests/hint_jade.chk b/tests/hint_jade.chk index f16accf..e8d9f81 100644 --- a/tests/hint_jade.chk +++ b/tests/hint_jade.chk @@ -962,7 +962,7 @@ There is a huge beanstalk growing out of the west pit up to the hole. You are at the bottom of the eastern pit in the Twopit Room. There is a small pool of oil in one corner of the pit. -> g oil +> get oil Your bottle is now full of oil. @@ -1325,7 +1325,7 @@ You're in Giant Room. There is a large nest here, full of golden eggs! -> g +> get OK @@ -1424,7 +1424,8 @@ down a gentle slope. The main corridor enters from the west. You are being followed by a very large, tame bear. -You are walking along a gently sloping north/south passage lined with oddly shaped limestone formations. +You are walking along a gently sloping north/south passage lined with +oddly shaped limestone formations. > fork @@ -1545,7 +1546,7 @@ There is a huge beanstalk growing out of the west pit up to the hole. You're in east pit. -> g oil +> get oil Your bottle is now full of oil. @@ -1848,7 +1849,7 @@ Do you want the hint? Once you've found all the other treasures, it is no longer possible to locate the one you're now missing. - +> You scored 91 out of a possible 430, using 297 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/hint_jade.log b/tests/hint_jade.log index d5550d0..d220260 100644 --- a/tests/hint_jade.log +++ b/tests/hint_jade.log @@ -1,4 +1,6 @@ ## Elicit hint for getting the jade (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in @@ -151,7 +153,7 @@ u drop appen e d -g oil +get oil u w d @@ -214,7 +216,7 @@ w d climb w -g +get n take bottl n @@ -249,7 +251,7 @@ se se w d -g oil +get oil u w w diff --git a/tests/hint_snake.chk b/tests/hint_snake.chk index 78ca80f..d5a1c44 100644 --- a/tests/hint_snake.chk +++ b/tests/hint_snake.chk @@ -192,7 +192,7 @@ You can't kill the snake, or drive it away, or avoid it, or anything like that. There is a way to get by, but you don't have the necessary resources right now. - +> You scored 55 out of a possible 430, using 25 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/hint_snake.log b/tests/hint_snake.log index 9ffc302..710c5e3 100644 --- a/tests/hint_snake.log +++ b/tests/hint_snake.log @@ -1,4 +1,6 @@ ## Elicit hint for dealing with snake +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1951269982 in diff --git a/tests/hint_urn.chk b/tests/hint_urn.chk index d51adc1..63b7542 100644 --- a/tests/hint_urn.chk +++ b/tests/hint_urn.chk @@ -74,7 +74,7 @@ Do you want the hint? This section is quite advanced. Find the cave first. - +> You scored 30 out of a possible 430, using 11 turns. You are obviously a rank amateur. Better luck next time. diff --git a/tests/hint_urn.log b/tests/hint_urn.log index 8154e71..b1f3d3a 100644 --- a/tests/hint_urn.log +++ b/tests/hint_urn.log @@ -1,4 +1,7 @@ ## Elicit hint for dealing with urn (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE Policy decision - no point in emulating advent430's extra \n here n seed 1495951709 u @@ -13,4 +16,4 @@ z z z y -y \ No newline at end of file +y diff --git a/tests/hint_witt.chk b/tests/hint_witt.chk index 5afca9f..315f402 100644 --- a/tests/hint_witt.chk +++ b/tests/hint_witt.chk @@ -2430,7 +2430,7 @@ Do you want the hint? Don't go west. - +> You scored 339 out of a possible 430, using 397 turns. Your score puts you in Master Adventurer Class C. diff --git a/tests/hint_witt.log b/tests/hint_witt.log index fd2a4f2..f97a906 100644 --- a/tests/hint_witt.log +++ b/tests/hint_witt.log @@ -1,4 +1,7 @@ ## Hint for Witt's End +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE Policy decision - no point in emulating advent430's extra \n here n seed 1635997320 in @@ -400,4 +403,4 @@ n n n y -y \ No newline at end of file +y diff --git a/tests/illformed.chk b/tests/illformed.chk index e18fbf9..925fc31 100644 --- a/tests/illformed.chk +++ b/tests/illformed.chk @@ -73,13 +73,13 @@ Sorry, but I no longer seem to remember how it was you got here. You're in front of building. -> seed +> waste This command requires a numeric argument. You're in front of building. -> waste +> seed This command requires a numeric argument. @@ -126,22 +126,6 @@ There is a bottle of water here. Nothing happens. -> say fee - -OK - -> say fie - -OK - -> say foe - -OK - -> say fum - -Nothing happens. - > in I don't know in from out here. Use compass points or name something @@ -161,539 +145,9 @@ There is a bottle of water here. What do you want to do with the keys? -> throw food +> +You scored 27 out of a possible 430, using 17 turns. -You aren't carrying it! - -> nothing food - -OK - -> calm food - -I'm game. Would you care to explain how? - -> walk food - -Where? - -> score food - -Huh? - -> foo food - -I don't know how. - -> brief food - -On what? - -> blast food - -Blasting requires dynamite. - -> find grate - -I can only tell you what you see as you move about and manipulate -things. I cannot tell you where remote things are. - -> light food - -I'm afraid I don't understand. - -> lock food - -I don't know how to lock or unlock such a thing. - -> unlock food - -I don't know how to lock or unlock such a thing. - -> extinguish food - -I'm afraid I don't understand. - -> suspend food - -Huh? - -> resume food - -Huh? - -> crawl - -Which way? - -You're inside building. - -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. - -> out - -You're in front of building. - -> stream - -You are in a valley in the forest beside a stream tumbling along a -rocky bed. - -> lock - -There is nothing here with a lock! - -> take water - -You have nothing in which to carry it. - -> blast - -Blasting requires dynamite. - -> building - -You're in front of building. - -> cave - -I don't know where the cave is, but hereabouts no stream can run on -the surface for long. I would try the stream. - -You're in front of building. - -> enter stream - -Your feet are now wet. - -> try three words - -Please stick to 1- and 2-word commands. - -> listen - -The stream is gurgling placidly. - -> carry - -Carry what? - -> forward - -I am unsure how you are facing. Use compass points or nearby objects. - -You're in front of building. - -> eat - -Eat what? - -> drink - -You have taken a drink from the stream. The water tastes strongly of -minerals, but is not unpleasant. It is extremely cold. - -> throw keys - -I see no keys here. - -> find keys - -I can only tell you what you see as you move about and manipulate -things. I cannot tell you where remote things are. - -> inven keys - -I can only tell you what you see as you move about and manipulate -things. I cannot tell you where remote things are. - -> nothing - -OK - -> in - -You're inside building. - -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. - -> listen - -The stream is gurgling placidly. - -> find keys - -I believe what you want is right here with you. - -> unlock keys - -You can't unlock the keys. - -> find foo - -Nothing happens. - -> find bar - -Sorry, I don't know the word "bar". - -> carry - -Carry what? - -> blast - -Blasting requires dynamite. - -> take bottle - -OK - -> find bottle - -You are already carrying it! - -> drink water - -The bottle of water is now empty. - -> fill bottle - -Your bottle is now full of water. - -> throw axe - -I see no axe here. - -> throw knife - -I see no knife here. - -> lock - -There is nothing here with a lock! - -> unlock - -There is nothing here with a lock! - -> throw - -Throw what? - -> attack - -There is nothing here to attack. - -> unlock chain - -I see no chain here. - -> fill urn - -I see no urn here. - -> eat keys - -Don't be ridiculous! - -> discard keys - -You aren't carrying it! - -> drink keys - -Don't be ridiculous! - -> off keys - -I'm afraid I don't understand. - -> break keys - -It is beyond your power to do that. - -> wake keys - -Don't be ridiculous! - -> take keys - -OK - -> feed keys - -I'm game. Would you care to explain how? - -> fly keys - -I'm game. Would you care to explain how? - -> pour keys - -You can't pour that. - -> throw keys - -OK - -> foo up - -There is no way to go that direction. - -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. - -> get - -Get what? - -> food - -OK - -> eat food - -Thank you, it was delicious! - -> drink blood - -I see no blood here. - -> drink keys - -Don't be ridiculous! - -> light keys - -I'm afraid I don't understand. - -> brief - -Okay, from now on I'll only describe a place in full the first time -you come to it. To get the full description, say "look". - -> frob grate - -Sorry, I don't know the word "frob". - -> read grate - -I see no grate here. - -> grate - -I don't know how to apply that word here. - -You're inside building. - -There are some keys on the ground here. - -There is a shiny brass lamp nearby. - -> pour bottle - -Your bottle is empty and the ground is wet. - -> fill bottle - -Your bottle is now full of water. - -> building - -I don't know how to apply that word here. - -You're inside building. - -There are some keys on the ground here. - -There is a shiny brass lamp nearby. - -> on - -Your lamp is now on. - -> pour bottle - -Your bottle is empty and the ground is wet. - -> xyzzy - ->>Foof!<< - -It is now pitch dark. If you proceed you will likely fall into a pit. - -> take rod - -OK - -> wave rod - -Nothing happens. - -> listen - -All is silent. - -> quit - -Do you really want to quit now? - -> n - -OK - -> read - -Read what? - -> look - -It is now pitch dark. If you proceed you will likely fall into a pit. - -> l - -It is now pitch dark. If you proceed you will likely fall into a pit. - -> x - -It is now pitch dark. If you proceed you will likely fall into a pit. - -> i - -You are currently holding the following: -Black rod -Small bottle - -> news - -Open Adventure is an author-approved open-source release of -Version 2.5 with, as yet, no gameplay changes. -Version 2.5 was essentially the same as Version II; the cave and the -hazards therein are unchanged, and top score is still 430 points. -There are a few more hints, especially for some of the more obscure -puzzles. There are a few minor bugfixes and cosmetic changes. You -can now save a game and resume it at once (formerly you had to wait a -while first), but it now costs you a few points each time you save the -game. Saved games are now stored in much smaller files than before. - -> go back - ->>Foof!<< - -You're inside building. - -There are some keys on the ground here. - -There is a lamp shining nearby. - -> fuck - -Watch it! - -> walk - -Where? - -> fly - -Though you flap your arms furiously, it is to no avail. - -> say boo - -Okay, "boo". - -> score - -You have garnered 27 out of a possible 430 points, using 118 turns. - -> z - -OK - -> score - -You have garnered 27 out of a possible 430 points, using 120 turns. - -> quit keys - -Huh? - -> out - -You're in front of building. - -> s - -You're in valley. - -> 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. - -> down - -You don't fit through a two-inch slit! - -You're at slit in streambed. - -> 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. - -> in - -You can't go through a locked steel grate! - -You're outside grate. - -The grate is locked. - -> seed -123 - -Seed set to -123 - -You're outside grate. - -The grate is locked. - -> no - -OK - -Are you trying to get into the cave? - -> quit - -Please answer the question. - -Are you trying to get into the cave? - -> yes - -I am prepared to give you a hint, but it will cost you 2 points. - -Do you want the hint? +You are obviously a rank amateur. Better luck next time. +To achieve the next higher rating, you need 19 more points. diff --git a/tests/illformed.log b/tests/illformed.log index 59315fe..1e31a41 100644 --- a/tests/illformed.log +++ b/tests/illformed.log @@ -1,4 +1,7 @@ ## Test for various cases not found in walkthroughs. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE This is busted under advent430 -- see comments within. foo y @@ -13,130 +16,16 @@ say rub say grate _ back -seed +# advent430 doesn't have this command waste +# seed should cause a compint when argumentless, +seed eat grate 23 eat building in # Z'ZZZ Word correct, but does nothing say F'UNJ -# Say bigwords when giant isn't around -say fee -say fie -say foe -say fum +# Meant to evoke "I don't know in from out here." in keys -throw food -nothing food -calm food -walk food -score food -foo food -brief food -blast food -find grate -light food -lock food -unlock food -extinguish food -suspend food -resume food -crawl -out -stream -lock -take water -blast -building -cave -enter stream -try three words -listen -carry -forward -eat -drink -throw keys -find keys -inven keys -nothing -in -listen -find keys -unlock keys -find foo -find bar -carry -blast -take bottle -find bottle -drink water -fill bottle -throw axe -throw knife -lock -unlock -throw -attack -unlock chain -fill urn -eat keys -discard keys -drink keys -off keys -break keys -wake keys -take keys -feed keys -fly keys -pour keys -throw keys -foo up -get -food -eat food -drink blood -drink keys -light keys -brief -frob grate -read grate -grate -pour bottle -fill bottle -building -on -pour bottle -xyzzy -take rod -wave rod -listen -quit -n -read -look -l -x -i -news -go back -fuck -walk -fly -say boo -score -z -score -quit keys -out -s -s -down -s -in -seed -123 -no -quit -yes diff --git a/tests/illformed2.chk b/tests/illformed2.chk new file mode 100644 index 0000000..ab2095b --- /dev/null +++ b/tests/illformed2.chk @@ -0,0 +1,553 @@ + +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 1635997320 + +Seed set to 1635997320 + +You're in front of building. + +> back + +You can't get there from here. + +You're in front of building. + +> say axe + +Okay, "axe". + +> say rub + +Okay, "rub". + +> say grate + +Okay, "grate". + +> _ + +Sorry, I don't know the word "_". + +> back + +Sorry, but I no longer seem to remember how it was you got here. + +You're in front of building. + +> 23 + +Sorry, I don't know the word "23". + +> say F'UNJ + +Nothing happens. + +> 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. + +> keys + +What do you want to do with the keys? + +> throw food + +You aren't carrying it! + +> nothing food + +OK + +> calm food + +I'm game. Would you care to explain how? + +> walk food + +Where? + +> score food + +Huh? + +> foo food + +I don't know how. + +> brief food + +On what? + +> blast food + +Blasting requires dynamite. + +> find grate + +I can only tell you what you see as you move about and manipulate +things. I cannot tell you where remote things are. + +> light food + +I'm afraid I don't understand. + +> lock food + +I don't know how to lock or unlock such a thing. + +> unlock food + +I don't know how to lock or unlock such a thing. + +> extinguish food + +I'm afraid I don't understand. + +> suspend food + +Huh? + +> resume food + +Huh? + +> crawl + +Which way? + +You're inside building. + +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. + +> out + +You're in front of building. + +> stream + +You are in a valley in the forest beside a stream tumbling along a +rocky bed. + +> lock + +There is nothing here with a lock! + +> take water + +You have nothing in which to carry it. + +> blast + +Blasting requires dynamite. + +> building + +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. + +> cave + +I don't know where the cave is, but hereabouts no stream can run on +the surface for long. I would try the stream. + +You're in front of building. + +> enter stream + +Your feet are now wet. + +> try three words + +Please stick to 1- and 2-word commands. + +> listen + +The stream is gurgling placidly. + +> carry + +Carry what? + +> forward + +I am unsure how you are facing. Use compass points or nearby objects. + +You're in front of building. + +> eat + +Eat what? + +> drink + +You have taken a drink from the stream. The water tastes strongly of +minerals, but is not unpleasant. It is extremely cold. + +> throw keys + +I see no keys here. + +> find keys + +I can only tell you what you see as you move about and manipulate +things. I cannot tell you where remote things are. + +> inven keys + +I can only tell you what you see as you move about and manipulate +things. I cannot tell you where remote things are. + +> nothing + +OK + +> in + +You're inside building. + +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. + +> listen + +The stream is gurgling placidly. + +> find keys + +I believe what you want is right here with you. + +> unlock keys + +You can't unlock the keys. + +> find bar + +Sorry, I don't know the word "bar". + +> carry + +Carry what? + +> blast + +Blasting requires dynamite. + +> take bottle + +OK + +> find bottle + +You are already carrying it! + +> drink water + +The bottle of water is now empty. + +> fill bottle + +Your bottle is now full of water. + +> throw axe + +I see no axe here. + +> throw knife + +I see no knife here. + +> lock + +There is nothing here with a lock! + +> unlock + +There is nothing here with a lock! + +> throw + +Throw what? + +> attack + +There is nothing here to attack. + +> unlock chain + +I see no chain here. + +> fill urn + +I see no urn here. + +> eat keys + +Don't be ridiculous! + +> discard keys + +You aren't carrying it! + +> drink keys + +Don't be ridiculous! + +> off keys + +I'm afraid I don't understand. + +> break keys + +It is beyond your power to do that. + +> wake keys + +Don't be ridiculous! + +> take keys + +OK + +> feed keys + +I'm game. Would you care to explain how? + +> fly keys + +I'm game. Would you care to explain how? + +> pour keys + +You can't pour that. + +> throw keys + +OK + +> foo up + +There is no way to go that direction. + +You're inside building. + +There are some keys on the ground here. + +There is a shiny brass lamp nearby. + +There is food here. + +> get + +Get what? + +> food + +OK + +> eat food + +Thank you, it was delicious! + +> drink blood + +I see no blood here. + +> drink keys + +Don't be ridiculous! + +> light keys + +I'm afraid I don't understand. + +> brief + +Okay, from now on I'll only describe a place in full the first time +you come to it. To get the full description, say "look". + +> frob grate + +Sorry, I don't know the word "frob". + +> read grate + +I see no grate here. + +> pour bottle + +Your bottle is empty and the ground is wet. + +> fill bottle + +Your bottle is now full of water. + +> building + +I don't know how to apply that word here. + +You're inside building. + +There are some keys on the ground here. + +There is a shiny brass lamp nearby. + +> on + +Your lamp is now on. + +> pour bottle + +Your bottle is empty and the ground is wet. + +> xyzzy + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> take rod + +OK + +> wave rod + +Nothing happens. + +> listen + +All is silent. + +> quit + +Do you really want to quit now? + +> n + +OK + +> read + +Read what? + +> look + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> go back + +>>Foof!<< + +You're inside building. + +There are some keys on the ground here. + +There is a lamp shining nearby. + +> fuck + +Watch it! + +> walk + +Where? + +> fly + +Though you flap your arms furiously, it is to no avail. + +> say boo + +Okay, "boo". + +> score + +You have garnered 32 out of a possible 430 points, using 101 turns. + +> z + +OK + +> score + +You have garnered 32 out of a possible 430 points, using 103 turns. + +> quit keys + +Huh? + +> out + +You're in front of building. + +> s + +You're in valley. + +> 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. + +> down + +You don't fit through a two-inch slit! + +You're at slit in streambed. + +> 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. + +> in + +You can't go through a locked steel grate! + +You're outside grate. + +The grate is locked. + +> seed -123 + +Seed set to -123 + +You're outside grate. + +The grate is locked. + +> +You scored 32 out of a possible 430, using 110 turns. + +You are obviously a rank amateur. Better luck next time. + +To achieve the next higher rating, you need 14 more points. diff --git a/tests/illformed2.log b/tests/illformed2.log new file mode 100644 index 0000000..b1e722a --- /dev/null +++ b/tests/illformed2.log @@ -0,0 +1,138 @@ +## Test for various cases not found in walkthroughs (advent430-compatible). +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +# Commented-out lines cause troble in advent430 +n +seed 1635997320 +back +# Next three lines were buggy +say axe +say rub +say grate +_ +back +#eat grate +23 +#eat building +#in +# Z'ZZZ Word correct, but does nothing +say F'UNJ +# Say bigwords when giant isn't around +#say fee +#say fie +#say foe +#say fum +# Meant to evoke "I don't know in from out here." +in +keys +throw food +nothing food +calm food +walk food +score food +foo food +brief food +blast food +find grate +light food +lock food +unlock food +extinguish food +suspend food +resume food +crawl +out +stream +lock +take water +blast +building +cave +enter stream +try three words +listen +carry +forward +eat +drink +throw keys +find keys +inven keys +nothing +in +listen +find keys +unlock keys +find bar +carry +blast +take bottle +find bottle +drink water +fill bottle +throw axe +throw knife +lock +unlock +throw +attack +unlock chain +fill urn +eat keys +discard keys +drink keys +off keys +break keys +wake keys +take keys +feed keys +fly keys +pour keys +throw keys +foo up +get +food +eat food +drink blood +drink keys +light keys +brief +frob grate +read grate +#grate +pour bottle +fill bottle +building +on +pour bottle +xyzzy +take rod +wave rod +listen +quit +n +read +look +#l +#x +#i +#news +go back +fuck +walk +fly +say boo +score +z +score +quit keys +out +s +s +down +s +in +seed -123 +#no +#quit +#yes diff --git a/tests/intransitivecarry.chk b/tests/intransitivecarry.chk index 510a9bd..4fe9674 100644 --- a/tests/intransitivecarry.chk +++ b/tests/intransitivecarry.chk @@ -35,7 +35,7 @@ OK OK - +> You scored 32 out of a possible 430, using 5 turns. You are obviously a rank amateur. Better luck next time. diff --git a/tests/intransitivecarry.log b/tests/intransitivecarry.log index 640a296..f6a7bfa 100644 --- a/tests/intransitivecarry.log +++ b/tests/intransitivecarry.log @@ -1,4 +1,6 @@ ## Carry when only one object is present +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n in carry lamp diff --git a/tests/isofoo.log b/tests/isofoo.log deleted file mode 100644 index 82b5906..0000000 --- a/tests/isofoo.log +++ /dev/null @@ -1,3 +0,0 @@ -## Test isolated 'foo' word -n -foo diff --git a/tests/issue36.chk b/tests/issue36.chk index 412f019..99d82df 100644 --- a/tests/issue36.chk +++ b/tests/issue36.chk @@ -33,7 +33,7 @@ Get what? OK - +> You scored 32 out of a possible 430, using 3 turns. You are obviously a rank amateur. Better luck next time. diff --git a/tests/issue36.log b/tests/issue36.log index cab1d04..279b7f3 100644 --- a/tests/issue36.log +++ b/tests/issue36.log @@ -1,4 +1,6 @@ ## Test handling of object after transitive verb. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 826186526 in diff --git a/tests/issue37.chk b/tests/issue37.chk index 6d8772b..4aa9320 100644 --- a/tests/issue37.chk +++ b/tests/issue37.chk @@ -32,7 +32,7 @@ OK You are currently holding the following: Tasty food - +> You scored 32 out of a possible 430, using 4 turns. You are obviously a rank amateur. Better luck next time. diff --git a/tests/issue37.log b/tests/issue37.log index ded6a10..b796aa6 100644 --- a/tests/issue37.log +++ b/tests/issue37.log @@ -1,4 +1,6 @@ ## Test handling of transitive verb after noun +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n in food diff --git a/tests/knife.chk b/tests/knife.chk new file mode 100644 index 0000000..76a3b6b --- /dev/null +++ b/tests/knife.chk @@ -0,0 +1,386 @@ + +Welcome to Adventure!! Would you like instructions? + +> no + +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 1640849217 + +Seed set to 1640849217 + +You're in front of building. + +> e + +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. + +> get lamp + +OK + +> xyzzy + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> get rod + +OK + +> on + +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". + +> 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. + +> d + +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. + +> wave rod + +A crystal bridge now spans the fissure. + +> w + +You are on the west side of the fissure in the Hall of Mists. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +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. + +A crystal bridge spans the fissure. + +> w + +You are on the west side of the fissure in the Hall of Mists. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +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. + +A crystal bridge spans the fissure. + +> w + +You are on the west side of the fissure in the Hall of Mists. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +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. + +A crystal bridge spans the fissure. + +> w + +You are on the west side of the fissure in the Hall of Mists. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> e + +A little dwarf just walked around a corner, saw you, threw a little +axe at you which missed, cursed, and ran away. + +You're on east bank of fissure. + +There is a little axe here. + +A crystal bridge spans the fissure. + +> w + +There is a threatening little dwarf in the room with you! + +One sharp nasty knife is thrown at you! + +It misses! + +You're on west bank of fissure. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> get knife + +The dwarves' knives vanish as they strike the walls of the cave. + +> look + +Sorry, but I am not allowed to give more detail. I will repeat the +long description of your location. + +There is a threatening little dwarf in the room with you! + +One sharp nasty knife is thrown at you! + +It misses! + +You are on the west side of the fissure in the Hall of Mists. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> get knife + +I see no knife here. + +> quit + +Do you really want to quit now? + +> yes + +OK + +You scored 59 out of a possible 430, using 50 turns. + +Your score qualifies you as a novice class adventurer. + +To achieve the next higher rating, you need 62 more points. diff --git a/tests/knife.log b/tests/knife.log new file mode 100644 index 0000000..1c811ea --- /dev/null +++ b/tests/knife.log @@ -0,0 +1,57 @@ +## Test whether KNIVES_VANISH can be issued twice +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE avoide spuriuous failure on second "get knife" +no +seed 1640849217 +e +get lamp +xyzzy +get rod +on +w +w +w +d +w +wave rod +w +e +w +e +w +e +w +e +w +e +w +e +w +e +w +e +w +e +w +e +w +e +w +e +w +e +w +e +w +e +w +e +w +e +w +get knife +look +get knife +quit +yes diff --git a/tests/lampdim.chk b/tests/lampdim.chk index 2ea31c6..cfbb010 100644 --- a/tests/lampdim.chk +++ b/tests/lampdim.chk @@ -2508,7 +2508,7 @@ There is a message scrawled in the dust in a flowery script, reading: There is a massive vending machine here, swung back to reveal a southward passage. - +> You scored 343 out of a possible 430, using 406 turns. Your score puts you in Master Adventurer Class C. diff --git a/tests/lampdim.log b/tests/lampdim.log index fe4fc4e..5042356 100644 --- a/tests/lampdim.log +++ b/tests/lampdim.log @@ -1,4 +1,6 @@ ## Test the case where your lamp goes dim +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in diff --git a/tests/lampdim2.chk b/tests/lampdim2.chk index bd6215a..740e719 100644 --- a/tests/lampdim2.chk +++ b/tests/lampdim2.chk @@ -2595,7 +2595,7 @@ It is now pitch dark. If you proceed you will likely fall into a pit. Your lamp has run out of power. - +> You scored 368 out of a possible 430, using 423 turns. Your score puts you in Master Adventurer Class C. diff --git a/tests/lampdim2.log b/tests/lampdim2.log index 7004519..0f33787 100644 --- a/tests/lampdim2.log +++ b/tests/lampdim2.log @@ -1,4 +1,6 @@ ## Try (and fail) to carry message at vending machine +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in @@ -426,4 +428,4 @@ n s n s -lamp on \ No newline at end of file +lamp on diff --git a/tests/lampdim3.log b/tests/lampdim3.log index cb8ba7a..e7b1b44 100644 --- a/tests/lampdim3.log +++ b/tests/lampdim3.log @@ -1,4 +1,6 @@ ## Die while closing +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in @@ -432,4 +434,4 @@ n s n up -down \ No newline at end of file +down diff --git a/tests/listen.log b/tests/listen.log index 135fdbc..b29570c 100644 --- a/tests/listen.log +++ b/tests/listen.log @@ -1,4 +1,6 @@ ## Check that listen command hears all objects as well as location sound +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause no seed 1181530211 e diff --git a/tests/listenloud.log b/tests/listenloud.log index 3e820e4..6d5ce61 100644 --- a/tests/listenloud.log +++ b/tests/listenloud.log @@ -1,4 +1,6 @@ ## Attempt to listen at a loud location +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56 n seed 1495951709 @@ -139,4 +141,4 @@ u reservoir Q'IBJ n -listen \ No newline at end of file +listen diff --git a/tests/lockchain.chk b/tests/lockchain.chk index 791991f..5645019 100644 --- a/tests/lockchain.chk +++ b/tests/lockchain.chk @@ -1888,3 +1888,12 @@ 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 219 out of a possible 430, using 328 turns. + +You may now consider yourself a "Seasoned Adventurer". + +To achieve the next higher rating, you need 32 more points. diff --git a/tests/lockchain.log b/tests/lockchain.log index a9db9e6..78ae677 100644 --- a/tests/lockchain.log +++ b/tests/lockchain.log @@ -1,4 +1,6 @@ ## Test multiple re-locking and unlocking of bear's chain +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56 n seed 1635997320 @@ -334,3 +336,4 @@ s listen n jump +n diff --git a/tests/logopt.chk b/tests/logopt.chk index 4defeae..e48c318 100644 --- a/tests/logopt.chk +++ b/tests/logopt.chk @@ -21,7 +21,7 @@ There is a bottle of water here. > - +> You scored 32 out of a possible 430, using 1 turn. You are obviously a rank amateur. Better luck next time. diff --git a/tests/logopt.log b/tests/logopt.log index 5074144..3ba2fb5 100644 --- a/tests/logopt.log +++ b/tests/logopt.log @@ -1,4 +1,6 @@ ## Exercise logging option and seed dump +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause #options: -l scratch.tmp n in diff --git a/tests/foofum.chk b/tests/magicwords.chk similarity index 98% rename from tests/foofum.chk rename to tests/magicwords.chk index 6424ad7..4db287a 100644 --- a/tests/foofum.chk +++ b/tests/magicwords.chk @@ -13,6 +13,14 @@ Seed set to 1838473132 You're in front of building. +> foo + +Nothing happens. + +> say foo + +Nothing happens. + > in You are inside a building, a well house for a large spring. @@ -25,6 +33,54 @@ There is food here. There is a bottle of water here. +> say fee + +OK + +> say fie + +OK + +> say foe + +OK + +> say foo + +Nothing happens. + +> z + +OK + +> say fee + +OK + +> say fie + +OK + +> say foe + +OK + +> say fum + +Well, that was remarkably pointless! + +> z + +OK + +> find foo + +Nothing happens. + +> z + +OK + > take lamp OK @@ -1822,15 +1878,8 @@ OK What's the matter, can't you read? Now you'd best start over. -> quit - -Do you really want to quit now? - -> yes - -OK - -You scored 253 out of a possible 430, using 304 turns. +> +You scored 253 out of a possible 430, using 317 turns. You have reached "Junior Master" status. diff --git a/tests/magicwords.log b/tests/magicwords.log new file mode 100644 index 0000000..02ed3aa --- /dev/null +++ b/tests/magicwords.log @@ -0,0 +1,359 @@ +## Test processing of various fee fie foe foo fum cases. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +# +# How they're supposed to work: +# +# 1. The word "fum", from the famous phrase, "fee fie foe fum" is treated +# as a red herring for the player and is handled differently in the +# logic of the game +# +# 2. Each word of the magic phrase and the word "fum" can be preceded by +# the word "say", so "say fee", "say fie", etc. will work, as +# well. For "say fum", 'Okay, "FUM"' should NOT be the response, +# similar to what is seen when other non-magic words are uttered with +# "say" +# +# 3. The sequence is triggered by the first word "fee" only. If any of +# the other words of the phrase or "fum" are said before "fee", +# "nothing happens" +# +# 4. The phrase "fee fie foe foo" must be entered as four separate +# commands, in order, without interruption. A move, like "east" or a +# non-move, like "look", are both considered interruptions +# +# 5. Once the sequence has begun, if any of the words of the phrase, +# including a second "fee", are said out of order, or "fum" is spoken at +# all during the sequence, the player is admonished for not being able +# to read. The assumption here is the player at some point in the time +# had previously read the phrase, but then messes up the order and/or +# thinks "fum" was a part of the phrase when they attempt to speak +# it. The player then must say "fee" again to restart the sequence. And +# to clarify, a second "fee" in the sequence triggers the admonishment, +# it does not restart the sequence +# +n +seed 1838473132 +# Test isolated 'foo' word +foo +say foo +in +# Say bigwords ending with foo when not in Giant's Room +say fee +say fie +say foe +say foo +z +# Say bigwords ending with fum when not in Giant's Room +say fee +say fie +say foe +say fum +z +find foo +z +take lamp +xyzzy +on +take rod +e +take cage +w +w +w +drop rod +take bird +take rod +w +free bird +wave rod +take necklace +drop rod +take bird +take rod +d +d +free bird +drop rod +drop cage +take cage +take bird +w +take coins +e +s +take jewelry +n +up +s +take gold +n +d +n +n +plugh +extinguish lamp +drop coins +drop jewelry +drop necklace +drop gold +plugh +on +s +take silver +s +sw +take axe +w +kill dragon +yes +drink blood +take rug +e +e +up +d +n +n +off +plugh +inven +drop rug +drop silver +out +s +w +n +take appendage +free bird +drop cage +listen +s +s +n +in +take water +plugh +on +plover +ne +take pyramid +s +plover +s +s +take rod +up +w +wave rod +drop rod +west +take diamonds +w +w +w +s +sw +se +s +kill machine +s +s +kill ogre +n +take ruby +s +w +n +n +n +nw +d +e +e +e +e +e +throw axe +take axe +n +n +n +off +plugh +drop ruby +drop diamonds +drop pyramid +plugh +on +s +s +u +n +n +d +bedquilt +throw axe +take axe +slab +s +d +water plant +u +w +u +reservoir +H'CFL +n +n +nw +u +u +u +u +ne +take ebony +sw +d +d +d +d +d +take water +s +s +s +s +d +s +d +water plant +u +drop appendage +e +d +get oil + +u +w +d +climb +w +n +oil door +drop bottle +n +take trident +w +d +se +n +w +drop trident +drop ebony +drop axe +drop lantern +e +take emerald +w +take lamp +take axe +take ebony +take trident +nw +s +take vase +se +throw axe +take axe +e +take pillow +w +drop axe +ne +e +n +open clam +s +u +e +u +n +off +plugh +drop pillow +drop vase +drop trident +drop emerald +drop ebony +take keys +take food +plugh +on +s +d +w +d +n +d +d +take pearl +u +u +s +w +w +w +w +d +climb +w +get eggs +n +take bottle +n +w +d +sw +u +toss eggs +ne +ne +barren +in +feed bear +unlock chain +take chain +take bear +fork +ne +e +take spices +drop keys +fork +w +w +sw +free bear +inven +sw +sw +d +se +se +w +d +get oil +up +e +take axe +w +w +d +climb +w +# Now that we're at the Giant's Room, actual testing can start +fee +fie +foe +fum diff --git a/tests/mazealldiff.chk b/tests/mazealldiff.chk index 87aeb0d..00168a4 100644 --- a/tests/mazealldiff.chk +++ b/tests/mazealldiff.chk @@ -2509,7 +2509,7 @@ You are in a maze of little twisty passages, all different. You are in a twisting little maze of passages, all different. - +> You scored 343 out of a possible 430, using 409 turns. Your score puts you in Master Adventurer Class C. diff --git a/tests/mazealldiff.log b/tests/mazealldiff.log index d129c13..72f142b 100644 --- a/tests/mazealldiff.log +++ b/tests/mazealldiff.log @@ -1,4 +1,6 @@ ## Coverage of all LOC_DIFFERENT* +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in diff --git a/tests/mazehint.chk b/tests/mazehint.chk index 0d00119..5c72b61 100644 --- a/tests/mazehint.chk +++ b/tests/mazehint.chk @@ -618,7 +618,7 @@ Do you want the hint? You can make the passages look less alike by dropping things. - +> You scored 71 out of a possible 430, using 113 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/mazehint.log b/tests/mazehint.log index 454b4f9..8cf1ddb 100644 --- a/tests/mazehint.log +++ b/tests/mazehint.log @@ -1,4 +1,6 @@ ## Elicit the maze hint. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1071883378 in diff --git a/tests/multifile.chk b/tests/multifile.chk index 910fee2..22bc511 100644 --- a/tests/multifile.chk +++ b/tests/multifile.chk @@ -1,7 +1,9 @@ Welcome to Adventure!! Would you like instructions? -> ## Test isolated 'foo' word +> ## Test handling of object after transitive verb. +> # SPDX-FileCopyrightText: Copyright Eric S. Raymond +> # SPDX-License-Identifier: BSD-2-Clause > n > n @@ -9,18 +11,44 @@ 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. -> foo -> foo +> seed 826186526 +> seed 826186526 -Nothing happens. +Seed set to 826186526 + +You're in front of building. + +> in +> 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. + +> get +> get + +Get what? + +> food +> food + +OK > inven > inven -You're not carrying anything. +You are currently holding the following: +Tasty food -You scored 32 out of a possible 430, using 2 turns. +You scored 32 out of a possible 430, using 4 turns. You are obviously a rank amateur. Better luck next time. diff --git a/tests/newfilter b/tests/newfilter new file mode 100755 index 0000000..c5bad71 --- /dev/null +++ b/tests/newfilter @@ -0,0 +1,11 @@ +#!/bin/sh +# +# Filter the output from Open Adventure versions to make it compatible with +# the filtered versions of logs made from advent430. +sed \ + -e '/bridge now spans the fissure/s//bridge spans the fissure/' \ + -e '/ground/s//surface/' \ + -e '/floor/s//surface/' \ + -e "/Well, that was remarkably pointless!/s//What's the matter, can't you read? Now you'd best start over./" \ + +# end diff --git a/tests/notrident.chk b/tests/notrident.chk index 8e08825..88a8d6d 100644 --- a/tests/notrident.chk +++ b/tests/notrident.chk @@ -1316,7 +1316,7 @@ You're at brink of small pit. > - +> You scored 179 out of a possible 430, using 224 turns. You may now consider yourself a "Seasoned Adventurer". diff --git a/tests/notrident.log b/tests/notrident.log index e51a325..1faab2d 100644 --- a/tests/notrident.log +++ b/tests/notrident.log @@ -1,4 +1,6 @@ ## Try to open clam without trident and fail +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1635997320 in diff --git a/tests/ogre_no_dwarves.chk b/tests/ogre_no_dwarves.chk index dff2bc5..37492fa 100644 --- a/tests/ogre_no_dwarves.chk +++ b/tests/ogre_no_dwarves.chk @@ -152,7 +152,7 @@ A formidable ogre bars the northern exit. The ogre, who despite his bulk is quite agile, easily dodges your attack. He seems almost amused by your puny effort. - +> You scored 59 out of a possible 430, using 23 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/ogre_no_dwarves.log b/tests/ogre_no_dwarves.log index 1d5b269..4326320 100644 --- a/tests/ogre_no_dwarves.log +++ b/tests/ogre_no_dwarves.log @@ -1,4 +1,6 @@ ## Try to attack ogre with no dwarves present (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 25508795 in @@ -23,4 +25,4 @@ s kill machine s s -attack \ No newline at end of file +attack diff --git a/tests/ogrehint.chk b/tests/ogrehint.chk index dbb8d8f..9391193 100644 --- a/tests/ogrehint.chk +++ b/tests/ogrehint.chk @@ -720,7 +720,7 @@ Brass lantern Leporine appendage Platinum pyramid - +> You scored 101 out of a possible 430, using 108 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/ogrehint.log b/tests/ogrehint.log index 12a9d02..38e423d 100644 --- a/tests/ogrehint.log +++ b/tests/ogrehint.log @@ -1,4 +1,6 @@ ## Elicit the ogre hint. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 437547289 seed 1071883378 diff --git a/tests/oilplant.chk b/tests/oilplant.chk index 2f74824..167a3b7 100644 --- a/tests/oilplant.chk +++ b/tests/oilplant.chk @@ -1146,7 +1146,7 @@ The plant indignantly shakes the oil off its leaves and asks, "Water?" You can't be serious! - +> You scored 185 out of a possible 430, using 187 turns. You may now consider yourself a "Seasoned Adventurer". diff --git a/tests/oilplant.log b/tests/oilplant.log index 35eab35..1b3bf85 100644 --- a/tests/oilplant.log +++ b/tests/oilplant.log @@ -1,4 +1,6 @@ ## Attempt to oil the beanstalk after watering it +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in diff --git a/tests/oldfilter b/tests/oldfilter new file mode 100755 index 0000000..c834e5f --- /dev/null +++ b/tests/oldfilter @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Filter the output from advent430 to make it compatible with +# newer ones +sed \ + -e '/ *$/s///' \ + -e '/bridge now spans the fissure/s//bridge spans the fissure/' \ + -e '/imbedded/s//embedded/' \ + -e '/persian/s//Persian/' \ + -e '/swiss/s//Swiss/' \ + -e '/eying/s//eyeing/' \ + -e '/threshhold/s//threshold/' \ + -e '/AXE/s//axe/' \ + -e '/BAR/s//bar/' \ + -e '/BOO/s//boo/' \ + -e '/DOING/s//doing/' \ + -e '/EW/s//ew/' \ + -e '/FROB/s//frob/' \ + -e '/GRATE/s//grate/' \ + -e '/NAGGING/s//nagging/' \ + -e '/RUB/s//rub/' \ + -e '/SOMETHING/s//something/' \ + -e '/ground/s//surface/' \ + -e '/floor/s//surface/' \ + + +#end + diff --git a/tests/oldstyle.log b/tests/oldstyle.log index a70456d..05b7c8a 100644 --- a/tests/oldstyle.log +++ b/tests/oldstyle.log @@ -1,5 +1,8 @@ ## Simple quit +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause #options: -o +#NOCOMPARE Comment not interpreted by advent430 n i l diff --git a/tests/outcheck.sh b/tests/outcheck.sh new file mode 100755 index 0000000..91901a5 --- /dev/null +++ b/tests/outcheck.sh @@ -0,0 +1,7 @@ +#! /bin/sh +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +case $? in + 0) echo "ok - $1 succeeded";; + *) echo "not ok - $1 failed";; +esac diff --git a/tests/oysterbug.chk b/tests/oysterbug.chk new file mode 100644 index 0000000..871b185 --- /dev/null +++ b/tests/oysterbug.chk @@ -0,0 +1,2930 @@ + +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 1838473132 + +Seed set to 1838473132 + +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 lamp + +OK + +> xyzzy + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> on + +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 + +> e + +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 + +> w + +You're in debris room. + +> 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. + +> drop rod + +OK + +> take bird + +OK + +> take rod + +OK + +> 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. + +> free bird + +OK + +> wave rod + +The bird flies about agitatedly for a moment, then disappears through +the crack. It reappears shortly, carrying in its beak a jade +necklace, which it drops at your feet. + +> take necklace + +OK + +> drop rod + +OK + +> take bird + +OK + +> take rod + +OK + +> d + +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. + +> d + +You are in the Hall of the Mountain King, with passages off in all +directions. + +A huge green fierce snake bars the way! + +> free bird + +The little bird attacks the green snake, and in an astounding flurry +drives the snake away. + +> drop rod + +OK + +> drop cage + +OK + +> take cage + +OK + +> take bird + +OK + +> w + +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 three foot black rod with a rusty star on an end lies nearby. + +> s + +You are in the south side chamber. + +There is precious jewelry here! + +> take jewelry + +OK + +> n + +You're in Hall of Mt King. + +A three foot black rod with a rusty star on an end lies nearby. + +> up + +You're in Hall of Mists. + +Rough stone steps lead up the dome. + +> s + +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. + +> d + +You're in Hall of Mt King. + +A three foot black rod with a rusty star on an end lies nearby. + +> n + +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! + +> n + +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. + +A hollow voice says "PLUGH". + +> plugh + +>>Foof!<< + +You're inside building. + +There are some keys on the ground here. + +There is food here. + +There is a bottle of water here. + +> extinguish lamp + +Your lamp is now off. + +> drop coins + +OK + +> drop jewelry + +OK + +> drop necklace + +OK + +> drop gold + +OK + +> plugh + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> on + +Your lamp is now on. + +You're at "Y2". + +> s + +You're in n/s passage above e/w passage. + +There are bars of silver here! + +> take silver + +OK + +> s + +You're in Hall of Mt King. + +A three foot black rod with a rusty star on an end lies nearby. + +> sw + +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 a secret canyon which here runs e/w. It crosses over a +very tight canyon 15 feet below. If you go down you may not be able +to get back up. + +There is a little axe here. + +> take axe + +OK + +> w + +You are in a secret canyon which exits to the north and east. + +A huge green fierce dragon bars the way! + +The dragon is sprawled out on a Persian rug!! + +> kill dragon + +With what? Your bare hands? + +> yes + +Congratulations! You have just vanquished a dragon with your bare +hands! (Unbelievable, isn't it?) + +You are in a secret canyon which exits to the north and east. + +There is a Persian rug spread out on the floor! + +The blood-specked body of a huge green dead dragon lies to one side. + +> drink blood + +Your head buzzes strangely for a moment. + +> take rug + +OK + +> e + +You're in secret e/w canyon above tight canyon. + +> e + +You are in the Hall of the Mountain King, with passages off in all +directions. + +A three foot black rod with a rusty star on an end lies nearby. + +> up + +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 Mists. + +Rough stone steps lead up the dome. + +> d + +There is a threatening little dwarf in the room with you! + +You're in Hall of Mt King. + +A three foot black rod with a rusty star on an end lies nearby. + +> n + +There is a threatening little dwarf in the room with you! + +You're in n/s passage above e/w passage. + +> n + +There is a threatening little dwarf in the room with you! + +You're at "Y2". + +A hollow voice says "PLUGH". + +> off + +Your lamp is now off. + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> plugh + +>>Foof!<< + +You're inside building. + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is precious jewelry here! + +There are many coins here! + +There are some keys on the ground here. + +There is food here. + +There is a bottle of water here. + +> inven + +You are currently holding the following: +Brass lantern +Wicker cage +Little bird in cage +Dwarf's axe +Bars of silver +Persian rug + +> drop rug + +OK + +> drop silver + +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. + +> w + +You are wandering aimlessly through the forest. + +> n + +You are wandering aimlessly through the forest. + +Your keen eye spots a severed leporine appendage lying on the ground. + +> take appendage + +OK + +> free bird + +OK + +> drop cage + +OK + +> listen + +The bird is singing to you in gratitude for your having returned it to +its home. In return, it informs you of a magic word which it thinks +you may find useful somewhere near the Hall of Mists. The magic word +changes frequently, but for now the bird believes it is "H'CFL". You +thank the bird for this information, and it flies off into the forest. + +> s + +You are wandering aimlessly through the forest. + +> s + +You're in valley. + +> n + +You're in front of building. + +> in + +You're inside building. + +There are bars of silver here! + +There is a Persian rug spread out on the floor! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is precious jewelry here! + +There are many coins here! + +There are some keys on the ground here. + +There is food here. + +There is a bottle of water here. + +> take water + +OK + +> plugh + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> on + +Your lamp is now on. + +You're at "Y2". + +> plover + +>>Foof!<< + +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west. A dark corridor leads ne. + +There is an emerald here the size of a plover's egg! + +> ne + +You're in the dark-room. A corridor leading south is the only exit. + +A massive stone tablet embedded in the wall reads: +"Congratulations on bringing light into the dark-room!" + +There is a platinum pyramid here, 8 inches on a side! + +> take pyramid + +OK + +> s + +You're in Plover Room. + +There is an emerald here the size of a plover's egg! + +> plover + +>>Foof!<< + +You're at "Y2". + +A hollow voice says "PLUGH". + +> s + +You're in n/s passage above e/w passage. + +> s + +You're in Hall of Mt King. + +A three foot black rod with a rusty star on an end lies nearby. + +> take rod + +OK + +> up + +You're in Hall of Mists. + +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. + +> wave rod + +A crystal bridge now spans the fissure. + +> drop rod + +OK + +> west + +You are on the west side of the fissure in the Hall of Mists. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> take diamonds + +OK + +> w + +There is a threatening little dwarf in the room with you! + +One sharp nasty knife is thrown at you! + +It misses! + +You are at the west end of the Hall of Mists. A low wide crawl +continues west and another goes north. To the south is a little +passage 6 feet off the floor. + +> w + +There is a threatening little dwarf in the room with you! + +You are at the east end of a very long hall apparently without side +chambers. To the east a low wide crawl slants up. To the north a +round two foot hole slants down. + +> w + +There is a threatening little dwarf in the room with you! + +You are at the west end of a very long featureless hall. The hall +joins up with a narrow north/south passage. + +> s + +There is a threatening little dwarf in the room with you! + +You are in a maze of twisty little passages, all different. + +> sw + +There is a threatening little dwarf in the room with you! + +You are in a little maze of twisty passages, all different. + +> se + +There is a threatening little dwarf in the room with you! + +You are in a little maze of twisting passages, all different. + +> s + +There is a threatening little dwarf in the room with you! + +Dead end + +There is a massive and somewhat battered vending machine here. The +instructions on it read: "Drop coins here to receive fresh batteries." + +> kill machine + +As you strike the vending machine, it pivots backward along with a +section of wall, revealing a dark passage leading south. + +> s + +There is a threatening little dwarf in the room with you! + +You are in a long, rough-hewn, north/south corridor. + +> s + +There is a threatening little dwarf in the room with you! + +You are in a large chamber with passages to the west and north. + +A formidable ogre bars the northern exit. + +> kill ogre + +The ogre, who despite his bulk is quite agile, easily dodges your +attack. He seems almost amused by your puny effort. + +One sharp nasty knife is thrown at you! + +The ogre, distracted by your rush, is struck by the knife. With a +blood-curdling yell he turns and bounds after the dwarf, who flees +in panic. You are left alone in the room. + +> n + +You are in the ogre's storeroom. The only exit is to the south. + +There is an enormous ruby here! + +> take ruby + +OK + +> s + +You are in a large chamber with passages to the west and north. + +> w + +You are in a long, rough-hewn, north/south corridor. + +> n + +Dead end + +There is a massive vending machine here, swung back to reveal a +southward passage. + +> n + +You are in a little maze of twisting passages, all different. + +> n + +You are in a little maze of twisty passages, all different. + +> nw + +You are in a maze of twisty little passages, all different. + +> d + +You're at west end of long hall. + +> e + +You're at east end of long hall. + +> e + +You're at west end of Hall of Mists. + +> e + +You're on west bank of fissure. + +A crystal bridge spans the fissure. + +> e + +You're on east bank of fissure. + +A three foot black rod with a rusty star on an end lies nearby. + +A crystal bridge spans the fissure. + +> e + +There is a threatening little dwarf in the room with you! + +One sharp nasty knife is thrown at you! + +It misses! + +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. + +> throw axe + +You killed a little dwarf. The body vanishes in a cloud of greasy +black smoke. + +You're in Hall of Mists. + +There is a little axe here. + +Rough stone steps lead up the dome. + +> take axe + +OK + +> n + +You're in Hall of Mt King. + +> n + +You're in n/s passage above e/w passage. + +> n + +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. + +A hollow voice says "PLUGH". + +> off + +Your lamp is now off. + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> plugh + +>>Foof!<< + +You're inside building. + +There are bars of silver here! + +There is a Persian rug spread out on the floor! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is precious jewelry here! + +There are many coins here! + +There are some keys on the ground here. + +There is food here. + +> drop ruby + +OK + +> drop diamonds + +OK + +> drop pyramid + +OK + +> plugh + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> on + +Your lamp is now on. + +You're at "Y2". + +A hollow voice says "PLUGH". + +> s + +You are in a low n/s passage at a hole in the floor. The hole goes +down to an e/w passage. + +> s + +You're in Hall of Mt King. + +> u + +You're in Hall of Mists. + +Rough stone steps lead up the dome. + +> n + +You are in the Hall of the Mountain King, with passages off in all +directions. + +> n + +You're in n/s passage above e/w passage. + +> d + +You are in a dirty broken passage. To the east is a crawl. To the +west is a large passage. Above you is a hole to another passage. + +> bedquilt + +There is a threatening little dwarf in the room with you! + +You are in Bedquilt, a long east/west passage with holes everywhere. +To explore at random select north, south, up, or down. + +> throw axe + +You killed a little dwarf. + +You're in Bedquilt. + +There is a little axe here. + +> take axe + +OK + +> slab + +You are in a large low circular chamber whose floor is an immense slab +fallen from the ceiling (Slab Room). East and west there once were +large passages, but they are now filled with boulders. Low small +passages go north and south, and the south one quickly bends west +around the boulders. + +> s + +You are at the west end of the Twopit Room. There is a large hole in +the wall above the pit at this end of the room. + +> d + +You are at the bottom of the western pit in the Twopit Room. There is +a large hole in the wall about 25 feet above you. + +There is a tiny little plant in the pit, murmuring "water, water, ..." + +> water plant + +The plant spurts into furious growth for a few seconds. + +You're in west pit. + +There is a 12-foot-tall beanstalk stretching up out of the pit, +bellowing "WATER!! WATER!!" + +> u + +You're at west end of Twopit Room. + +The top of a 12-foot-tall beanstalk is poking out of the west pit. + +> w + +You're in Slab Room. + +> u + +You are in a secret n/s canyon above a large room. + +> reservoir + +You are at the edge of a large underground reservoir. An opaque cloud +of white mist fills the room and rises rapidly upward. The lake is +fed by a stream, which tumbles out of a hole in the wall about 10 feet +overhead and splashes noisily into the water somewhere within the +mist. There is a passage going back toward the south. + +> H'CFL + +The waters have parted to form a narrow path across the reservoir. + +> n + +You are walking across the bottom of the reservoir. Walls of water +rear up on either side. The roar of the water cascading past is +nearly deafening, and the mist is so thick you can barely see. + +> n + +You are at the northern edge of the reservoir. A northwest passage +leads sharply up from here. + +The waters have parted to form a narrow path across the reservoir. + +> nw + +You are scrambling along a treacherously steep, rocky passage. + +> u + +You are on a very steep incline, which widens at it goes upward. + +> u + +You are at the base of a nearly vertical cliff. There are some +slim footholds which would enable you to climb up, but it looks +extremely dangerous. Here at the base of the cliff lie the remains +of several earlier adventurers who apparently failed to make it. + +> u + +You are climbing along a nearly vertical cliff. + +> u + +Just as you reach the top, your foot slips on a loose rock and you +make one last desperate grab. Your luck holds, as does your grip. +With an enormous heave, you lift yourself to the ledge above. + +You are on a small ledge at the top of a nearly vertical cliff. +There is a low crawl leading off to the northeast. + +> ne + +You have reached a dead end. + +There is a richly-carved ebony statuette here! + +> take ebony + +OK + +> sw + +You're at top of cliff. + +> d + +You are climbing along a nearly vertical cliff. + +> d + +You're at base of cliff. + +> d + +You are on a very steep incline, which widens at it goes upward. + +> d + +You are scrambling along a treacherously steep, rocky passage. + +> d + +You're north of reservoir. + +The waters have parted to form a narrow path across the reservoir. + +> take water + +Your bottle is now full of water. + +> s + +You're at bottom of reservoir. + +> s + +You're at reservoir. + +The waters have parted to form a narrow path across the reservoir. + +> s + +You are in a north/south canyon about 25 feet across. The floor is +covered by white mist seeping in from the north. The walls extend +upward for well over 100 feet. Suspended from some unseen point far +above you, an enormous two-sided mirror is hanging parallel to and +midway between the canyon walls. (The mirror is obviously provided +for the use of the dwarves who, as you know, are extremely vain.) A +small window can be seen in either wall, some fifty feet up. + +> break mirror + +It is too far up for you to reach. + +> s + +You are in a secret n/s canyon above a large room. + +> d + +You're in Slab Room. + +> s + +You're at west end of Twopit Room. + +The top of a 12-foot-tall beanstalk is poking out of the west pit. + +> d + +You're in west pit. + +There is a 12-foot-tall beanstalk stretching up out of the pit, +bellowing "WATER!! WATER!!" + +> water plant + +The plant grows explosively, almost filling the bottom of the pit. + +You're in west pit. + +There is a gigantic beanstalk stretching all the way up to the hole. + +> u + +You're at west end of Twopit Room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> drop appendage + +OK + +> e + +You are at the east end of the Twopit Room. The floor here is +littered with thin rock slabs, which make it easy to descend the pits. +There is a path here bypassing the pits to connect passages from east +and west. There are holes all over, but the only big one is on the +wall directly over the west pit where you can't get to it. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> d + +You are at the bottom of the eastern pit in the Twopit Room. There is +a small pool of oil in one corner of the pit. + +> get oil + +Your bottle is now full of oil. + +> + +> u + +You're at east end of Twopit Room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> w + +You're at west end of Twopit Room. + +Your keen eye spots a severed leporine appendage lying on the ground. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> d + +You're in west pit. + +There is a gigantic beanstalk stretching all the way up to the hole. + +> climb + +You clamber up the plant and scurry through the hole at the top. + +You are in a long, narrow corridor stretching out of sight to the +west. At the eastern end is a hole through which you can see a +profusion of leaves. + +> w + +You are in the Giant Room. The ceiling here is too high up for your +lamp to show it. Cavernous passages lead east, north, and south. On +the west wall is scrawled the inscription, "FEE FIE FOE FOO" [sic]. + +There is a large nest here, full of golden eggs! + +> n + +You are at one end of an immense north/south passage. + +The way north is barred by a massive, rusty, iron door. + +> unlock + +The door is extremely rusty and refuses to open. + +> oil door + +The oil has freed up the hinges so that the door will now move, +although it requires some effort. + +> drop bottle + +OK + +> n + +You are in a magnificent cavern with a rushing stream, which cascades +over a sparkling waterfall into a roaring whirlpool which disappears +through a hole in the floor. Passages exit to the south and west. + +There is a jewel-encrusted trident here! + +> take trident + +OK + +> w + +You are at the top of a steep incline above a large room. You could +climb down here, but you would not be able to climb up. There is a +passage leading back to the north. + +> d + +You are in a large low room. Crawls lead north, se, and sw. + +> se + +This is the Oriental Room. Ancient oriental cave drawings cover the +walls. A gently sloping passage leads upward to the north, another +passage leads se, and a hands and knees crawl leads west. + +There is a delicate, precious, ming vase here! + +> n + +You are following a wide path around the outer edge of a large cavern. +Far below, through a heavy white mist, strange splashing noises can be +heard. The mist rises up through a fissure in the ceiling. The path +exits to the south and west. + +> w + +You are in an alcove. A small nw path seems to widen after a short +distance. An extremely tight tunnel leads east. It looks like a very +tight squeeze. An eerie light can be seen at the other end. + +> drop trident + +OK + +> drop ebony + +OK + +> drop axe + +OK + +> drop lantern + +OK + +> e + +You're in Plover Room. + +There is an emerald here the size of a plover's egg! + +> take emerald + +OK + +> w + +You're in alcove. + +There is a lamp shining nearby. + +There is a little axe here. + +There is a richly-carved ebony statuette here! + +There is a jewel-encrusted trident here! + +> take lamp + +OK + +> take axe + +OK + +> take ebony + +OK + +> take trident + +OK + +> nw + +You're in misty cavern. + +> s + +You're in Oriental Room. + +There is a delicate, precious, ming vase here! + +> take vase + +OK + +> se + +There is a threatening little dwarf in the room with you! + +You are in a room whose walls resemble Swiss cheese. Obvious passages +go west, east, ne, and nw. Part of the room is occupied by a large +bedrock block. + +> throw axe + +You killed a little dwarf. + +You're in Swiss Cheese Room. + +There is a little axe here. + +> take axe + +OK + +> e + +You are in the Soft Room. The walls are covered with heavy curtains, +the floor with a thick pile carpet. Moss covers the ceiling. + +A small velvet pillow lies on the floor. + +> take pillow + +OK + +> w + +You're in Swiss Cheese Room. + +> drop axe + +OK + +> ne + +You're in Bedquilt. + +> e + +You are at a complex junction. A low hands and knees passage from the +north joins a higher crawl from the east to make a walking passage +going west. There is also a large room above. The air is damp here. + +> n + +You're in a large room carved out of sedimentary rock. The floor and +walls are littered with bits of shells embedded in the stone. A +shallow passage proceeds downward, and a somewhat steeper one leads +up. A low hands and knees passage enters from the south. + +There is an enormous clam here with its shell tightly closed. + +> open clam + +A glistening pearl falls out of the clam and rolls away. Goodness, +this must really be an oyster. (I never was very good at identifying +bivalves.) Whatever it is, it has now snapped shut again. + +> s + +You're at complex junction. + +> u + +You are in a large room full of dusty rocks. There is a big hole in +the floor. There are cracks everywhere, and a passage leading east. + +> e + +You're in dirty passage. + +> u + +You're in n/s passage above e/w passage. + +> n + +You're at "Y2". + +> off + +Your lamp is now off. + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> plugh + +>>Foof!<< + +You are inside a building, a well house for a large spring. + +There is a platinum pyramid here, 8 inches on a side! + +There are diamonds here! + +There is an enormous ruby here! + +There are bars of silver here! + +There is a Persian rug spread out on the floor! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is precious jewelry here! + +There are many coins here! + +There are some keys on the ground here. + +There is food here. + +> drop pillow + +OK + +> drop vase + +The vase is now resting, delicately, on a velvet pillow. + +> drop trident + +OK + +> drop emerald + +OK + +> drop ebony + +OK + +> take keys + +OK + +> take food + +OK + +> plugh + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +A hollow voice says "PLUGH". + +> on + +Your lamp is now on. + +You're at "Y2". + +A hollow voice says "PLUGH". + +> s + +You're in n/s passage above e/w passage. + +> d + +You're in dirty passage. + +> w + +You're in dusty rock room. + +> d + +You're at complex junction. + +> n + +You're in Shell Room. + +There is an enormous oyster here with its shell tightly closed. + +> d + +You are in a long sloping corridor with ragged sharp walls. + +> d + +You are in a cul-de-sac about eight feet across. + +Off to one side lies a glistening pearl! + +> take pearl + +OK + +> u + +You are in a long sloping corridor with ragged sharp walls. + +> u + +You're in Shell Room. + +There is an enormous oyster here with its shell tightly closed. + +> s + +You're at complex junction. + +> w + +You're in Bedquilt. + +> w + +You're in Swiss Cheese Room. + +There is a little axe here. + +> w + +You're at east end of Twopit Room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> w + +You are at the west end of the Twopit Room. There is a large hole in +the wall above the pit at this end of the room. + +Your keen eye spots a severed leporine appendage lying on the ground. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> d + +You are at the bottom of the western pit in the Twopit Room. There is +a large hole in the wall about 25 feet above you. + +There is a gigantic beanstalk stretching all the way up to the hole. + +> climb + +You clamber up the plant and scurry through the hole at the top. + +You're in narrow corridor. + +> w + +You're in Giant Room. + +There is a large nest here, full of golden eggs! + +> get eggs + +OK + +> n + +You are at one end of an immense north/south passage. + +There is an empty bottle here. + +The way north leads through a massive, rusty, iron door. + +> take bottle + +OK + +> n + +You're in cavern with waterfall. + +> w + +You're at steep incline above large room. + +> d + +You're in large low room. + +> sw + +You are in a long winding corridor sloping out of sight in both +directions. + +> u + +You are on one side of a large, deep chasm. A heavy white mist rising +up from below obscures all view of the far side. A sw path leads away +from the chasm into a winding corridor. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +A burly troll stands by the bridge and insists you throw him a +treasure before you may cross. + +> toss eggs + +The troll catches your treasure and scurries away out of sight. + +> ne + +You are on the far side of the chasm. A ne path leads away from the +chasm on this side. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +The troll is nowhere to be seen. + +> ne + +You're in a long east/west corridor. A faint rumbling noise can be +heard in the distance. + +> barren + +You are standing at the entrance to a large, barren room. A notice +above the entrance reads: "Caution! Bear in room!" + +> in + +You are inside a barren room. The center of the room is completely +empty except for some dust. Marks in the dust lead away toward the +far end of the room. The only exit is the way you came in. + +There is a ferocious cave bear eyeing you from the far end of the room! + +The bear is locked to the wall with a golden chain! + +> feed bear + +The bear eagerly wolfs down your food, after which he seems to calm +down considerably and even becomes rather friendly. + +> unlock + +The chain is now unlocked. + +> unlock chain + +It was already unlocked. + +> take chain + +OK + +> take bear + +OK + +> fork + +You are being followed by a very large, tame bear. + +The path forks here. The left fork leads northeast. A dull rumbling +seems to get louder in that direction. The right fork leads southeast +down a gentle slope. The main corridor enters from the west. + +> ne + +You are being followed by a very large, tame bear. + +The walls are quite warm here. From the north can be heard a steady +roar, so loud that the entire cave seems to be trembling. Another +passage leads south, and a low crawl goes east. + +> e + +You are being followed by a very large, tame bear. + +You are in a small chamber filled with large boulders. The walls are +very warm, causing the air in the room to be almost stifling from the +heat. The only exit is a crawl heading west, through which is coming +a low rumbling. + +There are rare spices here! + +> take spices + +OK + +> drop keys + +OK + +> fork + +You are being followed by a very large, tame bear. + +You're at fork in path. + +> w + +You are being followed by a very large, tame bear. + +You're in corridor. + +> w + +You are being followed by a very large, tame bear. + +You're on ne side of chasm. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +The troll is nowhere to be seen. + +> sw + +The troll steps out from beneath the bridge and blocks your way. + +You are being followed by a very large, tame bear. + +You're on ne side of chasm. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +A burly troll stands by the bridge and insists you throw him a +treasure before you may cross. + +> free bear + +The bear lumbers toward the troll, who lets out a startled shriek and +scurries away. The bear soon gives up the pursuit and wanders back. + +> inven + +You are currently holding the following: +Brass lantern +Small bottle +Glistening pearl +Rare spices +Golden chain + +> sw + +You're on sw side of chasm. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +The troll is nowhere to be seen. + +> sw + +You're in sloping corridor. + +> d + +You're in large low room. + +> se + +You're in Oriental Room. + +> se + +You're in Swiss Cheese Room. + +There is a little axe here. + +> w + +You're at east end of Twopit Room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> d + +You're in east pit. + +> get oil + +Your bottle is now full of oil. + +> up + +You're at east end of Twopit Room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> e + +You are in a room whose walls resemble Swiss cheese. Obvious passages +go west, east, ne, and nw. Part of the room is occupied by a large +bedrock block. + +There is a little axe here. + +> take axe + +OK + +> w + +You are at the east end of the Twopit Room. The floor here is +littered with thin rock slabs, which make it easy to descend the pits. +There is a path here bypassing the pits to connect passages from east +and west. There are holes all over, but the only big one is on the +wall directly over the west pit where you can't get to it. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> w + +You're at west end of Twopit Room. + +Your keen eye spots a severed leporine appendage lying on the ground. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> d + +You're in west pit. + +There is a gigantic beanstalk stretching all the way up to the hole. + +> climb + +You clamber up the plant and scurry through the hole at the top. + +You're in narrow corridor. + +> w + +You're in Giant Room. + +> fee + +OK + +> fie + +OK + +> foe + +OK + +> foo + +There is a large nest here, full of golden eggs! + +> take eggs + +OK + +> s + +You're in narrow corridor. + +> d + +You're in west pit. + +There is a gigantic beanstalk stretching all the way up to the hole. + +> u + +You're at west end of Twopit Room. + +Your keen eye spots a severed leporine appendage lying on the ground. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> w + +You're in Slab Room. + +> u + +You are in a secret n/s canyon above a large room. + +> s + +You are in a secret canyon which exits to the north and east. + +The body of a huge green dead dragon is lying off to one side. + +> e + +You're in secret e/w canyon above tight canyon. + +> e + +You're in Hall of Mt King. + +> n + +You're in n/s passage above e/w passage. + +> n + +You're at "Y2". + +A hollow voice says "PLUGH". + +> off + +Your lamp is now off. + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> plugh + +>>Foof!<< + +You're inside building. + +There is a richly-carved ebony statuette here! + +There is an emerald here the size of a plover's egg! + +There is a jewel-encrusted trident here! + +There is a delicate, precious, ming vase here! + +A small velvet pillow lies on the floor. + +There is a platinum pyramid here, 8 inches on a side! + +There are diamonds here! + +There is an enormous ruby here! + +There are bars of silver here! + +There is a Persian rug spread out on the floor! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is precious jewelry here! + +There are many coins here! + +> drop eggs + +OK + +> drop pearl + +OK + +> drop spices + +OK + +> drop chain + +OK + +> take rug + +OK + +> take ruby + +OK + +> take emerald + +OK + +> out + +You're in front of building. + +> w + +You have walked up a hill, still in the forest. The road slopes back +down the other side of the hill. There is a building in the distance. + +> n + +You are wandering aimlessly through the forest. + +> n + +You are wandering aimlessly through the forest. + +> n + +The forest thins out here to reveal a steep cliff. There is no way +down, but a small ledge can be seen to the west across the chasm. + +A small urn is embedded in the rock. + +> inven + +You are currently holding the following: +Brass lantern +Small bottle +Oil in the bottle +Dwarf's axe +Egg-sized emerald +Persian rug +Giant ruby + +> fill urn + +Your bottle is now empty and the urn is full of oil. + +> light urn + +The urn is now lit. + +> rub urn + +As you rub the urn, there is a flash of light and a genie appears. +His aspect is stern as he advises: "One who wouldst traffic in +precious stones must first learn to recognize the signals thereof." +He wrests the urn from the stone, leaving a small cavity. Turning to +face you again, he fixes you with a steely eye and intones: "Caution!" +Genie and urn vanish in a cloud of amber smoke. The smoke condenses +to form a rare amber gemstone, resting in the cavity in the rock. + +> take amber + +OK + +> drop rug + +OK + +> drop emerald + +The gem fits easily into the cavity. + +The Persian rug stiffens and rises a foot or so off the ground. + +> fly rug + +You board the Persian rug, which promptly whisks you across the chasm. +You have time for a fleeting glimpse of a two thousand foot drop to a +mighty river; then you find yourself on the other side. + +You are on a small ledge on one face of a sheer cliff. There are no +paths away from the ledge. Across the chasm is a small clearing +surrounded by forest. + +There is a Persian rug here, hovering in mid-air! + +A brilliant blue star sapphire is here! + +> take sapphire + +OK + +> fly rug + +The rug ferries you back across the chasm. + +You're at cliff. + +There is an emerald resting in a small cavity in the rock! + +There is a Persian rug here, hovering in mid-air! + +> take emerald + +OK + +> drop ruby + +The gem fits easily into the cavity. + +The Persian rug settles gently to the ground. + +> take rug + +OK + +> drop bottle + +OK + +> take ruby + +OK + +> e + +You are wandering aimlessly through the forest. + +> s + +You are wandering aimlessly through the forest. + +> e + +You're at hill in road. + +> e + +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. + +> in + +Tsk! A wizard wouldn't have to take 350 turns. This is going to cost +you a couple of points. + +You're inside building. + +There is a golden chain lying in a heap on the floor! + +There are rare spices here! + +Off to one side lies a glistening pearl! + +There is a large nest here, full of golden eggs! + +There is a richly-carved ebony statuette here! + +There is a jewel-encrusted trident here! + +There is a delicate, precious, ming vase here! + +A small velvet pillow lies on the floor. + +There is a platinum pyramid here, 8 inches on a side! + +There are diamonds here! + +There are bars of silver here! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is precious jewelry here! + +There are many coins here! + +> drop ruby + +OK + +> drop sapphire + +OK + +> drop amber + +OK + +> drop rug + +OK + +> look + +Sorry, but I am not allowed to give more detail. I will repeat the +long description of your location. + +You are inside a building, a well house for a large spring. + +There is a Persian rug spread out on the floor! + +There is a rare amber gemstone here! + +A brilliant blue star sapphire is here! + +There is an enormous ruby here! + +There is a golden chain lying in a heap on the floor! + +There are rare spices here! + +Off to one side lies a glistening pearl! + +There is a large nest here, full of golden eggs! + +There is a richly-carved ebony statuette here! + +There is a jewel-encrusted trident here! + +There is a delicate, precious, ming vase here! + +A small velvet pillow lies on the floor. + +There is a platinum pyramid here, 8 inches on a side! + +There are diamonds here! + +There are bars of silver here! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is precious jewelry here! + +There are many coins here! + +> plugh + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> on + +Your lamp is now on. + +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. + +> s + +You are in a low n/s passage at a hole in the floor. The hole goes +down to an e/w passage. + +> s + +You're in Hall of Mt King. + +> u + +You're in Hall of Mists. + +Rough stone steps lead up the dome. + +> w + +Out from the shadows behind you pounces a bearded pirate! "Har, har," +he chortles, "I'll just take all this booty and hide it away with me +chest deep in the maze!" He snatches your treasure and vanishes into +the gloom. + +You're on east bank of fissure. + +A three foot black rod with a rusty star on an end lies nearby. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're at west end of Hall of Mists. + +> s + +You are in a maze of twisty little passages, all alike. + +> e + +You are in a maze of twisty little passages, all alike. + +> s + +There is a threatening little dwarf in the room with you! + +One sharp nasty knife is thrown at you! + +It misses! + +You are in a maze of twisty little passages, all alike. + +> throw axe + +You killed a little dwarf. + +You are in a maze of twisty little passages, all alike. + +There is a little axe here. + +> take axe + +OK + +> s + +You are in a maze of twisty little passages, all alike. + +> s + +You are in a maze of twisty little passages, all alike. + +> n + +You are in a maze of twisty little passages, all alike. + +> e + +You are on the brink of a thirty foot pit with a massive orange column +down one wall. You could climb down here but you could not get back +up. The maze continues at this level. + +> e + +You are in a maze of twisty little passages, all alike. + +> nw + +Dead end + +There is an emerald here the size of a plover's egg! + +The pirate's treasure chest is here! + +> take emerald + +OK + +> take chest + +OK + +> se + +You are in a maze of twisty little passages, all alike. + +> n + +You're at brink of pit. + +> d + +You're in bird chamber. + +> e + +You are in an awkward sloping east/west canyon. + +> e + +You're in debris room. + +> off + +Your lamp is now off. + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> xyzzy + +>>Foof!<< + +You're inside building. + +There is a Persian rug spread out on the floor! + +There is a rare amber gemstone here! + +A brilliant blue star sapphire is here! + +There is an enormous ruby here! + +There is a golden chain lying in a heap on the floor! + +There are rare spices here! + +Off to one side lies a glistening pearl! + +There is a large nest here, full of golden eggs! + +There is a richly-carved ebony statuette here! + +There is a jewel-encrusted trident here! + +There is a delicate, precious, ming vase here! + +A small velvet pillow lies on the floor. + +There is a platinum pyramid here, 8 inches on a side! + +There are diamonds here! + +There are bars of silver here! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is precious jewelry here! + +There are many coins here! + +> drop emerald + +OK + +> drop chest + +OK + +> plugh + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> on + +Your lamp is now on. + +You're at "Y2". + +A hollow voice says "PLUGH". + +> s + +You're in n/s passage above e/w passage. + +> d + +Your lamp is getting dim. You'd best start wrapping this up, unless +you can find some fresh batteries. I seem to recall there's a vending +machine in the maze. Bring some coins with you. + +You're in dirty passage. + +> w + +You're in dusty rock room. + +> d + +You're at complex junction. + +> e + +You are in an anteroom leading to a large passage to the east. Small +passages go west and up. The remnants of recent digging are evident. +A sign in midair here says "Cave under construction beyond this point. +Proceed at own risk. [Witt Construction Company]" + +There are a few recent issues of "Spelunker Today" magazine here. + +> take magazine + +OK + +> e + +You are at Witt's End. Passages lead off in *ALL* directions. + +> drop magazine + +OK + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You're at Witt's End. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You're at Witt's End. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You're at Witt's End. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You're at Witt's End. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You're at Witt's End. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You're at Witt's End. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You're at Witt's End. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You're at Witt's End. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You're at Witt's End. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You're at Witt's End. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You're at Witt's End. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You're at Witt's End. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You have crawled around in some little holes and wound up back in the +main passage. + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> e + +You're in anteroom. + +> u + +You are at a complex junction. A low hands and knees passage from the +north joins a higher crawl from the east to make a walking passage +going west. There is also a large room above. The air is damp here. + +> u + +A sepulchral voice reverberating through the cave, says, "Cave closing +soon. All adventurers exit immediately through main office." + +You're in dusty rock room. + +> e + +You're in dirty passage. + +> u + +You're in n/s passage above e/w passage. + +> n + +You're at "Y2". + +> plover + +>>Foof!<< + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west. A dark corridor leads ne. + +> e + +Your lamp has run out of power. + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west. A dark corridor leads ne. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west. A dark corridor leads ne. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west. A dark corridor leads ne. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west. A dark corridor leads ne. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west. A dark corridor leads ne. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west. A dark corridor leads ne. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west. A dark corridor leads ne. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west. A dark corridor leads ne. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> e + +There is no way to go that direction. + +You're in Plover Room. + +> cave + +I need more detailed instructions to do that. + +You're in Plover Room. + +> e + +The sepulchral voice intones, "The cave is now closed." As the echoes +fade, there is a blinding flash of light (and a small puff of orange +smoke). . . . As your eyes refocus, you look around and find... + +You are at the northeast end of an immense room, even larger than the +Giant Room. It appears to be a repository for the "Adventure" +program. Massive torches far overhead bathe the room with smoky +yellow light. Scattered about you can be seen a pile of bottles (all +of them empty), a nursery of young beanstalks murmuring quietly, a bed +of oysters, a bundle of black rods with rusty stars on their ends, and +a collection of brass lanterns. Off to one side a great many dwarves +are sleeping on the floor, snoring loudly. A notice nearby reads: "Do +not disturb the dwarves!" An immense mirror is hanging against one +wall, and stretches to the other end of the room, where various other +sundry objects can be glimpsed dimly in the distance. + +> take oyster + +OK + +Interesting. There seems to be something written on the underside of +the oyster. + +> unlock oyster + +I advise you to put down the oyster before opening it. >WRENCH!< + +> +You scored 389 out of a possible 430, using 466 turns. + +Your score puts you in Master Adventurer Class B. + +To achieve the next higher rating, you need 22 more points. diff --git a/tests/foofum.log b/tests/oysterbug.log similarity index 62% rename from tests/foofum.log rename to tests/oysterbug.log index f713fc7..3b31c7e 100644 --- a/tests/foofum.log +++ b/tests/oysterbug.log @@ -1,4 +1,7 @@ -## Test processing of fee fie foe fum +## Demonstrate fix of buggy response to unlocking oyster while carrying it. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE This fails due to a known bug in advent430 n seed 1838473132 in @@ -173,6 +176,7 @@ take water s s s +break mirror s d s @@ -190,6 +194,7 @@ d climb w n +unlock oil door drop bottle n @@ -272,6 +277,7 @@ ne barren in feed bear +unlock unlock chain take chain take bear @@ -305,6 +311,164 @@ w fee fie foe -fum -quit -yes +foo +take eggs +s +d +u +w +u +s +e +e +n +n +off +plugh +drop eggs +drop pearl +drop spices +drop chain +take rug +take ruby +take emerald +out +w +n +n +n +inven +fill urn +light urn +rub urn +take amber +drop rug +drop emerald +fly rug +take sapphire +fly rug +take emerald +drop ruby +take rug +drop bottle +take ruby +e +s +e +e +in +drop ruby +drop sapphire +drop amber +drop rug +look +plugh +on +s +s +u +w +w +w +s +e +s +throw axe +take axe +s +s +n +e +e +nw +take emerald +take chest +se +n +d +e +e +off +xyzzy +drop emerald +drop chest +plugh +on +s +d +w +d +e +take magazine +e +drop magazine +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +u +u +e +u +n +plover +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +e +cave +e +take oyster +unlock oyster diff --git a/tests/panic.chk b/tests/panic.chk index bd23880..e44e632 100644 --- a/tests/panic.chk +++ b/tests/panic.chk @@ -2631,7 +2631,7 @@ The grate is locked. A mysterious recorded voice groans into life and announces: "This exit is closed. Please leave via main office." - +> You scored 365 out of a possible 430, using 422 turns. Your score puts you in Master Adventurer Class C. diff --git a/tests/panic.log b/tests/panic.log index de6f2c5..248fb72 100644 --- a/tests/panic.log +++ b/tests/panic.log @@ -1,4 +1,6 @@ ## Panic test - attempt to unlock grate after game closed. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in diff --git a/tests/panic2.chk b/tests/panic2.chk index ac42e46..db66581 100644 --- a/tests/panic2.chk +++ b/tests/panic2.chk @@ -2592,7 +2592,7 @@ A mysterious recorded voice groans into life and announces: You're at "Y2". - +> You scored 365 out of a possible 430, using 414 turns. Your score puts you in Master Adventurer Class C. diff --git a/tests/panic2.log b/tests/panic2.log index f95aa5b..ae05f60 100644 --- a/tests/panic2.log +++ b/tests/panic2.log @@ -1,4 +1,6 @@ ## Panic test - attempt to xyzzy out after game is closed. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in diff --git a/tests/pirate_carry.chk b/tests/pirate_carry.chk index f2e7893..72b1681 100644 --- a/tests/pirate_carry.chk +++ b/tests/pirate_carry.chk @@ -316,7 +316,7 @@ You are on the west side of the fissure in the Hall of Mists. A crystal bridge spans the fissure. - +> You scored 67 out of a possible 430, using 43 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/pirate_carry.log b/tests/pirate_carry.log index c84b393..2f7598e 100644 --- a/tests/pirate_carry.log +++ b/tests/pirate_carry.log @@ -1,4 +1,6 @@ ## Check that pirate steals loose treasure from ground (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1837473132 in @@ -44,4 +46,4 @@ e u w wave rod -w \ No newline at end of file +w diff --git a/tests/pirate_pyramid.chk b/tests/pirate_pyramid.chk index a7ac101..873cdaa 100644 --- a/tests/pirate_pyramid.chk +++ b/tests/pirate_pyramid.chk @@ -356,7 +356,7 @@ You are currently holding the following: Brass lantern Wicker cage - +> You scored 69 out of a possible 430, using 50 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/pirate_pyramid.log b/tests/pirate_pyramid.log index 1d16702..b5cbe49 100644 --- a/tests/pirate_pyramid.log +++ b/tests/pirate_pyramid.log @@ -1,4 +1,6 @@ ## Pirate mustn't take pyramid from plover/dark rooms (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1830473132 in diff --git a/tests/pirate_spotted.chk b/tests/pirate_spotted.chk index 5cfc3c4..e8fad72 100644 --- a/tests/pirate_spotted.chk +++ b/tests/pirate_spotted.chk @@ -984,7 +984,7 @@ There is a huge beanstalk growing out of the west pit up to the hole. You are at the bottom of the eastern pit in the Twopit Room. There is a small pool of oil in one corner of the pit. -> g oil +> get oil Your bottle is now full of oil. @@ -1351,7 +1351,7 @@ You're in Giant Room. There is a large nest here, full of golden eggs! -> g +> get OK @@ -1547,7 +1547,7 @@ There is a huge beanstalk growing out of the west pit up to the hole. You're in east pit. -> g oil +> get oil Your bottle is now full of oil. @@ -1839,7 +1839,7 @@ You're in Hall of Mists. Rough stone steps lead up the dome. - +> You scored 123 out of a possible 430, using 295 turns. You have achieved the rating: "Experienced Adventurer". diff --git a/tests/pirate_spotted.log b/tests/pirate_spotted.log index 1eeca5d..f998cdd 100644 --- a/tests/pirate_spotted.log +++ b/tests/pirate_spotted.log @@ -1,4 +1,6 @@ ## Spot pirate to manifest chest before last treasure (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in @@ -156,7 +158,7 @@ u drop appen e d -g oil +get oil u w d @@ -220,7 +222,7 @@ w d climb w -g +get n take bottl n @@ -250,7 +252,7 @@ se se w d -g oil +get oil u e w diff --git a/tests/pitfall.log b/tests/pitfall.log index 6c5adc7..1582283 100644 --- a/tests/pitfall.log +++ b/tests/pitfall.log @@ -1,5 +1,7 @@ ## Death by pitfall -# Die 3 times so we can cover all the opituary messages +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +# Die 3 times so we can cover all the obituary messages n seed 780351908 enter building diff --git a/tests/seedcrash.chk b/tests/placeholder.chk similarity index 61% rename from tests/seedcrash.chk rename to tests/placeholder.chk index 1f10129..349bf74 100644 --- a/tests/seedcrash.chk +++ b/tests/placeholder.chk @@ -7,9 +7,9 @@ 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 1635997320 +> seed 1838473132 -Seed set to 1635997320 +Seed set to 1838473132 You're in front of building. @@ -35,6 +35,17 @@ OK It is now pitch dark. If you proceed you will likely fall into a pit. +> on + +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 @@ -52,16 +63,7 @@ OK > w -It is now pitch dark. If you proceed you will likely fall into a pit. - -> on - -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". +You're in debris room. > w @@ -104,6 +106,10 @@ The bird flies about agitatedly for a moment, then disappears through the crack. It reappears shortly, carrying in its beak a jade necklace, which it drops at your feet. +> take necklace + +OK + > drop rod OK @@ -112,72 +118,6 @@ OK OK -> take jade - -OK - -> e - -You're in bird chamber. - -> e - -You are in an awkward sloping east/west canyon. - -> e - -You're in debris room. - -> off - -Your lamp is now off. - -It is now pitch dark. If you proceed you will likely fall into a pit. - -> xyzzy - ->>Foof!<< - -You're inside building. - -There are some keys on the ground here. - -There is food here. - -There is a bottle of water here. - -> drop jade - -OK - -> xyzzy - ->>Foof!<< - -It is now pitch dark. If you proceed you will likely fall into a pit. - -> on - -Your lamp is now on. - -You're in debris room. - -> w - -You are in an awkward sloping east/west canyon. - -> w - -You're in bird chamber. - -> w - -You're at top of small pit. - -A three foot black rod with a rusty star on an end lies nearby. - -Rough stone steps lead down the pit. - > take rod OK @@ -192,28 +132,7 @@ 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. - -> wave rod - -The bird flies agitatedly about the cage. - -A crystal bridge now spans the fissure. - -> drop rod - -OK - -> e - -You're in Hall of Mists. - -Rough stone steps lead up the dome. - -> n +> d You are in the Hall of the Mountain King, with passages off in all directions. @@ -225,10 +144,39 @@ A huge green fierce snake bars the way! The little bird attacks the green snake, and in an astounding flurry drives the snake away. +> drop rod + +OK + +> drop cage + +OK + +> take cage + +OK + > take bird OK +> w + +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 three foot black rod with a rusty star on an end lies nearby. + > s You are in the south side chamber. @@ -243,12 +191,125 @@ OK You're in Hall of Mt King. +A three foot black rod with a rusty star on an end lies nearby. + +> up + +You're in Hall of Mists. + +Rough stone steps lead up the dome. + +> s + +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. + +> d + +You're in Hall of Mt King. + +A three foot black rod with a rusty star on an end lies nearby. + +> n + +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! + +> n + +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. + +A hollow voice says "PLUGH". + +> plugh + +>>Foof!<< + +You're inside building. + +There are some keys on the ground here. + +There is food here. + +There is a bottle of water here. + +> extinguish lamp + +Your lamp is now off. + +> drop coins + +OK + +> drop jewelry + +OK + +> drop necklace + +OK + +> drop gold + +OK + +> plugh + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> on + +Your lamp is now on. + +You're at "Y2". + +> s + +You're in n/s passage above e/w passage. + +There are bars of silver here! + +> take silver + +OK + +> s + +You're in Hall of Mt King. + +A three foot black rod with a rusty star on an end lies nearby. + > sw +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 a secret canyon which here runs e/w. It crosses over a very tight canyon 15 feet below. If you go down you may not be able to get back up. +There is a little axe here. + +> take axe + +OK + > w You are in a secret canyon which exits to the north and east. @@ -257,10 +318,6 @@ A huge green fierce dragon bars the way! The dragon is sprawled out on a Persian rug!! -> feed dragon - -There's nothing here it wants to eat (except perhaps you). - > kill dragon With what? Your bare hands? @@ -276,6 +333,10 @@ There is a Persian rug spread out on the floor! The blood-specked body of a huge green dead dragon lies to one side. +> feed dragon + +Don't be ridiculous! + > drink blood Your head buzzes strangely for a moment. @@ -290,24 +351,44 @@ You're in secret e/w canyon above tight canyon. > e +You are in the Hall of the Mountain King, with passages off in all +directions. + +A three foot black rod with a rusty star on an end lies nearby. + +> up + +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 Mists. + +Rough stone steps lead up the dome. + +> d + +There is a threatening little dwarf in the room with you! + You're in Hall of Mt King. -> n - -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 - -OK +A three foot black rod with a rusty star on an end lies nearby. > n -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. +There is a threatening little dwarf in the room with you! + +You're in n/s passage above e/w passage. + +> n + +There is a threatening little dwarf in the room with you! + +You're at "Y2". + +A hollow voice says "PLUGH". > off @@ -321,17 +402,29 @@ It is now pitch dark. If you proceed you will likely fall into a pit. You're inside building. +There is a large sparkling nugget of gold here! + A precious jade necklace has been dropped here! +There is precious jewelry here! + +There are many coins here! + There are some keys on the ground here. There is food here. There is a bottle of water here. -> drop jewelry +> inven -OK +You are currently holding the following: +Brass lantern +Wicker cage +Little bird in cage +Dwarf's axe +Bars of silver +Persian rug > drop rug @@ -377,7 +470,7 @@ OK The bird is singing to you in gratitude for your having returned it to its home. In return, it informs you of a magic word which it thinks you may find useful somewhere near the Hall of Mists. The magic word -changes frequently, but for now the bird believes it is "F'UNJ". You +changes frequently, but for now the bird believes it is "H'CFL". You thank the bird for this information, and it flies off into the forest. > s @@ -400,10 +493,14 @@ There are bars of silver here! There is a Persian rug spread out on the floor! -There is precious jewelry here! +There is a large sparkling nugget of gold here! A precious jade necklace has been dropped here! +There is precious jewelry here! + +There are many coins here! + There are some keys on the ground here. There is food here. @@ -466,35 +563,17 @@ A hollow voice says "PLUGH". You're in n/s passage above e/w passage. -> d - -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 a dirty broken passage. To the east is a crawl. To the -west is a large passage. Above you is a hole to another passage. - -There is a little axe here. - -> take axe - -OK - -> u - -There is a threatening little dwarf in the room with you! - -You're in n/s passage above e/w passage. - > s -There is a threatening little dwarf in the room with you! - You're in Hall of Mt King. -> up +A three foot black rod with a rusty star on an end lies nearby. -There is a threatening little dwarf in the room with you! +> take rod + +OK + +> up You're in Hall of Mists. @@ -502,17 +581,18 @@ Rough stone steps lead up the dome. > w -There is a threatening little dwarf 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. -You're on east bank of fissure. +> wave rod -A three foot black rod with a rusty star on an end lies nearby. +A crystal bridge now spans the fissure. -A crystal bridge spans the fissure. +> drop rod -> w +OK -There is a threatening little dwarf in the room with you! +> west You are on the west side of the fissure in the Hall of Mists. @@ -520,45 +600,30 @@ There are diamonds here! A crystal bridge spans the fissure. +> take diamonds + +OK + > w There is a threatening little dwarf in the room with you! +One sharp nasty knife is thrown at you! + +It misses! + You are at the west end of the Hall of Mists. A low wide crawl continues west and another goes north. To the south is a little passage 6 feet off the floor. > w -There are 2 threatening little dwarves in the room with you. - -One sharp nasty knife is thrown at you! - -It misses! +There is a threatening little dwarf in the room with you! You are at the east end of a very long hall apparently without side chambers. To the east a low wide crawl slants up. To the north a round two foot hole slants down. -> throw axe - -You killed a little dwarf. The body vanishes in a cloud of greasy -black smoke. - -There is a threatening little dwarf in the room with you! - -One sharp nasty knife is thrown at you! - -It misses! - -You're at east end of long hall. - -There is a little axe here. - -> take axe - -OK - > w There is a threatening little dwarf in the room with you! @@ -676,14 +741,8 @@ You're at west end of Hall of Mists. You're on west bank of fissure. -There are diamonds here! - A crystal bridge spans the fissure. -> take diamonds - -OK - > e You're on east bank of fissure. @@ -694,127 +753,24 @@ A crystal bridge spans the fissure. > e -You're in Hall of Mists. - -Rough stone steps lead up the dome. - -> drop bottle - -OK - -> s - -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. - -There is a bottle of water here. - -> n - -You're in Hall of Mt King. - -> n - -You're in n/s passage above e/w passage. - -> n - -You're at "Y2". - -> off - -Your lamp is now off. - -It is now pitch dark. If you proceed you will likely fall into a pit. - -> plugh - ->>Foof!<< - -You're inside building. - -There are bars of silver here! - -There is a Persian rug spread out on the floor! - -There is precious jewelry here! - -A precious jade necklace has been dropped here! - -There are some keys on the ground here. - -There is food here. - -> drop gold - -OK - -> drop diamonds - -OK - -> drop pyramid - -OK - -> drop ruby - -OK - -> plugh - ->>Foof!<< - -It is now pitch dark. If you proceed you will likely fall into a pit. - -A hollow voice says "PLUGH". - -> on - -Your lamp is now on. - -You're at "Y2". - -> s - -You're in n/s passage above e/w passage. - -> s - -You are in the Hall of the Mountain King, with passages off in all -directions. - -> u - There is a threatening little dwarf in the room with you! +One sharp nasty knife is thrown at you! + +It misses! + 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. -There is a bottle of water here. - Rough stone steps lead up the dome. -> take water - -OK - > throw axe -You killed a little dwarf. +You killed a little dwarf. The body vanishes in a cloud of greasy +black smoke. You're in Hall of Mists. @@ -832,12 +788,87 @@ You're in Hall of Mt King. > n +You're in n/s passage above e/w passage. + +> n + +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. + +A hollow voice says "PLUGH". + +> off + +Your lamp is now off. + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> plugh + +>>Foof!<< + +You're inside building. + +There are bars of silver here! + +There is a Persian rug spread out on the floor! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is precious jewelry here! + +There are many coins here! + +There are some keys on the ground here. + +There is food here. + +> drop ruby + +OK + +> drop diamonds + +OK + +> drop pyramid + +OK + +> plugh + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> on + +Your lamp is now on. + +You're at "Y2". + +A hollow voice says "PLUGH". + +> s + You are in a low n/s passage at a hole in the floor. The hole goes down to an e/w passage. +> s + +You're in Hall of Mt King. + +> n + +You're in n/s passage above e/w passage. + > d -You're in dirty passage. +You are in a dirty broken passage. To the east is a crawl. To the +west is a large passage. Above you is a hole to another passage. > bedquilt @@ -873,6 +904,10 @@ You're in west pit. There is a 12-foot-tall beanstalk stretching up out of the pit, bellowing "WATER!! WATER!!" +> H'CFL + +Nothing happens. + > u You're at west end of Twopit Room. @@ -895,412 +930,15 @@ fed by a stream, which tumbles out of a hole in the wall about 10 feet overhead and splashes noisily into the water somewhere within the mist. There is a passage going back toward the south. -> F'UNJ +> Z'ZZZ -The waters have parted to form a narrow path across the reservoir. - -> n - -You are walking across the bottom of the reservoir. Walls of water -rear up on either side. The roar of the water cascading past is -nearly deafening, and the mist is so thick you can barely see. - -> n - -You are at the northern edge of the reservoir. A northwest passage -leads sharply up from here. - -The waters have parted to form a narrow path across the reservoir. - -> nw - -You are scrambling along a treacherously steep, rocky passage. - -> u - -You are on a very steep incline, which widens at it goes upward. - -> u - -You are at the base of a nearly vertical cliff. There are some -slim footholds which would enable you to climb up, but it looks -extremely dangerous. Here at the base of the cliff lie the remains -of several earlier adventurers who apparently failed to make it. - -> u - -You are climbing along a nearly vertical cliff. - -> u - -Just as you reach the top, your foot slips on a loose rock and you -make one last desperate grab. Your luck holds, as does your grip. -With an enormous heave, you lift yourself to the ledge above. - -You are on a small ledge at the top of a nearly vertical cliff. -There is a low crawl leading off to the northeast. - -> ne - -You have reached a dead end. - -There is a richly-carved ebony statuette here! - -> take ebony - -OK - -> sw - -You're at top of cliff. - -> d - -You are climbing along a nearly vertical cliff. - -> d - -You're at base of cliff. - -> d - -You are on a very steep incline, which widens at it goes upward. - -> d - -You are scrambling along a treacherously steep, rocky passage. - -> d - -You're north of reservoir. - -The waters have parted to form a narrow path across the reservoir. - -> take water - -Your bottle is now full of water. - -> s - -You're at bottom of reservoir. - -> s - -You're at reservoir. - -The waters have parted to form a narrow path across the reservoir. - -> s - -You are in a north/south canyon about 25 feet across. The floor is -covered by white mist seeping in from the north. The walls extend -upward for well over 100 feet. Suspended from some unseen point far -above you, an enormous two-sided mirror is hanging parallel to and -midway between the canyon walls. (The mirror is obviously provided -for the use of the dwarves who, as you know, are extremely vain.) A -small window can be seen in either wall, some fifty feet up. - -> s - -You are in a secret n/s canyon above a large room. - -> d - -You're in Slab Room. - -> s - -You're at west end of Twopit Room. - -The top of a 12-foot-tall beanstalk is poking out of the west pit. - -> d - -You're in west pit. - -There is a 12-foot-tall beanstalk stretching up out of the pit, -bellowing "WATER!! WATER!!" - -> water plant - -The plant grows explosively, almost filling the bottom of the pit. - -You're in west pit. - -There is a gigantic beanstalk stretching all the way up to the hole. - -> u - -You're at west end of Twopit Room. - -There is a huge beanstalk growing out of the west pit up to the hole. - -> e - -You are at the east end of the Twopit Room. The floor here is -littered with thin rock slabs, which make it easy to descend the pits. -There is a path here bypassing the pits to connect passages from east -and west. There are holes all over, but the only big one is on the -wall directly over the west pit where you can't get to it. - -There is a huge beanstalk growing out of the west pit up to the hole. - -> d - -You are at the bottom of the eastern pit in the Twopit Room. There is -a small pool of oil in one corner of the pit. - -> get oil - -Your bottle is now full of oil. - -> u - -You're at east end of Twopit Room. - -There is a huge beanstalk growing out of the west pit up to the hole. - -> w - -You're at west end of Twopit Room. - -There is a huge beanstalk growing out of the west pit up to the hole. - -> d - -You're in west pit. - -There is a gigantic beanstalk stretching all the way up to the hole. - -> climb - -You clamber up the plant and scurry through the hole at the top. - -You are in a long, narrow corridor stretching out of sight to the -west. At the eastern end is a hole through which you can see a -profusion of leaves. - -> w - -You are in the Giant Room. The ceiling here is too high up for your -lamp to show it. Cavernous passages lead east, north, and south. On -the west wall is scrawled the inscription, "FEE FIE FOE FOO" [sic]. - -There is a large nest here, full of golden eggs! - -> n - -You are at one end of an immense north/south passage. - -The way north is barred by a massive, rusty, iron door. - -> oil door - -The oil has freed up the hinges so that the door will now move, -although it requires some effort. - -> drop bottle - -OK - -> drop appendage - -OK - -> n - -You are in a magnificent cavern with a rushing stream, which cascades -over a sparkling waterfall into a roaring whirlpool which disappears -through a hole in the floor. Passages exit to the south and west. - -There is a jewel-encrusted trident here! - -> take trident - -OK - -> w - -You are at the top of a steep incline above a large room. You could -climb down here, but you would not be able to climb up. There is a -passage leading back to the north. - -> d - -You are in a large low room. Crawls lead north, se, and sw. - -> se - -This is the Oriental Room. Ancient oriental cave drawings cover the -walls. A gently sloping passage leads upward to the north, another -passage leads se, and a hands and knees crawl leads west. - -There is a delicate, precious, ming vase here! - -> n - -You are following a wide path around the outer edge of a large cavern. -Far below, through a heavy white mist, strange splashing noises can be -heard. The mist rises up through a fissure in the ceiling. The path -exits to the south and west. - -> w - -You are in an alcove. A small nw path seems to widen after a short -distance. An extremely tight tunnel leads east. It looks like a very -tight squeeze. An eerie light can be seen at the other end. - -> inven - -You are currently holding the following: -Brass lantern -Dwarf's axe -Jeweled trident -Ebony statuette - -> drop trident - -OK - -> drop ebony - -OK - -> drop axe - -OK - -> drop lantern - -OK - -> e - -You're in Plover Room. - -There is an emerald here the size of a plover's egg! - -> take emerald - -OK - -> w - -You're in alcove. - -There is a lamp shining nearby. - -There is a little axe here. - -There is a richly-carved ebony statuette here! - -There is a jewel-encrusted trident here! - -> take lamp - -OK - -> take axe - -OK - -> take ebony - -OK - -> take trident - -OK - -> nw - -You're in misty cavern. - -> s - -You're in Oriental Room. - -There is a delicate, precious, ming vase here! - -> take vase - -OK - -> se - -You are in a room whose walls resemble Swiss cheese. Obvious passages -go west, east, ne, and nw. Part of the room is occupied by a large -bedrock block. - -> e - -You are in the Soft Room. The walls are covered with heavy curtains, -the floor with a thick pile carpet. Moss covers the ceiling. - -A small velvet pillow lies on the floor. - -> take pillow - -OK - -> w - -You're in Swiss Cheese Room. - -> ne - -You're in Bedquilt. - -> e - -You are at a complex junction. A low hands and knees passage from the -north joins a higher crawl from the east to make a walking passage -going west. There is also a large room above. The air is damp here. - -> n - -You're in a large room carved out of sedimentary rock. The floor and -walls are littered with bits of shells embedded in the stone. A -shallow passage proceeds downward, and a somewhat steeper one leads -up. A low hands and knees passage enters from the south. - -There is an enormous clam here with its shell tightly closed. - -> n - -There is no way to go that direction. - -You're in Shell Room. - -There is an enormous clam here with its shell tightly closed. - -> seed 1635997320 - -Seed set to 1635997320 - -You're in Shell Room. - -There is an enormous clam here with its shell tightly closed. - -> n - -There is no way to go that direction. - -You're in Shell Room. - -There is an enormous clam here with its shell tightly closed. +Sorry, I don't know the word "Z'ZZZ". > > - -> - -> - - -You scored 179 out of a possible 430, using 216 turns. +You scored 183 out of a possible 430, using 150 turns. You may now consider yourself a "Seasoned Adventurer". -To achieve the next higher rating, you need 72 more points. +To achieve the next higher rating, you need 68 more points. diff --git a/tests/seedcrash.log b/tests/placeholder.log similarity index 57% rename from tests/seedcrash.log rename to tests/placeholder.log index 7f364cc..7d07add 100644 --- a/tests/seedcrash.log +++ b/tests/placeholder.log @@ -1,14 +1,17 @@ -## This crashed advent before the control path after seed was fixed. +## Test behavior of placeholder magic word at reservoir +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +# Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56 n -seed 1635997320 +seed 1838473132 in take lamp xyzzy +on take rod e take cage w -on w w drop rod @@ -17,47 +20,58 @@ take rod w free bird wave rod +take necklace drop rod take bird -take jade -e -e -e -off -xyzzy -drop jade -xyzzy -on -w -w -w take rod d -w -wave rod -drop rod -e -n +d free bird +drop rod +drop cage +take cage take bird +w +take coins +e s take jewelry n +up +s +take gold +n +d +n +n +plugh +extinguish lamp +drop coins +drop jewelry +drop necklace +drop gold +plugh +on +s +take silver +s sw +take axe w -feed dragon kill dragon yes +feed dragon drink blood take rug e e +up +d n -take silver n off plugh -drop jewelry +inven drop rug drop silver out @@ -81,18 +95,17 @@ take pyramid s plover s -d -take axe -u s +take rod up w +wave rod +drop rod +west +take diamonds w w w -throw axe -take axe -w s sw se @@ -113,31 +126,22 @@ d e e e -take diamonds e e -drop bottle -s -take gold -n +throw axe +take axe n n n off plugh -drop gold +drop ruby drop diamonds drop pyramid -drop ruby plugh on s s -u -take water -throw axe -take axe -n n d bedquilt @@ -145,81 +149,10 @@ slab s d water plant +H'CFL u w u reservoir -F'UNJ -n -n -nw -u -u -u -u -ne -take ebony -sw -d -d -d -d -d -take water -s -s -s -s -d -s -d -water plant -u -e -d -get oil -u -w -d -climb -w -n -oil door -drop bottle -drop appendage -n -take trident -w -d -se -n -w -inven -drop trident -drop ebony -drop axe -drop lantern -e -take emerald -w -take lamp -take axe -take ebony -take trident -nw -s -take vase -se -e -take pillow -w -ne -e -n -n -seed 1635997320 -n - - - +Z'ZZZ diff --git a/tests/plover.chk b/tests/plover.chk index 67e58af..c59ae86 100644 --- a/tests/plover.chk +++ b/tests/plover.chk @@ -1169,7 +1169,7 @@ Egg-sized emerald It is now pitch dark. If you proceed you will likely fall into a pit. - +> You scored 169 out of a possible 430, using 187 turns. You have achieved the rating: "Experienced Adventurer". diff --git a/tests/plover.log b/tests/plover.log index 740ed16..0d1b5a5 100644 --- a/tests/plover.log +++ b/tests/plover.log @@ -1,4 +1,6 @@ ## Test access to emerald room and plover teleport +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56 n seed 1495951709 diff --git a/tests/reach_ledge_short.chk b/tests/reach_ledge_short.chk index c5e083e..7c02c64 100644 --- a/tests/reach_ledge_short.chk +++ b/tests/reach_ledge_short.chk @@ -555,11 +555,9 @@ The ogre, distracted by your rush, is struck by the knife. With a blood-curdling yell he turns and bounds after the dwarf, who flees in panic. You are left alone in the room. -> +> z -I don't know how to apply that word here. - -You are in a large chamber with passages to the west and north. +OK > s @@ -622,10 +620,6 @@ A crystal bridge spans the fissure. There is a threatening little dwarf in the room with you! -One sharp nasty knife is thrown at you! - -It misses! - 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 @@ -657,12 +651,20 @@ A cheerful little bird is sitting here singing. > 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 n/s passage above e/w passage. There are bars of silver here! > 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. @@ -693,14 +695,14 @@ There is food here. It is now pitch dark. If you proceed you will likely fall into a pit. +A hollow voice says "PLUGH". + > on Your lamp is now on. You're at "Y2". -A hollow voice says "PLUGH". - > s You are in a low n/s passage at a hole in the floor. The hole goes @@ -716,12 +718,20 @@ A cheerful little bird is sitting here singing. > u +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 Mists. Rough stone steps lead up the dome. > n +There is a threatening little dwarf in the room with you! + You are in the Hall of the Mountain King, with passages off in all directions. @@ -729,12 +739,16 @@ A cheerful little bird is sitting here singing. > n +There is a threatening little dwarf in the room with you! + You're in n/s passage above e/w passage. There are bars of silver here! > d +There is a threatening little dwarf in the room with you! + You are in a dirty broken passage. To the east is a crawl. To the west is a large passage. Above you is a hole to another passage. @@ -747,7 +761,13 @@ To explore at random select north, south, up, or down. > throw axe -You killed a little dwarf. +You attack a little dwarf, but he dodges out of the way. + +There is a threatening little dwarf in the room with you! + +One sharp nasty knife is thrown at you! + +It misses! You're in Bedquilt. @@ -755,10 +775,12 @@ There is a little axe here. > take -OK +Take what? > slab +There is a threatening little dwarf in the room with you! + You are in a large low circular chamber whose floor is an immense slab fallen from the ceiling (Slab Room). East and west there once were large passages, but they are now filled with boulders. Low small @@ -767,11 +789,15 @@ around the boulders. > s +There is a threatening little dwarf in the room with you! + You are at the west end of the Twopit Room. There is a large hole in the wall above the pit at this end of the room. > d +There is a threatening little dwarf in the room with you! + You are at the bottom of the western pit in the Twopit Room. There is a large hole in the wall about 25 feet above you. @@ -781,6 +807,12 @@ There is a tiny little plant in the pit, murmuring "water, water, ..." The plant spurts into furious growth for a few seconds. +There is a threatening little dwarf in the room with you! + +One sharp nasty knife is thrown at you! + +It misses! + You're in west pit. There is a 12-foot-tall beanstalk stretching up out of the pit, @@ -788,20 +820,28 @@ bellowing "WATER!! WATER!!" > u +There is a threatening little dwarf in the room with you! + You're at west end of Twopit Room. The top of a 12-foot-tall beanstalk is poking out of the west pit. > west +There is a threatening little dwarf in the room with you! + You're in Slab Room. > u +There is a threatening little dwarf in the room with you! + You are in a secret n/s canyon above a large room. > reser +There is a threatening little dwarf in the room with you! + You are at the edge of a large underground reservoir. An opaque cloud of white mist fills the room and rises rapidly upward. The lake is fed by a stream, which tumbles out of a hole in the wall about 10 feet @@ -814,12 +854,16 @@ The waters have parted to form a narrow path across the reservoir. > n +There is a threatening little dwarf in the room with you! + You are walking across the bottom of the reservoir. Walls of water rear up on either side. The roar of the water cascading past is nearly deafening, and the mist is so thick you can barely see. > n +There is a threatening little dwarf in the room with you! + You are at the northern edge of the reservoir. A northwest passage leads sharply up from here. @@ -827,14 +871,20 @@ The waters have parted to form a narrow path across the reservoir. > u +There is a threatening little dwarf in the room with you! + You are scrambling along a treacherously steep, rocky passage. > u +There is a threatening little dwarf in the room with you! + You are on a very steep incline, which widens at it goes upward. > u +There is a threatening little dwarf in the room with you! + You are at the base of a nearly vertical cliff. There are some slim footholds which would enable you to climb up, but it looks extremely dangerous. Here at the base of the cliff lie the remains @@ -842,642 +892,222 @@ of several earlier adventurers who apparently failed to make it. > u +There is a threatening little dwarf in the room with you! + You are climbing along a nearly vertical cliff. > e There is no way to go that direction. -You are climbing along a nearly vertical cliff. - -> w - -There is no way to go that direction. - -You are climbing along a nearly vertical cliff. - -> d - -You're at base of cliff. - -> d - -You are on a very steep incline, which widens at it goes upward. - -> d - -You are scrambling along a treacherously steep, rocky passage. - -> d - -You're north of reservoir. - -The waters have parted to form a narrow path across the reservoir. - -> d - -There is no way to go that direction. - -You're north of reservoir. - -The waters have parted to form a narrow path across the reservoir. - -> take water - -Your bottle is now full of water. - -> s - -You're at bottom of reservoir. - -> s - -You're at reservoir. - -The waters have parted to form a narrow path across the reservoir. - -> s - -You are in a north/south canyon about 25 feet across. The floor is -covered by white mist seeping in from the north. The walls extend -upward for well over 100 feet. Suspended from some unseen point far -above you, an enormous two-sided mirror is hanging parallel to and -midway between the canyon walls. (The mirror is obviously provided -for the use of the dwarves who, as you know, are extremely vain.) A -small window can be seen in either wall, some fifty feet up. - -> s - -You are in a secret n/s canyon above a large room. - -> d - -You're in Slab Room. - -> s - -You're at west end of Twopit Room. - -The top of a 12-foot-tall beanstalk is poking out of the west pit. - -> d - -You're in west pit. - -There is a 12-foot-tall beanstalk stretching up out of the pit, -bellowing "WATER!! WATER!!" - -> water plant - -The plant grows explosively, almost filling the bottom of the pit. - -You're in west pit. - -There is a gigantic beanstalk stretching all the way up to the hole. - -> u - -You're at west end of Twopit Room. - -There is a huge beanstalk growing out of the west pit up to the hole. - -> e - -You are at the east end of the Twopit Room. The floor here is -littered with thin rock slabs, which make it easy to descend the pits. -There is a path here bypassing the pits to connect passages from east -and west. There are holes all over, but the only big one is on the -wall directly over the west pit where you can't get to it. - -There is a huge beanstalk growing out of the west pit up to the hole. - -> d - -You are at the bottom of the eastern pit in the Twopit Room. There is -a small pool of oil in one corner of the pit. - -> get oil - -Your bottle is now full of oil. - -> u - -You're at east end of Twopit Room. - -There is a huge beanstalk growing out of the west pit up to the hole. - -> west - -You're at west end of Twopit Room. - -There is a huge beanstalk growing out of the west pit up to the hole. - -> d - -You're in west pit. - -There is a gigantic beanstalk stretching all the way up to the hole. - -> climb - -You clamber up the plant and scurry through the hole at the top. - -You are in a long, narrow corridor stretching out of sight to the -west. At the eastern end is a hole through which you can see a -profusion of leaves. - -> west - -You are in the Giant Room. The ceiling here is too high up for your -lamp to show it. Cavernous passages lead east, north, and south. On -the west wall is scrawled the inscription, "FEE FIE FOE FOO" [sic]. - -There is a large nest here, full of golden eggs! - -> n - -You are at one end of an immense north/south passage. - -The way north is barred by a massive, rusty, iron door. - -> oil door - -The oil has freed up the hinges so that the door will now move, -although it requires some effort. - -> drop bottl - -OK - -> n - -You are in a magnificent cavern with a rushing stream, which cascades -over a sparkling waterfall into a roaring whirlpool which disappears -through a hole in the floor. Passages exit to the south and west. - -There is a jewel-encrusted trident here! - -> west - -You are at the top of a steep incline above a large room. You could -climb down here, but you would not be able to climb up. There is a -passage leading back to the north. - -> d - -You are in a large low room. Crawls lead north, se, and sw. - -> se - -This is the Oriental Room. Ancient oriental cave drawings cover the -walls. A gently sloping passage leads upward to the north, another -passage leads se, and a hands and knees crawl leads west. - -There is a delicate, precious, ming vase here! - -> n - -You are following a wide path around the outer edge of a large cavern. -Far below, through a heavy white mist, strange splashing noises can be -heard. The mist rises up through a fissure in the ceiling. The path -exits to the south and west. - -> west - -You are in an alcove. A small nw path seems to widen after a short -distance. An extremely tight tunnel leads east. It looks like a very -tight squeeze. An eerie light can be seen at the other end. - -> drop axe - -OK - -> drop lante - -OK - -> e - -You're in Plover Room. - -There is an emerald here the size of a plover's egg! - -> take emera - -OK - -> west - -You're in alcove. - -There is a lamp shining nearby. - -There is a little axe here. - -> take lamp - -OK - -> take - -OK - -> nw - -You're in misty cavern. - -> s - -You're in Oriental Room. - -There is a delicate, precious, ming vase here! - -> e - -There is no way to go that direction. - -You're in Oriental Room. - -There is a delicate, precious, ming vase here! - -> se - There is a threatening little dwarf in the room with you! -You are in a room whose walls resemble Swiss cheese. Obvious passages -go west, east, ne, and nw. Part of the room is occupied by a large -bedrock block. +One sharp nasty knife is thrown at you! -> throw axe +It gets you! -You killed a little dwarf. - -You're in Swiss Cheese Room. - -There is a little axe here. - -> e - -You are in the Soft Room. The walls are covered with heavy curtains, -the floor with a thick pile carpet. Moss covers the ceiling. - -A small velvet pillow lies on the floor. - -> west - -You're in Swiss Cheese Room. - -There is a little axe here. - -> d - -There is no way to go that direction. - -You're in Swiss Cheese Room. - -There is a little axe here. - -> ne - -You're in Bedquilt. - -> e - -You are at a complex junction. A low hands and knees passage from the -north joins a higher crawl from the east to make a walking passage -going west. There is also a large room above. The air is damp here. - -> u - -You are in a large room full of dusty rocks. There is a big hole in -the floor. There are cracks everywhere, and a passage leading east. - -> e - -You're in dirty passage. - -> u - -You're in n/s passage above e/w passage. - -There are bars of silver here! - -> n - -You're at "Y2". - -> plugh - ->>Foof!<< - -You are inside a building, a well house for a large spring. - -There is a Persian rug spread out on the floor! - -There are some keys on the ground here. - -There is food here. - -> take key - -OK - -> take food - -OK - -> plugh - ->>Foof!<< - -You're at "Y2". - -> s - -You're in n/s passage above e/w passage. - -There are bars of silver here! - -> d - -You're in dirty passage. - -> west - -You're in dusty rock room. - -> d - -You're at complex junction. - -> n - -You're in a large room carved out of sedimentary rock. The floor and -walls are littered with bits of shells embedded in the stone. A -shallow passage proceeds downward, and a somewhat steeper one leads -up. A low hands and knees passage enters from the south. - -There is an enormous clam here with its shell tightly closed. - -> d - -You are in a long sloping corridor with ragged sharp walls. - -> d - -You are in a cul-de-sac about eight feet across. - -> u - -You are in a long sloping corridor with ragged sharp walls. - -> u - -You're in Shell Room. - -There is an enormous clam here with its shell tightly closed. - -> s - -You're at complex junction. - -> west - -You're in Bedquilt. - -> west - -You're in Swiss Cheese Room. - -There is a little axe here. - -> west - -You're at east end of Twopit Room. - -There is a huge beanstalk growing out of the west pit up to the hole. - -> west - -You are at the west end of the Twopit Room. There is a large hole in -the wall above the pit at this end of the room. - -There is a huge beanstalk growing out of the west pit up to the hole. - -> d - -You are at the bottom of the western pit in the Twopit Room. There is -a large hole in the wall about 25 feet above you. - -There is a gigantic beanstalk stretching all the way up to the hole. - -> climb - -You clamber up the plant and scurry through the hole at the top. - -You're in narrow corridor. - -> west - -You're in Giant Room. - -There is a large nest here, full of golden eggs! - -> n - -You are at one end of an immense north/south passage. - -There is an empty bottle here. - -The way north leads through a massive, rusty, iron door. - -> take bottl - -OK - -> n - -You're in cavern with waterfall. - -There is a jewel-encrusted trident here! +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? > w -You're at steep incline above large room. +Please answer the question. + +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? > d -You're in large low room. +Please answer the question. -> se - -You're in Oriental Room. - -There is a delicate, precious, ming vase here! - -> se - -You are in a room whose walls resemble Swiss cheese. Obvious passages -go west, east, ne, and nw. Part of the room is occupied by a large -bedrock block. - -There is a little axe here. - -> west - -You're at east end of Twopit Room. - -There is a huge beanstalk growing out of the west pit up to the hole. +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? > d -You're in east pit. +Please answer the question. -> g oil +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? -Your bottle is now full of oil. +> d -> u +Please answer the question. -You're at east end of Twopit Room. +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? -There is a huge beanstalk growing out of the west pit up to the hole. +> d -> west +Please answer the question. -You're at west end of Twopit Room. +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? -There is a huge beanstalk growing out of the west pit up to the hole. +> d -> west +Please answer the question. -You're in Slab Room. +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? -> u +> take water -You are in a secret n/s canyon above a large room. +Please answer the question. + +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? > s -You are in a secret canyon which exits to the north and east. +Please answer the question. -The blood-specked body of a huge green dead dragon lies to one side. +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? + +> s + +Please answer the question. + +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? + +> s + +Please answer the question. + +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? + +> s + +Please answer the question. + +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? + +> d + +Please answer the question. + +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? + +> s + +Please answer the question. + +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? + +> d + +Please answer the question. + +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? + +> water plant + +Please answer the question. + +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? + +> u + +Please answer the question. + +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? > e -You're in secret e/w canyon above tight canyon. +Please answer the question. -> e +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? -You're in Hall of Mt King. +> d -A cheerful little bird is sitting here singing. +Please answer the question. -> n +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? -You're in n/s passage above e/w passage. +> get oil -There are bars of silver here! +Please answer the question. -> n +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? -You're at "Y2". +> u -> plugh +Please answer the question. ->>Foof!<< - -You're inside building. - -There is a Persian rug spread out on the floor! - -> take - -OK - -> out - -You're in front of building. +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? > west -You have walked up a hill, still in the forest. The road slopes back -down the other side of the hill. There is a building in the distance. +Please answer the question. + +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? + +> d + +Please answer the question. + +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? + +> climb + +Please answer the question. + +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? + +> west + +Please answer the question. + +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 -You are wandering aimlessly through the forest. - -> n - -You are wandering aimlessly through the forest. - -> n - -The forest thins out here to reveal a steep cliff. There is no way -down, but a small ledge can be seen to the west across the chasm. - -A small urn is embedded in the rock. - -> fill urn - -Your bottle is now empty and the urn is full of oil. - -> light - -The urn is now lit. - -> rub urn - -As you rub the urn, there is a flash of light and a genie appears. -His aspect is stern as he advises: "One who wouldst traffic in -precious stones must first learn to recognize the signals thereof." -He wrests the urn from the stone, leaving a small cavity. Turning to -face you again, he fixes you with a steely eye and intones: "Caution!" -Genie and urn vanish in a cloud of amber smoke. The smoke condenses -to form a rare amber gemstone, resting in the cavity in the rock. - -> take amber - OK -> drop rug - -OK - -> drop emera - -The gem fits easily into the cavity. - -The Persian rug stiffens and rises a foot or so off the ground. - -> fly - -You board the Persian rug, which promptly whisks you across the chasm. -You have time for a fleeting glimpse of a two thousand foot drop to a -mighty river; then you find yourself on the other side. - -You are on a small ledge on one face of a sheer cliff. There are no -paths away from the ledge. Across the chasm is a small clearing -surrounded by forest. - -There is a Persian rug here, hovering in mid-air! - -A brilliant blue star sapphire is here! - -> e - -There is no way to go that direction. - -You're on ledge. - -There is a Persian rug here, hovering in mid-air! - -A brilliant blue star sapphire is here! - - -You scored 81 out of a possible 430, using 237 turns. +You scored 79 out of a possible 430, using 128 turns. Your score qualifies you as a novice class adventurer. -To achieve the next higher rating, you need 40 more points. +To achieve the next higher rating, you need 42 more points. diff --git a/tests/reach_ledge_short.log b/tests/reach_ledge_short.log index a121333..4890a42 100644 --- a/tests/reach_ledge_short.log +++ b/tests/reach_ledge_short.log @@ -1,4 +1,6 @@ ## LOC_NOCLIMB.short (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in @@ -83,7 +85,7 @@ kill machi s s kill ogre - +z s west n @@ -214,7 +216,7 @@ se se west d -g oil +get oil u west west @@ -238,4 +240,4 @@ take amber drop rug drop emera fly -e \ No newline at end of file +e diff --git a/tests/reach_noclimb.chk b/tests/reach_noclimb.chk index 0a06bd0..6f42d7c 100644 --- a/tests/reach_noclimb.chk +++ b/tests/reach_noclimb.chk @@ -170,7 +170,7 @@ You're in west pit. There is a tiny little plant in the pit, murmuring "water, water, ..." - +> You scored 59 out of a possible 430, using 24 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/reach_noclimb.log b/tests/reach_noclimb.log index 42af7c5..ebf8ae3 100644 --- a/tests/reach_noclimb.log +++ b/tests/reach_noclimb.log @@ -1,4 +1,6 @@ ## LOC_NOCLIMB (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in @@ -24,4 +26,4 @@ w w w d -climb \ No newline at end of file +climb diff --git a/tests/reach_planttop.chk b/tests/reach_planttop.chk index 8009e1f..c19c9d2 100644 --- a/tests/reach_planttop.chk +++ b/tests/reach_planttop.chk @@ -387,7 +387,7 @@ You're at west end of Twopit Room. The top of a 12-foot-tall beanstalk is poking out of the west pit. - +> You scored 63 out of a possible 430, using 50 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/reach_planttop.log b/tests/reach_planttop.log index bbdf94b..e85f908 100644 --- a/tests/reach_planttop.log +++ b/tests/reach_planttop.log @@ -1,4 +1,6 @@ ## LOC_PLANTTOP (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in @@ -50,4 +52,4 @@ s s d water plant -climb \ No newline at end of file +climb diff --git a/tests/reincarnate.chk b/tests/reincarnate.chk index c8a8842..edaaf8a 100644 --- a/tests/reincarnate.chk +++ b/tests/reincarnate.chk @@ -143,7 +143,7 @@ There is food here. There is a bottle of water here. - +> You scored 47 out of a possible 430, using 18 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/reincarnate.log b/tests/reincarnate.log index 39be5dd..bdf93c4 100644 --- a/tests/reincarnate.log +++ b/tests/reincarnate.log @@ -1,4 +1,6 @@ ## Jump into a pit and die, then be reincarnated +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1495774850 in diff --git a/tests/resumefail.chk b/tests/resumefail.chk index 7be9b12..976fcc9 100644 --- a/tests/resumefail.chk +++ b/tests/resumefail.chk @@ -24,9 +24,10 @@ Is this acceptable? OK Can't open file /badfilename, try again. +File name: You're in front of building. - +> You scored 32 out of a possible 430, using 1 turn. You are obviously a rank amateur. Better luck next time. diff --git a/tests/resumefail.log b/tests/resumefail.log index 053b05f..c86b323 100644 --- a/tests/resumefail.log +++ b/tests/resumefail.log @@ -1,6 +1,9 @@ ## Resume from invalid filename +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE advent430 crashes on resume from invalid filename and we don't care. n seed 1240742801 resume y -/badfilename \ No newline at end of file +/badfilename diff --git a/tests/resumefail2.chk b/tests/resumefail2.chk index 756c094..2aa9441 100644 --- a/tests/resumefail2.chk +++ b/tests/resumefail2.chk @@ -11,12 +11,12 @@ down a gully. Can't open file y, try again. I'm sorry, but that Adventure was begun using Version -133.-7 of the -save file format, and this program uses Version 2.9. You must find an instance +save file format, and this program uses Version 3.1. You must find an instance using that other version in order to resume that Adventure. You're in front of building. - +> You scored 32 out of a possible 430, using 1 turn. You are obviously a rank amateur. Better luck next time. diff --git a/tests/resumefail2.log b/tests/resumefail2.log index 2754454..8a466d5 100644 --- a/tests/resumefail2.log +++ b/tests/resumefail2.log @@ -1,4 +1,7 @@ -## Resume from from generated save with version mismatch error +## Resume from generated save with version mismatch error +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE Reveals a bug in advent430 handling of saves with invalid versions. n resume y diff --git a/tests/savefail.chk b/tests/savefail.chk index de89a53..0f005a2 100644 --- a/tests/savefail.chk +++ b/tests/savefail.chk @@ -25,9 +25,10 @@ Is this acceptable? OK Can't open file /, try again. +File name: You're in front of building. - +> You scored 27 out of a possible 430, using 1 turn. You are obviously a rank amateur. Better luck next time. diff --git a/tests/savefail.log b/tests/savefail.log index f907d99..8e5fe6a 100644 --- a/tests/savefail.log +++ b/tests/savefail.log @@ -1,6 +1,9 @@ ## Save right after starting to invalid filename +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE advent430 crashes on save to invalid filename and we don't care. n seed 1240742801 save y -/ \ No newline at end of file +/ diff --git a/tests/saveresume.1.log b/tests/saveresume.1.log index fffe43f..c5def6a 100644 --- a/tests/saveresume.1.log +++ b/tests/saveresume.1.log @@ -1,4 +1,7 @@ ## Save right after starting +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE Can't compare to advent430 due to version skew n seed 1240742801 save diff --git a/tests/saveresume.2.log b/tests/saveresume.2.log index 7ceb5dd..9995b96 100644 --- a/tests/saveresume.2.log +++ b/tests/saveresume.2.log @@ -1,4 +1,7 @@ ## Resume and then quit +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE Can't compare to advent430 due to version skew n in resume diff --git a/tests/saveresume.3.log b/tests/saveresume.3.log index 13ed0be..aaffab4 100644 --- a/tests/saveresume.3.log +++ b/tests/saveresume.3.log @@ -1,5 +1,8 @@ ## Almost win, then save +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56 +#NOCOMPARE Seems to reveal a bug in advent430's save function. n seed 1838473132 in @@ -473,4 +476,4 @@ sw save y saveresume_win.adv -y \ No newline at end of file +y diff --git a/tests/saveresume.4.log b/tests/saveresume.4.log index caa8098..5accd34 100644 --- a/tests/saveresume.4.log +++ b/tests/saveresume.4.log @@ -1,6 +1,9 @@ ## Resume, then win +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Here to get class threshold of 426 # Note, savefile name has trailing space +#NOCOMPARE Reveals that advent430 does not resume in endgame gracefully. n resume y diff --git a/tests/saveresumeopt.chk b/tests/saveresumeopt.chk index bc08d9b..6e8ead0 100644 --- a/tests/saveresumeopt.chk +++ b/tests/saveresumeopt.chk @@ -1,7 +1,7 @@ You're in front of building. - +> You scored 27 out of a possible 430, using 2 turns. You are obviously a rank amateur. Better luck next time. diff --git a/tests/saveresumeopt.log b/tests/saveresumeopt.log index af159ff..d1889d1 100644 --- a/tests/saveresumeopt.log +++ b/tests/saveresumeopt.log @@ -1,2 +1,5 @@ ## Simple quit +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE New feature, -r option #options: -r saveresume.adv diff --git a/tests/savetamper.chk b/tests/savetamper.chk new file mode 100644 index 0000000..88f86f7 --- /dev/null +++ b/tests/savetamper.chk @@ -0,0 +1,17 @@ + +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. + +> resume + +A dark fog creeps in to surround you. From somewhere in the fog you +hear a stern voice. "This Adventure has been tampered with! You have +been dabbling in magic, knowing not the havoc you might cause thereby. +Leave at once, before you do irrevocable harm!" The fog thickens, +until at last you can see nothing at all. Your vision then clears, +and you find yourself back in The Real World. diff --git a/tests/savetamper.log b/tests/savetamper.log new file mode 100644 index 0000000..62a28f9 --- /dev/null +++ b/tests/savetamper.log @@ -0,0 +1,7 @@ +## Resume from artificial "corrupted" save +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE Can't compare to advent430 due to version skew +n +resume +cheat_savetamper.adv diff --git a/tests/snake_food.chk b/tests/snake_food.chk index 96f6900..56e73b3 100644 --- a/tests/snake_food.chk +++ b/tests/snake_food.chk @@ -151,7 +151,7 @@ Set of keys Brass lantern Wicker cage - +> You scored 57 out of a possible 430, using 22 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/snake_food.log b/tests/snake_food.log index 4950277..7717f39 100644 --- a/tests/snake_food.log +++ b/tests/snake_food.log @@ -1,4 +1,6 @@ ## Snake must vocally eat bird +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1951269982 in diff --git a/tests/softroom.chk b/tests/softroom.chk index cc168f3..3e9c86d 100644 --- a/tests/softroom.chk +++ b/tests/softroom.chk @@ -1350,7 +1350,7 @@ There is a delicate, precious, ming vase here! > - +> You scored 191 out of a possible 430, using 223 turns. You may now consider yourself a "Seasoned Adventurer". diff --git a/tests/softroom.log b/tests/softroom.log index b6b60e7..113001a 100644 --- a/tests/softroom.log +++ b/tests/softroom.log @@ -1,4 +1,6 @@ ## Drop vase in soft room after pillow removed +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Verify that the vase does not break n seed 1838473132 diff --git a/tests/specials.chk b/tests/specials.chk index 1b207d0..0606117 100644 --- a/tests/specials.chk +++ b/tests/specials.chk @@ -73,7 +73,7 @@ I'm as confused as you are. Mist is a white vapor, usually water, seen from time to time in caverns. It can be found anywhere but is frequently a sign of a deep -pit leading down to water.' +pit leading down to water. > fuck @@ -131,7 +131,7 @@ can now save a game and resume it at once (formerly you had to wait a while first), but it now costs you a few points each time you save the game. Saved games are now stored in much smaller files than before. - +> You scored 32 out of a possible 430, using 15 turns. You are obviously a rank amateur. Better luck next time. diff --git a/tests/specials.log b/tests/specials.log index 2891691..aad0a63 100644 --- a/tests/specials.log +++ b/tests/specials.log @@ -1,4 +1,7 @@ ## Test special words +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE The news text has changed n thank shazam diff --git a/tests/splatter.log b/tests/splatter.log index 2408bcd..13a0eb5 100644 --- a/tests/splatter.log +++ b/tests/splatter.log @@ -1,4 +1,6 @@ ## Adventurer fall down go boom. Also tests 'say' verb on magic words. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56 n seed 1838473132 diff --git a/tests/stashed.chk b/tests/stashed.chk new file mode 100644 index 0000000..58b989c --- /dev/null +++ b/tests/stashed.chk @@ -0,0 +1,2086 @@ + +Welcome to Adventure!! Would you like instructions? + +> no + +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 1318612053 + +Seed set to 1318612053 + +You're in front of building. + +> e + +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. + +> plugh + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> plove + +>>Foof!<< + +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west. A dark corridor leads ne. + +There is an emerald here the size of a plover's egg! + +> get emerald + +OK + +> w + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> drop emerald + +OK + +> e + +You're in Plover Room. + +> ne + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> get pyramid + +OK + +> s + +You're in Plover Room. + +> plove + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> plugh + +>>Foof!<< + +You're inside building. + +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. + +> drop pyramid + +OK + +> get lamp + +OK + +> get water + +OK + +> plugh + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> on + +Your lamp is now on. + +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. + +A hollow voice says "PLUGH". + +> s + +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! + +> d + +You are in a dirty broken passage. To the east is a crawl. To the +west is a large passage. Above you is a hole to another passage. + +> bedquilt + +You are in Bedquilt, a long east/west passage with holes everywhere. +To explore at random select north, south, up, or down. + +> w + +You are in a room whose walls resemble Swiss cheese. Obvious passages +go west, east, ne, and nw. Part of the room is occupied by a large +bedrock block. + +> e + +You are in the Soft Room. The walls are covered with heavy curtains, +the floor with a thick pile carpet. Moss covers the ceiling. + +A small velvet pillow lies on the floor. + +> take pillow + +OK + +> w + +You're in Swiss Cheese Room. + +> oriental + +This is the Oriental Room. Ancient oriental cave drawings cover the +walls. A gently sloping passage leads upward to the north, another +passage leads se, and a hands and knees crawl leads west. + +There is a delicate, precious, ming vase here! + +> take vase + +OK + +> n + +You are following a wide path around the outer edge of a large cavern. +Far below, through a heavy white mist, strange splashing noises can be +heard. The mist rises up through a fissure in the ceiling. The path +exits to the south and west. + +> w + +You are in an alcove. A small nw path seems to widen after a short +distance. An extremely tight tunnel leads east. It looks like a very +tight squeeze. An eerie light can be seen at the other end. + +There is an emerald here the size of a plover's egg! + +> take emerald + +OK + +> nw + +You're in misty cavern. + +> s + +You're in Oriental Room. + +> se + +You're in Swiss Cheese Room. + +> w + +You are at the east end of the Twopit Room. The floor here is +littered with thin rock slabs, which make it easy to descend the pits. +There is a path here bypassing the pits to connect passages from east +and west. There are holes all over, but the only big one is on the +wall directly over the west pit where you can't get to it. + +> w + +You are at the west end of the Twopit Room. There is a large hole in +the wall above the pit at this end of the room. + +> d + +You are at the bottom of the western pit in the Twopit Room. There is +a large hole in the wall about 25 feet above you. + +There is a tiny little plant in the pit, murmuring "water, water, ..." + +> water plant + +The plant spurts into furious growth for a few seconds. + +You're in west pit. + +There is a 12-foot-tall beanstalk stretching up out of the pit, +bellowing "WATER!! WATER!!" + +> u + +You're at west end of Twopit Room. + +The top of a 12-foot-tall beanstalk is poking out of the west pit. + +> e + +You're at east end of Twopit Room. + +The top of a 12-foot-tall beanstalk is poking out of the west pit. + +> d + +You are at the bottom of the eastern pit in the Twopit Room. There is +a small pool of oil in one corner of the pit. + +> fill bottle + +Your bottle is now full of oil. + +> u + +You're at east end of Twopit Room. + +The top of a 12-foot-tall beanstalk is poking out of the west pit. + +> e + +You're in Swiss Cheese Room. + +> ne + +You're in Bedquilt. + +> e + +You are at a complex junction. A low hands and knees passage from the +north joins a higher crawl from the east to make a walking passage +going west. There is also a large room above. The air is damp here. + +> u + +You are in a large room full of dusty rocks. There is a big hole in +the floor. There are cracks everywhere, and a passage leading east. + +> e + +You're in dirty passage. + +> u + +You're in n/s passage above e/w passage. + +There are bars of silver here! + +> n + +A little dwarf just walked around a corner, saw you, threw a little +axe at you which missed, cursed, and ran away. + +You're at "Y2". + +There is a little axe here. + +> plugh + +>>Foof!<< + +You're inside building. + +There is a platinum pyramid here, 8 inches on a side! + +There are some keys on the ground here. + +There is food here. + +> drop pillow + +OK + +> drop vase + +The vase is now resting, delicately, on a velvet pillow. + +> drop bottle + +OK + +> drop emerald + +OK + +> xyzzy + +>>Foof!<< + +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 + +> e + +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 + +> pit + +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. + +> drop rod + +OK + +> e + +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 + +> w + +You're at top of small pit. + +A three foot black rod with a rusty star on an end lies nearby. + +Rough stone steps lead down the pit. + +> drop bird + +OK + +> take rod + +OK + +> wave rod + +The bird flies about agitatedly for a moment, then disappears through +the crack. It reappears shortly, carrying in its beak a jade +necklace, which it drops at your feet. + +> take necklace + +OK + +> drop rod + +OK + +> take bird + +OK + +> d + +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. + +> s + +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 nugget + +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. + +> take bird + +OK + +> sw + +You are in a secret canyon which here runs e/w. It crosses over a +very tight canyon 15 feet below. If you go down you may not be able +to get back up. + +> w + +You are in a secret canyon which exits to the north and east. + +A huge green fierce dragon bars the way! + +The dragon is sprawled out on a Persian rug!! + +> kill dragon + +With what? Your bare hands? + +> yes + +Congratulations! You have just vanquished a dragon with your bare +hands! (Unbelievable, isn't it?) + +You are in a secret canyon which exits to the north and east. + +There is a Persian rug spread out on the floor! + +The blood-specked body of a huge green dead dragon lies to one side. + +> drink blood + +Your head buzzes strangely for a moment. + +> take rug + +OK + +> e + +You're in secret e/w canyon above tight canyon. + +> e + +You're in Hall of Mt King. + +> n + +You're in n/s passage above e/w passage. + +There are bars of silver here! + +> take bars + +OK + +> n + +You're at "Y2". + +There is a little axe here. + +> plugh + +>>Foof!<< + +You're inside building. + +There is an emerald here the size of a plover's egg! + +There is a bottle of oil here. + +There is a delicate, precious, ming vase here! + +A small velvet pillow lies on the floor. + +There is a platinum pyramid here, 8 inches on a side! + +There are some keys on the ground here. + +There is food here. + +> drop cage + +OK + +> drop necklace + +OK + +> drop nugget + +OK + +> drop bars + +OK + +> drop rug + +OK + +> xyzzy + +>>Foof!<< + +You're in debris room. + +> pit + +You're at top of small pit. + +A three foot black rod with a rusty star on an end lies nearby. + +Rough stone steps lead down the pit. + +> take rod + +OK + +> d + +You're in Hall of Mists. + +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. + +> wave rod + +A crystal bridge now spans the fissure. + +> w + +You are on the west side of the fissure in the Hall of Mists. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> take diamonds + +OK + +> w + +There is a threatening little dwarf in the room with you! + +You are at the west end of the Hall of Mists. A low wide crawl +continues west and another goes north. To the south is a little +passage 6 feet off the floor. + +> w + +There is a threatening little dwarf in the room with you! + +You are at the east end of a very long hall apparently without side +chambers. To the east a low wide crawl slants up. To the north a +round two foot hole slants down. + +> w + +There is a threatening little dwarf in the room with you! + +You are at the west end of a very long featureless hall. The hall +joins up with a narrow north/south passage. + +> s + +There is a threatening little dwarf in the room with you! + +You are in a maze of twisty little passages, all different. + +> s + +There is a threatening little dwarf in the room with you! + +You are in a maze of twisting little passages, all different. + +> e + +There is a threatening little dwarf in the room with you! + +You are in a little maze of twisting passages, all different. + +> s + +There is a threatening little dwarf in the room with you! + +Dead end + +There is a massive and somewhat battered vending machine here. The +instructions on it read: "Drop coins here to receive fresh batteries." + +> hit machine + +As you strike the vending machine, it pivots backward along with a +section of wall, revealing a dark passage leading south. + +> s + +There is a threatening little dwarf in the room with you! + +You are in a long, rough-hewn, north/south corridor. + +> s + +There is a threatening little dwarf in the room with you! + +You are in a large chamber with passages to the west and north. + +A formidable ogre bars the northern exit. + +> kill ogre + +The ogre, who despite his bulk is quite agile, easily dodges your +attack. He seems almost amused by your puny effort. + +One sharp nasty knife is thrown at you! + +The ogre, distracted by your rush, is struck by the knife. With a +blood-curdling yell he turns and bounds after the dwarf, who flees +in panic. You are left alone in the room. + +> n + +You are in the ogre's storeroom. The only exit is to the south. + +There is an enormous ruby here! + +> take ruby + +OK + +> s + +You are in a large chamber with passages to the west and north. + +> w + +You are in a long, rough-hewn, north/south corridor. + +> n + +Dead end + +There is a massive vending machine here, swung back to reveal a +southward passage. + +> n + +You are in a little maze of twisting passages, all different. + +> sw + +You are in a maze of twisting little passages, all different. + +> w + +You are in a maze of twisty little passages, all different. + +> d + +You're at west end of long hall. + +> n + +You are at a crossover of a high n/s passage and a low e/w one. + +> e + +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. + +> s + +You are in the south side chamber. + +There is precious jewelry here! + +> take jewelry + +OK + +> n + +You're in Hall of Mt King. + +> e + +You're in Hall of Mists. + +Rough stone steps lead up the dome. + +> w + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're at west end of Hall of Mists. + +> s + +You are in a maze of twisty little passages, all alike. + +> e + +Out from the shadows behind you pounces a bearded pirate! "Har, har," +he chortles, "I'll just take all this booty and hide it away with me +chest deep in the maze!" He snatches your treasure and vanishes into +the gloom. + +You are in a maze of twisty little passages, all alike. + +> s + +You are in a maze of twisty little passages, all alike. + +> s + +You are in a maze of twisty little passages, all alike. + +> s + +You are in a maze of twisty little passages, all alike. + +> n + +You are in a maze of twisty little passages, all alike. + +> e + +You are on the brink of a thirty foot pit with a massive orange column +down one wall. You could climb down here but you could not get back +up. The maze continues at this level. + +> e + +You are in a maze of twisty little passages, all alike. + +> nw + +Dead end + +There is an enormous ruby here! + +There are many coins here! + +There is precious jewelry here! + +There are diamonds here! + +The pirate's treasure chest is here! + +> drop rod + +OK + +> take jewelry + +OK + +> take chest + +OK + +> take ruby + +OK + +> take diamonds + +OK + +> take coins + +OK + +> se + +There is a threatening little dwarf in the room with you! + +You are in a maze of twisty little passages, all alike. + +> w + +There is a threatening little dwarf in the room with you! + +You are in a maze of twisty little passages, all alike. + +> s + +There is a threatening little dwarf in the room with you! + +You're at brink of pit. + +> d + +You're in bird chamber. + +> debris + +You're in debris room. + +> xyzzy + +>>Foof!<< + +You're inside building. + +There is a Persian rug spread out on the floor! + +There are bars of silver here! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is a small wicker cage discarded nearby. + +There is a little bird in the cage. + +There is an emerald here the size of a plover's egg! + +There is a bottle of oil here. + +There is a delicate, precious, ming vase here! + +A small velvet pillow lies on the floor. + +There is a platinum pyramid here, 8 inches on a side! + +There are some keys on the ground here. + +There is food here. + +> off + +Your lamp is now off. + +> drop coins + +OK + +> drop diamonds + +OK + +> drop jewelry + +OK + +> drop chest + +OK + +> drop lamp + +OK + +> take rug + +OK + +> take emerald + +OK + +> take cage + +OK + +> take bottle + +OK + +> w + +You're in front of building. + +> s + +You are in a valley in the forest beside a stream tumbling along a +rocky bed. + +> w + +You are wandering aimlessly through the forest. + +> drop bird + +OK + +> listen + +The bird is singing to you in gratitude for your having returned it to +its home. In return, it informs you of a magic word which it thinks +you may find useful somewhere near the Hall of Mists. The magic word +changes frequently, but for now the bird believes it is "N'BEH". You +thank the bird for this information, and it flies off into the forest. + +> drop cage + +OK + +> n + +You are wandering aimlessly through the forest. + +Your keen eye spots a severed leporine appendage lying on the ground. + +> take appendage + +OK + +> n + +You are wandering aimlessly through the forest. + +> e + +You are wandering aimlessly through the forest. + +> n + +You are wandering aimlessly through the forest. + +> n + +The forest thins out here to reveal a steep cliff. There is no way +down, but a small ledge can be seen to the west across the chasm. + +A small urn is embedded in the rock. + +> fill urn + +Your bottle is now empty and the urn is full of oil. + +> light urn + +The urn is now lit. + +> rub urn + +As you rub the urn, there is a flash of light and a genie appears. +His aspect is stern as he advises: "One who wouldst traffic in +precious stones must first learn to recognize the signals thereof." +He wrests the urn from the stone, leaving a small cavity. Turning to +face you again, he fixes you with a steely eye and intones: "Caution!" +Genie and urn vanish in a cloud of amber smoke. The smoke condenses +to form a rare amber gemstone, resting in the cavity in the rock. + +> drop rug + +OK + +> take amber + +OK + +> drop emerald + +The gem fits easily into the cavity. + +The Persian rug stiffens and rises a foot or so off the ground. + +> fly + +You board the Persian rug, which promptly whisks you across the chasm. +You have time for a fleeting glimpse of a two thousand foot drop to a +mighty river; then you find yourself on the other side. + +You are on a small ledge on one face of a sheer cliff. There are no +paths away from the ledge. Across the chasm is a small clearing +surrounded by forest. + +There is a Persian rug here, hovering in mid-air! + +A brilliant blue star sapphire is here! + +> take sapphire + +OK + +> fly + +The rug ferries you back across the chasm. + +You're at cliff. + +There is an emerald resting in a small cavity in the rock! + +There is a Persian rug here, hovering in mid-air! + +> take emerald + +OK + +> drop ruby + +The gem fits easily into the cavity. + +The Persian rug settles gently to the ground. + +> take rug + +OK + +> take ruby + +OK + +> e + +You are wandering aimlessly through the forest. + +> s + +You are wandering aimlessly through the forest. + +> e + +You have walked up a hill, still in the forest. The road slopes back +down the other side of the hill. There is a building in the distance. + +> e + +You're in front of building. + +> e + +You are inside a building, a well house for a large spring. + +There is a shiny brass lamp nearby. + +The pirate's treasure chest is here! + +There is precious jewelry here! + +There are diamonds here! + +There are many coins here! + +There are bars of silver here! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is a delicate, precious, ming vase here! + +A small velvet pillow lies on the floor. + +There is a platinum pyramid here, 8 inches on a side! + +There are some keys on the ground here. + +There is food here. + +> drop emerald + +OK + +> drop ruby + +OK + +> drop amber + +OK + +> drop rug + +OK + +> drop sapphire + +OK + +> fill bottle + +Your bottle is now full of water. + +> take lamp + +OK + +> plugh + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> on + +Your lamp is now on. + +You're at "Y2". + +There is a little axe here. + +> s + +You're in n/s passage above e/w passage. + +> s + +You're in Hall of Mt King. + +> sw + +You're in secret e/w canyon above tight canyon. + +> w + +You are in a secret canyon which exits to the north and east. + +The body of a huge green dead dragon is lying off to one side. + +> n + +You are in a secret n/s canyon above a large room. + +> reservoir + +You are at the edge of a large underground reservoir. An opaque cloud +of white mist fills the room and rises rapidly upward. The lake is +fed by a stream, which tumbles out of a hole in the wall about 10 feet +overhead and splashes noisily into the water somewhere within the +mist. There is a passage going back toward the south. + +> n'beh + +The waters have parted to form a narrow path across the reservoir. + +> n + +You are walking across the bottom of the reservoir. Walls of water +rear up on either side. The roar of the water cascading past is +nearly deafening, and the mist is so thick you can barely see. + +> n + +You are at the northern edge of the reservoir. A northwest passage +leads sharply up from here. + +The waters have parted to form a narrow path across the reservoir. + +> u + +You are scrambling along a treacherously steep, rocky passage. + +> u + +You are on a very steep incline, which widens at it goes upward. + +> u + +You are at the base of a nearly vertical cliff. There are some +slim footholds which would enable you to climb up, but it looks +extremely dangerous. Here at the base of the cliff lie the remains +of several earlier adventurers who apparently failed to make it. + +> u + +You are climbing along a nearly vertical cliff. + +> u + +Just as you reach the top, your foot slips on a loose rock and you +make one last desperate grab. Your luck holds, as does your grip. +With an enormous heave, you lift yourself to the ledge above. + +You are on a small ledge at the top of a nearly vertical cliff. +There is a low crawl leading off to the northeast. + +> ne + +You have reached a dead end. + +There is a richly-carved ebony statuette here! + +> take statuette + +OK + +> sw + +You're at top of cliff. + +> d + +You are climbing along a nearly vertical cliff. + +> d + +You're at base of cliff. + +> d + +You are on a very steep incline, which widens at it goes upward. + +> d + +You are scrambling along a treacherously steep, rocky passage. + +> d + +You're north of reservoir. + +The waters have parted to form a narrow path across the reservoir. + +> s + +You're at bottom of reservoir. + +> s + +You're at reservoir. + +The waters have parted to form a narrow path across the reservoir. + +> s + +You are in a north/south canyon about 25 feet across. The floor is +covered by white mist seeping in from the north. The walls extend +upward for well over 100 feet. Suspended from some unseen point far +above you, an enormous two-sided mirror is hanging parallel to and +midway between the canyon walls. (The mirror is obviously provided +for the use of the dwarves who, as you know, are extremely vain.) A +small window can be seen in either wall, some fifty feet up. + +> s + +You are in a secret n/s canyon above a large room. + +> d + +You are in a large low circular chamber whose floor is an immense slab +fallen from the ceiling (Slab Room). East and west there once were +large passages, but they are now filled with boulders. Low small +passages go north and south, and the south one quickly bends west +around the boulders. + +> s + +You're at west end of Twopit Room. + +The top of a 12-foot-tall beanstalk is poking out of the west pit. + +> d + +You're in west pit. + +There is a 12-foot-tall beanstalk stretching up out of the pit, +bellowing "WATER!! WATER!!" + +> water plant + +The plant grows explosively, almost filling the bottom of the pit. + +You're in west pit. + +There is a gigantic beanstalk stretching all the way up to the hole. + +> u + +You're at west end of Twopit Room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> e + +You're at east end of Twopit Room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> d + +You're in east pit. + +> fill bottle + +Your bottle is now full of oil. + +> u + +You're at east end of Twopit Room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> w + +You're at west end of Twopit Room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> d + +You're in west pit. + +There is a gigantic beanstalk stretching all the way up to the hole. + +> climb + +You clamber up the plant and scurry through the hole at the top. + +You are in a long, narrow corridor stretching out of sight to the +west. At the eastern end is a hole through which you can see a +profusion of leaves. + +> w + +You are in the Giant Room. The ceiling here is too high up for your +lamp to show it. Cavernous passages lead east, north, and south. On +the west wall is scrawled the inscription, "FEE FIE FOE FOO" [sic]. + +There is a large nest here, full of golden eggs! + +> take eggs + +OK + +> n + +You are at one end of an immense north/south passage. + +The way north is barred by a massive, rusty, iron door. + +> oil door + +The oil has freed up the hinges so that the door will now move, +although it requires some effort. + +> drop bottle + +OK + +> n + +You are in a magnificent cavern with a rushing stream, which cascades +over a sparkling waterfall into a roaring whirlpool which disappears +through a hole in the floor. Passages exit to the south and west. + +There is a jewel-encrusted trident here! + +> take trident + +OK + +> w + +You are at the top of a steep incline above a large room. You could +climb down here, but you would not be able to climb up. There is a +passage leading back to the north. + +> d + +You are in a large low room. Crawls lead north, se, and sw. + +> bedquilt + +You're in Bedquilt. + +> e + +You're at complex junction. + +> n + +You're in a large room carved out of sedimentary rock. The floor and +walls are littered with bits of shells embedded in the stone. A +shallow passage proceeds downward, and a somewhat steeper one leads +up. A low hands and knees passage enters from the south. + +There is an enormous clam here with its shell tightly closed. + +> open clam + +A glistening pearl falls out of the clam and rolls away. Goodness, +this must really be an oyster. (I never was very good at identifying +bivalves.) Whatever it is, it has now snapped shut again. + +> d + +You are in a long sloping corridor with ragged sharp walls. + +> d + +You are in a cul-de-sac about eight feet across. + +Off to one side lies a glistening pearl! + +> take pearl + +OK + +> shell + +You're in Shell Room. + +There is an enormous oyster here with its shell tightly closed. + +> s + +You're at complex junction. + +> u + +You're in dusty rock room. + +> e + +You're in dirty passage. + +> u + +You're in n/s passage above e/w passage. + +> n + +You're at "Y2". + +There is a little axe here. + +> plugh + +>>Foof!<< + +You're inside building. + +A brilliant blue star sapphire is here! + +There is a Persian rug spread out on the floor! + +There is a rare amber gemstone here! + +There is an enormous ruby here! + +There is an emerald here the size of a plover's egg! + +The pirate's treasure chest is here! + +There is precious jewelry here! + +There are diamonds here! + +There are many coins here! + +There are bars of silver here! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is a delicate, precious, ming vase here! + +A small velvet pillow lies on the floor. + +There is a platinum pyramid here, 8 inches on a side! + +There are some keys on the ground here. + +There is food here. + +> drop trident + +OK + +> drop pearl + +OK + +> drop statuette + +OK + +> drop appendage + +OK + +> take keys + +OK + +> take food + +OK + +> plugh + +>>Foof!<< + +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. + +There is a little axe here. + +> s + +You are in a low n/s passage at a hole in the floor. The hole goes +down to an e/w passage. + +> d + +You're in dirty passage. + +> bedquilt + +You're in Bedquilt. + +> w + +You're in Swiss Cheese Room. + +> oriental + +You're in Oriental Room. + +> w + +You're in large low room. + +> sw + +You are in a long winding corridor sloping out of sight in both +directions. + +> u + +You are on one side of a large, deep chasm. A heavy white mist rising +up from below obscures all view of the far side. A sw path leads away +from the chasm into a winding corridor. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +A burly troll stands by the bridge and insists you throw him a +treasure before you may cross. + +> throw eggs + +The troll catches your treasure and scurries away out of sight. + +> ne + +You are on the far side of the chasm. A ne path leads away from the +chasm on this side. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +The troll is nowhere to be seen. + +> barren + +You are standing at the entrance to a large, barren room. A notice +above the entrance reads: "Caution! Bear in room!" + +> e + +You are inside a barren room. The center of the room is completely +empty except for some dust. Marks in the dust lead away toward the +far end of the room. The only exit is the way you came in. + +There is a ferocious cave bear eyeing you from the far end of the room! + +The bear is locked to the wall with a golden chain! + +> throw food + +The bear eagerly wolfs down your food, after which he seems to calm +down considerably and even becomes rather friendly. + +> unlock chain + +The chain is now unlocked. + +> take chain + +OK + +> take bear + +OK + +> fork + +You are being followed by a very large, tame bear. + +The path forks here. The left fork leads northeast. A dull rumbling +seems to get louder in that direction. The right fork leads southeast +down a gentle slope. The main corridor enters from the west. + +> ne + +You are being followed by a very large, tame bear. + +The walls are quite warm here. From the north can be heard a steady +roar, so loud that the entire cave seems to be trembling. Another +passage leads south, and a low crawl goes east. + +> fee + +OK + +> fie + +OK + +> foe + +OK + +> foo + +Done! + +> e + +You are being followed by a very large, tame bear. + +You are in a small chamber filled with large boulders. The walls are +very warm, causing the air in the room to be almost stifling from the +heat. The only exit is a crawl heading west, through which is coming +a low rumbling. + +There are rare spices here! + +> take spices + +OK + +> fork + +You are being followed by a very large, tame bear. + +You're at fork in path. + +> w + +You are being followed by a very large, tame bear. + +You're in a long east/west corridor. A faint rumbling noise can be +heard in the distance. + +> w + +You are being followed by a very large, tame bear. + +You're on ne side of chasm. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +The troll is nowhere to be seen. + +> sw + +The troll steps out from beneath the bridge and blocks your way. + +You are being followed by a very large, tame bear. + +You're on ne side of chasm. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +A burly troll stands by the bridge and insists you throw him a +treasure before you may cross. + +> release bear + +The bear lumbers toward the troll, who lets out a startled shriek and +scurries away. The bear soon gives up the pursuit and wanders back. + +> sw + +You're on sw side of chasm. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +The troll is nowhere to be seen. + +> sw + +You're in sloping corridor. + +> d + +You're in large low room. + +> se + +You're in Oriental Room. + +> se + +You are in a room whose walls resemble Swiss cheese. Obvious passages +go west, east, ne, and nw. Part of the room is occupied by a large +bedrock block. + +> w + +You are at the east end of the Twopit Room. The floor here is +littered with thin rock slabs, which make it easy to descend the pits. +There is a path here bypassing the pits to connect passages from east +and west. There are holes all over, but the only big one is on the +wall directly over the west pit where you can't get to it. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> w + +You are at the west end of the Twopit Room. There is a large hole in +the wall above the pit at this end of the room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> d + +You are at the bottom of the western pit in the Twopit Room. There is +a large hole in the wall about 25 feet above you. + +There is a gigantic beanstalk stretching all the way up to the hole. + +> climb + +You clamber up the plant and scurry through the hole at the top. + +You're in narrow corridor. + +> w + +You're in Giant Room. + +There is a large nest here, full of golden eggs! + +> take eggs + +OK + +> n + +You are at one end of an immense north/south passage. + +There is an empty bottle here. + +The way north leads through a massive, rusty, iron door. + +> n + +You're in cavern with waterfall. + +> w + +You're at steep incline above large room. + +> d + +You're in large low room. + +> bedquilt + +You're in Bedquilt. + +> e + +You're at complex junction. + +> u + +You're in dusty rock room. + +> e + +You're in dirty passage. + +> u + +You're in n/s passage above e/w passage. + +> n + +There is a threatening little dwarf in the room with you! + +One sharp nasty knife is thrown at you! + +It misses! + +You're at "Y2". + +There is a little axe here. + +> plugh + +>>Foof!<< + +You're inside building. + +Your keen eye spots a severed leporine appendage lying on the ground. + +There is a richly-carved ebony statuette here! + +Off to one side lies a glistening pearl! + +There is a jewel-encrusted trident here! + +A brilliant blue star sapphire is here! + +There is a Persian rug spread out on the floor! + +There is a rare amber gemstone here! + +There is an enormous ruby here! + +There is an emerald here the size of a plover's egg! + +The pirate's treasure chest is here! + +There is precious jewelry here! + +There are diamonds here! + +There are many coins here! + +There are bars of silver here! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is a delicate, precious, ming vase here! + +A small velvet pillow lies on the floor. + +There is a platinum pyramid here, 8 inches on a side! + +> drop eggs + +OK + +> drop chain + +OK + +> drop spices + +OK + +> plugh + +>>Foof!<< + +You're at "Y2". + +A hollow voice says "PLUGH". + +There is a little axe here. + +> s + +You're in n/s passage above e/w passage. + +> look + +Sorry, but I am not allowed to give more detail. I will repeat the +long description of your location. + +You are in a low n/s passage at a hole in the floor. The hole goes +down to an e/w passage. + +> look + +Sorry, but I am not allowed to give more detail. I will repeat the +long description of your location. + +You are in a low n/s passage at a hole in the floor. The hole goes +down to an e/w passage. + +> n + +A sepulchral voice reverberating through the cave, says, "Cave closing +soon. All adventurers exit immediately through main office." + +You're at "Y2". + +There is a little axe here. + +> plugh + +A mysterious recorded voice groans into life and announces: + "This exit is closed. Please leave via main office." + +You're at "Y2". + +There is a little axe here. + +> s + +You're in n/s passage above e/w passage. + +> d + +You are in a dirty broken passage. To the east is a crawl. To the +west is a large passage. Above you is a hole to another passage. + +> w + +You're in dusty rock room. + +> d + +You're at complex junction. + +> e + +You are in an anteroom leading to a large passage to the east. Small +passages go west and up. The remnants of recent digging are evident. +A sign in midair here says "Cave under construction beyond this point. +Proceed at own risk. [Witt Construction Company]" + +There are a few recent issues of "Spelunker Today" magazine here. + +> take magazine + +OK + +> e + +You are at Witt's End. Passages lead off in *ALL* directions. + +> drop magazine + +OK + +> look + +Sorry, but I am not allowed to give more detail. I will repeat the +long description of your location. + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> look + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> look + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> look + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> look + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> look + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> look + +The sepulchral voice intones, "The cave is now closed." As the echoes +fade, there is a blinding flash of light (and a small puff of orange +smoke). . . . As your eyes refocus, you look around and find... + +You are at the northeast end of an immense room, even larger than the +Giant Room. It appears to be a repository for the "Adventure" +program. Massive torches far overhead bathe the room with smoky +yellow light. Scattered about you can be seen a pile of bottles (all +of them empty), a nursery of young beanstalks murmuring quietly, a bed +of oysters, a bundle of black rods with rusty stars on their ends, and +a collection of brass lanterns. Off to one side a great many dwarves +are sleeping on the floor, snoring loudly. A notice nearby reads: "Do +not disturb the dwarves!" An immense mirror is hanging against one +wall, and stretches to the other end of the room, where various other +sundry objects can be glimpsed dimly in the distance. + +> sw + +You are at the southwest end of the repository. To one side is a pit +full of fierce green snakes. On the other side is a row of small +wicker cages, each of which contains a little sulking bird. In one +corner is a bundle of black rods with rusty marks on their ends. A +large number of velvet pillows are scattered about on the floor. A +vast mirror stretches off to the northeast. At your feet is a large +steel grate, next to which is a sign that reads, "Treasure Vault. +Keys in main office." + +The grate is locked. + +> take pillow + +OK + +> + +> +You scored 391 out of a possible 430, using 345 turns. + +Your score puts you in Master Adventurer Class B. + +To achieve the next higher rating, you need 20 more points. diff --git a/tests/stashed.log b/tests/stashed.log new file mode 100644 index 0000000..a86a760 --- /dev/null +++ b/tests/stashed.log @@ -0,0 +1,353 @@ +## Test picking up stashed objects in endgame +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +no +seed 1318612053 +e +plugh +plove +get emerald +w +drop emerald +e +ne +get pyramid +s +plove +plugh +drop pyramid +get lamp +get water +plugh +on +s +d +bedquilt +w +e +take pillow +w +oriental +take vase +n +w +take emerald +nw +s +se +w +w +d +water plant +u +e +d +fill bottle +u +e +ne +e +u +e +u +n +plugh +drop pillow +drop vase +drop bottle +drop emerald +xyzzy +take rod +e +take cage +pit +drop rod +e +take bird +w +drop bird +take rod +wave rod +take necklace +drop rod +take bird +d +s +take nugget +n +n +drop bird +take bird +sw +w +kill dragon +yes +drink blood +take rug +e +e +n +take bars +n +plugh +drop cage +drop necklace +drop nugget +drop bars +drop rug +xyzzy +pit +take rod +d +w +wave rod +w +take diamonds +w +w +w +s +s +e +s +hit machine +s +s +kill ogre +n +take ruby +s +w +n +n +sw +w +d +n +e +take coins +e +s +take jewelry +n +e +w +w +w +s +e +s +s +s +n +e +e +nw +drop rod +take jewelry +take chest +take ruby +take diamonds +take coins +se +w +s +d +debris +xyzzy +off +drop coins +drop diamonds +drop jewelry +drop chest +drop lamp +take rug +take emerald +take cage +take bottle +w +s +w +drop bird +listen +drop cage +n +take appendage +n +e +n +n +fill urn +light urn +rub urn +drop rug +take amber +drop emerald +fly +take sapphire +fly +take emerald +drop ruby +take rug +take ruby +e +s +e +e +e +drop emerald +drop ruby +drop amber +drop rug +drop sapphire +fill bottle +take lamp +plugh +on +s +s +sw +w +n +reservoir +n'beh +n +n +u +u +u +u +u +ne +take statuette +sw +d +d +d +d +d +s +s +s +s +d +s +d +water plant +u +e +d +fill bottle +u +w +d +climb +w +take eggs +n +oil door +drop bottle +n +take trident +w +d +bedquilt +e +n +open clam +d +d +take pearl +shell +s +u +e +u +n +plugh +drop trident +drop pearl +drop statuette +drop appendage +take keys +take food +plugh +s +d +bedquilt +w +oriental +w +sw +u +throw eggs +ne +barren +e +throw food +unlock chain +take chain +take bear +fork +ne +fee +fie +foe +foo +e +take spices +fork +w +w +sw +release bear +sw +sw +d +se +se +w +w +d +climb +w +take eggs +n +n +w +d +bedquilt +e +u +e +u +n +plugh +drop eggs +drop chain +drop spices +plugh +s +look +look +n +plugh +s +d +w +d +e +take magazine +e +drop magazine +look +look +look +look +look +look +look +sw +# Here we are +take pillow + diff --git a/tests/takebird.chk b/tests/takebird.chk new file mode 100644 index 0000000..6c6b64d --- /dev/null +++ b/tests/takebird.chk @@ -0,0 +1,2084 @@ + +Welcome to Adventure!! Would you like instructions? + +> no + +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 1318612053 + +Seed set to 1318612053 + +You're in front of building. + +> e + +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. + +> plugh + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> plove + +>>Foof!<< + +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west. A dark corridor leads ne. + +There is an emerald here the size of a plover's egg! + +> get emerald + +OK + +> w + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> drop emerald + +OK + +> e + +You're in Plover Room. + +> ne + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> get pyramid + +OK + +> s + +You're in Plover Room. + +> plove + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> plugh + +>>Foof!<< + +You're inside building. + +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. + +> drop pyramid + +OK + +> get lamp + +OK + +> get water + +OK + +> plugh + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> on + +Your lamp is now on. + +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. + +A hollow voice says "PLUGH". + +> s + +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! + +> d + +You are in a dirty broken passage. To the east is a crawl. To the +west is a large passage. Above you is a hole to another passage. + +> bedquilt + +You are in Bedquilt, a long east/west passage with holes everywhere. +To explore at random select north, south, up, or down. + +> w + +You are in a room whose walls resemble Swiss cheese. Obvious passages +go west, east, ne, and nw. Part of the room is occupied by a large +bedrock block. + +> e + +You are in the Soft Room. The walls are covered with heavy curtains, +the floor with a thick pile carpet. Moss covers the ceiling. + +A small velvet pillow lies on the floor. + +> take pillow + +OK + +> w + +You're in Swiss Cheese Room. + +> oriental + +This is the Oriental Room. Ancient oriental cave drawings cover the +walls. A gently sloping passage leads upward to the north, another +passage leads se, and a hands and knees crawl leads west. + +There is a delicate, precious, ming vase here! + +> take vase + +OK + +> n + +You are following a wide path around the outer edge of a large cavern. +Far below, through a heavy white mist, strange splashing noises can be +heard. The mist rises up through a fissure in the ceiling. The path +exits to the south and west. + +> w + +You are in an alcove. A small nw path seems to widen after a short +distance. An extremely tight tunnel leads east. It looks like a very +tight squeeze. An eerie light can be seen at the other end. + +There is an emerald here the size of a plover's egg! + +> take emerald + +OK + +> nw + +You're in misty cavern. + +> s + +You're in Oriental Room. + +> se + +You're in Swiss Cheese Room. + +> w + +You are at the east end of the Twopit Room. The floor here is +littered with thin rock slabs, which make it easy to descend the pits. +There is a path here bypassing the pits to connect passages from east +and west. There are holes all over, but the only big one is on the +wall directly over the west pit where you can't get to it. + +> w + +You are at the west end of the Twopit Room. There is a large hole in +the wall above the pit at this end of the room. + +> d + +You are at the bottom of the western pit in the Twopit Room. There is +a large hole in the wall about 25 feet above you. + +There is a tiny little plant in the pit, murmuring "water, water, ..." + +> water plant + +The plant spurts into furious growth for a few seconds. + +You're in west pit. + +There is a 12-foot-tall beanstalk stretching up out of the pit, +bellowing "WATER!! WATER!!" + +> u + +You're at west end of Twopit Room. + +The top of a 12-foot-tall beanstalk is poking out of the west pit. + +> e + +You're at east end of Twopit Room. + +The top of a 12-foot-tall beanstalk is poking out of the west pit. + +> d + +You are at the bottom of the eastern pit in the Twopit Room. There is +a small pool of oil in one corner of the pit. + +> fill bottle + +Your bottle is now full of oil. + +> u + +You're at east end of Twopit Room. + +The top of a 12-foot-tall beanstalk is poking out of the west pit. + +> e + +You're in Swiss Cheese Room. + +> ne + +You're in Bedquilt. + +> e + +You are at a complex junction. A low hands and knees passage from the +north joins a higher crawl from the east to make a walking passage +going west. There is also a large room above. The air is damp here. + +> u + +You are in a large room full of dusty rocks. There is a big hole in +the floor. There are cracks everywhere, and a passage leading east. + +> e + +You're in dirty passage. + +> u + +You're in n/s passage above e/w passage. + +There are bars of silver here! + +> n + +A little dwarf just walked around a corner, saw you, threw a little +axe at you which missed, cursed, and ran away. + +You're at "Y2". + +There is a little axe here. + +> plugh + +>>Foof!<< + +You're inside building. + +There is a platinum pyramid here, 8 inches on a side! + +There are some keys on the ground here. + +There is food here. + +> drop pillow + +OK + +> drop vase + +The vase is now resting, delicately, on a velvet pillow. + +> drop bottle + +OK + +> drop emerald + +OK + +> xyzzy + +>>Foof!<< + +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 + +> e + +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 + +> pit + +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. + +> drop rod + +OK + +> e + +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 + +> w + +You're at top of small pit. + +A three foot black rod with a rusty star on an end lies nearby. + +Rough stone steps lead down the pit. + +> drop bird + +OK + +> take rod + +OK + +> wave rod + +The bird flies about agitatedly for a moment, then disappears through +the crack. It reappears shortly, carrying in its beak a jade +necklace, which it drops at your feet. + +> take necklace + +OK + +> drop rod + +OK + +> take bird + +OK + +> d + +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. + +> s + +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 nugget + +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. + +> take bird + +OK + +> sw + +You are in a secret canyon which here runs e/w. It crosses over a +very tight canyon 15 feet below. If you go down you may not be able +to get back up. + +> w + +You are in a secret canyon which exits to the north and east. + +A huge green fierce dragon bars the way! + +The dragon is sprawled out on a Persian rug!! + +> kill dragon + +With what? Your bare hands? + +> yes + +Congratulations! You have just vanquished a dragon with your bare +hands! (Unbelievable, isn't it?) + +You are in a secret canyon which exits to the north and east. + +There is a Persian rug spread out on the floor! + +The blood-specked body of a huge green dead dragon lies to one side. + +> drink blood + +Your head buzzes strangely for a moment. + +> take rug + +OK + +> e + +You're in secret e/w canyon above tight canyon. + +> e + +You're in Hall of Mt King. + +> n + +You're in n/s passage above e/w passage. + +There are bars of silver here! + +> take bars + +OK + +> n + +You're at "Y2". + +There is a little axe here. + +> plugh + +>>Foof!<< + +You're inside building. + +There is an emerald here the size of a plover's egg! + +There is a bottle of oil here. + +There is a delicate, precious, ming vase here! + +A small velvet pillow lies on the floor. + +There is a platinum pyramid here, 8 inches on a side! + +There are some keys on the ground here. + +There is food here. + +> drop cage + +OK + +> drop necklace + +OK + +> drop nugget + +OK + +> drop bars + +OK + +> drop rug + +OK + +> xyzzy + +>>Foof!<< + +You're in debris room. + +> pit + +You're at top of small pit. + +A three foot black rod with a rusty star on an end lies nearby. + +Rough stone steps lead down the pit. + +> take rod + +OK + +> d + +You're in Hall of Mists. + +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. + +> wave rod + +A crystal bridge now spans the fissure. + +> w + +You are on the west side of the fissure in the Hall of Mists. + +There are diamonds here! + +A crystal bridge spans the fissure. + +> take diamonds + +OK + +> w + +There is a threatening little dwarf in the room with you! + +You are at the west end of the Hall of Mists. A low wide crawl +continues west and another goes north. To the south is a little +passage 6 feet off the floor. + +> w + +There is a threatening little dwarf in the room with you! + +You are at the east end of a very long hall apparently without side +chambers. To the east a low wide crawl slants up. To the north a +round two foot hole slants down. + +> w + +There is a threatening little dwarf in the room with you! + +You are at the west end of a very long featureless hall. The hall +joins up with a narrow north/south passage. + +> s + +There is a threatening little dwarf in the room with you! + +You are in a maze of twisty little passages, all different. + +> s + +There is a threatening little dwarf in the room with you! + +You are in a maze of twisting little passages, all different. + +> e + +There is a threatening little dwarf in the room with you! + +You are in a little maze of twisting passages, all different. + +> s + +There is a threatening little dwarf in the room with you! + +Dead end + +There is a massive and somewhat battered vending machine here. The +instructions on it read: "Drop coins here to receive fresh batteries." + +> hit machine + +As you strike the vending machine, it pivots backward along with a +section of wall, revealing a dark passage leading south. + +> s + +There is a threatening little dwarf in the room with you! + +You are in a long, rough-hewn, north/south corridor. + +> s + +There is a threatening little dwarf in the room with you! + +You are in a large chamber with passages to the west and north. + +A formidable ogre bars the northern exit. + +> kill ogre + +The ogre, who despite his bulk is quite agile, easily dodges your +attack. He seems almost amused by your puny effort. + +One sharp nasty knife is thrown at you! + +The ogre, distracted by your rush, is struck by the knife. With a +blood-curdling yell he turns and bounds after the dwarf, who flees +in panic. You are left alone in the room. + +> n + +You are in the ogre's storeroom. The only exit is to the south. + +There is an enormous ruby here! + +> take ruby + +OK + +> s + +You are in a large chamber with passages to the west and north. + +> w + +You are in a long, rough-hewn, north/south corridor. + +> n + +Dead end + +There is a massive vending machine here, swung back to reveal a +southward passage. + +> n + +You are in a little maze of twisting passages, all different. + +> sw + +You are in a maze of twisting little passages, all different. + +> w + +You are in a maze of twisty little passages, all different. + +> d + +You're at west end of long hall. + +> n + +You are at a crossover of a high n/s passage and a low e/w one. + +> e + +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. + +> s + +You are in the south side chamber. + +There is precious jewelry here! + +> take jewelry + +OK + +> n + +You're in Hall of Mt King. + +> e + +You're in Hall of Mists. + +Rough stone steps lead up the dome. + +> w + +You're on east bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're on west bank of fissure. + +A crystal bridge spans the fissure. + +> w + +You're at west end of Hall of Mists. + +> s + +You are in a maze of twisty little passages, all alike. + +> e + +Out from the shadows behind you pounces a bearded pirate! "Har, har," +he chortles, "I'll just take all this booty and hide it away with me +chest deep in the maze!" He snatches your treasure and vanishes into +the gloom. + +You are in a maze of twisty little passages, all alike. + +> s + +You are in a maze of twisty little passages, all alike. + +> s + +You are in a maze of twisty little passages, all alike. + +> s + +You are in a maze of twisty little passages, all alike. + +> n + +You are in a maze of twisty little passages, all alike. + +> e + +You are on the brink of a thirty foot pit with a massive orange column +down one wall. You could climb down here but you could not get back +up. The maze continues at this level. + +> e + +You are in a maze of twisty little passages, all alike. + +> nw + +Dead end + +There is an enormous ruby here! + +There are many coins here! + +There is precious jewelry here! + +There are diamonds here! + +The pirate's treasure chest is here! + +> drop rod + +OK + +> take jewelry + +OK + +> take chest + +OK + +> take ruby + +OK + +> take diamonds + +OK + +> take coins + +OK + +> se + +There is a threatening little dwarf in the room with you! + +You are in a maze of twisty little passages, all alike. + +> w + +There is a threatening little dwarf in the room with you! + +You are in a maze of twisty little passages, all alike. + +> s + +There is a threatening little dwarf in the room with you! + +You're at brink of pit. + +> d + +You're in bird chamber. + +> debris + +You're in debris room. + +> xyzzy + +>>Foof!<< + +You're inside building. + +There is a Persian rug spread out on the floor! + +There are bars of silver here! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is a small wicker cage discarded nearby. + +There is a little bird in the cage. + +There is an emerald here the size of a plover's egg! + +There is a bottle of oil here. + +There is a delicate, precious, ming vase here! + +A small velvet pillow lies on the floor. + +There is a platinum pyramid here, 8 inches on a side! + +There are some keys on the ground here. + +There is food here. + +> off + +Your lamp is now off. + +> drop coins + +OK + +> drop diamonds + +OK + +> drop jewelry + +OK + +> drop chest + +OK + +> drop lamp + +OK + +> take rug + +OK + +> take emerald + +OK + +> take cage + +OK + +> take bottle + +OK + +> w + +You're in front of building. + +> s + +You are in a valley in the forest beside a stream tumbling along a +rocky bed. + +> w + +You are wandering aimlessly through the forest. + +> drop bird + +OK + +> listen + +The bird is singing to you in gratitude for your having returned it to +its home. In return, it informs you of a magic word which it thinks +you may find useful somewhere near the Hall of Mists. The magic word +changes frequently, but for now the bird believes it is "N'BEH". You +thank the bird for this information, and it flies off into the forest. + +> drop cage + +OK + +> n + +You are wandering aimlessly through the forest. + +Your keen eye spots a severed leporine appendage lying on the ground. + +> take appendage + +OK + +> n + +You are wandering aimlessly through the forest. + +> e + +You are wandering aimlessly through the forest. + +> n + +You are wandering aimlessly through the forest. + +> n + +The forest thins out here to reveal a steep cliff. There is no way +down, but a small ledge can be seen to the west across the chasm. + +A small urn is embedded in the rock. + +> fill urn + +Your bottle is now empty and the urn is full of oil. + +> light urn + +The urn is now lit. + +> rub urn + +As you rub the urn, there is a flash of light and a genie appears. +His aspect is stern as he advises: "One who wouldst traffic in +precious stones must first learn to recognize the signals thereof." +He wrests the urn from the stone, leaving a small cavity. Turning to +face you again, he fixes you with a steely eye and intones: "Caution!" +Genie and urn vanish in a cloud of amber smoke. The smoke condenses +to form a rare amber gemstone, resting in the cavity in the rock. + +> drop rug + +OK + +> take amber + +OK + +> drop emerald + +The gem fits easily into the cavity. + +The Persian rug stiffens and rises a foot or so off the ground. + +> fly + +You board the Persian rug, which promptly whisks you across the chasm. +You have time for a fleeting glimpse of a two thousand foot drop to a +mighty river; then you find yourself on the other side. + +You are on a small ledge on one face of a sheer cliff. There are no +paths away from the ledge. Across the chasm is a small clearing +surrounded by forest. + +There is a Persian rug here, hovering in mid-air! + +A brilliant blue star sapphire is here! + +> take sapphire + +OK + +> fly + +The rug ferries you back across the chasm. + +You're at cliff. + +There is an emerald resting in a small cavity in the rock! + +There is a Persian rug here, hovering in mid-air! + +> take emerald + +OK + +> drop ruby + +The gem fits easily into the cavity. + +The Persian rug settles gently to the ground. + +> take rug + +OK + +> take ruby + +OK + +> e + +You are wandering aimlessly through the forest. + +> s + +You are wandering aimlessly through the forest. + +> e + +You have walked up a hill, still in the forest. The road slopes back +down the other side of the hill. There is a building in the distance. + +> e + +You're in front of building. + +> e + +You are inside a building, a well house for a large spring. + +There is a shiny brass lamp nearby. + +The pirate's treasure chest is here! + +There is precious jewelry here! + +There are diamonds here! + +There are many coins here! + +There are bars of silver here! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is a delicate, precious, ming vase here! + +A small velvet pillow lies on the floor. + +There is a platinum pyramid here, 8 inches on a side! + +There are some keys on the ground here. + +There is food here. + +> drop emerald + +OK + +> drop ruby + +OK + +> drop amber + +OK + +> drop rug + +OK + +> drop sapphire + +OK + +> fill bottle + +Your bottle is now full of water. + +> take lamp + +OK + +> plugh + +>>Foof!<< + +It is now pitch dark. If you proceed you will likely fall into a pit. + +> on + +Your lamp is now on. + +You're at "Y2". + +There is a little axe here. + +> s + +You're in n/s passage above e/w passage. + +> s + +You're in Hall of Mt King. + +> sw + +You're in secret e/w canyon above tight canyon. + +> w + +You are in a secret canyon which exits to the north and east. + +The body of a huge green dead dragon is lying off to one side. + +> n + +You are in a secret n/s canyon above a large room. + +> reservoir + +You are at the edge of a large underground reservoir. An opaque cloud +of white mist fills the room and rises rapidly upward. The lake is +fed by a stream, which tumbles out of a hole in the wall about 10 feet +overhead and splashes noisily into the water somewhere within the +mist. There is a passage going back toward the south. + +> n'beh + +The waters have parted to form a narrow path across the reservoir. + +> n + +You are walking across the bottom of the reservoir. Walls of water +rear up on either side. The roar of the water cascading past is +nearly deafening, and the mist is so thick you can barely see. + +> n + +You are at the northern edge of the reservoir. A northwest passage +leads sharply up from here. + +The waters have parted to form a narrow path across the reservoir. + +> u + +You are scrambling along a treacherously steep, rocky passage. + +> u + +You are on a very steep incline, which widens at it goes upward. + +> u + +You are at the base of a nearly vertical cliff. There are some +slim footholds which would enable you to climb up, but it looks +extremely dangerous. Here at the base of the cliff lie the remains +of several earlier adventurers who apparently failed to make it. + +> u + +You are climbing along a nearly vertical cliff. + +> u + +Just as you reach the top, your foot slips on a loose rock and you +make one last desperate grab. Your luck holds, as does your grip. +With an enormous heave, you lift yourself to the ledge above. + +You are on a small ledge at the top of a nearly vertical cliff. +There is a low crawl leading off to the northeast. + +> ne + +You have reached a dead end. + +There is a richly-carved ebony statuette here! + +> take statuette + +OK + +> sw + +You're at top of cliff. + +> d + +You are climbing along a nearly vertical cliff. + +> d + +You're at base of cliff. + +> d + +You are on a very steep incline, which widens at it goes upward. + +> d + +You are scrambling along a treacherously steep, rocky passage. + +> d + +You're north of reservoir. + +The waters have parted to form a narrow path across the reservoir. + +> s + +You're at bottom of reservoir. + +> s + +You're at reservoir. + +The waters have parted to form a narrow path across the reservoir. + +> s + +You are in a north/south canyon about 25 feet across. The floor is +covered by white mist seeping in from the north. The walls extend +upward for well over 100 feet. Suspended from some unseen point far +above you, an enormous two-sided mirror is hanging parallel to and +midway between the canyon walls. (The mirror is obviously provided +for the use of the dwarves who, as you know, are extremely vain.) A +small window can be seen in either wall, some fifty feet up. + +> s + +You are in a secret n/s canyon above a large room. + +> d + +You are in a large low circular chamber whose floor is an immense slab +fallen from the ceiling (Slab Room). East and west there once were +large passages, but they are now filled with boulders. Low small +passages go north and south, and the south one quickly bends west +around the boulders. + +> s + +You're at west end of Twopit Room. + +The top of a 12-foot-tall beanstalk is poking out of the west pit. + +> d + +You're in west pit. + +There is a 12-foot-tall beanstalk stretching up out of the pit, +bellowing "WATER!! WATER!!" + +> water plant + +The plant grows explosively, almost filling the bottom of the pit. + +You're in west pit. + +There is a gigantic beanstalk stretching all the way up to the hole. + +> u + +You're at west end of Twopit Room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> e + +You're at east end of Twopit Room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> d + +You're in east pit. + +> fill bottle + +Your bottle is now full of oil. + +> u + +You're at east end of Twopit Room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> w + +You're at west end of Twopit Room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> d + +You're in west pit. + +There is a gigantic beanstalk stretching all the way up to the hole. + +> climb + +You clamber up the plant and scurry through the hole at the top. + +You are in a long, narrow corridor stretching out of sight to the +west. At the eastern end is a hole through which you can see a +profusion of leaves. + +> w + +You are in the Giant Room. The ceiling here is too high up for your +lamp to show it. Cavernous passages lead east, north, and south. On +the west wall is scrawled the inscription, "FEE FIE FOE FOO" [sic]. + +There is a large nest here, full of golden eggs! + +> take eggs + +OK + +> n + +You are at one end of an immense north/south passage. + +The way north is barred by a massive, rusty, iron door. + +> oil door + +The oil has freed up the hinges so that the door will now move, +although it requires some effort. + +> drop bottle + +OK + +> n + +You are in a magnificent cavern with a rushing stream, which cascades +over a sparkling waterfall into a roaring whirlpool which disappears +through a hole in the floor. Passages exit to the south and west. + +There is a jewel-encrusted trident here! + +> take trident + +OK + +> w + +You are at the top of a steep incline above a large room. You could +climb down here, but you would not be able to climb up. There is a +passage leading back to the north. + +> d + +You are in a large low room. Crawls lead north, se, and sw. + +> bedquilt + +You're in Bedquilt. + +> e + +You're at complex junction. + +> n + +You're in a large room carved out of sedimentary rock. The floor and +walls are littered with bits of shells embedded in the stone. A +shallow passage proceeds downward, and a somewhat steeper one leads +up. A low hands and knees passage enters from the south. + +There is an enormous clam here with its shell tightly closed. + +> open clam + +A glistening pearl falls out of the clam and rolls away. Goodness, +this must really be an oyster. (I never was very good at identifying +bivalves.) Whatever it is, it has now snapped shut again. + +> d + +You are in a long sloping corridor with ragged sharp walls. + +> d + +You are in a cul-de-sac about eight feet across. + +Off to one side lies a glistening pearl! + +> take pearl + +OK + +> shell + +You're in Shell Room. + +There is an enormous oyster here with its shell tightly closed. + +> s + +You're at complex junction. + +> u + +You're in dusty rock room. + +> e + +You're in dirty passage. + +> u + +You're in n/s passage above e/w passage. + +> n + +You're at "Y2". + +There is a little axe here. + +> plugh + +>>Foof!<< + +You're inside building. + +A brilliant blue star sapphire is here! + +There is a Persian rug spread out on the floor! + +There is a rare amber gemstone here! + +There is an enormous ruby here! + +There is an emerald here the size of a plover's egg! + +The pirate's treasure chest is here! + +There is precious jewelry here! + +There are diamonds here! + +There are many coins here! + +There are bars of silver here! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is a delicate, precious, ming vase here! + +A small velvet pillow lies on the floor. + +There is a platinum pyramid here, 8 inches on a side! + +There are some keys on the ground here. + +There is food here. + +> drop trident + +OK + +> drop pearl + +OK + +> drop statuette + +OK + +> drop appendage + +OK + +> take keys + +OK + +> take food + +OK + +> plugh + +>>Foof!<< + +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. + +There is a little axe here. + +> s + +You are in a low n/s passage at a hole in the floor. The hole goes +down to an e/w passage. + +> d + +You're in dirty passage. + +> bedquilt + +You're in Bedquilt. + +> w + +You're in Swiss Cheese Room. + +> oriental + +You're in Oriental Room. + +> w + +You're in large low room. + +> sw + +You are in a long winding corridor sloping out of sight in both +directions. + +> u + +You are on one side of a large, deep chasm. A heavy white mist rising +up from below obscures all view of the far side. A sw path leads away +from the chasm into a winding corridor. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +A burly troll stands by the bridge and insists you throw him a +treasure before you may cross. + +> throw eggs + +The troll catches your treasure and scurries away out of sight. + +> ne + +You are on the far side of the chasm. A ne path leads away from the +chasm on this side. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +The troll is nowhere to be seen. + +> barren + +You are standing at the entrance to a large, barren room. A notice +above the entrance reads: "Caution! Bear in room!" + +> e + +You are inside a barren room. The center of the room is completely +empty except for some dust. Marks in the dust lead away toward the +far end of the room. The only exit is the way you came in. + +There is a ferocious cave bear eyeing you from the far end of the room! + +The bear is locked to the wall with a golden chain! + +> throw food + +The bear eagerly wolfs down your food, after which he seems to calm +down considerably and even becomes rather friendly. + +> unlock chain + +The chain is now unlocked. + +> take chain + +OK + +> take bear + +OK + +> fork + +You are being followed by a very large, tame bear. + +The path forks here. The left fork leads northeast. A dull rumbling +seems to get louder in that direction. The right fork leads southeast +down a gentle slope. The main corridor enters from the west. + +> ne + +You are being followed by a very large, tame bear. + +The walls are quite warm here. From the north can be heard a steady +roar, so loud that the entire cave seems to be trembling. Another +passage leads south, and a low crawl goes east. + +> fee + +OK + +> fie + +OK + +> foe + +OK + +> foo + +Done! + +> e + +You are being followed by a very large, tame bear. + +You are in a small chamber filled with large boulders. The walls are +very warm, causing the air in the room to be almost stifling from the +heat. The only exit is a crawl heading west, through which is coming +a low rumbling. + +There are rare spices here! + +> take spices + +OK + +> fork + +You are being followed by a very large, tame bear. + +You're at fork in path. + +> w + +You are being followed by a very large, tame bear. + +You're in a long east/west corridor. A faint rumbling noise can be +heard in the distance. + +> w + +You are being followed by a very large, tame bear. + +You're on ne side of chasm. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +The troll is nowhere to be seen. + +> sw + +The troll steps out from beneath the bridge and blocks your way. + +You are being followed by a very large, tame bear. + +You're on ne side of chasm. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +A burly troll stands by the bridge and insists you throw him a +treasure before you may cross. + +> release bear + +The bear lumbers toward the troll, who lets out a startled shriek and +scurries away. The bear soon gives up the pursuit and wanders back. + +> sw + +You're on sw side of chasm. + +A rickety wooden bridge extends across the chasm, vanishing into the +mist. A notice posted on the bridge reads, "Stop! Pay troll!" + +The troll is nowhere to be seen. + +> sw + +You're in sloping corridor. + +> d + +You're in large low room. + +> se + +You're in Oriental Room. + +> se + +You are in a room whose walls resemble Swiss cheese. Obvious passages +go west, east, ne, and nw. Part of the room is occupied by a large +bedrock block. + +> w + +You are at the east end of the Twopit Room. The floor here is +littered with thin rock slabs, which make it easy to descend the pits. +There is a path here bypassing the pits to connect passages from east +and west. There are holes all over, but the only big one is on the +wall directly over the west pit where you can't get to it. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> w + +You are at the west end of the Twopit Room. There is a large hole in +the wall above the pit at this end of the room. + +There is a huge beanstalk growing out of the west pit up to the hole. + +> d + +You are at the bottom of the western pit in the Twopit Room. There is +a large hole in the wall about 25 feet above you. + +There is a gigantic beanstalk stretching all the way up to the hole. + +> climb + +You clamber up the plant and scurry through the hole at the top. + +You're in narrow corridor. + +> w + +You're in Giant Room. + +There is a large nest here, full of golden eggs! + +> take eggs + +OK + +> n + +You are at one end of an immense north/south passage. + +There is an empty bottle here. + +The way north leads through a massive, rusty, iron door. + +> n + +You're in cavern with waterfall. + +> w + +You're at steep incline above large room. + +> d + +You're in large low room. + +> bedquilt + +You're in Bedquilt. + +> e + +You're at complex junction. + +> u + +You're in dusty rock room. + +> e + +You're in dirty passage. + +> u + +You're in n/s passage above e/w passage. + +> n + +There is a threatening little dwarf in the room with you! + +One sharp nasty knife is thrown at you! + +It misses! + +You're at "Y2". + +There is a little axe here. + +> plugh + +>>Foof!<< + +You're inside building. + +Your keen eye spots a severed leporine appendage lying on the ground. + +There is a richly-carved ebony statuette here! + +Off to one side lies a glistening pearl! + +There is a jewel-encrusted trident here! + +A brilliant blue star sapphire is here! + +There is a Persian rug spread out on the floor! + +There is a rare amber gemstone here! + +There is an enormous ruby here! + +There is an emerald here the size of a plover's egg! + +The pirate's treasure chest is here! + +There is precious jewelry here! + +There are diamonds here! + +There are many coins here! + +There are bars of silver here! + +There is a large sparkling nugget of gold here! + +A precious jade necklace has been dropped here! + +There is a delicate, precious, ming vase here! + +A small velvet pillow lies on the floor. + +There is a platinum pyramid here, 8 inches on a side! + +> drop eggs + +OK + +> drop chain + +OK + +> drop spices + +OK + +> plugh + +>>Foof!<< + +You're at "Y2". + +A hollow voice says "PLUGH". + +There is a little axe here. + +> s + +You're in n/s passage above e/w passage. + +> look + +Sorry, but I am not allowed to give more detail. I will repeat the +long description of your location. + +You are in a low n/s passage at a hole in the floor. The hole goes +down to an e/w passage. + +> look + +Sorry, but I am not allowed to give more detail. I will repeat the +long description of your location. + +You are in a low n/s passage at a hole in the floor. The hole goes +down to an e/w passage. + +> n + +A sepulchral voice reverberating through the cave, says, "Cave closing +soon. All adventurers exit immediately through main office." + +You're at "Y2". + +There is a little axe here. + +> plugh + +A mysterious recorded voice groans into life and announces: + "This exit is closed. Please leave via main office." + +You're at "Y2". + +There is a little axe here. + +> s + +You're in n/s passage above e/w passage. + +> d + +You are in a dirty broken passage. To the east is a crawl. To the +west is a large passage. Above you is a hole to another passage. + +> w + +You're in dusty rock room. + +> d + +You're at complex junction. + +> e + +You are in an anteroom leading to a large passage to the east. Small +passages go west and up. The remnants of recent digging are evident. +A sign in midair here says "Cave under construction beyond this point. +Proceed at own risk. [Witt Construction Company]" + +There are a few recent issues of "Spelunker Today" magazine here. + +> take magazine + +OK + +> e + +You are at Witt's End. Passages lead off in *ALL* directions. + +> drop magazine + +OK + +> look + +Sorry, but I am not allowed to give more detail. I will repeat the +long description of your location. + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> look + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> look + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> look + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> look + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> look + +You are at Witt's End. Passages lead off in *ALL* directions. + +There are a few recent issues of "Spelunker Today" magazine here. + +> look + +The sepulchral voice intones, "The cave is now closed." As the echoes +fade, there is a blinding flash of light (and a small puff of orange +smoke). . . . As your eyes refocus, you look around and find... + +You are at the northeast end of an immense room, even larger than the +Giant Room. It appears to be a repository for the "Adventure" +program. Massive torches far overhead bathe the room with smoky +yellow light. Scattered about you can be seen a pile of bottles (all +of them empty), a nursery of young beanstalks murmuring quietly, a bed +of oysters, a bundle of black rods with rusty stars on their ends, and +a collection of brass lanterns. Off to one side a great many dwarves +are sleeping on the floor, snoring loudly. A notice nearby reads: "Do +not disturb the dwarves!" An immense mirror is hanging against one +wall, and stretches to the other end of the room, where various other +sundry objects can be glimpsed dimly in the distance. + +> sw + +You are at the southwest end of the repository. To one side is a pit +full of fierce green snakes. On the other side is a row of small +wicker cages, each of which contains a little sulking bird. In one +corner is a bundle of black rods with rusty marks on their ends. A +large number of velvet pillows are scattered about on the floor. A +vast mirror stretches off to the northeast. At your feet is a large +steel grate, next to which is a sign that reads, "Treasure Vault. +Keys in main office." + +The grate is locked. + +> take bird + +OK + +> +You scored 391 out of a possible 430, using 345 turns. + +Your score puts you in Master Adventurer Class B. + +To achieve the next higher rating, you need 20 more points. diff --git a/tests/takebird.log b/tests/takebird.log new file mode 100644 index 0000000..e4fa8e2 --- /dev/null +++ b/tests/takebird.log @@ -0,0 +1,353 @@ +## Verify that bird starts caged in endgame +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +no +seed 1318612053 +e +plugh +plove +get emerald +w +drop emerald +e +ne +get pyramid +s +plove +plugh +drop pyramid +get lamp +get water +plugh +on +s +d +bedquilt +w +e +take pillow +w +oriental +take vase +n +w +take emerald +nw +s +se +w +w +d +water plant +u +e +d +fill bottle +u +e +ne +e +u +e +u +n +plugh +drop pillow +drop vase +drop bottle +drop emerald +xyzzy +take rod +e +take cage +pit +drop rod +e +take bird +w +drop bird +take rod +wave rod +take necklace +drop rod +take bird +d +s +take nugget +n +n +drop bird +take bird +sw +w +kill dragon +yes +drink blood +take rug +e +e +n +take bars +n +plugh +drop cage +drop necklace +drop nugget +drop bars +drop rug +xyzzy +pit +take rod +d +w +wave rod +w +take diamonds +w +w +w +s +s +e +s +hit machine +s +s +kill ogre +n +take ruby +s +w +n +n +sw +w +d +n +e +take coins +e +s +take jewelry +n +e +w +w +w +s +e +s +s +s +n +e +e +nw +drop rod +take jewelry +take chest +take ruby +take diamonds +take coins +se +w +s +d +debris +xyzzy +off +drop coins +drop diamonds +drop jewelry +drop chest +drop lamp +take rug +take emerald +take cage +take bottle +w +s +w +drop bird +listen +drop cage +n +take appendage +n +e +n +n +fill urn +light urn +rub urn +drop rug +take amber +drop emerald +fly +take sapphire +fly +take emerald +drop ruby +take rug +take ruby +e +s +e +e +e +drop emerald +drop ruby +drop amber +drop rug +drop sapphire +fill bottle +take lamp +plugh +on +s +s +sw +w +n +reservoir +n'beh +n +n +u +u +u +u +u +ne +take statuette +sw +d +d +d +d +d +s +s +s +s +d +s +d +water plant +u +e +d +fill bottle +u +w +d +climb +w +take eggs +n +oil door +drop bottle +n +take trident +w +d +bedquilt +e +n +open clam +d +d +take pearl +shell +s +u +e +u +n +plugh +drop trident +drop pearl +drop statuette +drop appendage +take keys +take food +plugh +s +d +bedquilt +w +oriental +w +sw +u +throw eggs +ne +barren +e +throw food +unlock chain +take chain +take bear +fork +ne +fee +fie +foe +foo +e +take spices +fork +w +w +sw +release bear +sw +sw +d +se +se +w +w +d +climb +w +take eggs +n +n +w +d +bedquilt +e +u +e +u +n +plugh +drop eggs +drop chain +drop spices +plugh +s +look +look +n +plugh +s +d +w +d +e +take magazine +e +drop magazine +look +look +look +look +look +look +look +sw +# Good response "OK". +# Buggy response: "You can catch the bird, but you cannot carry it." +take bird diff --git a/tests/tall.chk b/tests/tall.chk index d962f77..05f456b 100644 --- a/tests/tall.chk +++ b/tests/tall.chk @@ -1024,7 +1024,7 @@ seems to open up. The canyon runs into a mass of boulders -- dead end. - +> You scored 77 out of a possible 430, using 163 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/tall.log b/tests/tall.log index 19081c6..259732e 100644 --- a/tests/tall.log +++ b/tests/tall.log @@ -1,4 +1,6 @@ ## Coverage of LOC_TALL, LOC_WIDEPLACE, LOC_TIGHTPLACE +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in @@ -164,4 +166,4 @@ e s n n -w \ No newline at end of file +w diff --git a/tests/tapdiffer b/tests/tapdiffer index b529a39..ea1cfa7 100755 --- a/tests/tapdiffer +++ b/tests/tapdiffer @@ -8,24 +8,46 @@ # A nonempty diff is shipped as a TAP YAML block following "not ok" # unless QUIET=1 in the environment. # +# This code is intended to be embedded in your project. The author +# grants permission for it to be distributed under the prevailing +# license of your project if you choose, provided that license is +# OSD-compliant; otherwise the following SPDX tag incorporates the +# MIT No Attribution license by reference. +# +# SPDX-FileCopyrightText: (C) Eric S. Raymond +# SPDX-License-Identifier: MIT-0 +# +# A newer version may be available at https://gitlab.com/esr/tapview +# Check your last commit dqte for this file against the commit list +# there to see if it might be a good idea to update. +# +diffopts=-u +while getopts bn opt +do + case $opt in + b) diffopts=-ub;; + *) echo "tapdiffer: unknown option ${opt}."; exit 1;; + esac +done +# shellcheck disable=SC2004 +shift $(($OPTIND - 1)) + legend=$1 checkfile=$2 trap 'rm /tmp/tapdiff$$' EXIT HUP INT QUIT TERM -if diff --text -u ${checkfile} - >/tmp/tapdiff$$ +if diff --text "${diffopts}" "${checkfile}" - >/tmp/tapdiff$$ then echo "ok - ${legend}" - exit 0 else - echo "not ok - ${checkfile}: ${legend}" + echo "not ok - ${legend}" if [ ! "${QUIET}" = 1 ] then echo " --- |" sed +# SPDX-License-Identifier: MIT-0 # -# This is version 1.6 # A newer version may be available at https://gitlab.com/esr/tapview +# Check your last commit date for this file against the commit list +# there to see if it might be a good idea to update. # OK="." FAIL="F" @@ -20,13 +20,16 @@ SKIP="s" TODO_NOT_OK="x" TODO_OK="u" +LF=' +' + ship_char() { # shellcheck disable=SC2039 printf '%s' "$1" # https://www.etalabs.net/sh_tricks.html } ship_line() { - report="${report}${1}\n" + report="${report}${1}$LF" } ship_error() { @@ -35,7 +38,7 @@ ship_error() { then echo "" fi - report="${report}${1}\n" + report="${report}${1}$LF" echo "${report}" exit 1 } @@ -77,6 +80,10 @@ context_pop () { then ship_line "Expected $(context_get expect) tests but only ${testcount} ran." status=1 + elif [ "$(context_get plan)" != "" ] && [ "$(context_get expect)" -lt "$(context_get count)" ] + then + ship_line "${testcount} ran but $(context_get expect) expected." + status=1 fi } diff --git a/tests/trident.chk b/tests/trident.chk index 37de9fe..c27ef42 100644 --- a/tests/trident.chk +++ b/tests/trident.chk @@ -1420,7 +1420,7 @@ Your lamp is now on. You're at "Y2". - +> You scored 167 out of a possible 430, using 231 turns. You have achieved the rating: "Experienced Adventurer". diff --git a/tests/trident.log b/tests/trident.log index 871834a..44c9fba 100644 --- a/tests/trident.log +++ b/tests/trident.log @@ -1,4 +1,6 @@ ## 161-point run to pirate appearance and death by dwarf +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56 n seed 1071883378 diff --git a/tests/troll_returns.chk b/tests/troll_returns.chk index b4d7c2e..6715126 100644 --- a/tests/troll_returns.chk +++ b/tests/troll_returns.chk @@ -748,7 +748,7 @@ There is a huge beanstalk growing out of the west pit up to the hole. You are at the bottom of the eastern pit in the Twopit Room. There is a small pool of oil in one corner of the pit. -> g oil +> get oil Your bottle is now full of oil. @@ -803,7 +803,7 @@ You're in Giant Room. There is a large nest here, full of golden eggs! -> g +> take OK @@ -912,7 +912,7 @@ mist. A notice posted on the bridge reads, "Stop! Pay troll!" A burly troll stands by the bridge and insists you throw him a treasure before you may cross. - +> You scored 75 out of a possible 430, using 140 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/troll_returns.log b/tests/troll_returns.log index 2b93a30..2806ade 100644 --- a/tests/troll_returns.log +++ b/tests/troll_returns.log @@ -1,4 +1,6 @@ ## See that troll returns if we stole his eggs before crossing +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in @@ -119,7 +121,7 @@ water plant u e d -g oil +get oil u w d @@ -128,7 +130,7 @@ w n oil door s -g +take n n w diff --git a/tests/turnpenalties.log b/tests/turnpenalties.log index 2cd3c9e..d44ecfc 100644 --- a/tests/turnpenalties.log +++ b/tests/turnpenalties.log @@ -1,4 +1,6 @@ ## check that the turn count penalties occur +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1951269982 in diff --git a/tests/urntest.chk b/tests/urntest.chk index 0ca8748..8724f9d 100644 --- a/tests/urntest.chk +++ b/tests/urntest.chk @@ -2105,7 +2105,7 @@ A small urn full of oil is embedded in the rock. If you mean to use the Persian rug, it does not appear inclined to cooperate. - +> You scored 265 out of a possible 430, using 358 turns. You have reached "Junior Master" status. diff --git a/tests/urntest.log b/tests/urntest.log index a02d663..8fffb2f 100644 --- a/tests/urntest.log +++ b/tests/urntest.log @@ -1,4 +1,6 @@ ## Test verbs on urn +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in diff --git a/tests/urntest2.chk b/tests/urntest2.chk index 25de76b..8f8e9cc 100644 --- a/tests/urntest2.chk +++ b/tests/urntest2.chk @@ -2030,7 +2030,7 @@ There is nothing here with which to fill it. The urn is now dark. - +> You scored 267 out of a possible 430, using 344 turns. You have reached "Junior Master" status. diff --git a/tests/urntest2.log b/tests/urntest2.log index e0c3a6b..22c0d9b 100644 --- a/tests/urntest2.log +++ b/tests/urntest2.log @@ -1,4 +1,6 @@ ## Test filling urn when you have no bottle +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Also, try to lock door after oiling it n seed 1838473132 diff --git a/tests/urntest3.chk b/tests/urntest3.chk index 1c95ae3..34c30e7 100644 --- a/tests/urntest3.chk +++ b/tests/urntest3.chk @@ -2015,7 +2015,7 @@ You are wandering aimlessly through the forest. There is nothing here with which to fill the bottle. - +> You scored 267 out of a possible 430, using 340 turns. You have reached "Junior Master" status. diff --git a/tests/urntest3.log b/tests/urntest3.log index a835588..fcbdf5e 100644 --- a/tests/urntest3.log +++ b/tests/urntest3.log @@ -1,4 +1,6 @@ ## Test filling urn twice. Also, try to lock door after oiling it. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in diff --git a/tests/vending.chk b/tests/vending.chk index 6e67b28..4f837bc 100644 --- a/tests/vending.chk +++ b/tests/vending.chk @@ -305,7 +305,7 @@ Attack what? The vending machine swings back to block the passage. - +> You scored 75 out of a possible 430, using 56 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/vending.log b/tests/vending.log index 883a3c7..3f4a469 100644 --- a/tests/vending.log +++ b/tests/vending.log @@ -1,4 +1,6 @@ ## Get batteries from the vending machine +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1635997320 in diff --git a/tests/wakedwarves.log b/tests/wakedwarves.log index d733fb9..4aab61a 100644 --- a/tests/wakedwarves.log +++ b/tests/wakedwarves.log @@ -1,4 +1,6 @@ ## Wake the dwarves and die. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in diff --git a/tests/wakedwarves2.log b/tests/wakedwarves2.log index adf737e..8237af1 100644 --- a/tests/wakedwarves2.log +++ b/tests/wakedwarves2.log @@ -1,4 +1,6 @@ ## Wake the dwarves by waving rod and die. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in diff --git a/tests/wakedwarves3.log b/tests/wakedwarves3.log index e04669b..1d86845 100644 --- a/tests/wakedwarves3.log +++ b/tests/wakedwarves3.log @@ -1,4 +1,6 @@ ## Wake the dwarves by attacking one and die. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1838473132 in diff --git a/tests/water_plant2.chk b/tests/water_plant2.chk index 1cbf119..9ec91c8 100644 --- a/tests/water_plant2.chk +++ b/tests/water_plant2.chk @@ -392,14 +392,6 @@ You are in the south side chamber. There is precious jewelry here! -> - -I don't know how to apply that word here. - -You are in the south side chamber. - -There is precious jewelry here! - > s There is no way to go that direction. @@ -448,8 +440,6 @@ There are bars of silver here! You're at "Y2". -A hollow voice says "PLUGH". - There is a little axe here. > d @@ -556,8 +546,6 @@ Your bottle is now full of water. > s -There is a threatening little dwarf in the room with you! - You are in a north/south canyon about 25 feet across. The floor is covered by white mist seeping in from the north. The walls extend upward for well over 100 feet. Suspended from some unseen point far @@ -568,7 +556,27 @@ small window can be seen in either wall, some fifty feet up. > s -A little dwarf with a big knife blocks your way. +There is a threatening little dwarf in the room with you! + +One sharp nasty knife is thrown at you! + +It misses! + +You are in a secret n/s canyon above a large room. + +> s + +There is a threatening little dwarf in the room with you! + +You are in a secret canyon which exits to the north and east. + +There is a Persian rug spread out on the floor! + +The blood-specked body of a huge green dead dragon lies to one side. + +> d + +There is no way to go that direction. There is a threatening little dwarf in the room with you! @@ -576,34 +584,34 @@ One sharp nasty knife is thrown at you! It misses! -You're in Mirror Canyon. +You are in a secret canyon which exits to the north and east. + +There is a Persian rug spread out on the floor! + +The blood-specked body of a huge green dead dragon lies to one side. > s -There is a threatening little dwarf in the room with you! - -You are in a secret n/s canyon above a large room. - -> d +There is no way to go that direction. There is a threatening little dwarf in the room with you! -You're in Slab Room. +One sharp nasty knife is thrown at you! -> s +It misses! -There is a threatening little dwarf in the room with you! +You are in a secret canyon which exits to the north and east. -You're at west end of Twopit Room. +There is a Persian rug spread out on the floor! -The top of a 12-foot-tall beanstalk is poking out of the west pit. +The blood-specked body of a huge green dead dragon lies to one side. > water plant What do you want to do with the water? - -You scored 65 out of a possible 430, using 85 turns. +> +You scored 65 out of a possible 430, using 84 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/water_plant2.log b/tests/water_plant2.log index 0136f1e..44aa76e 100644 --- a/tests/water_plant2.log +++ b/tests/water_plant2.log @@ -1,4 +1,7 @@ ## Check that pour correctly switches among plant states (fuzzed) +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE Behavior differs due to a parser bug fix. n seed 183847312 in @@ -56,7 +59,6 @@ s s u w - s w s diff --git a/tests/weirdbird.chk b/tests/weirdbird.chk index 52f5eb7..c583787 100644 --- a/tests/weirdbird.chk +++ b/tests/weirdbird.chk @@ -218,7 +218,7 @@ You're outside grate. The grate is open. - +> You scored 30 out of a possible 430, using 37 turns. You are obviously a rank amateur. Better luck next time. diff --git a/tests/weirdbird.log b/tests/weirdbird.log index 78ae7de..b0f9f79 100644 --- a/tests/weirdbird.log +++ b/tests/weirdbird.log @@ -1,4 +1,6 @@ ## Do pointless things to the bird to test odd cases. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 694608006 in @@ -36,7 +38,7 @@ take bird get rod wave rod attack bird -# Also, test grare as a motion verb. +# Also, test grate as a motion verb. grate up n diff --git a/tests/weirddwarf.chk b/tests/weirddwarf.chk index 1d73e0e..0216bc5 100644 --- a/tests/weirddwarf.chk +++ b/tests/weirddwarf.chk @@ -514,7 +514,7 @@ With what? Your bare hands? Feed what? - +> You scored 103 out of a possible 430, using 88 turns. Your score qualifies you as a novice class adventurer. diff --git a/tests/weirddwarf.log b/tests/weirddwarf.log index b7af3a0..613b5f9 100644 --- a/tests/weirddwarf.log +++ b/tests/weirddwarf.log @@ -1,4 +1,6 @@ ## Exercise various verbs on a dwarf +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause n seed 1071883378 in diff --git a/tests/win430.chk b/tests/win430.chk index a8e15a6..0a7e9c8 100644 --- a/tests/win430.chk +++ b/tests/win430.chk @@ -2099,10 +2099,10 @@ friendly elves carry the conquering adventurer off into the sunset. You scored 430 out of a possible 430, using 349 turns. -'Adventuredom stands in awe -- you have now joined the ranks of the +Adventuredom stands in awe -- you have now joined the ranks of the W O R L D C H A M P I O N A D V E N T U R E R S ! It may interest you to know that the Dungeon-Master himself has, to -my knowledge, never achieved this threshold in fewer than 330 turns.' +my knowledge, never achieved this threshold in fewer than 330 turns. To achieve the next higher rating would be a neat trick! Congratulations!! diff --git a/tests/win430.log b/tests/win430.log index c989c53..29e6564 100644 --- a/tests/win430.log +++ b/tests/win430.log @@ -1,4 +1,6 @@ ## Ryan Sarson's 430-point win. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause no seed 1318612053 e diff --git a/tests/wittsend.chk b/tests/wittsend.chk index dcd9bd4..b92cf2f 100644 --- a/tests/wittsend.chk +++ b/tests/wittsend.chk @@ -2628,7 +2628,7 @@ It is now pitch dark. If you proceed you will likely fall into a pit. You're in Plover Room. - +> You scored 342 out of a possible 430, using 423 turns. Your score puts you in Master Adventurer Class C. diff --git a/tests/wittsend.log b/tests/wittsend.log index 580f7c8..6c3a223 100644 --- a/tests/wittsend.log +++ b/tests/wittsend.log @@ -1,4 +1,6 @@ ## 342-point run to Witt's End and plover room. +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause # Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56 n seed 1635997320 diff --git a/tests/woodshint.chk b/tests/woodshint.chk index c26d471..7ea7cdd 100644 --- a/tests/woodshint.chk +++ b/tests/woodshint.chk @@ -314,7 +314,7 @@ You are wandering aimlessly through the forest. You're at end of road. - +> You scored 30 out of a possible 430, using 45 turns. You are obviously a rank amateur. Better luck next time. diff --git a/tests/woodshint.log b/tests/woodshint.log index 6db609a..acf2f61 100644 --- a/tests/woodshint.log +++ b/tests/woodshint.log @@ -1,4 +1,7 @@ ## Test hinting logic - elicit forest hint +# SPDX-FileCopyrightText: Copyright Eric S. Raymond +# SPDX-License-Identifier: BSD-2-Clause +#NOCOMPARE Behavior differs due to a parser bug fix. # Also some tests of intransitive-verb cases n seed 2099333241 @@ -63,4 +66,4 @@ n n n w -n \ No newline at end of file +n