Использование блоков переключателей для разбора ввода для текстового приключения? - PullRequest
2 голосов
/ 22 января 2011

Хорошо, у меня сейчас есть цель сделать базовое текстовое приключение.Однако для этого мне нужно / нужно иметь оператор switch, который может выполнять следующие действия:

  • Проверить, есть ли в строке слово SOMEWHERE внутри.чтобы увидеть, есть ли внутри строки два слова в любой комбинации где-то внутри.

Как бы я это сделал?Не могли бы вы показать мне код для этого конкретного примера:

У пользователя запрашиваются данные.Оператор switch проверяет «look box» как один случай и «sleep» как другой.Программа не заботится о том, в каком порядке находятся слова, но заботится о порядке букв.

Пожалуйста, объясните все подробно.Я только начал кодировать.

РЕДАКТИРОВАТЬ: Спасибо за все ответы.Я понимаю, что есть лучшие, более сложные и более полезные способы справиться с этим, но это просто не на моем уровне.

Ответы [ 6 ]

12 голосов
/ 22 января 2011

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

Если ваша цель - изучить C #, написав текстовое приключение, отлично, вы многому научитесь. Если ваша цель - написать текстовое приключение , то не используйте C #, используйте Inform7. Он прост в освоении, специально разработан для написания текстовых приключений и, вероятно, является языком самого высокого уровня в мире. Это удивительный язык программирования, и я очень рекомендую его.

Чтобы ответить на ваш конкретный вопрос: это не очень хороший способ. На самом деле текстовые процессоры приключений работают так: сначала вы пишете программу, которая разбивает введенное пользователем предложение на токены . Вы должны искать через строковый символ за символом, ища границы между словами, такими как пробелы, запятые, точки и так далее. Найдя границы, вы извлекаете подстроки между границами и пытаетесь распознать каждое слово, сравнивая его со словами в словаре.

Получив последовательность токенов, вы попытаетесь сопоставить последовательность с грамматикой . То есть вы видите, можно ли классифицировать последовательность токенов как команду, состоящую из одного слова, например {"look"}, или фразу с существительным глаголом, например {"look", "at", "the", "red", " кнопка "}. Вы хотите сломать это - «взгляд» - глагол, «на» - предлог, «красная кнопка» - объект глагола, «the» - артикль, «красный» - прилагательное и «кнопка» это существительное.

Звучит так, будто вы новичок, поэтому сначала сконцентрируйтесь на лексическом анализе; проходя по строке по одному символу за раз, определяя границы слов и создавая List<string> токенов. Методы грамматического анализа могут быть довольно сложными; сделать простые вещи и твердые в первую очередь.

2 голосов
/ 22 января 2011

Учитывая, что вы начинаете, мы можем сначала взглянуть на это в простом случае, но вы не можете использовать оператор switch для достижения этого.

Предположим, для простоты, что ваши команды ограничены 1 или 2 словами и что первое слово является глаголом, а второе будет, если оно есть, существительным. Это дает нам довольно много возможностей:

North
South
Examine
Take
Drop

и т.д ...

Учитывая, что у нас есть строка ввода strInput:

string strInput = "examine hat";

Мы хотим сначала разделить это. Мы можем сделать это используя String.Split:

string[] arguments = strInput.Split(' ');

Что даст нам строковый массив:

аргументы [0] - это исследовать

аргументы [1] - это hat

Обратите внимание, у нас не всегда 2-й, если пользователь набрал:

`North`

, то:

аргументы [0] - Север

Нам нужно проверить это! Теперь ужасный (но простой) способ проверить это:

if(arguments[0] == "North")
{
    // code to go North
}
else if(arguments[0] == "Take")
{
    // code to work with Take.  We'd check on arguments[1] here!
}
// etc...

К сожалению, этот код станет длинным, сложным и непригодным для использования. Как вы знаете, что вы можете и не можете делать на любой стадии? Как добавить новую команду? Итак, давайте используем замечательную функцию делегата в C #, а также представим Dictionary. Словарь позволяет нам сопоставить один тип (ключ) другому (в данном случае, делегату). Используя этот метод, мы можем создавать делегатов для обработки команд различного типа.

public delegate void HandleCommand(string[] _input);

Здесь мы делегировали делегата. Пока не беспокойтесь, но давайте представим некоторые функции, которые будут работать с командами:

public void Handle_North(string[] _input)
{
    // code to go North.  This function could just as easily be something that handles
    // *all* directions and checks _input[0] to see where to go!
}

public void Handle_Take(string[] _input)
{
    if(_input.Length > 1) // Did the user specify an object to take?
    {
        // code to handle Take.
    }
}

И так далее. Теперь нам нужно создать словарь для сопоставления команд с этими функциями:

Dictionary<String, HandleCommand> map = new Dictionary<String, HandleCommand>();

Здесь мы объявляем словарь, который отображает строки в наш тип делегата HandleCommand. Теперь нам нужно его заполнить!

map["north"] = Handle_North;
map["take"]  = Handle_Take;
// and the rest of our commands

Теперь, учитывая наш предыдущий пример, давайте разделим строку, как и прежде, и вызовем правильный обработчик!

string[] arguments = strInput.Split(' ');
if(arguments.Length > 0 && map.ContainsKey(arguments[0]))
    map[arguments[0]](arguments);  // calls our function!

Теперь у нас есть расширяемая система. Легко добавлять новые команды и обработчики! Это становится все сложнее, но по сути это хороший способ сделать то, что вы хотите.

РЕДАКТИРОВАТЬ: Я знаю, что ваш вопрос сказал, что он не должен заботиться о порядке слов. Если вы пишете текстовую приключенческую игру, вам лучше составить грамматику глагола / существительного или чего-то подобного, вместо того, чтобы вводить вещи случайным образом.

1 голос
/ 22 января 2011

Вот еще одна идея:

    string input = "look at the sleep box";
    bool caseA = input.Contains("sleep");
    bool caseB = input.Contains("look") && input.Contains("box");

    int scenarioId;
    if (caseA && caseB)
        scenarioId = 1;
    else if (caseA)
        scenarioId = 2;
    else if (caseB)
        scenarioId = 3;
    // more logic?
    else
        scenarioId = 0;

    switch (scenarioId)
    {
        case 1:
            Console.WriteLine("Do scenario 1");
            break;
        case 2:
            Console.WriteLine("Do scenario 2");
            break;
        case 3:
            Console.WriteLine("Do scenario 3");
            break;
        // more cases
        default:
            Console.WriteLine("???");
            break;
    }

Он использует if / then / else для оценки конкретного сценария, включая потенциальные комбинации, такие как ввод, например "look at the sleep box", а затем использует оператор switch для выполнения соответствующим образом.

1 голос
/ 22 января 2011

Вы не можете сделать это, используя switch, вам придется использовать тип структуры if-else-if.

string input=...
if(input.Contains("sleep")){ //contains sleep? 
  //do stuff for some word
}else if(input.Contains("look") && input.Contains("box")){ //contains look and box
  //do stuff for the combination thing
}

С switch каждый case должен быть некоторымстатическое, уникальное значение.Таким образом, вы не можете использовать .Contains как случай.

0 голосов
/ 02 сентября 2015

В настоящее время я пишу свой собственный текстовый движок приключений из-за того, что Inform / Adrift / Quest имеет тенденцию иметь фатальный недостаток, который меня беспокоит - кошмарный, запутанный синтаксис Inform (и это исходит от UX-дизайнера, который любит вещи как можно более легким для начинающих), уродливого читателя Адрифта и отсутствия в Адрифте / Квестах реальной поддержки классов / объектов.

Возможно, это не лучший способ сделать это, но пока он работает нормально. Я посмотрел в Regex, но решил сделать это так.

Первое, что нужно сделать, это разделить строку ввода / команды игрока в список. К счастью, в этих играх первым элементом этого списка почти всегда является глагол.

  • вид
  • в
  • синий
  • книга

Вам понадобятся классы данных глагол / объект / и т. Д., К которым можно получить доступ по ключу, содержащие все значения, которые могут соответствовать ему, например, «смотреть, исследовать, ex».

class Verb
{
    string uniqueID;
    List<string> values;
}

class Object
{
    public uniqueID; // Because this is shared with Verbs, you could do a better unique ID system, but hey...
    public List<string> articles;
    public List<string> adjectives;
    public List<string> noun;
}

Вам также понадобится набор подклассов «Действие», которые будут сопоставлены с входом игрока. Здесь вы «строите» структуру предложений, которая должна соответствовать входным данным игрока.

  • Действие (базовый класс)
    • вид
    • посмотрите {на} [объект]
    • посмотрите книгу
    • Перейти
    • Прыжок {вкл / на} [объект]

.

class Action
{
    string uniqueID;
    List<SentenceElement> sentence;

    void Execute();
    bool RestrictionsCheck();
}

class Look : Action
{
    override void Execute();
    override bool RestrictionsCheck();
}

class LookAtObject : Action
{
    override void Execute();
    override bool RestrictionsCheck();
}

class LookAtBook : Action
{
    override void Execute();
    override bool RestrictionsCheck();
}

Базовый класс Action имеет конструктор предложений, использующий SentenceElements. Это может быть Список, описывающий предложение по частям.

class SentenceElement
{
    List<string> values;
    bool isOptional;
}

class VerbID : SentenceElement {}
class ObjectID : SentenceElement {}
class ObjectAny : SentenceElement {}
class CharacterID : SentenceElement {}
class CharacterAny : SentenceElement {}
class TextOptional : SentenceElement {}
class TextRequired : SentenceElement {}
class Parameter : SentenceElement {}

Теперь вы можете искать в своих классах "Действие", сравнивать первый SentenceElement с первым входным глаголом игроков и вести список тех, которые соответствуют "потенциальным действиям". "string.Contains" будет работать.

Теперь вам нужно найти наиболее подходящее Действие, пройдя через команду ввода ваших игроков и пройдя через каждый SentenceElement, сравнивая их. Держите указатель того, где вы находитесь в каждом из них (playerInputIndex, потенциальный доступ к действию, индекс предложения в элементе).

Если вы найдете совпадение, увеличивайте playerInputIndex до тех пор, пока он не совпадет с SentenceElement, проверьте ваши настройки (isOptional и т. Д.), Затем перейдите к следующему фразеElementIndex и снова выполните сравнение. В конце концов вы достигнете конца ввода игрока или SentenceElements.

Сложность добавляется, когда у вас есть SentenceElements, которые являются «isOptional», поэтому его нужно будет проверить, или Действия, которые имеют SentenceElements типа ObjectAny, которые не должны соответствовать существующему, но отображают «Какой объект ты хотел поесть? " сообщение. Кроме того, в отличие от глаголов, у объектов есть дополнительные параметры соответствия, такие как префиксы / прилагательные / существительные, которые нужно учитывать.

Эта система также потребует нового класса для каждого действия, которое вы когда-либо захотите сделать. Каждое действие будет иметь собственные «Ограничения», которые оно также должно пройти, прежде чем оно будет выполнено, например «Жив ли указанный персонаж?» И т. Д.

0 голосов
/ 22 января 2011

Вы не можете использовать switch для этого напрямую, но опять же, я думаю, что вы не должны.Вероятно, у вас должна быть логика, которая находит слова в другом месте, чем логика, содержащая switch.

Попробуйте использовать перечисление для включения всех возможных действий:

public enum AdventureAction
{
    LookBox,
    Sleep
}

Попробуйте написать метод, который выполняет «синтаксический анализ»:

public static AdventureAction Parse(string text)
{
    if (text.Contains("look") && text.Contains("box"))
        return AdventureAction.LookBox;

    if (text.Contains("sleep"))
        return AdventureAction.Sleep;
}

И тогда выиспользуйте простое выражение switch для выполнения действия:

var action = Parse(input);
switch (action)
{
    case AdventureAction.LookBox:
        // do something interesting with the box
        break;

    case AdventureAction.Sleep:
        // go to sleep
        break;
}
...