-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathZILCourse.tex
More file actions
1900 lines (1627 loc) · 79.6 KB
/
ZILCourse.tex
File metadata and controls
1900 lines (1627 loc) · 79.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
%% LyX 2.3.1-1 created this file. For more info, see http://www.lyx.org/.
%% Do not edit unless you really know what you are doing.
\documentclass[english,openany]{scrbook}
\usepackage{fontspec}
\usepackage[a4paper]{geometry}
\geometry{verbose,tmargin=2cm,bmargin=2cm,lmargin=2.5cm,rmargin=2cm,headheight=1cm,headsep=0.5cm,footskip=1cm}
\usepackage{fancyhdr}
\pagestyle{fancy}
\setcounter{secnumdepth}{3}
\setcounter{tocdepth}{3}
\usepackage[unicode=true,
bookmarks=true,bookmarksnumbered=false,bookmarksopen=false,
breaklinks=false,pdfborder={0 0 0},pdfborderstyle={},backref=false,colorlinks=false]
{hyperref}
\hypersetup{pdftitle={ZIL},
pdfauthor={Marc S. Blank},
pdfsubject={ZIL Course},
pdfkeywords={Infocom ZIL Zork IF IntFiction Interactive Fiction}}
\makeatletter
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LyX specific LaTeX commands.
\providecommand\textquotedblplain{%
\bgroup\addfontfeatures{Mapping=}\char34\egroup}
\makeatother
\usepackage{listings}
\lstset{xleftmargin=25pt}
\usepackage{polyglossia}
\setdefaultlanguage[variant=american]{english}
\begin{document}
\title{ZIL}
\author{Marc S. Blank}
\date{October 1982}
\publishers{INFOCOM INTERNAL DOCUMENT - NOT FOR DISTRIBUTION}
\maketitle
\tableofcontents{}
\chapter{The Z System}
The Z System is composed of the various modules which are used to
create INTERLOGIC games. At the highest level is Z Implementation
Language (ZIL), which is an interpreted language running under MDL.
Since ZIL is a MDL subsystem, all of the debugging features of MDL
itself can be used in the creation and debugging of INTERLOGIC games.
ZIL code is run through the ZIL Compiler (ZILCH) producing Z Assembly
Language code which is, in turn, assembled by the Z Assembler Program
(ZAP) into machine-independent Z-codes. These Z-codes can be run on
any target machine which sports a Z-machine emulator (ZIP).
The author of an INTERLOGIC game need not be familiar with the workings
of the compiler, assembler, or emulators to a great extent. The compiler
does have a few idiosyncrasies, however, which will be noted as necessary.
The remainder of this manual describes MDL and ZIL, starting with
simple concepts but eventually describing the full power of the system.
\chapter{Writing in ZIL}
MDL (pronounced MUDDLE) is the host language for ZIL and knowledge
of MDL is an advantage in learning the ZIL system. However, there
are a number of important restrictions and simplifications built into
ZIL; it is therefore important that even seasoned MDLers read this
section.
\section{ZIL TYPEs}
The ZIL world contains a relatively small number of classes of objects
and these classes are called TYPEs. Every operation in ZIL expects
to receive objects of specific TYPEs as arguments. For simplicity,
'a FOO' will be used as a shorthand for 'an object of TYPE FOO'.
\subsection{FORM}
FORMs are represented as a collection of other objects (of any type)
surrounded by balanced angle-brackets. FORMs are used to perform the
various operations in the ZIL world. These operations may be either
built-in subroutines or user-defined ROUTINEs. Here are some FORMs:
\begin{description}
\item [{\texttt{<+~1~2>}}] This adds the integers 1 and 2.
\item [{\texttt{<SET~A~10>}}] This sets the local variable A to the integer
10.
\item [{\texttt{<G=?~<+~4~1>~10>}}] This returns TRUE if the sum of
4 and 1 is greater-than or equal to 10.
\item [{\texttt{<SETG~FOO~<{*}~,COUNTER~<RANDOM~4>\textcompwordmark >\textcompwordmark >}}] This
sets the global variable FOO to the product of the value of the global
variable COUNTER and a random number from 1 to 4.
\end{description}
The first element of a form indicates the operation to be performed
and all other elements are the arguments to that operation.
\subsection{Prefix Notation}
Prefix notation, sometimes referred to as Polish notation, is different
from the infix notation of ordinary arithmetic and reverse-Polish
notation of some calculators. Below are some examples of equivalent
expressions in infix and prefix notation:
\begin{verbatim}
4 + 7
<+ 4 7>
8 - 6
<- 8 6>
9 + (4 * 6 - 6 / 3)
<+ 9 <- <* 4 6> </ 6 3>>>
\end{verbatim}
It may take some time to become accustomed to prefix notation. One
thing to keep in mind is the balancing of brackets. Notice that with
prefix notation an operator can take an arbitrary number of arguments
and that the nesting is never ambiguous (i.e.~the parentheses of
infix notation are not necessary). In addition, operator precedence
can be completely ignored.
\subsection{Nested Expressions}
As can be seen from the previous examples, it is possible to nest
expressions. In fact, there is no limit on the depth of the nesting.
FORMs are evaluated from left to right, top to bottom.
\subsection{FIX (Integer)}
Objects of TYPE FIX represent integers in the range -32767 to 32767
and are always represented in decimal. Integers of greater magnitude
are illegal. Floating point numbers are not allowed in ZIL.
The following operations require two arguments, both FIXes: + (addition),
- (subtraction), {*} (multiplication), / (division), and MOD (modulus).
Each returns the appropriate FIX.
In addition, ABS requires one FIX and returns its absolute value,
and RANDOM, given a FIX, returns a FIX between one and that FIX, inclusive.
There are three predicates which operate on pairs of FIXes: L? (less
than), G? (greater than), and ==? (equal to). In addition, the predicate
0? (equal to zero?) takes a single FIX. All predicates return a 'true'
value or a 'false' value. See the section below on conditionals for
a full description of truth in the ZIL sense.
Here are some examples of the use of FIXes:
\begin{verbatim}
<+ 10 20>
<+ </ 10 2> 1>
\end{verbatim}
\subsection{ATOM (Variable)}
ATOMs can be thought of as variables. Their names can be almost anything,
but safest is a combination of capital letters, numbers, hyphens,
question marks, and dollar signs (e.g.~FOOBAR, V-WALK, V-\$VERIFY).
ATOMs can be thought of as coming in two varieties: LOCAL and GLOBAL.
LOCAL ATOMs are used as temporary variables within ROUTINEs (i.e.~pieces
of code). A LOCAL ATOM can be used in any number of ROUTINEs and there
are NO conflicts when one routine with LOCAL ATOM X calls another
routine with its own LOCAL ATOM X. Each LOCAL ATOM must be explicitly
created within the ROUTINE in which it is used. The mechanism by which
this is done is described in a following section. To set the value
of a LOCAL ATOM within a ROUTINE, one says:
\begin{verbatim}
<SET atom-name value>
\end{verbatim}
where 'atom-name' and 'value' correspond to an ATOM and an arbitrary
value. To retrieve the value of a LOCAL ATOM, one says:
\begin{verbatim}
.atom-name
\end{verbatim}
(period followed by 'atom-name') where 'atom-name' is the ATOM whose
value is required.
GLOBAL ATOMs have values which correspond to: rooms and objects, ROUTINEs,
flags, properties, variables and tables. The value of a GLOBAL ATOM
is accessible to all ROUTINEs at all times. To set the value of a
GLOBAL ATOM, one says:
\begin{verbatim}
<SETG atom-name value>
\end{verbatim}
analogously with SET. To retrieve the value of a GLOBAL ATOM, one
says:
\begin{verbatim}
,atom-name
\end{verbatim}
(comma followed by 'atom-name').
As will be seen later, OBJECTs, flags, and properties are set up during
OBJECT creation and are not explicitly SETG'ed.
\subsection{STRING}
STRINGs are what would be called 'character strings' in other languages.
They are used exclusively for printed text. They are represented by
a series of characters surrounded by double-quotes. If a double-quote
is necessary in the STRING, it must be preceded by a backslash. Here
are some strings, followed by their printed representation:
\begin{verbatim}
"Hello, there!" --> Hello, there!
"The man says \"Foobar!\"" --> The man says "Foobar!"
\end{verbatim}
\subsection{LIST}
LISTs are represented as a series of other objects surrounded by matching
parentheses. These are used within ROUTINEs for purposes of clarity
(seeing angle-brackets everywhere would be downright disorienting).
Their use will be described later.
\subsection{TABLE}
A TABLE is what might be referred to as an array in other languages.
TABLEs are structures containing arbitrary elements (e.g.~OBJECTs,
FIXes, STRINGs, etc.). They must be created at 'top-level' (i.e.~NOT
within a ROUTINE), as follows:
\begin{verbatim}
<TABLE element element element ...>
\end{verbatim}
A special kind of TABLE whose initial element is the number of other
elements in the TABLE is created as follows:
\begin{verbatim}
<LTABLE element element element ...>
\end{verbatim}
Note that the first element in this declaration is NOT the number
of other elements; that number will be automatically generated.
In ROUTINEs which need to know the length of a TABLE (e.g.~a general
routine which must search through a TABLE or one which randomly picks
an element from within a TABLE), LTABLE must be used. For TABLEs of
known size, LTABLE is not necessary.
By convention, the first element of a TABLE is element zero. To retrieve
an element from a TABLE, use:
\begin{verbatim}
<GET table element-number>
\end{verbatim}
To place an element within a TABLE, use:
\begin{verbatim}
<PUT table element-number>
\end{verbatim}
\subsection{OBJECTs}
OBJECTs correspond, in the game environment, to objects, rooms, and
characters (including the player). The creation of OBJECTs is described
below, but the operations used for their manipulation are described
here.
Any OBJECT may have, at most, one container and any number of contents.
An OBJECT's initial container is determined when it is created. The
location of an OBJECT can be returned by:
\begin{verbatim}
<LOC object>
\end{verbatim}
Similarly, one may determine whether or not an OBJECT is in another
particular OBJECT by saying:
\begin{verbatim}
<IN? object-1 object-2>
\end{verbatim}
which checks whether or not object-1 is contained within object-2.
OBJECTs can be placed inside other OBJECTs using MOVE:
\begin{verbatim}
<MOVE object-1 object-2>
\end{verbatim}
which moves object-1 into object-2. An OBJECT may be moved into never-never
land (i.e.~it can be made to have no container, equivalent to being
nowhere) with REMOVE:
\begin{verbatim}
<REMOVE object>
\end{verbatim}
To find the contents of a given OBJECT is a bit unusual. The 'first'
OBJECT contained in a given OBJECT can be found with:
\begin{verbatim}
<FIRST? object>
\end{verbatim}
Note that FIRST? ends with a question mark, indicating that it is
a predicate. If there is nothing contained in 'object', FIRST? returns
'false'.
Other OBJECTs within a given OBJECT can be found using NEXT? as follows:
\begin{verbatim}
<NEXT? object>
\end{verbatim}
NEXT? is defined thus: it returns the 'next' OBJECT which is ALSO
contained in the OBJECT's container. Ain't it confusing? Notice that
like FIRST?, NEXT? is a predicate. If there is no 'next' OBJECT, it
returns 'false'. As an example, let's assume that there is an object
X containing objects Y and Z. FIRST? of X will be Y. NEXT? of Y will
be Z. NEXT? of Z will be 'false'. (NEXT? of X is unknowable from this
example.)
OBJECTs may also have up to 32 condition flags, most of which are
designed into the substrate of the game. Among these are OPENBIT (whether
a door or container is presently open), TAKEBIT (whether an OBJECT
can be taken), DOORBIT (whether an OBJECT is a door), and ONBIT (whether
an OBJECT is a source of light). The initial state of these flags
is determined during OBJECT creation, and all of the substrate-contained
flags are described later.
To check whether a given flag is 'on', use:
\begin{verbatim}
<FSET? object flag>
\end{verbatim}
To set a flag (i.e.~turn it on) and clear a flag (i.e.~turn it off),
use:
\begin{verbatim}
<FSET object flag>
<FCLEAR object flag>
\end{verbatim}
As was noted earlier, OBJECTs and flags are GLOBAL ATOMs; therefore,
the following might appear in game code:
\begin{verbatim}
<FSET ,AIRLOCK-DOOR ,OPENBIT>
\end{verbatim}
This would cause the AIRLOCK-DOOR to be considered 'open'. The ramifications
of this particular example would include the ability to go through
the door without obstruction and the ability to look through the door.
The ramifications of the various flags is discussed below.
OBJECTs also have up to 31 'properties', whose values are explicitly
manipulated relatively infrequently during game writing. Among these
properties are SIZE (weight of an OBJECT), CAPACITY (total weight
that a container can hold), ACTION (ROUTINE to be called for special
case actions), and LDESC (long description). These properties are
set up during OBJECT definition and are described completely later
on. It should be noted, however, that new properties cannot be added
to an OBJECT while a game is running. If a property is needed for
a particular OBJECT, it must be initialized when the OBJECT is defined.
Retrieving the value of a property for a given OBJECT is done with:
\begin{verbatim}
<GETP object property>
\end{verbatim}
Similarly, setting the value of a property for a given OBJECT is done
with:
\begin{verbatim}
<PUTP object property value>
\end{verbatim}
Like condition flags, properties are GLOBAL ATOMs. In the context
of GETP and PUTP, their names must be prefixed by the letter P and
a question-mark. In other words:
\begin{verbatim}
<GETP ,RUSTY-KNIFE ,P?SIZE>
\end{verbatim}
would retrieve the SIZE property of the RUSTY-KNIFE.
If, in a call to GETP, the supplied OBJECT does not have the supplied
property defined, the result is 'false'. This default result can be
altered, if it is so desired, by placing a statement like this in
the ZIL 'load file' (see further on):
\begin{verbatim}
<PROPDEF property-name default-value>
\end{verbatim}
where 'property-name' is the name of the property for which a default
will exist, and 'default-value' is the value which GETP will return
if the property is not defined for a given OBJECT. Here's an example:
\begin{verbatim}
<PROPDEF SIZE 5>
\end{verbatim}
\section{Conditional Expressions}
One cannot discuss conditional expressions without explaining the
meaning of 'truth'. ZIL has a rather simplistic view of truth: anything
which is not zero is true. For historical reasons, a distinction is
made between 'false' and zero and it is the cause of some confusion.
GLOBAL ATOMs are frequently used to save the state of a global condition
(e.g.~SUIT-ON? might be 'true' or 'false' depending on whether one
is wearing the spacesuit). Two special tokens are used to mean 'true'
and 'false' in these cases: T and <> (open followed by closed angle
bracket). In ZIL code, therefore, one should use:
\begin{verbatim}
<SETG SUIT-ON? T>
<SETG SUIT-ON? <>>
\end{verbatim}
rather than:
\begin{verbatim}
<SETG SUIT-ON? 1>
<SETG SUIT-ON? 0>
\end{verbatim}
This distinction makes code more understandable to the casual observer:
in the former example, it is clear that SUIT-ON? is a condition flag.
In the latter, it is unclear whether SUIT-ON? is a condition flag
or a variable whose value is a FIX.
Some of the most common operations in ZIL are predicates and return
one of two values: true (not zero, usually one) and false (zero).
We have already described some of these: L?, G?, and ==? (for arithmetic)
and IN? and FSET? (for OBJECTs).
The other operations dealing with conditionals are mentioned here.
\subsection{EQUAL?}
EQUAL? takes from two to four arguments and determines whether the
first is equal to any of the other arguments.
\begin{verbatim}
<EQUAL? .OBJ ,LANTERN ,CANDLES>
\end{verbatim}
The example checks to see whether the value of the LOCAL ATOM OBJ
is either the LANTERN or the CANDLES (presumably these are OBJECTs).
EQUAL? returns T or <> (i.e.~'true' or 'false'). It can be used with
any TYPEs of arguments. Thus,
\begin{verbatim}
<EQUAL? .NUM 1 2 3>
\end{verbatim}
could be used to check whether or not the LOCAL ATOM NUM was equal
to one, two, or three.
\subsection{NOT}
NOT takes one argument. If it is not 'false', it returns 'false'.
If it IS 'false', it returns 'true'. Thus, if the LOCAL ATOM OBJ is
12, then
\begin{verbatim}
<NOT .OBJ>
\end{verbatim}
will return 'false'. To restate in another way, NOT returns 'true'
only if its argument was 'false'. It returns 'false' in every other
case.
\subsection{AND}
AND takes any number of expressions and evaluates them from left to
right. It returns 'true' only if ALL of the expressions are 'true'.
Otherwise it returns 'false'. For example,
\begin{verbatim}
<AND <G? .NUM 10> <L? .NUM 20>>
\end{verbatim}
returns 'true' if the value of LOCAL ATOM NUM is BOTH greater than
10 and less than 20. Otherwise, it returns 'false'.
\subsection{OR}
OR, similar to AND, takes any number of expressions and evaluates
them from left to right. However, it returns 'true' if ANY of the
expressions is 'true'. Otherwise, it returns 'false'. For example,
\begin{verbatim}
<OR <L? .NUM 11> <G? .NUM 19>>
\end{verbatim}
returns 'true' if the value of the LOCAL ATOM NUM is EITHER less than
11 OR greater than 19. Otherwise, it returns 'false'. (This is the
opposite of the example for AND.)
\subsection{COND}
By now, those unfamiliar with MDL have had about enough of conditionals
returning 'true' and 'false' and are probably wondering just who it
is who decides to do something depending on those values. The answer
is COND, probably the most commonly used operation in the language.
The structure of a COND expression is:
\begin{verbatim}
<COND (predicate expression expression ...)
(predicate expression expression ...)
...
(predicate expression expression ...)>
\end{verbatim}
First note that COND, like AND and OR, takes any number of arguments,
which are all LISTs. Each LIST contains at least two elements: a predicate
(i.e.~conditional expression) and something(s) to do if that predicate
returns 'true'.
COND works like this: starting with the first LIST, it evaluates the
predicate. If it returns 'true', then ALL of the remaining expressions
are evaluated in turn, and the COND itself returns the value of the
last of those expressions. If it returns 'false', then the next LIST
is examined in the same way. If none of the predicates returns 'true',
then the COND itself returns 'false'. Here is a typical example of
a COND expression:
\begin{verbatim}
<COND (<VERB? TAKE>
<TELL "Your hand passes through its object." CR>)
(<VERB? SCORE>
<TELL "How can you think of your score now?" CR>)>
\end{verbatim}
This COND may as well have been lifted directly from one of the ZORK
games. VERB?, one would assume, is a predicate of some sort, returning
'true' or 'false' depending on its argument(s). In fact, it is conventional
to name ROUTINEs which return only 'true' or 'false' with a trailing
question mark. It happens that VERB? compares its argument(s) with
the parser's idea of which action was requested by the player (see
far later on for a description of the parser's ideas about everything).
If they match, it returns 'true'. Otherwise, you guessed it. The TELL
operation is used for constructing printed text. Not surprisingly,
the 'CR' means that a carriage return is printed after the STRING
(much more on TELL later).
It has been said that ZORK is one giant COND. This may be exaggeration,
but it hits near the mark. Here are some more partial examples:
\begin{verbatim}
<COND (<NOT ,SUIT-ON?>
<JIGS-UP "You can't breathe vacuum and thus die.">)>
\end{verbatim}
Here is a case in which NOT is frequently used. In this example, the
ROUTINE JIGS-UP is called if the condition flag SUIT-ON? is NOT 'true'
(i.e.~the suit is not being worn). JIGS-UP means more or less that.
Predicates can be (and often are) built up using ANDs and ORs. Thus,
\begin{verbatim}
<COND (<AND <NOT <EQUAL? .OBJ ,WEASEL ,RAT-ANT>>
,SUIT-ON?>
<TELL "He spits on your suit." CR>)>
\end{verbatim}
The SET and SETG operations can often be nested directly into conditional
expressions (sometimes the compiler will do the wrong thing), like
this:
\begin{verbatim}
<COND (<SET OBJ <FIRST? .OBJ>>
<TELL "Something or other." CR>)>
\end{verbatim}
\chapter{ROUTINEs}
ROUTINEs are user-constructed subroutines which are the backbone of
the INTERLOGIC games. The main loop, parser, verb handlers, special
case handlers for OBJECTs, and the like are all ROUTINEs. Each ROUTINE
has a 'name', which is a GLOBAL ATOM.
A ROUTINE is defined as follows:
\begin{verbatim}
<ROUTINE name (argument-list ...) expression expression ...>
\end{verbatim}
where 'name' is a legal ATOM name, 'argument list' will be described
later on, and 'expression' is any legal ZIL expression. When a ROUTINE
is called, each of the expressions is evaluated in turn and the result
of the last evaluation is the value of the call to the ROUTINE.
\section{Argument List}
The argument list is a LIST which can be thought of as coming in three
parts, none of which are required. The first part are LOCAL ATOMs
corresponding to required arguments to the ROUTINE. Some examples:
\begin{verbatim}
<ROUTINE MOVE-ALL (LOC1 LOC2) ....>
<ROUTINE MOUSE-F () ...>
\end{verbatim}
In the first example, a ROUTINE named MOVE-ALL is defined to take
exactly two arguments. Within the context of MOVE-ALL, the LOCAL ATOMs
LOC1 and LOC2 will have the value of the arguments passed to MOVE-ALL.
The ROUTINE MOUSE-F takes no arguments. Here is a typical call to
MOVE-ALL:
\begin{verbatim}
<MOVE-ALL .CONT ,TRASH-BIN>
\end{verbatim}
In this case, LOC1 (in MOVE-ALL) will have the value of the LOCAL
ATOM CONT in the calling ROUTINE and LOC2 (again, in MOVE-ALL) will
have the TRASH-BIN as a value. Note that calling a ROUTINE is no different
than calling a built-in subroutine such as COND, +, or FCLEAR.
In addition to required arguments, ROUTINEs may have optional arguments
as well. Indicate that all further arguments are optional by placing
the STRING \textquotedbl OPTIONAL\textquotedbl{} after the ATOMs
(if any) representing the required ones. After the \textquotedbl OPTIONAL\textquotedbl ,
each optional argument is indicated by a LIST of two elements: a LOCAL
ATOM and a default value (if the argument is not passed by the calling
ROUTINE). For example,
\begin{verbatim}
<ROUTINE ALREADY (STR "OPTIONAL" (OBJ <>)) ...>
\end{verbatim}
ALREADY takes one or two arguments, only one of which (STR) is required.
The second (OBJ) is not required and, if not passed, will be SET to
'false'.
\begin{verbatim}
<ALREADY "open">
<ALREADY "open" ,AIRLOCK-DOOR>
\end{verbatim}
In the first example above, STR's value will be \textquotedbl open\textquotedbl{}
and OBJ's will be 'false'. In the second, OBJ's value will be the
AIRLOCK-DOOR. This ROUTINE is used in STARCROSS, and the result of
these examples would be:
\begin{verbatim}
It is already open.
The airlock door is already open.
\end{verbatim}
Oh, why not. Here's the ROUTINE for ALREADY in its full glory:
\begin{verbatim}
<ROUTINE ALREADY (STR "OPTIONAL" (OBJ <>))
<COND (.OBJ <TELL "The " D .OBJ>)
(T <TELL "It">)>
<TELL " is already " .STR "." CR>>
\end{verbatim}
At the risk of getting too far ahead of the game, it should be clear
that TELL is something which prints text; in fact, it cobbles together
arbitrary numbers of things into sentences. It takes, as arguments,
STRINGs (either 'in person' or as the value of an ATOM), the ATOM
CR (which means print a carriage return/line feed), and a few special
items. One of these is the ATOM D followed by something which had
better be an OBJECT (represented as the value of a LOCAL or GLOBAL
ATOM). It means to print the 'short description' of the OBJECT. In
the example above, \textquotedbl airlock door\textquotedbl{} would
have been defined as the short description of the AIRLOCK-DOOR OBJECT.
If you understand this example, you're doing just fine.
The third part of the argument list (also not required) refer to other
LOCAL ATOMs which you wish to use as temporary variables within the
ROUTINE. Any LOCAL ATOM you use in a ROUTINE MUST be indicated somewhere
within the argument list. To indicate the place in the argument list
at which temporary variables start, place the STRING \textquotedbl AUX\textquotedbl{}
at that point. This is followed by any number of either ATOM names
or LISTs (exactly like those for optional arguments) containing an
ATOM name and a default (i.e.~initial) value. It is illegal to retrieve
the value of a LOCAL ATOM unless it has either a default value in
the argument list or has been explicitly SET within the ROUTINE.
\begin{verbatim}
<ROUTINE COUNT (OBJ "AUX" (CNT 0)) ...>
\end{verbatim}
This ROUTINE which, incidentally, takes exactly one argument, initializes
the LOCAL ATOM CNT to zero. Presumably CNT is used to count something,
and somewhere in the innards of the ROUTINE are statements like:
\begin{verbatim}
<SET CNT <+ .CNT 1>>
\end{verbatim}
which would increment the value of CNT.
\section{Names for ATOMs}
It is useful, both for yourself and others with the temerity to look
at your code, for you to choose your ROUTINE, variable, etc. names
to be somewhat mnemonic. If one has a dirty rag in a game, it would
be best to call it DIRTY-RAG. A ROUTINE which counts grues might be
called COUNT-GRUES. It is common for ROUTINEs to have ATOMS with names
like OBJ, CNT, and DIR to refer to OBJECTs, counters, and directions.
The easier for you (and others) to remember what things are called,
the easier will be the writing and debugging. I remember some anguish
over not being able to find the brown rod in the STARCROSS listing
because it had (for historical reasons) been named CHIEF-KEY, unlike
the others which were called GREEN-KEY, RED-KEY, and other colorful
names.
\section{Looping}
Now and again, one wants one's ROUTINE to go into some kind of loop.
Searching through TABLEs comes to mind as a prime example. This is
done as follows:
\begin{verbatim}
<REPEAT () expression expression ...>
\end{verbatim}
Note that the empty-looking LIST is required, even though it seems
to have no purpose (the MDLers reading this are, no doubt, smirking).
The expressions are arbitrary and when the last of them is evaluated
the whole thing begins over again, time without end. Unless, somewhere
within, is a RETURN statement. RETURN (no arguments) exits a loop.
Here's a useless loop:
\begin{verbatim}
<REPEAT ()
<COND (<FSET? .OBJ ,INVISIBLE>
<SET CNT <+ .CNT 1>>)>
<COND (<NOT <SET OBJ <NEXT? .OBJ>>>
<RETURN>)>>
\end{verbatim}
Without describing the meaning of NEXT? and hoping that in the argument
list of whatever ROUTINE this is embedded within there is an initialization
of the ATOMs CNT and OBJ, this section of code might count the number
of invisible objects somewhere.
\section{Exiting a ROUTINE}
ROUTINEs evaluate all of the expressions after the argument list in
order and return the result of the last of these. One can, however,
at any point within a ROUTINE, cause the ROUTINE to return a specific
value using one of the following:
\begin{verbatim}
<RTRUE>
<RFALSE>
<RETURN anything>
\end{verbatim}
Each of these causes the ROUTINE to immediately stop its execution
are return 'true', 'false', and 'anything', respectively. RTRUE and
RFALSE turn out to be enormously important. A warning: RETURN used
within a REPEAT only causes the REPEAT to terminate. RTRUE and RFALSE,
used ANYWHERE, cause the entire ROUTINE to terminate.
\section{Restrictions in ROUTINEs}
The most important of these is that a ROUTINE may take, at most, three
arguments (any mix of required and optional). In addition, there can
only be sixteen LOCAL ATOMs in a ROUTINE. These restrictions are mentioned
for completeness: there are very few cases in which the first is important,
and none so far in which the second was important.
\section{Formatting ROUTINEs}
It should be mentioned explicitly that the formatting characters (space,
tab, carriage return, line feed, form feed, etc.) are completely ignored
by ZIL (and MDL) and can be sprinkled about liberally to make the
code appear more readable. It is common practice to 'line up' pieces
of code vertically within the more complicated structures (e.g.~COND,
REPEAT, etc.) so that it is obvious by inspection which pieces of
code correspond to which structure. Examination of existing game code
should be enlightening in this regard. The importance of good formatting
can hardly be overstated. Code which is not formatted or formatted
poorly is completely unreadable.
\chapter{Printing and TELL}
As was noted earlier, TELL is used to cobble together printed text.
TELL takes any number of arguments which are one of the following:
\begin{itemize}
\item Explicit STRINGs
\item STRINGs which are the value of an ATOM
\item STRINGs which are the value of an arbitrary expression, including
a call to another ROUTINE
\item The ATOM D followed by an OBJECT (represented as the value of an ATOM)
\item The ATOM N followed by a FIX (either explicitly or as the value of
an ATOM)
\end{itemize}
Here is a complicated example:
\begin{verbatim}
<TELL "You are in " D .LOC ". You have " N ,SCORE " points." CR>
\end{verbatim}
This might print:
\begin{verbatim}
You are in Red Hall. You have 37 points.
\end{verbatim}
supposing that LOCAL ATOM LOC's value was the RED-HALL OBJECT and
that GLOBAL ATOM SCORE's value was the FIX 37.
\chapter{PARSER 101}
This is intended to be an introduction to the world of the PARSER,
the most incomprehensible piece of code in the INTERLOGIC system.
A subsequent section will concern itself with advanced parser concepts.
It is best to think of the parser as a black box with from one to
three outputs: an action to be performed, a direct object, and an
indirect object. Each of the three corresponds to a GLOBAL ATOM: PRSA,
PRSO, and PRSI, respectively. Very frequently, the ACTION ROUTINE
for an OBJECT needs to check on which action is being performed. For
this case, a special operation has been constructed, namely the predicate
VERB?. Here's an example:
\begin{verbatim}
<VERB? WALK SCORE TAKE PUSH OPEN>
\end{verbatim}
This expression will return 'true' if the action requested was one
of WALK, SCORE, TAKE, PUSH, or OPEN. Otherwise, it will return 'false'.
\chapter{Main Loop}
Understanding the main loop is absolutely vital in designing games.
The flexibility and complexity allowed in INTERLOGIC games are almost
wholly due to the parser/main loop combination.
\section{Parser and Philosophy}
The main loop starts with a call to the parser. If the parser 'fails'
(e.g.~a word was unknown, the sentence didn't make sense, etc.),
nothing further happens and the loop is restarted. If it succeeds,
the three ATOMs described in the preceding section are SETG'ed and
a number of ROUTINEs are called in turn to determine the result of
the player's intended action.
Before indicating the order and purpose of the 'number of ROUTINEs',
it is important to understand the idea of 'handling'. In ZIL, we say
that a ROUTINE 'handled' the player's action if it finishes all of
the processing required for that action. Each ROUTINE decides whether
or not it considers the action 'handled', and its decision is final.
A ROUTINE 'handles' an action simply by returning a 'non-false' (i.e.~anything
other than a 'false').
It is a given in the INTERLOGIC world that no successfully parsed
user input should leave the user without an indication of the result
of that action. The following should never happen:
\begin{verbatim}
>kill the troll
>
\end{verbatim}
Someone, somewhere, should have 'handled' the requested action. This
'someone, somewhere' is defined to be the default action handler,
the last step in the chain of called ROUTINEs. It MUST print something,
and that thing should be the default response for that type of action.
For example, the default action handler for EAT might do this:
\begin{verbatim}
<TELL "I don't think the " D .OBJ " would agree with you." CR>
\end{verbatim}
In fact, it does. If EAT for a specific OBJECT wants to do something
different, then the OBJECT itself must supply a ROUTINE to 'handle'
the EAT action. An OBJECT supplies such a ROUTINE by having an ACTION
property (see the section on OBJECT properties, above).
\section{Basic Handler Sequence}
These are the basic steps in the 'handler' sequence:
\begin{description}
\item [{Preaction:}] This ROUTINE is intended to short circuit the remainder
of the sequence if some prerequisite for the intended action is not
met. There may be a preaction ROUTINE associated with any syntax (see
later on for definition of syntaxes). For example, the ROUTINE called
PRE-PUT checks to see whether the object to be put into some other
object is something which is takeable. This would prevent any number
of container ACTION ROUTINEs from doing precisely the same thing.
\item [{Indirect Object:}] A ROUTINE specified as the ACTION property
of an OBJECT.
\item [{Container:}] A ROUTINE specified as the CONTFCN property of the
PRSO's container. This is used only rarely.
\item [{Direct Object:}] A ROUTINE specified as the ACTION property of
an OBJECT.
\item [{Default Action:}] The default ROUTINE for a given action. An example
of this was given for the EAT action previously.
\end{description}
Some important things need to be noted about these previous steps:
\begin{itemize}
\item The order of the steps is crucial. The fact that the indirect object
ROUTINE is called BEFORE the direct object ROUTINE has implications
which have caused some confusion in the past. For example, if the
input sentence was \textquotedbl PUT THE EGG IN THE NEST\textquotedbl ,
and there were an ACTION for the nest which handled putting things
into it and an ACTION for the egg which handled putting it into things,
the nest's ACTION would take precedence. This might not, however,
be the thing intended.
\item There are cases in which an ACTION ROUTINE must be careful to check
whether the OBJECT it 'represents' is the direct object or the indirect
object. For example, the parser decides that the sentence \textquotedbl TAKE
SWORD FROM STONE\textquotedbl{} implies the action TAKE. So, however,
does \textquotedbl TAKE STONE\textquotedbl . If the STONE ACTION
ROUTINE checks only for the action being TAKE (e.g.~<VERB? TAKE>),
it will generate the same response for the two sentences above. This
is clearly wrong, and the ACTION ROUTINE should additionally check
the value of PRSO as well (e.g.~<AND <VERB? TAKE> <EQUAL? ,PRSO ,STONE>\textcompwordmark >).
\item The CONTFCN ROUTINE allows the container of an OBJECT to handle actions
on the OBJECT itself. This ROUTINE takes no arguments. Its use is
usually to handle special cases involving removing the OBJECT from
its container. For example, the nest in STARCROSS contains odds and
ends which cannot be removed without provoking the rat-ants. This
could only be implemented using the CONTFCN procedure.
\end{itemize}
\section{Advanced Handler Sequence}
The four basic steps in the sequence represent the large majority
of the cases in INTERLOGIC games. However, there are cases in which
these steps do not provide enough flexibility. For example, it is
difficult to see how one can short-circuit the WALK action in a specific
room without mucking around directly with the default WALK action
ROUTINE, which would be considered inelegant (to say the least). More
difficult would be to implement the death sequence in ZORK~I in which
almost every action is handled differently than during 'life'. To
have been forced to include special code in each action handler for
the case of 'death' would have been incredibly tedious.
The solution is to place another sequence around the basic one. This
makes the main loop look like the following:
\begin{itemize}
\item ACTOR ROUTINE. A ROUTINE which may be associated with each OBJECT
in the game which can perform actions. In ZORK~I, this would be the
game player only. In STARCROSS, however, there are the game player,
computer, and various aliens who can perform actions.
\item Location ROUTINE. A ROUTINE associated with the location in which
the ACTOR is acting. Note that 'location' need not be a ROOM, since
the ACTOR may be in a vehicle. See later for this unusual case.
\item Basic Handlers. As mentioned above.
\end{itemize}
Note first that the Location and ACTOR ROUTINEs are ACTION properties
of the corresponding OBJECT (in INTERLOGIC games, all of these things
are OBJECTs) and are set up when the OBJECT is created.
The ACTOR ROUTINE might be used, for example, to implement the death
sequence in ZORK~I. The Location ROUTINE might be used to implement
an area (say, floating in space) in which WALKing is not possible.
The ACTOR and Location ROUTINEs 'handle' an action in precisely the
same way as do the basic handlers, i.e.~by returning 'true'.
When the entire sequence is finished, the Location ROUTINE is called
again and its result is ignored. It might, for example, decide that
anything left on the ground at the end of an action is swept away.
\section{CLOCK}
When all of the above steps are finished, the CLOCK runs. Each move
may be thought of as one tick of the CLOCK. It is possible to cause
arbitrary events to happen at arbitrary times during a game (the mechanism
is described later) and all queued events which have come due are
processed at this step. After this step, the main loop repeats.
\chapter{ACTION ROUTINEs}
There are some conventions to writing ACTION ROUTINEs which will be
mentioned here. Some may be obvious from the preceding discussion,
others not.
A ROUTINE for an OBJECT (except as noted below) or ACTOR takes no
arguments. This is a consequence of the fact that each may be called
only once during the main loop and the purpose of the call is, therefore,
unambiguous.
The ROUTINE for a Location (ROOM or vehicle) however, takes exactly
one argument. By convention, it becomes the value of the LOCAL ATOM
RARG. This argument is used to determine the context of the call to
the ROUTINE. Two such contexts have been described: the one following
the call to the ACTOR ROUTINE, and the one following the result of
the entire action. A few others will be described later. Each context
has a code (a FIX, really) associated with it. Rather than memorize
the FIX associated with each context, a GLOBAL ATOM for each exists
whose value is the context code. The codes for the two contexts mentioned
here are the values of GLOBAL ATOMs M-BEG and M-END, respectively.
\begin{verbatim}
<ROUTINE ROOM-F (RARG)
<COND (<EQUAL? .RARG ,M-BEG> ...)
(<EQUAL? .RARG ,M-END> ...)>>
\end{verbatim}
The preceding might be the shell of the ACTION ROUTINE for a certain
ROOM. REMEMBER: The result of the call to a ROOM's ACTION ROUTINE
in the M-BEG case is important! If 'true', no further processing occurs.
There is no requirement for names of ACTION ROUTINEs, but it is best
to be consistent. My personal suggestion is that the ROUTINE for an
OBJECT called FOO should be FOO-F, but to each his own. Having a consistent
method insures that it will be easy to locate a piece of code within
a large file of ROUTINEs.
\chapter{The Parser and Objects}
This discussion deals with 'objects' in the within-the-game sense
(i.e.~not about 'rooms' and the like) and the way in which the parser
understands references to them. An understanding of this topic is
necessary in writing OBJECT and ROOM definitions, which is the subject
of the following sections.
Every object which can be referenced within a game may have from one
to four SYNONYMs and from zero to eight ADJECTIVEs. Whenever the parser
recognizes a noun phrase, it looks at all of the currently accessible
OBJECTs (the definition of 'accessible' is described in the next section)
for one which matches the nouns and adjectives within that phrase.
If more than one adjective is supplied by the game player, the first
is used (there are exceptions which will be described later). Two
nouns can be used within a noun phrase ONLY if separated by the word
\textquotedbl OF\textquotedbl{} (i.e.~\textquotedbl PAIR OF CANDLES\textquotedbl ).
In this case, the second noun is used and the first is ignored. Typically,
one would have both PAIR and CANDLES be SYNONYMs of the 'pair of candles'
OBJECT; then, one could refer to them either by \textquotedbl CANDLES\textquotedbl ,
\textquotedbl PAIR\textquotedbl , or \textquotedbl PAIR OF CANDLES\textquotedbl .
A word can be used as both a noun and an adjective in INTERLOGIC games.
For example, in DEADLINE, there is a 'bottle of Loblo tablets'. The
SYNONYMs for this OBJECT are BOTTLE, LOBLO, and TABLETS. The ADJECTIVE
is LOBLO. One might refer to the OBJECT legally as \textquotedbl BOTTLE\textquotedbl ,
\textquotedbl LOBLO\textquotedbl , \textquotedbl LOBLO TABLETS\textquotedbl ,
\textquotedbl BOTTLE OF LOBLO TABLETS\textquotedbl , \textquotedbl BOTTLE
OF LOBLO\textquotedbl , \textquotedbl TABLETS\textquotedbl , or
\textquotedbl BOTTLE OF TABLETS\textquotedbl . The flexibility here
is quite useful for one might imagine a player using any of these.
Naturally, simply saying \textquotedbl BOTTLE\textquotedbl{} might
be ambiguous if there were more than one BOTTLE accessible. If so,
the parser would ask the player to choose among the alternatives.
\chapter{Accessibility}
In the last section, it was stated that the parser determines which
OBJECTs are accessible at any given time. OBJECTs can be divided into
three classes, determining the ROOMs in which they can be accessed.
LOCAL OBJECTs can be in one place only; these represent ALL objects
which can be taken and some others. GLOBAL OBJECTs can be referenced
in ALL ROOMs; these represent concepts, names of characters, and the
like. LOCAL GLOBAL OBJECTs are objects which can appear in any number
of EXPLICITLY specified ROOMs (these ROOMs must specify the LOCAL
GLOBALs located therein - see the next section); they usually represent
things like doors and geography (rivers, cliffs, trees).
The parser's algorithm for finding objects based on the user's input
is to first look at LOCAL OBJECTs in the ROOM or the ACTOR. Only if
this fails will it look at LOCAL GLOBALs and GLOBALs. An real example
from STARCROSS demonstrates an interesting side effect: There are
two buttons, one a LOCAL OBJECT and one a LOCAL GLOBAL OBJECT accessible
in the same room. If the user says \textquotedbl PUSH BUTTON\textquotedbl ,
the parser will come back with the LOCAL OBJECT only! Since a LOCAL
OBJECT was found, it didn't bother looking elsewhere. Similarly, \textquotedbl PUSH
ALL BUTTONS\textquotedbl{} will still only find the LOCAL OBJECT!
The obvious question comes up: why not always check for LOCAL GLOBALs
and GLOBALs? The answer is efficiency; it is quite a bit slower to
search through the LOCAL GLOBALs and GLOBALs than to look at LOCAL
OBJECTs.
Note that \textquotedbl PUSH RED BUTTON\textquotedbl{} (assuming
the red button were the LOCAL GLOBAL) would work, since there is no
LOCAL OBJECT matching the adjective and noun. The LOCAL GLOBALs and
GLOBALs would then be examined, and the match be made.
\chapter{Creating a ROOM}
ROOMs are created through a call to the subroutine ROOM, as follows:
\begin{verbatim}
<ROOM room-name
(IN ROOMS)
(DESC "short description")
(FLAGS flag-11 ... flag-n)
(property value)
(property value)
...
(property value)>
\end{verbatim}
The first four lines are required; 'room-name' and 'short description'
are, obviously, variable. The 'room-name' will be the GLOBAL ATOM
whose value will be the ROOM. The 'short description' is used in brief
room descriptions and on the status line. The 'flags' include RLANDBIT
(meaning that the ROOM is on land) and ONBIT (meaning that the ROOM