Лучший способ разобрать команды в текстовой игре Java

1

Я разрабатываю текстовую игру в java, и я ищу лучший способ справиться с командами игроков. Команды позволяют игроку взаимодействовать с окружающей средой, например:

  • "посмотрите на север": чтобы иметь полное описание того, что у вас есть в северном направлении
  • "пить зелье": выбрать предмет, названный "зелья" в вашем инвентаре, и выпить его
  • "коснитесь" странной кнопки ": коснитесь объекта под названием" странная кнопка "и вызовите действие, если оно прикреплено к нему, например" oops you die... "
  • "инвентарь": иметь полное описание вашего инвентаря и т.д....

Моя цель состоит в том, чтобы разработать полный набор этих простых команд, но у меня возникли проблемы с тем, чтобы найти простой способ проанализировать его. Я хотел бы разработать гибкий и расширяемый синтаксический анализатор, который мог бы вызвать основную команду, такую как "смотреть", "использовать", "атаковать" и т.д.... и каждый из них имел бы определенный синтаксис и действия в игре.

Я нашел много инструментов для анализа аргументов командной строки, таких как -i -v --verbose, но ни один из них не обладает достаточной гибкостью для удовлетворения моих потребностей. Они могут анализировать один аргумент, но без учета конкретного синтаксиса для каждого из них. Я пробовал JCommander, который кажется совершенным, но я теряюсь между тем, что является аргументом, параметром, который вызывает кого и т.д.

Поэтому, если кто-то может помочь мне выбрать правильную библиотеку java для этого, это будет здорово :)

  • 3
    «Командная строка» - это строка, которую вы вводите для вызова программы. Это просто пользовательский ввод. У вас есть текстовая игра, а не командная строка. Используйте обычный парсер для этого.
  • 1
    Возможно, вы можете взглянуть на Jflex и Cup? Это сканер и лексер.
Показать ещё 2 комментария
Теги:
parsing

3 ответа

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);
}
  • 0
    Кажется, это хороший способ сделать это, да, но вам нужно написать много кода, чтобы убедиться, что пользовательский ввод нет? Например, проверка правильного количества аргументов и т. Д. Если бы я мог передать это парсеру, это было бы идеально.
  • 1
    Тогда я бы попробовал, например, JavaCup / JFlex или ANTLR. Но я бы беспокоился о том, чтобы разделить логику оценки того, какой вход является действительным, и как поступить с этим входом. Например, если вы хотите добавить тип напитка, вам придется обновить синтаксический валидатор и класс, который обрабатывает напиток (вместо простого добавления, скажем, другого случая в операторе switch внутри класса Drink).
Показать ещё 5 комментариев
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 соответствует.

1

Я не думаю, что вы хотите разбирать аргументы командной строки. Это означало бы, что каждый "ход" в вашей игре потребует запуска нового экземпляра 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
  • 0
    Да, это именно так, но я не хочу ждать конкретного ответа типа «выберите 1, 2 или 3». Игрок может делать все, что он хочет, и команда, которая может быть сложной, должна быть проанализирована и инициировать правильное действие.
  • 0
    Ну, так или иначе, вы должны прочитать выбор пользователя как строку и затем перейти оттуда. Вы можете сделать это так сложно, как вы хотите. Также убедитесь, что вы выполняете действие «перехватить все», которое, если вы не понимаете, чего хочет пользователь, позволит ему повторно ввести команду.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню