使用开关块来解析文本冒险的输入?

发布于 2024-10-13 16:12:31 字数 348 浏览 10 评论 0原文

好的,我现在的目标是制作一个基本的文本冒险。然而,要做到这一点,我需要/想要一个可以执行以下操作的 switch 语句:

  • 检查字符串内部是否有单词 SOMEWHERE。
  • 检查字符串内部是否有任意组合的两个单词。

我将如何实现这个目标?您能否向我展示这个特定示例的编码:

提示用户输入数据。 switch 语句检查“look box”作为一种情况,检查“sleep”作为另一种情况。该程序不关心任何单词的顺序,但关心字母的顺序。

请详细解释一切。我刚刚开始编码。

编辑:谢谢您的所有答案。我知道有更好、更复杂、更有用的方法来处理这个问题,但它还达不到我的水平。

Okay, I have a goal right now to make a basic text adventure. To do this, however, I would need/want to have a switch statement that can do the following:

  • Check to see if a string has a word SOMEWHERE inside.
  • Check to see if a string has two words in any combination somewhere inside.

How would I accomplish this? Could you show me coding for this specific example:

A user is prompted for data. A switch statement checks for "look box" as one case, and "sleep" as another. The program doesn't care what order any words are in, but does care about the order of the letters.

Please explain everything in detail. I just started coding.

EDIT: Thank you for all the answers. I understand there are better, more complicated, and more useful ways of handling this, but it just isn't at my level yet.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

妄断弥空 2024-10-20 16:12:31

人们有时会问我为什么不造一艘船。我很灵巧,喜欢建造东西,而且喜欢航海。我总是告诉他们,喜欢航海的人不应该造船,因为你最终会在车库里花三年时间造船,然后才能去航海。如果你的目标是航行,那就买一艘船。如果您的目标是建造一艘船,那就建造一艘船。

如果您的目标是通过编写文本冒险来学习 C#,那就太好了,您会学到很多东西。如果您的目标是编写文本冒险,那么不要使用 C#,而使用 Inform7。它很容易学习,专门为编写文本冒险而设计,并且可能是世界上最高级的语言。这是一种令人惊叹的编程语言,我强烈推荐它。

回答你的具体问题:这不是一个好方法。文本冒险处理器实际工作的方式是首先编写一个程序,将用户输入的句子分解为标记。您必须逐个字符地搜索字符串,寻找单词之间的边界,例如空格、逗号、句点等。一旦找到边界,就可以提取边界之间的子字符串,并尝试通过将其与字典中的单词进行比较来识别每个单词。

一旦你有了一个标记序列,你就可以尝试将序列与语法进行匹配。也就是说,您可以查看标记序列是否可以分类为单字命令(如 {"look"})或动词名词短语(如 {"look", "at", "the", "red", "按钮”}。你想把它分解——“look”是动词,“at”是介词,“the red button”是动词的宾语,“the”是冠词,“red”是形容词,“button”是名词。

听起来你是个初学者,所以先关注词法分析;一次一个字符地遍历字符串,识别单词边界,并构建一个标记列表。语法分析技术可能相当复杂;首先把简单的事情做好并扎实。

People sometimes ask me why I don't built a boat. I'm pretty handy, I like building things, and I sail. I always tell them that people who like to sail shouldn't build boats, because you end up spending three years in your garage building a boat before you can go sailing. If your goal is to sail, buy a boat. If your goal is to build a boat, build a boat.

If your goal is to learn C# by writing a text adventure, great, you'll learn a lot. If your goal is to write a text adventure, then don't use C#, use Inform7. It is easy to learn, specifically designed to write text adventures, and is probably the most high level language in the world. It is an amazing programming language and I highly recommend it.

To answer your specific question: that is not a good way to go about it. The way text adventure processors actually work is first you write a program that breaks the sentence typed by the user up into tokens. You have to search through the string character by character looking for boundaries between words, like spaces, commas, periods, and so on. Once you find the boundaries then you extract the substrings between the boundaries and try to recognize every word by comparing it to words in a dictionary.

Once you have a sequence of tokens you then try to match the sequence against a grammar. That is, you see if the sequence of tokens can be classified as a one-word command like {"look"} or a verb-noun phrase like {"look", "at", "the", "red", "button"}. You want to break that down - "look" is the verb, "at" is the preposition, "the red button" is the object of the verb, "the" is the article, "red" is the adjective and "button" is the noun.

It sounds like you're a beginner, so concentrate first on the lexical analysis; walking through the string one character at a time, identifying word boundaries, and building up a List<string> of tokens. Techniques for grammatical analysis can be quite complicated; get the simple stuff done and solid first.

平定天下 2024-10-20 16:12:31

考虑到您是从开始的,我们可以首先在简单的情况下看看这个,但您不能使用 switch 语句来实现这一点。

为了简单起见,我们假设您的命令仅限于 1 或 2 个单词,并且第一个单词是动词,第二个单词(如果存在)是名词。这给了我们很多可能性:

North
South
Examine
Take
Drop

等等...

假设我们有一个 strInput 输入字符串:

string strInput = "examine hat";

我们希望首先将其拆分。我们可以使用String.Split来做到这一点:

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

这将为我们提供一个字符串数组:

arguments [0]是检查

arguments [1]是帽子

请注意,我们并不总是有第二个,如果用户输入:

`North`

then:

argument [0] is North

我们需要检查这一点!现在,检查这一点的可怕(但简单)的方法是:

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!

现在我们有了一个可扩展的系统。添加新命令和处理程序很容易!它变得更加复杂,但本质上这是一种做你想做的事的好方法。

编辑:我知道你的问题说它不应该关心单词的顺序。如果您正在编写文本冒险游戏,您最好形成一些动词/名词或类似的语法,而不是允许随机输入内容。

Considering you're starting at, we can look at this in the simple case first, but you can't use a switch statement to achieve this.

Let's assume, for the purposes of simplicity, that your commands are limited to 1 or 2 words and that the first word is a verb and the second would, if present is a noun. This gives us quite a few possibilities:

North
South
Examine
Take
Drop

etc...

Given that we have an input string of strInput:

string strInput = "examine hat";

We want to first split this up. We can do this using String.Split:

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

Which will give us a string array:

arguments [0] is examine

arguments [1] is hat

Note, we don't always have a the 2nd one, if the user typed:

`North`

then:

arguments [0] is North

We'll need to check for this! Now, the horrible (but simple) way to check for this is:

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...

Unfortunately, this code is going to get long, complex and unusable. How do you know what you can and can't do at any stage? How do you add new command? So let's use the wonderful delegate feature of C#, and also introduce the Dictionary. A dictionary allows us to map one type (a key) to another (in this case, a delegate). Using this method, we can create delegates to handle different kinds of commands.

public delegate void HandleCommand(string[] _input);

Here, we delegated a delegate. Don't worry about it yet, but let's introduce some functions that will work with commands:

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.
    }
}

And so on. Now we need to create a dictionary to map the commands to these functions:

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

Here, we declare a dictionary that maps strings to our delegate type HandleCommand. Now we need to populate it!

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

Now, given our earlier example, let's split the string up as before, and call the right handler!

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

Now we have an extensible system. It is easy to add new commands and handlers! It gets more complicated, but in essence this is a good way to do what you want.

EDIT: I am aware that your question said that it should not care about the order of the words. If you're writing a text adventure game, you'd do well to form some grammer of Verb/Noun or some such rather than allowing things to be typed randomly.

神魇的王 2024-10-20 16:12:31

这是另一个想法:

    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 语句来相应执行。

Here's another idea:

    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;
    }

It uses if/then/else to assess a particular scenario including potential combinations such as input like "look at the sleep box" and then uses a switch statement to execute accordingly.

尤怨 2024-10-20 16:12:31

您不能使用 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 作为案例。

You can't do this using switch, you'll have to use the if-else-if type of structure.

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
}

With switch each case must be some static, unique value. So you can't use .Contains as a case.

月亮是我掰弯的 2024-10-20 16:12:31

您不能直接使用 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;
}

You can’t use switch for this directly, but then again, I think you shouldn’t. You should probably have the logic that finds the words in a different place than the logic that contains the switch.

Consider using an enum to contain all the possible actions:

public enum AdventureAction
{
    LookBox,
    Sleep
}

Consider writing a method that does the “parsing”:

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

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

And then you can use a simple switch statement to perform the action:

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

    case AdventureAction.Sleep:
        // go to sleep
        break;
}
夜灵血窟げ 2024-10-20 16:12:31

我目前正在编写自己的文本冒险引擎,因为 Inform/Adrift/Quest 都有一个致命的缺陷,让我烦恼——Inform 的噩梦般的、混乱的语法(这是来自一位 UX 设计师,他喜欢事情像对于初学者来说可能),Adrift 的阅读器丑陋,并且 Adrift/Quests 缺乏真正的类/对象支持。

这可能不是最好的方法,但到目前为止效果很好。我研究了正则表达式,但决定这样做。

首先要做的是将玩家的输入/命令字符串分割成一个列表。幸运的是,在这些游戏中,该列表的第一个元素几乎总是动词。

  • 看看
  • 蓝皮书
  • 您将需要可以通过键访问的动词/对象/等数据类,其中包含

可以与其匹配的所有值,例如“look,inspect,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;
}

您还需要一堆“Action”子类,这些子类将从玩家的输入中进行匹配。在这里,您“构建”需要与玩家输入匹配的句子结构。

  • 动作(基类)
    • 看{看}[对象]
    • 看书
    • 跳跃
    • 跳到{on/onto} [对象]

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 {}

现在,您可以搜索“Action”类,将第一个 SentenceElement 与玩家第一个输入的动词进行比较,并保留与“潜在动作”匹配的列表。 “string.Contains”将起作用。

现在,您必须通过单步执行玩家的输入命令,并单步执行比较它们的每个 SentenceElement 来找到最匹配的 Action。保留您在每个位置的索引(playerInputIndex、潜在动作索引、句子元素索引)。

如果找到匹配项,请增加playerInputIndex,直到它与SentenceElement不匹配,检查您的设置(isOptional等),然后移至下一个sentenceElementIndex并再次运行比较。最终您将到达玩家输入或 SentenceElements 的末尾。

当你有“isOptional”的 SentenceElements 时,复杂性就会增加,因此必须检查它,或者具有 ObjectAny 类型的 SentenceElements 的 Actions 不应该与现有的匹配,但会显示“您想要哪个对象”吃饭吗?信息。此外,与动词不同,对象具有额外的匹配参数,例如要考虑的前缀/形容词/名词。

该系统还需要为您想要执行的每个操作创建一个新类。每个操作都有自定义的“限制”,在运行之前它也必须通过,例如“引用的角色还活着吗?”等。

I'm currently writing my own text adventure engine due to Inform/Adrift/Quest all tending to have a fatal flaw that bugs me — Inform's nightmarish, obfuscated syntax (and this is coming from a UX designer who likes things to be as easy as possible for beginners), Adrift's ugly reader, and Adrift/Quests lack of real class/object support.

It may not be the best way to do it, but it's working fine so far. I looked into Regex, but decided to do it this way instead.

The first thing to do is split the player's input/command string into a List. Luckily, in these games, the first element of this list is almost always a verb.

  • look
  • at
  • blue
  • book

You will want verb/object/etc data classes that can be accessed by key, containing all the values that can match to it, such as "look, examine, 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;
}

You will also need a bunch of "Action" subclasses that will be matched from the player's input. Here you "build" your sentence structure that needs to be matched by the player's input.

  • Action (base class)
    • look
    • look {at} [object]
    • look {at} book
    • Jump
    • Jump {on/onto} [object]

.

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();
}

The base Action class has a sentence builder using SentenceElements. It can be a List that describes the sentence, piece by piece.

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 {}

You can now search through your "Action" classes, compare the first SentenceElement to the players first input verb, and keep a list of the ones that match as "potentialActions". "string.Contains" will work.

Now you have to find the closest matching Action by stepping through your players' input command, and stepping through every SentenceElement comparing them. Keep an index of where you are in each one (playerInputIndex, potentialActionsIndex, sentenceElementIndex).

If you find a match, increment the playerInputIndex until it doesn't match the SentenceElement, check your settings (isOptional, etc), then move to the next sentenceElementIndex and run the compare all over again. Eventually you'll reach the end of either the player's input, or the SentenceElements.

Complexity is added when you have SentenceElements that are "isOptional", so it will have to be checked for, or Actions that have SentenceElements of type ObjectAny that aren't supposed to match an existing one, but display a "Which object did you want to eat?" message. Also, objects have extra matching parameters such as prefixes/adjectives/nouns to factor in, unlike verbs.

This system will also require a new class for every action you could ever want to do. Each action would have custom "Restrictions" it must also pass before it will run, such as "Is the referenced character alive?", etc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文