!--------------------------------------------------------------------------
! FROBOZZICA: a demonstration by Gareth Rees
!
! The Inform library by default provides a system allowing "look up topic
! in book" and "consult book about topic", using grammar lines like
!
! Verb "consult"
! * noun "about" ConTopic -> Consult
!
! where the "ConTopic" routine just parses any old rubbish that the player
! might type, but sets the variable `consult_from' to the number of the
! first word in the rubbish. An object can then parse these words itself
! to determine what the player asked.
!
! However, this can get a bit tedious if a topic has many words relating to
! it and you don't care about an exact match, or if several books need to
! refer to the same topic. It would be better to get the parser to parse
! topics like objects. This example game shows how it can be done, and how
! a similar system can be implemented allowing conversational topics to be
! parsed in the same way.
!--------------------------------------------------------------------------
Constant Story "ENCYCLOPEDIA FROBOZZICA";
Constant Headline "^An interactive demonstration^by Gareth Rees^";
Include "parser";
Include "verblib";
Include "grammar";
!--------------------------------------------------------------------------
! TOPICS
!
! Each topic that may be looked up in a book or asked of an NPC is given an
! object of class `TopicClass' inside the object `Topics' (the contents of
! which are put into scope at the right time). The `TopicClass' makes sure
! that the topic's name is never printed, and so a correctly parsed topic
! cannot be distinguished from an unrecognised string of words. For
! example, "look up zork" may produce the question "What do you want to
! look up that in?" (if there were two books nearby), rather than "What do
! you want to look up the Zork in?", which would have given away the
! existence of the `Zork' topic.
!
! CAVEAT: if two topics share a word in common, then an attempt to look up
! that word will result in "Which do you mean, that or that?" Several
! solutions spring to mind: one is to avoid words in common wherever
! possible; another is to give the topics `parse_name' routines that don't
! allow the shared word on its own to refer to that topic.
!--------------------------------------------------------------------------
Class TopicClass
has proper
with short_name "that";
Object Topics "topics";
Nearby TopicZork "Zork" class TopicClass
with name "zork" "great" "underground" "empire" "gue";
Nearby TopicWizard "Wizard of Frobozz" class TopicClass
with name "wizard" "of" "frobozz";
Nearby TopicFlathead "Lord Dimwit Flathead" class TopicClass
with name "dimwit" "flathead" "excessive";
!--------------------------------------------------------------------------
! CONSULTING AND CONVERSATIONAL GRAMMAR
!
! We ensure that each possible pattern of input has two grammar lines
! associated with it, one involving a `scope=TopicScope' that parses topics
! as objects, and another using `ConTopic' or `ConTopicI' or so on, that
! reads any number of words (possibly stopping at a preposition).
!
! The idea is that if the topic is recognised, then the first grammar line
! will match, and generate a `NewConsult' or `Question' action. But if the
! topic is not recognised, the second line will match, and generate a
! `Consult' or `NoQuestion action. The second type of line acts as a
! `catch all' so that the player can't find out which words are valid
! topics except by looking them up in the correct book or asking them of
! the right person.
!
! There is a third type of grammar line, marked with (*), which can never
! be successfully parsed, but which is there to provide good error
! messages. For example, given the grammar lines
!
! Verb "read"
! ...
! * "about" ConTopicI "in" noun -> Consult ! (1)
! * "about" ConTopic "in" noun -> Consult; ! (2)
!
! and the input
!
! read about aardvark
!
! then the grammar line (1) can't match (because there was no word `in' in
! the input). If line (2) weren't present the error message would be "I
! only understood you as far as wanting to read about." But the ConTopic
! in line (2) matches the `aardvark', the preposition `in' is inferred by
! the parser, and it asks the question "What do you want to read about that
! in?", a much more acceptable error message.
!
! The grammar line (+) is necessary for "look" on its own to continue to
! work.
!--------------------------------------------------------------------------
[ TopicScope;
if (scope_stage == 1) rfalse;
if (scope_stage == 2) {
ScopeWithin(Topics);
rtrue;
}
"** Error: input should have matched a later line in grammar **";
];
[ NewConsultRSub; <<NewConsult second noun>>; ];
[ NewConsultSub; consult_words = 0; <<Consult noun>>; ];
Extend "look" first
* -> Look ! (+)
* "up" scope=TopicScope "in" noun -> NewConsultR;
Extend "look" last
* "up" ConTopic "in" noun -> Consult; ! (*)
Extend "consult" first
* noun "about" scope=TopicScope -> NewConsult
* noun "on" scope=TopicScope -> NewConsult;
Extend "read" first
* "about" scope=TopicScope "in" noun -> NewConsultR
* scope=TopicScope "in" noun -> NewConsultR;
Extend "read" last
* "about" ConTopic "in" noun -> Consult ! (*)
* ConTopic "in" noun -> Consult; ! (*)
[ QuestionSub; if (RunLife(noun,##Ask)~=0) rfalse; "No reply."; ];
[ RQuestionSub; <<Question second noun>>; ];
[ NoQuestionSub; <<Question noun 0>>; ];
[ SaySub; "Nothing happens."; ];
[ ConTopicPrep prep w; consult_from = wn;
do w=NextWordStopped(); until (w==prep or -1); if (w==-1) return -1;
wn--; consult_words = wn-consult_from;
if (consult_words==0) return -1; return 0; ];
[ ConTopicTo; return ConTopicPrep('to'); ];
[ ConTopicAt; return ConTopicPrep('at'); ];
Extend "ask" replace
* creature "about" scope=TopicScope -> Question
* creature "for" scope=TopicScope -> Question
* creature scope=TopicScope -> Question
* creature ConTopic -> NoQuestion;
Extend "say" replace
* scope=TopicScope "to" creature -> RQuestion
* scope=TopicScope "at" creature -> RQuestion
* ConTopicTo "to" creature -> NoQuestion
* ConTopicAt "at" creature -> NoQuestion
* ConTopic "to" creature -> NoQuestion ! (*)
* ConTopic -> Say;
Extend "tell" replace
* creature "about" scope=TopicScope -> Question
* creature "about" ConTopic -> NoQuestion
* creature scope=TopicScope -> Question
* creature ConTopic -> NoQuestion;
!--------------------------------------------------------------------------
! THE GAME
!
! Some example objects with which to test the above definitions. The
! extracts from the Encyclopedia Frobozzica have been lifted from the text
! written by Nino Ruffini, and available on the WWW at (for example)
! <
http://www.spies.com/harrison/frobozz.html>.
!--------------------------------------------------------------------------
[ Initialise;
location = Library;
"^^^^^Everything you wanted to know about Zork but were afraid to \
ask...^^";
];
Object Library "Library of Zork"
has light
with description "A vast chamber, filled with the knowledge, legends \
and lore of the Great Underground Empire.",
cant_go "You wander for a while among the bookstacks, but can't \
find a way out.";
Nearby Encyclopedia "Encyclopedia Frobozzica"
with name "encyclopedia" "encyclopaedia" "frobozzica" "book",
description "The Encyclopedia is so packed full of amazing facts \
that you'll have to look them up one at a time.",
before [;
NewConsult:
switch(second) {
TopicZork: "Formerly known as Quendor, the Great Underground \
Empire reached its height under King Duncanthrax, began \
declining under the excessive rule of Dimwit Flathead, \
and finally fell in 883 GUE. The area is now called the \
Land of Frobozz, after its largest province.";
TopicWizard: "The Wizard of Frobozz was once a member of the \
influential Accardi chapter of the Enchanters' Guild. \
This Wizard was a strange little man, usually wearing a \
long cloak, a high pointed hat with astrological signs, \
and a long stringy beard. Once a court wizard, he was \
exiled by Dimwit Flathead after accidentally turning \
Flathead's castle into a mountain of fudge.";
TopicFlathead: "Lord Dimwit Flathead the Excessive, the \
great-great-grandson of King Duncanthrax, ruled the Great \
Underground Empire from 770 GUE to 789 GUE. He was the \
seventh king of the Flathead Dynasty, coming to the \
throne after Mumberthrax, and before Loowit.";
}
"You can't find what you want in the Encyclopedia.";
];
Nearby Librarian "librarian"
has animate
with name "librarian",
description "His long beard, fiery eyes, and pointy black hat \
with stars on suggests that he may be more than he seems.",
life [;
Ask:
switch(second) {
TopicZork: "~Zork doesn't really exist. It's just a bunch of \
adventure games by a company called Infocom.~";
TopicWizard: "~Fie to that flamboyant fellow in a fez! His \
phantasms and fiends are but flippant fibs, foolish \
fictions and flights of fancy.~";
TopicFlathead: "~What a dimwit he was!~";
}
];
End;