forked from sevenecks/lambda-moo-programming
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwinding-duck-non-html5.html
587 lines (581 loc) · 24.8 KB
/
winding-duck-non-html5.html
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
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>
Lambda MOO Programmer's Tutorial
</title>
</head>
<body>
<h1>
Lambda MOO Programmer's Tutorial
</h1>
<h1>
This tutorial was taken from <a href=
"http://netlab.gmu.edu/muve/html/Winding-Duck.html">http://netlab.gmu.edu/muve/html/Winding-Duck.html</a>
and is included in this repository for posterity. It is
non-HTML5 for now.
</h1>
<ul>
<li>
<a href="#A1"><strong>Chapter 1: A Simple
Object</strong></a><br />
<ul>
<li>
<a href="#A1.1">Introduction</a>
</li>
<li>
<a href="#A1.2">Simple Wind</a>
</li>
<li>
<a href="#A1.3">Simple Messages</a>
</li>
<li>
<a href="#A1.4">Simple Drop</a>
</li>
</ul><br />
</li>
<li>
<a href="#A2"><strong>Chapter 2: A More Complex
Object</strong></a><br />
<ul>
<li>
<a href="#A2.1">Complex Wind</a>
</li>
<li>
<a href="#A2.2">Complex Messages</a>
</li>
<li>
<a href="#A2.3">Complex Drop</a>
</li>
</ul><br />
</li>
<li>
<a href="#A3"><strong>Chapter 3: Other Programming
Issues</strong></a><br />
<ul>
<li>
<a href="#A3.1">Description</a>
</li>
<li>
<a href=
"#A3.2%3EPermissions%3C/a%3E%20%3C/UL%3E%3C/UL%3E%3CHR%3E%3CH2%3E%3CA%20NAME=">
Chapter 1: A Simple Object</a>
<h3>
<a name="A1.1" id="A1.1">Introduction</a>
</h3>This is a programming example of reasonable
complexity. We are going to demonstrate property use,
forking, and generic object use.
<p>
Our example is a wind-up toy. The basic things that a
wind-up toy does is get wound, and then slowly wind
its way down, hopping or rolling or twirling along.
</p>
<p>
First, let's create a couple of objects to work from.
</p>
<pre>
@create $thing named Generic Wind-Up Toy,Toy
You now have Generic Wind-Up Toy (aka Toy) with object number #12221
and parent generic thing (#5).
@create #12221 named Wind-Up Duck,Duck
You now have Wind-Up Duck (aka Duck) with object number #12222 and
parent Generic Wind-Up Toy (#12221).
</pre>We'll refer to these as Toy and Duck, the aliases we gave in
the @create command.
<h3>
<a name="A1.2" id="A1.2">Simple Wind</a>
</h3>First, we need some way to tell if the toy has
been wound up. We'll create a property called "wound"
on the generic toy.
<pre>
@property toy.wound 0
Property added with value 0.
</pre>Before we can write our program, we need a verb... We want to
give commands like "wind duck" so we give it an argument list of
"this". The variable "this", when used inside a verb, refers to the
actual object (e.g. the duck) on which the verb called. Its place
in the argument list designates how the verb will be found by the
built-in parser.
<pre>
@verb toy:wind this
Verb added.
</pre>Now we can make a simple program to wind the toy.
<pre>
@program toy:wind
this.wound = this.wound + 2;
player:tell("You wind up the ", this.name,".");
player.location:announce(player.name, " winds up the ", this.name,".");
.
</pre>Remember that "this" takes the place of whatever we will type
in place of "this" in our command line, so when the program runs,
it will change the .wound property of the duck. "Player" is the
person who types the command. These are built in variables
available in every program, they just take on different meanings
depending on who types the command and what object is used in the
command. The idea behind adding 2 to this.wound is that you can
wind it a bunch of times to make it go longer.
<p>
Let's try it on our duck:
</p>
<pre>
wind duck
You wind up the Wind-Up Duck.
</pre>Everyone else sees:
<pre>
yduJ winds up the Wind-Up Duck.
</pre>
<h3>
<a name="A1.3" id="A1.3">Simple Messages</a>
</h3>Next, we want to make the duck actually move
around. Remember that we're doing all the programming
on the generic toy, and using the specific instance of
the duck to test things out. To be properly generic, we
need to define different properties that can hold
message strings for the motions of the toy. So let's
define two messages. One for the toy to start moving,
and another for it to print as it continues to wind
down.
<pre>
@property toy.startup_msg ""
Property added with value "".
@property toy.continue_msg ""
Property added with value "".
</pre>We named these with "_msg" in order for the messages to show
up in @messages, and be settable with the @message_name object is
"text" command. (See help @messages for more details.) Let's set
values of these messages on the duck. While we're at it, we should
give our duck a description!
<pre>
@startup duck is "waddles about and starts rolling forward."
You set the "startup" message of Wind-Up Duck (#12222).
@continue duck is "swivels its neck and emits a >> Quack <<"
You set the "continue" message of Wind-Up Duck (#12222).
@describe duck as "A yellow plastic duck with wheels at the bottom and a knob
for winding."
Description set.
</pre>
<h3>
<a name="A1.4" id="A1.4">Simple Drop</a>
</h3>We should be ready to roll now... We're going to
have the wind up toy start up when the player drops it.
This introduces another concept, that of specializing
an existing verb. The generic thing ($thing) defines
the verb :drop. Normally, if we type "drop duck" the
verb on $thing gets invoked on the duck (even though
it's a few levels removed from $thing). If we define a
:drop verb on the generic toy, we don't want to have to
type in all the code from $thing:drop; we just want to
have our new code executed after the normal drop gets
done. In order to get both things, we use the primitive
pass(@args).
<p>
This is the slipperiest concept in MOO programming,
yet the basis for the entire object oriented nature
of the MOO. An easy way to think of pass is like
another verb call: it just does the "basic thing"
that this verb normally does. We can add our
specialization code both before and after this basic
thing. The @args part means just to use the same verb
arguments in the base case as we do in our
specialization. More sophisticated users of pass may
wish to change these arguments; for now, just take it
as gospel. Sometimes you won't want to use pass at
all, if you don't want the basic thing to happen, but
want to completely override the default behavior.
This is OK, you just have to think about what you
want when deciding whether and where to put the call
to pass(@args).
</p>
<p>
We want the dropping to happen before any of our
startup, so we call pass(@args) right away. We better
check if the toy has been wound up, and not do
anything special if it hasn't. Next, we print out our
startup message, and schedule some tasks to be run a
little later to print out the progress of our toy.
Let's pick every 15 seconds to print out a message,
and print out as many messages as we have in
this.wound. Each time it actually prints a message,
it will "unwind" a little, so we decrement
this.wound. Note that the stuff between the fork and
the endfork doesn't get done until the fork actually
starts, and the forks start 15, 30, 45, etc. seconds
after all the code in our drop verb has finished.
</p>
<p>
Our @verb command looks a little funny, because we're
forced into using some more advanced features of MOO
verbs. If you have a verb named foo*bar, then in
order to invoke that verb, the user must type at
least "foo", and may type any part of "bar" that they
like; the * is putting in an abbreviation.
$thing:drop has these abbreviations built in, so you
can just type "d object" to drop something. Also,
drop has a synonym, "throw" (more properly, th*row).
Putting both verbs in a double-quoted string is the
way to say "two names for this verb". Really, any
number of synonyms are possible. We'll continue to
refer to this verb as "drop", even though we could
just as truthfully refer to it as "throw". We use
these names because we want to name our verb exactly
the same as the one on $thing, so people who prefer
to use "throw" will still get our specializations.
</p>
<pre>
@verb toy:"d*rop th*row" this
Verb added.
@program toy:drop
pass(@args);
if (this.wound)
this.location:announce_all(this.name, " ", this.startup_msg);
for x in [1..this.wound]
fork (x * 15)
this.location:announce_all(this.name," ", this.continue_msg);
this.wound = this.wound - 1;
endfork
endfor
endif
.
</pre>It's worth noting that 0 in MOO is also "false" as well as
being "zero", which is convenient. Thus we didn't have to use
(this.wound!=0) in our if statement, but could use just
(this.wound).
<p>
Now, let's drop our duck and see how it works!
</p>
<pre>
drop duck
You drop Wind-Up Duck.
Wind-Up Duck waddles about and starts rolling forward.
Wind-Up Duck swivels its neck and emits a >> Quack <<
Wind-Up Duck swivels its neck and emits a >> Quack <<
</pre>It actually waited 15 seconds in between each of those
messages. I tried it again, but this time I typed @forked right
away to see that the tasks had been scheduled:
<pre>
@forked
Queue ID Start Time Owner Verb (Line) [This]
-------- ---------- ----- ------------------
1231283976 Jul 23 15:03:28 1991 yduJ #12221:drop (6) [#12222]
577459244 Jul 23 15:03:43 1991 yduJ #12221:drop (6) [#12222]
</pre>
<hr />
<h2>
<a name="A2" id="A2">Chapter 2: A More Complex
Object</a>
</h2>How could we improve on our wind-up toy? Or,
rather, what are some of its problems?
<p>
What happens when someone picks up the toy while it's
going? What if you try to drop it somewhere you're
not allowed to? Perhaps we should only allow someone
to wind it up if they are holding it. What if someone
winds it five thousand times? Perhaps we should allow
programmers to make more complicated messages on
their child objects.
</p>
<p>
We'll address each of these issues in chapter 2.
</p>
<h3>
<a name="A2.1" id="A2.1">Complex Wind</a>
</h3>Let's start with requiring someone to be holding
the duck in order to wind it. We take the code from
before, and stick an if/else/endif around it.
<pre>
@program toy:wind
if (this.location == player)
this.wound = this.wound + 2;
player:tell("You wind up the ", this.name,".");
player.location:announce(player.name, " winds up the ", this.name,".");
else
player:tell("You have to be holding the ", this.name,".");
endif
.
</pre>That was simple enough... again 'this' is whatever wind-up
toy was wound, and 'this.location' is where the toy is now. If it
is in the player's inventory then this.location will be equal to
the variable 'player'.
<p>
Let's complicate the code once more, and then we'll
be done with the :wind verb. We should have a maximum
number of turns the toy will allow, otherwise a
malicious player could spend a few hours winding the
toy, and then unleash it on an unsuspecting public.
If we wanted to be really fancy, we could make the
toy break if they wound it too far, and require then
to repair it with a screwdriver (which we would
create for the purpose), but let's leave that as an
exercise for the reader.
</p>
<p>
We create a property for the maximum number of turns,
so different toys can have different maximums. We'll
give it a default value of 20, though, so it doesn't
have to be set for each new toy.
</p>
<pre>
@property toy.maximum 20
Property added with value 20.
</pre>Now we insert another set of if/else/endifs inside our
current set.
<pre>
@program toy:wind
if (this.location == player)
if (this.wound < this.maximum)
this.wound = this.wound + 2;
player:tell("You wind up the ", this.name,".");
player.location:announce(player.name, " winds up the ", this.name,".");
if (this.wound >= this.maximum)
player:tell("The knob comes to a stop while winding.");
endif
else
player:tell("The ",this.name," is already fully wound.");
endif
else
player:tell("You have to be holding the ", this.name,".");
endif
.
</pre>In order to add to the feel of the toy, we put in another
test: if, after adding 2 to the wound property, it has reached the
maximum, we tell the player they've come to the end. The more
completely you can describe an object's actions and responses to
actions, the richer the feel of the Virtual Reality.
<h3>
<a name="A2.2" id="A2.2">Complex Messages</a>
</h3>Earlier we showed how to make user settable
messages on an object. Now we will show how to enable
another programmer make more complicated messages on
eir windup toy.
<p>
A simple yet very useful method is to define a verb
for each message property, which just returns that
property, and then have all the uses of that property
use the verb instead. For example:
</p>
<pre>
@verb toy:continue_msg this none this
@program toy:continue_msg
return this.continue_msg;
.
</pre>However, it seems almost silly to make a whole bunch of
verbs, one for each property... And indeed it is. The built-in
variable "verb" is always set to the name that this verb was
invoked with, so we can get all the messages in one verb, with this
idiom:
<pre>
@verb toy:"wind_down_msg continue_msg startup_msg going_msg" this none this
@program toy:continue_msg
return this.(verb);
.
</pre>There's a lot of stuff to explain in this little bit of code!
First, the syntax of the @verb command, with the several messages
listed in a quoted string, indicates that each of those names is an
alias for the verbname, and the verb can be invoked by any of those
names. Second, the builtin variable "verb" is set to whichever name
was actually used. This enables the property reference to be a
simple construction from the verb name, using the expression syntax
for property references,
.(), where in this case expression is a simple
variable reference.
<p>
Here's an example of how we can customize just one
of those messages programmatically with the wind-up
duck. In the continue_msg we'll make the duck quack
once, twice, or thrice, randomly. We leave the
other messages alone, as we'll just be using static
text.
</p>
<pre>
@verb duck:continue_msg this none this
@program duck:continue_msg
times = {"once","twice","thrice"}[random(3)];
return "swivels its neck and quacks " + times + ".";
.
</pre>
<h3>
<a name="A2.3" id="A2.3">Complex Drop</a>
</h3>Let's tackle the drop verb now. There are a lot
of problems. First, what happens if someone picks up
the toy while it's still going? The messages still
appear! What should happen? Two ideas come to mind:
1. It should fully discharge its windings, as real
life windup toys are wont to do; 2. It should just
stop running, as though the player had grabbed the
windup knob to prevent it from further discharge.
We'll choose option 2 for our example. Option 1 would
be easiest to do by specializing the existing "take"
verb to reduce the .wound property to 0, printing
appropriate messages.
<p>
The major problem with our drop verb is that it
schedules all its tasks at one time. Once
scheduled, they *will* run, whether or not the
situation has changed. To solve this problem, we
only schedule one task at a time, making it the job
of that task to figure out whether it has wound
down, and schedule another task. It should also
check if it has been picked up; if so it should
neither print messages nor schedule the next task.
</p>
<p>
Having called this section "Complex Drop", we're
going to defer the complexity to an auxiliary verb,
and make Drop itself simpler.
</p>
<pre>
@program toy:drop
pass(@args);
if (this.wound)
this.location:announce_all(this.name, " ", this:startup_msg());
fork (15)
this:do_the_work();
endfork
endif
.
</pre>We will use the special arguments "this none this" in our
@verb command. This is a special kluge that means this verb is not
a command, and cannot be typed at the command line. It also won't
show up in @examine commands. This helps to keep down the clutter
of @examine, and because this verb is only an internal function, it
wouldn't make sense to type as a command line.
<pre>
@verb toy:do_the_work this none this
if (this.wound)
if ($object_utils:isa(this.location,$room))
this.location:announce_all(this.name," ", this:continue_msg());
this.wound = this.wound - 1;
if (this.wound)
fork (15)
this:do_the_work();
endfork
else
this.location:announce_all(this.name, " ", this:wind_down_msg());
endif
endif
if (this.wound < 0)
this.wound = 0;
endif
endif
</pre>Let's go through this step by step. First we check if we're
still wound up. Theoretically this should always be true, but it's
best to check. Assuming .would is true (that is, nonzero), we check
to see if we're in a room. The utility function
$object_utils:isa(x,y) checks to see if an object X will behave as
an object Y, that is, if Y is in the object's ancestor tree. We do
this because we only want to make our noises if we're wandering
around in a room. A player won't be a room, and so won't pass this
test. Now, we print our message, and decrement this.wound to say
we've wound down once. Next we think about scheduling the next
task. We check this.wound again, because if we subtracted the last
bit, we don't want to bother scheduling it. Note that we call this
very same verb, this:do_the_work. Just for extra feeling, we put in
a new message to be printed when the toy has actually wound down.
Oops! Better add that property... And finally, at the very end,
we've gotten all anal retentive with error checking and made sure
that this.wound never gets into negative numbers. (Notice that if
it *did* get into negative numbers, it would never stop. So this
check is not entirely pointless!)
<pre>
@property toy.wind_down_msg ""
Property added with value "".
@wind_down duck is "hiccups once and stops rolling."
You set the "wind_down" message of Wind-Up Duck (#12222).
</pre>
<hr />
<h2>
<a name="A3" id="A3">Chapter 3: Other Programming
Issues</a>
</h2>
<h3>
<a name="A3.1" id="A3.1">Description</a>
</h3>It might be nice if you could tell by looking at
the toy that it was moving, and not have to wait for
it to tell you so. Like "drop", this is done by
specializing an already existing verb, called
description, by calling pass(@args) and then doing
some additional stuff. There are a number of relevant
differences between this and most other verbs,
however.
<p>
Most verbs do something, that is, when you type a
command, it produces a set of messages, and changes
some properties on some objects. Some verbs,
however, are not commands, and don't even print any
messages. Instead, they return a value to the
calling verb, for that verb to use to produce
messages or change properties. These are analogous
to "functions" in other languages. Description is
one such verb. By default, that is, in the basic
case, it returns the property 'this.description'.
Verbs like 'look' call description on objects and
print out the strings they return. So when we
specialize description, we won't be calling
pass(@args) all by itself, and then printing out
more text, instead we'll call pass(@args), add text
to that, and return the whole pile.
</p>
<p>
Again because we're trying to write generically, we
create a property to hold a message to be added to
the description when the toy is running, and we set
it for the duck.
</p>
<pre>
@property toy.going_msg ""
Property added with value "".
@going duck is "The duck is rolling forward with a slight waddle."
You set the "going" message of Wind-Up Duck (#12222).
</pre>The description verb again needs the "this none this" args,
since it is just an internal verb called by programs.
<pre>
@verb toy:description this none this
Verb added.
@program toy:description;
basic = pass(@args);
if (this.wound)
return basic + " " + this:going_msg();
else
return basic;
endif
.
</pre>Pretty simple code, huh? First we call pass. We store away
that text in a variable 'basic', because we'll need it later. Then
we check to see if we're wound up. If we are, we return the basic
information plus our going_msg. We put in a couple of spaces to
separate the sentences. Otherwise, we're quiet, and we just return
the basic string. What does it look like?
<pre>
look duck
A yellow plastic duck with wheels at the bottom and a knob for winding. The
duck is rolling forward with a slight waddle.
</pre>
<h3>
<a name="A3.2" id="A3.2">Permissions</a>
</h3>To allow other people to create their own
wind-up toys, we set the generic toy world-readable
and `fertile':
<pre>
@chmod toy rf
Object permissions set to rf.
</pre>This isn't really good enough, due to the screwinesses of the
MOO permission system. Generally speaking, when you create an
object, you become the owner of all its properties as well. This is
called "having c permissions". But that means that someone else
(including the owner of the *verb* on the object) can't change the
property. To get around this problem, we set the wound property !c
(meaning "not c"). Then the wound property always belongs to the
owner of the generic toy, and not to the creator of any specific
instance.
<pre>
@chmod toy.wound !c
Property permissions set to r.
</pre>It tells us what the permissions ended up being, after
stripping off the "c", which is "r" for readable.
</li>
</ul>
</li>
</ul>
</body>
</html>