洞穴寻金游戏(亦称猛兽世界)是早期的计算机游戏之一。在这个游戏中,探险者在迷宫洞穴中漫游,寻找金子的同时避免被猛兽吃掉或掉入深渊。下面是有关这个游戏的叙述。
猛兽世界由含有多个屋子的洞穴组成。每间屋子可能为空,也可能包含单件物品。这类物品可以是下列对象之一:一只猛兽、一块金子、一道深渊、一个出口。猛兽指的是一种凶恶的动物,探险者遇见它(处在同一间屋子内)肯定送命。但是,用剑可以杀死猛兽。寻找金子是探险者的目的,然后通过带有出口的屋子逃离洞穴。探险者还需要避免掉入深渊,掉入深渊可以叫人送命。每个迷宫洞穴最多有一只猛兽、一块金子及一个出口。洞穴还可能有零到多个深渊。这些东西在单间屋子里只能出现一种。猛兽世界的边界由固体墙构成,探险者不能穿透墙体。探险者可以在各间屋子之间移动,但要冒着掉入深渊和被猛兽吃掉的危险。因为隔壁屋子可能存在猛兽或深渊。下图是一个4X4的猛兽世界,其中有一只猛兽、一块金子、两道深渊、一个出口。

      +------+------+------+------+
      |Player|      | Pit  |      |
      |Exit  |      |      |      |
      +------+------+------+------+
      |      |      |      |      |
      |        |      |      |      |
      +------+------+------+------+
      |        | Pit  |      |Wumpus|
      |        |      |      |      |
      +------+------+------+------+
      |        |      |      |Gold  |
      |        |      |      |      |
      +------+------+------+------+
探险者在猛兽世界的动作有:左转turn left、右转turn right、前行move forward、射击shoot、攀爬climb、攫取grab。左转、右转改变探险者行走和射击的方向。转身指的是在原有方向上左转或右转90度。比如,探险者从北边左转90度,将使探险者面向西面。射击指的是将箭射向当前方向上的隔壁屋子里。如果这个邻近的屋子里有猛兽,就将杀死它。前行指的是在当前方向上从一间屋子移动到另一间屋子。如果在这个方向上前行且没有墙的阻挡,就将进入到隔壁邻近的屋子中。攫取指的是如果当前屋子内有金子就抓取它。攀爬指的是如果处在洞穴的出口就爬出来。
用户可以通过一系列的感知行为感觉到猛兽世界里探险者所处的处境。如果探险者所处的隔壁屋子里有一只猛兽他就将能闻到(smell)猛兽的气味;如果隔壁屋子是一道深渊,则探险者能感觉到(feel)深渊的阴森;如果探险者处在有出口的屋子,他将能看到(see)上方透出的光线;如果探险者与金子同处一屋,他将能看到(see)金子发出的光芒。当探险者执行某些动作达到目的后,他将被告知结果。比如,当探险者与金子同处一屋,并且探险者发出了攫取动作,则要告诉探险者“你得到攫取到了金子(You grabbed the gold)”;当探险者射出了箭而被射的屋子里确有猛兽,则要告诉探险者“你杀死了猛兽(You killed the wumpus)“。最后,如果探险者试图直接走出洞穴他将撞到墙上。选手也只需要判断从不同方向接近的哪一间邻近屋子可能存在猛兽或深渊。


在这个实验中,你需要做的是:
1.    阅读上述关于猛兽世界的描述。设计猛兽世界的初始状态:猛兽世界的大小、探险者的位置和方向、以及可能的猛兽,金子,出口和深渊的位置。
2.    采用某种易于处理的表示方法来表示上述状态信息。
3.    从标准输入读入用户指令,由此修改以前的状态信息。
4.    将探险者当前正在体验的任何感知行为输出到屏幕上。
5.    判断用户的输赢,并将相应结果打印输出,终止游戏。
6.    如果用户输入文件结束符,或者游戏以正常输赢结束,打印猛兽世界的当前状态。


猛兽世界文件格式:由行组成, 每行有一个正文关键字,2或5个整数参数。关键字指明参数的意义。比如,前述猛兽世界可以用下述文件表述:

dim             4 4
player        0 0 1 0 1
wumpus          3 2
gold            3 3
pit             2 0
pit             1 2
exit            0 0

dim 分别表示x和y的大小。X代表水平,y代表垂直。
注意:猛兽世界的维数最大不会超出100X100。
player 有5个参数:按序为 x y o g a 。x,y 代表位置;o 代表探险者当前的方向,0代表东,1代表南,2代表西,3代表北;g 和a 是布尔量,分别用来指明探险者是否有金子或箭。注意:探险者最多只有一支箭。
wumpus 代表wumpus的位置,这里为(3,2)。
gold, pit, and exit分别指明金子、深渊、出口的位置。这里有两道深渊。
各关键字的出现必须按照这个次序,多个深渊应该集中出现。dim, player, and和exit行是必须出现的,其它行根据具体情况来定。
初始状态和中间状态、最终状态的保留的格式都是相同的。

选手(用户)输入格式:选手用正文发出指令。一行一条指令。指令由一个或两个单词组成。两个单词的指令中间由空格分隔。下面是一些可能的指令:

shoot
grab
climb
turn left
turn right
move forward

如果选手输入了非法的命令,程序必须做出"That command had no effect."的回应。

感知输出格式: 探险者在探险过程中的感知应该按下列方式,一行一行地精确打印输出。如果一个动作引起了多个感知,应该按下列显示的次序都打印出来。比如,如果探险者被猛兽咬死或探险者掉入深渊,首先应该报告他的所有感知。注意,一旦探险者从洞穴出来,只能报告最后两条感知中的一种,不存在其它的出洞感知。

You bumped into a wall.
You killed the wumpus.
Your arrow disappeared into the darkness.
You smell a wumpus.
You feel a breeze.
You see glitter.
You see light above.
You fell into a pit.
You were killed by the wumpus.
You grabbed the gold.
You are out with the gold.
You are out without the gold.

攫取金子和杀死猛兽:如果探险者和金子同处一间屋子,并且探险者执行了攫取(grab)动作,那么在接下来的游戏过程中探险者将一直怀抱金子。这意味着在刚才的那间屋子里不再有金子,因此它也将不再会引起探险者看到金子的光芒。如果由此保留猛兽世界的状态,则在状态文件中应该不再出现金子,并且player行上的g参数应该为1。
同样地,如果探险者将箭射向了猛兽所在的屋子,猛兽被杀死了,猛兽世界中将不再有猛兽出现,因此也不再会闻到猛兽的气味,猛兽也不会再出现在状态文件中。也要注意一旦射出箭,探险者也不再有箭,由此保留的游戏状态中的player行上的a参数应该为0。


输出感知:一旦有感知就要立刻输出。如果探险者处在洞穴边上的屋子并且试图往外移动,就应该报告撞墙的感知。比如,在上述猛兽世界状态布局中,探险者的位置为(0,0)并且他还试图往北移动,此时就要报告感知“You bumped into a wall.”,但探险者的位置没有改变。
如果探险者处于一间有金子的房间,就应该报告"You see glitter." 在本例中,如果探险者位于(2,3),并且向东移动到(3,3),那么就应该报告看到金子光芒的感知。如果探险者就在(3,3)屋子里左转、右转或朝墙移动(结果并没有移动成功),也应该报告看到金子光芒的感知。。一旦探险者拿到了金子,对金子光芒的感知就不应该再报告,因为目前金子为探险者所拥有。当探险者成功地执行了攫取动作时,应该报告"You grabbed the gold.",但是紧跟着这个动作后只能报告一次。如果探险者进入了一个带有出口的屋子,也应该报告"You see light above."。
如果探险者进入一间屋子,这间屋子的隔壁屋子(可以是东、南、西、北)带有深渊,应该报告
"You feel a breeze"。例如,在上述的例子中,屋子(1,2)带有深渊,处于屋子(1,2), (0,2), (2,3), (1,1), and (1,3)都要报告这个感知。注意,如果有多于一个的邻近深渊,应该为每个深渊报告这个感知。
与深渊的感知类似,如果猛兽在当前屋子或邻近屋子,都应该报告闻到猛兽气味的感知。如果探险者杀死了猛兽,那么将不再报告这个感知。


实验步骤
下面既是解题的步骤,也暗示了评分标准。注意:标注的得分是得分的上限。最终实验的得分还要由清晰性、功能分解、文档及程序没有bug等因素来决定。
第一步:装入和保存猛兽世界状态布局。(3分)
写程序读入按命令行输入的猛兽世界文件,并存储为某种合适的游戏格式。然后再次将猛兽世界存为指定的输出文件。
第二步:在猛兽世界中移动漫游(3分)
拓广程序功能。从用户接收左转、右转、前行等命令,程序更新猛兽世界的状态使得探险者由此移动。因此,在探险者前行一步后,如果你准备装入上述的猛兽世界此时探险者的位置应该在(0,1)。
第三步:执行掉入深渊及被猛兽吃掉(1分)
修改程序,使得当用户进入同一间存在深渊或猛兽的屋子时,程序发布消息而终止。所发布的消息分别为"You fell into a pit." 或 "You were killed by the wumpus."
第四步:加入感知(3分)
拓广程序功能。如果探险者撞墙、闻到猛兽气味、感到深渊阴森、看到金子光芒或拿到金子,将感知报告给用户。
第五步:执行攫取和射击(2分)
拓广程序功能。允许用户拿到金子。当用户得到金子后修改猛兽世界的状态,使得金子不再出现并且player的g参数为1。对于射击行为,如果猛兽被杀死,报告"You killed the wumpus"并且修改猛兽世界的表示状态使得猛兽不再出现,否则报告"Your arrow disappeared into the darkness."(在这个阶段,你可以假设箭的供应源源不断)
第六步:持有金子、爬出出口,及单箭状态(3分)
结束程序的时候,必须让程序知道你持有金子。因此在保留游戏状态时,金子的持有参数应该为1。并且如果是探险者得到金子,当他从出口爬出时,程序必须给出消息"You are out with the gold."然后才终止。如果用户在爬出时未持有金子,则必须给出消息"You are out without the gold."。在一间没有出口的屋子里执行爬的动作,将导致输出"You bumped into a wall."。另外,还要记住箭已经被射出,用户只有一次射击机会。如果用户在射出了唯一的箭后试图再次射击,所有这些后续的射击命令都将导致输出"That command has no effect."。

第七步:(选择性步骤)障碍物。在游戏中加入障碍物,即将某个(些)屋子设置成不能通过的障碍物,探险者从任何方向都不可进入。如果探险者试图进入将报告感知"You bumped into a wall." 在猛兽世界文件中,障碍物信息列在深渊信息之后。比如位置(1,1)上为障碍物可表示为:
blocked 1 1