Composite
Users
Guide
Composite Users Guide
Copyright 2001 by Kevin Forchione.
This manual is the copyrighted property of Kevin L. Forchione.
Permission is granted to distribute this material by digital and/or physical means
on the provision that the material is distributed in an unmodified form for
non-profit purposes. It is prohibited to redistribute any sub-section from this
material separately without the consent of the author. The author makes no
warranty of any kind with respect to this material, and disclaims all
warranties, including any implied warranties of merchantability or fitness for
any particular purpose, or the continued accuracy of this manual for future
versions of the product. I have made every attempt to make this documentation
accurate, but will not be held responsible for any loss of productivity
resulting from errors.
Manual version 1.0 (March
2001)
Written by Kevin L. Forchione,
e-mail: Kevin@lysseus.com
Composite.t is Copyright 2001 by Kevin Forchione for Lysseus
Dream Ltd.
Contents
And, of course, an examination of the desktop
reveals the axe.
Ever wanted to design a desk that has both a surface and a set of drawers? Composite class can help you do it! Simply
a. plug in the module after ADV.T/STD.T/ALT/PIANOSA/WORLDCLASS
b. add the initComposite() function to the preinit() function.
Suppose we want our foyer to have a wooden desk with a top and 3 drawers. Our desk is movable, but our drawers are not removable. Our desk would look something like the following:
desk: Composite, item
location
= startroom
noun =
'desk'
adjective
= 'wooden'
componentDesc = "wooden desk"
;
The first thing you’ll notice is that our desk derives from both Composite and item classes. Composite class derives from Component class, which is a mix-in class. This means that it doesn’t inherit from thing class, and must always come first in a class list.
Always list Composite and Component
classes first in object definitions
Also, desk doesn’t define any sdesc. Instead the description is handled by componentDesc. This is because Composite class uses sdesc to decide whether to use the object’s componentDesc or its parent object’s componentDesc as its description.
Only define sdesc in Composite and
Component class objects if you want to explicitly determine the object’s name.
Use componentDesc instead to allow Composite class to decide what the object
should be called.
The top of the desk is easy! The top of the desk derives from Component class and is a fixeditem surface. We can put things on it, but we can’t take it.
deskTop: Component, fixeditem, surface
myComposite = desk
location
= desk
noun =
'top'
adjective
= 'wooden' 'desk'
componentDesc = "top of wooden desk"
;
deskTop uses componentDesc just as the desk object does. In certain cases the desk top will be referred to as “wooden desk top” and in other cases simply as “desk”.
We also indicate its location as the desk. This isn’t a requirement of Composite class. Our desk is movable, and this means that the top will go wherever the desk goes.
Further, we indicate that desk is the Composite object for deskTop using the myComposite property. During preinit() myComposite is used to build a list of Components for each Composite object.
Components should use the myComposite
property
to indicate which object is their parent.
Now we need to define our drawers. We’ll have a top, middle, and bottom drawer. Our descriptions and vocabulary will be a bit verbose: “top drawer of wooden desk”, for instance, to illustrate that Composite knows to put an axe in topDrawer, neatly screening out any woodenChair that might be lurking in the room, or even the deskTop.
class Drawer: Component, fixeditem, openable
location
= desk
plural =
'drawers'
noun =
'drawer'
adjective
= 'wooden' 'desk'
pluraldesc = "wooden drawers"
myComposite = desk
isopen =
nil
;
topDrawer: Drawer
adjective
= 'top'
componentDesc = "top drawer of the wooden desk"
;
middleDrawer: Drawer
adjective
= 'middle'
componentDesc = "middle drawer of the wooden desk"
;
bottomDrawer: Drawer
adjective
= 'bottom'
componentDesc = "bottom drawer of the wooden desk"
;
The Drawer class derives from Component and defines the myComposite property. Each drawer then defines a componentDesc.
After we’ve compiled the module we should see something like the following:
Entryway
This large, formal entryway is slightly
intimidating: the walls are lined with somber portraits of gray-haired men from
decades past; a medieval suit of armor, posed with battle axe at the ready,
towers over a single straight-backed wooden chair. The front door leads back
outside to the south. A hallway leads north.
You see a wooden desk here.
>
Putting the axe on the desk is simple:
>put the battle axe on
the wooden desk
Done.
Next we attempt to put the axe in the desk and get a reasonably intelligent disambiguation question.
>put
axe in desk
Which
part of the wooden desk do you mean, the top drawer of the wooden desk, the
middle drawer of the wooden desk, or the bottom drawer of the wooden desk?
>wooden
Which wooden do you mean,
the top drawer of the wooden desk, the middle drawer of the wooden desk, or the
bottom drawer of the wooden desk?
>bottom
The
wooden desk is closed.
We’d better have a closer look at the desk:
>examine desk
It looks like an ordinary
wooden desk to me.
This is simply the default long description for the desk
object! There’s no mention of the desktop or drawers. This is because Composite
class doesn’t override any thing class properties other than sdesc. Any
mention of components in an examination will need to include them in the desk’s
ldesc method’s code.
Composite must determine which object, the parent or one of
its components, should perform the action. It does this through a simple set of
rules:
Determine the highest
Behavior Level for the object’s list of components. If the object defines a
Behavior Level greater than or equal to that of its components then the object
is chosen to complete the action.
If the object is not chosen
to complete the action then the component with the highest Behavior Level is
chosen to complete the action. If there is more than one object at this level
then we create a list of these components.
If we have a list of
components created from Rule #2 then check to see if any of these objects fail
verification and remove them from the list. If we are left with an empty list
use the original from Rule #2. Otherwise use the reduced list. If we are left
with 1 component then it is chosen to complete the action.
If the object was the direct
object for the verb then perform the command for each component in the list. If
the object was the indirect object for the verb then ask a disambiguation
question to reduce the list to 1 component.
The Behavior Levels mentioned in the Selection Rules above are straightforward, listed by order of their importance:
The object defines both
the verification and action method for the verb template.
The object only
defines the verification method for the verb template.
The object does not
define a verification method for the verb template.
Using these rules, examining the desk returns the desk as
the object that completes the action because verDoInspect() and doInspect() are
defined for the desk and all of its components.
Searching the desk, on the other hand, returns a different
result, because verDoSearch() and doSearch() are only defined for surface and
container class objects. The Drawers derive from openable class, which derives
from container class. Openable objects fail verification when they are not
open. So given a choice between searching two objects, one open, the other not,
the parser will choose the open object. Composite class behaves exactly the
same way.
>search desk
On the wooden desk you see
a battle axe.
But what’s the meaning of this display? The axe isn’t
actually on the desk, which is merely an item class object, and doesn’t define
doPutOn(). If you tried putting the axe on an item class object you would get
the default message, “There’s no good surface on the battle axe.” While the axe
is actually on the deskTop, the component’s description is being masked by the
use of its composite object’s componentDesc.
Acting on the component directly produces a different
message. In this case the explicit name is always used.
>search desk top
On the top of the wooden
desk you see a battle axe.
>examine desk top
On the top of the wooden
desk you see a battle axe.
According to our Selection Rules <<open
the desk>> presents the Composite class initially with 5 possible
candidates:
[ desk, deskTop,
topDrawer, middleDrawer, bottomDrawer ]
We begin in the desk’s dobjCheck() by first invoking
buildLists() and asking it to return a list composed of valid components and a
formatted verification and action method pointer list.
We must determine the verification and action methods for
the verb template, we do this by using verbinfo() and reformatting the output
into a standardized format for both one-object and two-object verb templates.
In the case of a two-object direct object verb template the
parser doesn’t constructs an action method. Most of the time this isn’t a
problem, since referring to the composite object as the direct-object of a
two-object command usually involves acting upon the composite object:
>put
the desk on the patio
Here the doPutOn() method of the direct-object is invoked
by the ioPutOn() method of the indirect-object. But we don’t want to simply put
one of the object’s components on the patio, we want to move the whole
composite.
There are instances where we need to know the direct-object
action method pointer.
>turn
the tv to channel 5.
If tv consists of a tuner and the television itself then we
are actually saying the following:
>turn
the tv tuner to channel 5.
During the formatting of the method pointers list we need
to assign an action method pointer for the direct-object in two-object
commands. Without these direct-object action method pointers the possibility
exists for a component to be bypassed by the selection process when indeed it
handles the action of the verb.
The Composite module provides action method pointers for
several verbs that might redirect the action of the indirect object to the
direct object.
When adding ioAction() properties to a verb, consider adding a
doActionPtr() method to return a direct object action method for the verb/prep
combination. This enables Composite to accurately identify the correct object
to use when the indirect object redirects action to the direct object.
We add the doActionPtr() method to a verb like this:
modify turnVerb
doActionPtr(prep) = {
switch(prep)
{
case toPrep:
return &doTurnTo;
case withPrep:
return &doTurnWith;
default:
return nil;
}
}
;
Once we know the verification and action methods we
retrieve the Behavior Level for the desk: BEHAVIOR_NONE. This becomes our
highValue. We build a nounList by checking highValue against the Behavior Level
for each Component in turn. If the Component has a Behavior Level greater than
highValue the list building starts over, adding the Component to the list and
setting highValue to the Component’s Behavior Level. If the list has elements
and the Behavior Level of the Component equals the highValue for the list then
it is added to the list. Otherwise it is rejected. At the end of the process we
have a list that looks like the one below:
[
BEHAVIOR_ACTIONED [ topDrawer, middleDrawer, bottomDrawer ]]
The Behavior Level for the entire list is
BEHAVIOR_ACTIONED, which means that all objects in the list define both the
verification and action methods for our verb. Next we check the verification
method for each object in the list. If
the object passes verification then it is added to a verifiedList. If the
verifiedList is not empty we replace the second element of our nounList with
it.
Finally, buildLists() returns a list composed of nounList
and verbList:
[[ BEHAVIOR_ACTIONED [ topDrawer, middleDrawer,
bottomDrawer ]], [ verDoOpen, nil, doOpen, nil, nil ]]
Because the Composite was used as the direct object of the
command we process all of its qualifying components:
>open desk
top drawer of the wooden desk: Opened.
middle drawer of the wooden desk: Opened.
bottom drawer of the wooden desk: Opened.
The process is nearly the same when a composite object is
the indirect object in a command. The sole difference is that the nounList must
be reduced to a single component. To do this we ask a disambiguation question and
then use TADS built-in functions to parse, disambiguate, and resolve the phrase
into a single object.
>put axe in desk
Which part of the wooden
desk do you mean, the top drawer of the wooden desk, the middle drawer of the
wooden desk, or the bottom drawer of the wooden desk?
>wooden
Which wooden do you mean,
the top drawer of the wooden desk, the middle drawer of the wooden desk, or the
bottom drawer of the wooden desk?
This second question is actually posed by the TADS parser.
The critical difference is that the Composite.disambiguate() method does not
allow for phrases such as “all”, “both”, “him”, “her”, “it”, etc. As we only
invoke this method for indirect object disambiguation, which requires a single
object, phrases such as “all” or “both” are inappropriate.
>top
Done.
>search desk
top drawer of the wooden
desk: In the top drawer of the wooden desk you see a battle axe.
middle drawer of the
wooden desk: There's nothing in the middle drawer of the wooden desk.
bottom drawer of the
wooden desk: There's nothing in the bottom drawer of the wooden desk.
top of the wooden desk:
There's nothing on the wooden desk.
>close bottom drawer
and middle drawer
bottom drawer of the
wooden desk: Closed.
middle drawer of the
wooden desk: Closed.
>search desk
top drawer of the wooden
desk: In the wooden desk you see a battle axe.
top of the wooden desk:
There's nothing on the wooden desk.
>close desk
Closed.
>search desk
There's nothing on the
wooden desk.
Notice the wording of the top drawer in the first and
second searches, as well as the use of object prefixes. Composite uses an
intelligent algorithm to determine when to refer to the object explicitly and
when to mask it behind its composite name.
Essentially object prefixes are used in multiple direct
object commands, and the objects are referred to explicitly using their
componentDesc. When these objects are the only one of their class (based on
first superclass) they are masked behind their composite object’s componentDesc.
If more than one component of a given class is being acted upon then they are
referred to explicitly by their componentDesc, both in the prefix and command
display portions.
There are several reasons why a component might not be
selected to complete the action of the verb:
1.
The component doesn’t point to the composite object with a
myComposite property.
2.
The composite is being used as a direct-object in a
two-object command, but no doActionPtr() was defined for the verb, or the doActionPtr()
doesn’t return a method pointer for the preposition.
3.
The composite object defines a Behavior Level equal to or
greater than the component.
4.
Another component has a Behavior Level greater than the
component.