The following is a somewhat condensed and highly edited version of a
March '97 thread regarding the processing of mud commands entitled
"Mud Grammar".
I have attempted to arrange the dialogue in a cohesive manner with my
comments in << >>. Any mistakes or loss of context are likely due to
my cutting and pasting. Comments as to the suitability of this
format are welcome. Enjoy. :)
<<start thread>>
[Carter T. Shock]
I posit that, in general, command interpreters for muds are Bad
Things. The proposal runs sort of like this:
The command interface to a mud can be viewed as a context free
grammar. As such, one should be able to whip up a command
interpreter with lex/yacc. I've started on it at least a dozen times
but never managed to quite finish. Is this worthwhile? Thoughts?
[JC Lawrence]
A good MUD grammar is incredibly context sensitive. A simple example
is the case where the presence or proximity of an object adds verbs to
the players. There is no need for this crap to wander through the
global namespace, or even warp the general grammar. That approach
tends to preclude having a dynamic language where users or objects can
add, redefine, mutate or delete verbs and nouns freely at runtime. A
Very Bad Idea.
[Nathan Yospe]
<<Referencing JCL's comments>>
Agreed. An object in someone's possession (in one of their bags or
pouches, for example) might generate a certain set of new verbs for
the owner, and in hand, a new set of new verbs, i.e. a grenade might
enable "prime" as a verb, when held. I accomplish this by means of
references in my internal language attachment scheme. Another example
is: there is a horse in the room. The new verb "mount" has been
enabled. "mount", in my system, might refer to a real function not
normally accessed by the player, or to a bytecode function written in
my internal programming language.
[Chris Gray]
<<In response to Carter>>
I think you'll get a more rigid parser than you want for a MUD. You'll
also get one that you can't add verbs to while the MUD is running. If
you do it the classical lex/yacc way, you won't even be able to add
words to the language!
[Adam Wiggins]
<<In response to JCL>>
I don't actually agree with this. This is where you end up with
things like "push" as a social, except for one room on the entire mud
where it pushes a button. In another room with a similar button but
written by a different person, you have to use "press." I dislike the
effect you get from the old adventurer games of "guess the verb" - I
want to know what commands I have available to interact with my
character right from the start. Thus I know if I see a button in the
room, I can always use a certain command straight off to try to push
it.
[Chris Gray]
Actually I tend to agree with this quite strongly. Even though I've
got lots of exceptions in my system. I've played "guess the verb" way
too many times! However, that said, I think you do want to allow some
exceptions, else you end up with the different kind of "guess the
verb", where the player knows that verb "balance" is used to find out
their balance at a bank, but now they have to figure out what the verb
is to try to get the see-saw to stay entirely off the ground. If you
put everything into one complex verb, you end up with *no* modularity
in your system, and changing anything has ramifications everywhere.
Its also a lot more painful to change a complex verb on the fly than
it is to add another simple verb.
So, I vote for a balanced situation - have a good set of verbs in the
main grammar, but allow special case verbs and grammars as needed.
[Adam Wiggins]
It's a matter of consistency. So you can avoid this with good
building - i.e. make people always use "push" to push buttons, pull to
pull cords, break to break tripwires or doors or whatever. I'm a
proponent of writing the system so that this is, at the least, very
easy, and at the best, impossible to use non-standard verbs. Sure,
there is overlap...and you can decide things based on context. I.e.,
we have a verb, "draw", which is mostly used for drawing weapons from
their sheathes, but also can be used to draw a card from a stack of
playing cards. Since we keep track of what the user is "doing" and
what objects they have recently interacted with, it's pretty easy to
determine that when they type "draw" and their last targeted object
was type "cards", they're probably more interested in drawing a card
from the stack on the table in front of them instead of whipping out
their sword. (Naturally they can override this with "draw sword".)
Of course, the trick is to do all your commands consistently this way;
one thing you can end up with (particularly if multiple people are
coding different commands) are "smart" commands and "not-so-smart"
commands; when does it determine what, etc. But the important thing
here, to me, is that you never type "draw" and see, "Huh?" or "Invalid
command." Users can get help on the draw command and it will specify
exactly how it works. I suppose this is a bit odd with really
ultra-specific verbs like "prime" for priming grenades... but it seems
to me that if you have grenades in your game, probably you have other
types of explosives as well and "prime" becomes a pretty important
verb. The main thing I dislike is the quest-y verbs. Dikus are
pretty horrible about this; in one room you type "hack bush" to chop
through the foliage, and in another you type "cut path". In some you
type "disarm tripwire" and others you type "break tripwire", because
the first was a trap of type tripwire which uses the disarm skill, the
second is a room-routine specifically for that, which doesn't show up
anywhere else in the mud and doesn't use any particular skill. This
is bad because it's "outside" the system.
[Carter T. Shock]
<< In response to JCL>>
I think we have a misunderstanding here. There's grammar (the
structure of a language) and then there's vocabulary (all of the
individual words that make up the language). I agree that the
vocabulary is dynamic. I don't think the grammar is. I'll go one step
more and suggest that how you implement the various entities (people,
critters, and objects) in your mud can influence the success of such
an approach.
Lessee... in most mud worlds, the "nouns" are dynamic, but the verbs
are not. I put nouns in quotes because often a noun/adjective pairing
is required to uniquely identify an object (the long sword, the black
sword, etc.). The verbs in the system are your commands. cast, throw,
hit, etc. If we want to allow dynamic or generic verbs, now you're
getting into natural language processing and I definitely don't want
to go there. So there are some distinct sentence structures that
emerge:
<verb> <object> "hit foo"
<verb> <object> <object> "give gold foo" "cast fireball foo"
So the first trick is to define your grammar... establish a mapping of
verbs to appropriate objects. We'll start with "hit". In most codes if
you "hit foo" the code first sees what the allowable targets are for
foo, then tries to locate a foo somewhere in the world that satisfies
the target rules (get_person_room_vis(), get_obj_room_vis() whatever).
The basic flow doesn't change a whole lot. We still have classes of
objects... people, things, places, etc.
[Nathan Yospe]
Hmmm. I, personally, am for dynamic verbs. Thus, my natural (command
form only, so far) language parser.
My objects respond with a Boolean for recognized on a command,
resulting in a list of possible objects. The context engine then
locates the proper choice. shoot, for example:
<verb> <object> <meanskey> <means>
<shoot> <the purple dinosaur> <with> <the rocket launcher>
<verb> <object> <lockey> <location>
<shoot> <the purple dinosaur> <in> <the kitchen>
<verb> <object> <speckey> <specifier>
<shoot> <the purple dinosaur> <in> <its left eye>
<shoot> <the purple dinosaur> <'s> <left eye>
And these are the simple examples. Of course, unless there is a reason
to specify, and the time to specify (IE: set up a good shot) you
usually type "sho dino" and be done with it.
[Carter T. Shock]
<<continuing from above.>>
One solution is to write our lexical analyzer so it enforces target
rules. Another is to write objects so they can respond to any command
(I've done sort of a mix here. Done in C++, the base generic object
has a handler for all available commands, all of which respond "You
can't do that" or somesuch. Real objects are derived from the base.
For commands that make sense on the derived object, overload the
virtual default handler, multiple levels of inheritance work nicely
here).
An example of where this works very nicely is with magic. Identify the
verb "cast" as special in your lexical analyzer. Rather than <verb>
<object> <object> it becomes <verb> <spell> <object>
[Nathan Yospe]
Of course, this assumes preset commands, something I'd hate to have.
Instead, I have my String class parse things as above, and feed the
results to the verb and the various potential objects. An object will
respond if it recognizes a verb that _needs_ to be recognized (IE:
prime), but some verbs are universal, and never bother asking the
object for permission (IE: shoot).
[Carter T. Shock]
<<continues.>>
Now the question, why bother?
Well, first off, it can make the mud more user-friendly. Rather than
simply reporting "huh?" on bad commands, a true parser should be able
to:
1) report the exact location of the error in the command
2) offer up suggestions (tab completion of commands)
Now, we could do all of this without learning lex/yacc, but doing it
in lex/yacc gives you a standard interface.. not to mention someone
else has written most of the ugly code for you already. Btw, the newer
flavors of the GNU lexer/parser software (glex and bison I think)
offer up a C++ version that encapsulates the parser as an object.
Nifty side effect is that you can have more than one. So, if you
really want to screw with the user's minds you could conceivably have
different command sets in different parts of your world.
[Chris Gray]
<< in response to Carter's "I agree that the vocabulary is dynamic. I
don't think the grammar is".>>
Well, mine is. It was one of the first things that I needed. In the
mail room, there are added commands which are used to send email to
people outside the MUD and read your email. In the newsroom there are
added commands which allow you to read and post news on the local
machine, which, if the machine has a regular newsfeed like mine, will
actually go out over the Internet. I *didn't* want such commands
available everywhere on the MUD!
Another example is the way my online building commands work. The
system has a verb-type called "VerbTail", where everything in the
command after the verb is just passed to the verb as a string. I use
that with verb @/build/construct/b, so that that command can then
parse the remaining input according to another, completely different,
grammar. In fact, if the first word of *that* tail is object/o or
room/r, then yet more grammars are invoked on further tails. So:
build room new north indoors in the Solarium
invokes the 'build' verb. It sees the rest of the input and passes it
to the build grammar. The verb is then 'room'. It takes the rest of
the input and passes it to the room-building grammar. The room
building grammar has a verb 'new' which requires a direction, a
room-type and a room-name, all of which are handled as "rest-of-input"
processing. It's kinda tough to do this with yacc! (Well, maybe not,
but doing it this way keeps all mention of the verbs/etc. for building
out of the main grammar - modularity in all things!)
How about simple things like "whisper" or "say"? Sure, we all have "
or something as a special-cased shortcut for "say", but what about
whisper, where you are whispering *to* someone in particular:
whisper to Fred Let's attack Sam!
Doesn't fit in a yacc grammar very well.
I also have a 'with' verb, although I don't currently have any real
uses for it in the current scenario. It takes an object, identifies it
in your inventory/location, and then looks for a grammar attached to
that object to parse the rest of the input with. I intend it for
powerful magical objects, e.g.: "with staff zap the frog".
Right now, I have a 'cast' verb for wizards. It takes the next word in
the input, and looks it up as a procedure in the player's "spell
table", which is just another table of whatever's that can be attached
to the player or other things. If it finds a procedure by that name,
it invokes it, and that procedure has access to the rest of the input
command. So, I can do "cast heal Fred", or "cast teleport Fred here",
etc. Sure, you can put this in some main, overall grammar, but with
this technique, wizards, who can write new code in the running system,
can easily add new spell commands to their own spell tables.
In my 'build' example above, if you enter "build twiddle", the build
command can't find the verb 'twiddle' in the build grammar, so it
reports "Unknown build command 'twiddle'." Similar for "build room
twiddle".
My grammars are already database objects that I can retrieve from
wherever I want. I currently don't do what you are saying (other than
the bit mentioned above), however.
Another issue: do you even want to lexically scan all input the same?
By making it context sensitive, you have some greater flexibility.
[Carter T. Shock]
<<in response to Nathan's "purple dinosaur" example and Chris's
"whisper to Fred" example>>
As far as the purple dinosaur goes, in all of the examples, whether
you are shooting in the next room, the left eye or whatever, the
meaning of "shoot" doesn't change. lex/yacc grammars can easily
capture the meaning of modifiers such as "with" or "in". The
"whisper"/"say" example parses nicely as well. I agree that some
commands should not be useful in all parts of the mud. It seems unfair
to the player to have the mud simply refuse to recognize your news
command when not in the post office. For a consistent interface with
the player shouldn't the mud recognize the syntax of the command as
valid, but simply deny the command's execution (tell them that the
command doesn't work here as opposed to pretending the command doesn't
exist).
[Nathan Yospe]
OK. How about "Use the gun to kill the purple beast."
Say your guns are out of ammo. Under my system, the Character would
set to beating Barney over the head with the rifle. (unless you were
_really_ good at pistol whipping)
[Adam Wiggins]
Yup. Of course, the reason this works like this is for two reasons I
can think of right offhand. First is that frequently these specialty
verbs come from scripts attached to objects which you may have on your
person or whatever. The mud (of course) doesn't want to search every
object in the game for that script in order to figure out if it's
valid, it only checks the 'normal' verbs. But I've already expressed
why I don't think objects should have this kind of freedom.
[Nathan Yospe]
This is fine for normal commands, but what of new commands specific to
a situation? What of commands open to interpretation, based on
context?
[Carter T. Shock]
I agree wholeheartedly with the lament about whether to use "push" or
"press" for the damned button (that may or may not respond to the
keyword "button", but that's another issue entirely). It is the
grammar that is context free; individual sentences are of course
context sensitive.
[Nathan Yospe]
Not absolutely. If you have no gun, "Kill the soldier with the gun."
should at least make sure you _really_ want to make the foolhardy
attempt to take down an armed soldier (carrying a gun, of course) with
your bare hands.
[Carter T. Shock]
The point about using a natural language processor is also well taken,
but I don't think that's what you've implemented there. If you allowed
natural language, I should be able to use any synonym of shoot and
purple dinosaur...
Let's consider when the user tries:
"use gun on purple dinosaur"
This is an example of context sensitive grammar. What does "use" mean?
Shoot the dinosaur? Whack him over the head with it? Scratch his tummy
with the butt of the rifle? It's ambiguous. Natural language
processors try to do the "right thing" in these situations (is the
dinosaur friend or foe? Is the rifle loaded? Are you a decent shot?
Does his tummy itch?). If, however, you allow exactly one meaning for
"use" (let's say it means "shoot") then the sentence is unambiguous
and the grammar context free.
[Chris Gray]
Well, yes, but of course this is a computer program, so you can only
use synonyms that have been defined for the various terms. I don't
find that too different from a natural language - a listener only
understands synonyms that they are familiar with. Well, I think of
'use' as a very generic verb. It should do little other than look up
on the gun and the dinosaur, properties that tell it what it should
do. This could be any of the examples that you discuss. So, as you
say, the syntactic structure of the command is context free, but the
semantic content of it isn't.
[Nathan Yospe]
You don't use a rifle for much outside of mayhem, and certainly not
with the statement "use". If it isn't loaded, standardUsage becomes
"bash ". Of course, the catchall for this is "kill ". Nah, "use " is
a wonderful verb. Leave it.
[Adam Wiggins]
I don't mind the system assuming some things, for instance if I just
fired three shots at the dinosaur and I type "shoot" it will fire at
the dinosaur again. But I like systems which are obvious in their
workings; the user is never left guessing as to what is going to
happen when she types a given command, knowing the context. For which
I think the "<verb> <object0> ... <objectn>" works pretty well. Only
rarely do I even find use for connecting verbs, one of those cases
being "look helm on joe"; otherwise you can usually infer from the
type of object what the connecting verb should be. "get sword bag" is
the same as "get the sword from the bag", and "put sword table" is the
same as "put the sword on the table".
[Carter T. Shock]
Finally, don't think that I'm some devotee of lex/yacc. No reason you
can't cook up your own parser. I'm just suggesting that it offers a
relatively easy and structured implementation of a grammar. Also, once
created, it's easy to modify the grammar (change meanings, add new
verbs, etc.). It is similar to the question of how you handle your
mud's contents. Depending on how you've implemented the mud's catalog
of items, "where sword" may do an iterative search of the entire mud
contents, or it may do an SQL query into a set of tables. Both work,
the difference is a question of style and portability -- portability
not in the sense of cross-platform compilation, but in the sense of
when someone else looks at your code if they know lex/yacc they will
be able to understand the mud command structure quickly rather than
having to pore over your home-grown parser.
[Chris Gray]
I will comment that I, for one, would not find a lex/yacc system
easier to read than my home-grown one (obviously!). However, I would
also claim that the same would be true of anyone who has never used
lex/yacc, and of many who have. Given documentation that says that a
"Verb1" form takes commands like "verb [separator] [art] {adj} <noun>
[punctuation]" and "verb [art] {adj} <noun> [separator]
[punctuation]", with examples like "pick the big red book up." and
"Pick up the big red book", it seems pretty straightforward to add a
new "put down" verb. Much easier for most folks that reading
documentation that talks about avoiding shift/reduce conflicts!
<<end thread>>