2

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

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

我将如何做到这一点?你能告诉我这个具体例子的编码吗:

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

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

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

4

6 回答 6

12

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

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

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

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

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

于 2011-01-22T15:08:36.920 回答
2

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

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

North
South
Examine
Take
Drop

ETC...

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

string strInput = "examine hat";

我们想先把它分开。我们可以使用String.Split

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

这会给我们一个字符串数组:

参数 [0] 是检查

论据 [1] 是帽子

请注意,如果用户键入以下内容,我们并不总是有第二个:

`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!

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

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

于 2011-01-22T15:07:29.560 回答
1

您不能使用 来执行此操作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作为案例使用。

于 2011-01-22T14:58:50.153 回答
1

这是另一个想法:

    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 来评估特定场景,包括可能的组合,例如 input like "look at the sleep box",然后使用 switch 语句相应地执行。

于 2011-01-22T15:01:27.003 回答
0

您不能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;
}
于 2011-01-22T15:03:36.610 回答
0

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

这可能不是最好的方法,但到目前为止它运行良好。我研究了正则表达式,但决定改用这种方式。

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

  • 蓝色的

您将需要可以通过键访问的动词/对象/等数据类,其中包含可以与之匹配的所有值,例如“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;
}

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

  • 动作(基类)
    • 看 {at} [对象]
    • 看{看}书
    • 跳转 {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 与玩家第一个输入动词进行比较,并将匹配的列表保留为“potentialActions”。“string.Contains”会起作用。

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

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

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

这个系统还需要一个新的类来处理你想要做的每一个动作。每个动作都会有自定义的“限制”,它还必须在运行之前通过,例如“引用的角色是否还活着?”等。

于 2015-09-02T06:22:07.467 回答