Я разрабатываю текстовую игру в java, и я ищу лучший способ справиться с командами игроков. Команды позволяют игроку взаимодействовать с окружающей средой, например:
Моя цель состоит в том, чтобы разработать полный набор этих простых команд, но у меня возникли проблемы с тем, чтобы найти простой способ проанализировать его. Я хотел бы разработать гибкий и расширяемый синтаксический анализатор, который мог бы вызвать основную команду, такую как "смотреть", "использовать", "атаковать" и т.д.... и каждый из них имел бы определенный синтаксис и действия в игре.
Я нашел много инструментов для анализа аргументов командной строки, таких как -i -v --verbose, но ни один из них не обладает достаточной гибкостью для удовлетворения моих потребностей. Они могут анализировать один аргумент, но без учета конкретного синтаксиса для каждого из них. Я пробовал JCommander, который кажется совершенным, но я теряюсь между тем, что является аргументом, параметром, который вызывает кого и т.д.
Поэтому, если кто-то может помочь мне выбрать правильную библиотеку java для этого, это будет здорово :)
Если вы не имеете дело со сложными командами, которые включают, например, арифметические выражения или хорошо сбалансированные круглые скобки, я предлагаю вам пойти с простым сканером.
Вот пример, который я нашел бы читаемым и легким в обслуживании:
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);
}
забавная часть этого состоит в том, чтобы некоторая команда была удобочитаемой человеком, которая в то же время обрабатывала машину.
Прежде всего, вам нужно определить синтаксис вашего языка, например:
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
соответствует.
Я не думаю, что вы хотите разбирать аргументы командной строки. Это означало бы, что каждый "ход" в вашей игре потребует запуска нового экземпляра JVM для запуска другой программы и дополнительной сложности сохранения состояния между сеансами JVM и т.д.
Это похоже на текстовую игру, в которой вы предлагаете пользователям, что делать дальше. Вероятно, вы просто хотите, чтобы пользователи вводили ввод на STDIN.
Например, скажем, ваш экран говорит:
Вы сейчас в темной комнате. Есть выключатель света, что вы хотите сделать?
1. turn on light
2. Leave room back the way you came.
Please choose option:
то пользователь вводит 1
или 2
или если вы хотите, чтобы вы причудливо turn on light
и т.д., тогда вы readLine()
из STDIN и разбираете строку, чтобы узнать, что выбрал пользователь. Я рекомендую вам посмотреть на java.util.Scanner
чтобы увидеть, как легко разобрать текст.
Scanner scanner = new Scanner(System.in);
String userInput = scanner.readLine();
//parse userInput string here