预计阅读本页时间:-
我们准备模仿一种非常流行的随机性行为:掷骰子。掷骰子最普遍的形式是用两个6面骰子,但也有其他可能。在一些奇特冒险游戏中,使用全部5种几何上可行的骰子:4、6、8、12和20面。聪明的古希腊人证明了仅有5种规则立方体的所有面的形状和大小都相同,这些立方体成为各种骰子的基础。骰子也可以做成其他面数的,但将不会是所有面都相同,因而它们各面朝上的几率也就不会相同。
计算机计算不受这些几何上的限制,因而可以设计一种具有任意面数的电子骰子。先从6面开始,再进行扩展。
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
我们想得到从1到6之间的一个随机数。然而,rand()产生的是从0到RAND_MAX范围内的整数;RAND_MAX在stdlib.h中定义,它通常是INT_MAX。因此,需要做一些调整。下面是一种方法:
1.把随机数对6取模,将产生从0到5的整数。
2.加1。新数将为从1到6范围内的整数。
3.为了方便扩展,将步骤1中的数字6用骰子面数来代替。
下面的代码实现了这些想法:
进一步,我们想实现这样的功能:它允许掷任意个骰子,并且返回点数总和。程序清单12.11实现了这样的功能。
程序清单12.11 diceroll.c文件
这个文件中加入了一些新东西。首先,它把rollem()变成由该文件私有的函数,这个函数用于辅助roll_n_dice();其次,为了举例说明外部链接如何工作,文件声明了一个外部变量roll_count,这个变量跟踪记录函数rollem()的调用次数。例子本身有一点不妥,但它显示了外部变量是如何工作的。
再次,文件包含下面的语句:
如果使用诸如rand()的标准库函数,您需要在程序中包含标准头文件(对rand()来说是stdlib.h),而不是声明函数,因为头文件中已经包含了正确的声明。我们将仿效这一做法,提供一个头文件diceroll.h以供函数roll_n_dice()使用。将文件名置于双引号而非尖括号中,是为了指示编译器在本地寻找文件,而不是到编译器存放标准头文件的标准位置去寻找文件。“在本地寻找”的意义取决于具体的C实现。一些常见的解释是将头文件与源代码文件放在同一个目录或文件夹中,或者与工程文件(如果编译器使用它们)放在同一个目录或文件夹中。程序清单12.12显示了该头文件的内容。
程序清单12.12 diceroll.h文件
这个头文件中包含函数原型声明和一个extern声明。因为文件diceroll.c包含了这一头文件,它也就实际上包含了roll_count的两个声明:
这是可以的。一个变量只可以有一个定义声明,但使用extern的声明是一个引用声明,这样的声明想用多少就可以用多少。
使用roll_n_dice()的程序也应该包含这一头文件。这样做不仅仅提供roll_n_dice()原型,还使得roll_count对程序可用。程序清单12.13证明了这些。
程序清单12.13 manydice.c文件
将程序清单12.13与包含程序清单12.11的文件一起编译。为了简化问题,把程序清单12.11、12.12和12.13放在同一文件中或同一目录下。运行最后得到的程序,输出应该像下面这样:
因为程序使用srand()来随机确定随机数种子,所以大多数情况下,即使有相同的输入也不可能得到相同的输出。注意,manydice.c中的main()确实可以访问diceroll.c中定义的变量roll_count。
可通过多种方式使用roll_n_dice()。对于sides为2的情形,程序模仿掷硬币,“面朝上”为2,“背朝上”为1(反之亦然,您可以随意选择)。您可以很容易地修改程序来像显示总体结果那样显示个别结果,或者建一个掷双骰子赌博模拟器。如果需要掷多次骰子,像在一些角色扮演类游戏中一样,很容易修改程序来产生下列输出:
randl()或rand()(但不是rollem())的另一个用处是创建一个猜数程序:计算机选数,您来猜。自己试着做一下。