|
|
from issue #16 Tales from the Code Front: Parlez-Vous Nalian?Lucian P. Smith, author of The Edifice, was awarded the 1997 XYZZY Award for Best Puzzle for the game's language puzzle. In the following article, he describes how he developed and built this compelling feature into The Edifice's plot and gameplay.One of the better gaming decisions I've made in my life so far was to set up the second level of The Edifice so the player would have to learn a new language. It seemed natural enough -- after all, what characteristic of early human development is more important than the ability to communicate with others? This article attempts to trace that idea from its initial spark to its final implementation. I'll address the code in pretty general terms here, but I've re-coded this puzzle as a separate module for anyone interested in exploring it in greater detail and uploaded it to GMD, where you should be able to find it at: ftp://ftp.gmd.de/if-archive/programming/inform6/library/contributions/nalian.inf Not all of the instances described below are in that module, but all the tricky parsing is. If you have additional questions after reading this article and perusing the module, please feel free to e-mail me directly. Before introducing the new language in the game, it was necessary to set up the proper context. All great discoveries are driven by necessity, so I made the scenario pretty basic: your son is sick, and needs Feverleaf, which you can't find in the forest on your own. Enter Stranger, who, the player should suspect, might have some. At this point, I could tell the player's goals were going to be twofold -- first, they would have to communicate to Stranger that their son is sick, and secondly, they would have to discover Stranger's word for 'Feverleaf', so they could ask for it. But a language with just the words, 'son', 'sick', and 'feverleaf' would be pretty dull. At the opposite extreme, I wouldn't want the player struggling through a 5,000 word vocabulary just to find those three words! So the vocabulary I chose had to be large enough to be interesting, but not so large as to be overwhelming. I also had to keep in mind that I'd have to code up responses to every reasonable sentence the player constructed, so I wanted to keep that number within reason. I turned to nouns first. The basic three pronouns were a necessity; I decided that one word was sufficient for he, she, and it, and also chose to ignore plurals. 'Son' and 'feverleaf' were necessities, of course, and I included a few other items in the area -- 'home/hut/house', 'weapon', and 'panther' (since I'd given Stranger a panther skin to wear, it only seemed logical that he would be proud of the fact that he managed to kill it, and would want to tell the player so.) Adjectives were next -- 'sick' was one of the basics, along with my, your, and his/her/its. 'Good' and 'bad' seemed pretty basic, and I rounded those off with 'strange' (his probable adjective of choice for the player), and 'dangerous' (his adjective of choice for himself). For verbs, I included 'give' and 'take'; these terms seemed to be necessary for an exchange to take place. If the player shows up with a weapon, Stranger gets angry, so 'leave' and 'threaten' were logical next choices. Once the player starts to communicate fluently, 'understand' would seem to be important, along with 'see' for good measure. Throw in 'is' with 'yes' and 'no', and I've created myself a rudimentary language! Creating the actual words was really rather fun. To ease the translation process, I gave each part of speech a common ending, with the exception of putting a few different options in for nouns. This led to logical connections between the words for 'I' and 'my', for example, further easing the player's task. To keep the language even more consistent, I tried to keep common words short, and I had fun with some of the roots -- 'panther' and 'dangerous' have the same root, for example, and the word for 'understand' comes from the words for 'take' and 'I'. For grammar, I kept it simple -- there is none. No new word endings for different parts of speech, and no significance attached to word order. This makes Nalian a poor choice for creating complex sentences, but I wasn't planning on doing that anyway. At this point, I was ready to start coding. Since most of Stranger's speeches would come from player input, I first needed to figure out how to correctly parse player input. Standard Inform parsing techniques were wholly inadequate to the task at hand, so I turned to the Inform properties 'grammar' and 'life'. Unfortunately, I had to copy some code here, but this wasn't terribly extensive. I got Inform to tell me where the player's sentence began and ended, and set up a loop to deal with each word of player input. There were four possibilities for each word the player typed, and all four needed unique responses: The word could be Nalian, 'English' (understood by the parser), gibberish (not understood by the parser), or punctuation. The appropriate responses would be recognition for the first, partial recognition for the second, bewilderment for the third, and the last should be ignored. The first order of business was to check if a typed word was in Nalian. After a brief unhappy experiment with byte arrays, I turned back to the old mainstay of IF programmers: objects, rooms, and containers. Each Nalian word became an object, stored in a new 'LanguageRoom'. A container called 'Sentence' served to collect player input -- any time a word-object was matched, it got dumped into the 'Sentence' bucket -- the new repository of player input. Gibberish words were easy to recognize -- I got a value of '0' for any word not in the parser's dictionary. English words were similarly simple -- any non-zero value not caught by my earlier 'Nalian' check. With punctuation, however, I ran into a problem -- I was getting '0' again. In addition, any attempt I made to try to put punctuation 'words' into the dictionary resulted in failure -- sometimes it even caused Inform to not recognize commas and periods at all any more! My solution for the contest was to hack the library. I found that the routines 'FirstWord' and 'FirstWordStopped' were throwing away any punctuation information they came across, so I changed this behavior for my calls to these routines from 'grammar' and 'life', being sure not to change the default behavior for normal library calls. Even this turned out to be inadequate, however. One of the bug reports I received from participants in the 1997 IF competition was that when the player tried to ask Stranger questions in Nalian, Stranger failed to understand them. This turned out to be because while Inform separates commas, periods, and quotation marks from the words they adjoin, it does not do this for other punctuation. Hence, the input:
>SAY "NA BEN LALSE," TO STRANGER gets separated as:
SAY " NA BEN LALSE , " TO STRANGER and the input:
>SAY "NA BEN LALSE?" TO STRANGER gets separated as:
SAY " NA BEN LALSE? " TO STRANGER
^^^^^^
Since 'lalse?' was different (to Inform) from 'lalse', the parser was failing to understand it as valid input. After a plea to the newsgroups, Torbjorn Andersson kindly answered and told me I could use the routine 'LanguageToInformese' -- a routine commonly used for foreign languages! In this case, it was a simple matter to convert question and exclamation marks to periods, since I was ignoring punctuation anyway. At this point, I was able to flag English and gibberish words, and had all Nalian words stored in 'Sentence'. Now came the hard part: coding responses to the various sentences! Examining the vocabulary to this point, I found that Nalian had a 25-word vocabulary. Not too large, but let's see: accounting for all possible three-word sentences results in 25 times 24 times 23... 13,800 potential sentences! Yikes! Clearly, I was not going to be able to code up 13,800 responses, so I had to resort to more devious methods. First of all, I set up responses for when there was only one word in Sentence. 25 responses were a lot, but manageable. Next, I reorganized the remaining sentences, putting verbs first, then nouns, then adjectives. For sentences with no verbs, I assumed an implied 'is' between the noun and any adjectives in the sentence (Sentences with nothing but strings of nouns or strings of adjectives could be dealt with with a simple response indicating puzzlement, and a general clue as to how to better use Nalian; likewise with sentences with more than one verb.) Since the presence of the word 'is' now didn't mean anything, if my routine found 'is' in the Sentence with other words, it could just throw it out and start over. In the same manner, the presence of the word 'yes' wouldn't change the essential meaning of the sentence, so I could throw that out, too. So, with eight nouns and eight adjectives, that meant only 64 responses -- some of which could be concatenated, and some of which had to be expanded. Nouns and adjectives which were crucial to communicating the player's plight were singled out (son, sick, feverleaf, and my) so that my routine would assume the player's sentence was basically correct if it contained the important bits of information -- Stranger is, after all, working to understand the player. However, to prevent abuse of this concession, I made Stranger unable to understand sentences that were more than five words in length, complaining that he couldn't follow your terrible accent all the way to the end. This allowed the potential sentence "Yes, my son is sick," to be understood, but not sentences of any greater length. For sentences with one verb in them, I focused my responses on one noun that followed. With my reorganized sentences, this meant that Stranger was more likely to pick up on certain key words than others, if the player tried more than one. By leaving them in, though, I could tailor certain responses to acknowledge more than just two key words. For sentences with only verbs and adjectives, I gave a general response that essentially told the player that his sentence didn't make much sense without a subject. With six verbs and eight nouns, this meant 48 basic responses here, some of which, again, were concatenated, and some of which were expanded. (For example, when the player used the word 'son', it was important to check if the word 'my' was also used, to distinguish the boy standing next to Stranger from the player's sick child.) Finally, I had to deal with the word 'no'. By this point, I had already written the bulk of Stranger's responses and was getting rather tired of it. This one simple word opened up a whole new unwelcomed vista of potential -- so I cheated. If the player used the word 'no' and a verb, Stranger took that as a command not to do that verb, and just stood there. If there were no other verbs in an otherwise reasonable-sounding sentence, Stranger simply acted disinterested. This was not ideal, but I really didn't want to code up another 64 responses for the nouns and adjectives. Even so, in the end this section takes up 52K. In the setup above, I showed how I had to condense Stranger's vocabulary so that the number of responses I had to code up was manageable. In actually writing the responses, however, I had to expand his vocabulary conceptually so it could express everything I wanted it to. One trick I used was to use 'yes' as an emphasis word. "I am dangerous," then, became "I am yes dangerous." To de-emphasize like sentences, I'd leave out 'is' -- "I dangerous." 'good' became the embodiment of every positive force in the universe, and 'bad' became the opposite. Generally, every word became much more than its original English counterpart. Also, I tried to use a lot of body language. With this context, individual words would be (hopefully) easier to figure out. When the player spoke just one word, I often had Stranger mime a response that illustrated that word. I also decided that nodding meant 'yes' to both of them, and shaking one's head meant 'no'. This cleared up another problem I had had -- when Stranger asks the player a question, how should I respond to the Inform verbs 'yes' and 'no'? Printing out 'you nod your head,' and 'you shake your head,' before giving Stranger's response turned out to work nicely here -- and also meant I had to add three new verbs, "Nod head," "Shake head," and "Shrug." Finally, I had to code up responses to the player showing Stranger things, or pointing at items. This was almost laughably easy, with a mere dozen items on that level. Unfortunately, this was where one of my game-crashing bugs showed up, if the player showed Stranger an unexpected item (like pointing in a direction, for example.), due to the way I cross- referenced the verbs 'Point' and 'Show' (If there was no default response for one, it called the other, and visa versa. Oops.) So, with all this analysis, have we discovered why this puzzle was so popular? Probably not. The analysis points to areas where it could have gone wrong, but not to the essence of what made it fun. Having not solved it myself, I am in the unique position of knowing the ins and outs of the puzzle intimately, but not having ever directly experienced it. I didn't even know if someone *could* solve it, or what paths they were more likely to take if they did! However, as best as I can figure from talking to people, I think the basic reason it was fun was that it let the player figure out a system, and then manipulate that system to produce a desired result. Making the system interesting and responsive were essential, but the basic concept is inherently intriguing, I think. I could be lalse, but that's the way I unen it.
Go to the next page in this issue Flip back to the previous page Go to the XYZZYnews home page
|