master fbe59584aea6 cached
23 files
291.5 KB
90.3k tokens
161 symbols
1 requests
Download .txt
Showing preview only (303K chars total). Download the full file or copy to clipboard to get everything.
Repository: brandon-rhodes/python-adventure
Branch: master
Commit: fbe59584aea6
Files: 23
Total size: 291.5 KB

Directory structure:
gitextract_kuy29xws/

├── .gitignore
├── LICENSE.ASF-2
├── README.md
├── adventure/
│   ├── MANIFEST.in
│   ├── README.txt
│   ├── TODO.txt
│   ├── __init__.py
│   ├── __main__.py
│   ├── data.py
│   ├── game.py
│   ├── model.py
│   ├── prompt.py
│   └── tests/
│       ├── __init__.py
│       ├── syntax.txt
│       ├── test_commands.py
│       ├── test_data.py
│       ├── test_walks.py
│       ├── vignettes.txt
│       ├── walkthrough1.txt
│       ├── walkthrough2.txt
│       ├── walkthrough3.txt
│       └── walkthrough4.txt
└── setup.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.coverage
htmlcov/
*~
__pycache__/
*.pyc
/MANIFEST
build/
dist/
*.egg-info


================================================
FILE: LICENSE.ASF-2
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.md
================================================
Welcome to the git repository for the Python 3 version of Adventure!
The project README is one level deeper, inside of the package itself:

[adventure/README.txt](adventure/README.txt)


================================================
FILE: adventure/MANIFEST.in
================================================
include LICENSE.*


================================================
FILE: adventure/README.txt
================================================
This is a faithful port of the “Adventure” game to Python 3 from the
original 1977 FORTRAN code by Crowther and Woods (it is driven by the
same ``advent.dat`` file!) that lets you explore Colossal Cave, where
others have found fortunes in treasure and gold, though it is rumored
that some who enter are never seen again.

This page:

http://rickadams.org/adventure/e_downloads.html

offers the original PHP source code at this link:

http://www.ifarchive.org/if-archive/games/source/advent-original.tar.gz

To encourage the use of Python 3, the game is designed to be played
right at the Python prompt.  Single-word commands can be typed by
themselves, but two-word commands should be written as a function call
(since a two-word command would not be valid Python)::

    >>> import adventure
    >>> adventure.play()
    WELCOME TO ADVENTURE!!  WOULD YOU LIKE INSTRUCTIONS?

    >>> no
    YOU ARE STANDING AT THE END OF A ROAD BEFORE A SMALL BRICK BUILDING.
    AROUND YOU IS A FOREST.  A SMALL STREAM FLOWS OUT OF THE BUILDING AND
    DOWN A GULLY.

    >>> east
    YOU ARE INSIDE A BUILDING, A WELL HOUSE FOR A LARGE SPRING.
    THERE ARE SOME KEYS ON THE GROUND HERE.
    THERE IS A SHINY BRASS LAMP NEARBY.
    THERE IS FOOD HERE.
    THERE IS A BOTTLE OF WATER HERE.

    >>> get(lamp)
    OK

    >>> leave
    YOU'RE AT END OF ROAD AGAIN.

    >>> south
    YOU ARE IN A VALLEY IN THE FOREST BESIDE A STREAM TUMBLING ALONG A
    ROCKY BED.

The original Adventure paid attention to only the first five letters of
each command, so a long command like ``inventory`` could simply be typed
as ``inven``.  This package defines a symbol for both versions of every
long word, so you can type the long or short version as you please.

You can save your game at any time by calling the ``save()`` command
with a filename, and then can resume it later::

    >>> save('advent.save')
    GAME SAVED

    >>> adventure.resume('advent.save')
    GAME RESTORED
    >>> look
    SORRY, BUT I AM NOT ALLOWED TO GIVE MORE DETAIL.  I WILL REPEAT THE
    LONG DESCRIPTION OF YOUR LOCATION.
    YOU ARE IN A VALLEY IN THE FOREST BESIDE A STREAM TUMBLING ALONG A
    ROCKY BED.

You can find two complete, working walkthroughs of the game in its
``tests`` directory, which you can run using the ``discover`` module that
comes built-in with Python 3::

    $ python3 -m unittest discover adventure

I wrote most of this package over Christmas vacation 2010, to learn more
about the workings of the game that so enthralled me as a child; the
project also gave me practice writing Python 3.  I still forget the
parentheses when writing ``print()`` if I am not paying attention.

Traditional Mode
================

You can also use this package to play Adventure at a traditional prompt
that does not require its input to be valid Python.  Use your operating
system command line to run the package::

    $ python3 -m adventure
    WELCOME TO ADVENTURE!!  WOULD YOU LIKE INSTRUCTIONS?

    >

At the prompt that will appear, two-word commands can simply be
separated by a space::

    > get lamp
    OK

For extra authenticity, the output of the Adventure game in this mode is
typed to your screen at 1200 baud.  You will note that although this
prints the text faster than you can read it anyway, your experience of
the game will improve considerably, especially when a move results in a
surprise.

Why is the game better at 1200 baud?  When a paragraph of text is
allowed to appear on the screen all at once, your eyes scan the entire
paragraph for important information, often ruining any surprises before
you can then settle down and read it from the beginning.  But at 1200
baud, you wind up reading the text in order as it appears, which unfolds
the narrative sequentially as the author of Adventure intended.

If you created a file with the in-game ``save`` command, you can restore
it later by naming it on the command line::

    > save mygame
    GAME SAVED
    > quit
    DO YOU REALLY WANT TO QUIT NOW?
    > y
    OK

    $ python3 -m adventure mygame
    GAME RESTORED
    >

Notes
=====

* Several Adventure commands conflict with standard Python built-in
  functions.  If you want to run the normal Python function ``exit()``,
  ``open()``, ``quit()``, or ``help()``, then import the ``builtin``
  module and run the copy of the function stored there.

* The word “break” is a Python keyword, so there was no possibility of
  using it in the game.  Instead, use one of the two synonyms defined by
  the PDP version of Adventure: “shatter” or “smash.”

Copyright
=========

The ``advent.dat`` game data file distributed with this Python package,
like the rest of the original source code for Adventure, is a public
domain work.  Phrases from the original work that have been copied into
my source code from the FORTRAN source (the famous phrase “You have
gotten yourself killed” and so forth) remain public domain and can be
used without attribution.

My own Python code that re-implements the game engine is:

Copyright 2010–2015 Brandon Rhodes

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Changelog
=========

| 1.6 — 2020 August 15 — add support for upper-case commands typed at the terminal; and fix exception if user dies with water in their bottle `(see #26) <https://github.com/brandon-rhodes/python-adventure/issues/26>`_.
| 1.5 — 2020 July 18 — fix for fatal exception when “lamp turn” is entered.
| 1.4 — 2016 January 31 — readline editing; added license; bug fix; test fix.
| 1.3 — 2012 April 27 — installs on Windows; fixed undefined commands
| 1.2 — 2012 April 5 — restoring saves from command line; 5-letter commands
| 1.1 — 2011 March 12 — traditional mode; more flexible Python syntax
| 1.0 — 2011 February 15 — 100% test coverage, feature-complete
| 0.3 — 2011 January 31 — first public release


================================================
FILE: adventure/TODO.txt
================================================

* Test dropping into the secret canyon from the direction that gets no dragon.


================================================
FILE: adventure/__init__.py
================================================
"""The Adventure game.

Copyright 2010-2015 Brandon Rhodes.  Licensed as free software under the
Apache License, Version 2.0 as detailed in the accompanying README.txt.

"""
import sys

if sys.version_info <= (3,):
    raise RuntimeError('Alas, Adventure requires Python 3 or later')

def load_advent_dat(data):
    import os
    from .data import parse

    datapath = os.path.join(os.path.dirname(__file__), 'advent.dat')
    with open(datapath, 'r', encoding='ascii') as datafile:
        parse(data, datafile)

def play(seed=None):
    """Turn the Python prompt into an Adventure game.

    With optional the `seed` argument the caller can supply an integer
    to start the Python random number generator at a known state.

    """
    global _game

    from .game import Game
    from .prompt import install_words

    _game = Game(seed)
    load_advent_dat(_game)
    install_words(_game)
    _game.start()
    print(_game.output[:-1])

def resume(savefile, quiet=False):
    global _game

    from .game import Game
    from .prompt import install_words

    _game = Game.resume(savefile)
    install_words(_game)
    if not quiet:
        print('GAME RESTORED\n')


================================================
FILE: adventure/__main__.py
================================================
"""Offer Adventure at a custom command prompt.

Copyright 2010-2015 Brandon Rhodes.  Licensed as free software under the
Apache License, Version 2.0 as detailed in the accompanying README.txt.

"""
import argparse
import os
import re
import readline
import sys
from time import sleep
from . import load_advent_dat
from .game import Game

BAUD = 1200

def baudout(s):
    out = sys.stdout
    for c in s:
        sleep(9. / BAUD)  # 8 bits + 1 stop bit @ the given baud rate
        out.write(c)
        out.flush()

def loop(args):
    parser = argparse.ArgumentParser(
        description='Adventure into the Colossal Caves.',
        prog='{} -m adventure'.format(os.path.basename(sys.executable)))
    parser.add_argument(
        'savefile', nargs='?', help='The filename of game you have saved.')
    args = parser.parse_args(args)

    if args.savefile is None:
        game = Game()
        load_advent_dat(game)
        game.start()
        baudout(game.output)
    else:
        game = Game.resume(args.savefile)
        baudout('GAME RESTORED\n')

    while not game.is_finished:
        line = input('> ').lower()
        words = re.findall(r'\w+', line)
        if words:
            baudout(game.do_command(words))

if __name__ == '__main__':
    try:
        loop(sys.argv[1:])
    except EOFError:
        pass


================================================
FILE: adventure/data.py
================================================
# -*- coding: utf-8 -*-

"""Parse the original PDP ``advent.dat`` file.

Copyright 2010-2015 Brandon Rhodes.  Licensed as free software under the
Apache License, Version 2.0 as detailed in the accompanying README.txt.

"""
from operator import attrgetter
from .model import Hint, Message, Move, Object, Room, Word

# The Adventure data file knows only the first five characters of each
# word in the game, so we have to know the full verion of each word.

long_words = { w[:5]: w for w in """upstream downstream forest
forward continue onward return retreat valley staircase outside building stream
cobble inward inside surface nowhere passage tunnel canyon awkward
upward ascend downward descend outdoors barren across debris broken
examine describe slabroom depression entrance secret bedquilt plover
oriental cavern reservoir office headlamp lantern pillow velvet fissure tablet
oyster magazine spelunker dwarves knives rations bottle mirror beanstalk
stalactite shadow figure drawings pirate dragon message volcano geyser
machine vending batteries carpet nuggets diamonds silver jewelry treasure
trident shards pottery emerald platinum pyramid pearl persian spices capture
release discard mumble unlock nothing extinguish placate travel proceed
continue explore follow attack strike devour inventory detonate ignite
blowup peruse shatter disturb suspend sesame opensesame abracadabra
shazam excavate information""".split() }

class Data(object):
    def __init__(self):
        self.rooms = {}
        self.vocabulary = {}
        self.objects = {}
        self.messages = {}
        self.class_messages = []
        self.hints = {}
        self.magic_messages = {}

    def referent(self, word):
        if word.kind == 'noun':
            return self.objects[word.n % 1000]

# Helper functions.

def make_object(dictionary, klass, n):
    if n not in dictionary:
        dictionary[n] = obj = klass()
        obj.n = n
    return dictionary[n]

def expand_tabs(segments):
    it = iter(segments)
    line = next(it)
    for segment in it:
        spaces = 8 - len(line) % 8
        line += ' ' * spaces + segment
    return line

def accumulate_message(dictionary, n, line):
    dictionary[n] = dictionary.get(n, '') + line + '\n'

# Knowledge of what each section contains.

def section1(data, n, *etc):
    """Handle record from “Section 1: long form descriptions”.

    Section 1: long form descriptions. Each line contains a location
    number, a TAB, and a line of text. The set of (necessarily adjacent)
    lines whose numbers are X form the long description of location X.

    """
    room = make_object(data.rooms, Room, n)
    if not etc[0].startswith('>$<'):
        room.long_description += expand_tabs(etc) + '\n'

def section2(data, n, line):
    """Handle record from “Section 2: short form descriptions”.

    Section 2: short form descriptions. Same format as long form.  Not
    all places have short descriptions.

    """
    make_object(data.rooms, Room, n).short_description += line + '\n'

def section3(data, x, y, *verbs):
    """Handle record from “Section 3: travel table”.

    Section 3: travel table. Each line contains a location number (X), a
    second 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.

    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: PROP(M MOD 100) must *not* be 0.
        If 400<M<=500: PROP(M MOD 100) must *not* be 1.
        If 500<M<=600: PROP(M MOD 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

    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

    This says that, from 11, 49 takes him to 8 unless PROP(3)=0, in
    which case he goes to 9. Verb 50 takes him to 9 regardless of
    PROP(3).

    """
    last_travel = data._last_travel
    if last_travel[0] == x and last_travel[1][0] == verbs[0]:
        verbs = last_travel[1]  # same first verb implies use whole list
    else:
        data._last_travel = [x, verbs]

    m, n = divmod(y, 1000)
    mh, mm = divmod(m, 100)

    if m == 0:
        condition = (None,)
    elif 0 < m < 100:
        condition = ('%', m)
    elif m == 100:
        condition = ('not_dwarf',)
    elif 100 < m <= 200:
        condition = ('carrying', mm)
    elif 200 < m <= 300:
        condition = ('carrying_or_in_room_with', mm)
    elif 300 < m:
        condition = ('prop!=', mm, mh - 3)

    if n <= 300:
        action = make_object(data.rooms, Room, n)
    elif 300 < n <= 500:
        action = n  # special computed goto
    else:
        action = make_object(data.messages, Message, n - 500)

    move = Move()
    if len(verbs) == 1 and verbs[0] == 1:
        move.is_forced = True
    else:
        move.verbs = [ make_object(data.vocabulary, Word, verb_n)
                       for verb_n in verbs if verb_n < 100 ] # skip bad "109"
    move.condition = condition
    move.action = action
    data.rooms[x].travel_table.append(move)

def section4(data, n, text, *etc):
    """Handle record from “Section 4: vocabulary”.

    Section 4: vocabulary. Each line contains a number (N), a TAB, and a
    five-letter word. Call M=N/1000. If M=0, then the word is a motion
    verb for use in travelling (see section 3). Else, if M=1, the word
    is an object. Else, if M=2, the word is an action verb (such as
    "CARRY" or "ATTACK"). Else, if M=3, the word is a special case verb
    (such as "DIG") and N MOD 1000 is an index into section 6. Objects
    from 50 to (currently, anyway) 79 are considered treasures (for
    pirate, closeout).

    """
    text = text.lower()
    text = long_words.get(text, text)
    word = make_object(data.vocabulary, Word, n)
    if word.text is None:  # this is the first word with index "n"
        word.text = text
    else:  # there is already a word sitting at "n", so create a synonym
        original = word
        word = Word()
        word.n = n
        word.text = text
        original.add_synonym(word)
    word.kind = ['travel', 'noun', 'verb', 'snappy_comeback'][n // 1000]
    if word.kind == 'noun':
        n %= 1000
        obj = make_object(data.objects, Object, n)
        obj.names.append(text)
        obj.is_treasure = (n >= 50)
        data.objects[text] = obj
    if text not in data.vocabulary:  # since duplicate names exist
        data.vocabulary[text] = word

def section5(data, n, *etc):
    """Handle record from “Section 5: object descriptions”.

    Section 5: object descriptions. Each line contains a number (N), a
    TAB, and a message. If N is from 1 to 100, the message is the
    "inventory" message for object N. Otherwise, N should be 000, 100,
    200, etc., and the message should be the description of the
    preceding object when its PROP value is N/100. The N/100 is used
    only to distinguish multiple messages from multi-line messages; the
    PROP info actually requires all messages for an object to be present
    and consecutive.  Properties which produce no message should be
    given the message ">$<".

    """
    if 1 <= n <= 99:
        data._object = make_object(data.objects, Object, n)
        data._object.inventory_message = expand_tabs(etc)
    else:
        n //= 100
        messages = data._object.messages
        if etc[0].startswith('>$<'):
            more = ''
        else:
            more = expand_tabs(etc) + '\n'
        messages[n] = messages.get(n, '') + more

def section6(data, n, *etc):
    """Handle record from “Section 6: arbitrary messages”.

    Section 6: arbitrary messages. Same format as sections 1, 2, and 5,
    except the numbers bear no relation to anything (except for special
    verbs in section 4).

    """
    message = make_object(data.messages, Message, n)
    message.text += expand_tabs(etc) + '\n'

def section7(data, n, room_n, fixed=None):
    """Handle record from “Section 7: object locations”.

    Section 7: object locations. Each line contains an object number and
    its initial location (zero (or omitted) if none).  If the object is
    immovable, the location is followed by a "-1". If it has two
    locations (e.g. the grate) the first location is followed with the
    second, and the object is assumed to be immovable.

    """
    obj = make_object(data.objects, Object, n)
    if room_n:
        room = make_object(data.rooms, Room, room_n)
        obj.drop(room)
    if fixed is not None:
        if fixed == -1:
            obj.is_fixed = True
        else:
            room2 = make_object(data.rooms, Room, fixed)
            obj.rooms.append(room2)  # exists two places, like grate
    obj.starting_rooms = list(obj.rooms)  # remember where things started

def section8(data, word_n, message_n):
    """Handle record from “Section 8: action defaults”.

    Section 8: action defaults. Each line contains an "action-verb"
    number and the index (in section 6) of the default message for the
    verb.

    """
    if not message_n:
        return
    word = make_object(data.vocabulary, Word, word_n + 2000)
    message = make_object(data.messages, Message, message_n)
    for word2 in word.synonyms:
        word2.default_message = message

def section9(data, bit, *nlist):
    """Handle record from “Section 9: liquid assets, etc.”.

    Section 9: liquid assets, etc. Each line contains a number (N) and
    up to 20 location numbers. Bit N (where 0 is the units bit) is set
    in COND(LOC) for each LOC given. The COND bits currently assigned
    are:

        0: light
        1: if bit 2 is on: on for oil, off for water
        2: liquid asset, see bit 1
        3: pirate doesn't go here unless following player

    Other bits are used to indicate areas of interest to "hint"
    routines:

        4: trying to get into cave
        5: trying to catch bird
        6: trying to deal with snake
        7: lost in maze
        8: pondering dark room
        9: at Witt's End

    COND(LOC) is set to 2, overriding all other bits, if LOC has forced
    motion.

    """
    for n in nlist:
        room = make_object(data.rooms, Room, n)
        if bit == 0:
            room.is_light = True
        elif bit == 1:
            room.liquid = make_object(data.objects, Object, 22) #oil
        elif bit == 2:
            room.liquid = make_object(data.objects, Object, 21) #water
        elif bit == 3:
            room.is_forbidden_to_pirate = True
        else:
            hint = make_object(data.hints, Hint, bit)
            hint.rooms.append(room)

def section10(data, score, line, *etc):
    """Handle record from “Section 10: class messages”.

    Section 10: class messages. Each line contains a number (N), a TAB,
    and a message describing a classification of player. The scoring
    section selects the appropriate message, where each message is
    considered to apply to players whose scores are higher than the
    previous N but not higher than this N. Note that these scores
    probably change with every modification (and particularly expansion)
    of the program.

    """
    data.class_messages.append((score, line))

def section11(data, n, turns_needed, penalty, question_n, message_n):
    """Handle record from “Section 11: hints”.

    Section 11: hints. Each line contains a hint number (corresponding
    to a COND bit, see section 9), the number of turns he must be at the
    right LOC(s) before triggering the hint, the points deducted for
    taking the hint, the message number (section 6) of the question, and
    the message number of the hint. These values are stashed in the
    "HINTS" array.

    HNTMAX is set to the max hint number (<= HNTSIZ). Numbers 1-3 are
    unusable since COND bits are otherwise assigned, so 2 is used to
    remember if he's read the clue in the repository, and 3 is used to
    remember whether he asked for instructions (gets more turns, but
    loses points).

    """
    hint = make_object(data.hints, Hint, n)
    hint.turns_needed = turns_needed
    hint.penalty = penalty
    hint.question = make_object(data.messages, Message, question_n)
    hint.message = make_object(data.messages, Message, message_n)

def section12(data, n, line):
    """Handle record from “Section 12: magic messages”.

    Section 12: magic messages. Identical to section 6 except put in a
    separate section for easier reference. Magic messages are used by
    the startup, maintenance mode, and related routines.

    """
    accumulate_message(data.magic_messages, n, line)

# Process every section of the file in turn.

def parse(data, datafile):
    """Read the Adventure data file and return a ``Data`` object."""
    data._last_travel = [0, [0]]  # x and verbs used by section 3

    while True:
        section_number = int(datafile.readline())
        if not section_number:  # no further sections
            break
        store = globals().get('section%d' % section_number)
        while True:
            fields = [ (int(field) if field.lstrip('-').isdigit() else field)
                       for field in datafile.readline().strip().split('\t') ]
            if fields[0] == -1:  # end-of-section marker
                break
            store(data, *fields)

    del data._last_travel  # state used by section 3
    del data._object       # state used by section 5

    data.object_list = sorted(set(data.objects.values()), key=attrgetter('n'))
    #data.room_list = sorted(set(data.rooms.values()), key=attrgetter('n'))
    for obj in data.object_list:
        name = obj.names[0]
        if hasattr(data, name):
            name = name + '2'  # create identifiers like ROD2, PLANT2
        setattr(data, name, obj)

    return data


================================================
FILE: adventure/game.py
================================================
"""How we keep track of the state of the game.

Copyright 2010-2015 Brandon Rhodes.  Licensed as free software under the
Apache License, Version 2.0 as detailed in the accompanying README.txt.

"""
# Numeric comments scattered through this file refer to FORTRAN line
# numbers, for those comparing this file and `advent.for`; so "#2012"
# refers to FORTRAN line number 2012 (which you can find easily in the
# FORTRAN using Emacs with an interactive search for newline-2012-tab,
# that is typed C-s C-q C-j 2 0 1 2 C-i).

import os
import pickle
import random
import zlib
from operator import attrgetter
from .data import Data
from .model import Room, Message, Dwarf, Pirate

YESNO_ANSWERS = {'y': True, 'yes': True, 'n': False, 'no': False}

class Game(Data):

    look_complaints = 3  # how many times to "SORRY, BUT I AM NOT ALLOWED..."
    full_description_period = 5  # how often we use a room's full description
    full_wests = 0  # how many times they have typed "west" instead of "w"
    dwarf_stage = 0  # DFLAG how active the dwarves are
    dwarves_killed = 0  # DKILL
    knife_location = None  # KNFLOC
    foobar = -1  # FOOBAR turn number of most recent still-valid "fee"
    gave_up = False
    treasures_not_found = 0  # TALLY how many treasures have not yet been seen
    impossible_treasures = 0  # TALLY2 how many treasures can never be retrieved
    lamp_turns = 330
    warned_about_dim_lamp = False
    bonus = 0  # how they exited the final bonus round
    is_dead = False  # whether we are currently dead
    deaths = 0  # how many times the player has died
    max_deaths = 3  # how many times the player can die
    turns = 0

    def __init__(self, seed=None):
        Data.__init__(self)
        self.output = ''
        self.yesno_callback = False
        self.yesno_casual = False       # whether to insist they answer

        self.clock1 = 30                # counts down from finding last treasure
        self.clock2 = 50                # counts down until cave closes
        self.is_closing = False         # is the cave closing?
        self.panic = False              # they tried to leave during closing?
        self.is_closed = False          # is the cave closed?
        self.is_done = False            # caller can check for "game over"
        self.could_fall_in_pit = False  # could the player fall into a pit?

        self.random_generator = random.Random()
        if seed is not None:
            self.random_generator.seed(seed)

    def random(self):
        return self.random_generator.random()

    def choice(self, seq):
        return self.random_generator.choice(seq)

    def write(self, more):
        """Append the Unicode representation of `s` to our output."""
        if more:
            self.output += str(more).upper()
            self.output += '\n'

    def write_message(self, n):
        self.write(self.messages[n])

    def yesno(self, s, yesno_callback, casual=False):
        """Ask a question and prepare to receive a yes-or-no answer."""
        self.write(s)
        self.yesno_callback = yesno_callback
        self.yesno_casual = casual

    # Properties of the cave.

    @property
    def is_dark(self):
        lamp = self.objects['lamp']
        if self.is_here(lamp) and lamp.prop:
            return False
        return self.loc.is_dark

    @property
    def inventory(self):
        return [ obj for obj in self.object_list if obj.is_toting ]

    @property
    def treasures(self):
        return [ obj for obj in self.object_list if obj.is_treasure ]

    @property
    def objects_here(self):
        return self.objects_at(self.loc)

    def objects_at(self, room):
        return [obj for obj in self.object_list if obj.is_at(room)]

    def is_here(self, obj):
        if isinstance(obj, Dwarf):
            return self.loc is obj.room
        else:
            return obj.is_toting or obj.is_at(self.loc)

    @property
    def is_finished(self):
        return (self.is_dead or self.is_done) and not self.yesno_callback

    # Game startup

    def start(self):
        """Start the game."""

        # For old-fashioned players, accept five-letter truncations like
        # "inven" instead of insisting on full words like "inventory".

        for key, value in list(self.vocabulary.items()):
            if isinstance(key, str) and len(key) > 5:
                self.vocabulary[key[:5]] = value

        # Set things going.

        self.chest_room = self.rooms[114]
        self.bottle.contents = self.water
        self.yesno(self.messages[65], self.start2)  # want instructions?

    def start2(self, yes):
        """Display instructions if the user wants them."""
        if yes:
            self.write_message(1)
            self.hints[3].used = True
            self.lamp_turns = 1000

        self.oldloc2 = self.oldloc = self.loc = self.rooms[1]
        self.dwarves = [ Dwarf(self.rooms[n]) for n in (19, 27, 33, 44, 64) ]
        self.pirate = Pirate(self.chest_room)

        treasures = self.treasures
        self.treasures_not_found = len(treasures)
        for treasure in treasures:
            treasure.prop = -1

        self.describe_location()

    # Routines that handle the aftermath of "big" actions like movement.
    # Although these are called at the end of each `do_command()` cycle,
    # we place here at the top of `game.py` to mirror the order in the
    # advent.for file.

    def move_to(self, newloc=None):  #2
        loc = self.loc
        if newloc is None:
            newloc = loc

        if self.is_closing and newloc.is_aboveground:
            self.write_message(130)
            newloc = loc  # cancel move and put him back underground
            if not self.panic:
                self.clock2 = 15
                self.panic = True

        must_allow_move = ((newloc is loc) or (loc.is_forced)
                           or (loc.is_forbidden_to_pirate))

        dwarf_blocking_the_way = any(
            dwarf.old_room is newloc and dwarf.has_seen_adventurer
            for dwarf in self.dwarves
            )

        if not must_allow_move and dwarf_blocking_the_way:
            newloc = loc  # cancel move they were going to make
            self.write_message(2)  # dwarf is blocking the way

        self.loc = loc = newloc  #74

        # IF LOC.EQ.0 ?
        is_dwarf_area = not (loc.is_forced or loc.is_forbidden_to_pirate)
        if is_dwarf_area and self.dwarf_stage > 0:
            self.move_dwarves()
        else:
            if is_dwarf_area and loc.is_after_hall_of_mists:
                self.dwarf_stage = 1
            self.describe_location()

    def move_dwarves(self):

        #6000
        if self.dwarf_stage == 1:

            # 5% chance per turn of meeting first dwarf
            if self.loc.is_before_hall_of_mists or self.random() < .95:
                self.describe_location()
                return
            self.dwarf_stage = 2
            for i in range(2):  # randomly remove 0, 1, or 2 dwarves
                if self.random() < .5:
                    self.dwarves.remove(self.choice(self.dwarves))
            for dwarf in self.dwarves:
                if dwarf.room is self.loc:  # move dwarf away from our loc
                    dwarf.start_at(self.rooms[18])
            self.write_message(3)  # dwarf throws axe and curses
            self.axe.drop(self.loc)
            self.describe_location()
            return

        #6010
        dwarf_count = dwarf_attacks = knife_wounds = 0

        for dwarf in self.dwarves + [ self.pirate ]:

            locations = { move.action for move in dwarf.room.travel_table
                          if dwarf.can_move(move)
                          and move.action is not dwarf.old_room
                          and move.action is not dwarf.room }
            # Without stabilizing the order with a sort, the room chosen
            # would depend on how the Room addresses in memory happen to
            # order the rooms in the set() - and make it impossible to
            # test the game by setting the random number generator seed
            # and then playing through the game.
            locations = sorted(locations, key=attrgetter('n'))
            if locations:
                new_room = self.choice(locations)
            else:
                new_room = dwarf.old_room
            dwarf.old_room, dwarf.room = dwarf.room, new_room
            if self.loc in (dwarf.room, dwarf.old_room):
                dwarf.has_seen_adventurer = True
            elif self.loc.is_before_hall_of_mists:
                dwarf.has_seen_adventurer = False

            if not dwarf.has_seen_adventurer:
                continue

            dwarf.room = self.loc

            if dwarf.is_dwarf:
                dwarf_count += 1
                # A dwarf cannot walk and attack at the same time.
                if dwarf.room is dwarf.old_room:
                    dwarf_attacks += 1
                    self.knife_location = self.loc
                    if self.random() < .095 * (self.dwarf_stage - 2):
                        knife_wounds += 1

            else:  # the pirate
                pirate = dwarf

                if self.loc is self.chest_room or self.chest.prop >= 0:
                    continue  # decide that the pirate is not really here

                treasures = [ t for t in self.treasures if t.is_toting ]
                if (self.platinum in treasures and self.loc.n in (100, 101)):
                    treasures.remove(self.platinum)

                if not treasures:
                    h = any( t for t in self.treasures if self.is_here(t) )
                    one_treasure_left = (self.treasures_not_found ==
                                         self.impossible_treasures + 1)
                    shiver_me_timbers = (
                        one_treasure_left and not h and not(self.chest.rooms)
                        and self.is_here(self.lamp) and self.lamp.prop == 1
                        )

                    if not shiver_me_timbers:
                        if (pirate.old_room != pirate.room
                            and self.random() < .2):
                            self.write_message(127)
                        continue  # pragma: no cover

                    self.write_message(186)
                    self.chest.drop(self.chest_room)
                    self.message.drop(self.rooms[140])

                else:
                    #6022  I'll just take all this booty
                    self.write_message(128)
                    if not self.message.rooms:
                        self.chest.drop(self.chest_room)
                    self.message.drop(self.rooms[140])
                    for treasure in treasures:
                        treasure.drop(self.chest_room)

                #6024
                pirate.old_room = pirate.room = self.chest_room
                pirate.has_seen_adventurer = False  # free to move

        # Report what has happened.

        if dwarf_count == 1:
            self.write_message(4)
        elif dwarf_count:
            self.write('There are {} threatening little dwarves in the'
                       ' room with you.\n'.format(dwarf_count))

        if dwarf_attacks and self.dwarf_stage == 2:
            self.dwarf_stage = 3

        if dwarf_attacks == 1:
            self.write_message(5)
            k = 52
        elif dwarf_attacks:
            self.write('{} of them throw knives at you!\n'.format(dwarf_attacks))
            k = 6

        if not dwarf_attacks:
            pass
        elif not knife_wounds:
            self.write_message(k)
        else:
            if knife_wounds == 1:
                self.write_message(k + 1)
            else:
                self.write('{} of them get you!\n'.format(knife_wounds))
            self.oldloc2 = self.loc
            self.die()
            return

        self.describe_location()

    def describe_location(self):  #2000

        loc = self.loc

        if loc.n == 0:
            self.die()

        could_fall = self.is_dark and self.could_fall_in_pit
        if could_fall and not loc.is_forced and self.random() < .35:
            self.die_here()
            return

        if self.bear.is_toting:
            self.write_message(141)

        if self.is_dark and not loc.is_forced:
            self.write_message(16)
        else:
            do_short = loc.times_described % self.full_description_period
            loc.times_described += 1
            if do_short and loc.short_description:
                self.write(loc.short_description)
            else:
                self.write(loc.long_description)

        if loc.is_forced:
            self.do_motion(self.vocabulary[2])  # dummy motion verb
            return

        if loc.n == 33 and self.random() < .25 and not self.is_closing:
            self.write_message(8)

        # for obj in self.objects.values():
        #     if obj.rooms and [room.n for room in obj.rooms] != [115]:
        #         if (len(obj.messages) == 0) or (0 not in obj.messages):
        #             raise ValueError('%r %r' % (obj, obj.rooms))
        #             print(obj, obj.rooms)

        if not self.is_dark:
            for obj in self.objects_here:

                if obj is self.steps and self.gold.is_toting:
                    continue

                if obj.prop < 0:  # finding a treasure the first time
                    if self.is_closed:
                        continue
                    obj.prop = 1 if obj in (self.rug, self.chain) else 0
                    self.treasures_not_found -= 1
                    if (self.treasures_not_found > 0 and
                        self.treasures_not_found == self.impossible_treasures):
                        self.lamp_turns = min(35, self.lamp_turns)

                if obj is self.steps and self.loc is self.steps.rooms[1]:
                    prop = 1
                else:
                    prop = obj.prop

                self.write(obj.messages[prop])

        self.finish_turn()

    def say_okay_and_finish(self, *ignored):  #2009
        self.write_message(54)
        self.finish_turn()

    #2009 sets SPK="OK" then...
    #2010 sets SPK to K
    #2011 speaks SPK then...
    #2012 blanks VERB and OBJ and calls:
    def finish_turn(self, obj=None):  #2600

        # Advance random number generator so each input affects future.
        self.random()

        # Check whether we should offer a hint.
        for hint in self.hints.values():
            if hint.turns_needed == 9999 or hint.used:
                continue
            if self.loc in hint.rooms:
                hint.turn_counter += 1
                if hint.turn_counter >= hint.turns_needed:
                    if hint.n != 5:  # hint 5 counter does not get reset
                        hint.turn_counter = 0
                    if self.should_offer_hint(hint, obj):
                        hint.turn_counter = 0

                        def callback(yes):
                            if yes:
                                self.write(hint.message)
                                hint.used = True
                            else:
                                self.write_message(54)

                        self.yesno(hint.question, callback)
                        return
            else:
                hint.turn_counter = 0

        if self.is_closed:
            if self.oyster.prop < 0 and self.oyster.is_toting:
                self.write(self.oyster.messages[1])
            for obj in self.inventory:
                if obj.prop < 0:
                    obj.prop = - 1 - obj.prop

        self.could_fall_in_pit = self.is_dark  #2605
        if self.knife_location and self.knife_location is not self.loc:
            self.knife_location = None

    # The central do_command() method, that should be called over and
    # over again with words supplied by the user.

    def do_command(self, words):
        """Parse and act upon the command in the list of strings `words`."""
        self.output = ''
        self._do_command(words)
        return self.output

    def _do_command(self, words):
        if self.yesno_callback is not None:
            answer = YESNO_ANSWERS.get(words[0], None)
            if answer is None:
                if self.yesno_casual:
                    self.yesno_callback = None
                else:
                    self.write('Please answer the question.')
                    return
            else:
                callback = self.yesno_callback
                self.yesno_callback = None
                callback(answer)
                return

        if self.is_dead:
            self.write('You have gotten yourself killed.')
            return

        #2608
        self.turns += 1
        if (self.treasures_not_found == 0
            and self.loc.n >= 15 and self.loc.n != 33):
            self.clock1 -= 1
            if self.clock1 == 0:
                self.start_closing_cave()  # no "return", to do their command
        if self.clock1 < 0:
            self.clock2 -= 1
            if self.clock2 == 0:
                return self.close_cave()  # "return", to cancel their command

        if self.lamp.prop == 1:
            self.lamp_turns -= 1

        if self.lamp_turns <= 30 and self.is_here(self.batteries) \
                and self.batteries.prop == 0 and self.is_here(self.lamp):
            #12000
            self.write_message(188)
            self.batteries.prop = 1
            if self.batteries.is_toting:
                self.batteries.drop(self.loc)
            self.lamp_turns += 2500
            self.warned_about_dim_lamp = False
        elif self.lamp_turns == 0:
            #12400
            self.lamp_turns = -1
            self.lamp.prop = 0
            if self.is_here(self.lamp):
                self.write_message(184)
        elif self.lamp_turns < 0 and self.loc.is_aboveground:
            #12600
            self.write_message(185)
            self.gave_up = True
            self.score_and_exit()
            return
        elif self.lamp_turns <= 30 and not self.warned_about_dim_lamp \
                and self.is_here(self.lamp):
            #12200
            self.warned_about_dim_lamp = True
            if self.batteries.prop == 1:
                self.write_message(189)
            elif not self.batteries.rooms:
                self.write_message(183)
            else:
                self.write_message(187)

        self.dispatch_command(words)

    def dispatch_command(self, words):  #19999

        if not 1 <= len(words) <= 2:
            return self.dont_understand()

        if words[0] == 'save' and len(words) > 1:
            # Handle suspend separately, since filename can be anything,
            # and is not restricted to being a vocabulary word (and, in
            # fact, it can be an open file).
            return self.t_suspend(words[0], words[1])

        words = [ self.vocabulary.get(word) for word in words ]
        if None in words:
            return self.dont_understand()

        word1 = words[0]
        word2 = words[1] if len(words) == 2 else None

        if word1 == 'enter' and (word2 == 'stream' or word2 == 'water'):
            if self.loc.liquid is self.water:
                self.write_message(70)
            else:
                self.write_message(43)
            return self.finish_turn()

        if (word1 == 'enter' or word1 == 'walk') and word2:
            #2800  'enter house' becomes simply 'house' and so forth
            word1, word2 = word2, None

        if ((word1 == 'water' or word1 == 'oil') and
            (word2 == 'plant' or word2 == 'door') and
            self.is_here(self.referent(word2))):
            word1, word2 = self.vocabulary['pour'], word1

        if word1 == 'say':
            return self.t_say(word1, word2) if word2 else self.i_say(word1)

        if word2 == 'say':
            return self.t_say(word2, word1)

        kinds = (word1.kind, word2.kind if word2 else None)

        #2630
        if kinds == ('travel', None):
            if word1.text == 'west':  #2610
                self.full_wests += 1
                if self.full_wests == 10:
                    self.write_message(17)
            return self.do_motion(word1)

        if kinds == ('snappy_comeback', None):
            self.write_message(word1.n % 1000)
            return self.finish_turn()

        if kinds == ('noun', None):
            verb, noun = None, word1
        elif kinds == ('verb', None):
            verb, noun = word1, None
        elif kinds == ('verb', 'noun'):
            verb, noun = word1, word2
        elif kinds == ('noun', 'verb'):
            noun, verb = word1, word2
        else:
            return self.dont_understand()

        if not noun:
            obj = None
        else:
            obj = self.referent(noun)
            obj_here = self.is_here(obj)
            if not obj_here:
                if obj is self.grate:
                    if self.loc.n in (1, 4, 7):
                        return self.dispatch_command([ 'depression' ])
                    elif 9 < self.loc.n < 15:
                        return self.dispatch_command([ 'entrance' ])
                elif noun == 'dwarf':
                    obj_here = any( d.room is self.loc for d in self.dwarves )
                elif obj is self.bottle.contents and self.is_here(self.bottle):
                    obj_here = True
                elif obj is self.loc.liquid:
                    obj_here = True
                elif (obj is self.plant and self.is_here(self.plant2)
                      and self.plant2.prop != 0):
                    obj = self.plant2
                    obj_here = True
                elif obj is self.knife and self.knife_location is self.loc:
                    self.knife_location = None
                    self.write_message(116)
                    return self.finish_turn()
                elif obj is self.rod and self.is_here(self.rod2):
                    obj = self.rod2
                    obj_here = True
                elif verb and (verb == 'find' or verb == 'inventory'):
                    obj_here = True  # lie; these verbs work for absent objects

            if not obj_here:
                return self.i_see_no(noun)

            if not verb:
                self.write('What do you want to do with the {}?\n'.format(
                        noun.text))
                return self.finish_turn()

        verb_name = verb.synonyms[0].text
        if obj:
            method_name = 't_' + verb_name
            args = (verb, obj)
        else:
            method_name = 'i_' + verb_name
            args = (verb,)
        method = getattr(self, method_name)
        method(*args)

    def dont_understand(self):
        #3000  (a bit earlier than in the Fortran code)
        n = self.random()
        if n < 0.20:    # 20% of the entire 1.0 range of random()
            self.write_message(61)
        elif n < 0.36:  # 20% of the remaining 0.8 left
            self.write_message(13)
        else:
            self.write_message(60)
        self.finish_turn()

    def i_see_no(self, thing):
        self.write('I see no {} here.\n'.format(getattr(thing, 'text', thing)))
        self.finish_turn()

    # Motion.

    def do_motion(self, word):  #8

        if word == 'null': #2
            self.move_to()
            return

        elif word == 'back':  #20
            dest = self.oldloc2 if self.oldloc.is_forced else self.oldloc
            self.oldloc2, self.oldloc = self.oldloc, self.loc
            if dest is self.loc:
                self.write_message(91)
                self.move_to()
                return
            alt = None
            for move in self.loc.travel_table:
                if move.action is dest:
                    word = move.verbs[0]  # arbitrary verb going to `dest`
                    break # Fall through, to attempt the move.
                elif (isinstance(move.action, Room)
                      and move.action.is_forced
                      and move.action.travel_table[0].action is dest):
                    alt = move.verbs[0]
            else:  # no direct route is available
                if alt is not None:
                    word = alt  # take a forced move if it's the only option
                else:
                    self.write_message(140)
                    self.move_to()
                    return

        elif word == 'look':  #30
            if self.look_complaints > 0:
                self.write_message(15)
                self.look_complaints -= 1
            self.loc.times_described = 0
            self.move_to()
            self.could_fall_in_pit = False
            return

        elif word == 'cave':  #40
            self.write_message(57 if self.loc.is_aboveground else 58)
            self.move_to()
            return

        self.oldloc2, self.oldloc = self.oldloc, self.loc

        for move in self.loc.travel_table:
            if move.is_forced or word in move.verbs:
                c = move.condition

                if c[0] is None or c[0] == 'not_dwarf':
                    allowed = True
                elif c[0] == '%':
                    allowed = 100 * self.random() < c[1]
                elif c[0] == 'carrying':
                    allowed = self.objects[c[1]].is_toting
                elif c[0] == 'carrying_or_in_room_with':
                    allowed = self.is_here(self.objects[c[1]])
                elif c[0] == 'prop!=':
                    allowed = self.objects[c[1]].prop != c[2]

                if not allowed:
                    continue

                if isinstance(move.action, Room):
                    self.move_to(move.action)
                    return

                elif isinstance(move.action, Message):
                    self.write(move.action)
                    self.move_to()
                    return

                elif move.action == 301:  #30100
                    inv = self.inventory
                    if len(inv) != 0 and inv != [ self.emerald ]:
                        self.write_message(117)
                        self.move_to()
                    elif self.loc.n == 100:
                        self.move_to(self.rooms[99])
                    else:
                        self.move_to(self.rooms[100])
                    return

                elif move.action == 302:  #30200
                    self.emerald.drop(self.loc)
                    self.do_motion(word)
                    return

                elif move.action == 303:  #30300
                    troll, troll2 = self.troll, self.troll2
                    if troll.prop == 1:
                        self.write(troll.messages[1])
                        troll.prop = 0
                        troll.rooms = list(troll.starting_rooms)
                        troll2.destroy()
                        self.move_to()
                        return
                    else:
                        places = list(troll.starting_rooms)
                        places.remove(self.loc)
                        self.loc = places[0]  # "the other side of the bridge"
                        if troll.prop == 0:
                            troll.prop = 1
                        if not self.bear.is_toting:
                            self.move_to()
                            return
                        self.write_message(162)
                        self.chasm.prop = 1
                        troll.prop = 2
                        self.bear.drop(self.loc)
                        self.bear.is_fixed = True
                        self.bear.prop = 3
                        if self.spices.prop < 0:
                            self.impossible_treasures += 1
                        self.oldloc2 = self.loc  # refuse to strand belongings
                        self.die()
                        return

        #50
        n = word.n
        if 29 <= n <= 30 or 43 <= n <= 50:
            self.write_message(9)
        elif n in (7, 36, 37):
            self.write_message(10)
        elif n in (11, 19):
            self.write_message(11)
        elif n in (62, 65):
            self.write_message(42)
        elif n == 17:
            self.write_message(80)
        else:
            self.write_message(12)
        self.move_to()
        return

    # Death and reincarnation.

    def die_here(self):  #90
        self.write_message(23)
        self.oldloc2 = self.loc
        self.die()

    def die(self):  #99
        self.deaths += 1
        self.is_dead = True

        if self.is_closing:
            self.write_message(131)
            self.score_and_exit()
            return

        def callback(yes):
            if yes:
                self.write_message(80 + self.deaths * 2)
                if self.deaths < self.max_deaths:
                    if self.bottle.contents is not None:
                        self.bottle.contents.hide()
                    self.is_dead = False
                    if self.lamp.is_toting:
                        self.lamp.prop = 0
                    for obj in self.inventory:
                        if obj is self.lamp:
                            obj.drop(self.rooms[1])
                        else:
                            obj.drop(self.oldloc2)
                    self.loc = self.rooms[3]
                    self.describe_location()
                    return
            else:
                self.write_message(54)
            self.score_and_exit()

        self.yesno(self.messages[79 + self.deaths * 2], callback)

    # Verbs.

    def ask_verb_what(self, verb, *args):  #8000
        self.write('{} What?\n'.format(verb.text))
        self.finish_turn()

    i_walk = ask_verb_what
    i_drop = ask_verb_what
    i_say = ask_verb_what
    i_nothing = say_okay_and_finish
    i_wave = ask_verb_what
    i_calm = ask_verb_what
    i_rub = ask_verb_what
    i_throw = ask_verb_what
    i_find = ask_verb_what
    i_feed = ask_verb_what
    i_break = ask_verb_what
    i_wake = ask_verb_what

    def write_default_message(self, verb, *args):
        self.write(verb.default_message)
        self.finish_turn()

    t_nothing = say_okay_and_finish
    t_calm = write_default_message
    t_quit = write_default_message
    t_score = write_default_message
    t_fee = write_default_message
    t_brief = write_default_message
    t_hours = write_default_message
    t_walk = write_default_message

    def i_carry(self, verb):  #8010
        is_dwarf_here = any( dwarf.room == self.loc for dwarf in self.dwarves )
        objs = self.objects_here
        if len(objs) != 1 or is_dwarf_here:
            self.ask_verb_what(verb)
        else:
            self.t_carry(verb, objs[0])

    def t_carry(self, verb, obj):  #9010
        if obj.is_toting:
            self.write(verb.default_message)
            self.finish_turn()
            return
        if obj.is_fixed or len(obj.rooms) > 1:
            if obj is self.plant and obj.prop <= 0:
                self.write_message(115)
            elif obj is self.bear and obj.prop == 1:
                self.write_message(169)
            elif obj is self.chain and self.chain.prop != 0:
                self.write_message(170)
            else:
                self.write_message(25)
            self.finish_turn()
            return
        if obj is self.water or obj is self.oil:
            if self.is_here(self.bottle) and self.bottle.contents is obj:
                # They want to carry the filled bottle.
                obj = self.bottle
            else:
                # They must mean they want to fill the bottle.
                if not self.bottle.is_toting:
                    self.write_message(104)
                elif self.bottle.contents is not None:
                    self.write_message(105)
                else:
                    self.t_fill(verb, self.bottle)  # hand off control to "fill"
                    return
                self.finish_turn()
                return
        if len(self.inventory) >= 7:
            self.write_message(92)
            self.finish_turn()
            return
        if obj is self.bird and obj.prop == 0:
            if self.rod.is_toting:
                self.write_message(26)
                self.finish_turn(obj)  # needs obj to decide to give hint
                return
            if not self.cage.is_toting:
                self.write_message(27)
                self.finish_turn()
                return
            self.bird.prop = 1
        if (obj is self.bird or obj is self.cage) and self.bird.prop != 0:
            self.bird.carry()
            self.cage.carry()
        else:
            obj.carry()
            if obj is self.bottle and self.bottle.contents is not None:
                self.bottle.contents.carry()
        self.say_okay_and_finish()

    def t_drop(self, verb, obj):  #9020
        if obj is self.rod and not self.rod.is_toting and self.rod2.is_toting:
            obj = self.rod2

        if not obj.is_toting:
            self.write(verb.default_message)
            self.finish_turn()
            return

        bird, snake, dragon, bear, troll = self.bird, self.snake, self.dragon, \
            self.bear, self.troll

        if obj is bird and self.is_here(snake):
            self.write_message(30)
            if self.is_closed:
                self.wake_repository_dwarves()
                return
            snake.prop = 1
            snake.destroy()

        elif obj is self.coins and self.is_here(self.machine):
            obj.destroy()
            self.batteries.drop(self.loc)
            self.write(self.batteries.messages[0])
            self.finish_turn()
            return

        elif obj is bird and self.is_here(dragon) and dragon.prop == 0:
            self.write_message(154)
            bird.destroy()
            bird.prop = 0
            if snake.rooms:
                self.impossible_treasures += 1
            self.finish_turn()
            return

        elif obj is bear and self.is_here(troll):
            self.write_message(163)
            troll.destroy()
            self.troll2.rooms = list(self.troll.starting_rooms)
            troll.prop = 2

        elif obj is self.vase and self.loc is not self.rooms[96]:
            if self.pillow.is_at(self.loc):
                self.vase.prop = 0
            else:
                self.vase.prop = 2
                self.vase.is_fixed = True
            self.write(self.vase.messages[self.vase.prop + 1])

        else:
            self.write_message(54)

        #9021
        if obj is self.bottle.contents:
            obj = self.bottle
        if obj is self.bottle and self.bottle.contents:
            self.bottle.contents.hide()
        if obj is self.cage and self.bird.prop != 0:
            bird.drop(self.loc)
        elif obj is self.bird:
            obj.prop = 0
        obj.drop(self.loc)
        self.finish_turn()
        return

    def t_say(self, verb, word):  #9030
        if word.n in (62, 65, 71, 2025):
            self.dispatch_command([ word.text ])
        else:
            self.write('Okay, "{}".'.format(word.text))
            self.finish_turn()

    def i_unlock(self, verb):  #8040  Handles "unlock" case as well
        objs = (self.grate, self.door, self.oyster, self.clam, self.chain)
        objs = list(filter(self.is_here, objs))
        if len(objs) > 1:
            self.ask_verb_what(verb)
        elif len(objs) == 1:
            self.t_unlock(verb, objs[0])
        else:
            self.write_message(28)
            self.finish_turn()

    i_lock = i_unlock

    def t_unlock(self, verb, obj):  #9040  Handles "lock" case as well
        if obj is self.clam or obj is self.oyster:
            #9046
            oy = 1 if (obj is self.oyster) else 0
            if verb == 'lock':
                self.write_message(61)
            elif not self.trident.is_toting:
                self.write_message(122 + oy)
            elif obj.is_toting:
                self.write_message(120 + oy)
            elif obj is self.oyster:
                self.write_message(125)
            else:
                self.write_message(124)
                self.clam.destroy()
                self.oyster.drop(self.loc)
                self.pearl.drop(self.rooms[105])
        elif obj is self.door:
            if obj.prop == 1:
                self.write_message(54)
            else:
                self.write_message(111)
        elif obj is self.cage:
            self.write_message(32)
        elif obj is self.keys:
            self.write_message(55)
        elif obj is self.grate or obj is self.chain:
            if not self.is_here(self.keys):
                self.write_message(31)
            elif obj is self.chain:
                #9048
                if verb == 'unlock':
                    if self.chain.prop == 0:
                        self.write_message(37)
                    elif self.bear.prop == 0:
                        self.write_message(41)
                    else:
                        self.chain.prop = 0
                        self.chain.is_fixed = False
                        if self.bear.prop != 3:
                            self.bear.prop = 2
                        self.bear.is_fixed = 2 - self.bear.prop
                        self.write_message(171)
                else:
                    #9049
                    if self.loc not in self.chain.starting_rooms:
                        self.write_message(173)
                    elif self.chain.prop != 0:
                        self.write_message(34)
                    else:
                        self.chain.prop = 2
                        if self.chain.is_toting:
                            self.chain.drop(self.loc)
                        self.chain.is_fixed = True
                        self.write_message(172)
            elif self.is_closing:
                if not self.panic:
                    self.clock2 = 15
                    self.panic = True
                self.write_message(130)
            else:
                #9043
                oldprop = obj.prop
                obj.prop = 0 if verb == 'lock' else 1
                self.write_message(34 + oldprop + 2 * obj.prop)
        else:
            self.write(verb.default_message)
        self.finish_turn()

    t_lock = t_unlock

    def t_light(self, verb, obj=None):  #9070
        if not self.is_here(self.lamp):
            self.write(verb.default_message)
        elif self.lamp_turns <= 0:
            self.write_message(184)
        else:
            self.lamp.prop = 1
            self.write_message(39)
            if self.loc.is_dark:
                return self.describe_location()
        self.finish_turn()

    i_light = t_light

    def t_extinguish(self, verb, obj=None):  #9080
        if not self.is_here(self.lamp):
            self.write(verb.default_message)
        else:
            self.lamp.prop = 0
            self.write_message(40)
            if self.loc.is_dark:
                self.write_message(16)
        self.finish_turn()

    i_extinguish = t_extinguish

    def t_wave(self, verb, obj):  #9090
        fissure = self.fissure

        if (obj is self.rod and obj.is_toting and self.is_here(fissure)
            and not self.is_closing):
            fissure.prop = 0 if fissure.prop else 1
            self.write(fissure.messages[2 - fissure.prop])
        else:
            if obj.is_toting or (obj is self.rod and self.rod2.is_toting):
                self.write(verb.default_message)
            else:
                self.write_message(29)

        self.finish_turn()

    def i_attack(self, verb):  #9120
        enemies = [ self.snake, self.dragon, self.troll, self.bear ]
        if self.dwarf_stage >= 2:
            enemies.extend(self.dwarves)
        dangers = list(filter(self.is_here, enemies))
        if len(dangers) > 1:
            return self.ask_verb_what(verb)
        if len(dangers) == 1:
            return self.t_attack(verb, dangers[0])
        targets = []
        if self.is_here(self.bird) and verb != 'throw':
            targets.append(self.bird)
        if self.is_here(self.clam) or self.is_here(self.oyster):
            targets.append(self.clam)
        if len(targets) > 1:
            return self.ask_verb_what(verb)
        elif len(targets) == 1:
            return self.t_attack(verb, targets[0])
        else:
            return self.t_attack(verb, None)

    def t_attack(self, verb, obj):  #9124  (but control goes to 9120 first)
        if obj is self.bird:
            if self.is_closed:
                self.write_message(137)
            else:
                obj.destroy()
                obj.prop = 0
                if self.snake.rooms:
                    self.impossible_treasures += 1
                self.write_message(45)
        elif obj is self.clam or obj is self.oyster:
            self.write_message(150)
        elif obj is self.snake:
            self.write_message(46)
        elif obj is self.dwarf:
            if self.is_closed:
                self.wake_repository_dwarves()
                return
            self.write_message(49)
        elif obj is self.dragon:
            if self.dragon.prop != 0:
                self.write_message(167)
            else:
                def callback(yes):
                    self.write(obj.messages[1])
                    obj.prop = 2
                    obj.is_fixed = True
                    oldroom1 = obj.rooms[0]
                    oldroom2 = obj.rooms[1]
                    newroom = self.rooms[ (oldroom1.n + oldroom2.n) // 2 ]
                    obj.drop(newroom)
                    self.rug.prop = 0
                    self.rug.is_fixed = False
                    self.rug.drop(newroom)
                    for oldroom in (oldroom1, oldroom2):
                        for o in self.objects_at(oldroom):
                            o.drop(newroom)
                    self.move_to(newroom)
                self.yesno(self.messages[49], callback, casual=True)
                return
        elif obj is self.troll:
            self.write_message(157)
        elif obj is self.bear:
            self.write_message(165 + (self.bear.prop + 1) // 2)
        else:
            self.write_message(44)
        self.finish_turn()

    def i_pour(self, verb):  #9130
        if self.bottle.contents is None:
            self.ask_verb_what(verb)
        else:
            self.t_pour(verb, self.bottle.contents)

    def t_pour(self, verb, obj):
        if obj is self.bottle:
            return self.i_pour(verb)
        if not obj.is_toting:
            self.write(verb.default_message)
        elif obj is not self.oil and obj is not self.water:
            self.write_message(78)
        else:
            self.bottle.prop = 1
            self.bottle.contents = None
            obj.hide()
            if self.is_here(self.plant):
                if obj is not self.water:
                    self.write_message(112)
                else:
                    self.write(self.plant.messages[self.plant.prop + 1])
                    self.plant.prop = (self.plant.prop + 2) % 6
                    self.plant2.prop = self.plant.prop // 2
                    return self.move_to()
            elif self.is_here(self.door):
                #9132
                self.door.prop = 1 if obj is self.oil else 0
                self.write_message(113 + self.door.prop)
            else:
                self.write_message(77)
        return self.finish_turn()

    def i_eat(self, verb):  #8140
        if self.is_here(self.food):
            self.t_eat(verb, self.food)
        else:
            self.ask_verb_what(verb)

    def t_eat(self, verb, obj):  #9140
        if obj is self.food:
            #8142
            self.food.destroy()
            self.write_message(72)
        elif obj in (self.bird, self.snake, self.clam, self.oyster,
                     self.dwarf, self.dragon, self.troll, self.bear):
            self.write_message(71)
        else:
            self.write(verb.default_message)
        self.finish_turn()

    def i_drink(self, verb):  #9150
        if self.is_here(self.water) or self.loc.liquid is self.water:
            self.t_drink(verb, self.water)
        else:
            self.ask_verb_what(verb)

    def t_drink(self, verb, obj):  #9150
        if obj is not self.water:
            self.write_message(110)
        elif self.is_here(self.water):
            self.bottle.prop = 1
            self.bottle.contents = None
            self.water.destroy()
            self.write_message(74)
        elif self.loc.liquid is self.water:
            self.write(verb.default_message)
        self.finish_turn()

    def t_rub(self, verb, obj):  #9160
        if obj is self.lamp:
            self.write(verb.default_message)
        else:
            self.write_message(71)
        self.finish_turn()

    def t_throw(self, verb, obj):  #9170
        if obj is self.rod and not self.rod.is_toting and self.rod2.is_toting:
            obj = self.rod2

        if not obj.is_toting:
            self.write(verb.default_message)
            self.finish_turn()
            return

        if obj.is_treasure and self.is_here(self.troll):
            # Pay the troll toll
            self.write_message(159)
            obj.destroy()
            self.troll.destroy()
            self.troll2.rooms = list(self.troll.starting_rooms)
            self.finish_turn()
            return

        if obj is self.food and self.is_here(self.bear):
            self.t_feed(verb, self.bear)
            return

        if obj is not self.axe:
            self.t_drop(verb, obj)
            return

        dwarves_here = [ d for d in self.dwarves if d.room is self.loc ]
        if dwarves_here:
            # 1/3rd chance that throwing the axe kills a dwarf
            if self.choice((True, False, False)):
                self.dwarves.remove(dwarves_here[0])
                self.dwarves_killed += 1
                if self.dwarves_killed == 1:
                    self.write_message(149)
                else:
                    self.write_message(47)
            else:
                self.write_message(48)  # Miss
            self.axe.drop(self.loc)
            self.do_motion(self.vocabulary['null'])
            return

        if self.is_here(self.dragon) and self.dragon.prop == 0:
            self.write_message(152)
            self.axe.drop(self.loc)
            self.do_motion(self.vocabulary['null'])
            return

        if self.is_here(self.troll):
            self.write_message(158)
            self.axe.drop(self.loc)
            self.do_motion(self.vocabulary['null'])
            return

        if self.is_here(self.bear) and self.bear.prop == 0:
            self.write_message(164)
            self.axe.drop(self.loc)
            self.axe.is_fixed = True
            self.axe.prop = 1
            self.finish_turn()
            return

        self.t_attack(verb, None)

    def i_quit(self, verb):  #8180
        def callback(yes):
            self.write_message(54)
            if yes:
                self.score_and_exit()
        self.yesno(self.messages[22], callback)

    def t_find(self, verb, obj):  #9190
        if obj.is_toting:
            self.write_message(24)
        elif self.is_closed:
            self.write_message(138)
        elif (self.is_here(obj) or
            obj is self.loc.liquid or
            obj is self.dwarf and any(d.room is self.loc for d in self.dwarves)):
            self.write_message(94)
        else:
            self.write(verb.default_message)
        self.finish_turn()

    t_inventory = t_find

    def i_inventory(self, verb):  #8200
        first = True
        objs = [ obj for obj in self.inventory if obj is not self.bear ]
        for obj in objs:
            if first:
                self.write_message(99)
                first = False
            self.write(obj.inventory_message)
        if self.bear.is_toting:
            self.write_message(141)
        if not objs:
            self.write_message(98)
        self.finish_turn()

    def t_feed(self, verb, obj):  #9210
        if obj is self.bird:
            self.write_message(100)
        elif obj is self.troll:
            self.write_message(182)
        elif obj is self.dragon:
            if self.dragon.prop != 0:
                self.write_message(110)
            else:
                self.write_message(102)
        elif obj is self.snake:
            if self.is_closed or not self.is_here(self.bird):
                self.write_message(102)
            else:
                self.write_message(101)
                self.bird.destroy()
                self.bird.prop = 0
                self.impossible_treasures += 1
        elif obj is self.dwarf:
            if self.is_here(self.food):
                self.write_message(103)
                self.dwarf_stage += 1
            else:
                self.write(verb.default_message)
        elif obj is self.bear:
            if not self.is_here(self.food):
                if self.bear.prop == 0:
                    self.write_message(102)
                elif self.bear.prop == 3:
                    self.write_message(110)
                else:
                    self.write(verb.default_message)
            else:
                self.food.destroy()
                self.bear.prop = 1
                self.axe.is_fixed = False
                self.axe.prop = 0
                self.write_message(168)
        else:
            self.write_message(14)
        self.finish_turn()

    def i_fill(self, verb):  #9220
        if self.is_here(self.bottle):
            return self.t_fill(verb, self.bottle)
        self.ask_verb_what(verb)

    def t_fill(self, verb, obj):
        if obj is self.bottle:
            liquid = self.loc.liquid
            if liquid is None:
                self.write_message(106)
            elif self.bottle.contents:
                self.write_message(105)
            else:
                self.bottle.contents = liquid
                self.bottle.prop = 0 if (liquid is self.water) else 2
                if self.bottle.is_toting:
                    liquid.is_toting = True
                if liquid is self.oil:
                    self.write_message(108)
                else:
                    self.write_message(107)
        elif obj is self.vase:
            #9222
            if self.vase.is_toting:
                if self.loc.liquid is None:
                    self.write_message(144)
                else:
                    self.write_message(145)
                    self.vase.drop(self.loc)
                    self.vase.prop = 2
                    self.vase.is_fixed = True
            else:
                self.write(verb.default_message)
        else:
            self.write(verb.default_message)
        self.finish_turn()

    def t_blast(self, verb, obj=None):  #9230
        if self.rod2.prop < 0 or not self.is_closed:
            self.write(verb.default_message)
            self.finish_turn()
            return
        if self.is_here(self.rod2):
            self.bonus = 135
        elif self.loc.n == 115:
            self.bonus = 134
        else:
            self.bonus = 133
        self.write_message(self.bonus)
        self.score_and_exit()

    i_blast = t_blast

    def i_score(self, verb):  #8240
        score, max_score = self.compute_score(for_score_command=True)
        self.write('If you were to quit now, you would score {}'
                   ' out of a possible {}.\n'.format(score, max_score))
        def callback(yes):
            self.write_message(54)
            if yes:
                self.score_and_exit()
        self.yesno(self.messages[143], callback)

    def i_fee(self, verb):  #8250
        for n in range(5):
            if verb.synonyms[n].text == verb.text:
                break  # so that 0=fee, 1=fie, 2=foe, 3=foo, 4=fum
        if n == 0:
            self.foobar = self.turns
            self.write_message(54)
        elif n != self.turns - self.foobar:
            self.write_message(151)
        elif n < 3:
            self.write_message(54)
        else:
            self.foobar = -1
            eggs = self.eggs
            start = eggs.starting_rooms[0]
            if (eggs.is_at(start) or eggs.is_toting and self.loc is start):
                self.write_message(54)
            else:
                troll = self.troll
                if not eggs.rooms and not troll.rooms and not troll.prop:
                    self.troll.prop = 1
                if self.loc is start:
                    self.write(eggs.messages[0])
                elif self.is_here(eggs):
                    self.write(eggs.messages[1])
                else:
                    self.write(eggs.messages[2])
                eggs.rooms = list(eggs.starting_rooms)
                eggs.is_toting = False
        self.finish_turn()

    def i_brief(self, verb):  #8260
        self.write_message(156)
        self.full_description_period = 10000
        self.look_complaints = 0
        self.finish_turn()

    def i_read(self, verb):  #8270
        if self.is_closed and self.oyster.is_toting:
            return self.t_read(verb, self.oyster)
        objs = (self.magazine, self.tablet, self.message)
        objs = list(filter(self.is_here, objs))
        if len(objs) != 1 or self.is_dark:
            self.ask_verb_what(verb)
        else:
            self.t_read(verb, objs[0])

    def t_read(self, verb, obj):  #9270
        if self.is_dark:
            return self.i_see_no(obj.names[0])
        elif (obj is self.oyster and not self.hints[2].used and
              self.oyster.is_toting):
            def callback(yes):
                if yes:
                    self.hints[2].used = True
                    self.write_message(193)
                else:
                    self.write_message(54)
            self.yesno(self.messages[192], callback)
        elif obj is self.oyster and self.hints[2].used:
            self.write_message(194)
        elif obj is self.message:
            self.write_message(191)
        elif obj is self.tablet:
            self.write_message(196)
        elif obj is self.magazine:
            self.write_message(190)
        else:
            self.write(verb.default_message)
        self.finish_turn()

    def t_break(self, verb, obj):  #9280
        if obj is self.vase and self.vase.prop == 0:
            self.write_message(198)
            if self.vase.is_toting:
                self.vase.drop(self.loc)
            self.vase.prop = 2
            self.vase.is_fixed = True
        elif obj is self.mirror and self.is_closed:
            self.write_message(197)
            self.wake_repository_dwarves()
            return
        elif obj is self.mirror:
            self.write_message(148)
        else:
            self.write(verb.default_message)
        self.finish_turn()

    def t_wake(self, verb, obj):  #9290
        if obj is self.dwarf and self.is_closed:
            self.write_message(199)
            self.wake_repository_dwarves()
        else:
            self.write(verb.default_message)
            self.finish_turn()

    def i_suspend(self, verb):
        self.write('Provide "{}" with a filename or open file'.format(
                verb.text))
        self.finish_turn()

    def t_suspend(self, verb, obj):
        if isinstance(obj, str):
            if os.path.exists(obj):  # pragma: no cover
                self.write('I refuse to overwrite an existing file.')
                return
            savefile = open(obj, 'wb')
        else:
            savefile = obj
        r = self.random_generator  # must replace live object with static state
        self.random_state = r.getstate()
        try:
            del self.random_generator
            savefile.write(zlib.compress(pickle.dumps(self), 9))
        finally:
            self.random_generator = r
            if savefile is not obj:
                savefile.close()
        self.write('Game saved')

    def i_hours(self, verb):
        self.write('Open all day')

    @classmethod
    def resume(self, obj):
        """Returns an Adventure game saved to the given file."""
        if isinstance(obj, str):
            savefile = open(obj, 'rb')
        else:
            savefile = obj
        game = pickle.loads(zlib.decompress(savefile.read()))
        if savefile is not obj:
            savefile.close()
        # Reinstate the random number generator.
        game.random_generator = random.Random()
        game.random_generator.setstate(game.random_state)
        del game.random_state
        return game

    def should_offer_hint(self, hint, obj): #40000
        if hint.n == 4:  # cave
            return self.grate.prop == 0 and not self.is_here(self.keys)

        elif hint.n == 5:  # bird
            bird = self.bird
            return self.is_here(bird) and self.rod.is_toting and obj is bird

        elif hint.n == 6:  # snake
            return self.is_here(self.snake) and not self.is_here(self.bird)

        elif hint.n == 7:  # maze
            return (not len(self.objects_here) and
                    not len(self.objects_at(self.oldloc)) and
                    not len(self.objects_at(self.oldloc2)) and
                    len(self.inventory) > 1)

        elif hint.n == 8:  # dark
            return self.emerald.prop != 1 and self.platinum.prop != 1

        elif hint.n == 9:  # witt
            return True

    def start_closing_cave(self):  #10000
        self.grate.prop = 0
        self.fissure.prop = 0
        del self.dwarves[:]
        self.troll.destroy()
        self.troll2.rooms = list(self.troll.starting_rooms)
        if self.bear.prop != 3:
            self.bear.destroy()
        for obj in self.chain, self.axe:
            obj.prop = 0
            obj.is_fixed = False
        self.write_message(129)
        self.clock1 = -1
        self.is_closing = True

    def close_cave(self):  #11000
        ne = self.rooms[115]  # ne end of repository
        sw = self.rooms[116]
        for obj in (self.bottle, self.plant, self.oyster, self.lamp,
                    self.rod, self.dwarf):
            obj.prop = -2 if obj is self.bottle else -1
            obj.drop(ne)
        self.loc = self.oldloc = self.oldloc2 = ne
        for obj in (self.grate, self.snake, self.bird, self.cage,
                    self.rod2, self.pillow):
            obj.prop = -2 if (obj is self.bird or obj is self.snake) else -1
            obj.drop(sw)
        self.mirror.rooms = [ne, sw]
        self.mirror.is_fixed = 1
        self.is_closed = True
        for obj in self.inventory:
            obj.is_toting = False
        self.write_message(132)
        self.move_to()

    # TODO: 12000
    # TODO: 12200
    # TODO: 12400
    # TODO: 12600

    def wake_repository_dwarves(self):  #19000
        self.write_message(136)
        self.score_and_exit()

    def compute_score(self, for_score_command=False):  #20000
        score = maxscore = 2

        for treasure in self.treasures:
            # if ptext(0) is zero?
            if treasure.n > self.chest.n:
                value = 16
            elif treasure is self.chest:
                value = 14
            else:
                value = 12

            maxscore += value

            if treasure.prop >= 0:
                score += 2
            if treasure.rooms and treasure.rooms[0].n == 3 \
                    and treasure.prop == 0:
                score += value - 2

        maxscore += self.max_deaths * 10
        score += (self.max_deaths - self.deaths) * 10

        maxscore += 4
        if not for_score_command and not self.gave_up:
            score += 4

        maxscore += 25
        if self.dwarf_stage:
            score += 25

        maxscore += 25
        if self.is_closing:
            score += 25

        maxscore += 45
        if self.is_closed:
            score += {0: 10, 135: 25, 134: 30, 133: 45}[self.bonus]

        maxscore += 1
        if 108 in (room.n for room in self.magazine.rooms):
            score += 1

        for hint in list(self.hints.values()):
            if hint.used:
                score -= hint.penalty

        return score, maxscore

    def score_and_exit(self):
        score, maxscore = self.compute_score()
        self.write('\nYou scored {} out of a possible {} using {} turns.'
                   .format(score, maxscore, self.turns))
        for i, (minimum, text) in enumerate(self.class_messages):
            if minimum >= score:
                break
        self.write('\n{}\n'.format(text))
        if i < len(self.class_messages) - 1:
            d = self.class_messages[i+1][0] + 1 - score
            self.write('To achieve the next higher rating, you need'
                       ' {} more point{}\n'.format(d, 's' if d > 1 else ''))
        else:
            self.write('To achieve the next higher rating '
                       'would be a neat trick!\n\nCongratulations!!\n')
        self.is_done = True


================================================
FILE: adventure/model.py
================================================
"""Classes representing Adventure game components.

Copyright 2010-2015 Brandon Rhodes.  Licensed as free software under the
Apache License, Version 2.0 as detailed in the accompanying README.txt.

"""
class Move(object):
    """An entry in the travel table."""

    is_forced = False
    verbs = []
    condition = None
    action = None

    def __repr__(self):
        verblist = [ verb.text for verb in self.verbs ]

        c = self.condition[0]
        if c is None:
            condition = ''
        elif c == '%':
            condition = ' %d%% of the time' % self.condition[1]
        elif c == 'not_dwarf':
            condition = ' if not a dwarf'
        elif c == 'carrying':
            condition = ' if carrying %s' % self.condition[1]
        elif c == 'carrying_or_in_room_with':
            condition = ' if carrying or in room with %s' % self.condition[1]
        elif c == 'prop!=':
            condition = ' if prop %d != %d' % self.condition[1:]

        if isinstance(self.action, Room):
            action = 'moves to %r' % (self.action.short_description
                or self.action.long_description[:20]).strip()
        elif isinstance(self.action, Message):
            action = 'prints %r' % self.action.text
        else:
            action = 'special %d' % self.action

        return '<{}{} {}>'.format('|'.join(verblist), condition, action)

class Room(object):
    """A location in the game."""

    long_description = ''
    short_description = ''
    times_described = 0
    visited = False

    is_light = False
    is_forbidden_to_pirate = False
    liquid = None
    trying_to_get_into_cave = False
    trying_to_catch_bird = False
    trying_to_deal_with_snake = False
    lost_in_maze = False
    pondering_dark_room = False
    at_witts_end = False

    def __init__(self):
        self.travel_table = []

    def __repr__(self):
        return '<room {} at {}>'.format(self.n, hex(id(self)))

    @property
    def is_forced(self):
        return self.travel_table and self.travel_table[0].is_forced

    @property
    def is_aboveground(self):
        return 1 <= self.n <= 8

    @property
    def is_before_hall_of_mists(self):
        return self.n < 15

    @property
    def is_after_hall_of_mists(self):
        return self.n >= 15

    @property
    def is_dark(self):
        return not self.is_light

class Word(object):
    """A word that can be used as part of a command."""

    text = None
    kind = None
    default_message = None

    def __init__(self):
        self.synonyms = [ self ]

    def __repr__(self):
        return '<Word {}>'.format(self.text)

    def __eq__(self, text):
        return any( word.text == text for word in self.synonyms )

    def add_synonym(self, other):
        """Every word in a group of synonyms shares the same list."""
        self.synonyms.extend(other.synonyms)
        other.synonyms = self.synonyms

class Object(object):
    """An object in the game, like a grate, or a rod with a rusty star."""

    def __init__(self):
        self.is_fixed = False
        self.is_treasure = False
        self.inventory_message = ''
        self.messages = {}
        self.names = []
        self.prop = 0
        self.rooms = []
        self.starting_rooms = []
        self.is_toting = False
        self.contents = None  # so the bottle can hold things

    def __repr__(self):
        return '<Object %d %s %x>' % (self.n, '/'.join(self.names), id(self))

    def __hash__(self):
        return self.n

    def __eq__(self, other):
        return any( text == other for text in self.names )

    def is_at(self, room):
        return room in self.rooms

    def carry(self):
        self.rooms[:] = []
        self.is_toting = True

    def drop(self, room):
        self.rooms[:] = [ room ]
        self.is_toting = False

    def hide(self):
        self.rooms[:] = []
        self.is_toting = False

    def destroy(self):
        self.hide()

class Message(object):
    """A message for printing."""
    text = ''

    def __str__(self):
        return self.text

class Hint(object):
    """A hint offered if the player loiters in one area too long."""

    turns_needed = 0
    turn_counter = 0
    penalty = 0
    question = None
    message = None
    used = False

    def __init__(self):
        self.rooms = []

class Dwarf(object):
    is_dwarf = True
    is_pirate = False

    def __init__(self, room):
        self.start_at(room)
        self.has_seen_adventurer = False

    def start_at(self, room):
        self.room = room
        self.old_room = room

    def can_move(self, move):
        if not isinstance(move.action, Room):
            return False
        room = move.action
        return (room.is_after_hall_of_mists
                and not room.is_forced
                and not move.condition == ('%', 100))

class Pirate(Dwarf):
    is_dwarf = False
    is_pirate = True


================================================
FILE: adventure/prompt.py
================================================
"""Routines that install Adventure commands for the Python prompt.

Copyright 2010-2015 Brandon Rhodes.  Licensed as free software under the
Apache License, Version 2.0 as detailed in the accompanying README.txt.

"""
import inspect

class ReprTriggeredPhrase(object):
    """Command that happens when Python calls repr() to print them."""

    def __init__(self, game, words):
        self.game = game
        self.words = tuple(words)  # protect against caller changing list

    def __repr__(self):
        """Run this command and return the message that results."""
        output = self.game.do_command(self.words)
        return output.rstrip('\n') + '\n'

    def __call__(self, arg=None):
        """Return a compound command of several words, like `get(keys)`."""
        if arg is None:
            return self
        words = arg.words if isinstance(arg, ReprTriggeredPhrase) else (arg,)
        return ReprTriggeredPhrase(self.game, self.words + words)

    def __getattr__(self, name):
        return ReprTriggeredPhrase(self.game, self.words + (name,))


def install_words(game):
    # stack()[0] is this; stack()[1] is adventure.play(); so, stack()[2]
    namespace = inspect.stack()[2][0].f_globals
    words = [ k for k in game.vocabulary if isinstance(k, str) ]
    words.append('yes')
    words.append('no')
    for word in words:
        identifier = ReprTriggeredPhrase(game, [ word ])
        namespace[word] = identifier
        if len(word) > 5:
            namespace[word[:5]] = identifier


================================================
FILE: adventure/tests/__init__.py
================================================


================================================
FILE: adventure/tests/syntax.txt
================================================
>>> import adventure
>>> adventure.play(seed=2)
WELCOME TO ADVENTURE!!  WOULD YOU LIKE INSTRUCTIONS?
<BLANKLINE>
>>> no
YOU ARE STANDING AT THE END OF A ROAD BEFORE A SMALL BRICK BUILDING.
AROUND YOU IS A FOREST.  A SMALL STREAM FLOWS OUT OF THE BUILDING AND
DOWN A GULLY.
<BLANKLINE>
>>> brief
OKAY, FROM NOW ON I'LL ONLY DESCRIBE A PLACE IN FULL THE FIRST TIME
YOU COME TO IT.  TO GET THE FULL DESCRIPTION, SAY "LOOK".
<BLANKLINE>

This doctest exercises all of the different ways that people try to
invoke Adventure commands from the Python prompt.

Simple movement can be invoked simply by typing the direction, but some
players will try to call them as functions instead.

>>> s
YOU ARE IN A VALLEY IN THE FOREST BESIDE A STREAM TUMBLING ALONG A
ROCKY BED.
<BLANKLINE>
>>> n()
YOU'RE AT END OF ROAD AGAIN.
<BLANKLINE>

That pretty much exhausts the possibilities when a word is being used
alone as a command.  But when two words are being combined, there are
several different ways that they might be called!

One word can be used as a function and the second as an argument:

>>> goto(building)
YOU ARE INSIDE A BUILDING, A WELL HOUSE FOR A LARGE SPRING.
<BLANKLINE>
THERE ARE SOME KEYS ON THE GROUND HERE.
<BLANKLINE>
THERE IS A SHINY BRASS LAMP NEARBY.
<BLANKLINE>
THERE IS FOOD HERE.
<BLANKLINE>
THERE IS A BOTTLE OF WATER HERE.
<BLANKLINE>

Or, a period can be used to separate the words.  Note that nouns and
verbs can be in either order.

>>> get.keys
OK
<BLANKLINE>
>>> lamp.get
OK
<BLANKLINE>

Why do we support putting the noun and verb in either order?  Because
some users think of verbs as methods supported by the game's nouns, and
will format their commands like method calls:

>>> food.get()
OK
<BLANKLINE>


================================================
FILE: adventure/tests/test_commands.py
================================================
"""Test suite.

Copyright 2010-2015 Brandon Rhodes.  Licensed as free software under the
Apache License, Version 2.0 as detailed in the accompanying README.txt.

"""
from unittest import TestCase
from adventure import load_advent_dat
from adventure.game import Game

class CommandTest(TestCase):

    def setUp(self):
        game = Game()
        load_advent_dat(game)
        self.words = set(w.synonyms[0].text for w in game.vocabulary.values())
        self.words.remove('suspend')

    def test_intransitive_commands_should_not_throw_exceptions(self):
        for word in self.words:
            game = Game()
            load_advent_dat(game)
            game.start()
            game.do_command(['no'])  # WOULD YOU LIKE INSTRUCTIONS?
            game.do_command([word])

    def test_transitive_commands_should_not_throw_exceptions(self):
        for word in self.words:
            game = Game()
            load_advent_dat(game)
            game.start()
            game.do_command(['no'])  # WOULD YOU LIKE INSTRUCTIONS?
            game.do_command(['enter'])  # so we are next to lamp
            game.do_command([word, 'lamp'])


================================================
FILE: adventure/tests/test_data.py
================================================
"""Test suite.

Copyright 2010-2015 Brandon Rhodes.  Licensed as free software under the
Apache License, Version 2.0 as detailed in the accompanying README.txt.

"""
import unittest

class DataTest(unittest.TestCase):

    def setUp(self):
        from adventure.data import Data
        from adventure import load_advent_dat
        self.data = Data()
        load_advent_dat(self.data)

    def test_long_description(self):
        self.assertEqual(self.data.rooms[4].long_description, """\
YOU ARE IN A VALLEY IN THE FOREST BESIDE A STREAM TUMBLING ALONG A
ROCKY BED.
""")

    def test_long_description_expands_tabs(self):
        self.assertIn("ALMOST AS IF ALIVE.  A COLD WIND BLOWS",
                      self.data.rooms[15].long_description)

    def test_short_description(self):
        self.assertEqual(self.data.rooms[4].short_description,
                         "YOU'RE IN VALLEY.\n")

    def test_object_message_expands_tabs(self):
        self.assertEqual(self.data.objects[24].messages[5], """\
YOU'VE OVER-WATERED THE PLANT!  IT'S SHRIVELING UP!  IT'S, IT'S...
""")

    def test_hint(self):
        hint = self.data.hints[4]
        self.assertEqual(hint.turns_needed, 4)
        self.assertEqual(hint.penalty, 2)
        self.assertEqual(hint.question.text,
                         "ARE YOU TRYING TO GET INTO THE CAVE?\n")
        self.assertEqual(hint.message.text, """\
THE GRATE IS VERY SOLID AND HAS A HARDENED STEEL LOCK.  YOU CANNOT
ENTER WITHOUT A KEY, AND THERE ARE NO KEYS NEARBY.  I WOULD RECOMMEND
LOOKING ELSEWHERE FOR THE KEYS.
""")

class ReprTest(unittest.TestCase):

    def setUp(self):
        from adventure.data import Data
        from adventure import load_advent_dat
        self.data = Data()
        load_advent_dat(self.data)

    def assertMove(self, room_i, entry_i, s):
        r = repr(self.data.rooms[room_i].travel_table[entry_i]).strip()
        self.assertEqual(r, s)

    def test_move_repr_look_good(self):
        m = self.assertMove
        m(1, 0, '<road|west|upward moves to "YOU\'RE AT HILL IN ROAD.">')
        m(108, 0, '<east|north|south|ne|se|sw|nw|upward|d 95% of the time'
          ' prints \'YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND'
          ' UP BACK IN THE\\nMAIN PASSAGE.\\n\'>')
        m(61, 2, '<south if not a dwarf moves to \'YOU ARE IN A MAZE OF\'>')
        m(15, 3, '<upward|pit|steps|dome|passage|east if carrying 50 moves'
          ' to \'THE DOME IS UNCLIMBA\'>')
        m(19, 6, '<sw if carrying or in room with 11 moves to '
          '"YOU CAN\'T GET BY THE">')
        m(17, 2, '<forward if prop 12 != 1 moves to "YOU DIDN\'T MAKE IT.">')

    def test_move_repr_works_on_all_moves(self):
        for room in self.data.rooms.values():
            for i, move in enumerate(room.travel_table):
                try:
                    repr(move)
                except:  # pragma: no cover
                    print(room, i)
                    raise

    def test_room_repr(self):
        self.assertRegex(repr(self.data.rooms[64]), '<room 64 at .*>')

    def test_object_repr(self):
        self.assertRegex(repr(self.data.objects['chest']),
                         r'<Object 55 chest/box/treasure .*>')

    def test_word_repr(self):
        self.assertEqual(repr(self.data.vocabulary['eat']), '<Word eat>')


================================================
FILE: adventure/tests/test_walks.py
================================================
"""Test suite.

Copyright 2010-2015 Brandon Rhodes.  Licensed as free software under the
Apache License, Version 2.0 as detailed in the accompanying README.txt.

"""
import doctest
import os
import shutil
import tempfile

def load_tests(loader, tests, pattern):
    cd = ChdirTemp()
    tests.addTests(doctest.DocFileSuite(
        '../README.txt', optionflags=doctest.NORMALIZE_WHITESPACE,
        setUp=cd.setup, tearDown=cd.teardown))
    tests.addTests(doctest.DocFileSuite('syntax.txt'))
    tests.addTests(doctest.DocFileSuite('vignettes.txt'))
    tests.addTests(doctest.DocFileSuite('walkthrough1.txt'))
    tests.addTests(doctest.DocFileSuite('walkthrough2.txt'))
    tests.addTests(doctest.DocFileSuite('walkthrough3.txt'))
    return tests

class ChdirTemp(object):
    def setup(self, doctest_object):
        self.old_directory = os.getcwd()
        self.tmp_directory = tempfile.mkdtemp()
        os.chdir(self.tmp_directory)

    def teardown(self, doctest_object):
        os.chdir(self.old_directory)
        shutil.rmtree(self.tmp_directory)


================================================
FILE: adventure/tests/vignettes.txt
================================================
>>> import io
>>> from itertools import cycle, islice

This file tests the behavior of the Adventure game in all sorts of
specific situations that would be very tedious to arrange in actual
walkthroughs.  As the basis for these tests, we will begin by starting a
game, entering the building, and carrying the lamp.

>>> import adventure
>>> adventure.play(seed=3)
WELCOME TO ADVENTURE!!  WOULD YOU LIKE INSTRUCTIONS?
<BLANKLINE>
>>> no
YOU ARE STANDING AT THE END OF A ROAD BEFORE A SMALL BRICK BUILDING.
AROUND YOU IS A FOREST.  A SMALL STREAM FLOWS OUT OF THE BUILDING AND
DOWN A GULLY.
<BLANKLINE>
>>> brief
OKAY, FROM NOW ON I'LL ONLY DESCRIBE A PLACE IN FULL THE FIRST TIME
YOU COME TO IT.  TO GET THE FULL DESCRIPTION, SAY "LOOK".
<BLANKLINE>
>>> enter
YOU ARE INSIDE A BUILDING, A WELL HOUSE FOR A LARGE SPRING.
<BLANKLINE>
THERE ARE SOME KEYS ON THE GROUND HERE.
<BLANKLINE>
THERE IS A SHINY BRASS LAMP NEARBY.
<BLANKLINE>
THERE IS FOOD HERE.
<BLANKLINE>
THERE IS A BOTTLE OF WATER HERE.
<BLANKLINE>
>>> on
YOUR LAMP IS NOW ON.
<BLANKLINE>
>>> get(lamp)
OK
<BLANKLINE>
>>> for _room in adventure._game.rooms.values():
...     _room.times_described = 1  # to avoid long descriptions

Now we can save this game.

>>> savefile = io.BytesIO()
>>> save(savefile)
GAME SAVED
<BLANKLINE>

Now, all of the tests below can begin by calling this restart() function
to place them back at the beginning of the adventure in a known state.
If a room is specified, note that restart() performs two turns in the
room before handing back control: the first when it asks the game to
move the caller into that room, and the second when it calls look().

>>> def restart(room=None, dwarves=False, objects=(), randoms=None):
...     """Restart the whole Adventure game from our stringio file."""
...
...     global game
...     savefile.seek(0)
...     adventure.resume(savefile, quiet=True)
...     game = adventure._game
...     if room is not None:
...         _go_to(room)
...     room = game.loc
...     if not dwarves:
...         game.dwarf_stage = 2
...         del game.dwarves[:]
...     for obj in objects:
...         if isinstance(obj, adventure.prompt.ReprTriggeredPhrase):
...             obj = game.referent(game.vocabulary[obj.words[0]])
...         obj.drop(room)
...     if randoms is not None:
...         game.random = list(randoms).pop

>>> class go_to(object):
...     """Special command that warps us directly to a location."""
...
...     def __init__(self, room):
...         self.room = room
...     def __repr__(self):
...         game.output = ''
...         _go_to(self.room)
...         return game.output.rstrip('\n') + '\n'

>>> def _go_to(room):
...     game.move_to(game.rooms[room] if isinstance(room, int) else room)

>>> def quiet(*args):
...     """Run one or more commands without printing their result."""
...
...     for arg in args:
...         repr(arg)
...     return None

With those definitions complete, we can proceed with the actual tests!

---------------------------------------
You Won't Get It Down The Steps, Either
---------------------------------------

>>> restart(room=18)
>>> look
THIS IS A LOW ROOM WITH A CRUDE NOTE ON THE WALL.  THE NOTE SAYS,
"YOU WON'T GET IT UP THE STEPS".
<BLANKLINE>
THERE IS A LARGE SPARKLING NUGGET OF GOLD HERE!
<BLANKLINE>
>>> get(gold)
OK
<BLANKLINE>
>>> go_to(13)
YOU'RE IN BIRD CHAMBER.
<BLANKLINE>
A CHEERFUL LITTLE BIRD IS SITTING HERE SINGING.
<BLANKLINE>
>>> w
YOU'RE AT TOP OF SMALL PIT.
<BLANKLINE>
>>> d
YOU ARE AT THE BOTTOM OF THE PIT WITH A BROKEN NECK.
<BLANKLINE>
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?
<BLANKLINE>
THE TROLL IS NOWHERE TO BE SEEN.
<BLANKLINE>
>>> n
OK
<BLANKLINE>
<BLANKLINE>
YOU SCORED 53 OUT OF A POSSIBLE 350 USING 9 TURNS.
<BLANKLINE>
YOUR SCORE QUALIFIES YOU AS A NOVICE CLASS ADVENTURER.
<BLANKLINE>
TO ACHIEVE THE NEXT HIGHER RATING, YOU NEED 78 MORE POINTS
<BLANKLINE>
>>> look
YOU HAVE GOTTEN YOURSELF KILLED.
<BLANKLINE>

-------------------
Pirates and dwarves
-------------------

If the dwarves activate when we are standing in one of their starting
rooms, then does the dwarf at our location get moved successfully to the
gold nuggets room?

>>> restart(room=41, dwarves=True, randoms=[None, .9, .9, .99])
>>> [ _dwarf.room.n for _dwarf in game.dwarves ]
[19, 27, 33, 44, 64]
>>> e
A LITTLE DWARF JUST WALKED AROUND A CORNER, SAW YOU, THREW A LITTLE
AXE AT YOU WHICH MISSED, CURSED, AND RAN AWAY.
<BLANKLINE>
YOU ARE ON THE WEST SIDE OF THE FISSURE IN THE HALL OF MISTS.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
THERE ARE DIAMONDS HERE!
<BLANKLINE>
>>> [ _dwarf.room.n for _dwarf in game.dwarves ]
[19, 18, 33, 44, 64]

If the pirate encounters us in the plover room or dark room, does he
leave the platinum pyramid since learning how to remove it from the room
is one of the game's puzzles?

>>> restart(room=100, objects=[pyramid])
>>> look
YOU'RE IN A SMALL CHAMBER LIT BY AN EERIE GREEN LIGHT.  AN EXTREMELY
NARROW TUNNEL EXITS TO THE WEST.  A DARK CORRIDOR LEADS NE.
<BLANKLINE>
THERE IS AN EMERALD HERE THE SIZE OF A PLOVER'S EGG!
<BLANKLINE>
THERE IS A PLATINUM PYRAMID HERE, 8 INCHES ON A SIDE!
<BLANKLINE>
>>> get(pyramid)
OK
<BLANKLINE>
>>> get(emerald)
OK
<BLANKLINE>
>>> game.pirate.room = game.loc
>>> look
OUT FROM THE SHADOWS BEHIND YOU POUNCES A BEARDED PIRATE!  "HAR, HAR,"
HE CHORTLES, "I'LL JUST TAKE ALL THIS BOOTY AND HIDE IT AWAY WITH ME
CHEST DEEP IN THE MAZE!"  HE SNATCHES YOUR TREASURE AND VANISHES INTO
THE GLOOM.
<BLANKLINE>
YOU'RE IN A SMALL CHAMBER LIT BY AN EERIE GREEN LIGHT.  AN EXTREMELY
NARROW TUNNEL EXITS TO THE WEST.  A DARK CORRIDOR LEADS NE.
<BLANKLINE>
>>> inventory
YOU ARE CURRENTLY HOLDING THE FOLLOWING:
<BLANKLINE>
BRASS LANTERN
PLATINUM PYRAMID
<BLANKLINE>

If we manage to collect all of the treasures before the pirate spots us,
then when we find him he goes to hide his treasure chest in the maze
without any other treasures to accompany it.

>>> restart()
>>> for t in game.treasures:
...     if t == 'chest': continue
...     t.drop(game.loc)
>>> quiet(look)
>>> game.dwarf_stage = 2
>>> game.loc = game.pirate.room = game.rooms[69]
>>> look
THERE ARE FAINT RUSTLING NOISES FROM THE DARKNESS BEHIND YOU.  AS YOU
TURN TOWARD THEM, THE BEAM OF YOUR LAMP FALLS ACROSS A BEARDED PIRATE.
HE IS CARRYING A LARGE CHEST.  "SHIVER ME TIMBERS!" HE CRIES, "I'VE
BEEN SPOTTED!  I'D BEST HIE MESELF OFF TO THE MAZE TO HIDE ME CHEST!"
WITH THAT, HE VANISHES INTO THE GLOOM.
<BLANKLINE>
YOU ARE IN A SECRET N/S CANYON ABOVE A LARGE ROOM.
<BLANKLINE>
>>> go_to(game.chest_room)
DEAD END
<BLANKLINE>
THE PIRATE'S TREASURE CHEST IS HERE!
<BLANKLINE>

If several dwarves throw knives that hit us, does a sensible message result?

>>> restart(room=75, dwarves=True, randoms=(.05,.05,.05,.05,.05,.05))
>>> game.dwarf_stage = 3
>>> for _dwarf in game.dwarves:
...     _dwarf.room = game.rooms[75]
>>> look
THERE ARE 5 THREATENING LITTLE DWARVES IN THE ROOM WITH YOU.
<BLANKLINE>
5 OF THEM THROW KNIVES AT YOU!
<BLANKLINE>
5 OF THEM GET YOU!
<BLANKLINE>
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?
<BLANKLINE>

If, when a treasure is found, the game figures out that all remaining
treasures are impossible to acquire - like the jewelry in the south side
chamber of the Hall of the Mountain King, if the bird dies before the
snake is driven away - then the lamp should be reduced to only having 35
turns left.

>>> restart()
>>> for t in game.treasures:
...     if t == 'jewelry' or t == 'silver': continue
...     t.drop(game.loc)
>>> quiet(look)
>>> game.bird.carry()
>>> go_to(119)
YOU ARE IN A SECRET CANYON WHICH EXITS TO THE NORTH AND EAST.
<BLANKLINE>
A HUGE GREEN FIERCE DRAGON BARS THE WAY!
<BLANKLINE>
>>> drop(bird)
THE LITTLE BIRD ATTACKS THE GREEN DRAGON, AND IN AN ASTOUNDING FLURRY
GETS BURNT TO A CINDER.  THE ASHES BLOW AWAY.
<BLANKLINE>
>>> game.lamp_turns
326
>>> go_to(28)
YOU ARE IN A LOW N/S PASSAGE AT A HOLE IN THE FLOOR.  THE HOLE GOES
DOWN TO AN E/W PASSAGE.
<BLANKLINE>
THERE ARE BARS OF SILVER HERE!
<BLANKLINE>
>>> game.lamp_turns
35

We should not be able to pick up a dwarf! (#21)

>>> restart(room=75, dwarves=True, randoms=(1,.05,.05,.05,.05,.05,.3))
>>> game.dwarf_stage = 3
>>> game.dwarves[0].room = game.rooms[75]
>>> take(dwarf)
YOU CAN'T BE SERIOUS!
<BLANKLINE>

----------------------------------
Making sure that hints are offered
----------------------------------

If you act confused, the game should offer you a hint.  Here are quick
(but certainly not thorough!) tests of whether the logic for each hint
allows it to be offered.

>>> restart(room=8)
>>> quiet(look, look)
>>> look
YOU ARE IN A 20-FOOT DEPRESSION FLOORED WITH BARE DIRT.  SET INTO THE
DIRT IS A STRONG STEEL GRATE MOUNTED IN CONCRETE.  A DRY STREAMBED
LEADS INTO THE DEPRESSION.
<BLANKLINE>
THE GRATE IS LOCKED.
<BLANKLINE>
ARE YOU TRYING TO GET INTO THE CAVE?
<BLANKLINE>
>>> yes
THE GRATE IS VERY SOLID AND HAS A HARDENED STEEL LOCK.  YOU CANNOT
ENTER WITHOUT A KEY, AND THERE ARE NO KEYS NEARBY.  I WOULD RECOMMEND
LOOKING ELSEWHERE FOR THE KEYS.
<BLANKLINE>

>>> restart(room=13, objects=[rod])
>>> quiet(get(rod), look, look)
>>> get(bird)
THE BIRD WAS UNAFRAID WHEN YOU ENTERED, BUT AS YOU APPROACH IT BECOMES
DISTURBED AND YOU CANNOT CATCH IT.
<BLANKLINE>
ARE YOU TRYING TO CATCH THE BIRD?
<BLANKLINE>
>>> no
OK
<BLANKLINE>

>>> restart(room=19)
>>> quiet(*[ look ] * 6)
>>> look
YOU ARE IN THE HALL OF THE MOUNTAIN KING, WITH PASSAGES OFF IN ALL
DIRECTIONS.
<BLANKLINE>
A HUGE GREEN FIERCE SNAKE BARS THE WAY!
<BLANKLINE>
ARE YOU TRYING TO SOMEHOW DEAL WITH THE SNAKE?
<BLANKLINE>

>>> restart(room=42, objects=[food])
>>> quiet(get(food), s, n)
>>> quiet(*islice(cycle((w, s, n)), 75 - 5))
>>> look
YOU ARE IN A MAZE OF TWISTY LITTLE PASSAGES, ALL ALIKE.
<BLANKLINE>
DO YOU NEED HELP GETTING OUT OF THE MAZE?
<BLANKLINE>
>>> yes
YOU CAN MAKE THE PASSAGES LOOK LESS ALIKE BY DROPPING THINGS.
<BLANKLINE>

>>> quiet(restart(room=99))
>>> quiet([ look ] * (25 - 2))
>>> look
YOU ARE IN AN ALCOVE.  A SMALL NW PATH SEEMS TO WIDEN AFTER A SHORT
DISTANCE.  AN EXTREMELY TIGHT TUNNEL LEADS EAST.  IT LOOKS LIKE A VERY
TIGHT SQUEEZE.  AN EERIE LIGHT CAN BE SEEN AT THE OTHER END.
<BLANKLINE>
ARE YOU TRYING TO EXPLORE BEYOND THE PLOVER ROOM?
<BLANKLINE>

>>> quiet(restart(room=108), [ look ] * (20 - 2))
>>> look
YOU ARE AT WITT'S END.  PASSAGES LEAD OFF IN *ALL* DIRECTIONS.
<BLANKLINE>
DO YOU NEED HELP GETTING OUT OF HERE?
<BLANKLINE>

------------------------
Your lamp is getting dim
------------------------

There is an elaborate set of messages generated for the player as their
lamp starts to grow dim.  You can, of course, replentish the lamp using
batteries from the vending machine in the maze.

>>> quiet(restart(room=34))
>>> game.chest.prop = 1
>>> quiet([ look ] * (330 - 30 - 3))
>>> look
YOUR LAMP IS GETTING DIM.  YOU'D BEST START WRAPPING THIS UP, UNLESS
YOU CAN FIND SOME FRESH BATTERIES.  I SEEM TO RECALL THERE'S A VENDING
MACHINE IN THE MAZE.  BRING SOME COINS WITH YOU.
<BLANKLINE>
YOU ARE IN A JUMBLE OF ROCK, WITH CRACKS EVERYWHERE.
<BLANKLINE>
>>> quiet(go_to(30), get(coins))
>>> go_to(140)
DEAD END
<BLANKLINE>
THERE IS A MASSIVE VENDING MACHINE HERE.  THE INSTRUCTIONS ON IT READ:
"DROP COINS HERE TO RECEIVE FRESH BATTERIES."
<BLANKLINE>
>>> drop(coins)
THERE ARE FRESH BATTERIES HERE.
<BLANKLINE>
>>> look
YOUR LAMP IS GETTING DIM.  I'M TAKING THE LIBERTY OF REPLACING THE
BATTERIES.
<BLANKLINE>
DEAD END
<BLANKLINE>
THERE IS A MASSIVE VENDING MACHINE HERE.  THE INSTRUCTIONS ON IT READ:
"DROP COINS HERE TO RECEIVE FRESH BATTERIES."
<BLANKLINE>
SOME WORN-OUT BATTERIES HAVE BEEN DISCARDED NEARBY.
<BLANKLINE>
>>> game.lamp_turns = 31
>>> n
YOUR LAMP IS GETTING DIM, AND YOU'RE OUT OF SPARE BATTERIES.  YOU'D
BEST START WRAPPING THIS UP.
<BLANKLINE>
YOU ARE IN A LITTLE MAZE OF TWISTING PASSAGES, ALL DIFFERENT.
<BLANKLINE>
>>> game.lamp_turns = 1
>>> n
YOUR LAMP HAS RUN OUT OF POWER.
<BLANKLINE>
IT IS NOW PITCH DARK.  IF YOU PROCEED YOU WILL LIKELY FALL INTO A PIT.
<BLANKLINE>
>>> go_to(5)
YOU'RE IN FOREST.
<BLANKLINE>
>>> w
THERE'S NOT MUCH POINT IN WANDERING AROUND OUT HERE, AND YOU CAN'T
EXPLORE THE CAVE WITHOUT A LAMP.  SO LET'S JUST CALL IT A DAY.
<BLANKLINE>
<BLANKLINE>
YOU SCORED 61 OUT OF A POSSIBLE 350 USING 309 TURNS.
<BLANKLINE>
YOUR SCORE QUALIFIES YOU AS A NOVICE CLASS ADVENTURER.
<BLANKLINE>
TO ACHIEVE THE NEXT HIGHER RATING, YOU NEED 70 MORE POINTS
<BLANKLINE>

If the batteries are in the user's inventory when replaced, then the old
ones are tossed on the ground rather than remaining in the inventory.

>>> restart(room=30)
>>> quiet(get(coins), go_to(140), drop(coins))
>>> get(batteries)
OK
<BLANKLINE>
>>> game.chest.prop = 1
>>> game.lamp_turns = 30
>>> look
YOUR LAMP IS GETTING DIM.  I'M TAKING THE LIBERTY OF REPLACING THE
BATTERIES.
<BLANKLINE>
DEAD END
<BLANKLINE>
THERE IS A MASSIVE VENDING MACHINE HERE.  THE INSTRUCTIONS ON IT READ:
"DROP COINS HERE TO RECEIVE FRESH BATTERIES."
<BLANKLINE>
SOME WORN-OUT BATTERIES HAVE BEEN DISCARDED NEARBY.
<BLANKLINE>

Finally, if you leave the batteries in the maze, you are warned to go
back and get them.

>>> quiet(restart(room=34), go_to(30), get(coins), go_to(140), drop(coins), n)
>>> game.lamp_turns = 30
>>> look
YOUR LAMP IS GETTING DIM.  YOU'D BEST GO BACK FOR THOSE BATTERIES.
<BLANKLINE>
YOU ARE IN A LITTLE MAZE OF TWISTING PASSAGES, ALL DIFFERENT.
<BLANKLINE>

----------------------------------
Basic and advanced command parsing
----------------------------------

Various complaints are associated with poorly-chosen requests.

>>> quiet(restart(room=34))
>>> enter(water)
WHERE?
<BLANKLINE>
>>> quiet(go_to(7))
>>> enter(water)
YOUR FEET ARE NOW WET.
<BLANKLINE>

You can try to enter the house with the 'enter' command instead of just
naming the house.

>>> quiet(restart(room=1))
>>> enter(house)
YOU'RE INSIDE BUILDING.
<BLANKLINE>
THERE ARE SOME KEYS ON THE GROUND HERE.
<BLANKLINE>
THERE IS FOOD HERE.
<BLANKLINE>
THERE IS A BOTTLE OF WATER HERE.
<BLANKLINE>

Both "oil" and "water" can be used as verbs, despite officially being
treated as nouns in the game vocabulary.

>>> restart(room=94)
>>> look
YOU ARE AT ONE END OF AN IMMENSE NORTH/SOUTH PASSAGE.
<BLANKLINE>
THE WAY NORTH IS BARRED BY A MASSIVE, RUSTY, IRON DOOR.
<BLANKLINE>
>>> oil(door)
I SEE NO OIL HERE.
<BLANKLINE>
>>> go_to(25)
YOU'RE IN WEST PIT.
<BLANKLINE>
THERE IS A TINY LITTLE PLANT IN THE PIT, MURMURING "WATER, WATER, ..."
<BLANKLINE>
>>> water(plant)
I SEE NO WATER HERE.
<BLANKLINE>

Various combinations of words yield somewhat sensible retorts; and
several nouns invoke small special cases in the code, like "grate" also
being a movement noun.

>>> restart(room=4, dwarves=True)
>>> look
YOU ARE IN A VALLEY IN THE FOREST BESIDE A STREAM TUMBLING ALONG A
ROCKY BED.
<BLANKLINE>

>>> game.do_command(['eat', 'pray', 'love'])
"I DON'T KNOW THAT WORD.\n\n"

>>> grate
YOU'RE OUTSIDE GRATE.
<BLANKLINE>
THE GRATE IS LOCKED.
<BLANKLINE>
>>> go_to(12)
YOU ARE IN AN AWKWARD SLOPING EAST/WEST CANYON.
<BLANKLINE>
>>> grate
YOU'RE BELOW THE GRATE.
<BLANKLINE>
THE GRATE IS LOCKED.
<BLANKLINE>

>>> carry(dwarf)
I SEE NO DWARF HERE.
<BLANKLINE>

>>> quiet(go_to(3))
>>> get(water)
OK
<BLANKLINE>
>>> inventory
YOU ARE CURRENTLY HOLDING THE FOLLOWING:
<BLANKLINE>
BRASS LANTERN
SMALL BOTTLE
WATER IN THE BOTTLE
<BLANKLINE>

>>> get(knife)
I SEE NO KNIFE HERE.
<BLANKLINE>
>>> game.dwarf_stage = 2
>>> go_to(27)
THERE IS A THREATENING LITTLE DWARF IN THE ROOM WITH YOU!
<BLANKLINE>
ONE SHARP NASTY KNIFE IS THROWN AT YOU!
<BLANKLINE>
IT MISSES!
<BLANKLINE>
YOU ARE ON THE WEST SIDE OF THE FISSURE IN THE HALL OF MISTS.
<BLANKLINE>
THERE ARE DIAMONDS HERE!
<BLANKLINE>
>>> get(knife)
THE DWARVES' KNIVES VANISH AS THEY STRIKE THE WALLS OF THE CAVE.
<BLANKLINE>
>>> get(knife)
I SEE NO KNIFE HERE.
<BLANKLINE>

>>> find(jewelry)
I CAN ONLY TELL YOU WHAT YOU SEE AS YOU MOVE ABOUT AND MANIPULATE
THINGS.  I CANNOT TELL YOU WHERE REMOTE THINGS ARE.
<BLANKLINE>

>>> lamp
WHAT DO YOU WANT TO DO WITH THE LAMP?
<BLANKLINE>

>>> quiet(restart(randoms=(None, 0.1, None, 0.3, None, 0.9)))
>>> carry(pour)
I DON'T KNOW THAT WORD.
<BLANKLINE>
>>> jump(eat)
I DON'T UNDERSTAND THAT!
<BLANKLINE>
>>> throw(drop)
WHAT?
<BLANKLINE>

---------------
Special motions
---------------

The "back" movement command has to handle several special cases, in case
the previous location was an intermediate "forced" room, or if it simply
cannot figure out how to return you to the previous location.

>>> quiet(restart(room=65, randoms=(None, None, 0.001,)))
>>> n
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU ARE IN BEDQUILT, A LONG EAST/WEST PASSAGE WITH HOLES EVERYWHERE.
TO EXPLORE AT RANDOM SELECT NORTH, SOUTH, UP, OR DOWN.
<BLANKLINE>
>>> back
SORRY, BUT I NO LONGER SEEM TO REMEMBER HOW IT WAS YOU GOT HERE.
<BLANKLINE>
YOU ARE IN BEDQUILT, A LONG EAST/WEST PASSAGE WITH HOLES EVERYWHERE.
TO EXPLORE AT RANDOM SELECT NORTH, SOUTH, UP, OR DOWN.
<BLANKLINE>

While testing "back" on the beanstalk, we also test for whether we can
run a command like "get" on the plant as seen from outside the pits.

>>> quiet(restart(), get(bottle), go_to(25), pour(water))
>>> quiet(go_to(113), get(water), go_to(25), pour(water))
>>> go_to(88)
YOU'RE IN NARROW CORRIDOR.
<BLANKLINE>
>>> d
YOU'RE IN WEST PIT.
<BLANKLINE>
THERE IS A GIGANTIC BEANSTALK STRETCHING ALL THE WAY UP TO THE HOLE.
<BLANKLINE>
>>> back
YOU CLAMBER UP THE PLANT AND SCURRY THROUGH THE HOLE AT THE TOP.
<BLANKLINE>
YOU'RE IN NARROW CORRIDOR.
<BLANKLINE>
>>> go_to(23)
YOU'RE AT WEST END OF TWOPIT ROOM.
<BLANKLINE>
THERE IS A HUGE BEANSTALK GROWING OUT OF THE WEST PIT UP TO THE HOLE.
<BLANKLINE>
>>> find(beanstalk)
I BELIEVE WHAT YOU WANT IS RIGHT HERE WITH YOU.
<BLANKLINE>
>>> go_to(67)
YOU'RE AT EAST END OF TWOPIT ROOM.
<BLANKLINE>
THERE IS A HUGE BEANSTALK GROWING OUT OF THE WEST PIT UP TO THE HOLE.
<BLANKLINE>

>>> go_to(91)
YOU'RE AT STEEP INCLINE ABOVE LARGE ROOM.
<BLANKLINE>
>>> d
YOU ARE IN A LARGE LOW ROOM.  CRAWLS LEAD NORTH, SE, AND SW.
<BLANKLINE>
>>> back
YOU CAN'T GET THERE FROM HERE.
<BLANKLINE>
YOU ARE IN A LARGE LOW ROOM.  CRAWLS LEAD NORTH, SE, AND SW.
<BLANKLINE>

If the player tries walking back across the troll bridge while still
carrying the bear, then disaster strikes: the bridge breaks beneath
them, and both player and bear wind up dead at the bottom of the pit.
But the game shows a bit of elegance, and refuses to strand the player's
belongings out of reach, either at the pit bottom or where they were
last standing on the far side of the troll bridge - since without the
bridge, the items would be permanently lost!  (We also try feeding the
bear a few times, to test some special cases in the feed() logic while
we are at it.)

>>> quiet(restart(), get(keys), go_to(123))
>>> game.bear.carry()
>>> game.bear.prop = 2
>>> game.bear.is_fixed = False
>>> w
YOU ARE BEING FOLLOWED BY A VERY LARGE, TAME BEAR.
<BLANKLINE>
YOU'RE ON NE SIDE OF CHASM.
<BLANKLINE>
A RICKETY WOODEN BRIDGE EXTENDS ACROSS THE CHASM, VANISHING INTO THE
MIST.  A SIGN POSTED ON THE BRIDGE READS, "STOP! PAY TROLL!"
<BLANKLINE>
A BURLY TROLL STANDS BY THE BRIDGE AND INSISTS YOU THROW HIM A
TREASURE BEFORE YOU MAY CROSS.
<BLANKLINE>
>>> drop(bear)
THE BEAR LUMBERS TOWARD THE TROLL, WHO LETS OUT A STARTLED SHRIEK AND
SCURRIES AWAY.  THE BEAR SOON GIVES UP THE PURSUIT AND WANDERS BACK.
<BLANKLINE>
>>> feed(bear)
THERE IS NOTHING HERE TO EAT.
<BLANKLINE>
>>> get(bear)
OK
<BLANKLINE>
>>> sw
JUST AS YOU REACH THE OTHER SIDE, THE BRIDGE BUCKLES BENEATH THE
WEIGHT OF THE BEAR, WHICH WAS STILL FOLLOWING YOU AROUND.  YOU
SCRABBLE DESPERATELY FOR SUPPORT, BUT AS THE BRIDGE COLLAPSES YOU
STUMBLE BACK AND FALL INTO THE CHASM.
<BLANKLINE>
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?
<BLANKLINE>
>>> quiet(yes, leave, get(lamp), on)
>>> go_to(117)
YOU'RE ON SW SIDE OF CHASM.
<BLANKLINE>
THERE ARE SOME KEYS ON THE GROUND HERE.
<BLANKLINE>
THE WRECKAGE OF A BRIDGE (AND A DEAD BEAR) CAN BE SEEN AT THE BOTTOM
OF THE CHASM.
<BLANKLINE>
THE TROLL IS NOWHERE TO BE SEEN.
<BLANKLINE>
>>> feed(bear)
DON'T BE RIDICULOUS!
<BLANKLINE>

Several motion words elicit special messages when they do not apply.

>>> quiet(restart(room=34))
>>> onward
I AM UNSURE HOW YOU ARE FACING.  USE COMPASS POINTS OR NEARBY OBJECTS.
<BLANKLINE>
YOU ARE IN A JUMBLE OF ROCK, WITH CRACKS EVERYWHERE.
<BLANKLINE>
>>> left
I AM UNSURE HOW YOU ARE FACING.  USE COMPASS POINTS OR NEARBY OBJECTS.
<BLANKLINE>
YOU ARE IN A JUMBLE OF ROCK, WITH CRACKS EVERYWHERE.
<BLANKLINE>
>>> right
I AM UNSURE HOW YOU ARE FACING.  USE COMPASS POINTS OR NEARBY OBJECTS.
<BLANKLINE>
YOU ARE IN A JUMBLE OF ROCK, WITH CRACKS EVERYWHERE.
<BLANKLINE>

>>> leave
I DON'T KNOW IN FROM OUT HERE.  USE COMPASS POINTS OR NAME SOMETHING
IN THE GENERAL DIRECTION YOU WANT TO GO.
<BLANKLINE>
YOU ARE IN A JUMBLE OF ROCK, WITH CRACKS EVERYWHERE.
<BLANKLINE>
>>> inward
I DON'T KNOW IN FROM OUT HERE.  USE COMPASS POINTS OR NAME SOMETHING
IN THE GENERAL DIRECTION YOU WANT TO GO.
<BLANKLINE>
YOU ARE IN A JUMBLE OF ROCK, WITH CRACKS EVERYWHERE.
<BLANKLINE>

>>> xyzzy
NOTHING HAPPENS.
<BLANKLINE>
YOU ARE IN A JUMBLE OF ROCK, WITH CRACKS EVERYWHERE.
<BLANKLINE>
>>> plugh
NOTHING HAPPENS.
<BLANKLINE>
YOU ARE IN A JUMBLE OF ROCK, WITH CRACKS EVERYWHERE.
<BLANKLINE>

>>> crawl
WHICH WAY?
<BLANKLINE>
YOU ARE IN A JUMBLE OF ROCK, WITH CRACKS EVERYWHERE.
<BLANKLINE>

---------------
Verb edge cases
---------------

And now, we exercise dozens of fiddly edge cases for verbs.

>>> quiet(restart())
>>> carry                       # ambiguous in the face of several objects
CARRY WHAT?
<BLANKLINE>

>>> quiet(restart(room=25))
>>> get(plant)
THE PLANT HAS EXCEPTIONALLY DEEP ROOTS AND CANNOT BE PULLED FREE.
<BLANKLINE>

>>> quiet(restart(), go_to(24))
>>> get(oil)
YOU HAVE NOTHING IN WHICH TO CARRY IT.
<BLANKLINE>
>>> quiet(go_to(3), get(bottle), go_to(24))
>>> get(oil)
YOUR BOTTLE IS ALREADY FULL.
<BLANKLINE>
>>> get(bottle)
YOU ARE ALREADY CARRYING IT!
<BLANKLINE>
>>> drop(oil)
YOU AREN'T CARRYING IT!
<BLANKLINE>
>>> drop(water)
OK
<BLANKLINE>
>>> look
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.
<BLANKLINE>
THERE IS A BOTTLE OF WATER HERE.
<BLANKLINE>

>>> restart(room=94, objects=[clam])
>>> look
YOU ARE AT ONE END OF AN IMMENSE NORTH/SOUTH PASSAGE.
<BLANKLINE>
THE WAY NORTH IS BARRED BY A MASSIVE, RUSTY, IRON DOOR.
<BLANKLINE>
THERE IS AN ENORMOUS CLAM HERE WITH ITS SHELL TIGHTLY CLOSED.
<BLANKLINE>
>>> unlock                      # two unlockable objects are present
UNLOCK WHAT?
<BLANKLINE>
>>> quiet(s)
>>> unlock                      # no unlockable objects also cause a problem
THERE IS NOTHING HERE WITH A LOCK!
<BLANKLINE>

>>> restart(room=103, objects=[trident])
>>> lock(clam)
WHAT?
<BLANKLINE>
>>> quiet(get(clam), get(trident))
>>> unlock
I ADVISE YOU TO PUT DOWN THE CLAM BEFORE OPENING IT.  >STRAIN!<
<BLANKLINE>
>>> quiet(drop(clam))
>>> unlock
A GLISTENING PEARL FALLS OUT OF THE CLAM AND ROLLS AWAY.  GOODNESS,
THIS MUST REALLY BE AN OYSTER.  (I NEVER WAS VERY GOOD AT IDENTIFYING
BIVALVES.)  WHATEVER IT IS, IT HAS NOW SNAPPED SHUT AGAIN.
<BLANKLINE>
>>> unlock
THE OYSTER CREAKS OPEN, REVEALING NOTHING BUT OYSTER INSIDE.  IT
PROMPTLY SNAPS SHUT AGAIN.
<BLANKLINE>

>>> restart(room=94)
>>> look
YOU ARE AT ONE END OF AN IMMENSE NORTH/SOUTH PASSAGE.
<BLANKLINE>
THE WAY NORTH IS BARRED BY A MASSIVE, RUSTY, IRON DOOR.
<BLANKLINE>
>>> unlock
THE DOOR IS EXTREMELY RUSTY AND REFUSES TO OPEN.
<BLANKLINE>

>>> restart()
>>> unlock(keys)
YOU CAN'T UNLOCK THE KEYS.
<BLANKLINE>
>>> quiet(get(keys), get(food), go_to(10))
>>> unlock(cage)
IT HAS NO LOCK.
<BLANKLINE>
>>> quiet(go_to(130))
>>> lock(chain)
IT WAS ALREADY LOCKED.
<BLANKLINE>
>>> unlock(chain)
THERE IS NO WAY TO GET PAST THE BEAR TO UNLOCK THE CHAIN, WHICH IS
PROBABLY JUST AS WELL.
<BLANKLINE>
>>> quiet(throw(food), unlock(chain))
>>> unlock(chain)
IT WAS ALREADY UNLOCKED.
<BLANKLINE>
>>> quiet(get(chain), w)
>>> lock(chain)
THERE IS NOTHING HERE TO WHICH THE CHAIN CAN BE LOCKED.
<BLANKLINE>
>>> quiet(e)
>>> lock(chain)
THE CHAIN IS NOW LOCKED.
<BLANKLINE>
>>> get(chain)
THE CHAIN IS STILL LOCKED.
<BLANKLINE>
>>> lock(bear)
I DON'T KNOW HOW TO LOCK OR UNLOCK SUCH A THING.
<BLANKLINE>

>>> restart()
>>> off
YOUR LAMP IS NOW OFF.
<BLANKLINE>
>>> quiet(drop(lamp), leave)
>>> on
YOU HAVE NO SOURCE OF LIGHT.
<BLANKLINE>
>>> off
YOU HAVE NO SOURCE OF LIGHT.
<BLANKLINE>
>>> quiet(enter, get(lamp), on, plugh)
>>> off
YOUR LAMP IS NOW OFF.
<BLANKLINE>
IT IS NOW PITCH DARK.  IF YOU PROCEED YOU WILL LIKELY FALL INTO A PIT.
<BLANKLINE>
>>> quiet(on)
>>> game.lamp_turns = 2
>>> quiet(look)
>>> look
YOUR LAMP HAS RUN OUT OF POWER.
<BLANKLINE>
IT IS NOW PITCH DARK.  IF YOU PROCEED YOU WILL LIKELY FALL INTO A PIT.
<BLANKLINE>
>>> on
YOUR LAMP HAS RUN OUT OF POWER.
<BLANKLINE>

>>> restart()
>>> wave(lamp)
NOTHING HAPPENS.
<BLANKLINE>
>>> wave(keys)
YOU AREN'T CARRYING IT!
<BLANKLINE>

The "attack" verb is a particularly long test, because its intransitive
form works hard to determine - in two stages, focusing first on real
enemies and then on some harmless objects - what on earth you could
possibly be designating as a target.

>>> restart(dwarves=True)
>>> attack
THERE IS NOTHING HERE TO ATTACK.
<BLANKLINE>
>>> quiet(go_to(117))
>>> attack
TROLLS ARE CLOSE RELATIVES WITH THE ROCKS AND HAVE SKIN AS TOUGH AS
THAT OF A RHINOCEROS.  THE TROLL FENDS OFF YOUR BLOWS EFFORTLESSLY.
<BLANKLINE>
>>> game.dwarf_stage = 2
>>> game.dwarves[0].room = game.loc
>>> attack                      # now there are two choices: dwarf and troll
ATTACK WHAT?
<BLANKLINE>
>>> attack(dwarf)
WITH WHAT?  YOUR BARE HANDS?
<BLANKLINE>
>>> quiet(go_to(103), get(clam), go_to(13))
>>> attack                      # cannot decide between oyster and bird
ATTACK WHAT?
<BLANKLINE>
>>> quiet(w, drop(clam), e)
>>> attack
THE LITTLE BIRD IS NOW DEAD.  ITS BODY DISAPPEARS.
<BLANKLINE>
>>> look                        # make sure the bird is really gone
YOU ARE IN A SPLENDID CHAMBER THIRTY FEET HIGH.  THE WALLS ARE FROZEN
RIVERS OF ORANGE STONE.  AN AWKWARD CANYON AND A GOOD PASSAGE EXIT
FROM EAST AND WEST SIDES OF THE CHAMBER.
<BLANKLINE>
>>> quiet(w)
>>> attack
THE SHELL IS VERY STRONG AND IS IMPERVIOUS TO ATTACK.
<BLANKLINE>
>>> quiet(go_to(119))
>>> attack(dragon)
WITH WHAT?  YOUR BARE HANDS?
<BLANKLINE>
>>> quiet(yes)
>>> attack(dragon)
FOR CRYING OUT LOUD, THE POOR THING IS ALREADY DEAD!
<BLANKLINE>

>>> quiet(restart(), get(bottle))
>>> pour
YOUR BOTTLE IS EMPTY AND THE GROUND IS WET.
<BLANKLINE>
>>> pour
POUR WHAT?
<BLANKLINE>
>>> quiet(go_to(24), get(oil), go_to(25))
>>> pour(bottle)
THE PLANT INDIGNANTLY SHAKES THE OIL OFF ITS LEAVES AND ASKS, "WATER?"
<BLANKLINE>
>>> quiet(go_to(24), get(oil))
>>> pour
YOUR BOTTLE IS EMPTY AND THE GROUND IS WET.
<BLANKLINE>
>>> pour(oil)
YOU AREN'T CARRYING IT!
<BLANKLINE>
>>> pour(lamp)
YOU CAN'T POUR THAT.
<BLANKLINE>

>>> restart()
>>> eat
THANK YOU, IT WAS DELICIOUS!
<BLANKLINE>
>>> eat
EAT WHAT?
<BLANKLINE>
>>> quiet(go_to(13))
>>> eat(bird)
I THINK I JUST LOST MY APPETITE.
<BLANKLINE>
>>> quiet(e, e)
>>> eat(rod)
DON'T BE RIDICULOUS!
<BLANKLINE>

>>> quiet(restart(), get(bottle), leave, w)
>>> drink
THE BOTTLE OF WATER IS NOW EMPTY.
<BLANKLINE>
>>> drink
DRINK WHAT?
<BLANKLINE>
>>> drink(lamp)
DON'T BE RIDICULOUS!
<BLANKLINE>

>>> quiet(restart())
>>> rub(lamp)
RUBBING THE ELECTRIC LAMP IS NOT PARTICULARLY REWARDING.  ANYWAY,
NOTHING EXCITING HAPPENS.
<BLANKLINE>
>>> rub(bottle)
I THINK I JUST LOST MY APPETITE.
<BLANKLINE>

>>> quiet(restart(objects=[axe]), get(axe))
>>> throw(food)
YOU AREN'T CARRYING IT!
<BLANKLINE>
>>> quiet(get(food), leave)
>>> throw(lamp)
OK
<BLANKLINE>
>>> look
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.
<BLANKLINE>
THERE IS A LAMP SHINING NEARBY.
<BLANKLINE>
>>> quiet(get(lamp), go_to(130))
>>> throw(axe)
THE AXE MISSES AND LANDS NEAR THE BEAR WHERE YOU CAN'T GET AT IT.
<BLANKLINE>
>>> get(axe)
YOU CAN'T BE SERIOUS!
<BLANKLINE>
>>> throw(food)
THE BEAR EAGERLY WOLFS DOWN YOUR FOOD, AFTER WHICH HE SEEMS TO CALM
DOWN CONSIDERABLY AND EVEN BECOMES RATHER FRIENDLY.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> quiet(leave)
>>> throw(axe)
THERE IS NOTHING HERE TO ATTACK.
<BLANKLINE>

>>> restart()
>>> quit()
DO YOU REALLY WANT TO QUIT NOW?
<BLANKLINE>
>>> no
OK
<BLANKLINE>
>>> quit()
DO YOU REALLY WANT TO QUIT NOW?
<BLANKLINE>
>>> yes
OK
<BLANKLINE>
<BLANKLINE>
YOU SCORED 61 OUT OF A POSSIBLE 350 USING 7 TURNS.
<BLANKLINE>
YOUR SCORE QUALIFIES YOU AS A NOVICE CLASS ADVENTURER.
<BLANKLINE>
TO ACHIEVE THE NEXT HIGHER RATING, YOU NEED 70 MORE POINTS
<BLANKLINE>

>>> restart()
>>> find(lamp)
YOU ARE ALREADY CARRYING IT!
<BLANKLINE>
>>> find(keys)
I BELIEVE WHAT YOU WANT IS RIGHT HERE WITH YOU.
<BLANKLINE>

>>> quiet(restart(), get(food), get(keys), go_to(130), throw(food), unlock)
>>> get(bear)
OK
<BLANKLINE>
>>> inventory
YOU ARE CURRENTLY HOLDING THE FOLLOWING:
<BLANKLINE>
SET OF KEYS
BRASS LANTERN
YOU ARE BEING FOLLOWED BY A VERY LARGE, TAME BEAR.
<BLANKLINE>

>>> quiet(restart(dwarves=True), go_to(10), get(cage), go_to(13))
>>> feed(bird)
IT'S NOT HUNGRY (IT'S MERELY PININ' FOR THE FJORDS).  BESIDES, YOU
HAVE NO BIRD SEED.
<BLANKLINE>
>>> quiet(get(bird), go_to(117))
>>> feed(troll)
GLUTTONY IS NOT ONE OF THE TROLL'S VICES.  AVARICE, HOWEVER, IS.
<BLANKLINE>
>>> quiet(go_to(119))
>>> feed(dragon)
THERE'S NOTHING HERE IT WANTS TO EAT (EXCEPT PERHAPS YOU).
<BLANKLINE>
>>> quiet(kill(dragon), yes)
>>> feed(dragon)
DON'T BE RIDICULOUS!
<BLANKLINE>
>>> quiet(go_to(19))
>>> feed(snake)
THE SNAKE HAS NOW DEVOURED YOUR BIRD.
<BLANKLINE>
>>> feed(snake)
THERE'S NOTHING HERE IT WANTS TO EAT (EXCEPT PERHAPS YOU).
<BLANKLINE>
>>> game.dwarf_stage = 2
>>> quiet(look)
>>> feed(dwarf)
THERE IS NOTHING HERE TO EAT.
<BLANKLINE>
>>> game.food.drop(game.loc)
>>> feed(dwarf)
YOU FOOL, DWARVES EAT ONLY COAL!  NOW YOU'VE MADE HIM *REALLY* MAD!!
<BLANKLINE>
>>> quiet(go_to(129), drop(food), e)
>>> feed(bear)
THERE'S NOTHING HERE IT WANTS TO EAT (EXCEPT PERHAPS YOU).
<BLANKLINE>
>>> feed(chain)
I'M GAME.  WOULD YOU CARE TO EXPLAIN HOW?
<BLANKLINE>

>>> quiet(restart(), get(bottle), drink)
>>> fill
YOUR BOTTLE IS NOW FULL OF WATER.
<BLANKLINE>
>>> fill
YOUR BOTTLE IS ALREADY FULL.
<BLANKLINE>
>>> quiet(drink, plugh)
>>> fill
THERE IS NOTHING HERE WITH WHICH TO FILL THE BOTTLE.
<BLANKLINE>
>>> quiet(drop(bottle), go_to(97))
>>> fill(vase)
YOU CAN'T FILL THAT.
<BLANKLINE>
>>> quiet(get(vase))
>>> fill(vase)
THERE IS NOTHING HERE WITH WHICH TO FILL THE VASE.
<BLANKLINE>
>>> quiet(go_to(38))
>>> fill(vase)
THE SUDDEN CHANGE IN TEMPERATURE HAS DELICATELY SHATTERED THE VASE.
<BLANKLINE>
>>> look
YOU ARE IN THE BOTTOM OF A SMALL PIT WITH A LITTLE STREAM, WHICH
ENTERS AND EXITS THROUGH TINY SLITS.
<BLANKLINE>
THE FLOOR IS LITTERED WITH WORTHLESS SHARDS OF POTTERY.
<BLANKLINE>
>>> get(vase)
YOU CAN'T BE SERIOUS!
<BLANKLINE>
>>> fill(lamp)
YOU CAN'T FILL THAT.
<BLANKLINE>
>>> fill
FILL WHAT?
<BLANKLINE>

>>> restart()
>>> blast
BLASTING REQUIRES DYNAMITE.
<BLANKLINE>

>>> restart()
>>> score
IF YOU WERE TO QUIT NOW, YOU WOULD SCORE 57 OUT OF A POSSIBLE 350.
<BLANKLINE>
DO YOU INDEED WISH TO QUIT NOW?
<BLANKLINE>
>>> yes
OK
<BLANKLINE>
<BLANKLINE>
YOU SCORED 61 OUT OF A POSSIBLE 350 USING 6 TURNS.
<BLANKLINE>
YOUR SCORE QUALIFIES YOU AS A NOVICE CLASS ADVENTURER.
<BLANKLINE>
TO ACHIEVE THE NEXT HIGHER RATING, YOU NEED 70 MORE POINTS
<BLANKLINE>

>>> quiet(restart(), go_to(92), fee, fie, foe)
>>> foo                         # does nothing obvious since the eggs are here
OK
<BLANKLINE>
>>> quiet(get(eggs), go_to(117))
>>> throw(eggs)
THE TROLL CATCHES YOUR TREASURE AND SCURRIES AWAY OUT OF SIGHT.
<BLANKLINE>
>>> quiet(fee, fie, foe)
>>> foo
DONE!
<BLANKLINE>
>>> cross                       # troll does not wait for us to cross!
THE TROLL STEPS OUT FROM BENEATH THE BRIDGE AND BLOCKS YOUR WAY.
<BLANKLINE>
YOU'RE ON SW SIDE OF CHASM.
<BLANKLINE>
A RICKETY WOODEN BRIDGE EXTENDS ACROSS THE CHASM, VANISHING INTO THE
MIST.  A SIGN POSTED ON THE BRIDGE READS, "STOP! PAY TROLL!"
<BLANKLINE>
A BURLY TROLL STANDS BY THE BRIDGE AND INSISTS YOU THROW HIM A
TREASURE BEFORE YOU MAY CROSS.
<BLANKLINE>

>>> quiet(restart(), go_to(106))
>>> read
I'M AFRAID THE MAGAZINE IS WRITTEN IN DWARVISH.
<BLANKLINE>
>>> quiet(off)
>>> read(magazine)
I SEE NO MAGAZINE HERE.
<BLANKLINE>
>>> quiet(on, get(magazine), go_to(101))
>>> read                        # confused by two choices
READ WHAT?
<BLANKLINE>
>>> read(tablet)
"CONGRATULATIONS ON BRINGING LIGHT INTO THE DARK-ROOM!"
<BLANKLINE>
>>> quiet(go_to(95), get(trident), go_to(103), unlock(clam), get(oyster))
>>> read(oyster)
HMMM, THIS LOOKS LIKE A CLUE, WHICH MEANS IT'LL COST YOU 10 POINTS TO
READ IT.  SHOULD I GO AHEAD AND READ IT ANYWAY?
<BLANKLINE>
>>> yes
IT SAYS, "THERE IS SOMETHING STRANGE ABOUT THIS PLACE, SUCH THAT ONE
OF THE WORDS I'VE ALWAYS KNOWN NOW HAS A NEW EFFECT."
<BLANKLINE>
>>> read(oyster)
IT SAYS THE SAME THING IT DID BEFORE.
<BLANKLINE>
>>> quiet(go_to(29), get(gold), go_to(112))
>>> game.pirate.room = game.rooms[112]
>>> look
OUT FROM THE SHADOWS BEHIND YOU POUNCES A BEARDED PIRATE!  "HAR, HAR,"
HE CHORTLES, "I'LL JUST TAKE ALL THIS BOOTY AND HIDE IT AWAY WITH ME
CHEST DEEP IN THE MAZE!"  HE SNATCHES YOUR TREASURE AND VANISHES INTO
THE GLOOM.
<BLANKLINE>
YOU ARE IN A LITTLE MAZE OF TWISTING PASSAGES, ALL DIFFERENT.
<BLANKLINE>
>>> s
DEAD END
<BLANKLINE>
THERE IS A MESSAGE SCRAWLED IN THE DUST IN A FLOWERY SCRIPT, READING:
"THIS IS NOT THE MAZE WHERE THE PIRATE LEAVES HIS TREASURE CHEST."
<BLANKLINE>
THERE IS A MASSIVE VENDING MACHINE HERE.  THE INSTRUCTIONS ON IT READ:
"DROP COINS HERE TO RECEIVE FRESH BATTERIES."
<BLANKLINE>
>>> read(message)
"THIS IS NOT THE MAZE WHERE THE PIRATE LEAVES HIS TREASURE CHEST."
<BLANKLINE>
>>> read(lamp)
I'M AFRAID I DON'T UNDERSTAND.
<BLANKLINE>

>>> quiet(restart(), go_to(97))
>>> smash(vase)
YOU HAVE TAKEN THE VASE AND HURLED IT DELICATELY TO THE GROUND.
<BLANKLINE>
>>> get(vase)
YOU CAN'T BE SERIOUS!
<BLANKLINE>
>>> quiet(restart(), go_to(97))
>>> get(vase)                   # can we also break it while carrying it?
OK
<BLANKLINE>
>>> smash(vase)
YOU HAVE TAKEN THE VASE AND HURLED IT DELICATELY TO THE GROUND.
<BLANKLINE>
>>> inventory
YOU ARE CURRENTLY HOLDING THE FOLLOWING:
<BLANKLINE>
BRASS LANTERN
<BLANKLINE>
>>> look
THIS IS THE ORIENTAL ROOM.  ANCIENT ORIENTAL CAVE DRAWINGS COVER THE
WALLS.  A GENTLY SLOPING PASSAGE LEADS UPWARD TO THE NORTH, ANOTHER
PASSAGE LEADS SE, AND A HANDS AND KNEES CRAWL LEADS WEST.
<BLANKLINE>
THE FLOOR IS LITTERED WITH WORTHLESS SHARDS OF POTTERY.
<BLANKLINE>
>>> quiet(go_to(109))
>>> smash(mirror)
IT IS TOO FAR UP FOR YOU TO REACH.
<BLANKLINE>
>>> smash(lamp)
IT IS BEYOND YOUR POWER TO DO THAT.
<BLANKLINE>

>>> quiet(restart())
>>> wake(keys)
DON'T BE RIDICULOUS!
<BLANKLINE>

>>> quiet(restart())
>>> suspend
PROVIDE "SUSPEND" WITH A FILENAME OR OPEN FILE
<BLANKLINE>

----------------
Closing the cave
----------------

Finally, there are a few situations we need to test that relate
specifically to the end-game.  For example, dying while the cave is
closing causes a very abrupt ending to the game.

>>> quiet(restart(), drop(lamp), go_to(13))
>>> game.is_closing = 1
>>> game.start_closing_cave()
>>> w
YOU FELL INTO A PIT AND BROKE EVERY BONE IN YOUR BODY!
<BLANKLINE>
IT LOOKS AS THOUGH YOU'RE DEAD.  WELL, SEEING AS HOW IT'S SO CLOSE TO
CLOSING TIME ANYWAY, I THINK WE'LL JUST CALL IT A DAY.
<BLANKLINE>
<BLANKLINE>
YOU SCORED 78 OUT OF A POSSIBLE 350 USING 7 TURNS.
<BLANKLINE>
YOUR SCORE QUALIFIES YOU AS A NOVICE CLASS ADVENTURER.
<BLANKLINE>
TO ACHIEVE THE NEXT HIGHER RATING, YOU NEED 53 MORE POINTS
<BLANKLINE>

Now we reload our saved game one last time, take it through the ending,
and then save anew so that we can do several tests in the repository.

>>> quiet(restart(), get(keys), go_to(9))
>>> game.is_closing = 1
>>> game.start_closing_cave()
>>> unlock
A MYSTERIOUS RECORDED VOICE GROANS INTO LIFE AND ANNOUNCES:
   "THIS EXIT IS CLOSED.  PLEASE LEAVE VIA MAIN OFFICE."
<BLANKLINE>
>>> game.clock2 = 1
>>> w
THE SEPULCHRAL VOICE ENTONES, "THE CAVE IS NOW CLOSED."  AS THE ECHOES
FADE, THERE IS A BLINDING FLASH OF LIGHT (AND A SMALL PUFF OF ORANGE
SMOKE). . . .    AS YOUR EYES REFOCUS, YOU LOOK AROUND AND FIND...
<BLANKLINE>
YOU'RE AT NE END.
<BLANKLINE>
>>> quiet(savefile.truncate())
>>> savefile.seek(0)
0
>>> save(savefile)
GAME SAVED
<BLANKLINE>

Most of the interesting end-game events are fatal, but not all.

>>> quiet(restart(), sw)
>>> get(cage)
OK
<BLANKLINE>
>>> attack(bird)
OH, LEAVE THE POOR UNHAPPY BIRD ALONE.
<BLANKLINE>
>>> drop(bird)
THE LITTLE BIRD ATTACKS THE GREEN SNAKE, AND IN AN ASTOUNDING FLURRY
DRIVES THE SNAKE AWAY.
<BLANKLINE>
THE RESULTING RUCKUS HAS AWAKENED THE DWARVES.  THERE ARE NOW SEVERAL
THREATENING LITTLE DWARVES IN THE ROOM WITH YOU!  MOST OF THEM THROW
KNIVES AT YOU!  ALL OF THEM GET YOU!
<BLANKLINE>
<BLANKLINE>
YOU SCORED 98 OUT OF A POSSIBLE 350 USING 13 TURNS.
<BLANKLINE>
YOUR SCORE QUALIFIES YOU AS A NOVICE CLASS ADVENTURER.
<BLANKLINE>
TO ACHIEVE THE NEXT HIGHER RATING, YOU NEED 33 MORE POINTS
<BLANKLINE>

>>> quiet(restart())
>>> attack(dwarf)
THE RESULTING RUCKUS HAS AWAKENED THE DWARVES.  THERE ARE NOW SEVERAL
THREATENING LITTLE DWARVES IN THE ROOM WITH YOU!  MOST OF THEM THROW
KNIVES AT YOU!  ALL OF THEM GET YOU!
<BLANKLINE>
<BLANKLINE>
YOU SCORED 98 OUT OF A POSSIBLE 350 USING 10 TURNS.
<BLANKLINE>
YOUR SCORE QUALIFIES YOU AS A NOVICE CLASS ADVENTURER.
<BLANKLINE>
TO ACHIEVE THE NEXT HIGHER RATING, YOU NEED 33 MORE POINTS
<BLANKLINE>

>>> quiet(restart(), sw)
>>> get(rod)                    # grab the rusty-mark rod
OK
<BLANKLINE>
>>> ne                          # move to where the rusty-star rods lie
YOU'RE AT NE END.
<BLANKLINE>
>>> throw(rod)                  # will the correct rod get dropped?
OK
<BLANKLINE>
>>> look
YOU ARE AT THE NORTHEAST END OF AN IMMENSE ROOM, EVEN LARGER THAN THE
GIANT ROOM.  IT APPEARS TO BE A REPOSITORY FOR THE "ADVENTURE"
PROGRAM.  MASSIVE TORCHES FAR OVERHEAD BATHE THE ROOM WITH SMOKY
YELLOW LIGHT.  SCATTERED ABOUT YOU CAN BE SEEN A PILE OF BOTTLES (ALL
OF THEM EMPTY), A NURSERY OF YOUNG BEANSTALKS MURMURING QUIETLY, A BED
OF OYSTERS, A BUNDLE OF BLACK RODS WITH RUSTY STARS ON THEIR ENDS, AND
A COLLECTION OF BRASS LANTERNS.  OFF TO ONE SIDE A GREAT MANY DWARVES
ARE SLEEPING ON THE FLOOR, SNORING LOUDLY.  A SIGN NEARBY READS: "DO
NOT DISTURB THE DWARVES!"  AN IMMENSE MIRROR IS HANGING AGAINST ONE
WALL, AND STRETCHES TO THE OTHER END OF THE ROOM, WHERE VARIOUS OTHER
SUNDRY OBJECTS CAN BE GLIMPSED DIMLY IN THE DISTANCE.
<BLANKLINE>
A THREE FOOT BLACK ROD WITH A RUSTY MARK ON AN END LIES NEARBY.
<BLANKLINE>

>>> restart()
>>> find(eggs)
I DARESAY WHATEVER YOU WANT IS AROUND HERE SOMEWHERE.
<BLANKLINE>
>>> sw
YOU'RE AT SW END.
<BLANKLINE>
>>> get(rod)
OK
<BLANKLINE>
>>> blast
THERE IS A LOUD EXPLOSION, AND YOU ARE SUDDENLY SPLASHED ACROSS THE
WALLS OF THE ROOM.
<BLANKLINE>
<BLANKLINE>
YOU SCORED 113 OUT OF A POSSIBLE 350 USING 13 TURNS.
<BLANKLINE>
YOU HAVE ACHIEVED THE RATING: "EXPERIENCED ADVENTURER".
<BLANKLINE>
TO ACHIEVE THE NEXT HIGHER RATING, YOU NEED 88 MORE POINTS
<BLANKLINE>

>>> restart()
>>> shatter(mirror)
YOU STRIKE THE MIRROR A RESOUNDING BLOW, WHEREUPON IT SHATTERS INTO A
MYRIAD TINY FRAGMENTS.
<BLANKLINE>
THE RESULTING RUCKUS HAS AWAKENED THE DWARVES.  THERE ARE NOW SEVERAL
THREATENING LITTLE DWARVES IN THE ROOM WITH YOU!  MOST OF THEM THROW
KNIVES AT YOU!  ALL OF THEM GET YOU!
<BLANKLINE>
<BLANKLINE>
YOU SCORED 98 OUT OF A POSSIBLE 350 USING 10 TURNS.
<BLANKLINE>
YOUR SCORE QUALIFIES YOU AS A NOVICE CLASS ADVENTURER.
<BLANKLINE>
TO ACHIEVE THE NEXT HIGHER RATING, YOU NEED 33 MORE POINTS
<BLANKLINE>

>>> restart()
>>> wake(dwarves)
YOU PROD THE NEAREST DWARF, WHO WAKES UP GRUMPILY, TAKES ONE LOOK AT
YOU, CURSES, AND GRABS FOR HIS AXE.
<BLANKLINE>
THE RESULTING RUCKUS HAS AWAKENED THE DWARVES.  THERE ARE NOW SEVERAL
THREATENING LITTLE DWARVES IN THE ROOM WITH YOU!  MOST OF THEM THROW
KNIVES AT YOU!  ALL OF THEM GET YOU!
<BLANKLINE>
<BLANKLINE>
YOU SCORED 98 OUT OF A POSSIBLE 350 USING 10 TURNS.
<BLANKLINE>
YOUR SCORE QUALIFIES YOU AS A NOVICE CLASS ADVENTURER.
<BLANKLINE>
TO ACHIEVE THE NEXT HIGHER RATING, YOU NEED 33 MORE POINTS
<BLANKLINE>


================================================
FILE: adventure/tests/walkthrough1.txt
================================================
>>> import adventure
>>> adventure.play(seed=1)
WELCOME TO ADVENTURE!!  WOULD YOU LIKE INSTRUCTIONS?
<BLANKLINE>
>>> yes
SOMEWHERE NEARBY IS COLOSSAL CAVE, WHERE OTHERS HAVE FOUND FORTUNES IN
TREASURE AND GOLD, THOUGH IT IS RUMORED THAT SOME WHO ENTER ARE NEVER
SEEN AGAIN.  MAGIC IS SAID TO WORK IN THE CAVE.  I WILL BE YOUR EYES
AND HANDS.  DIRECT ME WITH COMMANDS OF 1 OR 2 WORDS.  I SHOULD WARN
YOU THAT I LOOK AT ONLY THE FIRST FIVE LETTERS OF EACH WORD, SO YOU'LL
HAVE TO ENTER "NORTHEAST" AS "NE" TO DISTINGUISH IT FROM "NORTH".
(SHOULD YOU GET STUCK, TYPE "HELP" FOR SOME GENERAL HINTS.  FOR INFOR-
MATION ON HOW TO END YOUR ADVENTURE, ETC., TYPE "INFO".)
                              - - -
THIS PROGRAM WAS ORIGINALLY DEVELOPED BY WILLIE CROWTHER.  MOST OF THE
FEATURES OF THE CURRENT PROGRAM WERE ADDED BY DON WOODS (DON @ SU-AI).
CONTACT DON IF YOU HAVE ANY QUESTIONS, COMMENTS, ETC.
<BLANKLINE>
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.
<BLANKLINE>
>>> cave
I DON'T KNOW WHERE THE CAVE IS, BUT HEREABOUTS NO STREAM CAN RUN ON
THE SURFACE FOR LONG.  I WOULD TRY THE STREAM.
<BLANKLINE>
YOU'RE AT END OF ROAD AGAIN.
<BLANKLINE>
>>> inven()
YOU'RE NOT CARRYING ANYTHING.
<BLANKLINE>
>>> west
YOU HAVE WALKED UP A HILL, STILL IN THE FOREST.  THE ROAD SLOPES BACK
DOWN THE OTHER SIDE OF THE HILL.  THERE IS A BUILDING IN THE DISTANCE.
<BLANKLINE>
>>> e
YOU'RE AT END OF ROAD AGAIN.
<BLANKLINE>
>>> e
YOU ARE INSIDE A BUILDING, A WELL HOUSE FOR A LARGE SPRING.
<BLANKLINE>
THERE ARE SOME KEYS ON THE GROUND HERE.
<BLANKLINE>
THERE IS A SHINY BRASS LAMP NEARBY.
<BLANKLINE>
THERE IS FOOD HERE.
<BLANKLINE>
THERE IS A BOTTLE OF WATER HERE.
<BLANKLINE>
>>> downs
THE STREAM FLOWS OUT THROUGH A PAIR OF 1 FOOT DIAMETER SEWER PIPES.
IT WOULD BE ADVISABLE TO USE THE EXIT.
<BLANKLINE>
YOU'RE INSIDE BUILDING.
<BLANKLINE>
THERE ARE SOME KEYS ON THE GROUND HERE.
<BLANKLINE>
THERE IS A SHINY BRASS LAMP NEARBY.
<BLANKLINE>
THERE IS FOOD HERE.
<BLANKLINE>
THERE IS A BOTTLE OF WATER HERE.
<BLANKLINE>
>>> west
YOU'RE AT END OF ROAD AGAIN.
<BLANKLINE>
>>> s
YOU ARE IN A VALLEY IN THE FOREST BESIDE A STREAM TUMBLING ALONG A
ROCKY BED.
<BLANKLINE>
>>> s
AT YOUR FEET ALL THE WATER OF THE STREAM SPLASHES INTO A 2-INCH SLIT
IN THE ROCK.  DOWNSTREAM THE STREAMBED IS BARE ROCK.
<BLANKLINE>
>>> s
YOU ARE IN A 20-FOOT DEPRESSION FLOORED WITH BARE DIRT.  SET INTO THE
DIRT IS A STRONG STEEL GRATE MOUNTED IN CONCRETE.  A DRY STREAMBED
LEADS INTO THE DEPRESSION.
<BLANKLINE>
THE GRATE IS LOCKED.
<BLANKLINE>
>>> d
YOU CAN'T GO THROUGH A LOCKED STEEL GRATE!
<BLANKLINE>
YOU'RE OUTSIDE GRATE.
<BLANKLINE>
THE GRATE IS LOCKED.
<BLANKLINE>
>>> get(grate)
YOU CAN'T BE SERIOUS!
<BLANKLINE>
>>> enter
YOU CAN'T GO THROUGH A LOCKED STEEL GRATE!
<BLANKLINE>
YOU'RE OUTSIDE GRATE.
<BLANKLINE>
THE GRATE IS LOCKED.
<BLANKLINE>
ARE YOU TRYING TO GET INTO THE CAVE?
<BLANKLINE>
>>> no
OK
<BLANKLINE>
>>> house
YOU'RE AT END OF ROAD AGAIN.
<BLANKLINE>
>>> enter
YOU'RE INSIDE BUILDING.
<BLANKLINE>
THERE ARE SOME KEYS ON THE GROUND HERE.
<BLANKLINE>
THERE IS A SHINY BRASS LAMP NEARBY.
<BLANKLINE>
THERE IS FOOD HERE.
<BLANKLINE>
THERE IS A BOTTLE OF WATER HERE.
<BLANKLINE>
>>> carry(keys)
OK
<BLANKLINE>
>>> inven
YOU ARE CURRENTLY HOLDING THE FOLLOWING:
<BLANKLINE>
SET OF KEYS
<BLANKLINE>
>>> leave
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.
<BLANKLINE>
>>> depre
YOU'RE OUTSIDE GRATE.
<BLANKLINE>
THE GRATE IS LOCKED.
<BLANKLINE>
>>> unloc(grate)
THE GRATE IS NOW UNLOCKED.
<BLANKLINE>
>>> d
YOU ARE IN A SMALL CHAMBER BENEATH A 3X3 STEEL GRATE TO THE SURFACE.
A LOW CRAWL OVER COBBLES LEADS INWARD TO THE WEST.
<BLANKLINE>
THE GRATE IS OPEN.
<BLANKLINE>
>>> s
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU'RE BELOW THE GRATE.
<BLANKLINE>
THE GRATE IS OPEN.
<BLANKLINE>
>>> west
YOU ARE CRAWLING OVER COBBLES IN A LOW PASSAGE.  THERE IS A DIM LIGHT
AT THE EAST END OF THE PASSAGE.
<BLANKLINE>
THERE IS A SMALL WICKER CAGE DISCARDED NEARBY.
<BLANKLINE>
>>> take(cage)
OK
<BLANKLINE>
>>> west
IT IS NOW PITCH DARK.  IF YOU PROCEED YOU WILL LIKELY FALL INTO A PIT.
<BLANKLINE>
>>> west
YOU FELL INTO A PIT AND BROKE EVERY BONE IN YOUR BODY!
<BLANKLINE>
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?
<BLANKLINE>
>>> save
PLEASE ANSWER THE QUESTION.
<BLANKLINE>
>>> 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....
<BLANKLINE>
YOU'RE INSIDE BUILDING.
<BLANKLINE>
THERE IS A SHINY BRASS LAMP NEARBY.
<BLANKLINE>
THERE IS FOOD HERE.
<BLANKLINE>
THERE IS A BOTTLE OF WATER HERE.
<BLANKLINE>
>>> get(lamp)
OK
<BLANKLINE>
>>> leave
YOU'RE AT END OF ROAD AGAIN.
<BLANKLINE>
>>> depre
YOU'RE OUTSIDE GRATE.
<BLANKLINE>
THE GRATE IS OPEN.
<BLANKLINE>
>>> d
YOU'RE BELOW THE GRATE.
<BLANKLINE>
THE GRATE IS OPEN.
<BLANKLINE>
>>> west
YOU'RE IN COBBLE CRAWL.
<BLANKLINE>
>>> west
IT IS NOW PITCH DARK.  IF YOU PROCEED YOU WILL LIKELY FALL INTO A PIT.
<BLANKLINE>
>>> light(lamp)
YOUR LAMP IS NOW ON.
<BLANKLINE>
YOU ARE IN A DEBRIS ROOM FILLED WITH STUFF WASHED IN FROM THE SURFACE.
A LOW WIDE PASSAGE WITH COBBLES BECOMES PLUGGED WITH MUD AND DEBRIS
HERE, BUT AN AWKWARD CANYON LEADS UPWARD AND WEST.  A NOTE ON THE WALL
SAYS "MAGIC WORD XYZZY".
<BLANKLINE>
A THREE FOOT BLACK ROD WITH A RUSTY STAR ON AN END LIES NEARBY.
<BLANKLINE>
>>> get(rod)
OK
<BLANKLINE>
>>> up
YOU ARE IN AN AWKWARD SLOPING EAST/WEST CANYON.
<BLANKLINE>
THERE ARE SOME KEYS ON THE GROUND HERE.
<BLANKLINE>
THERE IS A SMALL WICKER CAGE DISCARDED NEARBY.
<BLANKLINE>
>>> get(keys)
OK
<BLANKLINE>
>>> u
YOU ARE IN A SPLENDID CHAMBER THIRTY FEET HIGH.  THE WALLS ARE FROZEN
RIVERS OF ORANGE STONE.  AN AWKWARD CANYON AND A GOOD PASSAGE EXIT
FROM EAST AND WEST SIDES OF THE CHAMBER.
<BLANKLINE>
A CHEERFUL LITTLE BIRD IS SITTING HERE SINGING.
<BLANKLINE>
>>> get(bird)
THE BIRD WAS UNAFRAID WHEN YOU ENTERED, BUT AS YOU APPROACH IT BECOMES
DISTURBED AND YOU CANNOT CATCH IT.
<BLANKLINE>
>>> inventory
YOU ARE CURRENTLY HOLDING THE FOLLOWING:
<BLANKLINE>
SET OF KEYS
BRASS LANTERN
BLACK ROD
<BLANKLINE>
>>> drop(rod)
OK
<BLANKLINE>
>>> inven
YOU ARE CURRENTLY HOLDING THE FOLLOWING:
<BLANKLINE>
SET OF KEYS
BRASS LANTERN
<BLANKLINE>
>>> get(bird)
YOU CAN CATCH THE BIRD, BUT YOU CANNOT CARRY IT.
<BLANKLINE>
>>> e
YOU ARE IN AN AWKWARD SLOPING EAST/WEST CANYON.
<BLANKLINE>
THERE IS A SMALL WICKER CAGE DISCARDED NEARBY.
<BLANKLINE>
>>> get(cage)
OK
<BLANKLINE>
>>> west
YOU'RE IN BIRD CHAMBER.
<BLANKLINE>
A THREE FOOT BLACK ROD WITH A RUSTY STAR ON AN END LIES NEARBY.
<BLANKLINE>
A CHEERFUL LITTLE BIRD IS SITTING HERE SINGING.
<BLANKLINE>
>>> get(bird)
OK
<BLANKLINE>
>>> get(rod)
OK
<BLANKLINE>
>>> look
SORRY, BUT I AM NOT ALLOWED TO GIVE MORE DETAIL.  I WILL REPEAT THE
LONG DESCRIPTION OF YOUR LOCATION.
<BLANKLINE>
YOU ARE IN A SPLENDID CHAMBER THIRTY FEET HIGH.  THE WALLS ARE FROZEN
RIVERS OF ORANGE STONE.  AN AWKWARD CANYON AND A GOOD PASSAGE EXIT
FROM EAST AND WEST SIDES OF THE CHAMBER.
<BLANKLINE>
>>> west
AT YOUR FEET IS A SMALL PIT BREATHING TRACES OF WHITE MIST.  AN EAST
PASSAGE ENDS HERE EXCEPT FOR A SMALL CRACK LEADING ON.
<BLANKLINE>
ROUGH STONE STEPS LEAD DOWN THE PIT.
<BLANKLINE>
>>> west
IF YOU PREFER, SIMPLY TYPE W RATHER THAN WEST.
<BLANKLINE>
THE CRACK IS FAR TOO SMALL FOR YOU TO FOLLOW.
<BLANKLINE>
YOU'RE AT TOP OF SMALL PIT.
<BLANKLINE>
ROUGH STONE STEPS LEAD DOWN THE PIT.
<BLANKLINE>
>>> look
SORRY, BUT I AM NOT ALLOWED TO GIVE MORE DETAIL.  I WILL REPEAT THE
LONG DESCRIPTION OF YOUR LOCATION.
<BLANKLINE>
AT YOUR FEET IS A SMALL PIT BREATHING TRACES OF WHITE MIST.  AN EAST
PASSAGE ENDS HERE EXCEPT FOR A SMALL CRACK LEADING ON.
<BLANKLINE>
ROUGH STONE STEPS LEAD DOWN THE PIT.
<BLANKLINE>
>>> d
YOU ARE AT ONE END OF A VAST HALL STRETCHING FORWARD OUT OF SIGHT TO
THE WEST.  THERE ARE OPENINGS TO EITHER SIDE.  NEARBY, A WIDE STONE
STAIRCASE LEADS DOWNWARD.  THE HALL IS FILLED WITH WISPS OF WHITE MIST
SWAYING TO AND FRO ALMOST AS IF ALIVE.  A COLD WIND BLOWS UP THE
STAIRCASE.  THERE IS A PASSAGE AT THE TOP OF A DOME BEHIND YOU.
<BLANKLINE>
ROUGH STONE STEPS LEAD UP THE DOME.
<BLANKLINE>
>>> w
YOU ARE ON THE EAST BANK OF A FISSURE SLICING CLEAR ACROSS THE HALL.
THE MIST IS QUITE THICK HERE, AND THE FISSURE IS TOO WIDE TO JUMP.
<BLANKLINE>
>>> w
THERE IS NO WAY ACROSS THE FISSURE.
<BLANKLINE>
YOU'RE ON EAST BANK OF FISSURE.
<BLANKLINE>
>>> e
YOU'RE IN HALL OF MISTS.
<BLANKLINE>
ROUGH STONE STEPS LEAD UP THE DOME.
<BLANKLINE>
>>> n
YOU ARE IN THE HALL OF THE MOUNTAIN KING, WITH PASSAGES OFF IN ALL
DIRECTIONS.
<BLANKLINE>
A HUGE GREEN FIERCE SNAKE BARS THE WAY!
<BLANKLINE>
>>> s
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU'RE IN HALL OF MT KING
<BLANKLINE>
A HUGE GREEN FIERCE SNAKE BARS THE WAY!
<BLANKLINE>
>>> up
YOU'RE IN HALL OF MISTS.
<BLANKLINE>
ROUGH STONE STEPS LEAD UP THE DOME.
<BLANKLINE>
>>> down
YOU'RE IN HALL OF MT KING
<BLANKLINE>
A HUGE GREEN FIERCE SNAKE BARS THE WAY!
<BLANKLINE>
>>> kill(snake)
ATTACKING THE SNAKE BOTH DOESN'T WORK AND IS VERY DANGEROUS.
<BLANKLINE>
>>> drop(cage)
OK
<BLANKLINE>
>>> look
SORRY, BUT I AM NOT ALLOWED TO GIVE MORE DETAIL.  I WILL REPEAT THE
LONG DESCRIPTION OF YOUR LOCATION.
<BLANKLINE>
YOU ARE IN THE HALL OF THE MOUNTAIN KING, WITH PASSAGES OFF IN ALL
DIRECTIONS.
<BLANKLINE>
THERE IS A SMALL WICKER CAGE DISCARDED NEARBY.
<BLANKLINE>
THERE IS A LITTLE BIRD IN THE CAGE.
<BLANKLINE>
A HUGE GREEN FIERCE SNAKE BARS THE WAY!
<BLANKLINE>
>>> get(bird)
OK
<BLANKLINE>
>>> look
YOU ARE IN THE HALL OF THE MOUNTAIN KING, WITH PASSAGES OFF IN ALL
DIRECTIONS.
<BLANKLINE>
A HUGE GREEN FIERCE SNAKE BARS THE WAY!
<BLANKLINE>
>>> inven
YOU ARE CURRENTLY HOLDING THE FOLLOWING:
<BLANKLINE>
SET OF KEYS
BRASS LANTERN
WICKER CAGE
BLACK ROD
LITTLE BIRD IN CAGE
<BLANKLINE>
>>> drop(bird)
THE LITTLE BIRD ATTACKS THE GREEN SNAKE, AND IN AN ASTOUNDING FLURRY
DRIVES THE SNAKE AWAY.
<BLANKLINE>
>>> look
YOU ARE IN THE HALL OF THE MOUNTAIN KING, WITH PASSAGES OFF IN ALL
DIRECTIONS.
<BLANKLINE>
A CHEERFUL LITTLE BIRD IS SITTING HERE SINGING.
<BLANKLINE>
>>> score
IF YOU WERE TO QUIT NOW, YOU WOULD SCORE 42 OUT OF A POSSIBLE 350.
<BLANKLINE>
DO YOU INDEED WISH TO QUIT NOW?
<BLANKLINE>
>>> no
OK
<BLANKLINE>
>>> s
A LITTLE DWARF JUST WALKED AROUND A CORNER, SAW YOU, THREW A LITTLE
AXE AT YOU WHICH MISSED, CURSED, AND RAN AWAY.
<BLANKLINE>
YOU ARE IN THE SOUTH SIDE CHAMBER.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
THERE IS PRECIOUS JEWELRY HERE!
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> score
IF YOU WERE TO QUIT NOW, YOU WOULD SCORE 44 OUT OF A POSSIBLE 350.
<BLANKLINE>
DO YOU INDEED WISH TO QUIT NOW?
<BLANKLINE>
>>> no
OK
<BLANKLINE>
>>> get(jewelry)
OK
<BLANKLINE>
>>> n
THERE IS A THREATENING LITTLE DWARF IN THE ROOM WITH YOU!
<BLANKLINE>
ONE SHARP NASTY KNIFE IS THROWN AT YOU!
<BLANKLINE>
IT MISSES!
<BLANKLINE>
YOU'RE IN HALL OF MT KING
<BLANKLINE>
A CHEERFUL LITTLE BIRD IS SITTING HERE SINGING.
<BLANKLINE>
>>> w
THERE IS A THREATENING LITTLE DWARF IN THE ROOM WITH YOU!
<BLANKLINE>
YOU ARE IN THE WEST SIDE CHAMBER OF THE HALL OF THE MOUNTAIN KING.
A PASSAGE CONTINUES WEST AND UP HERE.
<BLANKLINE>
THERE ARE MANY COINS HERE!
<BLANKLINE>
>>> score
IF YOU WERE TO QUIT NOW, YOU WOULD SCORE 46 OUT OF A POSSIBLE 350.
<BLANKLINE>
DO YOU INDEED WISH TO QUIT NOW?
<BLANKLINE>
>>> no
OK
<BLANKLINE>
>>> get(coins)
OK
<BLANKLINE>
>>> w
THERE ARE 2 THREATENING LITTLE DWARVES IN THE ROOM WITH YOU.
<BLANKLINE>
YOU ARE AT A CROSSOVER OF A HIGH N/S PASSAGE AND A LOW E/W ONE.
<BLANKLINE>
>>> n
THERE ARE 2 THREATENING LITTLE DWARVES IN THE ROOM WITH YOU.
<BLANKLINE>
DEAD END
<BLANKLINE>
>>> s
A LITTLE DWARF WITH A BIG KNIFE BLOCKS YOUR WAY.
<BLANKLINE>
THERE ARE 2 THREATENING LITTLE DWARVES IN THE ROOM WITH YOU.
<BLANKLINE>
2 OF THEM THROW KNIVES AT YOU!
<BLANKLINE>
NONE OF THEM HIT YOU!
<BLANKLINE>
DEAD END
<BLANKLINE>
>>> throw(axe)
YOU ATTACK A LITTLE DWARF, BUT HE DODGES OUT OF THE WAY.
<BLANKLINE>
THERE ARE 2 THREATENING LITTLE DWARVES IN THE ROOM WITH YOU.
<BLANKLINE>
2 OF THEM THROW KNIVES AT YOU!
<BLANKLINE>
NONE OF THEM HIT YOU!
<BLANKLINE>
DEAD END
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> throw(axe)
YOU ATTACK A LITTLE DWARF, BUT HE DODGES OUT OF THE WAY.
<BLANKLINE>
THERE ARE 2 THREATENING LITTLE DWARVES IN THE ROOM WITH YOU.
<BLANKLINE>
2 OF THEM THROW KNIVES AT YOU!
<BLANKLINE>
NONE OF THEM HIT YOU!
<BLANKLINE>
DEAD END
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> throw(axe)
YOU ATTACK A LITTLE DWARF, BUT HE DODGES OUT OF THE WAY.
<BLANKLINE>
THERE ARE 2 THREATENING LITTLE DWARVES IN THE ROOM WITH YOU.
<BLANKLINE>
2 OF THEM THROW KNIVES AT YOU!
<BLANKLINE>
NONE OF THEM HIT YOU!
<BLANKLINE>
DEAD END
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> throw(axe)
YOU KILLED A LITTLE DWARF.  THE BODY VANISHES IN A CLOUD OF GREASY
BLACK SMOKE.
<BLANKLINE>
THERE IS A THREATENING LITTLE DWARF IN THE ROOM WITH YOU!
<BLANKLINE>
ONE SHARP NASTY KNIFE IS THROWN AT YOU!
<BLANKLINE>
IT MISSES!
<BLANKLINE>
DEAD END
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> throw(axe)
YOU ATTACK A LITTLE DWARF, BUT HE DODGES OUT OF THE WAY.
<BLANKLINE>
THERE IS A THREATENING LITTLE DWARF IN THE ROOM WITH YOU!
<BLANKLINE>
ONE SHARP NASTY KNIFE IS THROWN AT YOU!
<BLANKLINE>
IT MISSES!
<BLANKLINE>
DEAD END
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> throw(axe)
YOU KILLED A LITTLE DWARF.
<BLANKLINE>
DEAD END
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> s
YOU ARE AT A CROSSOVER OF A HIGH N/S PASSAGE AND A LOW E/W ONE.
<BLANKLINE>
>>> w
YOU ARE AT THE EAST END OF A VERY LONG HALL APPARENTLY WITHOUT SIDE
CHAMBERS.  TO THE EAST A LOW WIDE CRAWL SLANTS UP.  TO THE NORTH A
ROUND TWO FOOT HOLE SLANTS DOWN.
<BLANKLINE>
>>> w
YOU ARE AT THE WEST END OF A VERY LONG FEATURELESS HALL.  THE HALL
JOINS UP WITH A NARROW NORTH/SOUTH PASSAGE.
<BLANKLINE>
>>> n
YOU ARE AT A CROSSOVER OF A HIGH N/S PASSAGE AND A LOW E/W ONE.
<BLANKLINE>
>>> n
DEAD END
<BLANKLINE>
>>> back
YOU ARE AT A CROSSOVER OF A HIGH N/S PASSAGE AND A LOW E/W ONE.
<BLANKLINE>
>>> w
YOU'RE AT EAST END OF LONG HALL.
<BLANKLINE>
>>> e
YOU ARE AT THE WEST END OF HALL OF MISTS.  A LOW WIDE CRAWL CONTINUES
WEST AND ANOTHER GOES NORTH.  TO THE SOUTH IS A LITTLE PASSAGE 6 FEET
OFF THE FLOOR.
<BLANKLINE>
>>> e
YOU ARE ON THE WEST SIDE OF THE FISSURE IN THE HALL OF MISTS.
<BLANKLINE>
THERE ARE DIAMONDS HERE!
<BLANKLINE>
>>> get(diamonds)
YOU CAN'T CARRY ANYTHING MORE.  YOU'LL HAVE TO DROP SOMETHING FIRST.
<BLANKLINE>
>>> inventory
YOU ARE CURRENTLY HOLDING THE FOLLOWING:
<BLANKLINE>
SET OF KEYS
BRASS LANTERN
WICKER CAGE
BLACK ROD
DWARF'S AXE
PRECIOUS JEWELRY
RARE COINS
<BLANKLINE>
>>> drop(cage)
OK
<BLANKLINE>
>>> get(diamonds)
OK
<BLANKLINE>
>>> n
YOU HAVE CRAWLED THROUGH A VERY LOW WIDE PASSAGE PARALLEL TO AND NORTH
OF THE HALL OF MISTS.
<BLANKLINE>
YOU'RE AT WEST END OF HALL OF MISTS.
<BLANKLINE>
>>> w
THERE ARE 2 THREATENING LITTLE DWARVES IN THE ROOM WITH YOU.
<BLANKLINE>
YOU'RE AT EAST END OF LONG HALL.
<BLANKLINE>
>>> throw(axe)
YOU ATTACK A LITTLE DWARF, BUT HE DODGES OUT OF THE WAY.
<BLANKLINE>
THERE ARE 2 THREATENING LITTLE DWARVES IN THE ROOM WITH YOU.
<BLANKLINE>
2 OF THEM THROW KNIVES AT YOU!
<BLANKLINE>
NONE OF THEM HIT YOU!
<BLANKLINE>
YOU'RE AT EAST END OF LONG HALL.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> throw(axe)
YOU ATTACK A LITTLE DWARF, BUT HE DODGES OUT OF THE WAY.
<BLANKLINE>
THERE ARE 2 THREATENING LITTLE DWARVES IN THE ROOM WITH YOU.
<BLANKLINE>
2 OF THEM THROW KNIVES AT YOU!
<BLANKLINE>
NONE OF THEM HIT YOU!
<BLANKLINE>
YOU'RE AT EAST END OF LONG HALL.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> throw(axe)
YOU ATTACK A LITTLE DWARF, BUT HE DODGES OUT OF THE WAY.
<BLANKLINE>
THERE ARE 2 THREATENING LITTLE DWARVES IN THE ROOM WITH YOU.
<BLANKLINE>
2 OF THEM THROW KNIVES AT YOU!
<BLANKLINE>
NONE OF THEM HIT YOU!
<BLANKLINE>
YOU ARE AT THE EAST END OF A VERY LONG HALL APPARENTLY WITHOUT SIDE
CHAMBERS.  TO THE EAST A LOW WIDE CRAWL SLANTS UP.  TO THE NORTH A
ROUND TWO FOOT HOLE SLANTS DOWN.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> throw(axe)
YOU ATTACK A LITTLE DWARF, BUT HE DODGES OUT OF THE WAY.
<BLANKLINE>
THERE ARE 2 THREATENING LITTLE DWARVES IN THE ROOM WITH YOU.
<BLANKLINE>
2 OF THEM THROW KNIVES AT YOU!
<BLANKLINE>
NONE OF THEM HIT YOU!
<BLANKLINE>
YOU'RE AT EAST END OF LONG HALL.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> throw(axe)
YOU ATTACK A LITTLE DWARF, BUT HE DODGES OUT OF THE WAY.
<BLANKLINE>
THERE ARE 2 THREATENING LITTLE DWARVES IN THE ROOM WITH YOU.
<BLANKLINE>
2 OF THEM THROW KNIVES AT YOU!
<BLANKLINE>
NONE OF THEM HIT YOU!
<BLANKLINE>
YOU'RE AT EAST END OF LONG HALL.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> throw(axe)
YOU KILLED A LITTLE DWARF.
<BLANKLINE>
THERE IS A THREATENING LITTLE DWARF IN THE ROOM WITH YOU!
<BLANKLINE>
ONE SHARP NASTY KNIFE IS THROWN AT YOU!
<BLANKLINE>
IT MISSES!
<BLANKLINE>
YOU'RE AT EAST END OF LONG HALL.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> throw(axe)
YOU ATTACK A LITTLE DWARF, BUT HE DODGES OUT OF THE WAY.
<BLANKLINE>
THERE IS A THREATENING LITTLE DWARF IN THE ROOM WITH YOU!
<BLANKLINE>
ONE SHARP NASTY KNIFE IS THROWN AT YOU!
<BLANKLINE>
IT MISSES!
<BLANKLINE>
YOU'RE AT EAST END OF LONG HALL.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> throw(axe)
YOU ATTACK A LITTLE DWARF, BUT HE DODGES OUT OF THE WAY.
<BLANKLINE>
THERE IS A THREATENING LITTLE DWARF IN THE ROOM WITH YOU!
<BLANKLINE>
ONE SHARP NASTY KNIFE IS THROWN AT YOU!
<BLANKLINE>
IT MISSES!
<BLANKLINE>
YOU ARE AT THE EAST END OF A VERY LONG HALL APPARENTLY WITHOUT SIDE
CHAMBERS.  TO THE EAST A LOW WIDE CRAWL SLANTS UP.  TO THE NORTH A
ROUND TWO FOOT HOLE SLANTS DOWN.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> throw(axe)
YOU ATTACK A LITTLE DWARF, BUT HE DODGES OUT OF THE WAY.
<BLANKLINE>
THERE IS A THREATENING LITTLE DWARF IN THE ROOM WITH YOU!
<BLANKLINE>
ONE SHARP NASTY KNIFE IS THROWN AT YOU!
<BLANKLINE>
IT GETS YOU!
<BLANKLINE>
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?
<BLANKLINE>
>>> get(axe)
PLEASE ANSWER THE QUESTION.
<BLANKLINE>
>>> yes
OKAY, NOW WHERE DID I PUT MY ORANGE SMOKE?....  >POOF!<
EVERYTHING DISAPPEARS IN A DENSE CLOUD OF ORANGE SMOKE.
<BLANKLINE>
YOU'RE INSIDE BUILDING.
<BLANKLINE>
THERE IS FOOD HERE.
<BLANKLINE>
THERE IS A BOTTLE OF WATER HERE.
<BLANKLINE>
>>> leave
YOU'RE AT END OF ROAD AGAIN.
<BLANKLINE>
THERE IS A SHINY BRASS LAMP NEARBY.
<BLANKLINE>
>>> get(lamp)
OK
<BLANKLINE>
>>> enter
YOU ARE INSIDE A BUILDING, A WELL HOUSE FOR A LARGE SPRING.
<BLANKLINE>
THERE IS FOOD HERE.
<BLANKLINE>
THERE IS A BOTTLE OF WATER HERE.
<BLANKLINE>
>>> xyzzy
IT IS NOW PITCH DARK.  IF YOU PROCEED YOU WILL LIKELY FALL INTO A PIT.
<BLANKLINE>

>>> import io
>>> savefile = io.BytesIO()
>>> save(savefile)
GAME SAVED
<BLANKLINE>

>>> w
IT IS NOW PITCH DARK.  IF YOU PROCEED YOU WILL LIKELY FALL INTO A PIT.
<BLANKLINE>
>>> w
IT IS NOW PITCH DARK.  IF YOU PROCEED YOU WILL LIKELY FALL INTO A PIT.
<BLANKLINE>
>>> w
IT IS NOW PITCH DARK.  IF YOU PROCEED YOU WILL LIKELY FALL INTO A PIT.
<BLANKLINE>
>>> w
THE CRACK IS FAR TOO SMALL FOR YOU TO FOLLOW.
<BLANKLINE>
YOU FELL INTO A PIT AND BROKE EVERY BONE IN YOUR BODY!
<BLANKLINE>
NOW YOU'VE REALLY DONE IT!  I'M OUT OF ORANGE SMOKE!  YOU DON'T EXPECT
ME TO DO A DECENT REINCARNATION WITHOUT ANY ORANGE SMOKE, DO YOU?
<BLANKLINE>
>>> no
OK
<BLANKLINE>
<BLANKLINE>
YOU SCORED 32 OUT OF A POSSIBLE 350 USING 132 TURNS.
<BLANKLINE>
YOU ARE OBVIOUSLY A RANK AMATEUR.  BETTER LUCK NEXT TIME.
<BLANKLINE>
TO ACHIEVE THE NEXT HIGHER RATING, YOU NEED 69 MORE POINTS
<BLANKLINE>
>>> w
YOU HAVE GOTTEN YOURSELF KILLED.
<BLANKLINE>

>>> savefile.seek(0)
0
>>> adventure.resume(savefile)
GAME RESTORED
<BLANKLINE>

>>> on(lamp)
YOUR LAMP IS NOW ON.
<BLANKLINE>
YOU'RE IN DEBRIS ROOM.
<BLANKLINE>
>>> w
YOU ARE IN AN AWKWARD SLOPING EAST/WEST CANYON.
<BLANKLINE>
>>> w
YOU'RE IN BIRD CHAMBER.
<BLANKLINE>
>>> w
YOU'RE AT TOP OF SMALL PIT.
<BLANKLINE>
ROUGH STONE STEPS LEAD DOWN THE PIT.
<BLANKLINE>
>>> d
YOU'RE IN HALL OF MISTS.
<BLANKLINE>
ROUGH STONE STEPS LEAD UP THE DOME.
<BLANKLINE>
>>> d
YOU'RE IN HALL OF MT KING
<BLANKLINE>
A CHEERFUL LITTLE BIRD IS SITTING HERE SINGING.
<BLANKLINE>
>>> w
YOU ARE IN THE WEST SIDE CHAMBER OF THE HALL OF THE MOUNTAIN KING.
A PASSAGE CONTINUES WEST AND UP HERE.
<BLANKLINE>
>>> w
YOU ARE AT A CROSSOVER OF A HIGH N/S PASSAGE AND A LOW E/W ONE.
<BLANKLINE>
>>> w
YOU'RE AT EAST END OF LONG HALL.
<BLANKLINE>
THERE ARE SOME KEYS ON THE GROUND HERE.
<BLANKLINE>
A THREE FOOT BLACK ROD WITH A RUSTY STAR ON AN END LIES NEARBY.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
THERE ARE DIAMONDS HERE!
<BLANKLINE>
THERE IS PRECIOUS JEWELRY HERE!
<BLANKLINE>
THERE ARE MANY COINS HERE!
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> get(keys)
OK
<BLANKLINE>
>>> get(rod)
OK
<BLANKLINE>
>>> get(diamonds)
OK
<BLANKLINE>
>>> get(jewel)
OK
<BLANKLINE>
>>> get(coins)
OK
<BLANKLINE>
>>> e
YOU'RE AT WEST END OF HALL OF MISTS.
<BLANKLINE>
>>> e
YOU ARE ON THE WEST SIDE OF THE FISSURE IN THE HALL OF MISTS.
<BLANKLINE>
THERE IS A SMALL WICKER CAGE DISCARDED NEARBY.
<BLANKLINE>
>>> jump
I DON'T KNOW HOW TO APPLY THAT WORD HERE.
<BLANKLINE>
YOU ARE ON THE WEST SIDE OF THE FISSURE IN THE HALL OF MISTS.
<BLANKLINE>
THERE IS A SMALL WICKER CAGE DISCARDED NEARBY.
<BLANKLINE>
>>> swing(rod)
A CRYSTAL BRIDGE NOW SPANS THE FISSURE.
<BLANKLINE>
>>> look
YOU ARE ON THE WEST SIDE OF THE FISSURE IN THE HALL OF MISTS.
<BLANKLINE>
THERE IS A SMALL WICKER CAGE DISCARDED NEARBY.
<BLANKLINE>
A CRYSTAL BRIDGE NOW SPANS THE FISSURE.
<BLANKLINE>
>>> jump
I RESPECTFULLY SUGGEST YOU GO ACROSS THE BRIDGE INSTEAD OF JUMPING.
<BLANKLINE>
YOU ARE ON THE WEST SIDE OF THE FISSURE IN THE HALL OF MISTS.
<BLANKLINE>
THERE IS A SMALL WICKER CAGE DISCARDED NEARBY.
<BLANKLINE>
A CRYSTAL BRIDGE NOW SPANS THE FISSURE.
<BLANKLINE>
>>> over
YOU'RE ON EAST BANK OF FISSURE.
<BLANKLINE>
A CRYSTAL BRIDGE NOW SPANS THE FISSURE.
<BLANKLINE>
>>> e
YOU'RE IN HALL OF MISTS.
<BLANKLINE>
ROUGH STONE STEPS LEAD UP THE DOME.
<BLANKLINE>
>>> s
THIS IS A LOW ROOM WITH A CRUDE NOTE ON THE WALL.  THE NOTE SAYS,
"YOU WON'T GET IT UP THE STEPS".
<BLANKLINE>
THERE IS A LARGE SPARKLING NUGGET OF GOLD HERE!
<BLANKLINE>
>>> get(gold)
YOU CAN'T CARRY ANYTHING MORE.  YOU'LL HAVE TO DROP SOMETHING FIRST.
<BLANKLINE>
>>> drop(rod)
OK
<BLANKLINE>
>>> get(gold)
OK
<BLANKLINE>
>>> n
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.
<BLANKLINE>
>>> u
THE DOME IS UNCLIMBABLE.
<BLANKLINE>
YOU'RE IN HALL OF MISTS.
<BLANKLINE>
>>> drop(gold)
OK
<BLANKLINE>
>>> u
YOU'RE AT TOP OF SMALL PIT.
<BLANKLINE>
ROUGH STONE STEPS LEAD DOWN THE PIT.
<BLANKLINE>
>>> e
YOU'RE IN BIRD CHAMBER.
<BLANKLINE>
>>> e
YOU ARE IN AN AWKWARD SLOPING EAST/WEST CANYON.
<BLANKLINE>
>>> e
YOU'RE IN DEBRIS ROOM.
<BLANKLINE>
>>> xyzzy
YOU'RE INSIDE BUILDING.
<BLANKLINE>
THERE IS FOOD HERE.
<BLANKLINE>
THERE IS A BOTTLE OF WATER HERE.
<BLANKLINE>
>>> score
IF YOU WERE TO QUIT NOW, YOU WOULD SCORE 40 OUT OF A POSSIBLE 350.
<BLANKLINE>
DO YOU INDEED WISH TO QUIT NOW?
<BLANKLINE>
>>> no
OK
<BLANKLINE>
>>> inventory
YOU ARE CURRENTLY HOLDING THE FOLLOWING:
<BLANKLINE>
SET OF KEYS
BRASS LANTERN
DWARF'S AXE
SEVERAL DIAMONDS
PRECIOUS JEWELRY
RARE COINS
<BLANKLINE>
>>> drop(diamonds)
OK
<BLANKLINE>
>>> drop(jewelry)
OK
<BLANKLINE>
>>> drop(coins)
OK
<BLANKLINE>
>>> score
IF YOU WERE TO QUIT NOW, YOU WOULD SCORE 70 OUT OF A POSSIBLE 350.
<BLANKLINE>
DO YOU INDEED WISH TO QUIT NOW?
<BLANKLINE>
>>> no
OK
<BLANKLINE>
>>> xyzzy
YOU'RE IN DEBRIS ROOM.
<BLANKLINE>
>>> w
YOU ARE IN AN AWKWARD SLOPING EAST/WEST CANYON.
<BLANKLINE>
>>> w
YOU'RE IN BIRD CHAMBER.
<BLANKLINE>
>>> w
YOU'RE AT TOP OF SMALL PIT.
<BLANKLINE>
ROUGH STONE STEPS LEAD DOWN THE PIT.
<BLANKLINE>
>>> d
YOU'RE IN HALL OF MISTS.
<BLANKLINE>
ROUGH STONE STEPS LEAD UP THE DOME.
<BLANKLINE>
THERE IS A LARGE SPARKLING NUGGET OF GOLD HERE!
<BLANKLINE>
>>> get(gold)
OK
<BLANKLINE>
>>> n
YOU'RE IN HALL OF MT KING
<BLANKLINE>
A CHEERFUL LITTLE BIRD IS SITTING HERE SINGING.
<BLANKLINE>
>>> n
YOU ARE IN A LOW N/S PASSAGE AT A HOLE IN THE FLOOR.  THE HOLE GOES
DOWN TO AN E/W PASSAGE.
<BLANKLINE>
THERE ARE BARS OF SILVER HERE!
<BLANKLINE>
>>> get(silver)
OK
<BLANKLINE>
>>> n
YOU ARE IN A LARGE ROOM, WITH A PASSAGE TO THE SOUTH, A PASSAGE TO THE
WEST, AND A WALL OF BROKEN ROCK TO THE EAST.  THERE IS A LARGE "Y2" ON
A ROCK IN THE ROOM'S CENTER.
<BLANKLINE>
>>> 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.
<BLANKLINE>
THE SHADOWY FIGURE SEEMS TO BE TRYING TO ATTRACT YOUR ATTENTION.
<BLANKLINE>
>>> wave
WAVE WHAT?
<BLANKLINE>
>>> e
YOU'RE AT "Y2".
<BLANKLINE>
>>> e
YOU ARE IN A JUMBLE OF ROCK, WITH CRACKS EVERYWHERE.
<BLANKLINE>
>>> e
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU ARE IN A JUMBLE OF ROCK, WITH CRACKS EVERYWHERE.
<BLANKLINE>
>>> d
YOU'RE AT "Y2".
<BLANKLINE>
>>> s
YOU ARE IN A LOW N/S PASSAGE AT A HOLE IN THE FLOOR.  THE HOLE GOES
DOWN TO AN E/W PASSAGE.
<BLANKLINE>
>>> s
YOU'RE IN HALL OF MT KING
<BLANKLINE>
A CHEERFUL LITTLE BIRD IS SITTING HERE SINGING.
<BLANKLINE>
>>> nw
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU ARE IN THE HALL OF THE MOUNTAIN KING, WITH PASSAGES OFF IN ALL
DIRECTIONS.
<BLANKLINE>
A CHEERFUL LITTLE BIRD IS SITTING HERE SINGING.
<BLANKLINE>
>>> ne
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU'RE IN HALL OF MT KING
<BLANKLINE>
A CHEERFUL LITTLE BIRD IS SITTING HERE SINGING.
<BLANKLINE>
>>> se
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU'RE IN HALL OF MT KING
<BLANKLINE>
A CHEERFUL LITTLE BIRD IS SITTING HERE SINGING.
<BLANKLINE>
>>> sw
YOU ARE IN A SECRET CANYON WHICH HERE RUNS E/W.  IT CROSSES OVER A
VERY TIGHT CANYON 15 FEET BELOW.  IF YOU GO DOWN YOU MAY NOT BE ABLE
TO GET BACK UP.
<BLANKLINE>
>>> w
YOU ARE IN A SECRET CANYON WHICH EXITS TO THE NORTH AND EAST.
<BLANKLINE>
A HUGE GREEN FIERCE DRAGON BARS THE WAY!
<BLANKLINE>
THE DRAGON IS SPRAWLED OUT ON A PERSIAN RUG!!
<BLANKLINE>
>>> w
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU ARE IN A SECRET CANYON WHICH EXITS TO THE NORTH AND EAST.
<BLANKLINE>
A HUGE GREEN FIERCE DRAGON BARS THE WAY!
<BLANKLINE>
THE DRAGON IS SPRAWLED OUT ON A PERSIAN RUG!!
<BLANKLINE>
>>> e
YOU'RE IN SECRET E/W CANYON ABOVE TIGHT CANYON.
<BLANKLINE>
>>> e
OUT FROM THE SHADOWS BEHIND YOU POUNCES A BEARDED PIRATE!  "HAR, HAR,"
HE CHORTLES, "I'LL JUST TAKE ALL THIS BOOTY AND HIDE IT AWAY WITH ME
CHEST DEEP IN THE MAZE!"  HE SNATCHES YOUR TREASURE AND VANISHES INTO
THE GLOOM.
<BLANKLINE>
YOU'RE IN HALL OF MT KING
<BLANKLINE>
A CHEERFUL LITTLE BIRD IS SITTING HERE SINGING.
<BLANKLINE>
>>> inventory
YOU ARE CURRENTLY HOLDING THE FOLLOWING:
<BLANKLINE>
SET OF KEYS
BRASS LANTERN
DWARF'S AXE
<BLANKLINE>
>>> get(bird)
YOU CAN CATCH THE BIRD, BUT YOU CANNOT CARRY IT.
<BLANKLINE>
>>> u
YOU'RE IN HALL OF MISTS.
<BLANKLINE>
ROUGH STONE STEPS LEAD UP THE DOME.
<BLANKLINE>
>>> w
YOU'RE ON EAST BANK OF FISSURE.
<BLANKLINE>
A CRYSTAL BRIDGE NOW SPANS THE FISSURE.
<BLANKLINE>
>>> w
YOU ARE ON THE WEST SIDE OF THE FISSURE IN THE HALL OF MISTS.
<BLANKLINE>
THERE IS A SMALL WICKER CAGE DISCARDED NEARBY.
<BLANKLINE>
A CRYSTAL BRIDGE NOW SPANS THE FISSURE.
<BLANKLINE>
>>> get(cage)
OK
<BLANKLINE>
>>> e
YOU'RE ON EAST BANK OF FISSURE.
<BLANKLINE>
A CRYSTAL BRIDGE NOW SPANS THE FISSURE.
<BLANKLINE>
>>> e
YOU'RE IN HALL OF MISTS.
<BLANKLINE>
ROUGH STONE STEPS LEAD UP THE DOME.
<BLANKLINE>
>>> d
YOU'RE IN HALL OF MT KING
<BLANKLINE>
A CHEERFUL LITTLE BIRD IS SITTING HERE SINGING.
<BLANKLINE>
>>> get(bird)
OK
<BLANKLINE>
>>> sw
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU ARE IN THE HALL OF THE MOUNTAIN KING, WITH PASSAGES OFF IN ALL
DIRECTIONS.
<BLANKLINE>
>>> sw
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU'RE IN HALL OF MT KING
<BLANKLINE>
>>> sw
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU'RE IN HALL OF MT KING
<BLANKLINE>
>>> sw
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU'RE IN HALL OF MT KING
<BLANKLINE>
>>> sw
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU'RE IN HALL OF MT KING
<BLANKLINE>
>>> sw
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU ARE IN THE HALL OF THE MOUNTAIN KING, WITH PASSAGES OFF IN ALL
DIRECTIONS.
<BLANKLINE>
>>> sw
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU'RE IN HALL OF MT KING
<BLANKLINE>
>>> sw
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU'RE IN HALL OF MT KING
<BLANKLINE>
>>> sw
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU'RE IN HALL OF MT KING
<BLANKLINE>
>>> sw
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU'RE IN HALL OF MT KING
<BLANKLINE>
>>> sw
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
YOU ARE IN THE HALL OF THE MOUNTAIN KING, WITH PASSAGES OFF IN ALL
DIRECTIONS.
<BLANKLINE>
>>> sw
YOU'RE IN SECRET E/W CANYON ABOVE TIGHT CANYON.
<BLANKLINE>
>>> w
YOU ARE IN A SECRET CANYON WHICH EXITS TO THE NORTH AND EAST.
<BLANKLINE>
A HUGE GREEN FIERCE DRAGON BARS THE WAY!
<BLANKLINE>
THE DRAGON IS SPRAWLED OUT ON A PERSIAN RUG!!
<BLANKLINE>
>>> drop(bird)
THE LITTLE BIRD ATTACKS THE GREEN DRAGON, AND IN AN ASTOUNDING FLURRY
GETS BURNT TO A CINDER.  THE ASHES BLOW AWAY.
<BLANKLINE>
>>> n
THE DRAGON LOOKS RATHER NASTY.  YOU'D BEST NOT TRY TO GET BY.
<BLANKLINE>
YOU ARE IN A SECRET CANYON WHICH EXITS TO THE NORTH AND EAST.
<BLANKLINE>
A HUGE GREEN FIERCE DRAGON BARS THE WAY!
<BLANKLINE>
THE DRAGON IS SPRAWLED OUT ON A PERSIAN RUG!!
<BLANKLINE>
>>> kill(dragon)
WITH WHAT?  YOUR BARE HANDS?
<BLANKLINE>
>>> throw(axe)
THE AXE BOUNCES HARMLESSLY OFF THE DRAGON'S THICK SCALES.
<BLANKLINE>
YOU ARE IN A SECRET CANYON WHICH EXITS TO THE NORTH AND EAST.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
A HUGE GREEN FIERCE DRAGON BARS THE WAY!
<BLANKLINE>
THE DRAGON IS SPRAWLED OUT ON A PERSIAN RUG!!
<BLANKLINE>
>>> yes
I DON'T KNOW THAT WORD.
<BLANKLINE>
>>> kill(dragon)
WITH WHAT?  YOUR BARE HANDS?
<BLANKLINE>
>>> yes
CONGRATULATIONS!  YOU HAVE JUST VANQUISHED A DRAGON WITH YOUR BARE
HANDS!  (UNBELIEVABLE, ISN'T IT?)
<BLANKLINE>
YOU ARE IN A SECRET CANYON WHICH EXITS TO THE NORTH AND EAST.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
THE BODY OF A HUGE GREEN DEAD DRAGON IS LYING OFF TO ONE SIDE.
<BLANKLINE>
THERE IS A PERSIAN RUG SPREAD OUT ON THE FLOOR!
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> get(dragon)
YOU CAN'T BE SERIOUS!
<BLANKLINE>
>>> get(rug)
OK
<BLANKLINE>
>>> n
YOU ARE IN A SECRET N/S CANYON ABOVE A LARGE ROOM.
<BLANKLINE>
>>> n
YOU ARE IN A NORTH/SOUTH CANYON ABOUT 25 FEET ACROSS.  THE FLOOR IS
COVERED BY WHITE MIST SEEPING IN FROM THE NORTH.  THE WALLS EXTEND
UPWARD FOR WELL OVER 100 FEET.  SUSPENDED FROM SOME UNSEEN POINT FAR
ABOVE YOU, AN ENORMOUS TWO-SIDED MIRROR IS HANGING PARALLEL TO AND
MIDWAY BETWEEN THE CANYON WALLS.  (THE MIRROR IS OBVIOUSLY PROVIDED
FOR THE USE OF THE DWARVES, WHO AS YOU KNOW, ARE EXTREMELY VAIN.)  A
SMALL WINDOW CAN BE SEEN IN EITHER WALL, SOME FIFTY FEET UP.
<BLANKLINE>
>>> n
YOU ARE AT THE EDGE OF A LARGE UNDERGROUND RESERVOIR.  AN OPAQUE CLOUD
OF WHITE MIST FILLS THE ROOM AND RISES RAPIDLY UPWARD.  THE LAKE IS
FED BY A STREAM, WHICH TUMBLES OUT OF A HOLE IN THE WALL ABOUT 10 FEET
OVERHEAD AND SPLASHES NOISILY INTO THE WATER SOMEWHERE WITHIN THE
MIST.  THE ONLY PASSAGE GOES BACK TOWARD THE SOUTH.
<BLANKLINE>
>>> s
YOU'RE IN MIRROR CANYON.
<BLANKLINE>
>>> s
YOU ARE IN A SECRET N/S CANYON ABOVE A LARGE ROOM.
<BLANKLINE>
>>> s
YOU ARE IN A SECRET CANYON WHICH EXITS TO THE NORTH AND EAST.
<BLANKLINE>
THE BODY OF A HUGE GREEN DEAD DRAGON IS LYING OFF TO ONE SIDE.
<BLANKLINE>
>>> e
YOU'RE IN SECRET E/W CANYON ABOVE TIGHT CANYON.
<BLANKLINE>
>>> e
YOU'RE IN HALL OF MT KING
<BLANKLINE>
>>> n
YOU ARE IN A LOW N/S PASSAGE AT A HOLE IN THE FLOOR.  THE HOLE GOES
DOWN TO AN E/W PASSAGE.
<BLANKLINE>
>>> n
YOU'RE AT "Y2".
<BLANKLINE>
>>> w
YOU'RE AT WINDOW ON PIT.
<BLANKLINE>
THE SHADOWY FIGURE SEEMS TO BE TRYING TO ATTRACT YOUR ATTENTION.
<BLANKLINE>
>>> e
YOU'RE AT "Y2".
<BLANKLINE>
A HOLLOW VOICE SAYS "PLUGH".
<BLANKLINE>
>>> plugh
YOU'RE INSIDE BUILDING.
<BLANKLINE>
THERE IS FOOD HERE.
<BLANKLINE>
THERE IS A BOTTLE OF WATER HERE.
<BLANKLINE>
THERE ARE DIAMONDS HERE!
<BLANKLINE>
THERE IS PRECIOUS JEWELRY HERE!
<BLANKLINE>
THERE ARE MANY COINS HERE!
<BLANKLINE>
>>> inventory
YOU ARE CURRENTLY HOLDING THE FOLLOWING:
<BLANKLINE>
SET OF KEYS
BRASS LANTERN
WICKER CAGE
DWARF'S AXE
PERSIAN RUG
<BLANKLINE>
>>> score
IF YOU WERE TO QUIT NOW, YOU WOULD SCORE 74 OUT OF A POSSIBLE 350.
<BLANKLINE>
DO YOU INDEED WISH TO QUIT NOW?
<BLANKLINE>
>>> n
OK
<BLANKLINE>
>>> drop(persian)
OK
<BLANKLINE>
>>> score
IF YOU WERE TO QUIT NOW, YOU WOULD SCORE 88 OUT OF A POSSIBLE 350.
<BLANKLINE>
DO YOU INDEED WISH TO QUIT NOW?
<BLANKLINE>
>>> n
OK
<BLANKLINE>
>>> plugh
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.
<BLANKLINE>
>>> s
YOU ARE IN A LOW N/S PASSAGE AT A HOLE IN THE FLOOR.  THE HOLE GOES
DOWN TO AN E/W PASSAGE.
<BLANKLINE>
>>> 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.
<BLANKLINE>
>>> e
YOU ARE ON THE BRINK OF A SMALL CLEAN CLIMBABLE PIT.  A CRAWL LEADS
WEST.
<BLANKLINE>
>>> d
YOU ARE IN THE BOTTOM OF A SMALL PIT WITH A LITTLE STREAM, WHICH
ENTERS AND EXITS THROUGH TINY SLITS.
<BLANKLINE>
>>> u
YOU ARE ON THE BRINK OF A SMALL CLEAN CLIMBABLE PIT.  A CRAWL LEADS
WEST.
<BLANKLINE>
>>> w
YOU'RE IN DIRTY PASSAGE.
<BLANKLINE>
>>> w
YOU ARE IN A LARGE ROOM FULL OF DUSTY ROCKS.  THERE IS A BIG HOLE IN
THE FLOOR.  THERE ARE CRACKS EVERYWHERE, AND A PASSAGE LEADING EAST.
<BLANKLINE>
>>> d
YOU ARE AT A COMPLEX JUNCTION.  A LOW HANDS AND KNEES PASSAGE FROM THE
NORTH JOINS A HIGHER CRAWL FROM THE EAST TO MAKE A WALKING PASSAGE
GOING WEST.  THERE IS ALSO A LARGE ROOM ABOVE.  THE AIR IS DAMP HERE.
<BLANKLINE>
>>> n
YOU'RE IN A LARGE ROOM CARVED OUT OF SEDIMENTARY ROCK.  THE FLOOR AND
WALLS ARE LITTERED WITH BITS OF SHELLS IMBEDDED IN THE STONE.  A
SHALLOW PASSAGE PROCEEDS DOWNWARD, AND A SOMEWHAT STEEPER ONE LEADS
UP.  A LOW HANDS AND KNEES PASSAGE ENTERS FROM THE SOUTH.
<BLANKLINE>
THERE IS AN ENORMOUS CLAM HERE WITH ITS SHELL TIGHTLY CLOSED.
<BLANKLINE>
>>> u
YOU ARE IN AN ARCHED HALL.  A CORAL PASSAGE ONCE CONTINUED UP AND EAST
FROM HERE, BUT IS NOW BLOCKED BY DEBRIS.  THE AIR SMELLS OF SEA WATER.
<BLANKLINE>
>>> d
YOU'RE IN SHELL ROOM.
<BLANKLINE>
THERE IS AN ENORMOUS CLAM HERE WITH ITS SHELL TIGHTLY CLOSED.
<BLANKLINE>
>>> d
YOU ARE IN A LONG SLOPING CORRIDOR WITH RAGGED SHARP WALLS.
<BLANKLINE>
>>> d
YOU ARE IN A CUL-DE-SAC ABOUT EIGHT FEET ACROSS.
<BLANKLINE>
>>> u
YOU ARE IN A LONG SLOPING CORRIDOR WITH RAGGED SHARP WALLS.
<BLANKLINE>
>>> u
YOU'RE IN SHELL ROOM.
<BLANKLINE>
THERE IS AN ENORMOUS CLAM HERE WITH ITS SHELL TIGHTLY CLOSED.
<BLANKLINE>
>>> get(clam)
OK
<BLANKLINE>
>>> s
YOU CAN'T FIT THIS FIVE-FOOT CLAM THROUGH THAT LITTLE PASSAGE!
<BLANKLINE>
YOU'RE IN SHELL ROOM.
<BLANKLINE>
>>> drop(clam)
OK
<BLANKLINE>
>>> unlock(clam)
YOU DON'T HAVE ANYTHING STRONG ENOUGH TO OPEN THE CLAM.
<BLANKLINE>
>>> s
YOU'RE AT COMPLEX JUNCTION.
<BLANKLINE>
>>> e
YOU ARE IN AN ANTEROOM LEADING TO A LARGE PASSAGE TO THE EAST.  SMALL
PASSAGES GO WEST AND UP.  THE REMNANTS OF RECENT DIGGING ARE EVIDENT.
A SIGN IN MIDAIR HERE SAYS "CAVE UNDER CONSTRUCTION BEYOND THIS POINT.
PROCEED AT OWN RISK.  [WITT CONSTRUCTION COMPANY]"
<BLANKLINE>
THERE ARE A FEW RECENT ISSUES OF "SPELUNKER TODAY" MAGAZINE HERE.
<BLANKLINE>
>>> e
YOU ARE AT WITT'S END.  PASSAGES LEAD OFF IN *ALL* DIRECTIONS.
<BLANKLINE>
>>> w
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND FOUND YOUR WAY
BLOCKED BY A RECENT CAVE-IN.  YOU ARE NOW BACK IN THE MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> n
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> s
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> n
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> se
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU ARE AT WITT'S END.  PASSAGES LEAD OFF IN *ALL* DIRECTIONS.
<BLANKLINE>
>>> nw
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> u
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> d
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> n
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> s
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU ARE AT WITT'S END.  PASSAGES LEAD OFF IN *ALL* DIRECTIONS.
<BLANKLINE>
>>> n
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> se
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> nw
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> u
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> d
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU ARE AT WITT'S END.  PASSAGES LEAD OFF IN *ALL* DIRECTIONS.
<BLANKLINE>
>>> n
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> s
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> n
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> se
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
DO YOU NEED HELP GETTING OUT OF HERE?
<BLANKLINE>
>>> yes
DON'T GO WEST.
<BLANKLINE>
>>> nw
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU ARE AT WITT'S END.  PASSAGES LEAD OFF IN *ALL* DIRECTIONS.
<BLANKLINE>
>>> u
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> d
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> n
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU'RE AT WITT'S END.
<BLANKLINE>
>>> s
YOU'RE IN ANTEROOM.
<BLANKLINE>
THERE ARE A FEW RECENT ISSUES OF "SPELUNKER TODAY" MAGAZINE HERE.
<BLANKLINE>
>>> w
YOU ARE IN BEDQUILT, A LONG EAST/WEST PASSAGE WITH HOLES EVERYWHERE.
TO EXPLORE AT RANDOM SELECT NORTH, SOUTH, UP, OR DOWN.
<BLANKLINE>
>>> w
YOU ARE IN A ROOM WHOSE WALLS RESEMBLE SWISS CHEESE.  OBVIOUS PASSAGES
GO WEST, EAST, NE, AND NW.  PART OF THE ROOM IS OCCUPIED BY A LARGE
BEDROCK BLOCK.
<BLANKLINE>
>>> w
YOU ARE AT THE EAST END OF THE TWOPIT ROOM.  THE FLOOR HERE IS
LITTERED WITH THIN ROCK SLABS, WHICH MAKE IT EASY TO DESCEND THE PITS.
THERE IS A PATH HERE BYPASSING THE PITS TO CONNECT PASSAGES FROM EAST
AND WEST.  THERE ARE HOLES ALL OVER, BUT THE ONLY BIG ONE IS ON THE
WALL DIRECTLY OVER THE WEST PIT WHERE YOU CAN'T GET TO IT.
<BLANKLINE>
>>> d
YOU ARE AT THE BOTTOM OF THE EASTERN PIT IN THE TWOPIT ROOM.  THERE IS
A SMALL POOL OF OIL IN ONE CORNER OF THE PIT.
<BLANKLINE>
>>> u
YOU'RE AT EAST END OF TWOPIT ROOM.
<BLANKLINE>
>>> w
THERE IS A THREATENING LITTLE DWARF IN THE ROOM WITH YOU!
<BLANKLINE>
ONE SHARP NASTY KNIFE IS THROWN AT YOU!
<BLANKLINE>
IT MISSES!
<BLANKLINE>
YOU ARE AT THE WEST END OF THE TWOPIT ROOM.  THERE IS A LARGE HOLE IN
THE WALL ABOVE THE PIT AT THIS END OF THE ROOM.
<BLANKLINE>
>>> throw(axe)
YOU KILLED A LITTLE DWARF.
<BLANKLINE>
YOU'RE AT WEST END OF TWOPIT ROOM.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> down
YOU ARE AT THE BOTTOM OF THE WESTERN PIT IN THE TWOPIT ROOM.  THERE IS
A LARGE HOLE IN THE WALL ABOUT 25 FEET ABOVE YOU.
<BLANKLINE>
THERE IS A TINY LITTLE PLANT IN THE PIT, MURMURING "WATER, WATER, ..."
<BLANKLINE>
>>> u
YOU'RE AT WEST END OF TWOPIT ROOM.
<BLANKLINE>
>>> w
YOU ARE IN A LARGE LOW CIRCULAR CHAMBER WHOSE FLOOR IS AN IMMENSE SLAB
FALLEN FROM THE CEILING (SLAB ROOM).  EAST AND WEST THERE ONCE WERE
LARGE PASSAGES, BUT THEY ARE NOW FILLED WITH BOULDERS.  LOW SMALL
PASSAGES GO NORTH AND SOUTH, AND THE SOUTH ONE QUICKLY BENDS WEST
AROUND THE BOULDERS.
<BLANKLINE>
>>> n
YOU ARE IN BEDQUILT, A LONG EAST/WEST PASSAGE WITH HOLES EVERYWHERE.
TO EXPLORE AT RANDOM SELECT NORTH, SOUTH, UP, OR DOWN.
<BLANKLINE>
>>> n
YOU HAVE CRAWLED AROUND IN SOME LITTLE HOLES AND WOUND UP BACK IN THE
MAIN PASSAGE.
<BLANKLINE>
YOU ARE IN BEDQUILT, A LONG EAST/WEST PASSAGE WITH HOLES EVERYWHERE.
TO EXPLORE AT RANDOM SELECT NORTH, SOUTH, UP, OR DOWN.
<BLANKLINE>
>>> n
YOU ARE IN A LARGE LOW ROOM.  CRAWLS LEAD NORTH, SE, AND SW.
<BLANKLINE>
>>> n
DEAD END CRAWL.
<BLANKLINE>
>>> s
YOU ARE IN A LARGE LOW ROOM.  CRAWLS LEAD NORTH, SE, AND SW.
<BLANKLINE>
>>> sw
YOU ARE IN A LONG WINDING CORRIDOR SLOPING OUT OF SIGHT IN BOTH
DIRECTIONS.
<BLANKLINE>
>>> u
YOU ARE ON ONE SIDE OF A LARGE, DEEP CHASM.  A HEAVY WHITE MIST RISING
UP FROM BELOW OBSCURES ALL VIEW OF THE FAR SIDE.  A SW PATH LEADS AWAY
FROM THE CHASM INTO A WINDING CORRIDOR.
<BLANKLINE>
A RICKETY WOODEN BRIDGE EXTENDS ACROSS THE CHASM, VANISHING INTO THE
MIST.  A SIGN POSTED ON THE BRIDGE READS, "STOP! PAY TROLL!"
<BLANKLINE>
A BURLY TROLL STANDS BY THE BRIDGE AND INSISTS YOU THROW HIM A
TREASURE BEFORE YOU MAY CROSS.
<BLANKLINE>
>>> sw
YOU'RE IN SLOPING CORRIDOR.
<BLANKLINE>
>>> d
YOU ARE IN A LARGE LOW ROOM.  CRAWLS LEAD NORTH, SE, AND SW.
<BLANKLINE>
>>> se
THIS IS THE ORIENTAL ROOM.  ANCIENT ORIENTAL CAVE DRAWINGS COVER THE
WALLS.  A GENTLY SLOPING PASSAGE LEADS UPWARD TO THE NORTH, ANOTHER
PASSAGE LEADS SE, AND A HANDS AND KNEES CRAWL LEADS WEST.
<BLANKLINE>
THERE IS A DELICATE, PRECIOUS, MING VASE HERE!
<BLANKLINE>
>>> get(vase)
OK
<BLANKLINE>
>>> n
YOU ARE FOLLOWING A WIDE PATH AROUND THE OUTER EDGE OF A LARGE CAVERN.
FAR BELOW, THROUGH A HEAVY WHITE MIST, STRANGE SPLASHING NOISES CAN BE
HEARD.  THE MIST RISES UP THROUGH A FISSURE IN THE CEILING.  THE PATH
EXITS TO THE SOUTH AND WEST.
<BLANKLINE>
>>> w
YOU ARE IN AN ALCOVE.  A SMALL NW PATH SEEMS TO WIDEN AFTER A SHORT
DISTANCE.  AN EXTREMELY TIGHT TUNNEL LEADS EAST.  IT LOOKS LIKE A VERY
TIGHT SQUEEZE.  AN EERIE LIGHT CAN BE SEEN AT THE OTHER END.
<BLANKLINE>
>>> e
SOMETHING YOU'RE CARRYING WON'T FIT THROUGH THE TUNNEL WITH YOU.
YOU'D BEST TAKE INVENTORY AND DROP SOMETHING.
<BLANKLINE>
OUT FROM THE SHADOWS BEHIND YOU POUNCES A BEARDED PIRATE!  "HAR, HAR,"
HE CHORTLES, "I'LL JUST TAKE ALL THIS BOOTY AND HIDE IT AWAY WITH ME
CHEST DEEP IN THE MAZE!"  HE SNATCHES YOUR TREASURE AND VANISHES INTO
THE GLOOM.
<BLANKLINE>
YOU'RE IN ALCOVE.
<BLANKLINE>
>>> inventory
YOU ARE CURRENTLY HOLDING THE FOLLOWING:
<BLANKLINE>
SET OF KEYS
BRASS LANTERN
WICKER CAGE
DWARF'S AXE
<BLANKLINE>
>>> drop(keys)
OK
<BLANKLINE>
>>> drop(lantern)
OK
<BLANKLINE>
>>> drop(cage)
OK
<BLANKLINE>
>>> drop(axe)
OK
<BLANKLINE>
>>> e
YOU'RE IN A SMALL CHAMBER LIT BY AN EERIE GREEN LIGHT.  AN EXTREMELY
NARROW TUNNEL EXITS TO THE WEST.  A DARK CORRIDOR LEADS NE.
<BLANKLINE>
THERE IS AN EMERALD HERE THE SIZE OF A PLOVER'S EGG!
<BLANKLINE>
>>> get(emerald)
OK
<BLANKLINE>
>>> ne
IT IS NOW PITCH DARK.  IF YOU PROCEED YOU WILL LIKELY FALL INTO A PIT.
<BLANKLINE>
>>> sw
THERE IS NO WAY TO GO THAT DIRECTION.
<BLANKLINE>
IT IS NOW PITCH DARK.  IF YOU PROCEED YOU WILL LIKELY FALL INTO A PIT.
<BLANKLINE>
>>> s
YOU'RE IN PLOVER ROOM.
<BLANKLINE>
>>> plover
IT IS NOW PITCH DARK.  IF YOU PROCEED YOU WILL LIKELY FALL INTO A PIT.
<BLANKLINE>
>>> drop(emerald)
I SEE NO EMERALD HERE.
<BLANKLINE>
>>> plover
YOU'RE IN PLOVER ROOM.
<BLANKLINE>
THERE IS AN EMERALD HERE THE SIZE OF A PLOVER'S EGG!
<BLANKLINE>
>>> get(emerald)
OK
<BLANKLINE>
>>> w
YOU'RE IN ALCOVE.
<BLANKLINE>
THERE ARE SOME KEYS ON THE GROUND HERE.
<BLANKLINE>
THERE IS A LAMP SHINING NEARBY.
<BLANKLINE>
THERE IS A SMALL WICKER CAGE DISCARDED NEARBY.
<BLANKLINE>
THERE IS A LITTLE AXE HERE.
<BLANKLINE>
>>> get(keys)
OK
<BLANKLINE>
>>> get(lamp)
OK
<BLANKLINE>
>>> get(axe)
OK
<BLANKLINE>
>>> nw
YOU'RE IN MISTY CAVERN.
<BLANKLINE>
>>> s
YOU'RE IN ORIENTAL ROOM.
<BLANKLINE>
>>> se
YOU'RE IN SWISS CHEESE ROOM.
<BLANKLINE>
>>> e
YOU ARE IN THE SOFT ROOM.  THE WALLS ARE COVERED WITH HEAVY CURTAINS,
THE FLOOR WITH A THICK PILE CARPET.  MOSS COVERS THE CEILING.
<BLANKLINE>
A SMALL VELVET PILLOW LIES ON THE FLOOR.
<BLANKLINE>
>>> get(pillow)
OK
<BLANKLINE>
>>> w
YOU'RE IN SWISS CHEESE ROOM.
<BLANKLINE>
>>> ne
YOU ARE IN BEDQUILT, A LONG EAST/WEST PASSAGE WITH HOLES EVERYWHERE.
TO EXPLORE AT RANDOM SELEC
Download .txt
gitextract_kuy29xws/

├── .gitignore
├── LICENSE.ASF-2
├── README.md
├── adventure/
│   ├── MANIFEST.in
│   ├── README.txt
│   ├── TODO.txt
│   ├── __init__.py
│   ├── __main__.py
│   ├── data.py
│   ├── game.py
│   ├── model.py
│   ├── prompt.py
│   └── tests/
│       ├── __init__.py
│       ├── syntax.txt
│       ├── test_commands.py
│       ├── test_data.py
│       ├── test_walks.py
│       ├── vignettes.txt
│       ├── walkthrough1.txt
│       ├── walkthrough2.txt
│       ├── walkthrough3.txt
│       └── walkthrough4.txt
└── setup.py
Download .txt
SYMBOL INDEX (161 symbols across 9 files)

FILE: adventure/__init__.py
  function load_advent_dat (line 12) | def load_advent_dat(data):
  function play (line 20) | def play(seed=None):
  function resume (line 38) | def resume(savefile, quiet=False):

FILE: adventure/__main__.py
  function baudout (line 18) | def baudout(s):
  function loop (line 25) | def loop(args):

FILE: adventure/data.py
  class Data (line 30) | class Data(object):
    method __init__ (line 31) | def __init__(self):
    method referent (line 40) | def referent(self, word):
  function make_object (line 46) | def make_object(dictionary, klass, n):
  function expand_tabs (line 52) | def expand_tabs(segments):
  function accumulate_message (line 60) | def accumulate_message(dictionary, n, line):
  function section1 (line 65) | def section1(data, n, *etc):
  function section2 (line 77) | def section2(data, n, line):
  function section3 (line 86) | def section3(data, x, y, *verbs):
  function section4 (line 176) | def section4(data, n, text, *etc):
  function section5 (line 210) | def section5(data, n, *etc):
  function section6 (line 236) | def section6(data, n, *etc):
  function section7 (line 247) | def section7(data, n, room_n, fixed=None):
  function section8 (line 269) | def section8(data, word_n, message_n):
  function section9 (line 284) | def section9(data, bit, *nlist):
  function section10 (line 325) | def section10(data, score, line, *etc):
  function section11 (line 339) | def section11(data, n, turns_needed, penalty, question_n, message_n):
  function section12 (line 362) | def section12(data, n, line):
  function parse (line 374) | def parse(data, datafile):

FILE: adventure/game.py
  class Game (line 23) | class Game(Data):
    method __init__ (line 43) | def __init__(self, seed=None):
    method random (line 61) | def random(self):
    method choice (line 64) | def choice(self, seq):
    method write (line 67) | def write(self, more):
    method write_message (line 73) | def write_message(self, n):
    method yesno (line 76) | def yesno(self, s, yesno_callback, casual=False):
    method is_dark (line 85) | def is_dark(self):
    method inventory (line 92) | def inventory(self):
    method treasures (line 96) | def treasures(self):
    method objects_here (line 100) | def objects_here(self):
    method objects_at (line 103) | def objects_at(self, room):
    method is_here (line 106) | def is_here(self, obj):
    method is_finished (line 113) | def is_finished(self):
    method start (line 118) | def start(self):
    method start2 (line 134) | def start2(self, yes):
    method move_to (line 157) | def move_to(self, newloc=None):  #2
    method move_dwarves (line 192) | def move_dwarves(self):
    method describe_location (line 327) | def describe_location(self):  #2000
    method say_okay_and_finish (line 389) | def say_okay_and_finish(self, *ignored):  #2009
    method finish_turn (line 397) | def finish_turn(self, obj=None):  #2600
    method do_command (line 440) | def do_command(self, words):
    method _do_command (line 446) | def _do_command(self, words):
    method dispatch_command (line 514) | def dispatch_command(self, words):  #19999
    method dont_understand (line 628) | def dont_understand(self):
    method i_see_no (line 639) | def i_see_no(self, thing):
    method do_motion (line 645) | def do_motion(self, word):  #8
    method die_here (line 783) | def die_here(self):  #90
    method die (line 788) | def die(self):  #99
    method ask_verb_what (line 822) | def ask_verb_what(self, verb, *args):  #8000
    method write_default_message (line 839) | def write_default_message(self, verb, *args):
    method i_carry (line 852) | def i_carry(self, verb):  #8010
    method t_carry (line 860) | def t_carry(self, verb, obj):  #9010
    method t_drop (line 914) | def t_drop(self, verb, obj):  #9020
    method t_say (line 980) | def t_say(self, verb, word):  #9030
    method i_unlock (line 987) | def i_unlock(self, verb):  #8040  Handles "unlock" case as well
    method t_unlock (line 1000) | def t_unlock(self, verb, obj):  #9040  Handles "lock" case as well
    method t_light (line 1071) | def t_light(self, verb, obj=None):  #9070
    method t_extinguish (line 1085) | def t_extinguish(self, verb, obj=None):  #9080
    method t_wave (line 1097) | def t_wave(self, verb, obj):  #9090
    method i_attack (line 1112) | def i_attack(self, verb):  #9120
    method t_attack (line 1133) | def t_attack(self, verb, obj):  #9124  (but control goes to 9120 first)
    method i_pour (line 1181) | def i_pour(self, verb):  #9130
    method t_pour (line 1187) | def t_pour(self, verb, obj):
    method i_eat (line 1214) | def i_eat(self, verb):  #8140
    method t_eat (line 1220) | def t_eat(self, verb, obj):  #9140
    method i_drink (line 1232) | def i_drink(self, verb):  #9150
    method t_drink (line 1238) | def t_drink(self, verb, obj):  #9150
    method t_rub (line 1250) | def t_rub(self, verb, obj):  #9160
    method t_throw (line 1257) | def t_throw(self, verb, obj):  #9170
    method i_quit (line 1321) | def i_quit(self, verb):  #8180
    method t_find (line 1328) | def t_find(self, verb, obj):  #9190
    method i_inventory (line 1343) | def i_inventory(self, verb):  #8200
    method t_feed (line 1357) | def t_feed(self, verb, obj):  #9210
    method i_fill (line 1399) | def i_fill(self, verb):  #9220
    method t_fill (line 1404) | def t_fill(self, verb, obj):
    method t_blast (line 1436) | def t_blast(self, verb, obj=None):  #9230
    method i_score (line 1452) | def i_score(self, verb):  #8240
    method i_fee (line 1462) | def i_fee(self, verb):  #8250
    method i_brief (line 1493) | def i_brief(self, verb):  #8260
    method i_read (line 1499) | def i_read(self, verb):  #8270
    method t_read (line 1509) | def t_read(self, verb, obj):  #9270
    method t_break (line 1533) | def t_break(self, verb, obj):  #9280
    method t_wake (line 1550) | def t_wake(self, verb, obj):  #9290
    method i_suspend (line 1558) | def i_suspend(self, verb):
    method t_suspend (line 1563) | def t_suspend(self, verb, obj):
    method i_hours (line 1582) | def i_hours(self, verb):
    method resume (line 1586) | def resume(self, obj):
    method should_offer_hint (line 1601) | def should_offer_hint(self, hint, obj): #40000
    method start_closing_cave (line 1624) | def start_closing_cave(self):  #10000
    method close_cave (line 1639) | def close_cave(self):  #11000
    method wake_repository_dwarves (line 1664) | def wake_repository_dwarves(self):  #19000
    method compute_score (line 1668) | def compute_score(self, for_score_command=False):  #20000
    method score_and_exit (line 1717) | def score_and_exit(self):

FILE: adventure/model.py
  class Move (line 7) | class Move(object):
    method __repr__ (line 15) | def __repr__(self):
  class Room (line 42) | class Room(object):
    method __init__ (line 60) | def __init__(self):
    method __repr__ (line 63) | def __repr__(self):
    method is_forced (line 67) | def is_forced(self):
    method is_aboveground (line 71) | def is_aboveground(self):
    method is_before_hall_of_mists (line 75) | def is_before_hall_of_mists(self):
    method is_after_hall_of_mists (line 79) | def is_after_hall_of_mists(self):
    method is_dark (line 83) | def is_dark(self):
  class Word (line 86) | class Word(object):
    method __init__ (line 93) | def __init__(self):
    method __repr__ (line 96) | def __repr__(self):
    method __eq__ (line 99) | def __eq__(self, text):
    method add_synonym (line 102) | def add_synonym(self, other):
  class Object (line 107) | class Object(object):
    method __init__ (line 110) | def __init__(self):
    method __repr__ (line 122) | def __repr__(self):
    method __hash__ (line 125) | def __hash__(self):
    method __eq__ (line 128) | def __eq__(self, other):
    method is_at (line 131) | def is_at(self, room):
    method carry (line 134) | def carry(self):
    method drop (line 138) | def drop(self, room):
    method hide (line 142) | def hide(self):
    method destroy (line 146) | def destroy(self):
  class Message (line 149) | class Message(object):
    method __str__ (line 153) | def __str__(self):
  class Hint (line 156) | class Hint(object):
    method __init__ (line 166) | def __init__(self):
  class Dwarf (line 169) | class Dwarf(object):
    method __init__ (line 173) | def __init__(self, room):
    method start_at (line 177) | def start_at(self, room):
    method can_move (line 181) | def can_move(self, move):
  class Pirate (line 189) | class Pirate(Dwarf):

FILE: adventure/prompt.py
  class ReprTriggeredPhrase (line 9) | class ReprTriggeredPhrase(object):
    method __init__ (line 12) | def __init__(self, game, words):
    method __repr__ (line 16) | def __repr__(self):
    method __call__ (line 21) | def __call__(self, arg=None):
    method __getattr__ (line 28) | def __getattr__(self, name):
  function install_words (line 32) | def install_words(game):

FILE: adventure/tests/test_commands.py
  class CommandTest (line 11) | class CommandTest(TestCase):
    method setUp (line 13) | def setUp(self):
    method test_intransitive_commands_should_not_throw_exceptions (line 19) | def test_intransitive_commands_should_not_throw_exceptions(self):
    method test_transitive_commands_should_not_throw_exceptions (line 27) | def test_transitive_commands_should_not_throw_exceptions(self):

FILE: adventure/tests/test_data.py
  class DataTest (line 9) | class DataTest(unittest.TestCase):
    method setUp (line 11) | def setUp(self):
    method test_long_description (line 17) | def test_long_description(self):
    method test_long_description_expands_tabs (line 23) | def test_long_description_expands_tabs(self):
    method test_short_description (line 27) | def test_short_description(self):
    method test_object_message_expands_tabs (line 31) | def test_object_message_expands_tabs(self):
    method test_hint (line 36) | def test_hint(self):
  class ReprTest (line 48) | class ReprTest(unittest.TestCase):
    method setUp (line 50) | def setUp(self):
    method assertMove (line 56) | def assertMove(self, room_i, entry_i, s):
    method test_move_repr_look_good (line 60) | def test_move_repr_look_good(self):
    method test_move_repr_works_on_all_moves (line 73) | def test_move_repr_works_on_all_moves(self):
    method test_room_repr (line 82) | def test_room_repr(self):
    method test_object_repr (line 85) | def test_object_repr(self):
    method test_word_repr (line 89) | def test_word_repr(self):

FILE: adventure/tests/test_walks.py
  function load_tests (line 12) | def load_tests(loader, tests, pattern):
  class ChdirTemp (line 24) | class ChdirTemp(object):
    method setup (line 25) | def setup(self, doctest_object):
    method teardown (line 30) | def teardown(self, doctest_object):
Condensed preview — 23 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (311K chars).
[
  {
    "path": ".gitignore",
    "chars": 75,
    "preview": ".coverage\nhtmlcov/\n*~\n__pycache__/\n*.pyc\n/MANIFEST\nbuild/\ndist/\n*.egg-info\n"
  },
  {
    "path": "LICENSE.ASF-2",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "README.md",
    "chars": 185,
    "preview": "Welcome to the git repository for the Python 3 version of Adventure!\nThe project README is one level deeper, inside of t"
  },
  {
    "path": "adventure/MANIFEST.in",
    "chars": 18,
    "preview": "include LICENSE.*\n"
  },
  {
    "path": "adventure/README.txt",
    "chars": 6289,
    "preview": "This is a faithful port of the “Adventure” game to Python 3 from the\noriginal 1977 FORTRAN code by Crowther and Woods (i"
  },
  {
    "path": "adventure/TODO.txt",
    "chars": 80,
    "preview": "\n* Test dropping into the secret canyon from the direction that gets no dragon.\n"
  },
  {
    "path": "adventure/__init__.py",
    "chars": 1173,
    "preview": "\"\"\"The Adventure game.\n\nCopyright 2010-2015 Brandon Rhodes.  Licensed as free software under the\nApache License, Version"
  },
  {
    "path": "adventure/__main__.py",
    "chars": 1326,
    "preview": "\"\"\"Offer Adventure at a custom command prompt.\n\nCopyright 2010-2015 Brandon Rhodes.  Licensed as free software under the"
  },
  {
    "path": "adventure/data.py",
    "chars": 14722,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"Parse the original PDP ``advent.dat`` file.\n\nCopyright 2010-2015 Brandon Rhodes.  Licensed a"
  },
  {
    "path": "adventure/game.py",
    "chars": 61027,
    "preview": "\"\"\"How we keep track of the state of the game.\n\nCopyright 2010-2015 Brandon Rhodes.  Licensed as free software under the"
  },
  {
    "path": "adventure/model.py",
    "chars": 4898,
    "preview": "\"\"\"Classes representing Adventure game components.\n\nCopyright 2010-2015 Brandon Rhodes.  Licensed as free software under"
  },
  {
    "path": "adventure/prompt.py",
    "chars": 1515,
    "preview": "\"\"\"Routines that install Adventure commands for the Python prompt.\n\nCopyright 2010-2015 Brandon Rhodes.  Licensed as fre"
  },
  {
    "path": "adventure/tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "adventure/tests/syntax.txt",
    "chars": 1727,
    "preview": ">>> import adventure\n>>> adventure.play(seed=2)\nWELCOME TO ADVENTURE!!  WOULD YOU LIKE INSTRUCTIONS?\n<BLANKLINE>\n>>> no\n"
  },
  {
    "path": "adventure/tests/test_commands.py",
    "chars": 1141,
    "preview": "\"\"\"Test suite.\n\nCopyright 2010-2015 Brandon Rhodes.  Licensed as free software under the\nApache License, Version 2.0 as "
  },
  {
    "path": "adventure/tests/test_data.py",
    "chars": 3321,
    "preview": "\"\"\"Test suite.\n\nCopyright 2010-2015 Brandon Rhodes.  Licensed as free software under the\nApache License, Version 2.0 as "
  },
  {
    "path": "adventure/tests/test_walks.py",
    "chars": 1060,
    "preview": "\"\"\"Test suite.\n\nCopyright 2010-2015 Brandon Rhodes.  Licensed as free software under the\nApache License, Version 2.0 as "
  },
  {
    "path": "adventure/tests/vignettes.txt",
    "chars": 40802,
    "preview": ">>> import io\n>>> from itertools import cycle, islice\n\nThis file tests the behavior of the Adventure game in all sorts o"
  },
  {
    "path": "adventure/tests/walkthrough1.txt",
    "chars": 93838,
    "preview": ">>> import adventure\n>>> adventure.play(seed=1)\nWELCOME TO ADVENTURE!!  WOULD YOU LIKE INSTRUCTIONS?\n<BLANKLINE>\n>>> yes"
  },
  {
    "path": "adventure/tests/walkthrough2.txt",
    "chars": 34501,
    "preview": ">>> import adventure\n>>> adventure.play(seed=2)\nWELCOME TO ADVENTURE!!  WOULD YOU LIKE INSTRUCTIONS?\n<BLANKLINE>\n>>> no\n"
  },
  {
    "path": "adventure/tests/walkthrough3.txt",
    "chars": 8998,
    "preview": ">>> import adventure\n>>> adventure.play(seed=4)\nWELCOME TO ADVENTURE!!  WOULD YOU LIKE INSTRUCTIONS?\n<BLANKLINE>\n>>> no\n"
  },
  {
    "path": "adventure/tests/walkthrough4.txt",
    "chars": 9372,
    "preview": ">>> import adventure\n>>> adventure.play(seed=4)\nWELCOME TO ADVENTURE!!  WOULD YOU LIKE INSTRUCTIONS?\n<BLANKLINE>\n>>> no\n"
  },
  {
    "path": "setup.py",
    "chars": 1095,
    "preview": "import os\nimport sys\nfrom distutils.core import setup\n\nif sys.version_info < (3,):\n    print('\\nSorry, but Adventure can"
  }
]

About this extraction

This page contains the full source code of the brandon-rhodes/python-adventure GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 23 files (291.5 KB), approximately 90.3k tokens, and a symbol index with 161 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!