524 lines
16 KiB
Plaintext
524 lines
16 KiB
Plaintext
>From stinglai@bureau.World.GOV Tue Jun 5 08:50:55 1990
|
|
From: stinglai@bureau.World.GOV (Stinglai Ka'abi)
|
|
Newsgroups: alt.mud
|
|
Subject: Beginner's guide to MUF programming
|
|
Keywords: MUD Muck MUF forth Atlantis and a partridge in a pear tree
|
|
Date: 5 Jun 90 09:19:45 GMT
|
|
Reply-To: blojo@ocf.berkeley.edu
|
|
Distribution: alt
|
|
|
|
|
|
[ Updated August 10th, 1990, by ChupChup for the release of TinyMUCK 2.2. ]
|
|
|
|
Zen in the Art of the Towers of Hanoi
|
|
(or The Basics of MUF in 10 megs or less.)
|
|
|
|
|
|
This is an introduction to MUF, a dialect/subset of forth used to do really
|
|
really neat things with TinyMuck 2.2. This intro was designed to be read
|
|
before any of the other MUF information; it (hopefully) should supply you
|
|
with a fair idea of what MUF is all about. It's written at a non-programming-
|
|
stud level, all the better for understanding.
|
|
|
|
|
|
All MUF programs work by performing operations on a stack.
|
|
|
|
For all you non-programmer types, a stack is just a tool used to store
|
|
information. Information is manipulated by "pushing" things onto the
|
|
stack and "popping" things off. The last thing you've placed on a stack
|
|
is always the next thing you would take off if you tried; it's like
|
|
piling objects on top of each other, when the only thing you can remove
|
|
>from the pile is the thing on top. For example, if you were to push the
|
|
number 23, then push the number 42, and then pop a value off the stack,
|
|
you would get 42. If you were to pop another value off the stack after this,
|
|
you would get 23. If you were to try and pop another value, you would get
|
|
an error, since the stack would now be empty. (This is a "stack underflow.")
|
|
|
|
The basic procedural unit in MUF is called the word. A word is simply a
|
|
sequence of instructions. In program text, a word always starts with a
|
|
colon, then the word's name. A semicolon marks the end of a word.
|
|
For example:
|
|
|
|
: detonate_explosives
|
|
(text of word here)
|
|
;
|
|
|
|
would define a word called detonate_explosives.
|
|
|
|
Parentheses are used to delineate program comments; everything inside comments
|
|
is ignored by the computer. The detonate_explosives word above, if run
|
|
as shown, would do absolutely nothing.
|
|
|
|
Indentation in MUF is arbitrary and serves to make the program more readable
|
|
to people.
|
|
|
|
|
|
In MUF, there are three types of constant values: integers, strings, and
|
|
database references. Each of these types is stored and retrieved from the
|
|
stack as a single unit (The string "Hello, Sailor!", for example, would be
|
|
stored on the stack in its entirety: "Hello, Sailor!"; it would not be stored
|
|
byte-by-byte or word-by-word or in any other such silly way.)
|
|
To push a constant onto the stack, you only need to state its value. The
|
|
following is a completely legitimate procedure:
|
|
|
|
: pointless_word
|
|
"Old Man"
|
|
"I'm"
|
|
37
|
|
"What?"
|
|
37
|
|
"Not old!"
|
|
;
|
|
|
|
However, run by itself, it wouldn't do anything visible to the user. It would,
|
|
however, create a stack which looks like this:
|
|
|
|
("Old Man" "I'm" 37 "What?" 37 "Not old!")
|
|
|
|
In the above stack, "Old Man" is the value on the bottom. "Not old!" is the
|
|
value on top of the stack, and would be the next value retrieved.
|
|
|
|
Placement of values on the same line of a program is arbitrary. Since each
|
|
value represents something being put on a stack, the word
|
|
|
|
: example
|
|
"Semprini?"
|
|
"All right, wot's all this then!"
|
|
;
|
|
|
|
is the same as:
|
|
|
|
:example
|
|
"Semprini?" "All right, wot's all this then!"
|
|
;
|
|
|
|
|
|
Functions which are available in the standard MUF library take values from the
|
|
top of the stack, do things with them, and usually leave something new back
|
|
on top of the stack. The words +, -, swap, pop, and random provide good
|
|
examples of this and are discussed here.
|
|
|
|
The + routine takes the top two integers from the stack, adds them together,
|
|
and leaves the result on top of the stack. In order to easily describe
|
|
what functions like this do, a certain stack notation is used: for +,
|
|
this would be (i1 i2 -- z). What's inside those parenthesis is a sort of
|
|
"Before and After" synopsis; the things to the left of the double-dash are
|
|
the "before", and those to the right are the "after". (i1 i2 -- i) says that
|
|
the function in question takes two integers away and leaves one. The letters
|
|
used here to tell what kind of data a stack object can be are: i for integer,
|
|
d for database object, s for string, v for variable, and x or y to mean
|
|
something that can be more than one type.
|
|
|
|
Here are short descriptions of the procedures listed above so you can get
|
|
the hang of how they work:
|
|
|
|
|
|
+ (i1 i2 -- i)
|
|
Adds i1 and i2 together. The word
|
|
|
|
: add_some_stuff
|
|
2 3 +
|
|
;
|
|
|
|
will return 5. The word
|
|
|
|
: add_some_more_stuff
|
|
2 3 4
|
|
5
|
|
+ + +
|
|
;
|
|
|
|
will return 14. When add_some_more_stuff first reaches the "+ + +" line,
|
|
the stack looks like:
|
|
|
|
(2 3 4 5).
|
|
|
|
The first + changes the stack to look like:
|
|
|
|
(2 3 9).
|
|
|
|
The next causes:
|
|
|
|
(2 12).
|
|
|
|
The final plus returns:
|
|
|
|
(14).
|
|
|
|
|
|
- (i1 i2 -- i)
|
|
Subtracts i2 from i1.
|
|
|
|
: subtract_arbitrary_things
|
|
10 7 -
|
|
;
|
|
|
|
will return 3 on top of the stack.
|
|
|
|
|
|
swap (x y -- y x)
|
|
Switches the top two things on the stack. This is useful for when you want
|
|
to know the value of x but want to save y for later use.
|
|
|
|
: swap_stuff_around
|
|
1 5
|
|
2 swap
|
|
3
|
|
"Three, sir!"
|
|
swap
|
|
"Boom!"
|
|
;
|
|
|
|
will, before it gets to the first swap, create a stack of (1 5 2).
|
|
After the swap, the stack looks like (1 2 5). It then accumulates another
|
|
3 and a string constant, to look like (1 2 5 3 "Three, sir!") It swaps
|
|
the last two again and adds another string, so the stack looks like:
|
|
(1 2 5 "Three, sir!" 3 "Boom!").
|
|
|
|
|
|
pop (x --)
|
|
Throws away the value on top of the stack. As shown in the stack diagram, it
|
|
returns nothing but takes something, and so decreases the stack's total size.
|
|
Useful when you really really want to get to the next thing on the stack so bad
|
|
you don't care what's on top. The word:
|
|
|
|
: needless_popping_waste
|
|
"Immanuel Kant"
|
|
"Heideggar"
|
|
pop
|
|
"David Hume"
|
|
"Schoppenhauer"
|
|
"Hegel"
|
|
pop pop
|
|
;
|
|
|
|
would leave the stack looking like ("Immanuel Kant" "David Hume").
|
|
|
|
|
|
random (-- i)
|
|
Doesn't even look at the stack, but piles a really really random integer on
|
|
top. The word:
|
|
|
|
: feel_lucky_punkP
|
|
random
|
|
random
|
|
random
|
|
;
|
|
|
|
would return a stack of three random numbers.
|
|
|
|
|
|
Because of the way the stack works, variables aren't as necessary in MUF as
|
|
they are in other languages, but they can be used to simplify stack-handling
|
|
operations. To declare a variable, you simply add the line "var <name>"
|
|
at the beginning of your program. Variables are of no specific type; a
|
|
variable which holds an integer can turn around the next second and hold
|
|
a string if it's feeling haughty enough.
|
|
|
|
The following words are important when dealing with variables:
|
|
|
|
! (x v --)
|
|
Set variable v to hold value x. The program:
|
|
|
|
|
|
var answer
|
|
|
|
: multiply-and-store
|
|
6 9 *
|
|
answer
|
|
!
|
|
;
|
|
|
|
will give the variable "answer" the value 42. This is the same as:
|
|
|
|
: multiply-and-store
|
|
6 9 * answer !
|
|
;
|
|
|
|
|
|
@ (v -- x)
|
|
This word (pronounced "fetch") retrieves the value of a variable and puts it
|
|
on the stack. You should remember this since a common mistake among beginning
|
|
MUF programmers is to forget to put fetch symbols in their programs.
|
|
The word
|
|
|
|
garply
|
|
|
|
by itself stands for the variable "garply", while the expression
|
|
|
|
garply @
|
|
|
|
stands for the value of that same variable. If you're familiar with Lisp, this
|
|
is analogous to the difference between garply and (garply).
|
|
|
|
The program:
|
|
|
|
var biggles
|
|
var fang
|
|
: more_silly_manipulation
|
|
10 biggles !
|
|
24 fang !
|
|
biggles @ fang @
|
|
+
|
|
;
|
|
|
|
will return the value 34 on top of the stack. The program:
|
|
|
|
var biggles
|
|
var fang
|
|
: more_silly_manipulation
|
|
10 biggles !
|
|
24 fang !
|
|
biggles fang +
|
|
;
|
|
|
|
is *wrong*. For reasons I won't go into now, since this guide was written
|
|
at the last moment and at great expense, the above word will return the
|
|
value 7 on top of the stack.
|
|
|
|
|
|
In MUF, there are two variables which are predefined and available for use at
|
|
all times. These variables are "me" and "loc", where "me" holds the
|
|
player's database reference, and loc holds the player's location's database
|
|
reference.
|
|
|
|
(Database references were mentioned before as the third type of constant, then
|
|
sort of ignored till now. For the sake of completeness, I will introduce the
|
|
word
|
|
|
|
dbref (i -- d)
|
|
Where i is an integer and d is a database reference, dbref converts between
|
|
the two types. The line
|
|
|
|
2032 dbref
|
|
|
|
will return item #2032 in the Muck database. This is useful since there
|
|
are lots of functions that operate on database references that won't work
|
|
on integers. [If you want to declare something in one of your programs as
|
|
being a dbref instead of an integer, you should just put a # in front. For
|
|
example, 69 means the integer 69, while #69 means object number 69. You
|
|
could say '69 dbref' instead of '#69', but it would be a little slower and
|
|
a little harder to read.]
|
|
|
|
Me @ will return the player's item reference, while loc @ will return the
|
|
room they are in. Trigger @ returns the item that triggered the current
|
|
program, whether it is a player, exit, room, whatever. A useful word to
|
|
know is:
|
|
|
|
name (d -- s)
|
|
Where d is a db reference and s is a string, name returns the name of item
|
|
x.
|
|
|
|
|
|
Now that you know about me @, another Muck function becomes useful. Its
|
|
synopsis is:
|
|
|
|
notify (d s --)
|
|
When d is a player, notify prints string s out to user d. The program
|
|
|
|
: warn
|
|
me @
|
|
"There is someone behind the next column waiting to jump you."
|
|
notify
|
|
;
|
|
|
|
would print said message on the user's screen.
|
|
|
|
|
|
Before you can really start writing neat stuff in Muck, there are two more
|
|
things you should know about. One is = and the other is the "if/then" setup.
|
|
|
|
= (i1 i2 -- i)
|
|
Returns 1 if integer i1 is equal to integer i2, otherwise returns 0.
|
|
|
|
: nonequals
|
|
2 3 =
|
|
;
|
|
|
|
returns 0.
|
|
|
|
If/then could be written up with a synopsis, but it would be sort of
|
|
complicated and probably a lie also. The way it works is this: If
|
|
pulls an item off the stack. If the item is 0, it skips over the
|
|
program after the IF and resumes execution at the THEN. If the item is not
|
|
0, the program will execute everything in between.
|
|
|
|
The naming of this construction as if/then can be somewhat confusing. It
|
|
certainly doesn't work quite like the if/then of normal languages, and the
|
|
THEN actually being called THEN is sort of confusing. As nearly as I can
|
|
tell, if/then is a sort of forth-creators' joke. It does not mean
|
|
"IF the previous is true THEN do this." like it does in most languages.
|
|
Rather, it means "IF the previous is true do this; THEN go on with the rest
|
|
of the program." Remarkably silly.
|
|
The word:
|
|
|
|
: word
|
|
2 3 =
|
|
if me @ "Your computer is broken!" notify
|
|
then me @ "Done executing this word." notify
|
|
;
|
|
|
|
will always print "Done executing this word." to the user, and will print
|
|
"Your computer is broken!" if something is really screwy with the math and
|
|
it actually thinks 2 = 3. Getting a bit more sophisticated, one can write
|
|
something like:
|
|
|
|
: word_up
|
|
"Your computer works fine."
|
|
2 3 =
|
|
if pop
|
|
"Your computer is broken. Sorry. Truth hurts."
|
|
then
|
|
me @ swap notify
|
|
;
|
|
When word_up is called, "Your computer works fine." gets put on the stack.
|
|
If your computer actually works, 2 is *not* equal to 3, so that right after
|
|
the = the stack looks like:
|
|
|
|
("Your computer works fine." 0)
|
|
|
|
The IF reads the 0 and skips all the way down to the THEN.
|
|
The SWAP in the last line is used since the NOTIFY word wants its
|
|
parameters in the opposite order of where they would be.
|
|
|
|
If your computer is broken, right after the =, the stack looks like:
|
|
|
|
("Your computer works fine." 1)
|
|
|
|
The IF reads this 1 and decides to keep executing. It then gets to the POP
|
|
which gets rid of the filthy lie about well-working computers and replaces
|
|
it with the painful truth.
|
|
|
|
|
|
*SAMPLE PROGRAM*
|
|
|
|
Ok, so you've been reading this whole thing so far, and you really want to
|
|
use this stuff to do something interesting. The following program does
|
|
something interesting, and uses the function
|
|
|
|
strcat (s1 s2 -- s)
|
|
Concatenate strings s1 and s2, returning the result.
|
|
|
|
it also uses
|
|
|
|
location (d -- d')
|
|
Takes db reference d and returns d', the db reference for its location.
|
|
|
|
and
|
|
|
|
dup (x -- x x)
|
|
Duplicate the top of the stack.
|
|
|
|
and
|
|
|
|
dbcmp (d1 d2 -- )
|
|
Works just like =, except operates on db references instead of integers.
|
|
|
|
|
|
: far_vision
|
|
#2032 (2032 is Celia's object number. )
|
|
dup (Make 2 copies; we're about to use 1. )
|
|
name (Celia might change her name in the future, so)
|
|
(instead of using "Celia" here we just look up)
|
|
(her name. )
|
|
|
|
" is currently in "
|
|
strcat (Attach name to sentence so far )
|
|
swap (Flip the sentence back so we can get at)
|
|
(Celia's dbref again. )
|
|
|
|
(Celia's dbref is now at top of stack. )
|
|
|
|
location (Where is Celia? )
|
|
name (What is the name of the place she is in?)
|
|
"." strcat strcat
|
|
|
|
me @ swap notify (Tell the player where Celia is.)
|
|
|
|
#2055 (Celia's hardsuit is #2055. )
|
|
location
|
|
#2032 (Celia again )
|
|
dbcmp (Has she got her hardsuit with her?)
|
|
if me @ "Watch out-- she's wearing her hardsuit!" notify then
|
|
;
|
|
|
|
|
|
Note that this program uses no variables (except for the universally
|
|
defined ME variable.)
|
|
|
|
|
|
In Muck, this program would be attached to, say, a homing device or a magic
|
|
staff. Now, if Boomer ever wants to find Celia, he can, and he'll even know
|
|
if she's defenseless or she's got her armor.
|
|
|
|
Without the comments and spaced out like you might see normally, this program
|
|
looks like:
|
|
|
|
: far_vision
|
|
#2032 dup
|
|
name " is currently in " strcat
|
|
swap location name
|
|
"." strcat strcat (Now we know where she is.)
|
|
|
|
me @ swap notify
|
|
|
|
#2055 location
|
|
#2032 dbcmp
|
|
if me @ "Watch out-- she's wearing her hardsuit!" notify
|
|
then
|
|
;
|
|
|
|
|
|
Words can also be called by other words; to do this, you treat your other
|
|
words just like library functions when you use them. When you have more
|
|
than one word in the same program, the word which is listed *last* is
|
|
the one executed, and all the ones listed before it are subroutines. The
|
|
above program could be rewritten:
|
|
|
|
: celia-identity
|
|
#2032
|
|
;
|
|
|
|
: far_vision
|
|
celia-identity dup
|
|
name " is currently in " strcat
|
|
swap location name
|
|
"." strcat strcat (Now we know where she is.)
|
|
|
|
me @ swap notify
|
|
|
|
#2055
|
|
location celia-identity dbcmp (Using celia-identity and spacing the )
|
|
(commands like this makes this bit a )
|
|
(little easier to understand. )
|
|
|
|
if me @ "Watch out-- she's wearing her hardsuit!" notify
|
|
then
|
|
;
|
|
|
|
|
|
Oodles and oodles of other neat MUF library routines are available, too
|
|
numerous to be detailed in an introduction such as this. A complete list,
|
|
as well as sample code, is available from such spiffy ftp sites as
|
|
prince.white.toronto.edu and belch.berkeley.edu.
|
|
|
|
If you're interested in seeing more sample code, write to me for
|
|
program listings for the Pan Galactic Gargle Blaster, walkie-talkies,
|
|
and several useful library routines.
|
|
|
|
Stinglai "Two Sheds" Ka'abi
|
|
Mail to: blojo@ocf.berkeley.edu
|
|
|
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
"We are too proud to fight." --Woodrow Wilson 1856-1924
|
|
"Violence never settles anything." --Genghis Khan 1162-1227
|
|
"The mice voted to bell the cat." --Aesop c. 620-c. 560 B.C.
|
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
Coming next week:
|
|
"Life, the Universe, and INTERCAL:"
|
|
A most excellent introduction to that programming language.
|
|
|
|
|
|
|