Phantasmal MUD Lib for DGD
|
Phantasmal Site > DGD > Writing a Library > Object Binding Binding Object Names to Objects While ParsingThis activity is also called "Object Binding", and you can find a longer explanation of what it is and what it does in Richard Bartle's article on the topic. Date: Sun, 4 Apr 1999 03:04:53 +0200 (CEST) From: "Felix A. Croes" To: DGD Mailing List Subject: Re: [DGD]parse_string "Erlend M. Simonsen" wrote: > I've been spending some time this easter trying to get my MudOS-like > parser using parse_string() up and running. And I'm ALMOST happy with > it. I've run into two problems, which I thought I'd see if any of you > had an answer to. > > 1. > When trying to get an object from a container I have the problem that > when my find_object() function is called, I have no idea which > environment to look for the object in. And I can't just scan all > containers in the environment looking for the object, since I might > end up with the wrong object if several instances of it is present. This same problem must have existed in the MudOS parser. I assume that it was solved in one of the following ways: 1) process all possible environments in a standard order. 2) if the grammar rule for an object should check an environment that depends on the greater context in which the rule is used, split it up into several rules where the grammar part is identical, but the LPC function called to find the object differs depending on the context. 3) postpone finding the object at the object rule level. Instead, find it at the sentence level, where the environment is known. The most useful and versatile solution is probably 2). It can be done by adding several intermediate object finding rules: object_in_carried_container: object ? find_in_container object_in_room: object ? find_in_room object_in_living: object ? find_in_living > I've been able to work around this due to a 'fortunate' parsing order > in parse_string(). It seems like parse_string() goes right to left, > which means it finds the correct container first, which I can remember > until I go looking for the object. It sounds a bit 'hackish' to me, > and I would like to do this the proper way. More precisely: parsing happens left to right, but LPC functions are called in bottom-up right-to-left order. It is not clear to me why this always picks the proper container. Are you sure that this is actually the case? > 2. I want to call can_get_obj_from_obj() and the like. The problem I > am having here is that I know nothing about the rule that is matched, > so I build the function name by checking the type of each element in > the result from parse_string(). Which basically breaks all STR > (string) parts of the rules. > > What I tried doing when building the function to call, is to treat all > strings as 'str', but that gives me function names like > can_buy_str_str_liv(), and that doesn't look too nice. I would rather > have it look like is can_buy_str_from_liv(). Anyone have solved this > problem and would like to share with me how they did it? I don't fully understand the problem as you described it, but my guess is that the solution I gave above will work here too: add intermediate rules that differ only in the LPC function called. Regards, Dworkin From: "Jason Cone" To: DGD Mailing List Subject: RE: [DGD]parse_string Date: Mon, 5 Apr 1999 11:05:58 -0500 > -----Original Message----- > From: Erlend Simonsen > Sent: Saturday, April 03, 1999 5:36 PM > To: DGD Mailing List > Subject: [DGD]parse_string > > > I've been spending some time this easter trying to get my MudOS-like > parser using parse_string() up and running. And I'm ALMOST happy with > it. I've run into two problems, which I thought I'd see if any of you > had an answer to. > > 1. > When trying to get an object from a container I have the problem that > when my find_object() function is called, I have no idea which > environment to look for the object in. And I can't just scan all > containers in the environment looking for the object, since I might > end up with the wrong object if several instances of it is present. With MudOS, every object is required to register itself with the driver as an object that can be targeted by the parser. Thus, you can effectively eliminate certain objects that shouldn't be directly interacted with in the game. In the event that multiple (registered) objects of the same class exist and are being targeted via a certain parsed sentence, it's up to the parser (not the actual grammar, but the functionality surrounding the grammar) to determine which object is flagged as the "found" object. DGD & parse_string() benefit heavily in this area as you can define this behavior based on what you want to accomplish. For example, in a 3D space, you could return the object that is "closest" to the person issuing the command or if the object exists in the person's inventory, you could return the object that was last used, etc. etc. This isn't possible with the MudOS parser; order of precedence is defined for you. All that to say, your find_object() function is going to have to take care of that for you. > 2. I want to call can_get_obj_from_obj() and the like. The problem I > am having here is that I know nothing about the rule that is matched, > so I build the function name by checking the type of each element in > the result from parse_string(). Which basically breaks all STR > (string) parts of the rules. > > What I tried doing when building the function to call, is to treat all > strings as 'str', but that gives me function names like > can_buy_str_str_liv(), and that doesn't look too nice. I would rather > have it look like is can_buy_str_from_liv(). Anyone have solved this > problem and would like to share with me how they did it? The following description is how CornerStone's verb system works (I'm returning to its development after a hiatus of sorts). Each verb exists as its own file ("look" == "/bin/verbs/_look.c"). /bin/verbs/_look.c inherits /std/inherit/verb.c which contains all the functions to set error messages, verb rules (described in a minute), and perform the actual verb-related actions. All verbs are handled by a verb daemon that also keeps track of the verbs' grammar objects (described later). Each verb is responsible for registering the rules that will be used for parsing out the input. For example, the "look" verb would register its rules via add_rules(({ "", "OBJ", "DIR", })); The token values will be described in a minute, but the above is called when the object is created. When a player issues a command and the first word of the command line is equal to a registered verb, the daemon will do the following: * Take each rule for the verb ("", "OBJ", "DIR") and query a rule daemon for an object that contains the actual grammar for that rule. This way, multiple verbs can use the same rule objects to take advantage of the processed parse_string() grammars. * The rule object will parse the input string (minus the first word) and call the necessary functions in the verb object. Take the "OBJ" rule, for example. If the input matches that rule, then it will call can_look_obj in the verb object. If that function returns 1, it will call do_look_obj in the verb object to perform the action. If either the can_ or the do_ functions returns 0, then the verb rule will fail and the verb daemon will proceed to the next verb rule for that verb. It was a real pain to design the verb system this way, but it's really turned out to be a blessing for (mainly) 2 reasons. 1) It's easier to maintain the code for the overall system by breaking delegating functionality to different subsystems. I also use the rule daemon for a trigger system (an add_action-like system that can optionally use verb-like rules). 2) You can dynamically add/remove rules from particular verbs without having to rebuild grammars. Some may say using multiple grammars instead of one huge grammar for every possible verb is inefficient, but I feel the maintainability and administration value of having it broken up outweighs that consideration. Ok, anyway, after that long diatribe, let's see if I can actually answer your question. Here are the tokens that I use in my system: OBJ Evaluates to: Object found in immediate inventory OR immediate environment Return value: Object LIV Evaluates to: Living object (NPC or other player) in immediate environment Return value: Object OBJI Evaluates to: Object in the caller's inventory Return value: Object OBJE Evaluates to: Object that is contained somewhere in the caller's immediate environment Return value: Object OBJA Evaluates to: A hypothetical object in the player's immediate inventory or immediate environment Return value: Ambiguous object packet ({ ({ Adjectives }), Noun, Object Index }) DIR Evaluates to: A legitimate direction according to the verb. Return value: String The only token I'm compelled to really describe in detail is the OBJA token. This token allows you to extract a noun (object) from the input command/sentence without it actually existing as a loaded object. Thus, if I type "look at the blue piece of paper", the piece of paper doesn't need to be an actual object that DGD is aware of whereas all of the OBJ* tokens evaluate to actually loaded objects. Dworkin answered your question pretty well with the "buy STR from LIV". Though, I would highly recommend separating the verb from the rules; "buy STR from LIV" should be "STR from LIV" and be associated with the "buy verb. This would allow rule reuse (I'm an OO freak, I guess). The one challenge I'm facing is how to handle chained tokens. For example, what would a rule look like that could evaluate something like "look at the first note on the board"? The obvious solution would be to have a rule that looked like "OBJ on OBJ". But what happens when the depth is really unknown? Could it be possible to have an OBJS token that could handle any number of objects that appear in relation to each other? Food for thought... I would love to hear ideas if any exist. Anyway, I think I've rambled entirely too long and probably haven't answered anything directly. JC From: Par Winzell Subject: Re: [DGD] Algorithm for Parsing Commands Date: Sat, 04 Sep 2004 07:49:11 -0500 > First, a question; for a long time, the paradigm has been to have a > basic command parser determine which verb to use, and then call that > verb function, which would parse the command further. What is the > reasoning behind this? Well, no. As I recall, by 1994, it was well established among those who spent their time thinking about such things that a global, static list of verbs is much more intuitive than e.g. LPMud's notion that rooms can add a verb that doesn't work elsewhere, or works differently elsewhere. The Skotos mudlib, my only hands-on experience with a modern mudlib, makes a very clear distinction between grammatical constructs, which are part of the 'text-based player interface' module, and 'actions' which belong in the 'virtual world' module. The code that deals with grammatical constructs (i.e. parses verb phrases) is preoccupied with the nearly-infinitely complicated task of unravelling the vagueries of the English language and produce useful error output when something's off. The code that deals with actions needs know nothing about language. As a simple example, 'light light'. Chances are the TextInterface module would turn this into either ACT_FLIP(light_switch) or ACT_IGNITE(torch). The actions are never ambiguous, and they could be generated by NPC scripting code just as easily as they are generated by the parser. Anyway, that wasn't your main question. :) > Assumption: Articles are treated as whitespace This assumption isn't necessary; adding article support is one of the simpler extensions. If you get 'the', you require that the noun resolves to a single object. If you get 'one' or 'a'/'an', you pick a random one. If you get 'six' you return six random ones. If you get 'sixth' you pick the sixth... etc. > 2) Determine which words are verbs, by comparing them to a list of > suitable verbs (this takes into account synonyms for verbs) > a) If there is no suitable verb, produce error output Yes, verbs are terminals in our grammar as well. > I know that this, so far, is still relatively simple; it can handle > "verb adjective noun preposition adjective noun" sentences all day > long. I would like to make it more robust, to handle sentences such as; I think you've pretty much got the essentials down. Parsers do double in complexity every time you try to make them smarter, but the examples below seem like fairly minor additions: > "cast gnusto at spellbook" - gnusto is a noun, but would forever be > missed because it doesn't exist in the player's environment SkotOS splits a sentence down by verbs, prepositions and articles. Those are grammatical constructs, as opposed to nouns and adjectives which are more or less free form. Since we allow any number of prepositions in our sentences, you can no longer usefully talk of 'the noun' or even 'the direct vs the indirect noun'. We had to come up with a new term, 'role', a single word label to describe the purpose of a noun in a sentence. Thus: 'wave my sword at the sky' and 'wave at the sword with my sky' will resolve to the same thing; when parsing is done, the sword will be stored under one label, the sky under another. This remapping of grammar construct to logical construct makes it a lot easier to write scripts -- the sword should obviously be able to respond identically to be shaken at the sky regardless of which method players use to shake it. To handle the 'gnusto' case, SkotOS introduced 'raw roles', where the parser would return the raw words rather than attempt noun resolution on a subset of the sentence, once it had finished chopping it up into roles. Thus the 'cast' verb could have two roles defined, 'spell' and 'target' perhaps. The 'to' preposition would be configured to map to the 'target' role for that verb, and the 'spell' role would be configured as a direct noun. The 'spell' role is then marked 'raw', to stop the parser from trying to interpret it, and "gnusto" will be sent on uninterpreted for some other code to deal with. > "wear red cloak from burlap sack" - Would only work if there was > preposition/adjective/noun checking in "wear". SkotOS by default will search the player's environment and inventory for noun resolution, but before it does the search it looks to see if there is a Merry script defined on the verb object (each verb's configuration is stored in a separate object) that wishes to replace the normal search operation for the current role. If there is, the script may return an array of objects whose inventories to seek. This is needed for something as trivial as 'take cup from bag'. > "read newspaper by candlelight" - another sentence that would just fail. > "check settings on dial" - and another sentence that would just fail. Both these would be handled by 'raw roles'. The only problem is that it is sometimes unpleasant to have to give up all noun parsing for a role in a verb. For the 'check' example, you'd sort of like 'check my sword' to still perform basic noun lookup, but if you mark the direct object role of check as 'raw', it's always going to be raw. That remains an unsolved irritation. Many other designs are no doubt possible. Zell From: dgd@dworkin.nl (Par Winzell) Date: Tue Apr 19 17:04:01 2005 Subject: [DGD] Inheriting the same program twice Petter Nyström wrote: > But your mail have had intended effect, and I find myself indeed > reconsidering. I will for sure give it some more thought. Maybe I will > come up with something that I feel sure enough about to replace my > current plans. > > I know that you at Skotos have done things like this, and if you have > any online material covering this, please point me there. (I vaguely > recall reading some article over at your website, but can't find it in a > brief search.) I suspect the DGD mailing list is probably as likely a place to find mails describing this as any internal Skotos documentation. Anyway, the idea is very simple, and I don't mind re-summarizing it. The Skotos approach -- and so far it looks like this will remain the architecture in SkotOS 2.0 -- begins by cleanly separating the virtual world from any interface considerations. The simplest way to keep this abstraction clean is to always imagine maintaining a Quake interface to the virtual world as well as the text interface, and ideally also make sure a NPC can react to anything a player can react to without having to parse text. A simple example: - I type 'gently place my sword on the ground' onto my command line. - My user object, which is a clone of an object in the TextIF module, sends my command line to the central text parser. The parser knows there is a place verb, it knows the place verb can take a direct object as well as an object prepositioned by 'on', and it finds that I am carrying precisely one sword. It finishes the interface input handling by ordering my body to execute the general action ACT_PLACE, which takes an object in my possession and a destination as arguments. Note: * Everything is an object pointer, at this point; the text input is long gone. * The ACT_PLACE action could equally well have been caused by the QuakeIF module as a response to e.g. right-clicking on an object in your inventory in the GUI, or it could have been triggered by a NPC as part of a script. In neither of these cases is text involved. * The injection of ACT_PLACE into the virtual world could be done in a zero-second callout without problem, drastically reducing the danger of thread contention in a DGD/MP world. - In the virtual world, ACT_PLACE performs a minor state change -- it atomically changes the environment (and perhaps proximity) of the object in question. But -- vitally -- it finishes by firing off a state change notification event to any object that cares. - Almost certainly any player within view does care, and so in practice control passes back to the interface modules. The TextIF module, for example, is now asked to describe an event that occured in the virtual world. It doesn't care that in this case it ordered the action a few milliseconds ago; the action might just as well have been ordered by a Cursed Ring of Sword Dropping. - Various hideously complicated text generation algorithms are now run which end up telling the player that he gently placed a sword on the ground; the sword that the player gently placed it on the ground; the ground that the player gently placed a sword on it, and any witness that the player gently placed a sword on the ground. * Alternately, any observer that happens to be running the Quake interface, receives a stream of UDP packets ordering the client to show me placing my sword on the ground. Or an NPC can directly respond to the event by subscribing to it. * Every event description can occur in a separate zero-second callout... as long as there is some sanity to the order in which the callouts are processed. I have yet to really think this through for DGD/MP, and I'm a little nervous about it. OK, so that all sounds very complicated compared to the 2.4.5 approach now that I type it out. There are probably other modern approaches that do not rely on a central parser... but I'm not sure those would be any simpler, and I really like the clean design of this one. Zell |