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

做者 謝恩銘,公衆號「程序員聯盟」。 轉載請註明出處。 原文:www.jianshu.com/p/7d03f054c…git

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

內容簡介


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

1. 前言


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

通過前面這麼多課的努力,咱們終於迎來了第一個比較正式的程序:一個 C語言小遊戲。編程

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

咱們的目的是讓你們看到通過以前幾課的學習,你已經能夠完成一些有意思的事了。dom

雖然咱們知道理論是很好的,可是若是咱們不能把所學的理論付諸實踐,那也很沒有意思。函數

信不信由你,你其實已經有水平實現本身的第一個有意思的程序了。學習

2. 準備工做和建議


程序的原理

在動手編程以前,得先跟你們說一下這個程序是幹什麼的。ui

咱們能夠稱呼這個遊戲爲《或多或少》。spa

遊戲的原理是這樣:

  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語言探索之旅 | 第一部分第九課:函數

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


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

相關文章
相關標籤/搜索