feat: 好几章
This commit is contained in:
252
3.编程思维体系构建/3.4.6.8.移动方向.md
Normal file
252
3.编程思维体系构建/3.4.6.8.移动方向.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# 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> ) 开始在多个维度上增长(特别是在引入多个标签的情况下),我们需要一种使其更易于维护的方法。
|
||||
|
||||
猜猜看该怎么办?
|
||||
Reference in New Issue
Block a user