REV0 C ++(Windows上的Visual Studio)405
#include"stdafx.h"
#include<stdlib.h>
#include<time.h>
int main(){srand(time(NULL));char i,h=rand()%19,w=rand()%19,p=19,d=0,q,e,m[]="e@LwQMQOSOLT";while(p-h&&p-w){for(i=3;i--;){q=(p+m[p%4*3+i])%20;if(q==w)puts("you smell a wumpus");if(q==h)puts("you feel a breeze");}scanf_s("%d",&i);e=(d+i/10)*m[p%4]%3;q=(p+m[p%4*3+e])%20;if(i%5){if(q==w){puts("YOU KILLED THE WUMPUS!");h=p;}else{puts("arrow missed");w=(w+m[w%4*3+rand()%3])%20;}}else{p=q;d=e;if(p==h)puts("YOU FELL IN A HOLE!");}if(p==w)puts("THE WUMPUS GOT YOU!");}}
以下是演练,证明(如果您没有在危险旁边就开始)正确的比赛就可以赢得比赛。玩家会感到微风,转身并进行完整的逆时针循环。由于他仅用5步就再次感觉到微风,他知道右边的洞,并且越走越远。同样,当他闻到汗味时,不知道是对还是对,他回头做顺时针循环。他花了5步才能再次闻到臭味,所以他知道它在左边,并且确定地射击。
如果他以另一种方式循环,他会更快地找到昏迷,并且知道这与他转弯的方向相同。
REV1 C(Cygwin上的GCC),奖金431-35%= 280.15
#define u(t,s,c) if(t){puts(s);c;}
i,d,e,a,b;main(){srand(time(0));char q,p=19,h=rand()%p,w=rand()%p,*m="e@LwQMQOSOLT-\\/\n \v ";
while(p-h&&p-w){
for(i=3;i--;){q=(p+m[p%4*3+i])%20;u(q==w,"you smell a wumpus",a|=2<<p)u(q==h,"you feel a breeze",b|=1<<p)}
for(i=20;i--;)printf("%c%c",i==p?m[d+12]:48+(a>>i&2)+(b>>i&1),m[i%4+15]);
scanf("%d",&i);e=(d+i/10)*m[p%4]%3;q=(p+m[p%4*3+e])%20;
if(i%5){u(q-w,"arrow missed",w=(w+m[w%4*3+rand()%3])%20;a=0)else u(1,"YOU KILLED THE WUMPUS!",h=p)}
else{p=q;d=e;u(p==h,"YOU FELL IN A HOLE!",)}
u(p==w,"THE WUMPUS GOT YOU!",)}}
为清楚起见添加了换行符。从修订版0开始的更改如下:
@Dennis非常感谢您在Windows的Cygwin Linux模拟器上推荐GCC编译器。该编译器不需要include
rev 0程序中的s,它允许int
变量使用默认类型,main.
这是改变生活的高尔夫技巧!
此外,在Linux中运行还意味着\f
确实会导致光标向下移动而不执行回车符(与Windows中仅产生可打印符号的情况不同)。这已大大缩短了打印板的printf语句。
丹尼斯(Dennis)在评论中提供了其他一些提示,这也是我自己的提示之一:检查箭头是否碰到腹部时的状态改变:if(q==w)
> if(q-w)
(..else ..颠倒了)
额外的图形显示可显示玩家知道的有关在哪里炼金的信息/感觉轻而易举地获得35%的奖励。(我删除了旧的调试版本,该版本显示了wumpus和孔的确切位置。可以在编辑历史记录中看到。)
REV2 C(Cygwin上的GCC),奖金389-35%= 252.85
#define Q(N) (N+"QTLOQMQOSOLT"[N%4*3+e])%20
#define P printf(
i,d,e,a,b;main(){int p=19,q=srand(&p),h=rand()%p,w=rand()%p;
while(p-h&&p-w){
for(e=3;e--;){q=Q(p);q-w||P"You smell a wumpus\n",a|=2<<p);q-h||P"You feel a breeze\n",b|=1<<p);}
for(i=20;i--;)P"%c%c",i-p?48+(a>>i&2)+(b>>i&1):"-\\/"[d],"\n \v "[i%4]);
scanf("%d",&i);e=(d+i/9)*"edde"[p%4]%3;q=Q(p);
if(i%5){e=rand()%3;w=q-w?P"Your arrow didn't hit anything\n",a=0)&Q(w):(p=20);}
else p=q,d=e;
}
P p-20?p-w?"YOU FELL IN A HOLE!\n":"THE WUMPUS GOT YOU!\n":"YOU KILLED THE WUMPUS!\n");}
再次感谢Dennis重构了我的代码:
Char常量m[]
替换为文字(我不知道您可以为文字编制索引。)
使用堆栈变量播种随机数(取决于系统,某些系统将内存分配随机化作为一种安全措施。)
当显示的消息放在参数内部时,必须将宏puts
替换为带有的宏,printf
并带有附加代码printf
(在格式字符串中没有足够的格式说明符的情况下,printf不会打印最后几个参数)这是必须采取的措施。)if
取而代之||
计算放置在新宏中的玩家/ wumpus的新位置。
赢/输消息放在while
循环外。if
由条件运算符代替。
在行中使用条件运算符来拍摄箭头。如果玩家错过了比赛,则需要打印一条消息并调整wumpus的位置。丹尼斯(Dennis)提供了几种组合方式以及将printf
wumpus位置计算成一个表达式的方法,但是我已经放弃了自己的方法。printf
返回打印的字符数,其Your arrow didn't hit anything\n
值为31(二进制11111)31&Q(w)==Q(w)
。
我对此编辑的其他贡献是消除了一些不必要的括号。
输出量
玩家已经在这里找到了Wumpus的位置,但他选择进行彻底的探索以找出坑的确切位置。不像我以前的调试版本那样,在整个游戏中都显示了坑洼和坑洞,而这只显示了玩家曾参观并感到微风(1)熔化坑洼(2)或两者兼有(3)的房间。(如果玩家a
射箭而未击中,则包含wumpus位置信息的变量将被重置。)
二十面体代表
注意:本节基于版本1
我的明星特色!我的代码中没有图形。要说明其工作原理,请参见下面的世界地图。二十面体上的任何点都可以由纬度0-3和经度0-4(或单个数字,long*4+lat
。)表示。地图上标记的经度线仅穿过那些经度为零的面,而纬度线则通过纬度为零的脸部中心。
玩家可以在3个可能的轴上定向,用以下符号表示:北-北-
东北-西南\
西北-东南/
。在任何给定的房间中,他在每个可用轴上都只有一个出口。在显示的显示中,播放器完成一个完整的顺时针循环。通常,很容易从玩家身上识别出他来自何处,并因此允许他去哪里。
对于初学者来说有点困难的一种情况是第四种。当您在这些极坐标行之一中看到一个斜率时,播放器来自最靠近该斜率外端的极坐标单元,并且通常面向赤道。因此,玩家面对东南,他的选择是:15(SOUTH,右边的单元格)25(northEAST,上面的单元格)或35(northWEST,下面的单元格)。
因此,基本上,我将二十面体映射到5x4网格,并按打印顺序将单元格编号为19到0。根据玩家的纬度和方向,通过下表从当前位置加上或减去当前位置来进行移动。
如果玩家离开棋盘的底部(西),他将回到板的顶部(东),反之亦然,因此他的位置将取模20。通常,通过将ascii 80(P
)赋予给出以下字符的原始值,但是原则上可以添加20的任意倍数而不影响操作。
Table of addition values for moves
Direction Symbol Latitude 0 1 2 3 Latitude 0 1 2 3
0, N-S - 1 -1 1 -1 Q O Q O
1, NE-SW \ -4 1 -1 4 L Q O T
2, NW-SE / 4 -3 3 -4 T M S L
玩家的输入(被10除以除去第二个数字)被添加到他的当前方向,并取模3以得到他的新方向。在大多数情况下,这都可以正常工作。但是,当他在极地室内并向杆子方向移动时会出现问题。折叠下面的地图时,很明显,如果他离开面向“东北”的房间,他将进入面向“东南”的新正方形,因此必须进行更正。这是e=(d+i/10)*m[p%4]%3;
通过乘以来完成的m[p%4]
。选择m []的前四个值,以便除了上面的函数外,它们还具有m[1]%3==m[2]%3==1
和的特性 m[0]%3==m[3]%3==2
。这将方向留给赤道房间,并对极地房间进行必要的校正。
进行校正的逻辑时间是在移动之后。但是,要保存字符,必须在移动之前完成。因此,必须对m []中的某些值进行转置。因此,例如,上面的表LT
不是最后2个字符TL
。
取消代码
这是1版代码,比2版的代码少混淆了。
这将在GCC / Linux上运行。我在注释中包含了使其在Visual Studio / Windows上运行所需的额外代码。有很大的不同!
//Runs on gcc/linux. For visual studio / windows, change printf(...)
//to printf(" %c%c%c",9*(i%4==1),i==p?m[d+12]:48+(a>>i&2)+(b>>i&1),10*!(i%2)) and uncomment the following lines
//#include"stdafx.h"
//#include<stdlib.h>
//#include<time.h>
//#pragma warning(once:996;once:430) //allow the use of scanf instead of scanf_s, allow default type=int.
//Though rather than using the pragma, it is shorter to follow compiler recommendation and use scanf_s and int.
#define u(t,s,c) if(t){puts(s);c;} //if(test){puts(string);additional code;}
i, //player input, loop counter
d,e, //current and proposed direction
a,b; //bit flags for where wumpus smelt / breeze felt
main(){
srand(time(0));
char q,p=19,h=rand()%p,w=rand()%p, //Initialise player, hole and wumpus. q stores proposed player position.
*m="e@LwQMQOSOLT-\\/\n \f "; //Chars 0-11: movetable. Chars 12-14:symbol for player. Chars 15-18: graphics format.
while(p-h&&p-w){
// Print warnings
for(i=3;i--;){q=(p+m[p%4*3+i])%20;u(q==w,"you smell a wumpus",a|=2<<p)u(q==h,"you feel a breeze",b|=1<<p)}
// graphic display
for(i=20;i--;)printf("%c%c",i==p?m[d+12]:48+(a>>i&2)+(b>>i&1),m[i%4+15]);
// Get player input and work out direction and room
scanf("%d",&i);
e=(d+i/10)*m[p%4]%3;
q=(p+m[p%4*3+e])%20;
// i%5 is false if player inputs 5 (move player) otherwise true (shoot arrow)
if(i%5)
{u(q-w,"arrow missed",w=(w+m[w%4*3+rand()%3])%20;a=0)else u(1,"YOU KILLED THE WUMPUS!",h=p)}
else{p=q;d=e;u(p==h,"YOU FELL IN A HOLE!",)}
u(p==w,"THE WUMPUS GOT YOU!",)
}
}
问题和好奇心
我已经利用@professorfish提到的观点,如果wumpus和pit在随机的地方开始,那么玩家就不需要在随机的地方开始。玩家总是从面向北的房间19开始。
我了解,由于wumpus不受“坑的影响”,因此wumpus可以开始进入或进入坑所在的房间。总的来说,这简化了一点。我没有具体的变量来表明游戏已经结束;当玩家与乌鸦或坑重合时结束。因此,当玩家获胜时,我会显示获胜消息,但将维修区移至玩家以跳出循环!我无法将玩家放进维修区,因为可能有wumpus,我会收到有关我不想要的wumpus的消息。
rev0程序在Visual Studio中运行完美,但是IDE在退出时说“变量i周围的堆栈损坏”。这是因为scanf函数正试图把一个int
成char.
丹尼斯报道了他的Linux机器上,因为这种不正确的行为。无论如何,通过在修订版1中使用正确的类型来修复它。
在rev 0中显示木板的那行很笨拙,在其他平台上看起来略有不同。在printf(" %c%c%c")
中间%c是显示的可打印字符。最后一个%c是ASCII 0或ASCII 10(\ n,在Windows中为带回车符的换行符。)在Windows中,似乎没有可在控制台中使用的字符,它将下降一行而不给出回车符。如果有的话,我不需要第一个c%(纬度1字符之前的ASCII 0或ASCII 9制表符。众所周知,制表符的行为未定义。)前导空格可改善格式设置(将纬度3和2字符放入更接近纬度1字符) 修订版1对此行进行了修订,该行使用\ f换页符,因此在printf的开头不需要格式字符。这使它更短,但是\ f在Windows中不起作用。