1768 | | | : ee

| PROGRAMMING YOUR OWN | DVENTURE GAMES IN

Adventures are played with Caps Lock off You can break out of programs with Control-C or Control-Shift-@ Notes in red added by Michael Bean, September 2022

A note about disk Drive numbers in file names and commands:

Pascal refers to Drive 1 and 2 as Volumes #4: and #5:. (The number sign and colons are usually typed as part of the drive name, but not always.) If no volume number is specified, Pascal defaults to using volume #4: Pascal can also be directed to use a specific disk, regardless of which drive it’s in, by typing the name of the disk instead of the drive volume (such as APPLEO: Again, the colon is typically typed as part of the disk name.)

As written, the programs and commands in this book do not specify a drive volume, and Pascal expects any needed files to be on the disk which is in Drive 1. When entering the programs or commands, it will often be more convenient to tell Pascal to look on Drive 2 (Volume #5:) instead.

Examples:

‘#5:a2.db80.x’

(*$i#5:mini1 .text*)

{Su#5:mt1.code}

Edit what file? #5:mtadvent

Alternatively you can specify the name of the disk on which a file is stored. This way no matter which drive the disk is in, Pascal will find the files when it needs them, as long as the specified disk is in one of the drives. However if the disk name is ever changed, or if the file is copied to a different disk, Pascal won't be able to find the files.

Examples:

‘diskname:a2.db80.x’

(*Sidiskname:mini1 .text*)

{Sudiskname:mt1.code}

Edit what file? diskname:mtadvent

Pages 25, 81, 203, 207, 209, 224, 225, 233, 234, 261, 262, 263, 273, 274, 282 and 283 utilize the “include” or “uses” instructions. Specifying a disk name or drive volume here is only important during the Compiling and Linking process, and does not affect the final game.

Pages 83 and 232 refer to description database files used by Adventure 2 and 3. Whatever volume number or disk name is used here is hard-coded into the game, and Pascal will not look elsewhere. It might be best to leave these as written, and ensure that all Adventure files will be in Drive 1 when played.

PROGRAMMING YOUR OWN ADVENTURE GAMES IN PASCAL

PROGRAMMING YOUR OWN ADVENTURE GAMES IN PASCAL

BY RICHARD C. VILE, JR.

[TaB) TAS BOOKS Inc. BLUE RIDGE SUMMIT, PA. 17214

FIRST EDITION FIRST PRINTING

Copyright© 1984 by TAB BOOKS Inc. Printed in the United States of America

Reproduction or publication of the content in any manner, without express permission of the publisher, is prohibited. No liability is assumed with respect to the use of the information herein.

Library of Congress Cataloging in Publication Data

Vile, Richard C., 1943- Programming your own adventure games in Pascal Includes index. 1. Computer games. 2. PASCAL (Computer program language) |. Title. GV1469.2.V55 1984 794.8'2 84-8577 ISBN 0-8306-0768-4 ISBN 0-8306-1768-X (pbk.)

Cover illustration by Larry Selman

Contents

Introduction

The Elements of Adventure Games

Rooms or Locations—Objects and Treasures—Beings and Monsters—Problems

Notations for Describing Adventures Rooms or Locations—Travel Indicators— Problems—Objects— Beings and Monsters—Embellishing the Map—Map Layout and Organization—Examples

Adventures and Problem Solving Clues to Problem Solutions—Problem Difficulty: From the Obvious to the Obscure—Repeatability— Logic— Surprise

UCSD Pascal Review What You Should Know about Pascal—What Is the UCSD System?

Preview of Adventure 1

Preview Chapters—Preview of Adventure 1—Chapter Previews

Pascal Adventure 1

Listing 6-1. Pascal Miniadventure

Representing the Map

Representing the Adventure Map—Enumerated Types in Pascal

viii

12

15

23

54

15

16

17

18

1g

20

21

22

23

Controlling the Play

Case Statements in Pascal—Controlling the Adventure Game

Mazes in the Middle

Local Declarations—Local Procedures and Functions

Other Techniques Used in Adventure 1

Preview of Adventure 2

Maps, Diagrams, and Code Outlines—Chapter Previews

Pascal Adventure 2

Command Processing in Adventure 2

An Overview of the Command Handling Code—Command Processing in Detail

Carry and Drop: Pascal Sets What Are Sets and How Can They Be Used?

Problems in Adventure 2

Events—Boolean Expressions and Events—Problems in Adventure 2

Other Techniques Used in Adventure 2

Counting Turns—Displaying the Contents of a Set—Simplification of Travel—The Use of File Variables—Scoring Your Adventures— The Help Command—The Lamp and the Light Command—The

Dig Command—The Eat Command—The Ogre

UCSD Pascal Development Techniques Managing Your Files Effectively—UCSD System Tricks and Pitfalls

Preview of MAKEDESC and BROWSE

Database Concepts—Previews of Chapters 19 through 24

MAKEDESC and BROWSE

Listing 19-1. Make80 Database Generator—Listing 19-2. Browse80 Database Snooper

Creating a Database of Descriptions

MAKEDESC: The Descriptions Generator—How to Use MAKEDESC Instruction Lines—Ditto Lines—

Continuation File Lines— Running MAKEDESC

Random Access Files in UCSD Pascal

What Are Random and Sequential Files?—The File Creating Program—The File Reading Program— Random Access—The BROWSE Program—Using Descriptions Databases in Your Adventures

The Structure of Adventure Databases

Index Files and the Descriptions Index—Using the Descriptions Databases in Adventure Games— The

BROWSE Program for Previewing Databases

Programming Techniques Used in MAKEDESC

Symbol Tables—Lookup Techniques: The Linear Search—Hashing: A More Efficient Search

Technique —The Hash Table Lookup Technique

59

63

66

68

78

115

125

130

135

139

145

147

166

173

177

182

24

25

26

27

28

29

30

What Else Can You Put on Disk?

Other Descriptions—Putting the Whole Program on Disk: Adventure Interpreters

Preview of Adventure 3

Outlines, Diagrams, and Maps—Chapter Previews

The Listings of Adventure 3

Listing 26-1. Adventure 3 Main Program—Listing 26-2. Location Procedure Unit—Listing 26-3. Com- mands 1 Unit—Listing 26-4. Commands 2 Unit—Listing 26-5. Commands 3 Unit—Listing 26-6. Problems Unit—Listing 26-7. Adventure 3 Data

Larger Programs: Using UCSD Units Why Units?—The Units in Adventure 3—Units: Syntax and Semantics—Linking Programs that Have Units—Maintaining a Program Written with Units

Problems in Adventure 3 The Probs Unit—The Problems in Adventure 3—Implementing Adventure 3 Problems

Other Techniques Used in Adventure 3 Putting More into the Database—Coding Techniques to Get Around UCSD Limitations—Trickery in Showgoodies

Writing Your Own Adventures Extending Adventure 3—Using a Skeleton Adventure—A Systematic Approach to Writing Adventures— Farewell

Appendix A Skeleton Adventure Program Appendix B_- The Adventure 2 Database Appendix C The Adventure 3 Database

Index

190

192

203

239

245

250

254

261

286

292

304

Introduction

Welcome to the world of adventure games and Pas- cal. The purpose of this book is to teach you how to use the Pascal programming language to create adventure games on your home computer. I shall cover topics related both to the design and creation of the games themselves and to the style and use of the Pascal language. I shall concentrate on the style of adventure game referred to as a text adventure. I shall use the UCSD implementation of Pascal, which is widely available on microcomputers.

ADVENTURE GAMES

Adventure originated in the 1970s. The first adventure was a game written by Don Woods and Willie Crowther. The language used was FOR- TRAN and the computer used was a PDP-10, a mainframe computer common in universities and research laboratories. The game was an exercise in problem solving, artificial intelligence, and simula- tion. The main goal of the original adventure was to be a problem-solving exercise.

The original adventure found its way onto many different computer installations. Since it was

viii

written in FORTRAN, people moved it to other computers besides the PDP-10. It was rewritten in other FORTRAN dialects and in other languages as well. In the early 1980s, it first became available for microcomputers. Microsoft, Inc. as well as other software companies adapted the game for use on personal microcomputers such as the Apple II and the TRS-80.

Since the advent of adventure on microcom- puters, an entire subindustry of adventure game software producers has been created. Many com- panies created and sold adventures in the spirit of the original adventure. Most notable of these is probably the series of games known as Zork. Zork was also originally written on a large computer. The authors, several M.I.T. students, created a company called Infocom and began marketing Zork and its descendants.

A programmer from Stromberg-Carlson in Florida, Scott Adams, created a system for writing adventure games on the TRS-80. He published an article in Creative Computing magazine describing the system and went on to create and market sev-

eral of his own adventure games. These games and Adams’ company, Adventure International have be- come industry standards.

A company in California, now known as Sierra On-Line Systems, created several unusual adven- tures for the Apple II. What made these adventures unusual was that they used the Apple high- resolution graphics display to show the player a picture of each location in the adventure as it was reached. Thus, textual descriptions and creative prose was replaced by graphics artistry. Several companies have now started writing and marketing adventure games in this style. Such adventures are referred to as hi-res adventures in honor of the original implementation.

Several adventure games that mimic the style and approach of the noncomputerized Dungeons and Dragons fantasy role-playing games are avail- able. Dungeons and Dragons was admittedly the inspiration for the original adventure. However, the style of original adventure was unlike that of D and D. In the D and D style games characters are created or “rolled.” (The terminology is derived from the fact that several different kinds of dice are used in the character creation process.) The characters have attributes such as strength, intelli- gence, wisdom, and charisma, with numerical val- ues attached. These attributes play an integral part in the progress of the game.

In this book I will deal strictly with text-style adventures in the spirit of original adventure. The emphasis will be on the creation and solving of interesting problems. The nature of adventure game problems is discussed in detail. The methods of representing and handling problems using Pascal programming techniques are given extensive coverage in the text. It is my belief that the scope for creativity in text adventures is great. There are as many adventures out there waiting to be in- vented as there are adventure and fantasy novels waiting to be written. The range of possibilities is limited only by your imagination and your pro- gramming skills.

PASCAL After BASIC, Pascal is the most popular pro-

gramming language available for personal and mi- crocomputers. It has been called by some the “language of the 80s.” Since its creation in the late 1960s, it has spread throughout the world, growing in popularity and acceptance each year. When a committee was formed in late 1979 to investigate the standardization of the language, it attracted the attention of many of the leading companies in the computer and electronics fields. The interest in obtaining a language standard was almost unpre- cedented in the computer field.

In addition to being popular, Pascal is excel- lent for programming. Adventure game writing in Pascal is a satisfying experience as I hope to dem- onstrate. The features of the language greatly simplify the process of translating an adventure game into computer form. The existence of the set as a primitive data type in the Pascal language makes implementing the carry and drop commands almost a snap. That is just one example. Many more will appear during the course of our investigation.

Even though Pascal has been standardized by the International Standards Organization through the efforts of the British Standards Institute, the Joint ANSI/IEEE Pascal Committee in the United States, and computer standards committees in many other countries, there remain a large number of incompatible implementations of the language. In the microcomputer world, one implementation in particular has become immensely popular. That is the UCSD (University of California at San Diego) Pascal P-System implementation. It is not just a translator for the Pascal language, but an entire software development environment for use on micro and minicomputers. I shall use the UCSD implementation for all the examples in this book. If you use a different version of Pascal, you may have to make minor changes to implement the programs found here.

I am going to assume that you are familiar with Pascal. You should know how to write Pascal pro- grams and should have written at least one or two of your own. You should have a textbook or reference that you can consult regarding matters of language syntax. I shall not attempt to teach the Pascal language from the ground up. There is a discussion

of prerequisites in more detail in Chapter 4.

As you read this book, you will learn new programming techniques in Pascal. You should study the sample programs carefully—reading programs is almost as good a way to learn pro- gramming as writing programs is. Then you should reread the programs and try modifying them in simple ways. Finally, you should attempt to write your own adventure games using the techniques described.

ORGANIZATION OF THE BOOK The book itself consists of five sections:

1. Adventure Game Concepts and Design— Chapters 1-5.

2. Adventure 1: Simple Pascal for Adventure Games—Chapters 6-11.

3. Adventure 2: A Complete Adventure Game in Pascal—Chapters 12-18.

4. MAKEDESC and BROWSE: A Simple Da- tabase for Adventure Games—Chapters 19-25.

5. Adventure 3: Advanced Adventure Game

Techniques in Pascal—Chapters 26-30.

Section 1 deals with general adventure game ideas. You should read it regardless of your level of experience in Pascal. Chapter 4 tells you about Pascal and the UCSD system and should help you decide how much you need to know before starting serious study of the book.

If you are a beginner at Pascal, you should read all the sections carefully. If you are an intermediate Pascal programmer—you have taken a course in the language or have programmed in Pascal for at least a year—you may skim Section 2 and start reading in detail in Section 3. If you are an experienced Pascal programmer—two years or more of programming in the language —you may skim Sections 2 and 3 and start serious study with Section 4. If you have some UCSD experience, you may not need to read Chapter 17, which deals with the effective use of the UCSD system for developing Pascal programs.

The appendices of the book treat miscellane- ous topics and include listings of the text of the descriptions databases used in Adventures 2 and 3.

The Elements of Adventure Games

In this chapter, I will discuss some of the ingre- dients necessary for creating adventure games. I will concentrate on games in the style of the original adventure; however, the techniques I describe apply to any adventure game.

A typical adventure game contains certain key stylistic ingredients that stamp it as an adventure. Among these are the following:

@ Rooms or Locations

Part of the raison d’etre of adventure is to explore a cave or similar adventure territory. Hence the rooms of the cave or the locations of the adven- ture territory and their interconnections form an important part in the design of the adventure.

@ Objects and Treasures

Most adventure games contain treasures. One of the objectives of these games is to locate the treasures and bring them to some “safe” place in the game map. Creating interesting treasures

and hiding them in unusual ways adds to the excitement of inventing and playing new adven- tures.

In addition to treasures, other objects may play a part in a typical adventure. For example, in the original adventure there are many objects that are not treasures but are necessary in order to make progress in the game. In this game, for example, both a little bird and a cage must be dealt with. It is the adventure writer’s obligation to constantly invent new twists to the nature and use of objects.

@ Beings and Monsters

Adventure games usually contain other beings that the player may encounter. Usually these beings are adversaries, such as the fierce green snake, the troll, the dragon, and the pirate of the original adventure. However, there is absolutely no reason why a being in an adventure game could not be an ally as well as an adversary.

@ Special conditions and Problems to solve.

Part of the difficulty in winning adventure games lies in the problems that are incorporated. The player does not merely locate the treasure, carry it out, and win! On the contrary, various obsta- cles must be overcome. Many times ‘dealing with these obstacles is a prerequisite even to locating a given treasure. Here is where the full scope of creativity and originality come into play in mak- ing a good adventure.

There are various styles of “problems” in ad- venture games. Some players relish a fight in which there are odds of losing—for example, the dwarfs in original adventure, who, if you are careless, can kill you. On the other hand, some players prefer a strictly logical challenge problems that can be solved totally with the in- tellect and involve no chance factors at all.

Now that I have enumerated the typical com- ponents of an adventure game, I will go into more detail in each category. The discussions below are intended to start your own creative processes going. They are not intended as a stock from which you merely choose a new combination to create your own adventure. To be truly successful at writing adventure games, in Pascal or whatever language, you must exercise your own individual creative powers. A definition of creativity that I especially like is the following:

To be creative—look around you at what everybody else is doing. Then don’t do that!

ROOMS OR LOCATIONS

The classical environment for an adventure game is an underground warren of caves, tunnels, or mazes. There is something romantic about exploring an underground empire, looking for trea- sure. Many variations on the cave theme are possi- ble. It is still possible to be creative by inventing new and unusual rooms. To take an analogy from music, Bach, Mozart, and Beethoven all used the same musical scales and keys to create their musi-

cal works. However, each composer produced art that was radically different from the other. There are many underground worlds still left to be in- vented that are as different from original adventure as Beethoven was from Bach!

What then are some general guidelines for creating the rooms of a new adventure? Let us consider a few:

Make Sure There is Variety. There should be a difference in character from one location to the next in a good adventure. There should be large rooms and small rooms. There should be rooms with lots of interest and others that are merely stops along the way. There should be objects spread around in a variety of rooms. It is probably a bad idea to put all the objects in one or two locations that are off in some distant and obscure corner of the map. The player of an adventure game likes to see progress being made.

By the same token, not all locations should be easy to find. Not all objects and treasures should be located right out in the open. Thus, even when the player reaches a given room, there may still be aspects of that room that are only revealed after the player solves a problem or two.

A good adventure map will have large ter- ritories that are easily accessible. It will also have one or two “chunks” of locations that are separated from the rest of the map by a narrow access path. There are various ways to achieve this goal:

@ Make the only entrance to the “chunk” via a room that has a large number of exits. This means that the player has to try a large number of exits before finding the right one.

@ Guard the only entrance to the “chunk”. That is, require that the player solve some sort of prob- lem in order to gain access to the entrance. An example of this sort of approach is the use of the fierce green snake in original adventure. It guards the only passageway to a large part of the cave. The only way to get through is to dispose of the snake.

@ Make the first part of the “chunk” dull and boring so that the player is less likely to explore further in that direction.

Require Exploration. Don’t create rooms in which everything is obvious from the very first description. There should always be rooms in which it takes some work to find out everything there is to know about them. There are many ways to accomplish this end. Some of them will be de- scribed later in this chapter. Here are just a few of them.

@ Require that a special command be given in order to obtain the full description of the room. There might be hints that this is necessary, either in the ordinary description of the room or in some other aspect of the game.

@ Require that certain conditions be met before giving the full description of the room or its contents. For example, the adventurer might be forced to possess a certain object or have ac- complished a certain goal before being able to fully discern the nature of a given room.

@ Require the discovery of information about a room from someplace else in the adventure. For example, a se ‘et map giving necessary infor- mation might be found somewhere totally re- mote from the location in question.

OBJECTS AND TREASURES

A typical adventure game has a myriad of trea- sures that must be located. It also has a variety of objects, some useful and some merely window dressing. To be different, you might try an adven- ture that has just a single treasure. But, usually it will be wise to stick to tradition. In order to invent interesting treasures to find, you must simply be creative. As mentioned above, this involves in- venting treasures that no one else has used before. Likewise, there are millions of objects that have never played a role in an adventure game. So being creative should be easy!

It should go without saying, but let’s say it

anyway:

Treasures should have some value.

This need not be an intrinsic value, but might be

value derived in some way from the circumstances of the game. For example, the game might be set in some imaginary kingdom in which dandelions were of inestimable value. In sucha situation, dandelions would be a legitimate treasure. Of course, it would be up to you, the adventure writer, to establish the value of dandelions. By inventing imaginary worlds, anything could potentially become a trea- sure. The ring of truth comes from the manner in which you set about convincing the adventurer that something ordinary could be of value in an imagi- nary setting. There is considerable scope here for inventiveness. As a challenge, try imagining something that you would ordinarily consider to- tally outrageous in the role of a treasure. Then try to invent an adventure setting and a description that makes that something utterly invaluable.

Objects are even easier to deal with than trea- sures. There are no requirements at all concerning objects. An object may be present simply because you will it to be so. No other explanation is neces- sary. In fact, it is your obligation to have at least some objects that have no use in the game what- soever. Unless, of course, you are a classical purist who requires that every element of a game have a purpose no matter how small. Most of us are not purists and are quite willing to populate our adven- tures with stray incidental artifacts. It makes the game more challenging: the adventurer must dis- cover, by reason or chance, which objects are useful and which are not.

Hints for Inventing Objects and Treasures

@ Browse through the dictionary and the ency- clopedia. You will come across an amazing amount of material in these works.

@ Play other adventure games. You may get ideas by enlarging on what has been done before. Try a new variation on old themes.

@ Read fantasy and science fiction novels for ideas. Don’t plagiarize, but let your mind roam and wan- der. Start with what you read and extend by making new hypotheses and asking “What if?”

BEINGS AND MONSTERS

Good fiction contains good characters. Good adventure games will have interesting creatures inhabiting them. Here again, you may allow your imagination to run wild as you dream up monsters and other creatures to put into your games.

There are some practical considerations here, however.

@ Beings, in general, may move around from loca- tion to location. The creation of moving beings is generally more difficult than the creation of creatures that stay in one location throughout the game.

@ Animate beings exhibit behavior. Places or loca- tions do not. It is a great challenge to incorporate behavior and reactions of creatures in your game.

Most adventure games are limited in this regard.

In this treatment, I will have creatures whose behavior patterns are somewhat limited. Doing a really serious job of simulating behavior would re- quire that I delve into advanced topics such as artificial intelligence, which I do not have the space to do.

PROBLEMS

To many players, it is the problems in a good adventure game that provide the true pleasure of playing them. I shall devote an entire chapter to this topic as I proceed. One aim will be to discuss not only the ingredients that go into creating good problems, but also the programming techniques needed to bring problem solving to life in Pascal adventures.

Notations for Describing Adventures

When you are creating an adventure game, it helps to draw maps. The adventure map summarizes the game in a concise notation that helps keep you organized. In this chapter, I describe my own nota- tion—the one that I use in the maps reproduced herein. You may end up adopting a system totally different than mine. That is perfectly acceptable. The idea is to have some way of describing your own adventure games.

ROOMS OR LOCATIONS

Most of any adventure map consists of the rooms or locations in the adventure. They should be laid out on paper in a rough representation of their “actual” geographic relationships. You obviously have to use some conventions here, especially for dealing with up and down.

Pick a standard symbol for representing a lo- cation and always write the name of the location inside the symbol. I like the hexagon or elongated hexagon shape for most rooms, with circles or ovals for maze rooms or other crowded locations. You may also choose to use different symbols depending

on the nature of the location. For example, a rec- tangle, as opposed to ahexagon, might represent a room containing a treasure.

TRAVEL INDICATORS

Adventure locations would be quite unin- teresting if it were not possible to travel between them. Adventure maps show ways to travel be- tween locations. Each possible path of travel may be indicated by a bold line joining the two locations. Arrowheads at the ends of the lines may be used to indicate whether or not the direction of travel is reversible. The absence of an arrowhead means that it is not possible or permissible to travel along this route in the direction indicated by the missing arrowhead.

Directions are part of the descriptions used in the play of the game itself. Each line of travel should be marked with a direction indicator. The usual directions are limited to n, s, e, w, u, and d. Occa- sionally, a minor compass point, such as NE or SW may be used.

Some travel paths may only be taken when

5

certain conditions are fulfilled. Some examples of this are

@ The player is carrying a certain object. @ The player has solved some problems. @ The player has removed an obstacle to travel.

These conditions may be indicated on the map by a brief description of the condition in words. This description can be written near the affected travel line or can be placed in a footnote.

PROBLEMS

Much of the fun of adventure games lies in the problems they pose for the player to solve. I elabo- rate on this theme in a future chapter. For now, I only want to show you how to indicate that prob- lems exist in various places on the adventure map.

Problems may be indicated by a special sym- bol. I use a balloon letter style question mark, enclosed in a circle as shown in Fig. 2-1.

?

Fig. 2-1. A problem indicator for adventure game maps.

This notation needs supplementation in order to distinguish one problem from another. One way is to assign a number to each of the problems in the adventure. Write the number of each problem near the appropriate question mark symbol. In a sepa- rate place, describe each problem in detail in words. Another possibility is to give each problema name analogous to the placenames given to loca- tions. The name of the problem could then be writ- ten near the ? symbol instead of a number. I use the numbering approach and write the number of the associated problem inside another full circle placed to the right of the circle containing the ? symbol.

OBJECTS Objects usually start out at a fixed location at

the beginning of the adventure. All such objects may be indicated on the map by writing the name of the object inside the box designating that location. Of course, this notation describes the adventure at the start of play. The player is free to pick up objects and move them around during play.

To indicate that there is a problem associated with an object, you can place a dash after the name of the object. Then a small ?, perhaps followed by the number of the problem, can be written after the dash. Occasionally, an object will have properties that are not part of the description of any problem. This situation can be handled by the use of footnotes that associate the textual description of the proper- ties with the name of the object itself.

You may like drawing a cartoon style cloud around the name of each object. This serves to make a graphic distinction between the placename and the object name. While this is not strictly necessary, some may find it esthetically or psy- chologically pleasing.

BEINGS AND MONSTERS

These may be considered to be objects. They are animate objects, true. However, when you are drawing the adventure map, you can treat them just as you treat the inanimate objects.

EMBELLISHING THE MAP

The artistically minded may embellish their adventure maps much further than these simple instructions indicate. Pictures of locations, mon- sters, and objects all make interesting viewing. Perspective drawings of the locations and their re- lationships is also fun. All of this has nothing per se to do with programming adventure games, but their creation may well stimulate the imagination.

MAP LAYOUT AND ORGANIZATION

For complex and detailed adventure games, you will find that you have to draw a large map. You won't be able to fit this map on a single sheet of standard sized paper. If you don’t want to go to the trouble of locating and purchasing oversized draw- ing paper, you will need to split your maps into multiple sheets. When you do this, try to group

your locations into geographically and/or logically related “clumps.” Then put one clump per page on your maps. Try to choose the clumps so that there is a minimum of possible travel paths between the different clumps. Then the connections between pages may be indicated with a symbol often used in drawing complex computer program flowcharts. The symbol in question is appropriately named the off-page connector. It looks like a pentagon with a number in it. The numbers in the pentagons are used to associate two connectors on different pages. They do not refer to any numbering of the map pages themselves. If a straight line leads into an off-page connector, look for a similar connector on another page. The matching connector should have a straight line leading out of it as shown in Fig. 2-2

—— [3

Fig. 2-2. An off-page connector for adventure game maps.

EXAMPLES

Each adventure game included in this book is mapped in detail using the techniques of this chap- ter. The resulting maps are included in the preview chapters that precede the complete listing of the Pascal source code for the game itself.

Adventures and Problem Solving

The essence of a good adventure lies in the prob- lems it poses for the user to solve. They must be varied, original, interesting, challenging without being impossible, intricate without being arbitrary, and so on. This is truly an area where your creative impulses may be vented to their fullest. There is no cookbook approach to creating good problems. There are a number of guidelines, however. Let’s do some exploring and see what we can discover.

CLUES TO PROBLEM SOLUTIONS

For each problem you contrive in an adven- ture, there should be at least one associated clue or hint. Such clues may take many forms:

@ They may be hidden in the description of loca- tions. Such clues should be subtle. In most cases the hint should be indirect. The player should be required to make some deduction based on the clue and other information. Only in .are cases and for extremely trivial and unimportant problems should you give away the answer directly.

The player should have some way of telling that part of a description is in fact a hint. This can be done by making the hint somehow different in tone or style. Make it just slightly “jar” the player into recognizing it.

@ Clues may be written on objects contained in the adventure. A clue may be written on a wall ona piece of parchment hidden in a closet or an old chest, on the bottom of a bottle, or just about anywhere. It may take an explicit command on the part of the adventurer to get at such clues. This gives you a chance to give hints about hints! You can take this more than one level deep in some cases. A few problems may even require an entire chain of clues in order to reveal their solu- tion.

To get at a message hidden inside a container, the player should have to open the container, take out the object on which the message is written, and explicitly ask to read the message. How all this is represented in a computer pro-

gram will be discussed later. Keep in mind that you will have to program your clues—so don’t get too carried away.

@ Clues may be implicit in the behavior of beings or entities within the adventure. The way a monster reacts to certain commands or situations may reveal weaknesses that the adventurer can exploit.

When you think of a good problem to include in your adventure, don’t stop there. Work on ways of making the problem solution entertaining for the player. Really good clues will enhance this. Don’t expect all this to magically come to you the instant you think of a problem. You should let the problem roll around in your subconscious for awhile. Write each problem ona separate sheet of paper and make notes as ideas come to you. Gradually crystallize these ideas into hints and clues. As you develop your adventure story line and structure, come back to these sheets again and again. Ask yourself if you can think of more or better clues. The effort you put into this will pay off in compliments from those who play your games.

PROBLEM DIFFICULTY: FROM THE OBVIOUS TO THE OBSCURE

Not all problems in an adventure should be equally difficult or easy. There should be a variety of levels of difficulty. There should be enough rela- tively easy problems in all parts of the game to keep the player coming back to the harder problems. The surest way to kill off interest in all but the most stubborn adventurers is to make all your problems next to impossible to solve.

How can the difficulty of a problem be esti- mated? One way is by the number of clues needed in order to solve the problem. The more information that must be gathered in order to figure out the solution, the harder the problem is likely to be to solve. Another factor is the individual clues them- selves. A problem that has one very obscure clue is probably harder to solve than one that has several easy clues.

When you start writing real adventures, get

others to play them. Then get feedback on the difficulty levels that you have put into them. If you have friends that are also writing adventures, so much the better. Trade adventures back and forth and then trade ideas about making problems both interesting and realistic in terms of their level of difficulty.

REPEATABILITY

There is a choice to be made in creating ad- venture problems. Do you want the solution to a problem to have an element of chance or not? For example, if you have to fight other beings, should there be an absolutely guaranteed method of win- ning or should there be some probability, however small, of failure even in the presence of perfect logic? This is a choice that you, the adventure writer, will have to make. It all depends on who will play your games and what their preferences are.

Absolutely repeatable problems will involve only logic in their solution. Problems that involve chance may still involve logic as well. The differ- ence is that the player is not in complete control of the outcome. There is a middle ground here. You may create a problem in which the player may take certain actions in order to guarantee that the chance element is ruled out. For example, if the player attacks acertain monster first, that might guarantee that there is a completely logical way to avoid being a victim of the monster. Again, it may require other kinds of actions on the part of the adventurer to secure such results. The player may need to get killed off a few times in order to notice what these conditions are. Here you have another way of creating wheels within wheels effects. The player may through experience learn more and more about how to solve a problem. There may be progress through several levels where chance still plays a part, before the player reaches the absolute logical pinnacle consisting of the problem solution.

LOGIC

Good problems should allow a player’s true problem-solving ability to be exercised. The solu- tion to a problem should not be completely arbi- trary, requiring the player to make wild or random

9

guesses in order to arrive at a solution. In short, problems should be logical.

What is logic? For our purposes, it is a system of reasoning. The player may draw conclusions about the play of the game. Such conclusions may be based on information contained directly in the game. This may take the form of hints as discussed above. It may be hidden in descriptions of locations or objects. On the other hand, it may be necessary to draw conclusions based on common sense; that is, based on the normal properties of objects or behavior of objects in a natural setting. For exam- ple, water makes plants grow; dry wood may be set on fire with matches; doors must be opened in order to see what is behind them; and you may have to dig in order to uncover buried treasure.

In deducing information, it may be necessary for a player to make guesses. These guesses should always be reasonable, not arbitrary. And good guesses might be rewarded with the offer of further information.

A good way to require the use of logic is by including more than one hint or clue regarding a problem’s solution. The player must discover two or more clues and connect them logically in order to solve the problem.

SURPRISE

I have just finished emphasizing that problems should have logical solutions. Having said that, I can now relax my position a bit and allow for the element of surprise.

There may be an inherent conflict between logic and surprise. What is totally logical is not surprising. What is surprising cannot be totally logical. What you wish to avoid is not illogical solu- tions, but arbitrary ones.

There is a fine line between a surprising solu- tion to a problem and an arbitrary one. To illustrate this point, consider the original adventure game. In that game there are two problems that serve as perfect examples. First consider the one with the surprising solution.

There is a fierce green snake that bars you from entering a certain passageway. This tunnel turns out to be the only entrance to the rest of the

10

cave. Therefore you must find a way to get rid of the snake. If you have played the game “correctly” up to that point, you reach the location of the snake car- rying a cage containing a timid little bird. You try everything you can think of to get by the snake, but all efforts fail. What to do next? Now, using logic, you might engage in the following sort of dialogue:

Q. What means are at my disposal?

A. None.

Q. Really?

A. Well, I do have this bird in the cage.

Q. What might you do with it?

A. I could get it to sing—maybe that would frighten the snake away.

Q. Oh well, I suppose. Go ahead and try it.

A. OK.

Q. What happened?

A. Absolutely nothing.

Q. Well, what else can be tried?

A. I guess I could let the bird go. Maybe I could catch the snake in the cage.

Q. Good idea. Go ahead and do it.

A. OK.

Q. What happened?

A. Amazingly enough, the bird drove off the

snake!

Surprise, surprise, surprise! You never thought the bird could have any positive effect on the snake. Nonetheless, you did discover the solution by a process of reasoning and common sense. (You thought the cage might be useful.)

Now for the problem with the arbitrary solu- tion. When you get extremely good at the original adventure, you find that you have accumulated 349 points out of a possible 350. Unless you stumbled on the way to get the 350th point, you are now faced with the nasty problem: “How do I get the last point?” This problem is particularly nasty since its solution could lie almost anywhere. Some people spend hours and hours, for example, looking for a location that they have not visited that might net them the extra point.

There are some magazines that exist in the game. They seem to play no role. However, if you

pick them up from their original location and carry them to Witt’s End, you get the extra point! How utterly disappointing this turns out to be when you discover it—truly arbitrary, truly deflating, truly anticlimactic.

The moral of the story is—try to avoid totally

arbitrary solutions. Don’t make players of your game guess that your Uncle Harry’s hair is brown in order to get a point! Don’t require them to perform random acts in random locations in order to secure the last point!

11

UCSD Pascal Review

All of the programs presented in this book were written using the UCSD Pascal system. I used the version of this system as adapted by Apple Com- puter Co. for use on the Apple II and Apple J//e. These programs should work with little if any mod- ification on any computer using the UCSD P- System. In this chapter I intend to accomplish the fol- lowing: @ Tell you what you should know about Pascal before digging in further. @ Explain to those that are unfamiliar with it what the UCSD System is. @ Highlight some of the features of the UCSD Sys- tem and some of the tricks of using it effectively. @ Give you some general suggestions that will be helpful when you start writing your own adven- tures.

WHAT YOU SHOULD KNOW ABOUT PASCAL

This is not an introduction to Pascal program- ming. I am going to assume that you know the

12

basics. That means that you have written at least a few Pascal programs. It means that you are familiar with the syntax of Pascal and the mechanics of writing programs. You should at least have an idea of the meanings of the following terms:

@ Procedures and functions.

@ Block structure.

mw Parameters: actual and formal.

@ Types (user-defined, predefined), type checking. @ Structured statements, control structures.

@ Declarations.

wm Value parameters, var parameters.

m Files (sequential), get, put, readln, writeln

@ Arrays.

@ Records.

I will be using most of these concepts as well as some others in the programs. In many cases, I will give detailed explanations of how a particular usage works, and why it makes sense in Pascal. Thus, if you are not an expert, you should be able to learn more about how to use many Pascal features.

see also page 143, “The #*%%!!? ESCAPE Key”

Just study the example programs diligently and try to follow the explanations.

I won't start out with the most basic topics, however. That is why I hope you are already con- versant with these concepts.

If the preceding list makes you feel tired or intimidated, you should probably obtain a good be- ginning textbook on Pascal. Read it in parallel or slightly ahead of your study of this book. There are many books already out on Pascal and many more appearing all the time. The best places to seek them out are your local computer store or bookstore. When you buy a book make sure that it is not limited to a subset of Pascal or to an implementation that is peculiar to one or two computers.

WHAT IS THE UCSD SYSTEM?

The UCSD Pascal system was originally de- veloped at the University of California at San Diego. It was intended to be used in the teaching of Pascal, and the goal was to implement the system on a variety of small computers, including mi- crocomputers. The project was conceived and di- rected by Dr. Kenneth Bowles, a professor of com- puter science at UCSD.

The system is just that. It is a complete software development environment for Pascal, in- cluding not only a Pascal compiler but also a com- plete operating system and many utility programs. When you use the UCSD system, it is in complete control of your computer. Thus, if you have a sys- tem with CP/M, you must reboot the system in order to use UCSD Pascal, and you no longer have access to CP/M. The same is true for most micros that support a manufacturer’s proprietary operating system. For example, when you run the UCSD system on the Apple II, you must forego access to the Apple DOS.

The UCSD system has been made into a com- mercial product by the SofTech Microsystems company in San Diego. It is now referred to as the P-System, and it runs on virtually every mi- crocomputer currently manufactured. The system is based on an interpreter that executes a pseu- code known as P-Code (whence the name P- System). The interpreter communicates with the

hardware and the peripherals of the computer via a small set of programs known as the BIOS (Basic Input Output System).

The BIOS includes the machine code neces- sary for booting the system from a floppy disk. The booting process involves the reading in of the BIOS and the P-Code interpreter from the floppy disk. Then the operating system portion of the UCSD system is loaded. It is written in P-Code and is interpreted by the P-Code interpreter. Once it has been loaded, control is passed to the interpreter, and off you go.

The UCSD system is one of the early menu- driven systems. A menu isa list of choices coded by numbers or letters from which the user chooses. The UCSD system has a command line that lists the commands available ina given context and indicates their abbreviations. You have to know what each command will accomplish by reading the users’ manuals, because there is no online help facility available.

In addition to the BIOS, the P-Code interpre- ter, and the operating system, the UCSD system provides

@ A full-screen text editor.

@ A filer for manipulating files on the floppy disk.

g A Pascal compiler for compiling Pascal programs into P-Code

@ A linker for combining certain P-Code files into programs that are executable by the P-Code in- terpreter.

@ A librarian program that enables you to create libraries of reusable pieces of P-Code. These pieces may then be incorporated into many dif- ferent programs.

There are other features of the UCSD system, but these are the ones that I will be using the most.

In order to learn about the UCSD P-System, you will need the users’ manuals for your particular implementation. I use the manuals for Apple Pascal as provided by Apple Computer. There are also textbooks available that discuss the use of the sys- tem, although they tend to be paraphrases of the UCSD documentation, organized differently and with different diagrams. Still they may in many

13

cases be useful—you will have to decide for your- self.

This book is not intended to be a tutorial on the UCSD Pascal system. I assume that you know enough of the basics to use it. I will, however, give instructions on some techniques that you may not

14

have picked up in your beginning use of the system. This will take the form of tips about general use of the system and some detailed instructions on pre- paring the programs in this book for actual use on your computer.

Preview of Adventure 1

In this book, I present substantial Pascal programs. Adventure games in Pascal tend to be large pro- grams; there is no getting around that. My approach will be to introduce you to the goals and content of each program with a preview chapter. Following the preview you will find the complete listing of the Pascal program itself. After that will be several text chapters explaining the design of the program, the Pascal features used, and the way the Pascal pro- gramming techniques relate to the adventure game.

In order to get the most out of each program, here is how I suggest you read this book:

w Read the outline chapter, noting the mention of any Pascal features with which you are not al- ready familiar. Pay particular attention to the outlines and diagrams. Try to grasp the overall design of the program.

gw Skim through the program listing in back to front fashion—see the explanation of how to do this in the preview of Adventure 1 later in this chapter. Write down questions you may have about how the program works. Refer to these questions as

you study the program in detail later on.

Read and study the chapters devoted to explain- ing the program. As you read, refer frequently to the actual program listing. If possible enter the code for the program in your own computer as you proceed.

@ After reading all the chapters pertaining to the specific program you are studying, go back and read the listing of the program once again. This time, read the program for details. Make notes of the particular coding techniques that you plan to use in your own adventures.

@ Finally, design your own adventure game, using the techniques illustrated by the program you have finished studying. Implement it using your own computer and UCSD Pascal system. Com- pare your finished game to the illustrative pro- gram in the book. Review your lists of questions, and see how many you now can answer yourself.

PREVIEW CHAPTERS Long programs can be intimidating, confusing,

15

and difficult to understand. So I would like to give you a perspective of a program before plunging into its details. By starting out with an idea of the overall structure of a program, you will be able to under- stand it more quickly. You will be able to fit details into the overall picture. Concentrating on program structure from the start will also encourage you to pay attention to the structure of the Pascal pro- grams that you write yourself. This is an important point often overlooked by beginning or relatively inexperienced programmers.

I will include diagrams that show the static structure of the program. These diagrams will list the program elements (declarations, functions and procedures by name, main program, and so on) in the order in which they appear in the program list- ing. In addition to the static structure diagrams, I will include diagrams for each program revealing its dynamic structure. These diagrams will be mod- eled after the style of diagram used in the structured design technique of programming. Such di- agrams reveal the relationships between the vari- ous procedures and functions in the program. They may also show how various pieces of data used by the program are passed around and modified by dif- ferent parts of the program.

(BD

Fig. 5-1. A diagrammatic representation of a procedure call in structure diagrams.

16

CAD ;

Fig. 5-2. Data connection in a structure diagram: A passes C to B.

Each procedure or function (as well as the main program itself) is represented in a structure dia- gram by an oval. The name of the procedure or function is written inside the oval. Lines with ar- rowheads connect various ovals in the diagram. A line that points from an oval named A to an oval named B, as shown in Fig. 5-1, indicates that pro- cedure or function A calls procedure or function B. That is, the procedure or function A has in its program text (or code, if you prefer) an invocation of procedure or function B. Ifthe tail of the line (the end without the arrowhead) has a diamond shape, @, it means that the call from A to B is conditional. The code in A will make some test. Depending on the outcome of the test, A may or may not call B. This information can be valuable in debugging a program; if there is a conditional call to B, but B is not called when it should be, there is no doubt a problem in the logic of A’s code.

Some lines that connect ovals will be labeled with variable names. This means that the data rep- resented by that variable passes between A and B. If A provides B with a variable named C, the dia- gram might look something like the one shown in Fig. 5-2.

Notice the smaller arrow beside C. The circle

PROGRAM miniadventure;

CONST, TYPE, and VAR declarations:

This section of the program contains the declarations of the constants, types, and variables used by the

rest of the program.

See Figure 5-4

procedures and functions:

This section consists of all the headers and code for all

the procedures and functions accessible to the main program block and the outermost level of the program.

See Figures 5-5 and 5-6

This section of code is the first to be executed. It is referred to as the main program block. When reading a Pascal program, you should always start here.

See Figure 5-7.

Fig. 5-3. The code outline for Adventure 1: the program outline.

on the tail of this arrow points toward the provider of C, in this case A. This information is also of potential importance. Many bugs in programs are due to variables somehow obtaining incorrect val- ues. A structure diagram can help reveal where a variable could be “going sour.” Careful study of the diagram with your code at hand often reveals prob- lems.

Structure diagrams give another viewpoint of a program, one that is more important in under- standing how the code works. When you study structure diagrams, notice how groups of functions and procedures form logical chunks of a program. Notice also how each procedure and function ac- complishes one logical task within a program. As you program in Pascal, this use of logical structure should be a conscious goal. It makes debugging and

program modification considerably easier.

In addition to all the diagrams that attempt to reveal the structure of the program, I shall include a survey of the chapters that follow the program list- ing. Each chapter will be described briefly. You will be given an idea of what to expect from each chap- ter.

PREVIEW OF ADVENTURE 1

The full listing of Adventure 1 is presented in Chapter 6. After you finish reading this chapter, you should read the listing of Adventure 1 in back-to- front style. What does that mean? In standard Pas- cal programs, the so-called main program block, that is, the code that is executed first, is located at the end of the program text. So when you read a Pascal program, you should really start at the back.

17

“Seunpeooid puke SUO!OUN} ay} : |, BINJUBAPY JO} aUII]NO apPOd aU! “S

‘puapeap © ¢ ‘yd ‘zmoueu SUOIJEDO} AU} (WO |AACI}

‘puapeapd 3Ynaa90ud

Huipnjoul) ye uooe 9y} ajpuey yey} Sainped0lq

dd 3una390ud ‘~moueud 3YNG3I0Ud

*9-G aunBi4 aag ‘saunpadoid puke suol}oUNn}

W901 aiow Jo auo sey yoey ‘azew ay} pue

uap S,a160 ay} ye UOH}Oe au} ajpueY yey} SaINp|ad01q ‘azewd 34Nd390Ud ‘woosas8od 34Nd3I0Ud

‘woo1aaid 34NG390Ud

“WOOJad! “°° ‘AINQNSEA ‘pes SUO!}E90] BY} (WO }@ACI} Buipnjoul) ye uoNoe au} ajpuey yeu} SAINpsd01q

‘ainqnsaad 3uNda90Ud ‘weysd 3UNdaI0Nd

‘uap s,ai60 ay} ye UOH|Oe ay} Buljpuey ul s}sissy ‘uonoeai80 3YNdIIOUd

jsiayja] Sey puewWOd ains Sayew }|

“@SOdNd Siy} JO} PUBWIWOD JO 19}}9] }Suly BY; Je

SOO} }| “A¥2} 0} SAYSIM JaAe|d By} UOHDAJIP 4} SEUIWW9}9q ‘suonoelp :AeMYoIYM NOILONN

‘aweb au} Jo pua au} ye as00s SJjahejd ay} Sayejnojeo ‘UFDILNI *2J09S NOILONN

‘Ke\d jo Buiuuibaq ay} ye aweb ayy jo aye}s au} jOoyas SAN|eA jenul ayy “sajqeuen wesBoud ye 0) sanjea subissy

‘azZiJeNu! JYNGIIONd

‘apin6 ay} pue aweb au} 0} 1aAe|d ay} Seonposju| UONINPOAU! JYNDIIONd

“Biy

“suolyesejoap eyep ay} :Z aINJUSAPY 40} aUIIJNO apod aU, “p-S ‘BI4

‘aweb ay} Bulunp paind90 OU eAey 410 sAeY

UdIUM S]UBAP JO 494} daay 0} SajqeUeA

payood ‘paddes ‘paddoip ‘SuiAueo ‘Bswpeas ‘ayeme ‘uajea ‘jinb ‘auop

sahejd

ay} Aq uaye} SUN} Jo JaquUNU ay} S}JUNOD sun}

0} pa}aaes} sey

jakejd ayy yey} aweb ay} ul seoejd ay)

soe} YoIUM ‘swoos Aq paexepul ‘Aeue ue Paysia

payeoo| si saAejd 9u}

a19UM SayedIpUl SOO! adAj jo ‘anjeA Ss} uolje 90}

JoXejd ayy Aq pad}

(@y42} 0} UO!}OaJIP) PUBWLWOD SpjOYy puewwoo wesBoid ay} Aq pasn sajqeuen

JAAR} JO SUOIDAIIP ajqisSod au} S}juesesdas SuoljoauIp

| unquaApy ul SUOI}e90| 34} s}Juaseideal swool sadA} pauyap-iasn

s]ue}SuOd pauyap-sasn

18

PROCEDURE pogreroom; PROCEDURE general description;

Local (or nested) procedure that is used to print

the general description

of the ogre’s demesne.

PROCEDURE pmaze;

Procedure that implements the maze. It is like a small

adventure inside the larger adventure. It uses numerous local procedures and functions.

FUNCTION bittest: BOOLEAN;

PROCEDURE describe;

PROCEDURE sameplace;

PROCEDURE treasure; These procedures provide general support for the action in the maze.

PROCEDURE pm1;

PROCEDURE pm2;

PROCEDURE pm3;

PROCEDURE pm 18;

PROCEDURE pm19; Procedures pm1, pm2, . . ., pm19 are the location procedures for the nineteen maze rooms. They are analogous to the location procedures pstart, pvestibule, and so on. They handle travel and action inside the maze itself.

The last part of the listing will contain the main program block. The code there will invoke other parts of the program, in particular the procedures and functions whose text appears earlier in the listing. This means that as you see references to procedures and functions in the main program block, you will have to look for the listings of these procedures and functions closer to the front of the program. In turn, these procedures and functions may refer to other procedures and functions. Un- less the author of the program has used FORWARD declarations (which I in general attempt to avoid in my adventure games), the procedures referred to will be listed even closer to the start of the listing.

Fig. 5-6. The code outline for Adventure 1: the procedures with local procedures.

Now you can see what I mean by reading the pro- gram back-to-front.

Figures 5-3 through 5-7 present the code out- line of Adventure 1. Figure 5-3 shows the entire program structure in brief, explaining the major parts of every Pascal program. It refers to the other figures that show various parts of the program in more detail. Figure 5-4 outlines the const, type, and var declarations of the entire program, giving brief descriptions of some of the more important declarations used by the program. Figure 5-5 out- lines the procedures and functions used by Adven- ture 1. Again, there are brief comments regarding some of the more important of these. Figure 5-6

19

BEGIN

Procedure invocations (once only) at the

start of the game play.

introduction; initialize;

REPEAT CASE location of start: pstart;

grandroom: pgrandroom;

Fig. 5-7. The code outline for Adventure 1: the Main program block. flames: pflames; END; UNTIL quit OR done;

Main control loop of the adventure game program. Various location procedures are repeately invoked until the game is complete. This is signaled by one of the two Boolean variables quit or done becoming TRUE. congratulations;

Procedure called to wrap up the game and summarize

the results including the score for the player.

outlines one of the longer procedures of the pro- gram, pmaze. This procedure is notable for having many local procedures and functions. Finally, Fig. 5-7 outlines the main program block of Adventure 1.

Figure 5-8 is the dynamic structure diagram of Adventure 1. It shows the relationships between various groupings of procedures and functions used by the program.

CHAPTER PREVIEWS

Chapter 7 is entitled “Representing the Map.” It discusses the use of enumerated types in Pascal programs generally an in adventure games specifi- cally. You should pay special attention to this brief chapter. It lays groundwork for a technique that is used repeatedly in later adventures. At the end of Chapter 7, the map of Adventure 1 is presented in the graphic format explained in Chapter 2.

20

Chapter 8 is entitled “Controlling the Play” and deals with the use of enumerated types in con- junction with Pascal case statements. Chapters 7 and 8 go hand in hand. Go back and reread both chapters after you finish them the first time. The use of enumerated types in Pascal case statements is one of the most powerful Pascal coding tech- niques. Unfortunately, it is also one of the most underused. Master the technique, and you will have a valuable tool for all your Pascal coding, not just your adventure game writing.

Chapter 9 is entitled “Mazes in the Middle” and deals with the implementation of the maze in Adventure 1. It discusses the use of local proce- dures and functions as well as local declarations in general.

Chapter 10, entitled “Other Techniques Used in Adventure 1” discusses what I have termed the ad hoc code of Adventure 1. It explains how mis-

congratulations

oe

eae

Conver) Come) SIciIcIo

ogrereaction

=

(em) a7

MRT ee Bet eS pm11 pm12 pm13 pm14 pm15

\wY WN” owas a

pm16 pm17 pm18 pm19 A / =" _

Fig. 5-8. The structure diagram for Adventure 1.

cellaneous features of the game were implemented problem in a very specific way and may not be as by direct Pascal coding techniques. The techniques generally applicable as some of the techniques de- are ad hoc in the sense that they solve a specific scribed in earlier chapters.

22

Pascal Adventure 1

This chapter presents the complete listing of the first Pascal adventure game. This example is meant more to illustrate features of Pascal than to be a serious game. To get the most out of this program, you should go through the following steps:

1. Skim read the program right now. Try to grasp the overall structure of the program and don’t worry too much about the fine details. Refer back to the code outlines in Chapter 5 as you skim. Jot down any questions you may have about the overall organization of the program.

2. Read and study Chapters 7 through 10, which discuss the details of the Pascal coding of this adventure. As you read these chapters, study

LISTING 6-1. PASCAL MINIADVENTURE

4,

Before typing, see page 143, “The #*%%!!? ESCAPE Key”

the listing of Adventure 1 and absorb the details. Imagine how you will apply the language fea- tures discussed to your own adventure game programs.

. When you have finished Chapter 10, go back and

reread the listing of Adventure 1 in detail. Check your list of questions from step 1, and see how many of them have now been answered. Write an adventure of your own using the techniques illustrated by Adventure 1. Make up your own map and your own location descrip- tions. Notice what parts of the Pascal code you can use almost unchanged and what parts you have to alter radically.

(in addition to Chapter 4, page 12.) COCO OIC CK OK OOK OOOO CICK GIO GK GK)

(x *) xX a dv @ nm t wor «& # 4 x) (xX x) (kK This is an example of the Pascal language x)

23

(*k features that are useful in writing advent~- *) (kX games. It is not really a serious game, but x) (k more an instructional exercise. *) (x x) COO OOOO OK GOK OK KK KKK KKK )

CXHo+k) ——e Compiler “swap” instruction, see Chapter 17, page 141 FROGRAM miniadventure;

CONST fF = 12s ew = 12; nw = os ne = ben Sew = 14; nonly = 1: nsew = Les newud = O14 Sw = 10; ns = 8 danly = S24 cin = aan § ud = 48; SU = 18s TYFE rooms = (start, grandroom, vestibule, narroawl, lakeshore, island, brink, iceroom, ogreroom, narrow2, pit, crystal, batscave, steam, deadend, ladder, maze, flames); Girections = (MaSaG@uWatta dd) § byte = O,.2553 VAR commands STRING;

(xk holds user typed direction *) chs CHAR:

dchars: SET OF CHAR: (* characters which correspond TQ the

24

acceptable initial letters OF

direction commands. initialized TQ

ae 2 aa —- e ' *) location: rooms ogreloacs: rooms; visited: ARRAY C start..flamesJ QF BOOLEAN; nexts directions; twopow: ARRAY EO n..dJ OF INTEGER; turns: INTEGERS done: BOOLEANS; quits BOOLEAN; eaten: ROOLEANS awake: BOOLEANS readmsg: BOOLEAN: carrying: BROOLEANS dropped: BOGLEANS trapped: BOOLEANS; cooked: BOOLEAN:

COOK KOK KKK)

(kK wip © x)

CK KKK KKK KKK KD

FROCEDURE wipe;

REGIN write (chr (ff));

END;

CKPiminii.text*) These are “include” instructions; see Chapter 17, page 140 for more info.

(XPiminiz.textxk) As written, they assume all files are on Drive 1 (Volume #4: in Pascal.)

(Xtimazel.textx) For this and for page 81, refer to “A note about disk Drive numbers in file (XGimini2. text) names and commands” on page 3 of this PDF.

OK OK A new file named “mini1.text” begins here

x)

COOK KOKO KKK KKK KK KK RK KKK KK KKK AK KKK KK) (x i fi¢¢ Fr © d@wteée«t i oO on *k) OK KKK KKK KKK KKK KKK KKK KKK KK KKK KKK KKK KKK)

FROCEDURE introduction; BEGIN

wipe; (kK clear screen *);

writeln (*Welcome to miniadventure!’);

25

writeln writeln writeln writeln writeln writeln writeln writeln writelny; writeln writeln writeln writeln writelns writeln writeln writeln writeln writeln writeln writelns writeln

(? Your goal will be to find a treasure’); (?and bring it to the starting point.’); (7You will also get points for finding’); (?each location in the adventure.’ ); (*Points will be deducted for various’); (7undesirable happenings: waking the’); (ogre, getting eaten, getting toasted,’); Cfetc.")3

(*I will guide you and be your eyes and’); (ears. I will describe your location’); (‘and give you special instructions’); ("from time to time.*)¢:

(*To command me, tell me a direction’);

(*ta take north, south, east,."); C(*west, Up, ar down.")s (?note: I only look at the first letter")y;

("of the command, so abbreviations’); ("are acceptable.’)s

(* When you are ready to begin your’);

writeln| (adventure, just press "return". 7)3

use “write” not “writeln” (writeln pushes top line of text off the screen) readln(cammand) ;

FE.ND

wipes

(xk FROCEDURE introduction *)5;

OOK ROKK KR KKK KK KK KKK KK KKK KKK KK KKK)

(x

i n

i t i a |] i z e x)

COO OOK KKK KKK KKK KK KKK KKK KKK KK KKK)

FROCEDURE initialize; VAR

loc:

BEGIN

26

location dchars done quit cooked eaten awake

rooms;

starts Eq? 4? false; false; false: false; false;

readmsqg := false; carrying := false; trapped := false; dropped := false; turns = Oy twopowEinI]:= 1;

twopowls]:= 2;

twopowlel:= 43

twopowlLwJ:= 8;

twopowlud: =16;

twopowld]:=32;

FOR loc := start TO flames DO

visitedClac] := false

(kK enddo *);

END (x PROCEDURE initialize k);

OX OK KKK KKK KKK KKK KKK KKK KKK KK KKK KK KKK KKK) (x S Cc oO r e x) COKKOOK KKK KKK KK RK KK KR KKK KK KKK KKK KKK KKK KK)

FUNCTION score: INTEGER; VAR

lac: rooms s

Sc: INTEGERS; BEGIN

tart TO flames DO

sc r= sc + 10 (x endif *) (kK endda X);

IF NOT quit THEN

Sc s= sc + BOs;

IF cooked THEN sc

27

ScCOre := sce END (* FUNCTION score X);

CORR OOK ROKK KK KKK KK ) (K congratulatians x) CORO OKO KK ROKK KKK KK KOK KK KK)

PROCEDURE congratulations; BEGIN

IF NOT cooked THEN REGIN IF NOT quit THEN REGIN

Capitalize as ‘Congratulations’ or ‘CONGRATULATIONS’ if you like

wreitelnm (7 XXKKK congratulations XkKKK")3;

writelns

writeln (*You got the treasure out in only")

writeln (turns:4,"* turns. *)3

END;

writeln (*You scored’, score:4, * points out’)

writeln (* of a maximum of 2OO points.")3

writeln ("Sa lang for now, came again saon!"): END ELSE

writeiln (Sorry about that - try again soan'!")

(kK endif xk);

readin (command); Wipes

END (x FROCEDURE congratulations *):

28

ORK KKK KR KK KKK KR KK KKK KK KKK KKK KKK KKK KKKKK) (x w A tft cc AH Ww aiy x) CROCK KKK OK KOK OK KOK OK KOK KK)

FUNCTION whichway:directions; BEGIN

turns = turns + 1; REF EAT REPEAT writelns write ("Which way?===%"); readin (command) ;

UNTIL length (command) + O;

ch := command(eid;

CASE ch OF *n's whichway = 3 7s" 5 whichway := 3 "e*s whichway : = @3 "wk whichway #= wy a a whichway #= us *d*s whichway = ds 7 qr quit r= trues ENDs

UNTIL ch IN dcharss writelns;

end (xk function whichway *)43

COCO OK KKK KKOKKK KK KKK KK XK) (x rm oO w a y x) COCO OOOO OOK OK ORK KKK KOK OK KOK Ko)

REGIN

29

writelnys writeln (Yau cannot go in that directian.*)3;

END s

(K(x figs Bs New file named “mini3.text Cte Sees eesesceeceseese ee eseseeeess 26 2.6.8 2)

Cx Oo g Yr @€@ a c t Gd @ FT x)

CGO OOOO IC GIGS IGG II KE

FROCEDURE ogqreaction; REGIN

IF NOT awake THEN BEGIN

writein ("This is the ogre’’s lair!’); weiteln (If you are not careful, you" "11")3 writeln ("wake him.")s;

IF (turns MOD 7)=0 THEN REGIN awake = true; wreiteln ("Now you’ *ve done it!"); writeln ("You woke the agre - better’); writeln (get out of here while you can*)3;

END (xk IF X)5

END ELSE REGIN

writeln ("You wouldn’*t listen ta me would’); writeln ("your You really better get out’);

wreiteln (‘af here before you get eaten! ")s;

IF carrying

THEN IF (turns MOD 2)=0 THEN BEGIN

30

writeln (€* Tao bad! ! The agre caught yau"); writeln (and roasted you for dinner.*)s; writeln ("Better luck next time!!");

eaten := trues quit m= trues

END BSE REGIN

wreiteln ("Get cut fast if you dan**t want"); wreiteln (*to be a big-mac for the ogre! !");

END ELSE IF (turns MOD S) =o THEN BREGIN

weiteln (* Too bad - you’ "ve been eaten! *)s

eaten : t. quit os= true;

END (k endif x) END (xk IF NOT awake k), END (x FROCEDURE ogreaction ); CCK KOKO OK OK OK OK KKK OK KK KKK) (x Pp S t a r t x) CK ORK OK KKK KKK KK KKK KK KKK KK RK KK KKK KKK KKK)

PROCEDURE pstarts:

REGIN IF carrying THEN done 3: true ELSE BEGIN

31

writelin ("You are standing by a hole in"); wreiteln ("the ground. It looks big enough") s writeln (*ta climb down."*);

CASE whichway (GF

My Sa @aWe noways ur writeln ("You can’ *t jump to the clouds!") 5 ds: location := vestibule; END s END (xX IF carrying *)5 END (x FROCEDURE pstart *);

COCO KOKO KK KOK KOKO KOK OK) (x Pp vy e s t i b u 1 e x) COOK O OKO K KOK OK OK KKK OK )

FROCEDURE pvestibule; REGIN Use proper capitalization in all of the descriptions throughout this game:

32

writeln ("you are in the entrance ta a cave’): writeln (*of passageways. there are halls"); writeln (‘leading off ta the north, south," ); writeln ("and e@ast. above you is a tunnel’); writeln ("leading to the surface.*); IF dropped THEN REGIN writeln (* Ta the north, through a narrow crack,’ writeln ("You can see the treasure. If you?) s writeln (stretch your arm through you might*); writeln ("reach it. Do you want to try?")s3; readin (command) gs IF (command = “yes*) OR (command = "y*) THEN BEGIN carrying : = trues; dropped := false; END (xk IF xX),

ENDs CASE whichway OF

ne location := nmarrowl; Sy locatian := grandraams es lacation := iceraoms; wed: moways

us location := starts

END (x CASE whichway X)5;

END (x FROCEDURE pvestibule *);5 OOO ORK OOK KK OOK KOK KKK OK KOK KK )

(x p aq roeaondreoaoo i m *) COOK OOK OOK OK KK KK )

FROCEDURE parandroam;

REGIN writeln (You are in a huge open roam, with"); writeln (?an immense expanse of ceiling.*); writeln (7A dark passage leads west and a*)3 writeln (*?narrow crawl leads downward." )3

CASE whichway OF

Wi location #= brinks d: location := iceroam;

Tig Sy Gate noways END; END (xk FROCEDURE pgrandraom *); OOOO OK KKK KKK KK KOK KKK KK KK KK KX)

Cx pon aeerreow fi x) COOK OOK KOKO KK KOKORO KK KK KKK KK KK)

FROCEDURE pnarrowl; BEGIN

writeln ("You are in a narrow passage which") 3

33

writeln ("continues to the north. It is"); writeln ("extremely narrow to the south.’)3

writeln (* A very tight crawl also leads east.”*) writeln (°A curious odar seeps through it.")4 writeln (7 I would think twice before trying’)s;

writeln (*to go that way!*)5

IF carrying THEN REGIN

writeln ("The treasure won"*t fit through”);

* 8

writeln ("the crack going south. Do you want");

writeln (*to leave it here?*’);

readin (command) ;

IF (command = “*“yes*) OR (cammand = “y") THEN BEGIN dropped := trues: carrying := false;

END (kx IF x); END (x IF carrying Xk); CASE whichway OF

akeshore;

mis location 1 ogreraoms:

@s location : Ss: writeln (? Walla ds noways END (x CASE whichway *); END (x PROCEDURE pnarrowl *) 3 Ct Peseseses se seseetse ses Fes F353 25393 2 (x p tl ak es RH oF Ff 6 *) COO OOO OR KIO KOK OK OK KKK KK ) PROCEDURE plakeshore;

34

It*’s too narrow to get through!’*

)

® a

BEGIN

writeln ("You are on the shore of a vast*);

writeln (‘underground lake. Narrow passages");

writeln (’wind away to the east and south. *); writeln ("A small island is visible in the’); writeln (center of the lake to the north.’);

CASE whichway OF

nz: location := island; 85 location := narrowly; @s location := narrow2; w.u,ds noaways

ENDs END (x PROCEDURE plakeshore *)3; CROOK KKK KK KKK KKK KKK KK KK KX)

(x p is 1 aon id x) CORK OK KKK KKK KKK KKK KKK KKK KKK KK KKK KKK KKK)

PROCEDURE pisland; BEGIN

writeln ("You are on a small island in the’*)s

writeln ("center of a huge underground lake.*) 3 writeln (*Dark frigid waters surround you an"), writeln ("’all sides. You can barely make out*) writeln ("the shoreline to the south.");

weiteln (*A small message is scratched in the*) writeln ("dirt here. It says: “*The treasure’) writeln (?may be found in the maze.**")3

CASE whichway OF

My GaWa lia di noways

Ss ENDs

readmsg

location := lakeshore;

:= true;

END (x FROCEDURE pisland *);

« "

35

COOK GOK KK KOK KOK KK KK)

Cx

b r i n k x)

(KKK OK OK OK KOK KKK OK KK KK KK KKK KKK KK KK)

FROCEDURE pbrinks BEGIN

END

writeln writeln writeln writeln writeln writeln writeln writeln writeln writeln

You are on the brink of a steep"); incline. The bottom of the pit’); is over fifty feet below you. ")s; You could probably slide down’); (safely, but I won’*t promise you"); (that you could get back up.*); (* To the west is a dark opening’ (into a rubble~filled tunnel. A’ ("vampire bat just flew out of it’ (* shrieking." ) 3

¢* ¢* (* ¢*

?

yg ds 3

CASE whichway OF

MN, Se Gaus

we ds

END;

(k PROCEDURE pbrink

noway location := ogreroaom: lacation #= pits

K)s

CK KKK KKK RK KKK KKK KKK KKK KK KKK KK KKK KK KKK)

(x

A

i

c oe r o oO Mm x)

CK KK KK KKK KKK KKK KKK KKK KKK KKK KKK KKK KE KKK)

FROCEDURE piceroam; REGIN

36

writeln writeln writeln writeln wreiteln writeln writeln writelin writeln

(*You are in a room whose walls are’*)s: ("formed from a deep blue crystalline”); (7ice. To the north a narrow tunnel"): Copens. From the other end of the tunnel’); Can aminous growling sound may be"); (heard. To the east a sparkling *)s Cluminescence emanates from a broaad*)s

(7 apening. To the west a passage");

(‘leads back toa the vestibule.” ),

CASE whichway OF

es lecation := crystals: ms lacation := oagrerocoms we location := vestibule; Salt,ds noway 3

END;

END (x PROCEDURE piceroom *)3;

COOK KK KOK OK KKK KK KK KK KKK KKK KK KK RK KK KK) (x Q g r e r QO Q m x) COOK OKO RKOK OK KK KKK KK KK KK KKK KKK KK)

FROCEDURE pogreroom:

)

VAR i,j: INTEGERS: FROCEDURE generaldescriptions: BEGIN writeln (* You are in a low room whose walls’); writeln (*’are covered with ominous dark’); writeln (*gouts of dried blood. The center’); writeln (of the room is dominated by a’); writeln (*firepit, which contains burned’); writeln (“out coals and a lang spit suspend-*) wreiteln ("ed aver its center." ); writeln ¢? Fram one dark carner emanates a*); writeln ("“harrible growling noise like that *) writeln (of some unspeakable manster snoring’ writeln (during its dream of rending you limb wreiteln (fram limb and making you its dinner!’ END (xX FROCEDURE generaldescriptian *); BEGIN

generaldescriptioan; ogreactions

IF NOT eaten THEN BEGIN

writeln

(*There are exits to the east,

west, *)

) )

37

New file named “maze1.text”

writeln C north, and south. 7); CASE whichway OF we lecatian := narrowls es location := batscave; mi location := narrow2; Ss location := iceraom; ds BEGIN quit = trues eaten := trues; writeln (*Oh no!! You dummy!!!" ) 5 writeln (You just fell in the firepit’): writeln (7and made such a ruckus that’); wreiteln (you woke the agre. IT hate ta’); writeln (*tell you this, but you are"); wreiteln ("also trapped!*); FOR i = 1 TO 2 DO BEGIN FOR j s= 1 TO 1900 DO; weite d?.7)¢8 END (xk DO x); writeln (*? You have heen added to the"); writeiln Coagre**s gourmet recipe library! *)3 writeln ("Better luck next time.")3 ENDs ue Noway § END (* CASE whichway *); END (x IF NOT eaten *)35 END (x PROCEDURE pogreroam ) 4 Ck CX x) COOK OK KOK KKK KOK KKK KOK OK ) (x a m a = e x)

COCO KICK KK KOK OK KKK KKK)

FROCEDURE pmaze; TYPE Mazerooams = (mi,m2,m3,m4,m5,m6,m7,m8,

38

m9,miO,milt,mi2Z,mis,mi4, miS,mil6,m17,m18,m19)

VAR mazelacs MAZeraams s bitset: ARRAYE directions] OF BOOLEAN;

FUNCTION bittest (ve INTEGER; dir: directions): BOOLEAN;

BEGIN IF (¢(v DIV twopowldird) MOD 2)=1 THEN bittest := true ELSE

bittest := false (kX endif *)3

END (* FUNCTION bittest x);

FROCEDURE describe(wh: INTEGER); VAR dirs directions;

BEGIN

writeln (You are in a maze of featureless"); writeln ("*passaqges. There are exits visible’); writeln (“in the following directions:’);

IF bittest (wh,.n) THEN write (*n IF bittest (wh,s) THEN write (*s IF bittest (wh,e@) THEN write (’e IF bittest (wh,w) THEN write (*w 73 IF bittest (wh,u) THEN write ("uu IF bittest (wh,d) THEN write (*d

writeln;

end (* procedure describe *);

FROCEDURE sameplaces BEGIN

writeln ¢* You have crawled around same*) writeln (*twisted tunnels and wound up*)

“2 Es

39

writeln (where you beqan.*)3: END (xk PROCEDURE sameplace *)>3

PROCEDURE treasure; REGIN

IF NOT carrying THEN REGIN IF readmsg THEN RE(GIN

writeln (*The treasure is here!!*);5 writeln (*Do you want to take it now?’);

readin (command) ;

IF (command = *yes*) OR (command = “y"*) THEN carrying #= true

(k endif xk); END ELSE BEGIN writein (*The light is extremely dim here’); writeln ¢(* You better get out or risk’); writeln (*falling into a pit.")3 END (* IF readmsq *); END (* IF NOT carrying ¥)5 END (X* PROCEDURE treasure *)3

PROCEDURE pml; BEGIN

writeln (*You are in a maze of featureless"):

writeln (*passages.")3 writeln (*from here you can go south, east,*);

40

writeln (* west, or up.")3

CASE whichway OF

Sy location := ladder; @s mazeloc := ms we mazelon s= m4; us location := steam: nads noway s END; END (x pmil kK); FROCEDURE pms BEGIN describe (nw) s CASE whichway OF ns mazeloc := mi; ws sameplace; G2, Satt.ds noways END s END (x PROCEDURE pm2 x); FROCEDURE pind; REGIN describe(ne)s CASE whichway OF ns mazeloc := mil;

e: sameplace; SaWalled? NOWAYS

END; END (x FROCEDURE pms *);

PROCEDURE pm4; REGIN

describe (sew) ;

CASE whichway OF

Ss mazeloc := m7;5 e: mazeloc := m3; ws mazeloc := mS; Malt, ds noway § ENDs END (xk FROCEDURE pm4 x); FROCEDURE pmSs REGIN describe(nanly); CASE whichway OF ns mazeloc := m1; BuWy Sally de noway§ END: END (x FROCEDURE pms *)3 FROCEDURE pmés REGIN describe(ne)s; CASE whichway OF ns mazeloc := m4; es sameplace;

SaWalta ds noway § END; END (kx PROCEDURE pmé& *)¢;

PROCEDURE pm73; BEGIN

42

describe (nsew) :

CASE whichway OF

ns mazeloc s= mdz, Ss mazeloc := m9; e: mazeloc := més we mazeloc := m6; unde noways END; END (kx FROCEDURE pm7 k)3; PROCEDURE pm&s; REGIN describe (nw) 3 CASE whichway OF ne mazeloc := mos Ws sameplaces

2,Satt.ds noways END: END (k FROCEDURE pm x);

FROCEDURE pm?; BEGIN

describe(sw); CASE whichway OF

Ss: mazeloc +: we mazeloc :

NaG@alta ds noways; END;

END (x FROCEDURE pm? *);

PROCEDURE pmids BEGIN

describe (ns):

CASE whichway OF

ms mazeloc := m8; Ss sameplace; SaWelladt NOQWAYS END; END (x PROCEDURE pmiO x), FROCEDURE pmii; BEGIN describe (newud) 3 CASE whichway OF nme mazelac s= m9; er mazeloc := més; we mazeloc := miOs ue mazeloc s= mi; d: mazeloc = mizs : noways END; END (kX FROCEDURE pmil *); PROCEDURE pmiis BEGIN describe (dn) s CASE whichway OF ms mazeloc = mis; ds: mazeloc := mié;

@x,SaWall? MOWAYysS

44

END; END (XX PROCEDURE pmi2

FROCEDURE pmi3; BEGIN

describe dn) ; CASE whichway OF

ne mazeloc ds mazeloc

2.S.W,lls maways END: END (x FROCEDURE pmis

FROCEDURE pmi4; BEGIN

describe(dn) s CASE whichway OF

ris mazeloc ds: mazelac

Gq SaWa ltt NOWAY &

END; END (* FROCEDURE pmi4

FROCEDURE pmiSs BEGIN

describe (ud) 5

CASE whichway OF

us mazelac : mazelac

NaSaeGaWt NOWays

ii 3 3 ee ai “28

me

ENDs END (* FROCEDURE pmiS *)3 FROCEDURE pmildés BEGIN describet(ns) ; CASE whichway OF ns mazelac := m1l7; Ss sameplace; @uWalla dt Nowey § END; END (xk FROCEDURE pmié& &)s FROCEDURE pmi7s BEGIN describei(ns)s; CASE whichway OF ns mazeloc = mi; 5: mazeloc := mild; @aWatts ds noway 3 END s END (kx PROCEDURE pmi7 *);

FROCEDURE pmi®s REGIN

describe (ns) ¢ CASE whichway OF

ms Mazeloc Ss mazeloc

I a naoways

46

END; END (x FROCEDURE pmi8 *);

FROCEDURE pmi?; BEGIN

describe (su) treasure;

CASE whichway OF

S mazeloc s= mig; ues mazeloc := mids

Me @aWadi noway § END; END (x PROCEDURE pmil9 *): BEGIN (xk FROCEDURE pmaze *); mazeloc := mi; REPEAT

CASE mazeloc OF

mis pmis Mie k pm2y mas pmss m4 s pm4s mos pms; Mos pmeas m? s pms mes pms sy ms pm? 3 miG: pmo; mids pmil; mics pmizy mic3s pmiss mids pmi4; mis pmics mig: pmlé; mis pmi7;

47

mi8: pmigy; miQs pm1l?;

END (x CASE mazeloc );

UNTIL location <=? maze;

END (k PROCEDURE pmaze *) 3

(CK OX a “) New file named “mini2.text OX K KK KKK KKK KK KK KKK KK KKK KK KKK KKK KR KKK KEK) (x Pp on aA F F @ W 2 x)

CKO KOK KKK KK KK KK KKK KKK KOK KKK KKK KKK KKK)

FROCEDURE pnarroaw2; BEGIN

writeln ("You are in a very narrow passage." )3 writeln (*To the west the passage opens out"); writeln ("by a lake shore. Toa the east it is"): writeln (even tighter. You just might be"); writeln (‘able to squeeze threaugh if you try"): writeln ("real hard.*);

writeln ¢° There is also a strange looking" )s; writeln ("alcove in the south wall that seems"); writeln (*toa open into a very dark tunnel.*)3;

CASE whichway OF

we location := lakeshore; e: location := steam; = location := ogreraoms: Matha: noways

END;

END (xX PROCEDURE pnarrow2 *) 4 COO OOK KK ROKK KK OK KK KKK KK )

(x p p i t x) COCO OOO OK KKK KKK KOK KOK KK KK KK )

PROCEDURE ppit; BEGIN

48

writeln (You are at the bottom of a fifty"): wreiteln ("foot pit. The walls are just a*)3: writeln (*hair tao steep ta climb. The pit"): wreiteln ("is empty except for a few ald"); writeln (dried bones ~ I can**t tell af they’)3 writeln Care human ar not! ! In the center")s writeln ("of the pit is a nmarraw shinny’); weiteln (leading downward." ) 3

CASE whichway OF ds: location := ladders

us BEGIN writeln ("If yau try ta climb that, *)3 writeln (Cyou’"“re sure to kill yourself!")s; END;

Ty Sa @aWe noways END; END (X PROCEDURE ppit *);

CK KK KKK KKK KKK KKK KK KKK KR KKK KKK KK KKK KKK) (x Pp c r y S$ t ail x) COOK OK OK KKK KK KKK KKK KOK KK KKK KKK KKK)

PROCEDURE perystals REGIN

writeln ("You are in a shining hall af crystal.’); writeln ("It is intensely cold but also chill-*)3; writeln (ingly beautiful. There are glass’); writeln (*formations rising from the floor’)s; writeln ("as if they had grown there, yet’); writeln ("delicately sculptured with multi-*)4 writeln ("faceted sides. An intense white’); writeln (light shines brilliantly from the’); writeln (*floor, which is also made of a"); writeln (*mirror smooth lead crystal. The Light’ )s: writeln ("is almost blinding and the many"); writeln (reflections that it sets off among”): writeln ("the crystal formations of the room’); writeln (*make it almost impossible to tell’),

writeln ("where the room begins and where’);

writeln ("it ends. *)3

CASE whichway OF

@s location := maze; NaWs lecatian := ogreraam;

Salles noway § END: END (x FROCEDURE crystal xk); COOK KK KK KKK KKK KK KK KKK KKK KKK) (x p b a t S$ © A Vv @ x) CK KK KKK KKK KKK KKK KKK KKK KKK KKK KKK KK KKK)

FROCEDURE pbatscaves;s REGIN

writeln (*You are in a steep cavern filled’);

writeln ("with shrieking vampire bats. writeln (* swoop and dive at you by the writeln (* thousands. If I were you, I writeln ("get out as quick as I could. writeln (*are openings to the west and

CASE whichway OF

we location := ogreroom; ns location := steam; @u Salt, noway §

END;

END (x FROCEDURE pbhatscave *)3;

COOK OOK OKO ROK KK KK KK KKK KKK KK KX) (x Pp S t e a m x) COOK OK KK KKK KKK KKK KKK KKK KK KK KKK KKK)

PROCEDURE psteam; BEGIN

50

They") 5s

sa 5 would’) s

There’); north. ")¢

END

writeln

writeln (’a stifling steamy vapor. There are’); writeln ("innumerable small geysers scattered’); writeln (about, each contributing its own steam’); writeln ("to the general mist.7);

writeln (*To the west is a dark opening, as"): writeln ("well as to the north. Further out’); writeln (in the middle of the room is a dark’): writeln ("opening in the floor into which weiner 24 writeln ("af the vapor seems to be seeping’):

(*You have entered a hall filled with’);

CASE whichway OF

we location := narrow2: ns location := de adend; ds: location := maze; Ss location := batscave; @ytts noways

END;

(kK PROCEDURE psteam *)3;

CK KK KKK KKK KKK KK KKK KKK KKK RR KKK KKK KKK KKK) (x Pp 1 aodqdqeor x) COOK GK KKK KKK KKK KKK KK KK KX)

PROCEDURE pladders BEGIN

writeln ("You are at the base of a huge ladder’);

writeln (reaching up out of sight. writeln (extend up at least SOO feet,

It must") s

and it will’);

writeln (’take someone brave in heart to scale’);

writeln (it. writeln ("lead north and down.’)s:

CASE whichway OF

ns lacation := maze; d: location := flames: us IF carrying

THEN

BEGIN

There are alsa passages which’);

51

writeln (*? You can’*"*t carry the treasure up the’);

writeln ("ladder - it**s much too heavy! "); END ELSE

location := vestibule (xX endif *)s;

@,SaWE noaways

END: END (xk PROCEDURE pladder *); COOK KKK KKK KKK KKK KKK KKK KKK KK KKK KKK RK KKK) (x p f 1 a m e S *) CHK KKK KKK KKK KKK KK KKK KKK KKK KKK KKK KK KKK KK)

PROCEDURE pflames;3

BEGIN weiteln (Unfortunately you have fallen into’)s; writeln ("an underground fire pit. It is the"); writeln ("source of the heat that produces’); writeln (’the geysers in the steam room. You") s

writeln ("*have been toasted to a crisp to put’); writeln ("it politely." )5

END (x FROCEDURE pflames x);

CHR KKK KKK KKK KKK KKK KK KK KKK KKK KK KKK KKK KKK) (x p d @ a d e n d x) CHK KK KKK KKK KKK KK KKK KK KKK KKK KKK KKK KKK KKK)

PROCEDURE pdeadend; BEGIN

writeln ("Dead end.*); CASE whichway OF Ss location := steam;

52

Na@aWylta cs

END;

END

BEGIN (k ===

noways

(xk PROCEDURE pdeadend *);

CHK KKK KKK KKK KK KK KKK KKK KKK KKK)

cE

adventure

{ses

x)

CKKK KK KKK KKK KKK KKK KKK KKK KKEKK)

introductions initialize;

REPEAT

visitedLlocationg)

CASE location OF

starts: grandroom: vestibule: narrowls: lakeshore: island: brinks iceroaoms agreroom: narraws: pit: crystal: batscave: steams deadend: ladders MAZE flames:

END (x CASE

UNTIL quit OR done;

cangratulations;

END.

= true;

pstarts pgrandroom; pvestibule; pnarrowls plakeshoare; pisland; pbrink; piceroom; pogreroom; pnarraw2; ppits perystal; pbhatscavey; psteams; pdeadend; pladders pmaze; pflames;

lacation *);

53

Representing the

In this chapter, I begin to show you how to use the Pascal language to write adventure programs. The Apple version of UCSD Pascal has been used in all the examples in this book. These programs should run without major modifications on any system with UCSD Pascal.

Chapter 6 has presented the listing of Adven- ture 1 in Pascal. This adventure would not present much of a challenge to a seasoned adventurer. In fact, even beginners would probably find it boring. My purpose was not to write a serious game; I wanted to introduce you to some of the features of Pascal that are useful in writing adventures. By studying these techniques, you can learn to write your own adventures in Pascal. I hope that you also gain a deeper understanding of Pascal and become a better Pascal programmer as a result.

REPRESENTING THE ADVENTURE MAP

Figure 7-1 presents most of the initial map of Adventure 1. The notation for the map is explained in more detail in Chapter 2. Notice the use of the

54

Map

circled question marks and circled numbers to indi- cate the problems of the adventure. Figure 7-2 shows the map of the maze incorporated in Adven- ture 1. Finally, Fig. 7-3 lists the problems by number and gives explanations of each one.

There are 18 rooms shown on the map in Fig. 7-1, not counting all the individual maze locations. I will represent these rooms in Pascal by using an enumerated type:

TYPE

rooms = (start, grandroom, vestibule, narrow1, lakeshore, island, brink,iceroom, ogreroom,narrow2, pit,crystal, batscave,steam,deadend, ladder, maze,flames) ;

VAR location: rooms;

The concept of an enumerated type deserves a bit of discussion.

la Ww Ss

deadend

Fig. 7-1. The map of Adventure 1.

55

Fig. 7-2. The map of maze in Adventure 1.

56

You must bring the treasure to start in order to win.

To guarantee a win, you must carry the treasure to narrow1 drop it,

go around to the vestibule, and pull the treasure through the crack.

You can get to the vestibule from the iceroom, but you must carry the treasure through the ogreroom, and you could be eaten in the process.

You cannot carry the treasure up the ladder—it’s too heavy! You must find

another way out while still carrying the treasure.

You must find the treasure deep in the maze. Before the treasure will be visible to you, you must discover the message on the island.

Fig. 7-3. The problems of Adventure 1.

ENUMERATED TYPES IN PASCAL

Programmers sometimes have difficulty com- ing to grips with the concept and use of enumerated types. The reason for this is a psychological one. A large part of traditional programming, whether it be in assembly language or in so-called “high-level” languages such as FORTRAN or BASIC, has dealt with the invention of representations. Programmers have become used to this necessity. In Pascal, the concept of enumerated type provides a facility which removes the need to invent representations in certain situations. Suddenly the very program- ming language itself solves part of the traditional representational problem. The programmer is not

used to this. The programmer is fixated on solving all representational problems—indeed the pro- grammer has come to love this aspect of program- ming. When faced with a ready-made solution, the circuits become overloaded.

To make this more concrete, suppose for the moment that you were writing a miniadventure in BASIC. You might use a variable to keep track of which room or location the player happens to be in at any given point in the game. Now BASIC can on- ly manipulate two kinds of data—numbers and strings. Therefore, you must invent a correspon- dence between the set of rooms in your game and a set of numbers. The numbers will be used inter-

57

nally to represent the rooms. You might choose

start = 1 vestibule = 2 grandroom = 3

and so on.

On the other hand, it would be equally valid to use

start = 1001

vestibule = 1002

grandroom = 1003 and so on.

If the latter representation were chosen over the former, there is a likelihood that the choice would be based on some intended programming trickery. That is, part of the BASIC program might be written to “know” that numbers greater than 1000 represent rooms.

In general, unless you resort to coding tricks, the specific numbers chosen to represent the rooms are purely arbitrary. They only serve to distinguish one location from another and at some level, to identify which location is under scrutiny. The key points about these numbers are:

@ They are explicity chosen by the programmer.

@ They represent an external concept (rooms) in an internal form (numbers).

@ They must be correlated mentally by the pro- grammer; that is, the details of the representa- tion are a factor in the coding of the program.

Pascal’s enumerated type facility enables you to set aside the problem of representation in this case. The enumerated type given above effectively creates a new kind of data that Pascal can manipu- late, auser-defined type called rooms. The variable location may be thought of as a variable that can take on any roomas its value. The user may think of rooms in terms of their names as chosen in the type declaration. There is no need to choose a set of numbers to correlate to the names: the Pascal com- piler takes care of this for you. Since you really don’t care what the specific numbers are (unless

58

you had planned on some clever trickery), it is better to let the details be swept under the rug of compilation. When you write your program, you can think in terms of room names, not numbers!

Since long years of training have made most programmers feel that we should be personally re- sponsible for internal representations, it is difficult to make our minds let go of that feeling of responsi- bility. However, when we finally do let go, the freedom to think in terms of the problem instead of in terms of the computer is the result. Once you become accustomed to it, this can lead to an enor- mous feeling of power and relief.

If you read through Adventure 1, you will see that assignment statements such as:

location := vestibule;

location := flames;

are commonplace. How much more suggestive than:

location := 2; location := 17;

Of course, the enumerated type rooms only represents the collection of locations in the adven- ture. It does not include information about the con- nections between locations. Figure 7-1 includes all the connections. This version of the adventure will only accept commands that indicate a direction in which to travel. In particular, it only recognizes six different directions represented by the single let- ters n, s, e, w, u, and d.

From any given location only certain direc- tions lead to other locations. For example, from the Crystal room you may only proceed e, w, or n. There is no way to go u, d, or s. This information must be encoded in some fashion and interpreted by the adventure program in order for the game to be played. This has been accomplished by two techniques that I explain in the next chapter. In the process, I shall reveal how the general play of the game is controlled. Both the representation of the adventure map and the control of the game hinge on the use of the Pascal case statement.

Controlling the Play

In the last chapter, I discussed enumerated types in Pascal. The power of enumerated types is greatly enhanced by the use of the Pascal case statement. There are several examples of case statements in Adventure 1. They all are quite important in making the game work. I am devoting this chapter to a discussion of some of these statements.

CASE STATEMENTS IN PASCAL

The general form of a Pascal case statement may be indicated as follows:

case E of

I1: l2:

al; a2;

In this general description, there are a number of elements that require further explanation.

case expression E

E must be an expression that evaluates to one of the elements of what in Pascal is referred to as a scalar type. In particular, enumerated types are examples of scalar types. In the context of adven- ture games, E could be a variable representing a value of type rooms. Or, it could be a call to a function that returns a value from the type direc- tions= (n,e,s,w,u,d); More on this below.

The idea of the case statement is to allow a program to take different actions based on the par- ticular values of an enumerated type. The elements 11,12, ...,ln of the general case statement repre- sent the possible values of the enumerated type. Actually each li may be a list of elements from an enumerated type. The elements al,a2, ... ,an in the general description represent the Pascal code that is to be activated in response to the corre- sponding value or list of values li.

59

In Adventure 1 there are two major uses of the case statement. Both involve the flow of control through the game program.

CONTROLLING THE ADVENTURE GAME

If adventure were real, the player would either be in a location at a given time, or he would be traveling between two locations. In the act of traveling the player would leave one location for another by heading off in a specific direction, such as north, east, or down. Thus, in programming adventure these ideas must be represented in some way using Pascal code. Specifically, you must find ways to represent:

@ The adventurer’s location.

@ The possible directions out of each location and the destinations they represent.

@ The decision as to which direction to take.

m The changing of location from one room to another.

The Adventurer’s Location

I have already discussed the use of an enumer- ated type (which I called rooms) to represent the possible locations. To store a specific value of the rooms type, I invented a variable called location.

var

location: rooms;

At the beginning of the game, the location variable is assigned the value start. This represents the initial room or starting point of the adventurer. At various places in the adventure game code, as- signments will be made to this variable. Such as- signments will be dictated by the adventurer’s choice of direction.

To organize the game, a separate Pascal pro- cedure has been written for each value of the rooms type. These procedures have all been given names that consist of the letter p for procedure, followed by an identifier from the rooms type. For exam- ple, there are pstart, pflames, pgrandroom, pogreroom, and so on. Each of these procedures handles the activities that may take place in the

60

corresponding location The main program code consists largely of a case statement with case expression of type rooms:

case location of

start: pstart; grandroom: pgrandroom; vestible: pvestibule; narrow1: pnarrow1; lakeshore: plakeshore; island: pisland;

end;

The meaning of this statement is extremely simple: When the player is in room x, activate the procedure px, which handles activities in that room. At any time during the game, one of these procedures will be in execution. The execution of that procedure corresponds to the adventurer actu- ally being in that room in real life. The program’s structure very closely reflects the way we imagine a real life adventure would happen.

Look back at the main program listing in Chapter 6 and notice that the case statement is enclosed within a while statement. This is neces- sary in order to ensure that the case statement is repeated over and over again. Otherwise, the game would cease after pstart was executed once.

The use of a case statement nested inside a while statement is common in Pascal programming. Remember it—you will probably use it often during your Pascal programming career.

Changing Locations

Part of the activity in each room is deciding which way to go next. The adventure map shows that in a given location it is impossible to proceed in all possible directions. Only a subset of the possible directions represent actual paths in the imaginary adventure world. The code must reflect this fact. In Adventure 1, each location procedure contains another case statement that handles the travel from location to location. The case statement also en- codes the possible directions that may be taken. This is best explained by considering an example:

The following code is found in procedure plakeshore:

case whichway of n: location := island; s: location := narrow1; e: location := narrow2;

w,u,d: noway;

end;

The identifier whichway is the name of a function that determines the direction the adventurer wishes to take. This is described in more detail below. The identifier noway is the name of a proce- dure that simply prints the message:

“There is no way to go in that direction.”

on the game player’s display screen. The operation of the case statement is once again quite simple.

@ A direction is chosen by the adventurer. This is accomplished by a call to whichway.

wlf the direction chosen represents a possible avenue of travel, the new room is assigned to the variable location in the appropriate section of the case statement. This will cause a new loca- tion procedure to be invoked on the next cycle of the main case statement.

g If the direction chosen does not represent a pos- sible route in the adventure map, the noway procedure is called. The value of the variable location does not change. This means that the same location procedure will be invoked on the next cycle. The adventurer gets another chance to find a way out.

Deciding on a Direction

In this adventure, only very simple commands are used; only one of the six directions Nn, S, e, W, U, or d may be specified. The function whichway is responsible for this interaction with the player. It prompts by “asking”

Which way?===>

The player responds by keying in a “command.” This may be any string of characters. The Pascal code examines only the first character of the string:

command{1]

If this character is one of the letters n, S, e, W, U, or d, whichway sets its return value to the corre- sponding value from the enumerated type direction. For example, if the adventurer types in never, whichway will return n as the direction to try.

Protecting Against Empty Responses

There is one problem that the Pascal code in whichway must solve. If the adventurer simply pushes the RETURN key the variable command, which is used to store the response, will contain an empty string. The length of the empty string is, of course, 0. The character to be assigned to ch using command{1] is then nonexistent. If a statement in the program attempts to access this character, the Pascal run time system will complain with a cryptic message. The adventurer will be yanked from pleasant fantasy back to mundane computer reality. You must avoid this if at all possible, and it turns out to be quite simple to do so. The program simply checks the length of command and refuses to touch it until the length is greater than 0.

Changing Location

The act of changing location is represented by a simple assignment statement giving a new value to the variable location. If you now go back to the listing again, you will find that such assignments are scattered all over the case statements that begin with

case whichway of

These assignments collectively determine the map of the adventure. That is, the connections between different locations is implicit in these assignments.

This last point is worth a minute’s reflection. If you were writing adventure games in a language

61

like BASIC, you would probably encode the map as an array of numbers. The array would probably have two dimensions, with any pair of subscripts repre- senting a pair of locations. The contents of the array for each such pair would be yet another number. This number would be a code representing one of two things:

1. The direction to take to get to the room rep- resented by the second subscript from the room represented by the first subscript.

or

2. A number that means there is no connection between the pair of locations in question.

In terms of Adventure 1, you could imagine an

62

array MAP[18,18]. The rooms start, grandroom, vestibule, narrow1, and so on would be rep- resented by the numbers 1 to 18. Then the pair of subscripts 1,3 would represent start, vestibule; the pair of subscripts 4,17 would represent narrow1, flames; the pair of subscripts 5,9 would represent lakeshore; ogreroom; and the pair of subscripts 5,6 would represent lakeshore, island.

The directions n,s,e,w,u,d, and noway might be represented by the numbers 1,2,3,4,5,6, and—1. Then MAP[1,3] would equal 6; MAP[4,17] would equal—1; MAP[5,9] would equal—1; and MAP[5,6] would equal 1.

I could have taken an approach like this in Adventure 1. However, it was unnecessary to do so, because the Pascal implementation I have cho- sen was available and was so much easier to under- stand and work with.

Mazes in the Middle

Adventure 1 has a maze in it. The original adven- ture game had not one but two mazes in it. Many subsequent adventures, especially those of the un- derground or cave variety, also contain mazes.

The maze offers a good opportunity for intro- ducing more of the structural features of Pascal. The maze is conceptually like a single location. Yet in practice it contains many individual locations. While you are in the maze you are “trapped” in a miniature adventure within an adventure.

The entire adventure in Pascal is implemented by a program. The maze is implemented by a single procedure, pmaze. By analogy, because the maze is like a miniature adventure, pmaze must be like a miniature program. Indeed as you shall see, proce- dures can have almost all of the features that a Pascal program can have.

Although pmaze is a single procedure that controls play in the maze, it has its own local or nested procedures and a nested function. Local pro- cedures and functions are just like the procedures and functions that contain them, as far as Pascal syntax rules are concerned. However, they may

only be invoked from the procedure or function that contains them or from other functions and proce- dures that are also local in the same sense.

The pmaze procedure also contains a local TYPE and VAR section. The types and variables thus declared may only be used inside pmaze. They are not “visible” outside pmaze; pmaze is referred to as the scope of these declarations. The scope includes any nested procedures or functions that do not redeclare the types or variables in question.

The concept of scope is fundamental to Pascal and other languages like it, which are referred to as block structured languages. It is beyond the scope (pun intended) of this chapter to go into a full dis- cussion. For full detail on this subject, consult your authoritative Pascal language textbook. However, by imitating these program layouts you should not run afoul of the scope rules.

Local or nested declarations, including proce- dures and functions, are provided in the language to allow for better program structure. If a variable logically belongs only to a certain procedure, it is best to declare it there and there only. Then it

63

cannot interfere with other variables that have been declared in the outermost part of the program. A good example of this (in general) is a loop control variable for a counting loop. Such a variable should be a VAR declared locally within the procedure or function that uses it. In some implementations UCSD Pascal allows you to violate this guideline. The International Standards Organization (ISO) standard for Pascal does not.

Local procedures and functions are useful for breaking down the code of a large procedure or function. Good program design requires that each procedure perform a single logical purpose. The name of the procedure should suggest that purpose inasimple way. pmaze is a good example, although the name is somewhat cryptic. It would have been more understandable if I could have called it some- thing like handle__the__play__while__the __adventurer__is__in__the__maze, but UCSD Pascal limits the significance of an identifier to its first eight characters. So if I ever named another procedure with a long name beginning with handle__the play while __ the __ player _is__in_. .. , I would have a name conflict. In any case, the procedure pmaze handles a single pur- pose that is easy to state and to comprehend. Nevertheless, it is useful to be able to write proce- dures and functions that are subordinate to pmaze and that help pmaze carry out parts of its task. I shall discuss these subordinate functions and pro- cedures later.

LOCAL DECLARATIONS

The procedure pmaze declares an enumer- ated type called mazerooms. The identifiers in this type represent the individual maze locations. It is used in a fashion analogous to the rooms enumer- ated type in the main program. The maze rooms are not given individual names, nor are they given individual descriptions. Thus, I just call them m1, m2, m3, and so on.

There is a single local variable declared in pmaze—mazeloc. It is of the type mazerooms. Its value represents the current location of the player in the maze. The value is always one of the elements of the local enumerated type maze-

64

rooms. The variable mazeloc is entirely analogous to the global variable location.

I could have implemented the maze by simply extending the global type rooms to include the maze locations and using the variable location to keep track of them as well as of the other locations. In fact, this is what I do in the next adventure later on. For now, I have chosen the local approach to illustrate a point about Pascal. I shall let you decide which implementation of the maze you like better when you have seen both.

LOCAL PROCEDURES AND FUNCTIONS

The pmaze procedure has a large number of local procedures and a local function. The proce- dures fall into two classes—“location” procedures and “support” procedures. The support procedures carry out general tasks. They may be invoked from several places within the overall pmaze code, or as in the case of treasure, they may simply serve to make the program more understandable.

The support procedures are describe, same- place, and treasure. They perform functions that are suggested by their names. The describe proce- dure is used to print the general description dis- played when the player visits any of the maze loca- tions. The sameplace procedure is used when a move by the player results in landing in the same maze location as before the move was made. The treasure procedure handles the discovery of the treasure that is located in one of the maze rooms. In addition, describe tells the player in which direc- tions it is possible to continue from any given maze location. This makes it easier to map the maze.

The location procedures of pmaze are pm1 pm2, . . ..pm19. Each of these procedures corre- sponds to one maze location. They are analogous to the location procedures of the entire adventure (pmaze itself is one of these). However, pm1 through pm19 handle only the maze locations, which are sublocations within the maze. All of these procedures perform the following duties:

gw Print a description by calling describe. w@ Determine the direction of travel desired by the player, by calling whichway.

@ Determine the resulting maze location or adven- ture location after the player’s move is carried out. This is accomplished in each procedure by using a case statement as discussed in the previ- ous chapter.

The location procedure pm1 does a little ex- tra. In response to a player’s directions, it allows not only mazeloc but also location to change. This is the way the player gets out of the maze—by

returning to pm1 and then giving a direction that goes out of the maze.

The only local function in pmaze is bittest. It is an example of poor Pascal coding practice. It is included because I wrote it that way originally, and because it illustrates the kind of thinking habits that FORTRAN and assembly-language programmers fall into. It uses so-called “bit-fiddling” techniques that are better represented in Pascal using set vari- ables.

65

Other Techniques Used in Adventure 1

In this book, I make every attempt to implement the adventures using “general” code. That is I want procedures and functions to serve as many parts of the adventure as possible. I also want to write procedures and functions that may be used as pat- terns. Some, like whichway, can be used directly in other adventures. Others like the location proce- dures provide a skeleton or template. The same gen- eral approach may be used in other adventures to accomplish the same goals.

There are many times, however, when you just have to give up on generality. There are cases in which you need fairly elaborate code in order to implement some feature of the game, and that code cannot be used for anything but that single feature. There are examples of this kind of code in Adven- ture 1.

The location procedure pstart, pvestibule, pnarrow1, pisland, pogreroom, ppit, pladder, and pflames each contain one or more sections of spe- cial code. Let’s just go through them to get a flavor of the kind of things that are possible.

66

The pisland Procedure

This procedure provides the simplest possible example. The procedure pisland contains the statement

readmsg: = TRUE;

What this represents in the game is the fact that the adventurer has read the message to be found on the island. This is important later on—in the maze— because the treasure cannot be found until the mes- sage has been read. This is an extremely simple example of solving a problem. In this case, the adventurer solves the problem just by visiting the island. Later, when you add commands to your adventures, you might require the adventurer to explicitly command the guide to READ MESSAGE or something like that.

There is a general principle in this example, as well. The variable readmsg is of the Boolean type. This means that its value may either be true or false. Such variables are useful for representing

events in adventure games. More precisely, a Boolean variable may be used to record the occur- rence of an event. In this example, the event was the reading of the message. As long as readmsg has the value false, the event has not occured. When and if the value becomes true, the event has oc- curred.

The pflames Procedure

This procedure is similar to the pisland proce- dure. It sets two Boolean variables, namely, cooked and done to true. This effectively says that the adventurer has been cooked and that the ad- venture is over. Not much more to say, is there?

The pstart and ppit Procedures

Both of these procedures have slightly dif- ferent case statements than the other location pro- cedures in Adventure 1. In each, there is one direc- tion that causes a special message to be printed. This happens rather than either the location being changed or the noway message being printed. In each case, the difference is not important to the way the game is played or to the result. The only pur- pose for the extra messages is to add interest.

In ppit, ifthe player tries to go u, the message:

Try to climb that, and you'll kill yourself.

appears. This message is consistent with the ear- lier description in location brink. There, the player is warned that sliding down into the pit is possible, but climbing back out doesn’t seem possible. The ppit special message confirms this message.

The pvestibule, pnarrow1, pladder, and pstart Procedures

In each of these procedures, one or more Boolean variables is examined in order to detect conditions that require special action. In each case, further setting of Boolean variables may take place depending on what happens.

In pladder, the variable carrying is ex- amined. If carrying is true, it indicates that the adventurer has located the treasure and is carrying it. In this case, it is impossible for him to climb the

ladder, because the treasure is too heavy. Other- wise, going up the ladder is allowed.

In pnarrow1, the variable carrying is ex- amined again. If it is true here, the guide tells the adventurer that going south with the treasure is not feasible, because the crack in that direction is too narrow. The adventurer is then given a chance to drop the treasure. If the answer is yes, Carrying is set to false and the variable dropped is set to true. The latter variable is used in pvestibule later on.

In pvestibule, if dropped is true, the treasure is sitting abandoned in location narrow1. The guide tells the adventurer this and allows the treasure to be reached for through the crack. If the adventurer agrees to try to reach for it (can you imagine anyone who would not agree to do that?), carrying is set back to true and dropped is set back to false. This allows the adventurer to win the game by then returning to location start.

In pstart, the variable carrying is looked at. If it is true, the variable done is set to true, which means that the game is over. The fact that carrying is true when the game ends means that the adven- turer has succeeded in finding the treasure and getting it back to the starting location. In short, the adventurer has won.

The pogreroom Procedure

This location contains the most elaborate spe- cial case code of any location. In fact, there is a subsidiary procedure, ogreaction, which is in- voked to handle a lot of the action.

The procedures pogreroom and ogreaction deal with the adventurer’s interactions with the ogre. There are only bad experiences to be had in the ogreroom! The Boolean variables awake, and eaten control these. If awake is true, you risk being eaten by the ogre. Your chances are 1 to 5 if you are not carrying the treasure, but 1 in 2 if you are carrying it! If the ogre is not yet awake, each time you visit his lair, you stand a 1 in 7 chance of waking him.

If you should be silly enough to ask to go in direction d while you are in the ogreroom, you will also be eaten. This corresponds to walking directly into the ogre’s fire-pit.

67

Preview of Adventure 2

The second example of a Pascal adventure game is based on the first. The map has been extended considerably, and many additional capabilities have been added. This is the first “real” adventure game, because it allows the player to issue commands and control the play much more directly than the first game did. In many ways, Adventure 2 is the heart of the book. I shall continue to discuss it in one way or another up until Chapter 25.

MAPS, DIAGRAMS, AND CODE OUTLINES

Figures 11-1 through 11-9 present the code outlines, structure diagram, and maps of Adventure 2. Because Adventure 2 is an extension and modifi- cation of Adventure 1, you should compare Figs. 11-1 through 11-9 to Figs. 5-1 through 5-8. The map of Adventure 2 is similar to that of Adventure 1, with some extensions. Figure 11-7 shows the dif- ferences between the map of Adventure 1, which was presented in Figs. 7-1 and 7-2, and the map of Adventure 2. Figure 11-8 presents the map of the additions to Adventure 1.

68

CHAPTER PREVIEWS

Chapter 13 is called “Command Processing in Adventure 2” and discusses the use of enumerated types in representing commands. It shows how the cmdlookup function uses a linear search with a sentinel to determine which command the player has entered. In the process, it deals with arrays indexed by an enumerated type, the input of values of an enumerated type, the Pascal succ function, and other related topics.

Chapter 14 is called “Carry and Drop: Pascal Sets.” It deals with the use of the Pascal set types to represent collections of objects in the adventure game. In particular, it shows how the player’s “stash,” that is, the objects being carried at a given time, is represented by a Pascal set. It also shows how the collection of items at each location is rep- resented in a similar fashion. A comparison of the use of Pascal sets with the implementation of sets in ad hoc BASIC code presented. Finally, the use of the set operators in Pascal is discussed in the course of presenting the implementation of the

PROGRAM miniadventure;

CONST, TYPE, and VAR declarations:

This section of the program contains the declarations of the constants, types, and variables used by the rest of the program.

See Figure 11-2

procedures and functions

This section contains the headers and code for the procedures and functions

accessible to the main program block and the outermost level of the program.

See Figures 11-3 and 11-4

The main program block of Adventure 2.

See Figure 11-5

carry and drop commands in the adventure game.

Chapter 16 is entitled “Problems in Adventure 2.” Problems, as I have emphasized, are the soul of adventure games. The techniques for representing problems and their solutions in Pascal code are therefore paramount. This chapter deals with the use of Boolean variables and Boolean expressions in the problem-solving process. It discusses the particular problems posed by Adventure 2 and both their external face, the way the player views them,

Fig. 11-1. The code outline for Adventure 2: the program outline.

and their internal representation, the way they are implemented.

The Pascal set membership operator, IN, and its role in Boolean expressions is discussed. Other elements of Boolean expressions are explained in- cluding numeric relationships, and the AND, OR, and NOT operators.

Chapter 16 deals with the ad hoc techniques of Adventure 2. It is called “Other Techniques Used in Adventure 2.” The Pascal coding solutions for

69

rooms directions cmds

} As in Adventure 1

represents the commands the user may issue in

Adventure 2 apart from directions objects

represents the objects found in Adventure 2 collection

represents any subset of objects placerec

a record type describing entries in the index

part of the descriptions database

xfile narrate file variables corresponding to the descriptions database index and text data files, respectively places: ARRAY [rooms] of placerec; an array to hold a copy of the descriptions database index in memory whatshere: ARRAY [rooms] of collection; an array corresponding to the set of objects present at each location—modified by the carry and drop commands. stash represents the objects carried by the player cmdnames, objnames

Fig. 11-2. The code outline for Adventure 2: important data declarations.

arrays of strings used to match command names

and object names typed by the player hasdug counts the number of times the player has dug done, quit, carrying, dropped, chgloc, etc. variables to track the occurrence of various events during game play

counting the number of turns the player has taken, displaying the contents of a set, scoring the game, dealing with the lamp, and implementing the eat and dig commands are explained. The handling of the ogre character in the game is discussed. The

70

simplification of the process of changing locations is explained, in particular, the function of the new procedure travel.

Adventure 2 uses files for some of its informa- tion. This is hinted at in Chapter 16. However, the

information in files is a complete data base of de- detail in the next section of the book starting with

scriptions of all the locations in the adventure Chapter 18.

game. How this database is created and all the Chapter 17 is a collection of helpful hints re- techniques associated with it are dealt within great garding the use of the UCSD Pascal system. It is

PROCEDURE introduction; PROCEDURE initialize;

} As in Adventure 1

PROCEDURE showobjects; Tells the player what objects are present, if any, at the location currently being visited. PROCEDURE show; Prints the full description of the location just entered.

FUNCTION score: INTEGER; } As in Adventure 1 FUNCTION objlookup: objects;

Determines which object name, if any, a user-typed string matches; returns the internal value of said object.

FUNCTION ckobject (it: objects) : BOOLEAN; Determines if a given object is present in the current location; returns true if it is; false, otherwise. PROCEDURE pcarry; PROCEDURE pdrop; PROCEDURE phelp; PROCEDURE plight; Procedures used to PROCEDURE pinventory support the execution PROCEDURE ppush; of specific commands PROCEDURE pdig; in Adventure 2 PROCEDURE popen; PROCEDURE plook; PROCEDURE peat;

FUNCTION cmdlookup:cmds; Similar to objlookup, but determines commands rather than objects.

PROCEDURE listen;

FUNCTION docommand:CHAR;

FUNCTION whichway: directions;

PROCEDURE noway;

PROCEDURE travel (nloc,sloc,eloc,wloc,uloc,dloc:rooins);

Procedures and functions for general command handling and changing player's location from one “room” to another.

PROCEDURE cklamp; Monitors the lamp; warns player about staying in the dark, and so on.

Fig. 11-3. Code outline for Adventure 2: the procedures and functions: Part |.

71

PROCEDURE oreaction; PROCEDURE pstart; PROCEDURE pvestibule; PROCEDURE pnarrow1; PROCEDURE pisland; PROCEDURE pogreroom; PROCEDURE ppit; PROCEDURE pladder; PROCEDURE pflames;

Location procedures

as in Adventure 1. Because travel is itself a

procedure in Adventure 2, many locations no longer need a location procedure. (See Figure 11-5)

PROCEDURE pmaze; Location procedure for the maze. PROCEDURE describe; Local procedures as in PROCEDURE sameplace; Adventure 1. PROCEDURE travel (nloc,sloc,eloc,wloc,uloc,dloc:rooms);

Local proceure—handles travel within the maze.

PROCEDURE pm1; Local procedure—handles travel within the maze.

BEGIN

REPEAT CASE location OF maze,m1: pm1; m2: travel (m1,same,m0,m0,m0,m0);

m19: travel (m0,m18,m0,m0,m15,m0); END; UNTIL (location<maze) or (location-flames);

END(+ PROCEDURE pmaze *);

Function block for pmaze handles travel inside the maze. AREPEAT statement invokes the local travel procedure until the player gets out of the maze.

Fig. 11-4. The code outline for Adventure 2: the procedures and functions: Part Il.

entitled “UCSD Pascal Development i1echniques.” your files, entering them into the system using the There are discussions of how to manage your files UCSD Editor, splitting a large program into multi- effectively and UCSD tricks and pitfalls. Organizing ple files, and using the include option in the com-

72

BEGIN

introduction; initialize;

} As in Adventure 1

REPEAT visited [location] := TRUE; show (location); cklamp;

Perform general processing associated with changing locations.

CASE location OF

start: pstart;

grandroom: travel (nowhere, nowhere, nowhere, brink, nowhere, stairs);

vestibule: pvestibule;

narrow1: pnarrow1;

lakeshore: travel (island, narrow1, narrow2, nowhere, nowhere, nowhere);

flames:pflames; END; UNTIL quit OR done;

Main control loop of Adventure 2. If a visit is made to a location for which no special handling is required,

the travel procedure is invoked to process commands

and move to a new location. Other locations, for

example maze and narrow! still require location procedures. These location procedures handle special conditions

as well as invoke travel.

congratulations; } As in Adventure 1

Fig. 11-5. The code outline for Adventure 2: the main program block.

piler are all touched on in the discussion. Acouple the UCSD system. Finally, a general discussion of of filer tricks involving the prefix command and the _ pitfalls in software development under the UCSD K(runch command are introduced—these trickscan system is presented.

save you time and trouble in your day to day use of

73

congratulations introduction

cmdlookup

objlookup ckobject

Fig. 11-6. Structure diagram for Adventure 2.

74

lakeshore

deadend

Sara’

Fig. 11-7. Map of Adventure 2: Page 1.

75

2 = fe} ° > ® c fe}

£

Fig. 11-8. Map of Adventure 2: Page 2.

76

Fig. 11-9. Problems of Adventure 2.

You must carry the lamp and light it in order not to fall into a pit. You must bring the treasure to start in order to win the game.

The treasure in Adventure 2 is locked in achest. You must find and carrv the key—otherwise, youcannot open the chest when you dig it up.

You must visit the island and read the message before you can dig up the treasure.

The treasure in Adventure 2 is buried. In order to be able to dig for it, you must locate and carry the shovel. When you dig for the treasure, you must dig three times to fully reveal the chest.

You must drop the treasure at narrow1 and push it through the crack leading to the vestible. There is no other way to get it out.

You cannot carry the treasure up the ladder—it is too heavy. You must discover the trick of Problem 5.

77

Adventure 2’s description database file can be found in Appendix B, page 286.

PROGRAM CONST

f e TYPE

roams

Pascal Adventure 2

miniadventures

(start, grandroam,

lakeshore, ogreroam, hatscave, Maze, stai

incline, honeycomb, mudroam, deeppool, rockyroam,

vestibule, narrowl,

island, brink, narrow2, pit, crystal,

Steams

rs, echoes,

narroaow4, river,

alcove, #1

AMES «

Mo, Mo, m7, MB, mis, milo, mi?, same, moawhere®)

mis, mi4,

directions = (Ny

78

Sy @,

deadend,

mo, mid,

Wey Uy Gd):

iceraam,

ladder,

WAIT TH CCIM 9 reundroam, coldroom, narrows,

siltroam,

mo, ml, me, m3, m4. mii, mis,

19, m19,

cmds = (carry, drop, help, light, invent, take, tally, push, dig, look, apen, unlock, eat, moacmd) s

objects = (lamp, treasure, keys sandwich, bottle, shovel, nook j)s

collectian = GET OF abjects;

pname = §STRINGL40];

storyline = STRINGCS8OI;

byte = O.,.2553

whichsect = (indexsectian, descsection);

“whichsect” is changed to “whichsection” here; this doesn’t matter to Apple Pascal, but may cause problems with other versions of Pascal

placerec

RECORD

CASE section Jwhichsection | OF

indexsections ( tableentry: INTEGER) ¢

deecsectian: { names pnames id: INTEGER; dbegins INTEGERS dends INTEGER s links byte 3 END s VAR

xfiles FILE OF placerecs;

narrates FILE OF storylines

places: ARRAYCraoms] OF placerecs

whatshere: ARRAYC rooms] OF collections

visited: ARRAY Draams] QF BOOLEAN;

stash: collectian;

cammands: STRING;

79

80

{ holds user typed directian

head, tails

{ hald SEFARATE words OF command

cmdnamess ob jJmamess che

dcharss

{ characters which correspond TO the acceptable initial directiean

C*n*,

lacatioan: ogreloc:

speciall:s nexts turns:

is

indarks hasdug

dane: quit: lits eaten: awake: readmsqs carrying:

STRING;

ARRAY Cemds] OF STRING;

% 4

%,

a

ARRAY Cob jects] OF STRING;

CHAR,

SET OF CHAR;

commands.

"ery Tw, TU, rooms s

rooms:

SET OF roams;

directions;

INTEGER; INTEGER; INTEGER; INTEGER:

BOOLEAN; ROOLEAN; BOOLEAN; BOOLEAN; BOOLEAN: BOOLEAN; BOOLEAN:

dropped: BOOLEAN: trapped: BOOLEAN s coakeds EQOLEAN s candig: BOOLEAN § chagloc: ROOLEAN:

CORO OK OK KF

t p @e }

OOK KOKO KK KD:

PROCEDURE wi pes

BEGIN

letters OF initialized

7q°J

TO

“a

write (chr (ff) s END §

{(SiaZ.ud,text?

($iaze,~us. text}

($ial.me2. text?

{fiad,.maze. text}

(Fia2.main,. text}

{ source FILE: aZ.ul.text 3 new file named “a2.u1.text” CORO OK OK ROKK CKO KOK KK ROK KKK 4K XK 3:

{ i nt roo dou i « ¢t i Oo on +

OOK OOK OKO OKO OK KKK KKK KOK OK KKK K 3:

PROCEDURE introductians REGIN

£

Wipes t clear screen

wreitelnmy

wreiteln ("Welcome to miniadventure!")3;

wreiteln ("Your goal will be to find a treasure’); writeln (Cand bring it back ta your starting"); writeln (*point. You will also get points’); writeln ("for finding each lacatian in the*); writeln (adventure. Foints will be deducted’); wreiteln (far various undesirable happenings: *); writeln (waking the agre, getting eaten,*); writeln (“getting toasted, etc.*);

wreiteln writeln (*I will guide you and be your eyes")+s writlen (and ears. Command me with one oar’); writeln ("two word phrases, such as");

writeln (" "CARRY KEY" or "“NORTH". 7) 3

writeln ("I anly look at the first letter’); writeln (’of commands that tell me which"); wreiteln (direction to take. Thus, i take’): writeiln (? "NORTH" and "N" toa be the same.*) 3 writelns

writeln (* When you are ready ta begin your’); weiteln (adventure, just press RETURN");

readin (command)

wipe;

rd =2

END ¢€ PROCEDURE introduction

81

JOO OKOROKROK ROKK KKK OK KOK KOK KOK KK KKK KF ¢ i rn i t i a 1 i z =] 3 OOO OOOO ORK GOK KOK KKK KOK KKK}

PROCEDURE initialize;

VAI locs raoams;

BEGIN location := start; dchars Pe is es a er he ro dane := false; quit := false; cooked r= false; eaten r= false; lit := false; awake := falses readmsq = false; carrying := false; trapped := false; dropped := false; candig := false; chgloc r= trues turns = OF indark f= OF hasduq r= O%

FOR loc := start TO nowhere DO RBREGIN

visitedClac] := fa whatsherelloc] :=

stash s= CI;

Csandwichdis; Chottleds

whatsherelCdeadend] whatsherelmudroaamd

whatsherelstart] = Clampd; whatsherelCcoldraom] = Eshovel 1; whatsherelCnarraw4] = Chey;

cmdnamesCcarry] : cemdnamesCdropd r= "drop? cmdnameslhelp 4 H cmdnameslClight] g

82

cmdnameslinvent] = “inventory’s;

cmdnamesCtaked *take"s cmdnames tally = "scare’ 3 cmdnamestCpush J = "push? 4 cmdnamesCdiqi = "dig"; codnamesllaak J = *jJoaak’*

a = "apen” sy = "unlock 3 = “@at" y = "sentinel";

cmdnamesCopend cmdnameslCunlockd cmdnamesCeat J cmdnamesCnacmdd

aeons ms oes ts 23 8s me ose BS

ob jnamesllampd = “lamp* s

eb jnameslCtreasurel:= “treasure’;y objnamest€ shovel] := *shovel*; ob jnameslkey r= “khey"s

ob jnamest(sandwichi:= “sandwich?’ 5 abjnamesCbottle] := “hoattle’; ob jnameslnoobj)] := “sentinel’ ;

reset (xfile, *“a2.db80.x*)

“a2.db80.x” and “a2.db80” are the database files used reset (narrate, *a2,db8o*)

by Adventure 2 for descriptions.

loc := start; Their location on disk is hard-coded into the game, and

seek (xfile, 31); Pascal will only look there. As written, Pascal expects

get (xfile); these files to be on the disk in Drive 1. See “A note

placesfClocd] := xfile ; about disk Drive numbers in file names and commands” on page 3 of this PDF for more information.

REPEAT

loc := suce (lac); get (xfiled; placeslCloc] := xfile™;

UNTIL, loc = flames; close (xfile); END ¢ PROCEDURE initialize 3};

(OOO OOO OIOOIICIOIO CIO GOK KOK 3

{ 5s h oF w Oo 6b ji ec t $8 3 COORG OK K KKK 3

PROCEDURE showobjectss;

VAR lobj: objectss

83

BEGIN

FOR labj := lamp TO noabj ba IF lobj IN whatsherellocationd THEN REGIN

write ("There is a *)3 write (objnamesClabjd);

writeln (* here." )5

END ¢{¢ IF lobj IN .. 3 { aenddo 3;

END ¢ FROCEDURE showoabjects 233

COOK OOOO KRACK KOK KK KKK 5 £ c=-9 h O w }

CeCe eeeeese se cesses sce Ses seeles 222255 2 2 2 3:

FROCEDURE show(where: rooms) & VAR i: INTEGER: BEGIN IF (chgloc AND lit) OR (location = start) THEN BEGIN

WITH places{where] DO REGIN

FOR i s= dbegin TO dend bO BEGIN

seek (narrate,1); get (narrate) ; write (narrate™) | END ¢ DO 353 END WITH places 3; END ¢ IF chglac +f;

84

showob jects; END ¢€ PROCEDURE show 3

CKKK KKK KKK KKK KKK KKK KKK KKK KKK KKK KKK KKK { S Cc Q r e } CK RK KKK KKK KKK KKK KKK KKK KKK KKK KKK KKK KK KG

FUNCTION score: INTEGER; VAR

locs roams;

SCs INTEGER s BEGIN

Sa s= OF

FOR loc :* start TO flames bdQO IF visitedClaci] THEN

Sc. 8 sa + &

{ endif +}

{ andda 4

TF NOT quit

THEN sc = sc + 1405

IF caoked

THEN

SC #= sc = nil) § IF eaten THEN

Sc 8= sc - nO g

IF awake THEN ae

cor= sc ~ Bis SCOre r= scx END FUNCTION score ?}3

COCO OOOO OOK OK OKI OK OK 3

{ congratudlatians 3 OOOO OOO OOK HOOK OK IOKOK OK 7}:

85

FROCEDURE cangratulatians; BEGIN

IF NOT cooked THEN BEGIN

IF NOT quit THEN BEGIN

writeln KKKKK Congratulations *xXKKK"); writelns

write (*You got the treasure aut in anly *)3 writeln (turns:4,* turns." )¢§

END; writeln (* You scored *, score:4)3;

writeln points out of a maximum of FOO pts.*)3 writeln ¢(*So0 long for now, came again saon!*)¢;

END ELSE writeln (Sorry about that ~ try again soan!*)

{ endif 33

readin (cammand) ¢ Wipes

END ¢{ FROCEDURE cangratulatioans 33

COOK OKOOKRKOKKOK OK KOKOK KOK KKK} { ao b ji dl o oo k up 3 CROOK GOO COKOK KK XK KKK}

FUNCTION oabjlookup: objects VAF

lobj: objects: REGIN

abjnameslnoeobj] := tail; labj := lamp;

86

WHILE tail <2 abjnamesllobj] pa REGIN

{ Old debug statements ~ leave in for historical edification.

write (tail);

write (oe):

writeln (objnamesllobj]);

+

lobj := succ(lobj)s;

ab jloakup := lobjs END;

COCO OOOO OK OOK OK OK 2: 5

t c k oO Fb j @ t 3 OC OOKIORO OOOO OK Kok?

FUNCTION ckobject (it:objects): BOOLEAN; BEGIN

ckobject := false;

IF it IN whatsherellocation]) THEN

ckobject := true tC endif 34

END ¢ FUNCTION ckhobject 33

{ source FILE: a@w.us.text 3 neat Gleam So tent COO OKO ORK KOCK KOK OK KOK KK 3 { p C a r er y } COCO OK OK KOK ROOK ROK OK CK KOK KOK >:

FROCEDURE pearry; VAR its ab jectss BEGIN it s= abjlockups; IF NOT ckobject (it)

87

THEN BEGIN

write (*'I don*’*t see any write (tail); writeln (¢(* here.’ )¢s

END ELSE BEGIN

writeln (7 Qk"*)s;

stash := stash + Cit;

whatsherelClocation] := whatshereClocationgy

END ¢€ IF NOT it IN «wan 35

END ¢{ PROCEDURE pearry 33

")s

Citd

COO OO OK OKKK KK OK KKK XK}

{

d r Q

FROCEDURE pdrop; VAR

its ob jects;

BEGIN

88

it = objlookups;

IF NOT (it IN stash) THEN BEGIN

write (*You are not carrying any

wreitelm (tail)ds

END ELSE BEGIN

writeln ("Ok") 3s

stash := stash ~ Citds

whatsherelClacatian] := whatsherelLlocations

So

Pp ) OOOO OOOO OOOO OOIOICIOIGIGOO GIGI?

citds

as 2

*)

END IF NOT it IN stash 3; END ¢ PROCEDURE drop 33

COCO OOO GOK OKO OK KOK KK D { p h e 1 p } StSCCCCCeCesess Se Ce ses Less ss FF 2 2 5355 8;

FROCEDURE phelp; BEGIN

IF readmsg THEN BEGIN

writeln (*The treasure is deep in the maze’);

END ELSE BEGIN

writeln (* There are hints to be found’);

writeln (*near the lake and in the alcove.’);

END { IF readmsg 33 END ¢ FROCEDURE phelp ?}3;

COO OOOO OOOO GOK TOK OK KKK > { p 1 i g h t 3 OOOO IIOIOR OIG OOO OK KOK AOK >

PROCEDURE plight; VAR

cmd: STRING; BEGIN

IF NOT (lamp IN stash) THEN writeln (You are not carrying the lamp.*) ELSE BEGIN

IF (tail *on*) OR (tail = “lamp’*)

89

THEN lit s= true ELSE lit := false { endif ts END ¢ IF NOT lamp IN stash 33 IF lit THEN writeln ("Your lamp is now on.”*) ELSE

writeln (* Your lamp is of f.7)¢s

$ Ps

{ endif 33

END { FROCEDURE plight 33

colle

CK RK KKK KK KKK KKK KKK KKK KKK KK KKK KKK KKK KS ¢ Poionv @ nm ta riey +

ORR RR KKK ORK KKK KKK KK KKK KKK KKK KK KG

FROCEDURE pinventorys VAR

lobj: objects; BEGIN

IF stash <2 C7 THEN BEGIN

writeln (* You are currently holdings

FOR lobj := lamp TO noobj DO BEGIN

IF lobj IN stash THEN

writeln (abjnamesllobj]) { endif }

END ¢ FOR lobj := END IF stash ¢3 CJ

END FROCEDURE pinventory 33

{

CK KKK KKK KKK KK KKK KKK KKK KKK KKK KK KKK KK KD

90

COO OOKKAOK KK KKK KK KK KKK KKK KS

ut $ h 3

-

PROCEDURE ppushs VAR

newtails STRING; ps INTEGERS: REGIN

p := pos (* *, taild; IF p = © THEN

newtail := ELSE BEGIN

newtail := copy(tail,ptl,length(tail)d-p)j tail := copy(tail,1,p~1);

END ¢ IF pO 35

IF (tail = “treasure’*) THEN BEGIN IF (treasure IN whatsherelCnarrowl)) AND (location = narroawl) THEN BEGIN

writeln (7 Ok");

whatsherelCvestibule] := whatsherel[vestibule] + Ctreasurel;

whatsherelCnarrow1 ] := whatshereCnarrowl] ~ Ctreasureds

END ¢ IF treasure ws. 35 END ELSE BEGIN

writeln (*Sorry, but I don**t think’); writeln (*?I can do that.")3;

END { IF tail = *treasure® 33

END ¢ PROCEDURE ppush 335

91

OOO KOK OK RR KOK KKK KKK KK KK KKK KKK KKK KG t p d i g 3 CORO ROKK KKK KK KKK KKK KKK KKK KKK}

PROCEDURE pdig; BEGIN

candiq := (shovel IN stash)

AND Creadmsaq) s

IF (lacatian = mi) AND candig

THEN

hasdug := hasdug + i

{ endif 33

IF (leacation <3 m19)

THEN

writeln ("You can**t dig here at all.’)

ELSE

IF hasdug = 0 THEN BEGIN

writeln ("You can’*t dig here yet.")s;

END

ELSE IF hasdug = 1 THEN BEGIN

writeln ("*That**’s a nice pile af dirt you"); writeln (*have shovelled. Re careful"); writeln ("not te block your way out! *")3

END ELSE IF hasdug = 2 THEN REGIN

92

writeln (7?I see the top of a weather--*); writeln ("beaten chest.” )s

END

ELSE IF hasdug = 3 THEN BEGIN

writeln (* You have unearthed an old"); writeln ("treasure chest. Tt is’); writeln (*secured with a massive"); writeln ("brass lock.")s

END

ELSE IF hasdug = 4 THEN REGIN

writeln (* There’ *s nothing else here.’

writeln (but if you try hard, you"); writeln ("might diq dawn ta the*); wreiteln Clava pits!")43

END ELSE

IF hasdug = 4 THEN BEGIN

cooked s= trues dane f= trues show (flames) ; lacation s= flames; chgloc f= trues

END ¢ IF hasdug = 4 3 { END IF hasdug = 3 3 { END IF hasdug = 3 3} { END IF hasdug = { END IF hasdug = 1

> 2 ar) 4, 2

)

93

{ END IF hasdug = O 3 { END IF (location #2 m1i9 3;

ny

END {< FROCEDURE pdig 3}; COOK IOK GOK KK KKK ROKK KK KKK AK KKK KD

{ Pp oO p ein 3 COROROK KOK K KKK KKK AK KKK KKK KKK KR KKK KKK KD

PROCEDURE popens

BEGIN

IF (location = m19) AND (hasdug += 3) AND (key IN stash)

THEN

BEGIN

writeln (*Ok*)s whatshere(€m19] := whatshere(€m19] + Ctreasured; writeln (*The chest is full of treasure’);

END ¢{ IF 3;

IF (lacatian = mi?) AND (hasduqg == 3) AND ( NOT (key IN stash)) THEN BEGIN Cl*'m afraid you’"11 need a key*)s

writeln (*ta open that brass lack!")43

wreiteln END {€ IF 33 IF location «<2: mil?

THEN

wreitelnm ( There’ *s nothing here toa apen!*)

{ endif 3; END { FROCEDURE popen 34

94

COICO KOK KK RK OK OK KOK KKK} ¢ Pp tla ai ik } CORR ROK ROKR KKK KOK KK XOKK KKK KK KKK KKK}

FROCEDURE plaoks VAR

savcehaqs BOOLEAN; BEGIN

gsavchg : chqgloacs chqaloc := trues;

IF location «<= flames THEN

show (locatian) ELSE

END ¢ FROCEDURE plook 3};

CORO KKK RK KKK KR KKK KK KKK KKK KD

{ p e a t 3 CROOK KK KK KKK KK KKK KKK KK KKK KK KD

FROCEDURE peat: BEGIN

IF sandwich IN stash

THEN BEGIN

writelnm (Oh, yummy! !*") 3 Stash := stash ~ ECsandwichds

END

ELSE IF tail = “sandwich’ THEN

writeln (* Yau don*"t have a sandwich’)

ELSE

writeln (* Don’ *t be ridiculous! !*)

+

{ endif 3

95

{ endif 3 END ¢ PROCEDURE peat 33

CORO OKO KK HO KKK OK 3:

¢ c m gd lof ag k ww p 3 COCO ROOK KK KOK KOKORO KKK KKK KK KG

FUNCTION cmdloakup: cmds; VAR

p: INTEGER;

lemd : cmdss BEGIN

wreitelms

write (7 === 2");

readin (command) ;

p := pos (* *, command);

IF p=0 THEN BEGIN

= copy (command, 1, pl); tail :=

copy (command, pti, length (caommand)~p)s

END ¢ IF p=0 3};

cmdnamesCnocmd] := head; lemd := carry:

WHILE head <> cmdnameslClemd] DO lemd s= suce (Clemd) { END DO }3

cmdlookup := lems; END ¢€¢ FUNCTION cmdlookup 33 COOK COIR CCK ROKOKOK OK KKK KK KD { 1 i $s t ein +

COO O OOOO OK OK OK ROK KKK OK OK KK KD:

96

PROCEDURE listen; VAR

lemd: cmdsy; BEGIN

REFEAT

lemd := cmdlookups CASE lemd OF

carrey: pearrys

drap:s pPdraps

helps phelps;

light: plight; add a space between x ares pinventorys comma and apostrophe takes pearrys

tally: BEGIN J

write ("*Ghould you quit mow," )3 writeln ("your score would be *)5 wreiteln ((score 140) :3)3

writeln (* points of a possible 300,’

END; pushes ppushs; dig: pdiqs; loaks ploaks opens popens; unlocks popen; eats peat; nocmds $

END: UNTIL lemd = nocmd; END ¢ PROCEDURE listen 33 COOK OOOO OO OOK OOK OK OK x 3

{ do «c¢ o m m aon ad } COO ROOOK OKRA KK KKK K KKK KKK KKK KKK KK KD

FUNCTION docommand: CHAR; BEGIN

head s= "75 tail r= 7" 5 REFEAT listen UNTIL length (head) = Of;

docommand := headCilds END ¢ FROCEDURE docommand 133;

COOK OK KKK OK OK KOK KK}

,

{ w Hh i Hh w aiey 3 COR ORK KOK KK KKK KKK KK KKK KKK}

FUNCTION whichway:directions; BEGIN

turns = turns + 1; REFEAT

ch := docommand;

CASE ch OF *n's whichway := m3 *@?s whichway := s3 "e's whichway := eF 7wis whichway : = wy 7? is whichway s= us "d’"s whichway s= ds ?q's quit s= trues

END;

UNTIL ch IN dchars; writelns; end ¢ function whichway 3; CK KKK KK KKK KK RK KKK KKK KK KKK KKK KKK

{ n Oo w a y } CORR KKK KKK RK KKK KKK KKK KKK KKK KKK KK)

98

add space betwen procedure noways go and apostrophe

begin | writelns;

write (*There is no way to go"); writeln (’in that direction.’)s

chglac := false; END;

COO OO OK OK KKK OK OK K

t Cc kk 1 a m Pp } COO OOOO OK OK KOK OK ORCK OK KOK 3

PROCEDURE cklamps BEGIN

IF (location «<> start) AND NOT lit THEN indark 3: { endif 35

IF indark = 4

THEN

RBREGIN quit s= trues cooked = trues dane r= trues

writeln ¢(* You fell into a pit and were’); writeln ("*killed. Too bad!! Maybe next’):

writeln (time you"’11 listen to me*); writeln (‘and keep your lamp on!");

END ELSE

IF (indark 3} ©) AND (NOT lit) AND (lacation “+ start)

99

THEN REGIN

writeln ("It is now pitch dark.") 3 writeln ¢(* I wouldn’**t go too far.*); writeln (* You might fall into a pit AND writeln ("killed.")s;

END ¢ IF indark + © 33 { END IF indark + 4 3;

IF (turns += 75) AND (turns < 80) THEN BEGIN

be"

writeln (7? I would think about wrapping this");

writeln ("up, since your lamp is getting dim.

END

ELSE IF turns = 150 THEN REGIN

wreiteln (You’?re in trauble mow! *)5 weiteln (Your lamp just went aut! *)¢3 lit = false;

END ¢{ IF turns = 150 3 { END IF turns = 7S wae FY

END { FROCEDURE cklamp 3;

OOOO KKK OK OK ROOK KKK KK >

£ t r a Vv e il 3 COOOOROROKKOOK KK KOK K KK KK KKK KKK KK KK KD

PROCEDURE travel ¢ nloc, sloc,

eloc, wloc,

100

2

7)

uloc, dloc: rooms) ;

PROCEDURE newloc(loc:roams) REGIN

IF loc=nowhere THEN

chgloc END ¢ IF 33

END { FROCEDURE newloc }3 BEGIN {kx travel xx}

CASE whichway OF ms newloc (nlac); gs: newloc (sloac)s @: newloc (eloc); we newloc (wloac); us newloc (ulac)s ds: newloc (dloc);

END ¢ CASE whichway 33

END FROCEDURE travel 33

{ source FILE: a@.m2.text 3 COOK OKOK KKK KKK KK KOK KK KG if oO gr @ a c t i oO nn 3 COKOOKOKKROK OK KKK KOK KK KK KKK KKK KKK KKK KG

new file named “a2.m2.text”

PROCEDURE ogreaction; REGIN

IF NOT awake

THEN

BEGIN writeln ("This is the ogre’*s lair!"); writeln (If you are not careful, you’? 11"); writeln (*wake him.*)3;

IF (turns MOD 3)=0

101

THEN BEGIN

awake := trues

writeln ("Now you" "ve dane it!")s5

writeln (*You woke the ogre - better’)s; weiteln ("get out of here while you can’);

writeln (* You wouldn’*t listen ta me would’); writeln ("you? You really better get out’); writeln (of here before you get eaten! *),

IF treasure IN stash THEN IF (turns MOD 2)=0 THEN BEGIN

writeln (*Too bad!! The ogre caught you"); writeln ("and roasted you for dinner.’): wreiteln ("Better luck next time!!")s3

eaten = trues quit o:= trues END ELSE BEGIN

writeln ("Get out fast if you don*."t want’); writeln (to be a big-mac far the ogre! !")45

‘Big Mac’ if we’re getting technical...

END ELSE IF (turns MOD 2)=0 THEN REGIN writeln ("Too bad ~- you" "ve been eaten! ");

102

eaten 2 trues quit f= trues END

Q %.

{ endif 3} END ¢ IF NOT awake 33

END ¢ FROCEDURE ogreaction 33

COCO OOOO KIO KOK KOK KOK K 7:

t p S t. a r t >

OOOO OKO KKK KOK OK 3:

FROCEDURE pstart;

REGIN IF treasure IN stash THEN done : = true ELSE REGIN

CASE whichway OF

Ms Bp @aWe noways

us writeln ("You can**t jump to the clouds!*);

d: location := vestibule;

END {€ IF carrying f3

chgloc := (location «<> start); END ¢ PROCEDURE pstart 343 COOK GOK OK OK KOK 3 if p v @ Ss t i bh u | @ »

ORO ROKK OK OK OKKOKOK ROK KOK KOK KK KK KD

PROCEDURE pvestibule;s BEGIN

103

IF treasure IN whatsherel€narrowl] addspace between THEN narrow and apostrophe BEGIN

write (’To the north, through a narrow’); writeln ("crack,*)3 writeln ("you can see the treasure. ")3

END { IF treasure ... 33

travel (narrowl, grandroom,iceroom, nowhere, start,nowhere) ;

END {¢€ FROCEDURE pvestibule 3;

COOK KOK ROKK KK KOK KK KK KKK t Pp na rr aiew i il 3?

COCO OKO KOKORO OK KOK K 3}

PROCEDURE pnarrowl: BEGIN

CASE whichway OF = lakeshore;

ogreroom;:

*?s too narrow to get through! ")s;

ne location : es location =: Ss writeln (?*

It wWyltads noways

END ¢ CASE whichway 73;

chgloc := (location “<> narrowl)s

END { PROCEDURE pnarrowl 33

COO OOK OOOO KOK KK} { p i 5s lt aooni qd 3 OOOO OOK OKO KK KK KK KKK

PROCEDURE pislands REGIN

travel (nowhere, lakeshore, nowhere, nowhere, nowhere, nowhere) 4

readmsqg := trues

104

END { FROCEDURE pisland ty3

OCR KOO ROOK OKO HOOK KOK 7 t Q g r e r Q Q m } COOK KOO OK GK KKK OK KKK 3:

PROCEDURE pogreroaaoms VAR

re INTEGER: REG IN

agreactions IF NOT eaten THEN

BEGIN

wreiteln ("There are exits to the east,"); writeln ("*north, and west");

CASE whichway OF

Ws location = narrowl: e: location := batscaves ns location :* narrow2; ds: BEGIN quit 2 trues eaten 8 = trues writeln (?Oh no!!! You dummy!!!" )s

writeln ("You just fell into the firepit’): writeln (and made such a ruckus that"); writeln ("you woke the ogre. I hate to’); writeln (’tell you this, but you are’); writeln ("also trapped!’);

BEGIN FOR j s= 1 TO 1000 DO; write ("2.7") 3

END { DO 33

writelngs

writeln (*You"’* ve just been added to the’);

105

Cogre’’*’s gourmet recipe library!*)3

writeln (Better luck next time.*)s

writeln ENDs

Salk NOWwAYs

woe v8

END CASE whichway

END ¢ IF NOT eaten 33

chgqloc := (location “> agrerooam) ;

END { FROCEDURE pogreroaam 33 new file named “a2.maze.text”

%

{ source FILE: aZ.maze.text 3} COKK OKRA KKK KKK KKK KKK K KKK KKK KKK KKK KK KG { p m a z e }

COOK OKK KK AK KKK KKK KK KK KKK KKK KKK KK KK KD PROCEDURE pmazes

TYFE

Mazerooms = mO,.same;

VAR

mazelac: MAZeroamMss;

FROCEDURE describe;

BEGIN writeln ("You are in a maze of *)s writeln (’featureless passages." ); writelns;

end { procedure describe 3; procedure sameplace;

begin

(7? You have crawled around’);

{*some twisted tunnels and*); (*7wound up where you began.”*)3;

writeln writeln writeln

%

END { PROCEDURE sameplace 33;

FROCEDURE travel

nloc, sloc,

106

eloc, wloc, uloc, dlocs rooms);

FROCEDURE newloc (loacrrooms) ; BEGIN

IF loc=moO THEN noway ELSE IF loc=same THEN sameplace ELSE location := loc { endif 3 { endif 33

END ¢ FROCEDURE newloc +3 BEGIN (xk travel XxX? CASE whichway OF ns newloc (nloc)s Ss newloc (sloc)s: e: newloc (eloc)s we newloc (wlac)s us newloc (uloc);

ds: newloc (dloc); END { CASE whichway 33

END ¢ PROCEDURE travel 33;

PROCEDURE pmls BEGIN

writeln ("From here you can go south, writeln (west, or up.") 3

CASE whichway OF

Ss location := ladders; e: location s= m2;

east.” )3

107

us location nads naways END; END { pmi 33

ws lacation

BEGIN FROCEDURE pmaze 34

REFEAT IF (location “> mi) AND (location <> maze) THEN

108

describe { endif showoh jects; ckhlampy

CASE locatian OF

MAZE, mis pml; ms travel mos travel m4s travel ms travel més travel m7: travel m8: travel mo s travel mids travel mids travel mi2s travel miss travel mi4: travel mics:

BEGIN

(m1, Same, mM, mO,MO,MO) 3 (m1, m0, same,mO,mO,mO) 5 (mO.M7 4 M34 MF, MO, MO) §

(m1,mO,MO,MO,MO,mMO) 3

(m4,m0, same,~mO,mO,mO) 3 (mS,m?,mo,mB,~mO,mO) 5

(m,MO,MO, SAMS,MO,MO) § (mO,M11,MmO,M1O9O,MO,MO) § (mB, Same,MmO,~mMO,mMO, MO) 5 (m?,MO,MG,M1O,mi,~mi2);: (m12,mO,MmO,~MmO,MO,m14G) § (m14,mO,mMO,mMO,MO,m17) | (m1S,mO,mo,mO,mO, M18)

writeln I seem to remember buried’);

writeln ("treasure near this location.”*)

travel (mO,mO,mO,mO,m1,~mM1?)

END ;

midé: travel (m17,same,mO,mO,mO,mO) ; mi7: travel (m18,.m16,mO,MmO,MO,mMO) 5 mis

BEGIN

writeln (’I seem to remember a treasure’); writeln (*near here.*)3 travel (m19,m17,mO,mO,MO,mMO)

END; mivs travel (mO,m18,mO,mOo,m1S,mO) :

END CASE location 3,

UNTIL (location «< maze) OR (location = flames): chqloc := trues

END ¢ FROCEDURE pmaze 33 { source FILE: a@.main.text } new file named “a2.main.text”

OOOO OOOO OO OOOO II IIE: £ p p i tt } OOOO OO OOOO OGIO IO OOK IK 3

FROCEDURE ppits BEGIN

CASE whichway OF

d: location := ladders; multiple statements

us x writeln (*Try ta climb that’); in a case selector writeln (Cand you’ "ll kill yourself!*)s; must be bookended END; by BEGIN and END; Ty Sa @qgws naways The BEGIN statement must be added END; between u: and writeln as chgloc := (location <> pit); indicated by the asterisk. Type “u: BEGIN” and press return. END ¢€ FROCEDURE ppit 35, Tab over under BEGIN and continue

typing the writeln instructions, CROOK OK CK KOK KKK KKK KKK followed by “END;” as shown. { Pp tladqddeor } COC OOOO OOK ORK KKK KK}

FROCEDURE pladder; BEGIN

109

CASE whichway OF

ms location t= maze; d: location := flames; us

IF treasure IN stash THEN BEGIN

writeln (You can’’*t carry the treasure");

writelnm ("up the ladder -")3 writeln ("it*"*s much toa heavy! ");

END ELSE

location := vestibule { endif 33

@,.S_Ws NOwWAYy s

END;

chgloc := (lacation «<> ladder) 3:

+

END ¢ PROCEDURE pladder 33 CREST EESRE EDEL EET CEES EL LESS EERE SESE RS

{ p ag 1 a m e 3

STUTTTETTRTTTTUTTTTTETTTT TET TOTTORI TS

FROCEDURE pflames; BEGIN

cooked := trues done f= trues

END {€ FROCEDURE pflames 3}; COOK OOK OK KOK OK KKK OK OK}

BEGIN { =ee==> adventure {ss=== } CKKKKKKKKKRK KKK KKK KKK KKKK KG

introduction; initialize;

110

REPEAT

visitedClocatiand := true; show(locatioan) 3

cklamp;

CASE location OF

start: grandroams

vestibule: narrowls: lakeshore:

islands brink:

iceraoms

ogreraams narroaw2s

pit: crystals:

batscave:

steams:

deadend:

ladder: Mazes stairs:

pstarty;

travel (nowhere, nowhere, nowhere,

pvestibules pnarrawl; travel (island,

narrow? , nowhere. pislands travel (nowhere, nowhere, nowhere,

travel (ogreroom,

crystal,

nowhere . pogreraams travel (nowhere,

nowhere, brink, stairs):

narrowl,

nowhere, nowhere) 5

nowhere, ogreroom, pit); nowhere. vestibule, nowhere)

ogreraom,

steam, lakeshore,

NOW Er & . ppits

travel (ogreraom,

nawhere) 3

nowhere.

Maze, agreraam,

nowhere,

nowhere) 3

travel (steam, moawhere,

nowhere, nowhere,

travel (deadend, nowhere, nowhere,

travel (nowhere, nowhere. nowhere,

pladder;

PMaAZes

travel (nowhere, nowhere.

ogreroom., nowhere) batscave,. narrows. MAZE) § steam. nowhere, nowhere) §

nowher &, nowhere.

111

END ¢

112

echoes:

inclines

warmrooms

roundrooms:

honeycomb:

mudroams

deeppool:

caldroom:

narrows:

narrow4s:

rivers

rockyrooms:

siltrooms

alcoves

flames:

CASE

grandraom,

travel (warmroom, incline, nowhere, nowhere, stairs, deeppool); travel (nowhere, nawhere, roundroam, honeycomb, echoes, nawhere) 3 travel (nowhere, echoes, nowhere, nawhere, nowhere, flames); travel (honeycomb, nowhere, nowhere, incline, nowhere, mudroam) § travel (nowhere, narrows, incline, nowhere, nowhere, nowhere) s travel (nowhere, nowhere, nowhere, nowhere, roundroam, siltroom)s: travel (nowhere, nowhere, nowhere, nowhere, mudroom, coldroaam) ; travel (nowhere, nowhere, nowhere, nowhere, deeppool, nowhere): travel (honeycomb, narrow4, nowhere, nowhere, nowhere, nowhere) ; travel (narrows, river, nowhere, nowhere, nowhere, maze) s travel (narroaw4, rockyroom, nowhere, nowhere, nowhere, nowhere); travel (river, siltroam, alcave, nowhere, nawhere, nowhere) 5 travel ¢rockyream, nowhere, nowhere, alcove, muidraam, nowhere) ; travel (nowhere, nowhere, siltroom, nowhere, nowhere, nowhere) 3 pflames;

+

location 33

echoes) §

UNTIL quit OR done; cangratulationss

END.

113

Command Processing in Adventure 2

This chapter is one of the most important of the book. It deals with topics that, taken one at a time, are pretty simple, but when put all together form a major part of all our subsequent adventure game implementations. You will probably want to read it more than once. It is also a good idea to refer to the actual Pascal code frequently while reading.

The first example of an adventure game would be considered a mere “toy” by devotees. Why? Mainly because it did not allow the player to use traditional adventure game commands. It limited the player to travel commands or directions. The consequence of this was the fact that “solving” all the problems was totally unchallenging. It was sim- ply a matter of answering yes or no questions (with most of the successful answers obviously being yes). Of course, you want your own adventure games to allow “real” command processing.

Commands are important because they add a sense of control to the game. The player is par- ticipating. Not everything is yes or no. Even the commands that the guide will recognize and carry out must be guessed by the player. Beyond that,

commands provide the vehicle by which players solve the problems posed by the adventure game. This is the essence of true adventure, and without it adventures are not really worth playing.

In Adventure 2 I have included commands that take the form of one or two word sentences. These are simple verbs or verbs with a single noun as object. Such commands provide adequate complex- ity. More advanced adventure games allow a vari- ety of multiple word commands. Such games often claim that the player can use English commands. This is an overstatement. Most English sentences will completely befuddle such adventure games. In any case, implementing a parser for such commands is a much greater challenge than I am prepared to attempt in this book. So I will stick to our one or two word commands. Even these will allow you to im- plement interesting problems in your adventures.

The commands that have been added to the game are carry, drop, help, light, inventory, take (synonym for carry), tally, push, dig, look, open, unlock (synonym for open), and eat.

There is one support procedure for each possi-

115

ble command. These procedures are named by pre- fixing the letter p to the command name itself. So for example, there are the procedures pcarry, pdrop, plight, and so on. The command support procedures are to commands, what the location procedures are to locations. They provide all the Pascal code needed to carry out the corresponding command. The commands that are synonyms for other commands use the same support procedures as the commands for which they are synonyms.

AN OVERVIEW OF THE COMMAND HANDLING CODE

Figure 13-1 shows the procedures and func- tions used by Adventure 2 for command processing. The top levels of this diagram duplicate Adventure 1. What is new are the functions and procedures below travel and whichway—docommang, listen, and cmdlookup, which are various procedures de- signed to do special processing for individual com- mands. Much of the code that I describe in this chapter can be reused in all of your adventure games.

The travel Procedure

The travel procedure causes either a new lo- cation to be determined or the message “There is no way to go in that direction” to be printed. The travel procedure invokes the whichway function to determine which direction the player desires to take. I discuss travel in more detail in Chapter 16.

The whichway Function

The whichway function is also similar to the one used in Adventure 1. It differs in that it does not contain the code for prompting the user. Rather it invokes a new function called docommand; docommand returns a single character that indi- cates the direction the player wants to go. There is the possibility that this character does not corre- spond to one of the legal travel indicators. In sucha case, the command is simply ignored and the player is reprompted for a command.

There are two weaknesses with the command processing in Adventure 2 that will be remedied in Adventure 3:

116

gw Any word beginning with a travel letter (n, s, e, w, u, or d) is taken to be equivalent to the travel command. Thus, if the player types NEVER- MORE, the command processor will assume that the command was n, or north.

w@ If a command that is neither a legitimate com- mand nor a direction indicator is typed, the player is simply reprompted. There is no indica- tion given that the guide failed to understand.

__ This can be misleading at times—the player may

assume that the command was understood. For example, TURN ON LIGHT will be ignored, and the lamp, if not yet lit, will stay unlit.

The docommand Procedure

The docommand procedure is really a “front” for listen, which does the real work. the docom- mand procedure sits in a while loop calling the listen procedure. The listen procedure eventually returns, having set the variables head and tail. The docommand procedure returns the first character of the head string. It makes sure this is valid by first determining that there is something in the string to return. This is accomplished by using the intrinisic function length:

length (head) > 0

The listen Procedure

The listen procedure is the command dis- patcher for Adventure 2. It supervises the process of determining which command the player wants to have executed. It invokes the appropriate command procedure for each command entered.

The cmdlookup Function

The parts of a compiler that read, interpret, and verifiy the syntax of a program are called scan- ners and parsers. The cmdlookup function provides a very rudimentary scanner and parser combination for Adventure 2. It prompts for the players com- mand and breaks that command into two pieces— head and tail. The head string always determines which command will be executed. The tail string will contain the object of the command verb if there

arection AY

docommand listen

cmdlookup

Ka

Fig. 13-1. The structure diagram of the command processing in Adventure 2.

is any. It is always possible for tail to be the empty As I hinted earlier, cmdlookup is not at all string: length (tail) = 0. sophisticated. It doesn’t even bother to make sure

117

that the character strings in head and tail are sensi- ble for command and object names. It will blithely allow you to type:

*&ANO$H@W(2>< —--++==

as a command. When it fails to match the string & %# '(/2>< to the name of any legal command or direction, it will reprompt you for another com- mand. It ismonumentally stupid! However, it is just smart enough to do the required job.

COMMAND PROCESSING IN DETAIL

Parts of the command processing code involve some rather complex but very useful programming techniques. These programming strategies are detailed in the following pages.

docommand: Calling listen in a Loop

There really isn’t much more to say about docommand. It conveniently separates an enclos- ing loop around listen. That loop could have been incorporated into listen itself. The only difference would be a miniscule gain in efficiency. Because docommand is involved in direct response to in- teractive commands, you probably wouldn’t even notice the difference. If you are the curious type, making that modification would be a good exercise to try.

listen: Another Case Statement for Control

The listen procedure uses a local variable Icmd of type cmds to control its case statement. The cmds type is an enumerated type that names all the commands available to the player:

cmds = (carry, drop, help, light, inventory, take, tally, push, dig, look, open, unlock, eat, nocmd);

I shall discuss the use of this type and the setting of the variable Icmd in detail below.

The REPEAT . . . UNTIL loop in listen en- sures that the player’s commands are carried out

118

until a command that is either a direction or an unrecognizable sequence is entered. All of the fun takes place inside the cmdlookup function, and I devote the remainder of this chapter to its work- ings.

The cmdlookup Function

The cmdlookup function is not very long—a little over half a page of well-spaced Pascal source code. Yet it packs a lot of wallop. In the course of unraveling its inner mysteries, I shall delve into the following subjects:

@ The input of enumerated types.

@ Arrays indexed by enumerated types.

@ The conversion of values—external to internal.

@ The method of searching an array using a linear search.

@ Arrays of strings and string comparison.

@ Sentinels in a linear search.

@ The succ function and its use in searching.

There is a lot to cover. The only way to reach the end is to plunge ahead. So, here we go!!

The Input of Enumerated Types. I have done more than a little to give Pascal’s enumerated types “rave reviews.” Now I come to one of the annoying weaknesses of enumerated types in many Pascal implementations: it is usually impossible to READ or WRITE a value of an enumerated type directly, that is, using the identifiers contained in the declaration of the enumerated type.

In Adventure 2, this is exemplified by the array of strings that I have called cmdnames. cmd- names is indexed by the enumerated type cmds:

cmdnames: ARRAY{[cmds] of STRING;

In order to really understand what is going on here, I need to elaborate on this declaration and its implications for the Pascal language. Bear with me while I digress from the consideration of the actual code of cmdlookup.

Arrays Indexed by an Enumerated Type. I am assuming that you know what an array is. I also

assume that you have used arrays before in your Pascal programs and are familiar with the syntax for declaring arrays. However, the idea of using an enumerated type as the index for an aray may be new to you.

Ordinarily, the index of an array is simply a range of integers

x: ARRAY[1 . . 100] OF STRING;

and individual elements of an array are referenced using single values from the range as sub- scripts—x[1], x[5], x[29], and so on. An array cor- responds to a list of items. The index is like the number indicating the position of the item in the list. Because the identifiers of an enumerated type are assigned numeric values by the Pascal com- piler, you may think of the enumerated type itself as being just like a range of integers. Therefore, using an enumerated type as the index of an array in a declaration is a normal thing to do.

When an array is indexed by an enumerated type, you can think of the array as a whole as a “named list” of items. To illustrate, consider the following list of men’s nicknames. The list is enu- merated by the names for which nicknames are being listed:

Name Nickname Richard Dick Thomas Tom Lawrence Larry William Bill John Jack Arnold Arnie James Jim

This list may be represented in Pascal as an array of strings indexed by the enumerated type:

name = (Richard, Thomas, Lawrence, Wil- liam, John, Arnold, James);

The array declaration would look like this:

nicknames: ARRAY[name] OF STRING;

Then assignment statements such as

nickname[Richard] := ’Dick’; nickname[Lawrence] := ’Larry’; nickname[James] := ‘Jim’;

are perfectly legitimate. Each one constructs one of the entries in the original list of nicknames given above.

The Conversion of Values: External to Internal. The identifiers in a Pascal enumerated type represent external values. The numbers as- signed to the identifiers in an enumerated type represent internal values. They are the values that the Pascal compiler prefers to use when ma- nipulating the enumerated type inside the comput-

er. In the best of all possible Pascal implementa-

tions, you should be able to write the following sort of code:

writeln (“Your wish is my command > ”); read (Icmd); CASE Icmd OF

Carry: pcarry; drop: pdrop; help: phelp; light: plight; invent: pinventory;

and so on. That is, given a variable of type cmds, such as Icmd, you should be able to read a value of Icmd. The user of the program containing the above code should be able to type one of the identifiers in the enumerated type in response to the prompt, for instance

Your wish is my command > carry keys

This should have the effect of the identifier carry being read and the corresponding internal value of the enumerated type being stored in the variable Icmd. (The second part of the input line would be ignored until another read command was issued by the program—presumably this would take place in the pcarry procedure.)

119

This is an example of the concept of conversion of representations. It is worth talking about this a little further to give you an idea of why our “best of all implementations” scenario is usually not the reality.

What kind of values are stored in a variable like Icmd? In the guts of the actual Pascal program, they are whole numbers: 1, 2, 3, and so on. The actual numbers are not guaranteed to be any particular values. The definition of Pascal only requires that the numbers assigned be consecutive. It is true that 90% of all Pascal compilers will assign values be- ginning with 0. I would venture to say 100%, but then I am not personally acquainted with every Pascal compiler ever written! The correspondence between the identifiers in the declaration and the numbers chosen to represent these identifiers is maintained by the Pascal compiler. So whenever your program refers to one of the identifiers in the enumerated type, the right numeric value is sub- stituted for it in the translated program.

Once a Pascal program has been compiled, the identifiers of an enumerated type are forgotten. They have all been assigned their internal numeric values. This is fine as long as you never wish the program to input or output a value of any enumer- ated type. Should you wish to do so, you are faced with the following fact: the identifiers (as they ap- pear in the declaration of the enumerated type) are character strings, but the internal values corre- sponding to those identifiers are numbers. For example, the cmds enumerated type might be as- signed internal values as follows:

External (identifier)

carry drop

help

light inventory take

tally

push

dig

look

open

Internal (number)

SCOONODOUFWNHKH OC

120

External (identifier) Internal (number)

unlock 11 eat 12 nocmd 13

The question becomes how do you “compute” a variable whose type is a user-defined enumerated type? In particular, how do you input such a value in response to a user’s command that is typed as a character string. The answer is basically pretty boring. You input the external representation of such a value and convert that value to its internal form yourself. This requires writing explicit Pascal code to do the job. In Adventure 2 the procedure cmdlookup performs this task.

The first piece in solving the puzzle of enu- merated type input is an array of strings. The array must be indexed by the enumerated type. The strings in the array are the identifiers used in the declaration of the enumerated type. Thus for cmds, you have the array cmdnames, mentioned above. For example, cmdnames{inventory] := inven- tory ; The array cmdnames is set up in the proce- dure initialize. This is similar to the example of nicknames that I gave earlier.

How do you use the string array? You use it to match a string typed by the user to one of the identifiers in the cmds type. This is the lookup aspect of cmdlookup.

Actually, the strings read by cmdlookup may consist of more than just the command name itself. This is true because many command verbs take objects: carry keys, drop treasure, light lamp, eat crow, and so on. Also, the player may perversely type anything at all that forms a legal string: “Take me to your leader,” or “Come with me to the Cas- bah.” cmdlookup must first dissect the input string into two parts, which I have called head and tail. So cmdil-okup first reads the player’s response to the prompt into the string variable command. It dis- sects Command into two pieces by locating the first blank in the command string (if any). The part of the command preceding the first blank is stored in the variable head. The part of the command after the first blank (but not including it) is stored in the variable tail.

The variable head will be used in the com- mand lookup process. The variable tail is used by the various command support procedures to deter- mine the details of specific commands. For exam- ple, if head is the command carry, tail will be used to determine what, if anything, will be carried.

Figure 13-2 illustrates the picking apart of command. There is a “bug” in this code. Can you spot it? Can you fix it?

Searching an Array Using the Linear Search. Searching is a technique used in many different computer programs. More often than not, it is a list that is searched. This is the case in Adventure 2. There are many kinds of search techniques, but I shall concentrate on one of the

command ~~~¥P ‘carry A key” p:=pos (‘A’, command);

command —~ 8 Carty A key”

ws

head := copy (command, 1, p-1);

simplest, the linear search.

The general technique of linear search as- sumes the existence of a linearly ordered collection of items. In this case the collection is the list of strings represented by the array cmdnames.

Any collection of “things” organized in a lineup of some kind may be subjected to linear search. Here are some examples from real life:

w A pile of magazines on a coffee table.

w A bin of watercolor paintings on sale at the art gallery.

m A pile of essays waiting to be graded by an English teacher.

g A collection of recipes on 3x5 cards.

tail := copy (command, p+1, length (command)-—p);

command —~~w ‘carry A key”

~~ length (command)—p 1 J

head —~e “carry”

tail —w “key”

Fig. 13-2. The dissection of a command into head and tail.

121

@ The want ads in your local newspaper.

In all these examples, if you were searching for a specific item such as last month’s issue of your favorite computer magazine, a painting by a specific artist, an essay by your favorite pupil, a recipe for shrimp jambalaya, or an ad for a used printer for your computer, you would be apt to start at the beginning and search through the collection one item at atime. You would look at each item to see if it was the one you wanted, and continue until you either found that item or came to the end of the collection.

In some cases, you might take advantage of extraneous information to speed up your speech. For example, you might remember the color of the cover of the magazine and limit your search to magazines whose covers were of that color. You might look for a painting that had the recognizable style of the specific artist you were interested in. You might look for certain key words in the news- paper ad, such as printer or computer.

Because you are human, you have sophisti- cated pattern matching abilities with which a com- puter cannot yet compete. A computer, searching a list of items, is not able to use such cues in most cases. It has to take each item in turn to see whether or not it is the one being sought. This is always true when the list being searched has no other structure than that of a list. Our arrays are simply big unsorted piles—like a collection of twenty years’ worth of LIFE magazines, well shuf- fled from use.

Here’s how to search:

1. First ask whether or not there are any more items left in the pile you are searching. If the answer is yes, continue the search by doing step 2. If the answer is no, stop the search.

2. Isthe next item in the pile the item for which you are looking? If it is, you have succeeded, so quit. If it is not, do step 3.

3. Put aside the item you just examined and re- jected. Continue the search from step 1.

The following easy-to-remember names can

122

be given to the three steps in the above procedure: 1. TEST, 2. COMPARE, and 3. LOOP. I shall refer to these steps in the following discussion.

Arrays of Strings and String Compari- son. To be able to perform searches of string ar- rays, you must be able to compare one string to another. For cmdlookup, you must be able to com- pare strings in cmdnames with the string in head. UCSD Pascal allows you to make comparisons of string variables for equality and inequality:

S1 = S2 is true <=> S1 and S2 contain the same character string.

S1 <> $2 is true<=>S1 and S2 differ in at least one character position.

It is also possible to compare two character strings lexicographically. This is a fancy word for “in dictionary order.” The mathematical comparison symbols < and > are used to mean the following:

S1 < S2is true <=> The string in S1 would come before the string in S2, if both strings were in the dictionary.

S1 > S2 is true <=> The string in S1 would come after the string in S1, if both strings were in the dictio- nary.

The search in cmdlookup only uses the comparison for inequality.

The test for the completion of a linear search, “Are we out of items to consider?” is not conceptu- ally part of the search itself. It seems like unwanted extra baggage. You will soon see that it really is.

Suppose you knew ahead of time that what you were searching for definitely was one of the items in the collection being searched; or to put it another way, suppose you knew at the start that a successful search was guaranteed. Then the TEST part of the procedure would be superfluous. You might think the whole search would be superfluous! Leaving that issue aside for a moment, let’s see if we can

“Ordinary” array locations

} Extra location at

the end of the array to hold the sentinel value.

think of a way to guarantee that all your linear searches have happy endings.

Figure 13-3 gives the basic idea—an extra location in the search collection. Now why would you want to increase the number of items to be searched through? To guarantee success, of course. You will always use the extra location to store a “copy” of the item you are looking for. Then if that item turns out not to be in the collection proper, you will still find it in the extra location at the end. You won’t have to worry about testing to determine whether or not any items are left. At the very worst, you will find what you are looking for just before you run out of items to consider.

The extra item added to the collection is known as a sentinel. It “stands guard” against the possibility of failure.

You now have a slightly different problem to solve with your search. There are now two possible ways to “succeed.”

1. Find the sentinel. 2. Find the item you are searching for before reaching the sentinel.

Fig. 13-3. A picture of an array set up for the sentinel search technique.

In the first case, even though you succeed in one sense, you fail in the larger sense. Case 2 could be dubbed “real success.” How do you know if you “really” succeed? Simple! After you succeed (which you know you will, because you have a sentinel), you check to see if you are at the sentinel location. If you are not, then you really did succeed. If you are at the sentinel you were only helped over the finish line. Real success awaits in some future search.

You may have been wondering why nocmd was one of the identifiers in the enumerated type cmds. After all, you probably couldn’t imagine typing nocmd as a command. It is there as a place holder for the sentinel that will be used in the linear search in cmdlookup. The cmdlookup function guarantees the success of the linear search by storing the string contained in head in cndnames [nocmd]:

cmdnames[nocmd] := head; The search is a very simple while loop:

WHILE head <> cmdnames[Icmd] DO

123

Icmd := succ (Icmd) (* END DO *);

When this loop terminates, the value stored in Icmd will either be the value of cmds correspond- ing to the command the player typed or the value nocmd.

The succ Function. In order to cause Icmd (a variable of type cmds) to take on the “next” value of its enumerated type, the intrinsic function Succ is provided. Its action corresponds to picking the next identifier from the list of identifiers given in the declaration of the enumerated type. It is analogous

124

to saying x := x + 1 for a variable x of the integer type. For example,

succ (carry) = drop succ (tally) = push succ (eat) = nocmd succ (open) = unlock

The succ function is undefined for the last element in an enumerated type. For cmds, succ (ncmd) is undefined. An attempt to use succ (nocmd) in an expression will cause an error when that expression is evaluated at run time.

Carry and Drop: Pascal Sets

In the last chapter I discussed the overall im- plementation of command processing in Adventure 2. In this chapter I will look more closely at two of the commands themselves: carry and drop. Both of these commands make use of the Pascal concept of a set.

The Pascal language allows for variables of type set. Very few languages allow the programmer to create and manipulate sets. This is somewhat surprising, because the idea of a set is a common one in the real world and is often used in programs. I shall discuss the concept briefly below and show how it is simulated in languages that do not support it directly.

WHAT ARE SETS AND HOW CAN THEY BE USED?

The concept of the set may be given a very precise mathematical definition. I will not subject you to sucha treatment, however. Let us define set by giving some synonyms: collection, group, and aggregate, etc. Try to understand set by consider- ing some examples:

g A stamp collection. w A set of tools.

g@ A set of silverware. g A collection of books.

Any group of similar items may be considered to be a set.

Sets occur in many natural situations. In our adventures, the collection of objects that the player is carrying at any given time is a set. It turns out to be very easy to represent this in Pascal. In other languages, it is not so easy. Let us briefly consider how a set can be represented in BASIC.

Sets in BASIC

In adventure games, the player can pick up and carry various objects. Consider the problem of keeping track of which objects are being carried. First of all, you need to represent the objects them- selves. In Pascal, this is best done by an enumer- ated type:

object = (lamp, treasure, key, sandwich, bot- tle, shovel, noobj);

125

In BASIC, there is no best or natural way to represent the objects. One straightforward way is to choose numeric variables whose values are similar to those that might be assigned to the iden- tifiers in the Pascal enumerated type:

LA = 1 TR =2 KE = 3 SA = 4 BO = 5 SH = 6 NO = 7

Most BASIC interpreters will allow you to spell out longer variable names, even though only the first two characters are significant:

LAMP = 1 TREASURE = 2 KEY = 3

SANDWICH

an |

Now, how do you represent the set of objects that the player is carrying? A simple way is to use an array:

DIM CARRY(7)

Each entry in the array corresponds to one object. The value of the array entry represents whether or not that object is being carried. The value of 1 means that the object is being carried, the value of 0 means that the object is not being carried. The collection of values stored in the array repre- sents the set of items being carried by the player.

For example, if CARRY(6) = 0, the player is not carrying the shovel. If CARRY(1) = 1, the player is carrying the lamp.

Storing several values, each of which is either 0 or 1, in an array is wasteful of memory. Another technique for representing a set using Os and 1s is to use a bit string instead of an array. A bit string is

126

nothing more than an ordinary whole number thought of in its binary form. The Os and 1s in the binary numeral for the number may be used in the same way the Os and Is in the array are used.

In this simple example, you can get by with a number between 0 and 63 to represent any possible collection of the 6 objects. The “object” noobj is not something the player can carry. It is used in a fashion similar to that of nocmd in the last chapter. Thus, it can be ignored when you consider the set representation problem.

Each object is assigned to a particular power of two between 1 and 32:

LAMP <===> 1 = 2 to the Oth power

TREASURE <===> 2 = 2 to the 1st power

KEY <===> 4=2 to the 2nd power

SANDWICH <===> 8 = 2 to the 3rd power

BOTTLE <===>16 =2to