16
\$\begingroup\$

Parsing user commands in a text adventure is a spectrum from Adventure's simple "go north" to some mind-bogglingly clever ones in hhgttg.

I seem to remember reading nice how-tos in computer magazines back in the 80s, but now I find almost nothing on the 'net except a brief Wikipedia ref.

How would you do it?


Update: I went with the simplest approach possible in my Ludum Dare entry.

\$\endgroup\$
  • 3
    \$\begingroup\$ Is there a particular problem you're trying to solve? \$\endgroup\$ – Trevor Powell Apr 8 '12 at 12:17
  • \$\begingroup\$ @TrevorPowell considering embarking on making a little text adventure for fun, and just want to acquaint myself of the 'state of the art' rather than just diving in and solving it my way is all \$\endgroup\$ – Will Apr 8 '12 at 21:04
  • 1
    \$\begingroup\$ Use Inform; that's the best strategy you could ever use. There is virtually no reason to hand-code a text adventure these days. \$\endgroup\$ – Nicol Bolas Apr 9 '12 at 18:29
  • \$\begingroup\$ @NicolBolas unless Ludum Dare is approaching? ;) \$\endgroup\$ – Will Apr 10 '12 at 5:44
  • 1
    \$\begingroup\$ It's not defeatist as much as being pragmatic. Going into all the techniques advanced text parsing (beyond the obvious stuff anybody can come up with) is probably outside the scope of a single answer here. \$\endgroup\$ – Tetrad Apr 12 '12 at 15:48
10
\$\begingroup\$

Did you search in the interactive fiction community? They still write parsers and some try to push the envelope by implementing new techniques such as natural language processing.

See for example this link for articles describing approaches used:

http://ifwiki.org/index.php/Past_raif_topics:_Development:_part_2#Parsing

\$\endgroup\$
  • 4
    \$\begingroup\$ Aha, "text adventure" becomes "interactive fiction" and suddenly its much more googlable! Who'd have thought it'd even change name since I played it? :) Still, looking at those leads, and actually not much gets explained sadly \$\endgroup\$ – Will Apr 8 '12 at 21:09
9
\$\begingroup\$

The term you want is 'natural language processing', or NLP. However, bear in mind that formal methods are designed to try and understand real world texts, whereas you only usually need something that works for a limited subset of your natural language.

Typically you can start out with a simple grammar and vocabulary, then write a parser for it. A grammar might be something simple like this:

sentence = verb [preposition] object
verb = "get" | "go" | "look" | "examine"
preposition = "above" | "below"
object = ["the"] [adjective] noun
adjective = "big" | "green"
noun = "north" | "south" | "east" | "west" | "house" | "dog"

The above is a variant on Backus-Naur form, the standard way of representing grammars. Anyway, you can use a parser generator to generate code to parse this grammar, or write your own fairly easily if your language has decent string handling. (Look up 'recursive descent parsers', which use one function for each line of the grammar.)

Once parsed, you can work out if the sentence makes sense - "go north" may make sense, but "get the green north" does not. You can solve this in 2 ways; make the grammar more formal (eg. have different types of verbs only valid with certain types of noun) or check the nouns against the verb afterwards. The first way can help you to give out better error messages to the player, but you always need to do the second to some degree anyway, as you always need to check context - eg. "take the green key" is grammatically correct and syntactically correct, but you still need to check that the green key is present.

Eventually your program ends up with a validated command with all the various parts checked; then it's just a case of calling the right function with the arguments to perform the action.

\$\endgroup\$
6
\$\begingroup\$

The state of the art for making text adventures today is using Inform 7. Inform 7 source reads "like English," in the same way that Inform-based games let you "write English." For example, from Emily Short's Bronze:

A thing has some text called scent. The scent of a thing is usually "nothing".
The block smelling rule is not listed in any rulebook.
Carry out smelling something:
    say "From [the noun] you smell [scent of the noun]."
Instead of smelling a room:
    if a scented thing can be touched by the player, say "You smell [the list of scented things which can be touched by the player].";
    otherwise say "The place is blissfully odorless."

The Inform 7 parser is closely integrated with the Inform 7 IDE, and the entire source code is not available for study yet:

\$\endgroup\$
5
\$\begingroup\$

The two best current sources for learning to create a text adventure parser are (as was mentioned) the IF community and the mud community. If you search the major forums for those (Intfiction.org/forum, the newsgroup rec.arts.int-fiction, Mud Connector, Mudbytes, Mudlab, Top Mud Sites) you'll find some answers, but if you're just looking for articles I would recommend Richard Bartle's explanation of the parser in MUD II:

http://www.mud.co.uk/richard/commpars.htm

And this explanation on rec.arts.int-fiction:

http://groups.google.com/group/rec.arts.int-fiction/msg/f545963efb72ec7b?dmode=source

No disrespect meant to the other answers, but creating a CF grammar or using BNF is not the solution for this problem. That's not to say it couldn't be a solution for a different problem, that is, creating a more advanced natural language parser, but that's the subject of considerable research and not IMO in the scope of a text adventure.

\$\endgroup\$
4
\$\begingroup\$

In my first-year at university we made an adventure game in Prolog, and for the user input we had to use definite clause grammar or DCG. See http://www.amzi.com/manuals/amzi/pro/ref_dcg.htm#DCGCommandLanguage for an example of using it as a command language. It seemed like a principled (it was uni after all) and flexible approach at the time.

\$\endgroup\$
1
\$\begingroup\$

You need to define a domain specific language that is all the sentences which are correct in your game. To this end you have to define a grammar for your language (vocabulary and syntax). The type of grammar you need is a Context Free Grammar and there are tools which automatically generate a parser starting from a synthetic description of the grammar such as ANTLR (www.antlr.org). The parser only checks whether a sentence is correct or not and produce an Abstract Syntax Tree (AST) of the sentence which is a navigable representation of the sentence where each word has the role you specified in the grammar. By navigating the AST you have to add the code that assess what is the semantics each word takes when playing that role with respect to the other words in the sentence and verify whether the semantics is correct.

For instance the sentence 'The stone eats the man' is syntactically correct but not necessarily semantically correct (unless in your world stones, maybe magic stones, can eat men).

If also the semantics is correct then you can, for instance, change the world according to it. This could change the context and thus the same sentence could no longer be semantically correct (for instance there could be no man to eat)

\$\endgroup\$
1
\$\begingroup\$

I used the Tads3 (www.tads3.org) engine for some of the text adventures I wrote. It's more for computer programmers though but a very powerful language. If you're a programmer Tads3 will be tons easier to code things faster than Inform7, which I've used before as well. The problem with Inform7 for programmers is as famous as "guess the verb" is for players of text adventures in that if you don't write your sentences VERY carefully you're going to break the game. If you have the patience to do it you can easily write up a parser in Java using the Tokenizer class. Example I wrote using a global JTextArea and a global String[] array. It strips out unwanted characters except leaves A-Z and 0-9 as well as the question mark (for a "help" command shortcut):

// put these as global variables just after your main class definition
public static String[] parsed = new String[100];
// outputArea should be a non-editable JTextArea to display our results
JTextArea outputArea = new JTextArea();
/*
 * parserArea is the JTextBox used to grab input
 * and be sure to MAKE sure somewhere to add a 
 * java.awt.event.KeyListener on it somewhere where
 * you initialize all your variables and setup the
 * constraints settings for your JTextBox's.
 * The KeyListener method should listen for the ENTER key 
 * being pressed and then call our parseText() method below.
 */
JTextArea parserArea = new JTextArea();

public void parseText(){
    String s0 = parserArea.getText();// parserArea is our global JTextBox
    s0 = s0.replace(',',' ');
    s0 = s0.replaceAll("[^a-zA-Z0-9? ]","");
    // reset parserArea back to a clean starting state
    parserArea.setCaretPosition(0);
    parserArea.setText("");
    // erase what had been parsed before and also make sure no nulls found
    for(int i=0;i < parsed.length; i++){
      parsed[i] = "";
    }
    // split the string s0 to array words by breaking them up between spaces
    StringTokenizer tok = new StringTokenizer(s0, " ");
    // use tokenizer tok and dump the tokens into array: parsed[]
    int iCount = 0;
    if(tok.countTokens() > 0){
      while(tok.hasMoreElements()){
        try{
          parsed[iCount] = tok.nextElement().toString();
          if(parsed[iCount] != null && parsed[iCount].length()>1){
            // if a word ENDS in ? then strip it off
            parsed[iCount] = parsed[iCount].replaceAll("[^a-zA-Z0-9 ]","");
           }
        }catch(Exception e){
          e.printStackTrace();
        }
          iCount++;
        }


      /*
       * handle simple help or ? command.
       * parsed[0] is our first word... parsed[1] the second, etc.
       * we can use iCount from above as needed to see how many...
       * ...words got found.
       */
      if(parsed[0].equalsIgnoreCase("?") || 
        parsed[0].equalsIgnoreCase("help")){
          outputArea.setText("");// erase the output "screen"
          outputArea.append("\nPut help code in here...\n");
        }
      }

      // handle other noun and verb checks of parsed[] array in here...

    }// end of if(tok.countTokens() > 0)... 

}// end of public void parseText() method

...I left out the main class definition and variable initialize() method, etc. because it's assumed if you know Java you already know how to set that up. The main class for this should probably extend JFrame and in your public static void main() method just create an instance of it. Hopefully some of this code helps.

EDITED - Okay, so now what you'd do next is create an Actions class and scan for an action (i.e. "get lamp" or "drop sword"). To make it simpler you'd have to have a RoomScan object or method to scan everything visible in scope and scan for only those objects on that action. The object itself handles the action handling and by default you should have an Item class handle all known actions in a default way, which can be over-ridden. Now, if for example, an item you want to "get" is held by a non-player character, the default response for getting that item held by its owner should be something like "The won't let you have it." Now you'd have to create a ton of default action responses to this in the Item or Thing class. This is basically coming from a Tads3 perspective on over all design. Because in Tads3 each item has its own default action handling routine on it which the parser calls if an action on it is initialized. So... I'm just telling you, Tads3 already has all this in place, so it's VERY easy to code in a text adventure in that language. But if you want to do it from scratch, like in Java (above), then I personally would handle it much the same way Tads3 was designed. In that way, you can override default actions handling routines on different objects themselves, so for example if you want to "get lamp" and the butler is holding it, it could trigger a response in the "get" action method of default for Item or Object and tell you that "The butler refuses to hand over the brass lamp." I mean... once you've been a programmer long enough like I have, then this is all VERY easy stuff. I'm over 50 years old and been doing this since I was age 7. My father was a Hewlett Packard instructor in the '70's so I learned a TON from him initially on computer programming. I'm also in the US Army Reserves as basically a server administrator now. Um... yeah, so don't give up. It's not that hard once you really break it down to what you want your program to do. Sometimes trial and error is the best way to go on this sort of stuff. Just test it and see and don't ever give up. Okay? Coding is an art. It can be done in many different ways. Don't let one way or the other seem to block you into a corner on design.

\$\endgroup\$
  • \$\begingroup\$ This unfortunately leaves out the most difficult part of a text parser, namely identifying the verb, subject and object of the user input and mapping it to an action. \$\endgroup\$ – Philipp Sep 27 '17 at 10:48
  • \$\begingroup\$ True but how I'd do it is create an actions class and store a bunch of actions in, say, a dictionary class, then scan for actions words. If the action involves a 2nd word (like for a "take" action, maybe "take lamp") then have a bunch of item (or noun) objects scanned for where those objects themselves would have script to handle actions made on them. This is all assuming you'd code the entire thing right into Java and not try and make an actual external file read to compile text adventures. \$\endgroup\$ – William Chelonis Sep 27 '17 at 18:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.