ceshi
This commit is contained in:
322
技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.11.设置条件.md
Normal file
322
技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.11.设置条件.md
Normal file
@@ -0,0 +1,322 @@
|
||||
# 11.设置条件
|
||||
|
||||
到目前为止,所有对象的属性都是数据:文本、数字。但属性也可以是代码。
|
||||
|
||||
在上一章中,我们通过关闭洞穴入口(进入洞穴的通道)来限制玩家的行动自由。这已经让游戏变得更有挑战性了,但这并没有带来多少谜题,除非我们为玩家提供一个微小的可能性来打开通道。真正的挑战应该是让玩家找到通道打开的条件。
|
||||
|
||||
让我们举一个简单的例子。为了越过守卫进入山洞,玩家必须杀死或贿赂守卫(或两者兼而有之,这很有价值)。换句话说:
|
||||
|
||||
- 当警卫死亡时(HP=0),入口开放
|
||||
- 当警卫拿着银币 (贿赂警卫) 时,入口开放
|
||||
- 两者都不是,入口关闭
|
||||
|
||||
打开一个封闭的通道(在这里是进入洞穴)涉及到改变一些属性值:
|
||||
|
||||
- 目的地从 NULL(空地点) 变为洞穴
|
||||
- **textGo**从 "警卫阻止你...... "改为 "你走进山洞"
|
||||
- 在一些特殊情况下,描述和细节不需要改变。但对于一个门洞或栅栏,其中之一(或两者)通常会包含一些从 "开放 "到 "关闭 "的文字。
|
||||
|
||||
有许多方法来实现这一目标。在这里,我们将讨论一种简单、可维护和通用的方法。
|
||||
|
||||
首先,我们定义了两个独立的通道:一个代表开放通道,另一个代表封闭通道。除了上面列出的那些,这两条通道在每一个属性上都是相同的。(在你下面看到的生成的地图中,注意有两个箭头通向洞穴;一个是实线,一个是虚线)。
|
||||
|
||||
接下来,我们引入一个名为条件的新属性,它决定了某个对象是否存在。这两个通道将被赋予互斥的条件,因此在任何时候都只能有一个存在。
|
||||
|
||||
每个条件将被实现为一个布尔函数:**TRUE**意味着该对象存在,**FALSE**意味着它不存在。
|
||||
|
||||
```c
|
||||
bool intoCaveIsOpen(void)
|
||||
{
|
||||
return guard->health == 0 || silver->location == guard;
|
||||
}
|
||||
|
||||
bool intoCaveIsClosed(void)
|
||||
{
|
||||
return guard->health > 0 && silver->location != guard;
|
||||
}
|
||||
```
|
||||
|
||||
::: warning 🤔 思考题:你能仿照上面例子自己写一些条件函数吗?
|
||||
:::
|
||||
|
||||
新的属性条件是一个指向这样一个函数的指针。
|
||||
|
||||
```c
|
||||
bool (*condition)(void);
|
||||
```
|
||||
|
||||
接下来,我们可以立即开始为 object.txt 中的新属性分配函数。
|
||||
|
||||
## object.txt
|
||||
|
||||
```txt
|
||||
- intoCave
|
||||
condition intoCaveIsOpen
|
||||
description "a cave entrance to the east"
|
||||
tags "east", "entrance"
|
||||
location field
|
||||
destination cave
|
||||
detail "The entrance is just a narrow opening in a small outcrop.\n"
|
||||
textGo "You walk into the cave.\n"
|
||||
|
||||
- intoCaveBlocked
|
||||
condition intoCaveIsClosed
|
||||
description "a cave entrance to the east"
|
||||
tags "east", "entrance"
|
||||
location field
|
||||
prospect cave
|
||||
detail "The entrance is just a narrow opening in a small outcrop.\n"
|
||||
textGo "The guard stops you from walking into the cave.\n"
|
||||
```
|
||||
|
||||
::: warning 🤔 思考题:尝试自己实现上面的伪代码
|
||||
:::
|
||||
|
||||
这两个 "条件 "函数是如此具体,每一个条件函数都只用这一次。现在,我们可以在我们需要的地方定义这些函数。许多编程语言都支持匿名函数,像这样:
|
||||
|
||||
```txt
|
||||
- intoCave
|
||||
condition { return guard->health == 0 || silver->location == guard; }
|
||||
...
|
||||
|
||||
- intoCaveBlocked
|
||||
condition { return guard->health > 0 && silver->location != guard; }
|
||||
...
|
||||
```
|
||||
|
||||
所以现在我们可以把额外的段落和条件添加到 object.txt 中,就像前面解释的那样。
|
||||
|
||||
## new object.txt
|
||||
|
||||
```txt
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "object.h"
|
||||
|
||||
typedef struct object {
|
||||
bool (*condition)(void);
|
||||
const char *description;
|
||||
const char **tags;
|
||||
struct object *location;
|
||||
struct object *destination;
|
||||
struct object *prospect;
|
||||
const char *details;
|
||||
const char *contents;
|
||||
const char *textGo;
|
||||
int weight;
|
||||
int capacity;
|
||||
int health;
|
||||
} OBJECT;
|
||||
|
||||
extern OBJECT objs[];
|
||||
|
||||
- field
|
||||
description "an open field"
|
||||
tags "field"
|
||||
details "The field is a nice and quiet place under a clear blue sky."
|
||||
capacity 9999
|
||||
|
||||
- cave
|
||||
description "a little cave"
|
||||
tags "cave"
|
||||
details "The cave is just a cold, damp, rocky chamber."
|
||||
capacity 9999
|
||||
|
||||
- silver
|
||||
description "a silver coin"
|
||||
tags "silver", "coin", "silver coin"
|
||||
location field
|
||||
details "The coin has an eagle on the obverse."
|
||||
weight 1
|
||||
|
||||
- gold
|
||||
description "a gold coin"
|
||||
tags "gold", "coin", "gold coin"
|
||||
location cave
|
||||
details "The shiny coin seems to be a rare and priceless artefact."
|
||||
weight 1
|
||||
|
||||
- guard
|
||||
description "a burly guard"
|
||||
tags "guard", "burly guard"
|
||||
location field
|
||||
details "The guard is a really big fellow."
|
||||
contents "He has"
|
||||
health 100
|
||||
capacity 20
|
||||
|
||||
- player
|
||||
description "yourself"
|
||||
tags "yourself"
|
||||
location field
|
||||
details "You would need a mirror to look at yourself."
|
||||
contents "You have"
|
||||
health 100
|
||||
capacity 20
|
||||
|
||||
- intoCave
|
||||
condition { return guard->health == 0 || silver->location == guard; }
|
||||
description "a cave entrance to the east"
|
||||
tags "east", "entrance"
|
||||
location field
|
||||
destination cave
|
||||
details "The entrance is just a narrow opening in a small outcrop."
|
||||
textGo "You walk into the cave."
|
||||
|
||||
- intoCaveBlocked
|
||||
condition { return guard->health > 0 && silver->location != guard; }
|
||||
description "a cave entrance to the east"
|
||||
tags "east", "entrance"
|
||||
location field
|
||||
prospect cave
|
||||
details "The entrance is just a narrow opening in a small outcrop."
|
||||
textGo "The guard stops you from walking into the cave."
|
||||
|
||||
- exitCave
|
||||
description "an exit to the west"
|
||||
tags "west", "exit"
|
||||
location cave
|
||||
destination field
|
||||
details "Sunlight pours in through an opening in the cave's wall."
|
||||
textGo "You walk out of the cave."
|
||||
|
||||
- wallField
|
||||
description "dense forest all around"
|
||||
tags "west", "north", "south", "forest"
|
||||
location field
|
||||
details "The field is surrounded by trees and undergrowth."
|
||||
textGo "Dense forest is blocking the way."
|
||||
|
||||
- wallCave
|
||||
description "solid rock all around"
|
||||
tags "east", "north", "south", "rock"
|
||||
location cave
|
||||
details "Carved in stone is a secret password 'abccb'."
|
||||
textGo "Solid rock is blocking the way."
|
||||
```
|
||||
|
||||
::: warning 🤔 思考题:尝试自己实现这些功能,并看看与你之前设计的有何不同
|
||||
:::
|
||||
|
||||
为了使这些条件发挥作用,我们需要调整函数 isHolding 和 getDistance。
|
||||
|
||||
## misc.c
|
||||
|
||||
```c
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "object.h"
|
||||
#include "misc.h"
|
||||
|
||||
bool isHolding(OBJECT *container, OBJECT *obj)
|
||||
{
|
||||
return validObject(obj) && obj->location == container;
|
||||
}
|
||||
|
||||
OBJECT *getPassage(OBJECT *from, OBJECT *to)
|
||||
{
|
||||
if (from != NULL && to != NULL)
|
||||
{
|
||||
OBJECT *obj;
|
||||
for (obj = objs; obj < endOfObjs; obj++)
|
||||
{
|
||||
if (isHolding(from, obj) && obj->prospect == to)
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DISTANCE getDistance(OBJECT *from, OBJECT *to)
|
||||
{
|
||||
return to == NULL ? distUnknownObject :
|
||||
!validObject(to) ? distNotHere :
|
||||
to == from ? distSelf :
|
||||
isHolding(from, to) ? distHeld :
|
||||
isHolding(to, from) ? distLocation :
|
||||
isHolding(from->location, to) ? distHere :
|
||||
isHolding(from, to->location) ? distHeldContained :
|
||||
isHolding(from->location, to->location) ? distHereContained :
|
||||
getPassage(from->location, to) != NULL ? distOverthere :
|
||||
distNotHere;
|
||||
}
|
||||
|
||||
OBJECT *actorHere(void)
|
||||
{
|
||||
OBJECT *obj;
|
||||
for (obj = objs; obj < endOfObjs; obj++)
|
||||
{
|
||||
if (isHolding(player->location, obj) && obj != player &&
|
||||
obj->health > 0)
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int listObjectsAtLocation(OBJECT *location)
|
||||
{
|
||||
int count = 0;
|
||||
OBJECT *obj;
|
||||
for (obj = objs; obj < endOfObjs; obj++)
|
||||
{
|
||||
if (obj != player && isHolding(location, obj))
|
||||
{
|
||||
if (count++ == 0)
|
||||
{
|
||||
printf("%s:\n", location->contents);
|
||||
}
|
||||
printf("%s\n", obj->description);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
```
|
||||
|
||||
::: warning 🤔 思考题:想想我们调整了什么
|
||||
:::
|
||||
|
||||
注意:
|
||||
|
||||
1. 警卫不可能会死,所以可以说我们的条件函数中的**HP**是很无用的。当然,这很容易通过添加一个 kill 命令来解决,见第 20 章。
|
||||
2. 这两个条件函数是互补的;它们有资格成为重复的代码。为了消除这一点,我们可能决定让一个函数调用另一个函数(用'!'操作符来否定结果)。一个匿名函数没有(稳定的)名字,但我们可以用它的对象来指代它。我们可以用 intoCaveBlocked 的条件函数代替。
|
||||
3. 为了简单起见,条件函数没有参数。实际上,传递一个参数 OBJECT *obj 可能更好;这使得编写更多的通用条件函数成为可能,可以在多个对象中重复使用。
|
||||
4. 在理论上,任何对象都可以成为 "条件"。在下一章,你可以看到一个类似的技术被应用于此。
|
||||
|
||||
::: warning 🤔 思考题:想一想上面第二点要怎么用 C 来实现?
|
||||
:::
|
||||
|
||||
输出样例
|
||||
|
||||
Welcome to Little Cave Adventure.
|
||||
You are in an open field.
|
||||
You see:
|
||||
a silver coin
|
||||
a burly guard
|
||||
a cave entrance to the east
|
||||
dense forest all around
|
||||
|
||||
--> go entrance
|
||||
The guard stops you from walking into the cave.
|
||||
|
||||
--> get coin
|
||||
You pick up a silver coin.
|
||||
|
||||
--> give coin
|
||||
You give a silver coin to a burly guard.
|
||||
|
||||
--> go entrance
|
||||
You walk into the cave.
|
||||
|
||||
You are in a little cave.
|
||||
You see:
|
||||
a gold coin
|
||||
an exit to the west
|
||||
solid rock all around
|
||||
|
||||
--> quit
|
||||
|
||||
Bye!
|
||||
Reference in New Issue
Block a user