!-------------------------------------------------------------------------- ! 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; <>; ]; [ NewConsultSub; consult_words = 0; <>; ]; 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; <>; ]; [ NoQuestionSub; <>; ]; [ 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) ! . !-------------------------------------------------------------------------- [ 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;