C語言探索之旅 | 第一部分第十課:第一個C語言小游戲

作者 謝恩銘,公眾號「程序員聯盟」(微信號:coderhub)。
轉載請注明出處。
原文:http://www.lxweimin.com/p/7d03f054c2d1

《C語言探索之旅》全系列

內容簡介


  1. 前言
  2. 準備工作和建議
  3. 我的代碼
  4. 改進方案
  5. 第一部分第十一課預告

1. 前言


上一課是 C語言探索之旅 | 第一部分第九課:循環語句

經過前面這么多課的努力,我們終于迎來了第一個比較正式的程序:一個 C語言小游戲。

雖然這個游戲沒有圖形界面,是命令行的形式,但是不論怎樣,這都是一個小小的里程碑。

我們的目的是讓大家看到經過之前幾課的學習,你已經可以完成一些有意思的事了。

雖然我們知道理論是很好的,但是如果我們不能把所學的理論付諸實踐,那也很沒有意思。

信不信由你,你其實已經有水平實現自己的第一個有意思的程序了。

2. 準備工作和建議


程序的原理

在動手編程之前,得先跟大家說一下這個程序是干什么的。

我們可以稱呼這個游戲為《或多或少》。

游戲的原理是這樣:

  1. 每一輪電腦從 1 到 100 中隨機抽一個整數。

  2. 電腦請求你猜這個數字,因此你要輸入一個 1 到 100 之間的整數。

  3. 電腦將你輸入的數和它抽取的數進行比較,并告知你的數比它的數大了還是小了。

  4. 然后它會再次讓你輸入數字,并告訴你比較的結果。

  5. 一直到你猜到這個數為止,一輪結束。

游戲的目的,當然就是用最少的次數猜到這個“神秘”數字。雖然沒有絢麗的圖形界面,但是或多或少,這都是你的第一個游戲了,應該值得驕傲。

下面演示了一輪的樣式,你要編程來實現它:

這個數字是什么?50
猜小了!
這個數字是什么?75
猜小了!
這個數字是什么?85
猜大了!
這個數字是什么?80
猜大了!
這個數字是什么?78
猜小了!
這個數字是什么?79
太棒了,你猜到了這個神秘數字!!

隨機抽取一個數


但大家要問了:“如何隨機地抽取一個數呢?不知道怎么辦啊,臣妾做不到啊。”

誠然,我們還沒學習如何來產生一個隨機數。讓親愛的電腦兄來做這個是不簡單的:它很會做運算,但是要它隨機選擇一個數,它還不知道怎么做呢。

事實上,為了“嘗試”得到一個隨機數,我們不得不讓電腦來做一些復雜的運算。好吧,歸根結底還是做運算。

我們有兩個解決方案:

  • 請用戶通過 scanf 函數輸入這個神秘數字,那么就需要兩個玩家咯。一個選數字,一個猜數字。

  • 孤注一擲地讓電腦來為我們自動產生一個隨機數。好處是:只需要一個玩家,可以自娛自樂。缺點是:需要學習該怎么做...

我們來學習用第二種方案編寫這個游戲,當然你也可以之后自己編寫第一種方案的代碼。

為了生成一個隨機數,我們要用到 rand() 函數(rand 是英語 random 的縮寫,表示“隨機的”)。

顧名思義,這個函數能為我們生成隨機數。但是我們還想要這個隨機數是在 1 到 100 的整數范圍內(如果沒有限定范圍,那會很復雜)。

我們會用到以下的形式:

srand(time(NULL));
mysteryNumber = (rand() % (MAX - MIN + 1)) + MIN;

第一行(srand 函數)用于初始化隨機數的生成器。srand 其實是 seed random 的縮寫。seed 在英語中是“種子”的意思。

給出 百度百科 的簡單解釋:

srand 和 rand 配合使用產生偽隨機數序列。rand 函數在產生隨機數前,需要系統提供的生成偽隨機數序列的種子,rand 根據這個種子的值產生一系列隨機數。如果系統提供的種子沒有變化,每次調用 rand 函數生成的偽隨機數序列都是一樣的。srand(unsigned seed) 通過參數 seed 改變系統提供的種子值,從而可以使得每次調用 rand 函數生成的偽隨機數序列不同,從而實現真正意義上的“隨機”。通常可以利用系統時間來改變系統的種子值,即 srand(time(NULL)),可以為 rand 函數提供不同的種子值,進而產生不同的隨機數序列。


所謂的“偽隨機數”指的并不是假的隨機數,這里的“偽”是有規律的意思。其實絕對的隨機數只是一種理想狀態的隨機數,計算機只能生成相對的隨機數即偽隨機數。計算機生成的偽隨機數既是隨機的又是有規律的 -- 一部份遵守一定的規律,一部份則不遵守任何規律。比如“世上沒有兩片形狀完全相同的樹葉”,這正點到了事物的特性 -- 規律性;但是每種樹的葉子都有近似的形狀,這正是事物的共性 -- 規律性。從這個角度講,我們就可以接受這樣的事實了:計算機只能產生偽隨機數而不是絕對的隨機數。


通過 time() 函數來獲得計算機系統當前的日歷時間(Calendar Time),處理日期時間的函數都是以本函數的返回值為基礎進行運算。其原型為:time_t time(time_t * t); 如果你已經聲明了參數t,你可以從參數t返回現在的日歷時間,同時也可以通過返回值返回現在的日歷時間,即從一個時間點(例如:1970 年 1 月 1 日 0 時 0 分 0 秒)到現在此時的秒數。如果參數為空(NULL),函數將只通過返回值返回現在的日歷時間。

如果我們在使用 rand 函數前沒有用 srand 函數制定 seed 的值,或者雖然用了 srand 函數,但是給它的參數是一個常量,比如 srand(1),那么每次程序運行 rand 產生的數字都是一樣的。只有用例如 time() 函數來給一個每次都不一樣的 seed 值,才能使得 rand 的返回值不一樣,才能做到“隨機”。

srand 函數只需要在 rand 函數前面調用一次就夠了,也只能調用一次,之后你想要調用 rand 函數幾次都無所謂,但是每個程序中不能用兩次 srand 函數,切記。

上面代碼格式中的 MAX 和 MIN 是常量或 const 類型的變量。MAX 是 Maximum 的縮寫,表示“最大”。MIN 是 Minimum 的縮寫,表示“最小”。顧名思義,MAX 和 MIN 分別是你規定的范圍的最大值和最小值。

建議在程序的一開始定義這兩個 const 類型的變量:

const int MAX = 100, MIN = 1;

引入的庫


為了程序能夠順利運行,我們需要引入三個庫:

  • stdio.h
  • stdlib.h
  • time.h

我們以前的課說過庫的作用。庫里面提供一些定義好的函數,比如 time.h里面就有我們的 time() 函數,stdlib 中有 rand 和 srand 函數。

好啦,我不繼續透露了。我們已經說明了游戲的原理,給出了一輪游戲的運行例子,也給出了主要的隨機數生成代碼,該輪到你來完成游戲的代碼了。加油,相信你可以的!

4. 我的代碼


希望大家自己先寫代碼,查閱一些資料,或復習前面幾課的內容。運行成功了或實在寫不出來才來看答案。

以下給出我的版本。當然了,這個游戲的代碼可以有不同的版本。你完全可以自己發揮。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main (int argc, char** argv)
{
    int mysteryNumber = 0, guessNumber = 0;
    const int MAX = 100, MIN = 1;
    // 生成隨機數
    srand(time(NULL));
    mysteryNumber = (rand() % (MAX - MIN + 1)) + MIN;
    /* 程序的循環部分, 如果用戶沒猜中數字,就一直進行循環 */
    do
    {
        // 請求用戶輸入所猜數字
        printf("這個數字是什么 ? ");
        scanf("%d", &guessNumber);
        // 比較用戶輸入的數字和神秘數字
        if (mysteryNumber > guessNumber)
            printf("猜小了 !\n\n");
        else if (mysteryNumber < guessNumber)
            printf("猜大了 !\n\n");
        else
            printf ("太棒了,你猜到了這個神秘數字 !!\n\n");
    } while (guessNumber != mysteryNumber);

    return 0;
}

程序的解釋(從上到下的順序)

  1. 預處理指令:就是開頭的那三行,以 # 開始。include 是英語“包含,引入”的意思,所以表示引入什么庫。

  2. 變量:這個游戲中,不需要太多變量,只有一個用于記錄用戶輸入的數字的變量 guessNumber,和一個電腦隨機抽取的數字 mysteryNumber。guess 表示“猜”,mystery 表示“神秘”,number 表示“數字”。我們也定義了兩個常量(const 變量,其實叫只讀變量比較準確)MAX 和 MIN,值分別是 100 和 1。這樣定義的好處是,如果你后面要改這兩個數值,會很方便,直接改這一行的兩個值就好了。如果沒有用 MAX 和 MIN 而是在程序里每一個地方寫 100 和 1 的話,那如果以后要改數值,工作量就大了。

  3. 隨機數:srand 和 rand 那兩行,用于生成在 1 和 100 之間的一個隨機數,值賦給 mysteryNumber。

  4. 循環:我選擇用 do...while 循環。理論上一個 while 循環也可以做到,但我覺得這里用 do...while 可能更合邏輯。為什么呢?還記得 do...while 循環的特點嗎?就是循環體里的指令至少會執行一次,不像 while 循環可能一次也不執行。這里我們至少要讓用戶輸入一次數字,不可能用戶一次也不輸入就猜到了數字。

  5. 在每一次進入循環體里運行時,我們都請求用戶輸入一個數字,并且把這個數字的值賦給 guessNumber 變量,接下來就比較 guessNumber 和 mysteryNumber(需要猜的數字)的大小:

    • mysteryNumber 大于 guessNumber,那么輸出“猜小了”,繼續循環;
    • mysteryNumber 小于 guessNumber,那么輸出“猜大了”,繼續循環;
    • mysteryNumber 等于 guessNumber,也就是 else 語句的情況,就說明我們猜對了,輸出“太棒了,你猜到了這個神秘數字!”,結束循環。
  6. 循環也需要一個條件,我們給出的條件是:只要猜的數字和神秘數字不一樣,循環就繼續。

4. 改進方案

現在這個游戲還是很基礎很簡單的,但是可以有以下的改進方案:

  1. 增加一個記錄步數的計數器,在你猜對的時候輸出:“太棒了,你用**步猜到了這個神秘數字!”

  2. 目前的程序只進行一輪就結束了,如果玩家不過癮,還想繼續下一輪怎么辦呢?可以加入一個問題:“你還想繼續玩嗎?”,等待用戶輸入數字來回答。定義一個布爾值 continue(continue 表示“繼續”)來存儲用戶輸入的回答,比如 continue 的默認值是 1,就是用戶默認是繼續玩下一輪的;但如果用戶輸入 0,那么程序停止,游戲結束。

  3. 增加一個模式:雙人模式。可以你出題我來猜。但是我希望你能夠在程序一開始就讓用戶選擇是玩哪一種模式,是經典的人機對戰,還是人人對戰。如果是雙人模式的人人對戰,那么就不是用 srand 和 rand 來產生神秘數字了,而是讓玩家一通過 scanf 來輸入這個數字。

  4. 設置幾個難度級別,讓玩家選擇:初級(1-100 中的一個數),中級(1-1000 中的一個數),高級(1-10000 中的一個數)。如果你這樣設計,就需要改寫 MAX 值了,而此時 MAX 就不能再是一個 const 變量了,必須要把 MAX 前面的 const 去掉,MIN 的還能保留。

你也可以自己增設難度,想出更多好玩的點子來豐富這個游戲。通過完善和改進這個小游戲,你會學到更多。

5. 第一部分第十一課預告


今天的課就到這里,一起加油吧!

下一課:C語言探索之旅 | 第一部分第十一課:函數

下一課我們來學習函數這個極為重要和有用的內容吧!


我是 謝恩銘,公眾號「程序員聯盟」(微信號:coderhub)運營者,慕課網精英講師 Oscar 老師,終生學習者。
熱愛生活,喜歡游泳,略懂烹飪。
人生格言:「向著標桿直跑」

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容