Files
fzu-product/3.编程思维体系构建/3.4.6.8.移动方向.md
2023-04-14 15:41:04 +08:00

253 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 8.移动方向
<em>传统的文本冒险使用</em><em>指南针方向</em><em>进行导航。</em>
例如,我们在第 6 章中绘制的地图上,玩家可能想<strong>向东</strong>移动,从田野移动到洞穴。我们可以通过给连接<strong>Cave</strong>的通道标上“east”来实现这一点。但是我们首先需要解决两个问题。
1. 我们可能仍然想把这段通道称为“entrance”和“east”。但现在一个对象只能有一个标签。
2. 在更大的地图上具有更多的位置和道路标签“east”将被定义多次。到目前为止标签在我们的游戏中是全球独一无二的没有重复项。
思考题:你能否想出解决办法?
这些问题同样适用于其他对象,而不仅仅是通道。
在我们的冒险中,我们有一枚银币和一枚金币。一方面,如果不接受在只有一枚硬币存在的地点得到硬币,那将是愚蠢的。
另一方面,在两种硬币都在同一位置存在的情况下,玩家应该可以有两个拾取选择。
这立即将我们带到了解析器的第三个问题:
1. 一个标签只能是一个单词;“ sliver coin”这是俩单词他不接受啊
所有三个问题都将在本章中解决,从问题#1 开始。它通过为每个对象提供一个标签列表来解决,而不仅仅是一个标签。
# object.h
```c
typedef struct object {
const char *description;
const char **tags;
struct object *location;
struct object *destination;
} OBJECT;
extern OBJECT objs[];
#define field (objs + 0)
#define cave (objs + 1)
#define silver (objs + 2)
#define gold (objs + 3)
#define guard (objs + 4)
#define player (objs + 5)
#define intoCave (objs + 6)
#define exitCave (objs + 7)
#define wallField (objs + 8)
#define wallCave (objs + 9)
#define endOfObjs (objs + 10)
```
# object.c
```c
#include <stdio.h>
#include "object.h"
static const char *tags0[] = {"field", NULL};
static const char *tags1[] = {"cave", NULL};
static const char *tags2[] = {"silver", "coin", "silver coin", NULL};
static const char *tags3[] = {"gold", "coin", "gold coin", NULL};
static const char *tags4[] = {"guard", "burly guard", NULL};
static const char *tags5[] = {"yourself", NULL};
static const char *tags6[] = {"east", "entrance", NULL};
static const char *tags7[] = {"west", "exit", NULL};
static const char *tags8[] = {"west", "north", "south", "forest", NULL};
static const char *tags9[] = {"east", "north", "south", "rock", NULL};
//我们不固定标签长度在结束的时候用NULL来标记
OBJECT objs[] = {
{"an open field" , tags0, NULL , NULL },
{"a little cave" , tags1, NULL , NULL },
{"a silver coin" , tags2, field, NULL },
{"a gold coin" , tags3, cave , NULL },
{"a burly guard" , tags4, field, NULL },
{"yourself" , tags5, field, NULL },
{"a cave entrance to the east", tags6, field, cave },
{"an exit to the west" , tags7, cave , field },
{"dense forest all around" , tags8, field, NULL },
{"solid rock all around" , tags9, cave , NULL }
//我们用NULL来阻绝进入一个你不知道的地方
};
```
当然,要让这个改动生效,我们还需要调整<em>noun.c</em>中的<em>objectHasTag</em>函数。
<em>同时,我们将让函数 getVisible</em>和<em>getPossession</em> 告知玩家他必须更具体的选择你到底是银币还是金币 ,而不是随机选择任何一个对象。
# noun.h
```c
extern OBJECT *getVisible(const char *intention, const char *noun);
extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun);
```
# <strong>noun.c</strong>
```c
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "object.h"
#include "misc.h"
static bool objectHasTag(OBJECT *obj, const char *noun)
{
if (noun != NULL && *noun != '\0')
{
const char **tag;
for (tag = obj->tags; *tag != NULL; tag++)
{
if (strcmp(*tag, noun) == 0) return true;
}//扫描对象的tag列表
}
return false;
}
static OBJECT ambiguousNoun;//我们需要这玩意的地址帮助我们把它当成一个返回值
static OBJECT *getObject(const char *noun, OBJECT *from, DISTANCE maxDistance)
{
OBJECT *obj, *res = NULL;
for (obj = objs; obj < endOfObjs; obj++)
{
if (objectHasTag(obj, noun) && getDistance(from, obj) <= maxDistance)
{
res = res == NULL ? obj : &ambiguousNoun;//标签不明确的解决方案哦
}
}
return res;
}
OBJECT *getVisible(const char *intention, const char *noun)
{
OBJECT *obj = getObject(noun, player, distOverthere);
if (obj == NULL)
{
if (getObject(noun, player, distNotHere) == NULL)
{
printf("I don't understand %s.\n", intention);
}
else
{
printf("You don't see any %s here.\n", noun);
}
}
else if (obj == &ambiguousNoun)//模糊匹配
{
printf("Please be specific about which %s you mean.\n", noun);
obj = NULL;
}
return obj;
}
OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun)
{
OBJECT *obj = NULL;
if (from == NULL)
{
printf("I don't understand who you want to %s.\n", verb);
}
else if ((obj = getObject(noun, from, distHeldContained)) == NULL)
{
if (getObject(noun, player, distNotHere) == NULL)
{
printf("I don't understand what you want to %s.\n", verb);
}
else if (from == player)
{
printf("You are not holding any %s.\n", noun);
}
else
{
printf("There appears to be no %s you can get from %s.\n",
noun, from->description);
}
}
else if (obj == &ambiguousNoun)//模糊匹配
{
printf("Please be specific about which %s you want to %s.\n",
noun, verb);
obj = NULL;
}
else if (obj == from)
{
printf("You should not be doing that to %s.\n", obj->description);
obj = NULL;
}
return obj;
}
```
问题 #3 可以通过从函数<em>parseAndExecute</em>中删除一个 [空格](http://en.wikipedia.org/wiki/Space_(punctuation))字符来解决(下面的第 10 行)。这个解决方案远非完美('silver' 和 'coin' 之间的双空格是大咩的),但直到我们在第 13 章中让自己成为一个更好的解析器之前。
# parsexec.c
```c
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "location.h"
#include "inventory.h"
bool parseAndExecute(char *input)
{
char *verb = strtok(input, " \n");
char *noun = strtok(NULL, "\n");
if (verb != NULL)
{
if (strcmp(verb, "quit") == 0)
{
return false;
}
else if (strcmp(verb, "look") == 0)
{
executeLook(noun);
}
else if (strcmp(verb, "go") == 0)
{
executeGo(noun);
}
else if (strcmp(verb, "get") == 0)
{
executeGet(noun);
}
else if (strcmp(verb, "drop") == 0)
{
executeDrop(noun);
}
else if (strcmp(verb, "give") == 0)
{
executeGive(noun);
}
else if (strcmp(verb, "ask") == 0)
{
executeAsk(noun);
}
else if (strcmp(verb, "inventory") == 0)
{
executeInventory();
}
else
{
printf("I don't know how to '%s'.\n", verb);
}
}
return true;
}
```
模块<em>main.c</em>、<em>inventory.c</em>、<em>location.c</em>、<em>move.c</em> 和<em>misc.c</em>保持不变
现在对象数组 ( <em>object.c</em> ) 开始在多个维度上增长(特别是在引入多个标签的情况下),我们需要一种使其更易于维护的方法。
猜猜看该怎么办?