我正在用java开发一个基于文本的游戏,我正在寻找处理玩家命令的最佳方法。命令允许玩家与环境交互,例如:
我的目标是开发一套完整的简单命令,但是我很难找到一种简单的方法来解析它。我想开发一个灵活且可扩展的解析器,它可以调用主命令,如“look”,“use”,“attack”等......并且每个命令都会在游戏中具有特定的语法和动作。
我找到了许多解析命令行参数的工具,比如-i -v --verbose,但它们似乎都没有足够的灵活性来满足我的需求。它们可以逐个解析,但不考虑每个参数的特定语法。我试过看起来很完美的JCommander但是我在参数,参数,谁叫谁等之间迷失了......
因此,如果有人可以帮助我选择正确的java库来做到这一点,那就太好了:)
答案 0 :(得分:2)
除非您处理涉及例如算术表达式或平衡括号的复杂命令字符串,否则我建议您使用普通的扫描仪。
这是一个我觉得可读且易于维护的例子:
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());
}
}
并将其用作
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);
您甚至可以将它变得更加漂亮并使用注释,如下所示:
@Retention(RetentionPolicy.RUNTIME)
@interface Command {
String value();
}
@Command("drink")
class Drink implements Action {
...
}
@Command("look")
class Look implements Action {
...
}
使用如下:
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);
}
答案 1 :(得分:1)
我认为您不想解析命令行参数。这意味着游戏中的每个“移动”都需要运行一个新的JVM实例来运行不同的程序,以及在JVM会话之间保存状态等额外的复杂性。
这看起来像一个基于文本的游戏,您可以提示用户下一步该做什么。您可能只想让用户在STDIN上输入输入。
示例,假设您的屏幕显示:
你现在在一个黑暗的房间里。有一个电灯开关 你想做什么?
1. turn on light
2. Leave room back the way you came.
Please choose option:
然后用户输入1
或2
,或者如果你想要花哨turn on light
等,那么你从STDIN得到readLine()
并解析字符串以查看用户选择了。我建议您查看java.util.Scanner
以了解如何轻松解析文本
Scanner scanner = new Scanner(System.in);
String userInput = scanner.readLine();
//parse userInput string here
答案 2 :(得分:1)
它的有趣之处在于让一些命令是人类可读的,同时它的机器可以解析。
首先,您需要定义语言的语法,例如:
look (north|south|east|west)
但它是正则表达式,一般来说,这不是解释语法规则的最佳方式,所以我想说这更好:
Sequence("look", Xor("north", "south", "east", "west"));
所以通过这样做,我认为你已经有了这个想法。你需要定义类似的东西:
public abstract class Syntax { public abstract boolean match(String cmd); }
然后
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; }
使用一堆工厂函数来包装构造函数,返回语法。那么你最终会得到这样的东西:
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
/* ... */
);
}
左右。
现在你需要的只是一个根据这些规则的递归解析器。
你可以看到,它的递归结构,非常容易编码,而且非常容易维护。通过这样做,您的命令不仅是人类可读的,而且是机器可解析的。
确定它还没有完成,你需要定义动作。但它很容易吗?这是典型的OO技巧。所有需要做的就是在Atom
匹配时执行某些操作。