|
Table of Content | |
Computer games are a perfect example of programs that often use pattern matching. One class of computer games in general the adventure game is a perfect example of games that use pattern matching. An adventure style game excepts English-like commands from the user parses these commands and acts upon them. In this section we will develop an adventure game shell. That is it will be a reasonably functional adventure style game capable of accepting and processing user commands. All you need do is supply a story line and a few additional details to develop a fully functioning adventure class game.
An adventure game usually consists of some sort of maze through which the player moves. The program processes commands like go north or go right to move the player through the maze. Each move can deposit the player in a new room of the game. Generally each room or area contains objects the player can interact with. This could be reward objects such as items of value or it could be an antagonistic object like a monster or enemy player.
Usually an adventure game is a puzzle of some sort. The player finds clues and picks up useful object in one part of the maze to solve problems in other parts of the maze. For example a player could pick up a key in one room that opens a chest in another; then the player could find an object in the chest that is useful elsewhere in the maze. The purpose of the game is to solve all the interlocking puzzles and maximize one's score (however that is done). This text will not dwell upon the subtleties of game design; that is a subject for a different text. Instead we'll look at the tools and data structures required to implement the game design.
The Madventure game's use of pattern matching is quite different from the previous examples appearing in this chapter. In the examples up to this point the matching routines specifically checked the validity of an input string; Madventure does not do this. Instead it uses the pattern matching routines to simply determine if certain key words appear on a line input by the user. The program handles the actual parsing (determining if the command is syntactically correct). To understand how the Madventure game does this it would help if we took a look at how to play the Madventure game.
The Madventure prompts the user to enter a command. Unlike the original adventure game that required commands like "GO NORTH" (with no other characters other than spaces as part of the command) Madventure allows you to write whole sentences and then it attempts to pick out the key words from those sentences. For example Madventure accepts the "GO NORTH" command; however it also accepts commands like "North is the direction I want to go" and "I want to go in the north direction." Madventure doesn't really care as long as it can find "GO" and "NORTH" somewhere on the command line. This is a little more flexible that the original Adventure game structure. Of course this scheme isn't infallible it will treat commands like "I absolutely positively do NOT want to go anywhere near the north direction" as a "GO NORTH" command. Oh well the user almost always types just "GO NORTH" anyway.
A Madventure command usually consists of a noun keyword and a verb keyword. The Madventure recognizes six verbs and fourteen nouns. The verbs are
verbsgo | get | drop | inventory | quit | help
The nouns are
nouns
north |
south | east | west | lime | beer | card | sign | program | homework | money | form |
coupon
Obviously Madventure does not allow all combinations of verbs and nouns. Indeed the following patterns are the only legal ones:
LegalCmds
go direction
| get item | drop item | inventory | quit | help
direction
north |
south | east | west
item
lime | beer
| card | sign | program | homework | money | form | coupon
However
the pattern does not enforce this grammar. It just
locates a noun and a verb on the line and
if found
sets the noun and verb
variables to appropriate values to denote the keywords it finds. By letting the main
program handle the parsing
the program is somewhat more flexible.
There are two main patterns in the Madventure program: NounPat
and VerbPat. These patterns match words (nouns or verbs) using a regular
expression like the following:
(ARB* ` ` |) word (` ` | EOS)
This regular expression matches a word that appears at the
beginning of a sentence
at the end of a sentence
anywhere in the middle of a sentence
or a sentence consisting of a single word. Madventure uses a macro (MatchNoun
or MatchVerb) to create an expression for each noun and verb in the above
expression.
To get an idea of how Madvent processes words
consider the
following VerbPat pattern:
VerbPat pattern {sl_match2
MatchGo}
MatchVerb MatchGO
MatchGet
"GO"
1
MatchVerb MatchGet
MatchDrop
"GET"
2
MatchVerb MatchDrop
MatchInv
"DROP"
3
MatchVerb MatchInv
MatchQuit
"INVENTORY"
4
MatchVerb MatchQuit
MatchHelp
"QUIT"
5
MatchVerb MatchHelp
0
"HELP"
6
The MatchVerb macro expects four parameters.
The first is an arbitrary pattern name; the second is a link to the next pattern in the
list; the third is the string to match
and the fourth is a number that the matching
routines will store into the verb variable if that string matches (by
default
the verb variable contains zero). It is very easy to add new verbs
to this list. For example
if you wanted to allow "run" and "walk" as
synonyms for the "go" verb
you would just add two patterns to this list:
VerbPat pattern {sl_match2
MatchGo}
MatchVerb MatchGO
MatchGet
"GO"
1
MatchVerb MatchGet
MatchDrop
"GET"
2
MatchVerb MatchDrop
MatchInv
"DROP"
3
MatchVerb MatchInv
MatchQuit
"INVENTORY"
4
MatchVerb MatchQuit
MatchHelp
"QUIT"
5
MatchVerb MatchHelp
MatchRun
"HELP"
6
MatchVerb MatchRun
MatchWalk
"RUN"
1
MatchVerb MatchWalk
0
"WALK"
1
There are only two things to consider when adding new verbs: first don't forget that the next field of the last verb should contain zero; second the current version of Madventure only allows up to seven verbs. If you want to add more you will need to make a slight modification to the main program (more on that later). Of course if you only want to create synonyms as we've done here you simply reuse existing verb values so there is no need to modify the main program.
When you call the match routine and pass it
the address of the VerbPat pattern
it scans through the input string looking
for the first verb. If it finds that verb ("GO") it sets the verb
variable to the corresponding verb value at the end of the pattern. If match
cannot find the first verb
it tries the second. If that fails
it tries the third
and so
on. If match cannot find any of the verbs in the input string
it does not
modify the verb variable (which contains zero). If there are two or more of
the above verbs on the input line
match will locate the first verb in the verb list
above. This may not be the first verb appearing on the line. For example
if you say
"Let's get the money and go north" the match routine will match the
"go" verb
not the "get" verb. By the same token
the NounPat
pattern would match the north noun
not the money noun. So this command would be identical
to "GO NORTH."
The MatchNoun is almost identical to the MatchVerb macro; there is however one difference - the MatchNoun macro has an extra parameter which is the name of the data structure representing the given object (if there is one). Basically all the nouns (in this version of Madventure) except NORTH SOUTH EAST and WEST have some sort of data structure associated with them.
The maze in Madventure consists of nine rooms defined by the data structure:
Room struct north word ? south word ? west word ? east word ? ItemList word MaxWeight dup (?) Description word ? Room ends
The north
south
west
and east fields contain near pointers to other rooms. The program uses the CurRoom
variable to keep track of the player's current position in the maze. When the player
issues a "GO" command of some sort
Madventure copies the appropriate value from
the north
south
west
or east field
to the CurRoom variable
effectively changing the room the user is in. If one
of these pointers is NULL
then the user cannot move in that direction.
The direction pointers are independent of one another. If
you issue the command "GO NORTH" and then issue the command "GO SOUTH"
upon arriving in the new room
there is no guarantee that you will wind up in the original
room. The south field of the second room may not point at the room that led
you there. Indeed
there are several cases in the Madventure game where this occurs.
The ItemList array contains a list of near
pointers to objects that could be in the room. In the current version of this game
the
objects are all the nouns except north
south
east
and west. The player can carry these
objects from room to room (indeed
that is the major purpose of this game). Up to MaxWeight
objects can appear in the room (MaxWeight is an assembly time constant that is currently
four; so there are a maximum of four items in any one given room). If an entry in the ItemList
is non-NULL
then it is a pointer to an Item object. There may be zero to MaxWeight
objects in a room.
The Description field contains a pointer to a
zero terminated string that describes the room. The program prints this string each time
through the command loop to keep the player oriented.
The second major data type in Madventure is the Item
structure. This structure takes the form:
Item struct Value word ? Weight word ? Key word ? ShortDesc word ? LongDesc word ? WinDesc word ? Item ends
The Value field contains an integer value
awarded to the player when the player drops this object in the appropriate room. This is
how the user scores points.
The Weight field usually contains one or two
and determines how much this object "weighs." The user can only carry around MaxWeight
units of weight at any one given time. Each time the user picks up an object
the weight
of that object is added to the user's total weight. When the user drops an object
Madventure subtracts the object's weight from the total.
The Key field contains a pointer to a room
associated with the object. When the user drops the object in the Key room
the user is awarded the points in the Value field and the object disappears
from the game. If the user drops the object in some other room
the object stays in that
room until the user picks it up again.
The ShortDesc
LongDesc
and WinDesc
fields contain pointers to zero terminated strings. Madventure prints the ShortDesc
string in response to an INVENTORY command. It prints the LongDesc string
when describing a room's contents. It prints the WinDesc string when the user
drops the object in its Key room and the object disappears from the game.
The Madventure main program is deceptively simple. Most of the logic is hidden in the pattern matching routines and in the parsing routine. We've already discussed the pattern matching code; the only important thing to remember is that it initializes the noun and verb variables with a value uniquely identifying each noun and verb. The main program's logic uses these two values as an index into a two dimensional table that takes the following form:
| No Verb | GO | GET | DROP | Inventory | Quit | Help | |
|---|---|---|---|---|---|---|---|
| No Noun | - | - | - | - | Inventory | Quit | Help |
| North | - | Do North | - | - | - | - | - |
| South | - | Do South | - | - | - | - | - |
| East | - | Do East | - | - | - | - | - |
| West | - | Do West | - | - | - | - | - |
| Lime | - | - | Get Item | Drop Item | - | - | - |
| Beer | - | - | Get Item | Drop Item | - | - | - |
| Card | - | - | Get Item | Drop Item | - | - | - |
| Sign | - | - | Get Item | Drop Item | - | - | - |
| Program | - | - | Get Item | Drop Item | - | - | - |
| Homework | - | - | Get Item | Drop Item | - | - | - |
| Money | - | - | Get Item | Drop Item | - | - | - |
| Form | - | - | Get Item | Drop Item | - | - | - |
| Coupon | - | - | Get Item | Drop Item | - | - | - |
The empty entries in this table correspond to illegal commands. The other entries are addresses of code within the main program that handles the given command.
To add more nouns (objects) to the game you need only extend the NounPat pattern and add additional rows to the table (of course you may need to add code to handle the new objects if they are not easily handled by the routines above). To add new verbs you need only extended the VerbPat pattern and add new columns to this table.
Other than the goodies mentioned above the rest of the program utilizes techniques appearing throughout this and previous chapters. The only real surprising thing about this program is that you can implement a fairly complex program with so few lines of code. But such is the advantage of using pattern matching techniques in your assembly language programs.
; MADVENT.ASM
;
; This is a "shell" of an adventure game that you can use to create
; your own adventure style games.
.xlist
.286
include stdlib.a
includelib stdlib.lib
matchfuncs
.list
dseg segment para public 'data'
; Equates:
NULL equ 0
MaxWeight equ 4 ;Max weight user can carry at one time.
; The "ROOM" data structure defines a room
or area
where a player can
; go. The NORTH
SOUTH
EAST
and WEST fields contain the address of
; the rooms to the north
south
east
and west of the room. The game
; transfers control to the room whose address appears in these fields
; when the player supplies a GO NORTH
GO SOUTH
etc.
command.
;
; The ITEMLIST field contains a list of pointers to objects appearing
; in this room. In this game
the user can pick up and drop these
; objects (if there are any present).
;
; The DESCRIPTION field contains a (near) address of a short description
; of the current room/area.
Room struct
north word ? ;Near pointers to other structures where
south word ? ; we will wind up on the GO NORTH
GO SOUTH
west word ? ; etc.
commands.
east word ?
ItemList word MaxWeight dup (?)
Description word ? ;Description of room.
Room ends
; The ITEM data structure describes the objects that may appear
; within a room (in the ITEMLIST above). The VALUE field contains
; the number of points this object is worth if the user drops it
; off in the proper room (i.e
solves the puzzle). The WEIGHT
; field provides the weight of this object. The user can only
; carry four units of weight at a time. This field is usually
; one
but may be more for larger objects. The KEY field is the
; address of the room where this object must be dropped to solve
; the problem. The SHORTDESC field is a pointer to a string that
; the program prints when the user executes an INVENTORY command.
; LONGDESC is a pointer to a string the program prints when des-
; cribing the contents of a room. The WINDESC field is a pointer
; to a string that the program prints when the user solves the
; appropriate puzzle.
Item struct
Value word ?
Weight word ?
Key word ?
ShortDesc word ?
LongDesc word ?
WinDesc word ?
Item ends
; State variables for the player:
CurRoom word Room1 ;Room the player is in.
ItemsOnHand word MaxWeight dup (?) ;Items the player carries.
CurWeight word 0 ;Weight of items carried.
CurScore word 15 ;Player's current score.
TotalCounter word 9 ;Items left to place.
Noun word 0 ;Current noun value.
Verb word 0 ;Current verb value.
NounPtr word 0 ;Ptr to current noun item.
; Input buffer for commands
InputLine byte 128 dup (?)
; The following macros generate a pattern which will match a single word
; which appears anywhere on a line. In particular
they match a word
; at the beginning of a line
somewhere in the middle of the line
or
; at the end of a line. This program defines a word as any sequence
; of character surrounded by spaces or the beginning or end of a line.
;
; MatchNoun/Verb matches lines defined by the regular expression:
;
; (ARB* ' ' | e) string (' ' | EOS)
MatchNoun macro Name
next
WordString
ItemVal
ItemPtr
local WS1
WS2
WS3
WS4
local WS5
WS6
WordStr
Name Pattern {sl_match2
WS1
next}
WS1 Pattern {MatchStr
WordStr
WS2
WS5}
WS2 Pattern {arb
0
0
WS3}
WS3 Pattern {Matchchar
' '
0
WS4}
WS4 Pattern {MatchStr
WordStr
0
WS5}
WS5 Pattern {SetNoun
ItemVal
0
WS6}
WS6 Pattern {SetPtr
ItemPtr
0
MatchEOS}
WordStr byte WordString
byte 0
endm
MatchVerb macro Name
next
WordString
ItemVal
local WS1
WS2
WS3
WS4
local WS5
WordStr
Name Pattern {sl_match2
WS1
next}
WS1 Pattern {MatchStr
WordStr
WS2
WS5}
WS2 Pattern {arb
0
0
WS3}
WS3 Pattern {Matchchar
' '
0
WS4}
WS4 Pattern {MatchStr
WordStr
0
WS5}
WS5 Pattern {SetVerb
ItemVal
0
MatchEOS}
WordStr byte WordString
byte 0
endm
; Generic patterns which most of the patterns use:
MatchEOS Pattern {EOS
0
MatchSpc}
MatchSpc Pattern {MatchChar
' '}
; Here are the list of nouns allowed in this program.
NounPat pattern {sl_match2
MatchNorth}
MatchNoun MatchNorth
MatchSouth
"NORTH"
1
0
MatchNoun MatchSouth
MatchEast
"SOUTH"
2
0
MatchNoun MatchEast
MatchWest
"EAST"
3
0
MatchNoun MatchWest
MatchLime
"WEST"
4
0
MatchNoun MatchLime
MatchBeer
"LIME"
5
Item3
MatchNoun MatchBeer
MatchCard
"BEER"
6
Item9
MatchNoun MatchCard
MatchSign
"CARD"
7
Item2
MatchNoun MatchSign
MatchPgm
"SIGN"
8
Item1
MatchNoun MatchPgm
MatchHW
"PROGRAM"
9
Item7
MatchNoun MatchHW
MatchMoney
"HOMEWORK"
10
Item4
MatchNoun MatchMoney
MatchForm
"MONEY"
11
Item5
MatchNoun MatchForm
MatchCoupon
"FORM"
12
Item6
MatchNoun MatchCoupon
0
"COUPON"
13
Item8
; Here is the list of allowable verbs.
VerbPat pattern {sl_match2
MatchGo}
MatchVerb MatchGO
MatchGet
"GO"
1
MatchVerb MatchGet
MatchDrop
"GET"
2
MatchVerb MatchDrop
MatchInv
"DROP"
3
MatchVerb MatchInv
MatchQuit
"INVENTORY"
4
MatchVerb MatchQuit
MatchHelp
"QUIT"
5
MatchVerb MatchHelp
0
"HELP"
6
; Data structures for the "maze".
Room1 room {Room1
Room5
Room4
Room2
{Item1
0
0
0}
Room1Desc}
Room1Desc byte "at the Commons"
0
Item1 item {10
2
Room3
GS1
GS2
GS3}
GS1 byte "a big sign"
0
GS2 byte "a big sign made of styrofoam with funny "
byte "letters on it."
0
GS3 byte "The ETA PI Fraternity thanks you for return"
byte "ing their sign
they"
cr
lf
byte "make you an honorary life member
as long as "
byte "you continue to pay"
cr
lf
byte "your $30 monthly dues
that is."
0
Room2 room {NULL
Room5
Room1
Room3
{Item2
0
0
0}
Room2Desc}
Room2Desc byte 'at the "C" on the hill above campus'
0
Item2 item {10
1
Room1
LC1
LC2
LC3}
LC1 byte "a lunch card"
0
LC2 byte "a lunch card which someone must have "
byte "accidentally dropped here."
0
LC3 byte "You get a big meal at the Commons cafeteria"
byte cr
lf
byte "It would be a good idea to go visit the "
byte "student health center"
cr
lf
byte "at this time."
0
Room3 room {NULL
Room6
Room2
Room2
{Item3
0
0
0}
Room3Desc}
Room3Desc byte "at ETA PI Frat House"
0
Item3 item {10
2
Room2
BL1
BL2
BL3}
BL1 byte "a bag of lime"
0
BL2 byte "a bag of baseball field lime which someone "
byte "is obviously saving for"
cr
lf
byte "a special occasion."
0
BL3 byte "You spread the lime out forming a big '++' "
byte "after the 'C'"
cr
lf
byte "Your friends in Computer Science hold you "
byte "in total awe."
0
Room4 room {Room1
Room7
Room7
Room5
{Item4
0
0
0}
Room4Desc}
Room4Desc byte "in Dr. John Smith's Office"
0
Item4 item {10
1
Room7
HW1
HW2
HW3}
HW1 byte "a homework assignment"
0
HW2 byte "a homework assignment which appears to "
byte "to contain assembly language"
0
HW3 byte "The grader notes that your homework "
byte "assignment looks quite"
cr
lf
byte "similar to someone else's assignment "
byte "in the class and reports you"
cr
lf
byte "to the instructor."
0
Room5 room {Room1
Room9
Room7
Room2
{Item5
0
0
0}
Room5Desc}
Room5Desc byte "in the computer lab"
0
Item5 item {10
1
Room9
M1
M2
M3}
M1 byte "some money"
0
M2 byte "several dollars in an envelope in the "
byte "trashcan"
0
M3 byte "The waitress thanks you for your "
byte "generous tip and gets you"
cr
lf
byte "another pitcher of beer. "
byte "Then she asks for your ID."
cr
lf
byte "You are at least 21 aren't you?"
0
Room6 room {Room3
Room9
Room5
NULL
{Item6
0
0
0}
Room6Desc}
Room6Desc byte "at the campus book store"
0
Item6 item {10
1
Room8
AD1
AD2
AD3}
AD1 byte "an add/drop/change form"
0
AD2 byte "an add/drop/change form filled out for "
byte "assembly to get a letter grade"
0
AD3 byte "You got the form in just in time. "
byte "It would have been a shame to"
cr
lf
byte "have had to retake assembly because "
byte "you didn't realize you needed to "
cr
lf
byte "get a letter grade in the course."
0
Room7 room {Room1
Room7
Room4
Room8
{Item7
0
0
0}
Room7Desc}
Room7Desc byte "in the assembly lecture"
0
Item7 item {10
1
Room5
AP1
AP2
AP3}
AP1 byte "an assembly language program"
0
AP2 byte "an assembly language program due in "
byte "the assemblylanguage class."
0
AP3 byte "The sample program the instructor gave "
byte "you provided all the information"
cr
lf
byte "you needed to complete your assignment. "
byte "You finish your work and"
cr
lf
byte "head to the local pub to celebrate."
byte cr
lf
0
Room8 room {Room5
Room6
Room7
Room9
{Item8
0
0
0}
Room8Desc}
Room8Desc byte "at the Registrar's office"
0
Item8 item {10
1
Room6
C1
C2
C3}
C1 byte "a coupon"
0
C2 byte "a coupon good for a free text book"
0
C3 byte 'You get a free copy of "Cliff Notes for '
byte 'The Art of Assembly'
cr
lf
byte 'Language Programming" Alas
it does not '
byte "provide all the"
cr
lf
byte "information you need for the class
so you "
byte "sell it back during"
cr
lf
byte "the book buy-back period."
0
Room9 room {Room6
Room9
Room8
Room3
{Item9
0
0
0}
Room9Desc}
Room9Desc byte "at The Pub"
0
Item9 item {10
2
Room4
B1
B2
B3}
B1 byte "a pitcher of beer"
0
B2 byte "an ice cold pitcher of imported beer"
0
B3 byte "Dr. Smith thanks you profusely for your "
byte "good taste in brews."
cr
lf
byte "He then invites you to the pub for a "
byte "round of pool and"
cr
lf
byte "some heavy duty hob-nobbing
"
byte "CS Department style."
0
dseg ends
cseg segment para public 'code'
assume ds:dseg
; SetNoun- Copies the value in SI (the matchparm parameter) to the
; NOUN variable.
SetNoun proc far
push ds
mov ax
dseg
mov ds
ax
mov Noun
si
mov ax
di
stc
pop ds
ret
SetNoun endp
; SetVerb- Copies the value in SI (the matchparm parameter) to the
; VERB variable.
SetVerb proc far
push ds
mov ax
dseg
mov ds
ax
mov Verb
si
mov ax
di
stc
pop ds
ret
SetVerb endp
; SetPtr- Copies the value in SI (the matchparm parameter) to the
; NOUNPTR variable.
SetPtr proc far
push ds
mov ax
dseg
mov ds
ax
mov NounPtr
si
mov ax
di
stc
pop ds
ret
SetPtr endp
; CheckPresence-
; BX points at an item. DI points at an item list. This
; routine checks to see if that item is present in the
; item list. Returns Carry set if item was found
; clear if not found.
CheckPresence proc
; MaxWeight is an assembly-time adjustable constant that determines
; how many objects the user can carry
or can be in a room
at one
; time. The following repeat macro emits "MaxWeight" compare and
; branch sequences to test each item pointed at by DS:DI.
ItemCnt = 0
repeat MaxWeight
cmp bx
[di+ItemCnt]
je GotIt
ItemCnt = ItemCnt+2
endm
clc
ret
GotIt: stc
ret
CheckPresence endp
; RemoveItem- BX contains a pointer to an item. DI contains a pointer
; to an item list which contains that item. This routine
; searches the item list and removes that item from the
; list. To remove an item from the list
we need only
; store a zero (NULL) over the top of its pointer entry
; in the list.
RemoveItem proc
; Once again
we use the repeat macro to automatically generate a chain
; of compare
branch
and remove code sequences for each possible item
; in the list.
ItemCnt = 0
repeat MaxWeight
local NotThisOne
cmp bx
[di+ItemCnt]
jne NotThisOne
mov word ptr [di+ItemCnt]
NULL
ret
NotThisOne:
ItemCnt = ItemCnt+2
endm
ret
RemoveItem endp
; InsertItem- BX contains a pointer to an item
DI contains a pointer to
; and item list. This routine searches through the list for
; the first empty spot and copies the value in BX to that point.
; It returns the carry set if it succeeds. It returns the
; carry clear if there are no empty spots available.
InsertItem proc
ItemCnt = 0
repeat MaxWeight
local NotThisOne
cmp word ptr [di+ItemCnt]
0
jne NotThisOne
mov [di+ItemCnt]
bx
stc
ret
NotThisOne:
ItemCnt = ItemCnt+2
endm
clc
ret
InsertItem endp
; LongDesc- Long description of an item.
; DI points at an item - print the long description of it.
LongDesc proc
push di
test di
di
jz NoDescription
mov di
[di].item.LongDesc
puts
putcr
NoDescription: pop di
ret
LongDesc endp
; ShortDesc- Print the short description of an object.
; DI points at an item (possibly NULL). Print the short description for it.
ShortDesc proc
push di
test di
di
jz NoDescription
mov di
[di].item.ShortDesc
puts
putcr
NoDescription: pop di
ret
ShortDesc endp
; Describe: "CurRoom" points at the current room. Describe it and its
; contents.
Describe proc
push es
push bx
push di
mov di
ds
mov es
di
mov bx
CurRoom
mov di
[bx].room.Description
print
byte "You are currently "
0
puts
putcr
print
byte "Here you find the following:"
cr
lf
0
; For each possible item in the room
print out the long description
; of that item. The repeat macro generates a code sequence for each
; possible item that could be in this room.
ItemCnt = 0
repeat MaxWeight
mov di
[bx].room.ItemList[ItemCnt]
call LongDesc
ItemCnt = ItemCnt+2
endm
pop di
pop bx
pop es
ret
Describe endp
; Here is the main program
that actually plays the game.
Main proc
mov ax
dseg
mov ds
ax
mov es
ax
meminit
print
byte cr
lf
lf
lf
lf
lf
byte "Welcome to "
'"MADVENTURE"'
cr
lf
byte 'If you need help
type the command "HELP"'
byte cr
lf
0
RoomLoop: dec CurScore ;One point for each move.
jnz NotOverYet
; If they made too many moves without dropping anything properly
boot them
; out of the game.
print
byte "WHOA! You lost! You get to join the legions of "
byte "the totally lame"
cr
lf
byte 'who have failed at "MADVENTURE"'
cr
lf
0
jmp Quit
; Okay
tell 'em where they are and get a new command from them.
NotOverYet: putcr
call Describe
print
byte cr
lf
byte "Command: "
0
lesi InputLine
gets
strupr ;Ignore case by converting to U.C.
; Okay
process the command. Note that we don't actually check to see
; if there is a properly formed sentence. Instead
we just look to see
; if any important keywords are on the line. If they are
the pattern
; matching routines load the appropriate values into the noun and verb
; variables (nouns: north=1
south=2
east=3
west=4
lime=5
beer=6
; card=7
sign=8
program=9
homework=10
money=11
form=12
coupon=13;
; verbs: go=1
get=2
drop=3
inventory=4
quit=5
help=6).
;
; This code uses the noun and verb variables as indexes into a two
; dimensional array whose elements contain the address of the code
; to process the given command. If a given command does not make
; any sense (e.g.
"go coupon") the entry in the table points at the
; bad command code.
mov Noun
0
mov Verb
0
mov NounPtr
0
ldxi VerbPat
xor cx
cx
match
lesi InputLine
ldxi NounPat
xor cx
cx
match
; Okay
index into the command table and jump to the appropriate
; handler. Note that we will cheat and use a 14x8 array. There
; are really only seven verbs
not eight. But using eight makes
; things easier since it is easier to multiply by eight than seven.
mov si
CurRoom ;The commands expect this here.
mov bx
Noun
shl bx
3 ;Multiply by eight.
add bx
Verb
shl bx
1 ;Multiply by two - word table.
jmp cseg:jmptbl[bx]
; The following table contains the noun x verb cross product.
; The verb values (in each row) are the following:
;
; NONE GO GET DROP INVNTRY QUIT HELP unused
; 0 1 2 3 4 5 6 7
;
; There is one row for each noun (plus row zero
corresponding to no
; noun found on line).
jmptbl word Bad ;No noun
no verb
word Bad ;No noun
GO
word Bad ;No noun
GET
word Bad ;No noun
DROP
word DoInventory ;No noun
INVENTORY
word QuitGame ;No noun
QUIT
word DoHelp ;No noun
HELP
word Bad ;N/A
NorthCmds word Bad
GoNorth
Bad
Bad
Bad
Bad
Bad
Bad
SouthCmds word Bad
GoSouth
Bad
Bad
Bad
Bad
Bad
Bad
EastCmds word Bad
GoEast
Bad
Bad
Bad
Bad
Bad
Bad
WestCmds word Bad
GoWest
Bad
Bad
Bad
Bad
Bad
Bad
LimeCmds word Bad
Bad
GetItem
DropItem
Bad
Bad
Bad
Bad
BeerCmds word Bad
Bad
GetItem
DropItem
Bad
Bad
Bad
Bad
CardCmds word Bad
Bad
GetItem
DropItem
Bad
Bad
Bad
Bad
SignCmds word Bad
Bad
GetItem
DropItem
Bad
Bad
Bad
Bad
ProgramCmds word Bad
Bad
GetItem
DropItem
Bad
Bad
Bad
Bad
HomeworkCmds word Bad
Bad
GetItem
DropItem
Bad
Bad
Bad
Bad
MoneyCmds word Bad
Bad
GetItem
DropItem
Bad
Bad
Bad
Bad
FormCmds word Bad
Bad
GetItem
DropItem
Bad
Bad
Bad
Bad
CouponCmds word Bad
Bad
GetItem
DropItem
Bad
Bad
Bad
Bad
; If the user enters a command we don't know how to process
print an
; appropriate error message down here.
Bad: printf
byte "I'm sorry
I don't understand how to '%s'\n"
0
dword InputLine
jmp NotOverYet
; Handle the movement commands here.
; Movements are easy
all we've got to do is fetch the NORTH
SOUTH
; EAST
or WEST pointer from the current room's data structure and
; set the current room to that address. The only catch is that some
; moves are not legal. Such moves have a NULL (zero) in the direction
; field. A quick check for this case handles illegal moves.
GoNorth: mov si
[si].room.North
jmp MoveMe
GoSouth: mov si
[si].room.South
jmp MoveMe
GoEast: mov si
[si].room.East
jmp MoveMe
GoWest: mov si
[si].room.West
MoveMe: test si
si ;See if move allowed.
jnz SetCurRoom
printf
byte "Sorry
you cannot go in this direction."
byte cr
lf
0
jmp RoomLoop
SetCurRoom: mov CurRoom
si ;Move to new room.
jmp RoomLoop
; Handle the GetItem command down here. At this time the user
; has entered GET and some noun that the player can pick up.
; First
we will make sure that item is in this room.
; Then we will check to make sure that picking up this object
; won't overload the player. If these two conditions are met
; we'll transfer the object from the room to the player.
GetItem: mov bx
NounPtr ;Ptr to item user wants.
mov si
CurRoom
lea di
[si].room.ItemList ;Ptr to item list in di.
call CheckPresence ;See if in room.
jc GotTheItem
printf
byte "Sorry
that item is not available here."
byte cr
lf
0
jmp RoomLoop
; Okay
see if picking up this object will overload the player.
GotTheItem: mov ax
[bx].Item.Weight
add ax
CurWeight
cmp ax
MaxWeight
jbe WeightOkay
printf
byte "Sorry
you are already carrying too many items "
byte "to safely carry\nthat object\n"
0
jmp RoomLoop
; Okay
everything's cool
transfer the object from the room to the user.
WeightOkay: mov CurWeight
ax ;Save new weight.
call RemoveItem ;Remove item from room.
lea di
ItemsOnHand ;Ptr to player's list.
call InsertItem
jmp RoomLoop
; Handle dropped objects down here.
DropItem: lea di
ItemsOnHand ;See if the user has
mov bx
NounPtr ; this item on hand.
call CheckPresence
jc CanDropIt1
printf
byte "You are not currently holding that item\n"
0
jmp RoomLoop
; Okay
let's see if this is the magic room where this item is
; supposed to be dropped. If so
award the user some points for
; properly figuring this out.
CanDropIt1: mov ax
[bx].item.key
cmp ax
CurRoom
jne JustDropIt
; Okay
success! Print the winning message for this object.
mov di
[bx].item.WinDesc
puts
putcr
; Award the user some points.
mov ax
[bx].item.value
add CurScore
ax
; Since the user dropped it
they can carry more things now.
mov ax
[bx].item.Weight
sub CurWeight
ax
; Okay
take this from the user's list.
lea di
ItemsOnHand
call RemoveItem
; Keep track of how may objects the user has successfully dropped.
; When this counter hits zero
the game is over.
dec TotalCounter
jnz RoomLoop
printf
byte "Well
you've found where everything goes "
byte "and your score is %d.\n"
byte "You might want to play again and see if "
byte "you can get a better score.\n"
0
dword CurScore
jmp Quit
; If this isn't the room where this object belongs
just drop the thing
; off. If this object won't fit in this room
ignore the drop command.
JustDropIt: mov di
CurRoom
lea di
[di].room.ItemList
call InsertItem
jc DroppedItem
printf
byte "There is insufficient room to leave "
byte "that item here.\n"
0
jmp RoomLoop
; If they can drop it
do so. Don't forget we've just unburdened the
; user so we need to deduct the weight of this object from what the
; user is currently carrying.
DroppedItem: lea di
ItemsOnHand
call RemoveItem
mov ax
[bx].item.Weight
sub CurWeight
ax
jmp RoomLoop
; If the user enters the INVENTORY command
print out the objects on hand
DoInventory: printf
byte "You currently have the following items in your "
byte "possession:"
cr
lf
0
mov di
ItemsOnHand[0]
call ShortDesc
mov di
ItemsOnHand[2]
call ShortDesc
mov di
ItemsOnHand[4]
call ShortDesc
mov di
ItemsOnHand[6]
call ShortDesc
printf
byte "\nCurrent score: %d\n"
byte "Carrying ability: %d/4\n\n"
0
dword CurScore
CurWeight
inc CurScore ;This command is free.
jmp RoomLoop
; If the user requests help
provide it here.
DoHelp: printf
byte "List of commands:"
cr
lf
lf
byte "GO {NORTH
EAST
WEST
SOUTH}"
cr
lf
byte "{GET
DROP} {LIME
BEER
CARD
SIGN
PROGRAM
"
byte "HOMEWORK
MONEY
FORM
COUPON}"
cr
lf
byte "SHOW INVENTORY"
cr
lf
byte "QUIT GAME"
cr
lf
byte "HELP ME"
cr
lf
lf
byte "Each command costs you one point."
cr
lf
byte "You accumulate points by picking up objects and "
byte "dropping them in their"
cr
lf
byte " appropriate locations."
cr
lf
byte "If you drop an item in its proper location
it "
byte "disappears from the game."
cr
lf
byte "The game is over if your score drops to zero or "
byte "you properly place"
cr
lf
byte " all items."
cr
lf
byte 0
jmp RoomLoop
; If they quit prematurely
let 'em know what a wimp they are!
QuitGame: printf
byte "So long
your score is %d and there are "
byte "still %d objects unplaced\n"
0
dword CurScore
TotalCounter
Quit: ExitPgm ;DOS macro to quit program.
Main endp
cseg ends
sseg segment para stack 'stack'
stk db 1024 dup ("stack ")
sseg ends
zzzzzzseg segment para public 'zzzzzz'
LastBytes db 16 dup (?)
zzzzzzseg ends
end Main
|
Table of Content | |
Chapter Sixteen: Pattern Matching
(Part 12)
29 SEP 1996