我們将要模拟一個非常流行的遊戲——擲骰子。骰子的形式多種多樣,最普遍的是使用兩個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 /* for rand() */ int rollem(int sides) { int roll; roll = rand() % sides 1; return roll; }
我們還想用一個函數提示用戶選擇任意面數的骰子,并返回點數總和。如以下程序diceroll.c所示。
/* diceroll.c -- dice role simulation */ /* compile with mandydice.c */ #include diceroll.h #include stdio.h #include stdlib.h /* for library rand() */ int roll_count = 0; /* external linkage */ static int rollem(int sides) /* private to this file */ { int roll; roll = rand() % sides 1; roll_count; /* count function calls */ 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 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是頭文件中的内容。
//diceroll.h extern int roll_count; int roll_n_dice(int dice, int sides);
該頭文件中包含一個函數原型和一個extern聲明。由于diceroll.c文件包含了該文件,direroll.c實際上包含了roll_count的兩個聲明:
extern int roll_count; // from header file int roll_count = 0; // from source code file
這樣做沒問題。一個變量隻能有一個定義式聲明,但是帶extern的聲明是引用式聲明,可以有多個引用式聲明。
使用roll_n_dice()函數的程序都要包含diceroll.h頭文件。包含該頭文件後,程序便可使用roll_n_dice()函數和roll_count變量。如程序manydice.c所示。
/* manydice.c -- multiple dice rolls */ /* compile with diceroll.c */ #include stdio.h #include stdlib.h /* for library srand() */ #include time.h /* for time() */ #include diceroll.h /* for roll_n_dice() */ /* and for roll_count */ int main(void) { int dice,roll; int sides; srand((unsigned int) time(0)); /* randomize seed */ printf(Enter the number of sides per die, 0 to stop.n while (scanf(, &sides) == 1 & 0 ) { printf(How many dice?n if ((status =scanf(, &dice)) != 1) { if (status == EOF) break; /* exit loop */ else { printf(You should have entered an integer. printf( Lets begin again.n while (getchar() != ) continue; /* dispose of bad input */ printf(How many sides? Enter 0 to stop.n continue; /* new loop cycle */ } } 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); /* use extern variable */ printf(GOOD FORTUNE TO YOU!n return 0; }
要與包含程序diceroll.c的文件一起編譯該文件。可以把程序diceroll.c、diceroll.h和manydice.c都放在同一文件夾或目錄中。運行該程序,下面是一個輸出示例:
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
You have rolled a 5 using 2 6-sided dice.
How many sides? Enter 0 to stop.
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. 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())還可以用來創建一個猜數字程序,讓計算機選定一個數字,你來猜。讀者感興趣的話可以自己編寫這個程序。
,
更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!