C/C++实现投骰子游戏
我们将要模拟一个非常流行的游戏——掷骰子。
骰子的形式多种多样,最普遍的是使用两个6面骰子。在一些冒险游戏中,会使用5种骰子:4面、6面、8面、12面和20面。聪明的古希腊人证明了只有5种正多面体,它们的所有面都具有相同的形状和大小。各种不同类型的骰子就是根据这些正多面体发展而来。也可以做成其他面数的,但是其所有的面不会都相等,因此各个面朝上的几率就不同。
计算机计算不用考虑几何的限制,所以可以设计任意面数的电子骰子。我们先从6面开始。
我们想获得1~6的随机数。然而,rand()生成的随机数在0~RAND_MAX之间。RAND_MAX被定义在stdlib.h中,其值通常是INT_MAX。因此,需要进行一些调整,方法如下。
1.把随机数求模6,获得的整数在0~5之间。
2.结果加1,新值在1~6之间。
3.为方便以后扩展,把第1步中的数字6替换成骰子面数。
下面的代码实现了这3个步骤:
#include/*提供rand()的原型*/ introllem(intsides) { introll; roll=rand()%sides+1; returnroll; }
我们还想用一个函数提示用户选择任意面数的骰子,并返回点数总和。
/*diceroll.c--掷骰子模拟程序*/ /*与mandydice.c一起编译*/ #include"diceroll.h" #include#include /*提供库函数rand()的原型*/ introll_count=0;/*外部链接*/ staticintrollem(intsides)/*该函数属于该文件私有*/ { introll; roll=rand()%sides+1; ++roll_count;/*计算函数调用次数*/ returnroll; } introll_n_dice(intdice,intsides) { intd; inttotal=0; if(sides<2) { printf("Needatleast2sides.\n"); return-2; } if(dice<1) { printf("Needatleast1die.\n"); return-1; } for(d=0;d 该文件加入了新元素。第一,rollem()函数属于该文件私有,它是roll_n_dice()的辅助函数。第二,为了演示外部链接的特性,该文件声明了一个外部变量roll_count。该变量统计调用rollem()函数的次数。这样设计有点蹩脚,仅为了演示外部变量的特性。第三,该文件包含以下预处理指令:
#include"diceroll.h"如果使用标准库函数,如rand(),要在当前文件中包含标准头文件(对rand()而言要包含stdlib.h),而不是声明该函数。因为头文件中已经包含了正确的函数原型。我们效仿这一做法,把roll_n_dice()函数的原型放在diceroll.h头文件中。把文件名放在双引号中而不是尖括号中,指示编译器在本地查找文件,而不是到编译器存放标准头文件的位置去查找文件。“本地查找”的含义取决于具体的实现。一些常见的实现把头文件与源代码文件或工程文件(如果编译器使用它们的话)放在相同的目录或文件夹中。
//diceroll.h externintroll_count; introll_n_dice(intdice,intsides);该头文件中包含一个函数原型和一个extern声明。由于direroll.c文件包含了该文件,direroll.c实际上包含了roll_count的两个声明:
externintroll_count;//头文件中的声明(引用式声明) introll_count=0;//源代码文件中的声明(定义式声明)这样做没问题。一个变量只能有一个定义式声明,但是带extern的声明是引用式声明,可以有多个引用式声明。
使用roll_n_dice()函数的程序都要包含diceroll.c头文件。包含该头文件后,程序便可使用roll_n_dice()函数和roll_count变量。/*manydice.c--多次掷骰子的模拟程序*/ /*与diceroll.c一起编译*/ #include#include /*为库函数srand()提供原型*/ #include /*为time()提供原型*/ #include"diceroll.h"/*为roll_n_dice()提供原型,为roll_count变量提供声明*/ intmain(void) { intdice,roll; intsides; intstatus; srand((unsignedint)time(0));/*随机种子*/ printf("Enterthenumberofsidesperdie,0tostop.\n"); while(scanf("%d",&sides)==1&&sides>0) { printf("Howmanydice?\n"); if((status=scanf("%d",&dice))!=1) { 905 if(status==EOF) break;/*退出循环*/ else { printf("Youshouldhaveenteredaninteger."); printf("Let'sbeginagain.\n"); while(getchar()!='\n') continue;/*处理错误的输入*/ printf("Howmanysides?Enter0tostop.\n"); continue;/*进入循环的下一轮迭代*/ } } roll=roll_n_dice(dice,sides); printf("Youhaverolleda%dusing%d%d-sideddice.\n", roll,dice,sides); printf("Howmanysides?Enter0tostop.\n"); } printf("Therollem()functionwascalled%dtimes.\n", roll_count);/*使用外部变量*/ 906 printf("GOODFORTUNETOYOU!\n"); return0; } 要与包含程序清单12.11的文件一起编译该文件。可以把程序清单12.11、12.12和12.13都放在同一文件夹或目录中。运行该程序,下面是一个输出示例:
Enterthenumberofsidesperdie,0tostop. 6 Howmanydice? 2 Youhaverolleda12using26-sideddice. Howmanysides?Enter0tostop. 6 Howmanydice? 2 Youhaverolleda4using26-sideddice. Howmanysides?Enter0tostop. 6 Howmanydice? 2 907 Youhaverolleda5using26-sideddice. Howmanysides?Enter0tostop. 0 Therollem()functionwascalled6times. GOODFORTUNETOYOU!因为该程序使用了srand()随机生成随机数种子,所以大多数情况下,即使输入相同也很难得到相同的输出。注意,manydice.c中的main()访问了定义在diceroll.c中的roll_count变量。
有3种情况可以导致外层while循环结束:side小于1、输入类型不匹配(此时scanf()返回0)、遇到文件结尾(返回值是EOF)。为了读取骰子的点数,该程序处理文件结尾的方式(退出while循环)与处理类型不匹配(进入循环的下一轮迭代)的情况不同。
可以通过多种方式使用roll_n_dice()。sides等于2时,程序模仿掷硬币,“正面朝上”为2,“反面朝上”为1(或者反过来表示也行)。很容易修改该程序单独显示点数的结果,或者构建一个骰子模拟器。如果要掷多次骰子(如在一些角色扮演类游戏中),可以很容易地修改程序以输出类似的结果:
Enterthenumberofsets;enterqtostop. 18 Howmanysidesandhowmanydice? 63 Hereare18setsof36-sidedthrows. 908 121069814815914121711710 13814 Howmanysets?Enterqtostop. qrand1()或rand()(不是rollem())还可以用来创建一个猜数字程序,让计算机选定一个数字,你来猜。读者感兴趣的话可以自己编写这个程序。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。