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 <stdlib.h> /* 提供rand()的原型 */
int rollem(int sides)
{
int roll;
roll = rand() % sides + 1;
return roll;

}

我們還想用一個函數提示用戶選擇任意麪數的骰子,並返回點數總和。

/* diceroll.c -- 擲骰子模擬程序 */
/* 與 mandydice.c 一起編譯 */
#include "diceroll.h"
#include <stdio.h>
#include <stdlib.h>      /* 提供庫函數 rand()的原型 */
int roll_count = 0;      /* 外部鏈接 */
static int rollem(int sides)  /* 該函數屬於該文件私有 */
{
int roll;
roll = rand() % sides + 1;
++roll_count;       /* 計算函數調用次數 */
return roll;
}
int roll_n_dice(int dice, int sides)
{
int d;
int total = 0;
if (sides < 2)
{
printf("Need at least 2 sides.\n");
return -2;
}
if (dice < 1)
{
printf("Need at least 1 die.\n");
return -1;
}
for (d = 0; d < dice; d++)
total += rollem(sides);
return total;
}

該文件加入了新元素。第一,rollem()函數屬於該文件私有,它是 roll_n_dice()的輔助函數。第二,爲了演示外部鏈接的特性,該文件聲明瞭 一個外部變量roll_count。該變量統計調用rollem()函數的次數。這樣設計有 點蹩腳,僅爲了演示外部變量的特性。第三,該文件包含以下預處理指令:

#include "diceroll.h"

如果使用標準庫函數,如 rand(),要在當前文件中包含標準頭文件(對 rand()而言要包含stdlib.h),而不是聲明該函數。因爲頭文件中已經包含了 正確的函數原型。我們效仿這一做法,把roll_n_dice()函數的原型放在 diceroll.h頭文件中。把文件名放在雙引號中而不是尖括號中,指示編譯器在 本地查找文件,而不是到編譯器存放標準頭文件的位置去查找文件。“本地 查找”的含義取決於具體的實現。一些常見的實現把頭文件與源代碼文件或 工程文件(如果編譯器使用它們的話)放在相同的目錄或文件夾中。

//diceroll.h
extern int roll_count;
int roll_n_dice(int dice, int sides);

該頭文件中包含一個函數原型和一個 extern 聲明。由於 direroll.c 文件 包含了該文件, direroll.c實際上包含了roll_count的兩個聲明:

extern int roll_count;   // 頭文件中的聲明(引用式聲明)
int roll_count = 0;     // 源代碼文件中的聲明(定義式聲明)

這樣做沒問題。一個變量只能有一個定義式聲明,但是帶 extern 的聲明 是引用式聲明,可以有多個引用式聲明。
使用 roll_n_dice()函數的程序都要包含 diceroll.c 頭文件。包含該頭文件 後,程序便可使用roll_n_dice()函數和roll_count變量。

/* manydice.c -- 多次擲骰子的模擬程序 */
/* 與 diceroll.c 一起編譯*/
#include <stdio.h>
#include <stdlib.h>    /* 爲庫函數 srand() 提供原型 */
#include <time.h>     /* 爲 time() 提供原型      */
#include "diceroll.h"   /* 爲roll_n_dice()提供原型,爲roll_count變量 提供聲明 */
int main(void)
{
int dice, roll;
int sides;
int status;
srand((unsigned int) time(0)); /* 隨機種子 */
printf("Enter the number of sides per die, 0 to stop.\n");
while (scanf("%d", &sides) == 1 && sides > 0)
{
printf("How many dice?\n");
if ((status = scanf("%d", &dice)) != 1)
{
905
if (status == EOF)
break;       /* 退出循環 */
else
{
printf("You should have entered an integer.");
printf(" Let's begin again.\n");
while (getchar() != '\n')
continue;   /* 處理錯誤的輸入 */
printf("How many sides? Enter 0 to stop.\n");
continue;       /* 進入循環的下一輪迭代 */
}
}
roll = roll_n_dice(dice, sides);
printf("You have rolled a %d using %d %d-sided dice.\n",
roll, dice, sides);
printf("How many sides? Enter 0 to stop.\n");
}
printf("The rollem() function was called %d times.\n",
roll_count);     /* 使用外部變量 */
906
printf("GOOD FORTUNE TO YOU!\n");
return 0;
}

要與包含程序清單12.11的文件一起編譯該文件。可以把程序清單 12.11、12.12和12.13都放在同一文件夾或目錄中。運行該程序,下面是一個 輸出示例:

Enter the number of sides per die, 0 to stop.
6
How many dice?
2
You have rolled a 12 using 2 6-sided dice.
How many sides? Enter 0 to stop.
6
How many dice?
2
You have rolled a 4 using 2 6-sided dice.
How many sides? Enter 0 to stop.
6
How many dice?
2
907
You have rolled a 5 using 2 6-sided dice.
How many sides? Enter 0 to stop.
0
The rollem() function was called 6 times.
GOOD FORTUNE TO YOU!

因爲該程序使用了srand()隨機生成隨機數種子,所以大多數情況下,即 使輸入相同也很難得到相同的輸出。注意,manydice.c中的main()訪問了定 義在diceroll.c中的roll_count變量。
有3種情況可以導致外層while循環結束:side小於1、輸入類型不匹配 (此時scanf()返回0)、遇到文件結尾(返回值是EOF)。爲了讀取骰子的 點數,該程序處理文件結尾的方式(退出while循環)與處理類型不匹配 (進入循環的下一輪迭代)的情況不同。
可以通過多種方式使用roll_n_dice()。sides等於2時,程序模仿擲硬 幣,“正面朝上”爲2,“反面朝上”爲1(或者反過來表示也行)。很容易修改 該程序單獨顯示點數的結果,或者構建一個骰子模擬器。如果要擲多次骰子 (如在一些角色扮演類遊戲中),可以很容易地修改程序以輸出類似的結 果:

Enter the number of sets; enter q to stop.
18
How many sides and how many dice?
6 3
Here are 18 sets of 3 6-sided throws.
908
12 10 6 9 8 14 8 15 9 14 12 17 11 7 10
13 8 14
How many sets? Enter q to stop.
q

rand1()或 rand()(不是 rollem())還可以用來創建一個猜數字程序,讓 計算機選定一個數字,你來猜。讀者感興趣的話可以自己編寫這個程序。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章