From the “Final instructions” section of the article introducing Wizard’s Castle (Power 1980):
These instructions are meant to be a guide only. Feel free to experiment with the various responses when running the game (this is have the fun of the game). For the best results use equal parts imagination and common sense.
I first took these words as license to tinker with the source code and make the game say and do different things. But, now that I type them out, it seems more like Power was actually encouraging players to experiment in order to figure out how to play the game.
Wizard’s Castle is a little too primitive to offer the kind of combinatorial possibilities of play that a game like Nethack offers, but there is potential. This implementation is done to fulfill the following goals:
- Faithfully play it as originally coded.
- Provide the options to play the Commodore PET and MSDOS versions of the game.
- Make it easy to expand the game’s scope of play and to offer some minor fixes and improvements.
I have consulted the original article, as well as source code for the other editions of the game.
To use:
- Clone repository
- Run
make install
this will install it into${XDG_DATA_HOME}/common-lisp/source/org.wobh.common-lisp.games.wizards-castle
- in REPL
(require "asdf")
(as needed) (asdf:load-system "org.wobh.common-lisp.games.wizards-castle")
(wizards-castle:play)
NOTE: zot
and wizard
are nicknames of the package, and you may see
that instead of wizards-castle
in these examples.
If you just want to play the game use of the these functions:
Function: wizards-castle:play *rest args &key &allow-other-keys -> castle
Parameters and values will go undocumented. If you want to customize
the game, it’ll be better to use the wizards-castle-user
package.
play
plays the game with defaults to the game as originally
coded. The following keyword options to play
are available:
Function: wizards-castle:play-ohare *rest args &key &allow-other-keys -> castle
Parameters and values will go undocumented. If you want to customize
the game, it’ll be better to use the wizards-castle-user
package.
Play the Commodore PET game as adapted by John O’Hare.
The curse notice text is bound to *curse-notice-ohare*
.
I have not yet figured out how to format the output for the shorter line length in this edition.
Function: wizards-castle:play-stetson *rest args &key &allow-other-keys -> castle
Parameters and values will go undocumented. If you want to customize
the game, it’ll be better to use the wizards-castle-user
package.
Play the MSDOS game as adapted by J.T. Stetson.
NOTE: zot-user
and wizard-user
are nicknames of the package, and you may see
that instead of wizards-castle-user
in these examples.
If you wish to experiment, play-testing, or try new features you can
switch to the interactive wizards-castle-user
package in the REPL:
CL-USER> (in-package #:wizards-castle-user) WIZARDS-CASTLE-USER>
Right now, running a test game is just like running a regular game with a few shortcuts to skip the castle and adventurer setup phases.
WIZARDS-CASTLE-USER> (setup-test) #S(ADVENTURER ...) #S(CASTLE ...) WIZARDS-CASTLE-USER> (play-test)
a random state.
implementation dependent–Derived from (make-random-state t)
.
A special variable holding a reusable random-state. Used by the
testing environment to recreate castle objects and replay
games. Used in setup-test
and play-test
.
An adventurer object.
nil
An adventurer object for testing. It’s provides a default value for
play-test
and test-eval
.
Use setup-test
to set this value.
a castle.
nil
A castle object for testing. Instead of making deep copies of the
testing environment’s castle object, the testing environment reuses
the random state *r*
to regenerate it.
Use setup-test
to set this value.
-- Function: make-test-env adv-name -> adventurer
The set of predefined adventurers is roughly as follows:
:basic
- A human with randomly selected sex: average abilities and equipment; poor but at least owns a lamp. This is the character I would always make in the character setup phase.
:blind-adept
- A female human fighter: highly capable and skilled, well armed and armored, but poor, blind.
:bookworm
- A male hobbit: smart and fast (skilled in running-away), but weak; poor, unarmed and unarmored; has a book stuck to his hands.
:valkyrie
- A female dwarf: strong, somewhat graceful and more brave than smart; well armored but less well armed, poor and poorly equipped.
:barbarian
- A male human: strong, agile, but dumb and forgetful; well-armed, but poorly armored; poor and poorly equipped.
:sorceress
- A female elf: highly intelligent, somewhat graceful but weak; no money, poorly armed and armored; has many flares, and the runestaff but lazy and lethargic.
:tourist
- A human male: moderate iq, but weak and clumsy; unarmed, unarmored, no equipment; extremely rich, but has hole in his wallet (leech).
See the source code for their exact specifications.
An adventurer
object.
Make a test adventurer object from a predefined set.
-- Function: setup-test &key adv-name map-all-rooms enter-castle -> adventurer, castle
A keyword for make-test-adv
, default :basic
.
See cooresponding parameter in make-test-adv
.
Causes all the rooms in the test castle to be mapped.
Enters the adventurer into the castle. This is what you want if you
wish to work with test-eval
.
the adventurer
object assigned to *a*
.
the castle
object assigned to *z*
.
Sets up *a*
and *z*
using a copy of *r*
for the random state in
making *z*
and make-test-adv
for *a*
.
The values of *a*
and *z*
are modified.
-- Function : castle-find item &key castle -> index
a symbol representing a creature in the castle.
Some symbols are imported, some are not.
a castle
default *z*
.
a room ref type either :index
or :subscripts
. Default is :subscripts
.
(TODO, support :cas-coords
)
either the array row major index or a list of subscripts
Returns coordinates or index where nearest item
can be found.
-- Function : castle-find item &key castle -> index
a symbol representing a creature in the castle.
Some symbols are imported, some are not.
a castle
default *z*
.
a room ref type either :index
or :subscripts
. Default is :subscripts
.
(TODO, support :cas-coords
)
either the array row major index or a list of subscripts
Returns coordinates or index where nearest item
can be found.
-- Function : castle-scry room-ref &key castle -> message
either a row-major-index or a list of valid array subscripts for the
cas-rooms
of the castle
.
a castle. Defaults to *z*
.
This is mainly useful for getting the “castle coordinates” of an item for reference in a test game.
-- Function: castle-room-swap room-ref-this room-ref-that &key castle -> castle
either a row-major-index or a list of valid array subscripts for the
cas-rooms
of the castle
.
either a row-major-index or a list of valid array subscripts for the
cas-rooms
of the castle
.
a castle. Defaults to *z*
.
Swap the contents of two rooms in the castle
.
-- Function: how-convenient item &key castle -> castle
a symbol of a room type or contents in ~castle
a castle. Defaults to *z*
.
Moves the item to the first room east of the entrance. Helpful for moving curse-countering treasures for testing.
-- Function: play-test &key adventurer castle last-castle forget-type curse-notify gaze-map cas-coords sleep-of-death random-state -> castle
Provide an adventurer object to the play functions. This bypasses the
adventurer setup phase. Use the make-adventurer
function to create a
custom adventurer. The make-test-adv
function will create a few
pre-made adventurer characters.
Provide a castle object for the main adventure. This bypasses the
castle setup phase. If the castle object already has a
cas-adventurer
object defined, the game will use that, if not, the
game will proceed with the adventurer setup.
either :random
or :mapped
. Default :random
message printed when the adventurer gains a curse.
How to handle clairvoyant visions from Crystal Orbs.
nil
- do nothing
:naive
- map whatever the vision says
:ask
- ask user whether to map
:smart
- check if already mapped, before mapping
:skeptic
- check if already mapped, and ask if not
What format should castle coordinates be presented in. Default :zot
.
:zot
- original
:array
- array subscripts
number of seconds to pause after adventurer dies. Default 1 (in the game it defaults to 7).
random state to use during game. Defaults to *z*
.
Play a testing game with a resuable random-state. The equivalent of the following:
(let ((*random-state* (make-random-state *r*))) (play :adventurer *a* :castle *z* :last-castle t))
Function: test-eval wiz-form &key castle history -> message, latest-events
a castle. Defaults to *z*
.
a string
Evaluate a “wiz-form” to test it’s effects on a castle. It returns
message from begin-turn
and latest events in castle history.
To see the current implementation of make-wiz-form
and other,
related functions, see comments in “wizards-castle.lisp” file. These
functions are subject to change.
By default, to conform to original code, *forgetfulness*
is set to
:random
which unmaps a random room in the castle, regardless of
whether it was mapped or not. Set *forgetfulness*
to :mapped
and it will forget a room that has already been mapped.
Gazing into orbs can give information about other rooms in the castle, but this information isn’t reflected in the map, because what the orbs say isn’t always true.
Setting *gaze-mapper*
to :naive
will cause let the
adventurer to map the rooms that the orbs inform about, even if the
information is untrue.
Setting *gaze-mapper*
to :ask
will cause the game to ask the
player if it should map the creature at the coordinates specified.
The format string used by wiz-format
. By default, this is set to
*all-caps*
. You can set it to *mixed-case*
when you want less
obnoxious output strings.
Common Lisp’s array subscripts have to be translated into the system
used for the orginal game. By default this is set to :wizard
and so
coordinate will be translated. When set to :array
the game uses
array coordinates.
To run tests: (asdf:test-system "org.wobh.common-lisp.games.wizards-castle")
The test package is simply a package of Lisp assertions. Running the tests tries to load the test package and if it loads without errors all the tests passed.
Extract the tests from the main file to their own package.
Extract playtesting features from the main file to their own package.
Make it possible to customize game messaging.
I know this isn’t in keeping with the spirit of Rogue-like games or this ancestor, but it would be convenient for play testers and “advanced” users.
With SBCL:
$ sbcl --load "wizards-castle.lisp" \
--load "wizards-castle-user.lisp" \
--eval '(progn (zot-user:setup-test :adv-name :basic) (zot-user:play-test))'
When the user presses Ctrl C
there can be an abort restart which
calls (sb-ext:save-lisp-and-die "zot-save-{datetime}.sbcl-core")
Then the session could be resumed with:
$ sbcl --core "zot-save-{datetime}.sbcl-core"
And maybe? the save will be resumed at the debugger and you can resume
playing with the continue
restart.
I haven’t had much luck with trying this manually.
Another likely problem is the core file is only guaranteed to work with the same SBCL version that created it.
Restarts would have to be defined on a per-implementation basis and using them should be documented.
This would significantly reduce how many symbols have to be exported from the game package, but would cause a bunch of keywords to be defined.
- Power, Joseph R.; Wizard’s Castle; Recreational Computing; 1980, July-August pgs 10-17
- O’Hare, John; Wizard’s Castle; Baf’s guide to the Interactive Fiction Archive; http://www.wurb.com/if/index; page: http://www.wurb.com/if/game/678
- Stetson, J.F.; Wizard’s Castle; Baf’s guide to the Interactive Fiction Archive; http://www.wurb.com/if/index; page: http://www.wurb.com/if/game/678
- Licht, Derell; Wizard’s Castle; http://home.comcast.net/~derelict/winwiz.html
- Interview with Joseph Power: http://www.armchairarcade.com/neo/node/1381