Compare commits

...

213 commits
1.13 ... master

Author SHA1 Message Date
Eric S. Raymond
7d848c89e1 Add spell-checking to the regression tests. 2025-05-25 11:06:46 -04:00
Eric S. Raymond
7bbf994fce Spellcheck the manual page a well. No errors. 2025-05-25 11:03:34 -04:00
Eric S. Raymond
3e989aec53 Spellcheck the YAML - yielded one trivial fix. 2025-05-25 10:57:34 -04:00
Eric S. Raymond
9df69fe034 Ready to ship 1.20. 2024-09-23 18:48:11 -04:00
Eric S. Raymond
a2bb39dc7e Eliminate a confusing dummy argument in a macro. 2024-09-23 14:22:25 -04:00
Eric S. Raymond
5c90880f0a Comment and macro cleanup. 2024-09-23 06:06:32 -04:00
Eric S. Raymond
92451f1fff Eliminate thew last property inequality outside a macro. 2024-09-23 05:08:11 -04:00
Eric S. Raymond
96ad6c6245 Reflow. 2024-09-23 04:38:24 -04:00
Eric S. Raymond
40742e112b Expand a macro to simplify code. 2024-09-23 04:26:59 -04:00
Eric S. Raymond
a4fd14caf7 Back away from trying for the found member until we make stashed work. 2024-09-23 04:26:59 -04:00
Eric S. Raymond
9a6e4406f5 Confine uses of PROP_STASHIFY() to advent.h
Now it shouyld be possible to manipulate a stashed flag by only
changing macros.
2024-09-23 04:26:59 -04:00
Eric S. Raymond
08f0351817 Introduce OBJECT_STASHIFY. 2024-09-23 04:26:59 -04:00
Eric S. Raymond
0157e58668 Remove an unneeded layer of macro indirection. 2024-09-23 03:35:52 -04:00
Eric S. Raymond
354e56a69b Clean up some comments. 2024-09-23 03:35:52 -04:00
Eric S. Raymond
9c3f4b0c90 Reflow. 2024-09-22 13:08:31 -04:00
Eric S. Raymond
20fd7c589f Typo fix. 2024-09-20 22:19:14 -04:00
Eric S. Raymond
8c553af53e Avoid a GNUism, POSIX strncasecmp() is declarted in strings.h. 2024-09-20 22:05:25 -04:00
Eric S. Raymond
f1cb740c41 Define TRUNCLEN and explain its issues. 2024-09-20 22:04:16 -04:00
Eric S. Raymond
cf4adf8d02 Repair truncation in oldstyle mode.
Sure would be nice to remember while this code had TOKEN + TOKEN
where one would think it should just say TOKEN.
2024-09-20 10:49:44 -04:00
Eric S. Raymond
acdfa96315 Fix a busted comment. 2024-09-20 10:40:22 -04:00
Eric S. Raymond
3d6c689ffa Make oldstyle correctly suppress line editing. 2024-09-20 10:29:37 -04:00
Eric S. Raymond
8dcc6e6641 Correct missing negative. 2024-09-20 10:03:15 -04:00
Eric S. Raymond
e17ff128da Remove obsolete comment part. 2024-06-30 17:20:46 -04:00
Eric S. Raymond
cb293f4aa4 Rename some macos for clarity. 2024-06-30 14:48:44 -04:00
Eric S. Raymond
124e7768b4 Cease relying on C storage starting zeroed. 2024-06-27 19:50:56 -04:00
Eric S. Raymond
63e8579f74 Ready to shp 1.19. 2024-06-27 13:39:27 -04:00
Eric S. Raymond
1080b45d39 Comment typo fix. 2024-06-27 13:29:28 -04:00
Eric S. Raymond
86fe4bd121 Verify that tesrts still match advent430 where expected. 2024-06-27 13:04:33 -04:00
Eric S. Raymond
bd499dc532 Fix GitLab issue #69: repeated knive caveat message 2024-06-25 16:42:14 -04:00
Eric S. Raymond
ae6eced72d Incorporate Ryan Sarson's test for correct knife message. 2024-06-25 13:08:50 -04:00
Eric S. Raymond
7903ac1bb8 More validation, with -Wall and -Wextra. 2024-06-03 21:12:41 -04:00
Eric S. Raymond
b51612131d Typo fix. 2024-04-29 11:43:41 -04:00
Eric S. Raymond
62cd0c78da Reissue 1.18 - same code, corrected metadata. 2024-02-15 12:56:01 -05:00
Eric S. Raymond
bad34acf1e Improve the project summary. 2024-02-05 08:07:31 -05:00
Eric S. Raymond
0fafbe379f Ubuntu-discuss doesn't want to see release notifications. 2024-02-05 07:51:00 -05:00
Eric S. Raymond
fcf6935689 Ready to ship 1.18. 2024-02-05 07:49:07 -05:00
Eric S. Raymond
b610a62685 Documentation polishing. 2024-02-05 07:43:23 -05:00
Eric S. Raymond
6174129116 Add a detail to the installation instructiions. 2024-02-04 16:56:20 -05:00
Eric S. Raymond
a2fa136988 Remove unused production. 2024-02-04 16:16:05 -05:00
Eric S. Raymond
43a08621e4 asciidoc -> asciidoctor. 2024-02-04 15:52:03 -05:00
Eric S. Raymond
f24fcd2971 Perform full code validation on every make check. 2024-02-04 12:48:33 -05:00
Eric S. Raymond
580f409ee6 At this revision, make cppcheck runs clean. 2024-02-04 11:44:04 -05:00
Eric S. Raymond
5b917084b0 Minor repair of savefile validation code. 2024-02-04 11:43:36 -05:00
Eric S. Raymond
1ef39055f3 Make reflow run black. 2024-02-04 10:07:58 -05:00
Eric S. Raymond
0175344caa 1TBS reflow, the bracening. 2024-01-29 12:14:56 -05:00
Eric S. Raymond
be429016af 1TBS reflow with clang-format. 2024-01-28 08:09:39 -05:00
Eric S. Raymond
c11938aed5 Place1TBS mandatory braces. 2024-01-28 07:14:46 -05:00
Eric S. Raymond
258b7703f2 Simplify SPDX copyright lines to the shortest canonical form...
...because if we leave them longer than 80 chars, reflow is going to
mess them up.
2024-01-28 07:14:46 -05:00
Eric S. Raymond
83c6a66887 Typo fix. 2024-01-15 05:10:46 -05:00
Eric S. Raymond
3c09c25cc3 Update tapview and tapdiffer. 2024-01-15 05:03:53 -05:00
Eric S. Raymond
f26514b5dd Fix SPDX headers. 2024-01-15 04:58:21 -05:00
Eric S. Raymond
cf7ea0cd89 Remove debugging debris. 2024-01-15 04:57:47 -05:00
Eric S. Raymond
2db3bed3f1 Fix up copyright notices. SPDX wants only one per file. 2024-01-03 06:30:40 -05:00
Eric S. Raymond
a6ec8a9595 Simplify some dependencies. 2024-01-03 06:15:04 -05:00
Eric S. Raymond
75bc031b46 Reduce include complexity. 2024-01-03 05:55:57 -05:00
Eric S. Raymond
dd65960ec5 Ready to ship 1.17. 2024-01-02 21:58:14 -05:00
Eric S. Raymond
90f96a25da pylint cleanup. 2024-01-02 21:53:23 -05:00
Eric S. Raymond
c10e9694e3 Corrected fix, and test, for #67. 2024-01-02 17:19:42 -05:00
Eric S. Raymond
11d966f349 Add -a option to synopsis. 2024-01-02 16:57:15 -05:00
Eric S. Raymond
e1a17fd442 NEWS update. 2024-01-02 16:38:59 -05:00
Eric S. Raymond
eb63994931 Better explanation of backwards compatibility with 4.30. 2024-01-02 16:34:54 -05:00
Eric S. Raymond
0b82afa5c1 Commit a test script. 2024-01-02 16:03:30 -05:00
Eric S. Raymond
b247359d7f Initialize dwarf locations in YAML, not C.
Minimizes magic numbers.
2024-01-02 12:12:32 -05:00
Eric S. Raymond
1f9f39d789 Address GitLab issue #67: saying Z'ZZZ at the reservoir causes the water to part/crash together 2024-01-02 10:42:11 -05:00
Eric S. Raymond
2582e240bd Fix defective copyright notices. 2024-01-02 09:46:09 -05:00
Eric S. Raymond
9fbd603f8d Typo fix. 2024-01-02 09:28:39 -05:00
Nicola Smaniotto
5016978156
saveresume.c: add missing semicolon
Fixes compilation when ADVENT_NOSAVE is set
2023-11-27 17:41:26 +01:00
Eric S. Raymond
7d4dd78679 Apply black to move Python style to standard form. 2023-09-17 16:17:30 -04:00
Eric S. Raymond
ab4653b89c Comment polishing. 2023-05-10 20:08:10 -04:00
Torbjörn Andersson
b28eb66868 Spelling fixes. 2023-04-18 18:44:08 +02:00
Eric S. Raymond
f0119f8431 Comment rtpo fix. 2023-04-18 08:02:14 -04:00
Eric S. Raymond
5075c63cf4 Add some map tags. 2023-04-18 07:51:55 -04:00
Eric S. Raymond
ebf3e389c0 Add exolanatory comment. 2023-04-17 09:19:26 -04:00
Eric S. Raymond
6cef68cda5 Use fixed-lwength types to make the save format more portable 2023-04-17 07:24:51 -04:00
Eric S. Raymond
234da6b468 Remove unnecessary code duplication. 2023-04-17 07:06:56 -04:00
Eric S. Raymond
adbb5c1204 Ready to ship 1.16. 2023-04-15 14:44:20 -04:00
Eric S. Raymond
869c53d1b1 Comment polishing. 2023-04-15 10:00:23 -04:00
Eric S. Raymond
ef78c36ddb Improve state-macro encapsulation. 2023-04-15 09:29:28 -04:00
Eric S. Raymond
5d3205e1e9 Add explanatory comments. 2023-04-15 06:29:59 -04:00
Eric S. Raymond
a6b41fdb8f Simplify initialization code. All tests pass, 100% coverage. 2023-04-14 08:24:51 -04:00
Eric S. Raymond
9ff036d789 Make build with conditional compilation easier. 2023-04-14 06:16:29 -04:00
Eric S. Raymond
88feaab0c0 Remove a remnant line number. 2023-04-10 19:29:11 -04:00
Eric S. Raymond
4d4e8dce96 Magic-number elimination. 2023-04-10 19:27:07 -04:00
Eric S. Raymond
82c3ae5e65 Condition in alternative state-management macros.
These do not entirely work yet. #define FOUNDBOOL to enable them.

All tests pass, 100% coverage.
2023-04-10 19:26:18 -04:00
Eric S. Raymond
1af01ff91f Confine use of STATE_NOTFOUND to macros. 2023-04-08 23:04:34 -04:00
Eric S. Raymond
9e9731d59b Turn an odd test into a macro disjunction.
Was game.objects[i].prop > STATE_NOTFOUND.

This finishes off all the statec comparisons we want to macroize.
2023-04-08 21:17:55 -04:00
Eric S. Raymond
191d3cb043 More nontrivial macro abstraction of state checks.
game.prop < 0 becomes an alternation of macros.
2023-04-08 20:32:04 -04:00
Eric S. Raymond
c82d1214fa First nontrivial replacement of object state test by macro.
Test was game.objects[i].prop >= 0, but we know that state -1
(STATE_NOTFOUND) can't occiur here because the object is in
a lit room.
2023-04-08 20:02:27 -04:00
Eric S. Raymond
02987d0330 Encapsulate object-state state tests and setttings in macros.
This isn't a complete refwctoring, just the part than can be done with
transparetly correct capture of inlinre logic into macros.

No logic changes. Tests pass, 100% coverage.
2023-04-08 19:35:41 -04:00
Eric S. Raymond
9cd7c53d78 Refactor put() function and calls. 2023-04-07 13:19:51 -04:00
Eric S. Raymond
ab2779cd93 Remove unneeded use of put(). 2023-04-07 13:09:57 -04:00
Eric S. Raymond
819aed5c4e Add test for behavior of stashed objects in endgame. 2023-04-07 12:33:52 -04:00
Eric S. Raymond
8d4d64fafb Structurization of hints. 2023-04-07 10:52:16 -04:00
Eric S. Raymond
eebc87f889 Structurize object info. 2023-04-07 10:35:13 -04:00
Eric S. Raymond
8fe07c8bf3 Structurize locatiuon info. 2023-04-07 09:30:33 -04:00
Eric S. Raymond
ff9c73a37d Structurize the informaruin about dwarves. 2023-04-07 09:15:20 -04:00
Eric S. Raymond
195e6e149b Apply loc_t type. 2023-04-07 08:18:50 -04:00
Eric S. Raymond
4369284c75 Boolification. 2023-04-07 07:35:28 -04:00
Torbjörn Andersson
7e21108e95 Spelling fixes. 2023-04-07 10:50:01 +02:00
Eric S. Raymond
829c13d1d5 Fix some screwy formatting of conditionals...
...probably by the original FOTRAN-to-C translator.
2023-04-06 21:29:39 -04:00
Eric S. Raymond
2f60504e07 Comment typo fixes. 2023-04-06 21:01:48 -04:00
Eric S. Raymond
5de3b8ff70 In notes.adoc, update the new-features list and credits. 2023-04-06 20:07:25 -04:00
Eric S. Raymond
520d365f74 Resume now detects if a save has incompatible endianness. 2023-04-06 19:41:22 -04:00
Eric S. Raymond
4b08b726f9 Simplify the signature of savefile(). 2023-04-06 19:26:04 -04:00
Eric S. Raymond
58cf204eba Implement the magic-cookie check and its test. 2023-04-06 19:03:02 -04:00
Eric S. Raymond
d16822a583 Give savefiles an identifting header. SAve format version gets bumped. 2023-04-06 17:54:02 -04:00
Eric S. Raymond
3640e5cb96 Refactoring step - change some visibilities. 2023-04-06 17:44:11 -04:00
Eric S. Raymond
e99cc69de0 Quiet make test down. 2023-04-06 17:28:43 -04:00
Eric S. Raymond
e8991f69e9 Simplify and speed up testing. 2023-04-06 16:38:21 -04:00
Eric S. Raymond
3e72486724 Comment typo fix 2023-04-06 12:13:38 -04:00
Eric S. Raymond
f5ff25f52a Magic-number elimination. 2023-04-06 10:03:24 -04:00
Eric S. Raymond
e1a528a4c5 Move NEWS file to asciidoc. 2023-04-05 21:48:02 -04:00
Eric S. Raymond
9726d8207c Add some SPDX headers. 2023-04-05 21:36:52 -04:00
Eric S. Raymond
fe378b9e13 Improved behavior when magic words are said before they're seen.
Note: there is a tiny risk that this could break savefile
compatibility, as I swiped an unused int member in the game structure
and turned it into a bool. int and bool *should* be the same sixe...
2023-04-05 19:49:38 -04:00
Eric S. Raymond
4ce4de190e Magic number removal. 2023-04-05 18:02:47 -04:00
Eric S. Raymond
9b53140f1c GO_* enums no longer have an order constraint. 2023-04-05 17:48:26 -04:00
Eric S. Raymond
3c6648882d How to set up prerequisites for oldcompare 2023-04-04 19:14:45 -04:00
Eric S. Raymond
00bdc2133a Improve TAP messages slightly. 2023-04-04 09:53:10 -04:00
Eric S. Raymond
20e1b9d930 Update tapdiffer version. 2023-04-04 09:32:18 -04:00
Eric S. Raymond
1ce4fc2ac3 Improve copyright lines. 2023-04-03 17:05:01 -04:00
Eric S. Raymond
642b7e4890 Ready to ship 1.15. 2023-04-03 11:36:39 -04:00
Eric S. Raymond
ec1d99f354 Commenting fix. 2023-04-03 11:36:39 -04:00
Eric S. Raymond
6ebc2bf0e3 Documentation fix. 2023-04-03 11:25:11 -04:00
Eric S. Raymond
25230068fe Partial REUSE cmpliance. 2023-04-03 11:25:11 -04:00
Eric S. Raymond
71abcb4e65 Add explanatory comment. 2023-04-03 09:42:55 -04:00
Eric S. Raymond
2dd1ccc535 Fix for Gitlab issue #65: revisiting "fee fie foe foo" and "fum".
100% coverage, "make oldcompare" passes.
2023-04-03 09:24:21 -04:00
Eric S. Raymond
0a1f5dbb43 Nothing but FOO can start a magic-word sequence,
100% coverage ans oldcompare passes.
2023-04-03 07:14:11 -04:00
Eric S. Raymond
20cb8b9ffb Back out test lines that break compatibility with advent430.
Coverage is still 100% even withthese lines commebted out.
2023-04-02 18:28:45 -04:00
Eric S. Raymond
b86d4afd20 Back out the behavior change to magic words outside the Giant's Room.
There are twi reasons for this. One is to decompolicate verifying
the fix for issue $85: revisiting "fee fie foe foo" and "fum". The
other is because I'm now convinced that the test should notr be
"Are you in the Giant's Room" but :Have you read the grafitti there?
2023-04-02 17:53:09 -04:00
Eric S. Raymond
d9ddf4d805 Consolidate all magicwords tests into one file. 100% coverage. 2023-04-02 17:45:04 -04:00
Eric S. Raymond
9c6219a27d The oldcompare production no longer requires savefiles. 2023-04-02 11:54:37 -04:00
Eric S. Raymond
34516ecf39 Test name change, 2023-04-02 10:42:10 -04:00
Eric S. Raymond
7dc3482c5b Start building a better test load for micwords. 2023-04-02 10:23:07 -04:00
Eric S. Raymond
40a4acb868 Comment typo fixes. 2023-04-02 07:41:49 -04:00
Torbjörn Andersson
54927c33e5 Spelling fixes 2023-04-02 07:32:24 +02:00
Eric S. Raymond
e49fb5f367 Close out oldcompare.
At this revision all test logs either pass checkfile comparison
against advent439 or have a NOCOMPARE comment explaining why they don't
pass, turning into a TAP skip. 100% coverage.
2023-04-01 18:37:08 -04:00
Eric S. Raymond
59a5afb72e Improve advent430 comparisons. 2023-04-01 16:58:52 -04:00
Eric S. Raymond
f95442b310 Test comment typo fixes. 2023-04-01 11:34:46 -04:00
Eric S. Raymond
97a69d8cbd Merge two NOCOMPARE tests. 2023-04-01 11:01:28 -04:00
Eric S. Raymond
0d2332573b Tweak a fuzzed test fotr advent430 compability. 100% coverage. 2023-04-01 10:47:49 -04:00
Eric S. Raymond
7af8492169 Add a warning comment. 2023-04-01 08:46:44 -04:00
Eric S. Raymond
3af590e972 Trim the illformed test. Still 100% coverage. 2023-03-30 20:09:10 -04:00
Eric S. Raymond
2c8aa9668c Add explanatory comments to two tests. 2023-03-30 08:46:35 -04:00
Eric S. Raymond
30a98cc916 Factor out the test of the bare word "seed". 2023-03-30 01:05:32 -04:00
Eric S. Raymond
872b94f927 Factor out most of illformed that doesn't cause advent430 problems. 2023-03-30 00:05:13 -04:00
Eric S. Raymond
74c3158f42 Add more NOCOMPARE exclusions due to version skew. 2023-03-29 22:05:27 -04:00
Eric S. Raymond
ef180a0731 Truncatte bigfail test because the RNG gets confused by resurrection. 2023-03-29 18:25:35 -04:00
Eric S. Raymond
77ac2f1570 Coverage exclusions for -d. 2023-03-29 18:16:43 -04:00
Eric S. Raymond
ff46cf7fac Add -d option 2023-03-29 16:04:36 -04:00
Eric S. Raymond
c80162b467 More test cleanup to converge with advent430 branch. 2023-03-29 14:40:38 -04:00
Eric S. Raymond
65f221e03f Add a more carefully factored test...
...so we can exclude the part advent430 won't pass.
2023-03-29 10:59:42 -04:00
Eric S. Raymond
cbb5572b21 Clean up some tests, adding newlines at EOF where they're missing.
The advent430 command interpreter accidentally accepted input lines with no
trailing newline. Thus, these files produced spurious mismatches.
2023-03-29 10:55:56 -04:00
Eric S. Raymond
a5eeb0b2bc Add a useful warning to the tests makefile 2023-03-29 09:36:58 -04:00
Eric S. Raymond
4eccfa127c Document a bugfix and isolate it in a test. 2023-03-29 09:24:12 -04:00
Eric S. Raymond
dc8b19bcdc Implement NOCOMPARE magic. 2023-03-23 14:58:06 -04:00
Eric S. Raymond
2070db3a2a Test simplification - we don't care about the difference in processing blank lines. 2023-03-22 21:20:24 -04:00
Eric S. Raymond
e3fbac804b Reconciliation with advent430. 2023-03-22 20:39:36 -04:00
Eric S. Raymond
5f7ce87040 Change prompt generation to be more compatible with the advent430 branch. 2023-03-22 17:09:14 -04:00
Eric S. Raymond
1e05134b47 Minor fix to prompt generation. 2023-03-22 17:03:50 -04:00
Eric S. Raymond
1ffb81b70b YAML markup fix. 2023-03-22 14:48:49 -04:00
Eric S. Raymond
fefd2a19fb Remove redundant test. 2023-03-22 14:48:49 -04:00
Eric S. Raymond
4fa3d4d758 Test cleanup and reconvilation with advent430. 2023-03-22 14:48:49 -04:00
Eric S. Raymond
07a0f066ba Quoting fixes in the YAML. 2023-03-22 00:05:58 -04:00
Eric S. Raymond
a006bdd272 TAPify the ancient-diffs report. 2023-03-22 00:05:58 -04:00
Eric S. Raymond
327efd3678 Test cleanup. 2023-03-22 00:05:58 -04:00
Eric S. Raymond
b044e9ab42 Move in the direction of being able to do ancient regression tests. 2023-03-21 17:27:30 -04:00
Eric S. Raymond
8fd3eb8b92 Testing simplification. 2023-03-16 10:18:56 -04:00
Eric S. Raymond
9fdbe73315 Revert "Tapify output of cheat."
There was a better way.
2023-03-16 10:15:47 -04:00
Eric S. Raymond
9758883ea2 More TAPification. 2023-03-16 09:48:16 -04:00
Eric S. Raymond
f5d15ab1f2 Tapify output of cheat. 2023-03-16 08:32:59 -04:00
Eric S. Raymond
f07b3ba2d4 Address GitLab issue #66: Missing couple of ; in saveresume.c 2023-03-16 07:43:17 -04:00
Eric S. Raymond
87855f8124 Avoid a compiler warning. 2023-03-14 14:58:26 -04:00
Eric S. Raymond
786832210e Magic-number elimination. 2023-03-14 14:58:26 -04:00
Eric S. Raymond
9b89dd2829 Experimental test production. 2023-03-14 14:58:26 -04:00
Eric S. Raymond
17840d0e15 Comment polishing. 2023-03-12 17:59:10 -04:00
Eric S. Raymond
5929a68b88 Real test coverage dor SAVE_TAMPERING. 2023-03-12 17:37:58 -04:00
Eric S. Raymond
43af075fa9 Cleann up scratchfile after tesrs. 2023-03-12 17:21:15 -04:00
Eric S. Raymond
47c3d14a11 Comment polishing. 2023-03-12 17:14:18 -04:00
Eric S. Raymond
db8ca5eb26 Documentation polishing. 2023-03-12 12:58:11 -04:00
Eric S. Raymond
ca5b6975dc NEWS update. 2023-03-12 12:41:13 -04:00
Eric S. Raymond
282842c4a9 Whitespace trimming of save and resume names. 2023-03-12 12:10:30 -04:00
Eric S. Raymond
7723f3fc1a Add coverage exception now that SAVE_TAMPERING is back. 2023-03-12 10:11:52 -04:00
Eric S. Raymond
0ffb297801 Relax the savefile validity check a little.
There was a very old bug, probably predating the OpenAdventure port,
that would poke a stashed value of -1 into the snake object if you did
a save in endgame, and then fail the savefile validation on resume.
This was masked for a long time by a bug in put() just fixed a couple
of revisions ago.
2023-03-12 10:03:43 -04:00
Eric S. Raymond
04df0ce64c Add message and bailout on invalid save.
Note: save/resume still fails at saveresume.4
at this revision.
2023-03-12 06:39:57 -04:00
Eric S. Raymond
569a39aa7c Drop an attempt to pacify cppcheck that's no longer needed. 2023-03-11 10:58:38 -05:00
Eric S. Raymond
1652df4540 Add ANSI prototype, 2023-03-11 10:40:01 -05:00
Eric S. Raymond
ba3248224e Add C coverage exclsion required by unbreaking the property setter. 2023-03-11 10:29:38 -05:00
Eric S. Raymond
b7bf85904d Fix a formal-type glitch. 2023-03-11 09:44:51 -05:00
Eric S. Raymond
71f05c4567 Bugfix: bird starts caged in endgame.
Turns out the code of put() had been modified ibcorrectly, but
the bug was masked in all but a few cases, noe of which would be
encountered in normal gameplay.

A test to ensure this bug does noit recur has been added.
2023-03-11 09:35:26 -05:00
Eric S. Raymond
fe8a82927c Document a bugfix. 2023-03-10 17:17:38 -05:00
Eric S. Raymond
b125fe7b2a Fix interrupted-magic-words cosmetic bug.
C patch by Torbjörn Andersson.
2023-03-10 16:43:27 -05:00
Eric S. Raymond
3971a61ab0 Ready to ship 1.14. 2023-03-09 10:19:53 -05:00
Eric S. Raymond
8f527fb433 Fix whitespace glitch. 2023-03-09 10:19:53 -05:00
Eric S. Raymond
643eab4e9c Use $(advent) rather than advent where needed. 2023-03-09 08:54:20 -05:00
Eric S. Raymond
f53476f826 Complete fix of GitLab issue #64: Coverage summary looks wrong 2023-03-08 22:55:12 -05:00
Eric S. Raymond
83c32598be Partially address Gitlsb issue #64: Coverage summary looks wrong 2023-03-08 21:54:45 -05:00
Torbjörn Andersson
98b95e92ee Another typo fix. 2023-03-08 18:15:51 +01:00
Eric S. Raymond
8d409c6b3b Add a comment to the code that fixes the inventory-count bug. 2023-03-07 08:12:36 -05:00
Eric S. Raymond
e1ce7d6b6a Documentation polishing. 2023-03-07 06:27:08 -05:00
Eric S. Raymond
f911e42453 Eliminate some forwards. 2023-03-06 23:41:07 -05:00
Eric S. Raymond
12d39ef72b Improve Makefile recipe for coverage testing. 2023-03-06 12:55:17 -05:00
Eric S. Raymond
1efd1027f7 Add test for interspersed non-motion command amidst magic words. 2023-03-04 20:26:13 -05:00
Eric S. Raymond
1121bb8aa5 NEWS and notes.adoc update. 2023-03-04 20:16:07 -05:00
Eric S. Raymond
a01c08385c Prevent a spurious coverage error. 2023-03-04 19:58:57 -05:00
Eric S. Raymond
643656fcc3 Complain to user on save/resume.restore when it's disabled. 2023-03-04 19:48:17 -05:00
Eric S. Raymond
ac0c5fc024 Document -a option. 2023-03-03 17:18:00 -05:00
Rob Swindell (on Debian Linux)
dfff80faa8 Add optional auto-save/restore feature using -a <filename> option
To enable use with online Bulletin Board Systems (BBSes) where users
may be disconnected unexpectedly, but would naturally want to resume
playing their same game, added support for an optional save game
path/filename to be specified on the command-line (very similar to
"-r <filename>"), except this save/restore file is:
1. automatically loaded/restored if it exists
2. automatically created when starting a new game
3. automatically updated when exiting a game for any reason
4. cannot be changed to a different path/filename by the user

Since a BBS server program can be expected to send a SIGHUP or SIGTERM
to the game process upon user disconnection (or timeout), those
signals are caught and a graceful termination will occur which saves
the current game state.

Build with ADVENT_AUTOSAVE defined to enable this option.

BUG:
The 'info' command still reports the save/suspend/pause commands as
valid, though they are not when this build option is used (same is
true of ADVENT_NOSAVE, and that doesn't apparently bother anyone).
2023-03-02 19:44:47 -08:00
Eric S. Raymond
426684fec2 Increase retrigressiveness of oldstyle a bit. 2023-03-01 17:46:13 -05:00
Eric S. Raymond
162c5abc8a Comment typo fix. 2023-03-01 16:03:13 -05:00
Eric S. Raymond
1f644a1d7d Don't crap out on failed diff. 2023-03-01 14:01:44 -05:00
Eric S. Raymond
17782cab67 Another comment typo fix. 2023-03-01 13:52:54 -05:00
Eric S. Raymond
94e7cc6505 Address Gitlab isse #62: Comment typos. 2023-03-01 13:04:53 -05:00
Eric S. Raymond
3f34adad3b Move pylint suppressions inline. 2023-02-28 19:46:50 -05:00
236 changed files with 15709 additions and 7382 deletions

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
# SPDX-FileCopyrightText: (C) Eric S. Raymond
# SPDX-License-Identifier: BSD-2-Clause
advent
*.gcda
*.gcno

View file

@ -1,3 +1,5 @@
# SPDX-FileCopyrightText: (C) Eric S. Raymond
# SPDX-License-Identifier: BSD-2-Clause
stages:
- ci-build
- build

View file

@ -1,3 +1,5 @@
#SPDX-FileCopyrightText: (C) Eric S. Raymond
#SPDX-License-Identifier: BSD-2-Clause
extralines="""
<p>There is a <a href="http://esr.gitlab.io/open-adventure/coverage/">code coverage analysis</a> and a <a href="http://esr.gitlab.io/open-adventure/coverage/adventure.yaml.html">symbol coverage analysis</p>
"""

View file

@ -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

View file

@ -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 <esr@thyrsus.com>
# 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

View file

@ -1,4 +1,6 @@
= Installing Open Adventure =
// SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
// 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

View file

@ -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 <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
VERS=$(shell sed -n <NEWS '/^[0-9]/s/:.*//p' | head -1)
# To build with save/resume disabled, pass CFLAGS="-DADVENT_NOSAVE"
# To build with auto-save/resume enabled, pass CFLAGS="-DADVENT_AUTOSAVE"
VERS=$(shell sed -n <NEWS.adoc '/^[0-9]/s/:.*//p' | head -1)
.PHONY: debug indent release refresh dist linty html clean
.PHONY: check coverage
CC?=gcc
CCFLAGS+=-std=c99 -D_DEFAULT_SOURCE -DVERSION=\"$(VERS)\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all $(CFLAGS) -g
CCFLAGS+=-std=c99 -Wall -Wextra -D_DEFAULT_SOURCE -DVERSION=\"$(VERS)\" -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-all $(CFLAGS) -g $(EXTRA)
LIBS=$(shell pkg-config --libs libedit)
INC+=$(shell pkg-config --cflags libedit)
@ -61,29 +65,46 @@ clean:
cheat: $(CHEAT_OBJS) dungeon.o
$(CC) $(CCFLAGS) $(DBX) -o cheat $(CHEAT_OBJS) dungeon.o $(LDFLAGS) $(LIBS)
check: advent cheat
CSUPPRESSIONS = --suppress=missingIncludeSystem --suppress=invalidscanf
cppcheck:
@-cppcheck -I. --quiet --template gcc -UOBJECT_SET_SEEN --enable=all $(CSUPPRESSIONS) *.[ch]
pylint:
@-pylint --score=n *.py */*.py
check: advent cheat pylint cppcheck spellcheck
cd tests; $(MAKE) --quiet
spellcheck:
@batchspell adventure.yaml advent.adoc
reflow:
@clang-format --style="{IndentWidth: 8, UseTab: ForIndentation}" -i $$(find . -name "*.[ch]")
@black --quiet *.py
# Requires gcov, lcov, libasan6, and libubsan1
# The last two are Ubuntu names, might vary on other distributions.
# After this, run your browser on coverage/open-adventure/index.html
# to see coverage results. Browse coverage/adventure.yaml.html
# to see symbol coverage over the YAML file.
coverage: debug
coverage: clean debug
cd tests; $(MAKE) coverage --quiet
.SUFFIXES: .adoc .html .6
# Note: to suppress the footers with timestamps being generated in HTML,
# we use "-a nofooter".
# To debug asciidoc problems, you may need to run "xmllint --nonet --noout --valid"
# on the intermediate XML that throws an error.
.SUFFIXES: .html .adoc .6
# Requires asciidoc and xsltproc/docbook stylesheets.
.adoc.6:
a2x --doctype manpage --format manpage $<
asciidoctor -D. -a nofooter -b manpage $<
.adoc.html:
asciidoc $<
.adoc:
asciidoc $<
asciidoctor -D. -a nofooter -a webfonts! $<
html: advent.html history.html hints.html
# README.adoc exists because that filename is magic on GitLab.
DOCS=COPYING NEWS README.adoc advent.adoc history.adoc notes.adoc hints.adoc advent.6 INSTALL.adoc
DOCS=COPYING NEWS.adoc README.adoc advent.adoc history.adoc notes.adoc hints.adoc advent.6 INSTALL.adoc
TESTFILES=tests/*.log tests/*.chk tests/README tests/decheck tests/Makefile
# Can't use GNU tar's --transform, needs to build under Alpine Linux.
@ -94,9 +115,6 @@ advent-$(VERS).tar.gz: $(SOURCES) $(DOCS)
(tar -T MANIFEST -czvf advent-$(VERS).tar.gz)
@(rm advent-$(VERS))
indent:
astyle -n -A3 --pad-header --min-conditional-indent=1 --pad-oper *.c
release: advent-$(VERS).tar.gz advent.html history.html hints.html notes.html
shipper version=$(VERS) | sh -e -x
@ -128,6 +146,11 @@ linty: CCFLAGS += -Winit-self
linty: CCFLAGS += -Wpointer-arith
linty: advent cheat
# These seem to be more modern options for enabling coverage testing.
# Documenting them here in case a future version bump disables --coverage.
#debug: CCFLAGS += -ftest-coverage
#debug: CCFLAGS += -fprofile-arcs
debug: CCFLAGS += -O0
debug: CCFLAGS += --coverage
debug: CCFLAGS += -ggdb
@ -136,10 +159,3 @@ debug: CCFLAGS += -fsanitize=address
debug: CCFLAGS += -fsanitize=undefined
debug: linty
CSUPPRESSIONS = --suppress=missingIncludeSystem --suppress=invalidscanf
cppcheck:
cppcheck -I. --template gcc --enable=all $(CSUPPRESSIONS) *.[ch]
PYSUPPRESSIONS = line-too-long,invalid-name,missing-function-docstring,too-many-lines,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,consider-using-in,dict-iter-missing-items
pylint:
@pylint --score=n --disable=$(PYSUPPRESSIONS) *.py */*.py

View file

@ -1,10 +1,37 @@
= Open Adventure project news =
// SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
// 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::

View file

@ -1,4 +1,6 @@
= README for Open Adventure =
// SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
// 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.

3012
actions.c

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,19 @@
= advent(6) =
:doctype: manpage
// SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
// 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

View file

@ -1,3 +1,5 @@
# SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
[Desktop Entry]
Type=Application
Name=Open Adventure

435
advent.h
View file

@ -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 <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <inttypes.h>
#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 = {

View file

@ -2,30 +2,8 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!--
Copyright © 2017 Dr. Tobias Quathamer <toddy@debian.org>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
SPDX-FileCopyrightText: 2017 Dr. Tobias Quathamer <toddy@debian.org>
SPDX-License-Identifier: BSD-2-Clause
-->
<svg width="128" height="128" version="1.1"
xmlns="http://www.w3.org/2000/svg">

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 4 KiB

Before After
Before After

View file

@ -1,3 +1,10 @@
# SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
# 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

162
cheat.c
View file

@ -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 <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <editline/readline.h>
#include "advent.h"
#include <editline/readline.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
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 */

View file

@ -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

View file

@ -1,4 +1,6 @@
= Non-spoiler hints =
// SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
// SPDX-License-Identifier: CC-BY-4.0
Say the words you see. They can have interesting effects.

View file

@ -1,5 +1,7 @@
= A brief history of Colossal Cave Adventure =
by Eric S. Raymond
// SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
// 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]

147
init.c
View file

@ -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 <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#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;
}

2610
main.c

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,6 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
# 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<<COND_%s)" % f) for f in flaglist])
trail = "|".join([("(1<<COND_H%s)" % f['name']) for f in hints])
trail = "|".join([("(1<<COND_H%s)" % f["name"]) for f in hints])
if trail:
line += "|" + trail
if line.startswith("|"):
@ -250,6 +277,7 @@ def get_condbits(locations):
cnd_str += " " + line + ",\t// " + name + "\n"
return cnd_str
def get_motions(motions):
template = """ {{
.words = {},
@ -270,6 +298,7 @@ def get_motions(motions):
ignore += word.upper()
return mot_str
def get_actions(actions):
template = """ {{
.words = {},
@ -302,20 +331,22 @@ def get_actions(actions):
for word in contents["words"]:
if len(word) == 1:
ignore += word.upper()
act_str = act_str[:-1] # trim trailing newline
act_str = act_str[:-1] # trim trailing newline
return act_str
def bigdump(arr):
out = ""
for (i, _) in enumerate(arr):
if i % 10 == 0:
if out and out[-1] == ' ':
if out and out[-1] == " ":
out = out[:-1]
out += "\n "
out += str(arr[i]).lower() + ", "
out = out[:-2] + "\n"
return out
def buildtravel(locs, objs):
assert len(locs) <= 300
assert len(objs) <= 100
@ -335,31 +366,31 @@ def buildtravel(locs, objs):
# location number (Y), and a list of motion numbers (see section 4).
# each motion represents a verb which will go to Y if currently at X.
# Y, in turn, is interpreted as follows. Let M=Y/1000, N=Y mod 1000.
# If N<=300 it is the location to go to.
# If 300<N<=500 N-300 is used in a computed goto to
# a section of special code.
# If N>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 300<N<=500 N-300 is used in a computed goto to
# a section of special code.
# If N>500 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<M<100 it is done with M% probability.
# If M=100 unconditional, but forbidden to dwarves.
# If 100<M<=200 he must be carrying object M-100.
# If 200<M<=300 must be carrying or in same room as M-200.
# If 300<M<=400 game.prop(M % 100) must *not* be 0.
# If 400<M<=500 game.prop(M % 100) must *not* be 1.
# If 500<M<=600 game.prop(M % 100) must *not* be 2, etc.
# If M=0 it's unconditional.
# If 0<M<100 it is done with M% probability.
# If M=100 unconditional, but forbidden to dwarves.
# If 100<M<=200 he must be carrying object M-100.
# If 200<M<=300 must be carrying or in same room as M-200.
# If 300<M<=400 game.prop(M % 100) must *not* be 0.
# If 400<M<=500 game.prop(M % 100) must *not* be 1.
# If 500<M<=600 game.prop(M % 100) must *not* be 2, etc.
# If the condition (if any) is not met, then the next *different*
# "destination" value is used (unless it fails to meet *its* conditions,
# in which case the next is found, etc.). Typically, the next dest will
# be for one of the same verbs, so that its only use is as the alternate
# destination for those verbs. For instance:
# 15 110022 29 31 34 35 23 43
# 15 14 29
# 15 110022 29 31 34 35 23 43
# 15 14 29
# This says that, from loc 15, any of the verbs 29, 31, etc., will take
# him to 22 if he's carrying object 10, and otherwise will go to 14.
# 11 303008 49
# 11 9 50
# 11 303008 49
# 11 9 50
# This says that, from 11, 49 takes him to 8 unless game.prop[3]=0, in which
# case he goes to 9. Verb 50 takes him to 9 regardless of game.prop[3].
ltravel = []
@ -370,13 +401,17 @@ def buildtravel(locs, objs):
verbmap[word.upper()] = i
except TypeError:
pass
def dencode(action, name):
"Decode a destination number"
if action[0] == "goto":
try:
return locnames.index(action[1])
except ValueError:
sys.stderr.write("dungeon: unknown location %s in goto clause of %s\n" % (action[1], name))
sys.stderr.write(
"dungeon: unknown location %s in goto clause of %s\n"
% (action[1], name)
)
raise ValueError
elif action[0] == "special":
return 300 + action[1]
@ -384,11 +419,15 @@ def buildtravel(locs, objs):
try:
return 500 + msgnames.index(action[1])
except ValueError:
sys.stderr.write("dungeon: unknown location %s in carry clause of %s\n" % (cond[1], name))
sys.stderr.write(
"dungeon: unknown location %s in carry clause of %s\n"
% (cond[1], name)
)
else:
print(cond)
raise ValueError
return '' # Pacify pylint
return "" # Pacify pylint
def cencode(cond, name):
if cond is None:
return 0
@ -400,13 +439,19 @@ def buildtravel(locs, objs):
try:
return 100 + objnames.index(cond[1])
except ValueError:
sys.stderr.write("dungeon: unknown object name %s in carry clause of %s\n" % (cond[1], name))
sys.stderr.write(
"dungeon: unknown object name %s in carry clause of %s\n"
% (cond[1], name)
)
sys.exit(1)
elif cond[0] == "with":
try:
return 200 + objnames.index(cond[1])
except IndexError:
sys.stderr.write("dungeon: unknown object name %s in with clause of %s\n" % (cond[1], name))
sys.stderr.write(
"dungeon: unknown object name %s in with clause of %s\n"
% (cond[1], name)
)
sys.exit(1)
elif cond[0] == "not":
try:
@ -422,11 +467,17 @@ def buildtravel(locs, objs):
state = i
break
else:
sys.stderr.write("dungeon: unmatched state symbol %s in not clause of %s\n" % (cond[2], name))
sys.stderr.write(
"dungeon: unmatched state symbol %s in not clause of %s\n"
% (cond[2], name)
)
sys.exit(0)
return 300 + obj + 100 * state
except ValueError:
sys.stderr.write("dungeon: unknown object name %s in not clause of %s\n" % (cond[1], name))
sys.stderr.write(
"dungeon: unknown object name %s in not clause of %s\n"
% (cond[1], name)
)
sys.exit(1)
else:
print(cond)
@ -436,11 +487,13 @@ def buildtravel(locs, objs):
if "travel" in loc:
for rule in loc["travel"]:
tt = [i]
dest = dencode(rule["action"], name) + 1000 * cencode(rule.get("cond"), name)
dest = dencode(rule["action"], name) + 1000 * cencode(
rule.get("cond"), name
)
tt.append(dest)
tt += [motionnames[verbmap[e]].upper() for e in rule["verbs"]]
if not rule["verbs"]:
tt.append(1) # Magic dummy entry for null rules
tt.append(1) # Magic dummy entry for null rules
ltravel.append(tuple(tt))
# At this point the ltravel data is in the Section 3
@ -461,7 +514,7 @@ def buildtravel(locs, objs):
travel[-1][-1] = "false" if travel[-1][-1] == "true" else "true"
while rule:
cond = newloc // 1000
nodwarves = (cond == 100)
nodwarves = cond == 100
if cond == 0:
condtype = "cond_goto"
condarg1 = condarg2 = 0
@ -484,7 +537,7 @@ def buildtravel(locs, objs):
else:
condtype = "cond_not"
condarg1 = cond % 100
condarg2 = (cond - 300) // 100.
condarg2 = (cond - 300) // 100.0
dest = newloc % 1000
if dest <= 300:
desttype = "dest_goto"
@ -495,19 +548,24 @@ def buildtravel(locs, objs):
else:
desttype = "dest_special"
destval = locnames[dest - 300]
travel.append([len(tkey)-1,
locnames[len(tkey)-1],
rule.pop(0),
condtype,
condarg1,
condarg2,
desttype,
destval,
"true" if nodwarves else "false",
"false"])
travel.append(
[
len(tkey) - 1,
locnames[len(tkey) - 1],
rule.pop(0),
condtype,
condarg1,
condarg2,
desttype,
destval,
"true" if nodwarves else "false",
"false",
]
)
travel[-1][-1] = "true"
return (travel, tkey)
def get_travel(travel):
template = """ {{ // from {}: {}
.motion = {},
@ -523,11 +581,12 @@ def get_travel(travel):
out = ""
for entry in travel:
out += template.format(*entry)
out = out[:-1] # trim trailing newline
out = out[:-1] # trim trailing newline
return out
if __name__ == "__main__":
with open(YAML_NAME, "r", encoding='ascii', errors='surrogateescape') as f:
with open(YAML_NAME, "r", encoding="ascii", errors="surrogateescape") as f:
db = yaml.safe_load(f)
locnames = [x[0] for x in db["locations"]]
@ -535,35 +594,39 @@ if __name__ == "__main__":
objnames = [el[0] for el in db["objects"]]
motionnames = [el[0] for el in db["motions"]]
(travel, tkey) = buildtravel(db["locations"],
db["objects"])
(travel, tkey) = buildtravel(db["locations"], db["objects"])
ignore = ""
try:
with open(H_TEMPLATE_PATH, "r", encoding='ascii', errors='surrogateescape') as htf:
with open(
H_TEMPLATE_PATH, "r", encoding="ascii", errors="surrogateescape"
) as htf:
# read in dungeon.h template
h_template = DONOTEDIT_COMMENT + htf.read()
with open(C_TEMPLATE_PATH, "r", encoding='ascii', errors='surrogateescape') as ctf:
with open(
C_TEMPLATE_PATH, "r", encoding="ascii", errors="surrogateescape"
) as ctf:
# read in dungeon.c template
c_template = DONOTEDIT_COMMENT + ctf.read()
except IOError as e:
print('ERROR: reading template failed ({})'.format(e.strerror))
print("ERROR: reading template failed ({})".format(e.strerror))
sys.exit(-1)
c = c_template.format(
h_file = H_NAME,
arbitrary_messages = get_arbitrary_messages(db["arbitrary_messages"]),
classes = get_class_messages(db["classes"]),
turn_thresholds = get_turn_thresholds(db["turn_thresholds"]),
locations = get_locations(db["locations"]),
objects = get_objects(db["objects"]),
obituaries = get_obituaries(db["obituaries"]),
hints = get_hints(db["hints"]),
conditions = get_condbits(db["locations"]),
motions = get_motions(db["motions"]),
actions = get_actions(db["actions"]),
tkeys = bigdump(tkey),
travel = get_travel(travel),
ignore = ignore
h_file=H_NAME,
arbitrary_messages=get_arbitrary_messages(db["arbitrary_messages"]),
classes=get_class_messages(db["classes"]),
turn_thresholds=get_turn_thresholds(db["turn_thresholds"]),
locations=get_locations(db["locations"]),
objects=get_objects(db["objects"]),
obituaries=get_obituaries(db["obituaries"]),
hints=get_hints(db["hints"]),
conditions=get_condbits(db["locations"]),
motions=get_motions(db["motions"]),
actions=get_actions(db["actions"]),
tkeys=bigdump(tkey),
travel=get_travel(travel),
ignore=ignore,
dwarflocs=", ".join(db["dwarflocs"]) + ",",
)
# 0-origin index of birds's last song. Bird should
@ -571,29 +634,30 @@ if __name__ == "__main__":
deathbird = len(dict(db["objects"])["BIRD"]["sounds"]) - 1
h = h_template.format(
num_locations = len(db["locations"])-1,
num_objects = len(db["objects"])-1,
num_hints = len(db["hints"]),
num_classes = len(db["classes"])-1,
num_deaths = len(db["obituaries"]),
num_thresholds = len(db["turn_thresholds"]),
num_motions = len(db["motions"]),
num_actions = len(db["actions"]),
num_travel = len(travel),
num_keys = len(tkey),
bird_endstate = deathbird,
arbitrary_messages = get_refs(db["arbitrary_messages"]),
locations = get_refs(db["locations"]),
objects = get_refs(db["objects"]),
motions = get_refs(db["motions"]),
actions = get_refs(db["actions"]),
state_definitions = statedefines
num_locations=len(db["locations"]) - 1,
num_objects=len(db["objects"]) - 1,
num_hints=len(db["hints"]),
num_classes=len(db["classes"]) - 1,
num_deaths=len(db["obituaries"]),
num_thresholds=len(db["turn_thresholds"]),
num_motions=len(db["motions"]),
num_actions=len(db["actions"]),
num_travel=len(travel),
num_keys=len(tkey),
bird_endstate=deathbird,
arbitrary_messages=get_refs(db["arbitrary_messages"]),
locations=get_refs(db["locations"]),
objects=get_refs(db["objects"]),
motions=get_refs(db["motions"]),
actions=get_refs(db["actions"]),
state_definitions=statedefines,
ndwarflocs=str(len(db["dwarflocs"])),
)
with open(H_NAME, "w", encoding='ascii', errors='surrogateescape') as hf:
with open(H_NAME, "w", encoding="ascii", errors="surrogateescape") as hf:
hf.write(h)
with open(C_NAME, "w", encoding='ascii', errors='surrogateescape') as cf:
with open(C_NAME, "w", encoding="ascii", errors="surrogateescape") as cf:
cf.write(c)
# end

View file

@ -1,45 +1,58 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
# 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)

1221
misc.c

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,7 @@
= Open Adventure Maintainer's Notes =
by Eric S. Raymond
// SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
// 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

View file

@ -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 <ctype.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <inttypes.h>
#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 */

260
score.c
View file

@ -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 <stdlib.h>
#include "advent.h"
#include "dungeon.h"
#include <stdlib.h>
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 */

View file

@ -1,3 +1,7 @@
<--
SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
SPDX-License-Identifier: BSD-2-Clause
-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="en">
<head>

View file

@ -1,3 +1,8 @@
/*
SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
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 */
/* Dwarf starting locations */
const int dwarflocs[NDWARVES] = {{{dwarflocs}}};
/* end */

View file

@ -1,3 +1,7 @@
/*
SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
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;

View file

@ -1,4 +1,7 @@
# Test-suite makefile for opeb-adventure
# Test-suite makefile for open-adventure
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# 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 <pitfall.log >/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

View file

@ -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

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Observe axe after throwing at bear
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
seed 1838473132
in

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Test throwing axe at non-dwarves.
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
# Added coverage of LOC_DEADCRAWL and CROSS_BRIDGE
n
seed 1838473132

View file

@ -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.

8
tests/badmagic.log Normal file
View file

@ -0,0 +1,8 @@
## Resume from filename withoy the right magic at the front
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
#NOCOMPARE advent430 doesn't have this test
n
resume
y
../main.o

View file

@ -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.

View file

@ -1,4 +1,7 @@
## Get to dragon, refuse to use bare hands
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# 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

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Test many nonlethal failure conditions
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# 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

View file

@ -2930,7 +2930,7 @@ OK
> take bird
OK
You are already carrying it!
> free bird

View file

@ -1,4 +1,6 @@
## Attempt to kill snake with bird in the endgame
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
seed 1838473132
in

View file

@ -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.

View file

@ -1,5 +1,8 @@
## Verify that the bird is weightless in inventory
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# 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

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Coverage of LOC_BOULDERS2.short
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# 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
e

View file

@ -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

View file

@ -1,4 +1,6 @@
## Break the mirror in endgame and die
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# 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

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Try to carry bird without cage, then kill bird
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# 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
attack

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Try to carry the bird after freeing it instead of listening
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
seed 1495951709
attack

View file

@ -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!!

View file

@ -1,4 +1,7 @@
## Resume from absurd save file with numdie = -900
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
#NOCOMPARE Can't compare to advent430 due to version skew
n
resume
cheat_numdie.adv

View file

@ -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.

View file

@ -1,5 +1,8 @@
## Resume from absurd save file with numdie = -1000
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# 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

4
tests/compare Executable file
View file

@ -0,0 +1,4 @@
#! /bin/sh
# Display diff for an individual test
test=$1
../advent <${test}.log | ./tapdiffer "${test}" "${test}.chk"

View file

@ -1,4 +1,6 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# 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 = """
<tr>
<td class="headerItem"><a href="#{name}">{name}:</a></td>
<td class="headerCovTableEntry">{total}</td>
<td class="headerCovTableEntry">{covered}</td>
<td class="headerCovTableEntry">{percent:.1f}%</td>
</tr>
'''
"""
HTML_CATEGORY_SECTION = '''
HTML_CATEGORY_SECTION = """
<tr id="{id}"></tr>
{rows}
<tr>
<td>&nbsp;</td>
</tr>
'''
"""
HTML_CATEGORY_HEADER = '''
HTML_CATEGORY_HEADER = """
<tr>
<td class="tableHead" width="60%" colspan="{colspan}">{label}</td>
{cells}
</tr>
'''
"""
HTML_CATEGORY_HEADER_CELL = '<td class="tableHead" width="15%">{}</td>\n'
HTML_CATEGORY_COVERAGE_CELL = '<td class="{}">&nbsp;</td>\n'
HTML_CATEGORY_ROW = '''
HTML_CATEGORY_ROW = """
<tr>
<td class="coverFile" colspan="{colspan}">{id}</td>
{cells}
</tr>
'''
"""
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))

View file

@ -1,4 +1,6 @@
## Jump into a pit and die, refuse reincarnation
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
seed 1495774850
in

View file

@ -1,4 +1,6 @@
## Last-minute defeat, with lava. Also tests vase drop before pillow.
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
# Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56
n
seed 1838473132

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Take nugget and fail to climb to the dome
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
seed 1838473132
in

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Check that dead dragon actually moves its location (fuzzed)
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
seed 18084731
in

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Try to carry the bird after freeing it instead of listening
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
seed 1495951709
in
@ -25,4 +27,4 @@ take bird
drop cage
d
d
feed snake
feed snake

View file

@ -1,4 +1,6 @@
## Speak a magic word at an inopportune time and drown.
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
# Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56
n
seed 1838473132

View file

@ -1,4 +1,6 @@
## In which the dwarf kills you
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
seed 1494912171
in

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Check that dwarf spawns in alternative location (fuzzed)
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
seed 383847
in
@ -8,3 +10,4 @@ w
w
d
d
n

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Be done with Giant Room and eggs (fuzzed)
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# 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
inven

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Vanishing eggs in Giant Room (fuzzed)
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# 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
e

View file

@ -1,4 +1,6 @@
## 428-point walkthrough
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
# Based on walkthrough at http://www.ecsoftwareconsulting.com/node/56
n
seed 1838473132

View file

@ -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

View file

@ -1,5 +1,8 @@
### Check that water is unavailable in endgame
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# 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

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Fail to get maze hint by being empty-handed (fuzzed)
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
seed 25508795
in

View file

@ -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.

View file

@ -1,4 +1,7 @@
## Qualify for ogre hint but fail due to dwarves dead (fuzzed)
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
#NOCOMPARE Fails due uninteresting difference in whitespace process.
n
seed 1838473132
in

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Qualify for ogre hint but fail due to nearby dwarf (fuzzed)
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
seed 25508795
in

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Fail getting wood hint by finding appendage (fuzzed)
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
n
z
@ -24,4 +26,4 @@ z
z
z
z
z
z

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Attempt to fill lamp, attempt to fill bottle with no source
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
in
carry lamp

View file

@ -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".

View file

@ -1,4 +1,6 @@
## Fill the vase
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
# Arthur O'Dwyer <arthur.j.odwyer@gmail.com> writes:
#
# (4) Lastly, here's a test case for you! Go get the VASE; then get the

View file

@ -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.

View file

@ -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 <esr@thyrsus.com>
# 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

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Coverage of LOC_FOOTSLIP
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
seed 1838473132
in

View file

@ -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.

View file

@ -1,4 +1,6 @@
## Observe amber, ruby, sapphire after state change
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
n
seed 1635997320
in

View file

@ -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".

View file

@ -1,6 +1,9 @@
## Test many nonlethal failure conditions
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# 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

View file

@ -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.

Some files were not shown because too many files have changed in this diff Show more