2

I'm developing a text based game in java and I'm looking for the best way to deal with player's commands. Commands allow the player to interact with the environment, like :

  • "look north" : to have a full description of what you have in the north direction
  • "drink potion" : to pick an object named "potion" in your inventory and drink it
  • "touch 'strange button'" : touch the object called 'strange button' and trigger an action if there is one attached to it, like "oops you died..."
  • "inventory" : to have a full description of your inventory etc...

My objective is now to develop a complete set of those simple commands but I'm having trouble to find an easy way to parse it. I would like to develop a flexible and extensible parser which could call the main command like "look", "use", "attack", etc... and each of them would have a specific syntax and actions in the game.

I found a lot of tools to parse command line arguments like -i -v --verbose but none of them seems to have the sufficient flexibility to fit my needs. They can parse one by one argument but without taking into account a specific syntax for each of them. I tried JCommander which seems to be perfect but I'm lost between what is an argument, a parameter, who call who, etc...

So if someone could help me to pick the correct java library to do that, that would be great :)

4
  • 3
    "Command line" is the line you type to invoke a program. These are just user input. What you have is a text-based game, not a command line. Use a regular parser for this. Commented Dec 22, 2014 at 16:33
  • 1
    Perhaps you can have a look at Jflex and Cup? They are a scanner and a lexer. Commented Dec 22, 2014 at 16:34
  • @Udy i think op wants something more like lexical analysis instead of cli parser. Commented Dec 22, 2014 at 16:34
  • if you want to write yourself one, i can show you a nice paradigm to achieve this. Commented Dec 22, 2014 at 16:35

3 Answers 3

2

Unless you're dealing with complex command strings that involve for instance arithmetic expressions or well balanced parenthesis I would suggest you go with a plain Scanner.

Here's an example that I would find readable and easy to maintain:

interface Action {
    void run(Scanner args);
}

class Drink implements Action {
    @Override
    public void run(Scanner args) {
        if (!args.hasNext())
            throw new IllegalArgumentException("What should I drink?");
        System.out.println("Drinking " + args.next());
    }
}

class Look implements Action {
    @Override
    public void run(Scanner args) {
        if (!args.hasNext())
            throw new IllegalArgumentException("Where should I look?");
        System.out.println("Looking " + args.next());
    }
}

And use it as

Map<String, Action> actions = new HashMap<>();
actions.put("look", new Look());
actions.put("drink", new Drink());

String command = "drink coke";

// Parse
Scanner cmdScanner = new Scanner(command);
actions.get(cmdScanner.next()).run(cmdScanner);

You could even make it fancier and use annotations instead as follows:

@Retention(RetentionPolicy.RUNTIME)
@interface Command {
    String value();
}

@Command("drink")
class Drink implements Action {
    ...
}

@Command("look")
class Look implements Action {
    ...
}

And use it as follows:

List<Action> actions = Arrays.asList(new Drink(), new Look());

String command = "drink coke";

// Parse
Scanner cmdScanner = new Scanner(command);
String cmd = cmdScanner.next();
for (Action a : actions) {
    if (a.getClass().getAnnotation(Command.class).value().equals(cmd))
        a.run(cmdScanner);
}
Sign up to request clarification or add additional context in comments.

7 Comments

It seems to be a nice way to do it yes, but you have to write a lot of code to verify the user input no ? Like checking the correct number of arguments, etc... If I could delegate that to a parser that would be perfect.
Then I would try out for instance JavaCup/JFlex or ANTLR. But I would worry about splitting up the logic of judging which input that is valid and how to deal with that input. If you for instance want to add a type of beverage, you would both have to update the syntax validator and the class that handles the drinking (instead of just adding, say another case in a switch statement inside the Drink class).
Taking your comment into consideration I would add throws SyntaxException to the Action.run interface method. Whenever an action class encounters something unexpected, a SyntaxException could be thrown and the syntax and semantics would go hand in hand.
Adding new objects should not be a problem because I want to split it from the logic, for example the command drink will look for the beverage in a database and if it doesn't find it, then it'll pop a messsage to the player. The only thing I have to care about is : does my command looks like "drink <something>", and : does "something" exist ? is it a beverage ? Quantity > 0 in inventory ? I'm trying to do so for "modifiers", like for example, the beverage give +5pv, +10 strength or just kill me. I'm thinking about attach a list of actions to an object, directly in the database.
If the available beverage strings are stored in a database I don't see how you could use a parser library or even have a clean separation of syntax / semantics. Any such approach would require a complete grammar for the valid commands.
|
1

I don't think you want to parse command line arguments. That would mean each "move" in your game would require running a new JVM instance to run a different program and extra complexity of saving state between JVM sessions etc.

This looks like a text based game where you prompt users for what to do next. You probably just want to have users enter input on STDIN.

Example, let's say your screen says:

You are now in a dark room. There is a light switch what do you want to do?

1. turn on light
2. Leave room back the way you came.

Please choose option:

then the user types 1 or 2 or if you want to be fancy turn on light etc. then you readLine() from the STDIN and parse the String to see what the user chose. I recommend you look at java.util.Scannerto see how to easily parse text

Scanner scanner = new Scanner(System.in);
String userInput = scanner.readLine();
//parse userInput string here

2 Comments

Yes it's exactly that, but I don't want to wait for a specific response like "choose 1, 2 or 3". The player can do whatever he wants and the command, which could be a complex one, must be parsed and trigger the correct action.
Well, either way you have to read the user's choice as a String and then go from there. You can make it as complex as you want. Also be sure to have a catch all action that if you don't understand what the user wants, allow the user to re-enter a command
1

the fun part of it is to have some command is human readable, which at the same time, it's machine parsable.

first of all, you needs to define the syntax of your language, for example:

look (north|south|east|west)

but it's in regular expression, it's generally speaking not a best way to explain a syntactical rule, so i would say this is better:

Sequence("look", Xor("north", "south", "east", "west"));

so by doing this, i think you've got the idea. you need to define something like:

public abstract class Syntax { public abstract boolean match(String cmd); }

then

public class Atom extends Syntax { private String keyword; }

public class Sequence extends Syntax { private List<Syntax> atoms; }

public class Xor extends Syntax { private List<Syntax> atoms; }

use a bunch of factory functions to wrap the constructors, returning Syntax. then you will have something like this eventually:

class GlobeSyntax
{
    Syntax syntax = Xor( // exclusive or
         Sequence(Atom("look"), 
                  Xor(Atom("north"), Atom("south"), Atom("east"), Atom("west"))),
         Sequence(Atom("drink"),
                  Or(Atom("Wine"), Atom("Drug"), Atom("Portion"))), // may drink multiple at the same time
         /* ... */
    );
}

or so.

now what you need is just a recursive parser according to these rules.

you can see, it's recursive structure, very easy to code up, and very easy to maintain. by doing this, your command is not only human readable, but machine parsable.

sure it's not finished yet, you needs to define action. but it's easy right? it's typical OO trick. all to need to do is to perform something when Atom is matched.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.