内容簡介
- 前言
- 解方(1. 遊戲的代碼)
- 解方(2. 詞庫的代碼)
- 第二部分第十一課預告
1. 前言
經過上一課 C語言探索之旅 | 第二部分第九課:實戰"懸挂小人"遊戲 之後,相信大家都或多或少都寫了自己的“懸挂小人”的遊戲代碼吧。
這一課我們就來"終結"這個遊戲吧 (聽着怎麼有點吓人…)。
"Yes, you are terminated."
2. 解方(1. 遊戲的代碼)
如果你開始閱讀這裡,說明:
- 或者你寫完了遊戲,想來看看我們怎麼寫。
- 或者你沒完成這個遊戲,想來看看怎麼寫。
不管你是哪種情況,我都會介紹一下如何來完成這個遊戲。
“說不說在我,聽不聽在您”~
事實上,我自己花了比想象中更多的時間來完成這遊戲。
人生總是這樣的,“理想豐滿,現實骨感;看似美滿,人艱不拆”。
但是,我還是堅信大家是有能力獨自完成這個小遊戲的(如果你認真學習了之前的 C語言課程),可以去查閱網上資料,花點時間(幾十分鐘,幾小時,幾天?),這并不是一次競賽,是以不用着急。
我更希望您花了不少時間,最終實作了這個遊戲;比之您隻花 5 分鐘,然後就來看答案要好很多。
千萬不要覺得我是一蹴而就寫成這個遊戲的,這個遊戲雖小,但也還沒簡單到可以在腦中構思好一切,然後“下筆如有神”:我也是一步步寫出來的。
我們将會分 2 步來介紹我們的解方:
- 首先我們會示範如何一步步寫遊戲的主體部分,一開始我們會隻有一個猜測的單詞,而且是固定的;我選了 BOTTLE(表示“瓶子”),因為我們要測試對于單詞中有大于等于兩個相同字母的情況是否處理正确了(BOTTLE 中有 2 個 T)。
- 然後我們會示範如何加入詞庫的處理程式,以便每一輪遊戲可以從詞庫中随機抽取一個單詞。
牢記:重要的不是結果,而是我們思考的方式和過程。
分析 main 函數
大家都知道,我們的 C語言程式都是由 main 函數作為入口的。
我們也不要忘了引入一些标準庫的頭檔案:stdio.h,stdlib.h,ctype.h(為了 toupper 函數)。
是以,我們的程式一開始會是這樣的:
是不是很簡單啊,慢慢來麼。
我們的 main 函數将控制遊戲的大部分運作,并且調用我們将要寫的不少函數。
我們來聲明一些必要的變量吧。這些變量也不是一次就能全部想到的,都是寫一點,想到一些。“羅馬不是一日建成的, 小人也不是一日能懸挂完的”。
上述的變量中,起到關鍵作用的就是 letterFound 這個 int 型數組了。這個數組用于表示猜測的單詞中哪些字母已經猜到,哪些還沒猜到。
一開始,我們實作得簡單些:我們的單詞 BOTTLE 有 6 個字母,是以我們的數組就固定是 6 個元素的數組。
如果元素為 0,表示對應的那個字母還沒猜到;如果為 1,則表示已猜到。随着遊戲的進行,這個數組的元素值會被修改。
例如,如果當下我們玩遊戲直到:
那麼,letterFound 這個數組的值應該是這樣:
之後我們要測試遊戲的一輪是否已經勝利也就比較簡單了:隻需要測試 letterFound 數組的所有元素是否都等于 1。
我們就來寫判斷一輪是否勝利的函數吧,取名為 win(表示 “勝利”)好了。
可以看到,我們的 win 函數的參數是一個 int 型數組,我們在 main 函數中調用 win 函數時,會将我們的 letterFound 數組傳給它。
這個函數很簡單:周遊數組,隻要還有一個元素為 0,那遊戲還沒勝利;如果所有元素都為 1,則遊戲勝利。
為了與此函數搭配,我們還需要寫一個函數,起名叫 researchLetter,這個函數将有兩個功能:
- 傳回一個布爾值(在 C語言裡用 int 型表示),用于表示所猜的字母是否存在于單詞中。
- 更新 letterFound 數組的元素,如果所猜的字母在單詞中,那麼就把對應的元素值修改為 1。
researchLetter 這個函數的好處還在于:不會在找到第一個存在的字母後就停止,而會繼續查找,是以對于像 BOTTLE 這樣有兩個字母相同的單詞就可以一次揭示兩個 T 了。
好,寫完這兩個函數(放在 main 函數後面),我們繼續寫我們的 main 函數。我們添加一句歡迎詞:
然後添加一個主循環,是一個 while 循環:
每輪遊戲在 leftTimes(剩餘猜測機會)大于 0 并且還沒勝利的情況下,是不會停止的。
- 如果剩餘次數為 0,則本輪遊戲失敗。
- 如果勝利,那本輪就赢了。
在這兩種情況下,都要停止遊戲。
我們在 while 循環裡添加如下代碼:
上面的代碼用于:
- 列印剩餘機會數。
- 列印單詞(其中還沒猜到的字母用星号
表示)。*
接下來,我們寫請求使用者輸入一個字母的代碼:
還記得我們之前寫的函數 readCharacter 嗎?它用于讀取使用者的第一個輸入的字母,讀到回車符結束,而且它會把該字母轉成大寫。
以上代碼調用 researchLetter 函數在單詞中查找使用者輸入的字母,如果沒找到,則剩餘猜測機會數扣除一次。
如果字母存在于單詞中,則 researchLetter 函數還會更新 letterFound 數組(每個元素對應了神秘單詞的每一個字母的猜測情況),将其中對應的 0(還沒猜到)改為 1(已經猜到)。
這樣,win 函數在判斷的時候,如果 letterFound 數組的每一個元素都為 1,則傳回 1,表示本輪勝利,猜到單詞的全部字母了。
暫時,while 循環體的内容就到這裡了,然後我們還要寫跳出 while 循環之後的代碼(或者勝利或者失敗):
遊戲主體部分的代碼就到這裡了,給出我們到目前為止的完整程式:
這一部分的程式,你可以将其存放在一個
.c
檔案中,例如叫 hangman.c。
然後用 gcc 編譯(如果是在 IDE 裡面,例如 CodeBlocks,那直接點選編譯運作):
運作:
接下來我們要開始第二部分:詞庫的代碼。
根據這部分的代碼,我們還會接着修改和添加 main 函數的内容。
好吧,稍作休息,繼續前進!
3. 解方(2. 詞庫的代碼)
我們已經編寫了遊戲主體部分的基本代碼,但是我們的遊戲目前還不能做到每輪随機抽取一個單詞。
是以,接下來我們就帶大家編寫處理詞庫的代碼。
首先,我們需要建立一個檔案,用于存放所有的單詞。
在 Linux / Unix / macOS 作業系統下,我們都可以直接建立一個不帶字尾名的檔案。在 Windows 下可以建立 .txt 結尾的文本檔案。
我寫這個遊戲是在 Linux 系統下,是以直接用 Vim 或 Emacs 或其他編輯器建立一個檔案, 位于我們源檔案的相同目錄下:dictionary。
在裡面寫入以下單詞(每行一個,用回車符隔開):
當然了,我這裡隻是舉個例子,你可以建立屬于自己的詞庫。
建立兩個檔案
處理這個檔案的代碼将會不少(至少,我是這麼預感的),是以,我們建立一個 .c 源檔案,可以命名為 dictionary.c。
順便,我們也建立 dictionary.h 這個頭檔案,其中存放 dictionary.c 中的函數的原型,這樣我們在 main 函數裡就可以通過
來引入這些函數的定義了。
在 dictionary.c 中,首先我們引入一些頭檔案:
chooseWord 函數
這個函數用于從檔案 dictionary 中随機選取一個單詞,此函數隻有一個參數:指向記憶體中可以寫入單詞的位址的指針,這個指針實參将由 main 函數提供。
函數傳回值是 int 變量:1 表示一切順利;0 表示出現錯誤。
此函數的開頭是這樣:
聲明了一些變量,我們接着寫:
這段代碼不難吧,就是嘗試打開詞庫(dictionary 檔案),并檢測 dictionary 檔案指針是否為 NULL。
如果為 NULL,表示打開失敗。如果打開檔案失敗,則程式中止,因為沒有進行下去的必要了。
上面這段代碼中,我們借助 fgetc 函數周遊整個檔案(一個字元一個字元讀取)。
我們統計讀到的回車符(
\n
)的數目,每讀到一個
\n
,我們對 wordNum(單詞總數)的值加 1。
我們通過以上代碼,就可以知道詞庫中的單詞總數了,就是 wordNum 的值。
然後,我們需要一個函數,根據 wordNum 的值計算一個僞随機數出來,作為随機選取的單詞編号,我們就來寫一個函數,命名為:randomNum。
randomNum 函數
此函數裡的代碼我們之前編寫第一個 C語言小遊戲:“或多或少” 時已經用過了,就是簡單的僞随機數生成。
作用:用于傳回一個介于 0 ~ (單詞總數 - 1) 之間的随機數。
寫好了 randomNum 函數,我們立即來使用它:
接着,我們需要重新回到檔案開始處來進行讀取,為了回到檔案開始處,可以調用函數 rewind。
dictionary.h 檔案
其中包含我們的 dictionary.c 中的函數原型,内容如下:
完整的 dictionary.c 檔案
修改 hangman.c 檔案
現在,既然我們的處理詞庫的函數已經寫完了,也就是在 dictionary.c 中,那麼我們需要相應地修改我們的 hangman.c 檔案中的 main 函數和其他幾個子函數:
有了之前所有課程的知識,靠着注釋,應該不難看懂。
完整的 hangman.c 檔案
好了,這個小遊戲已經寫完了,用 gcc 編譯并運作看看吧!
然後:
4. 第二部分第十一課預告
今天的課就到這裡,一起加油吧!
下一課:C語言探索之旅 | 第二部分第十一課:練習題和習作
作者:謝恩銘
出處:公衆号「程式員聯盟」
原文連結:https://www.jianshu.com/p/b239b1774f4b
轉載請注明出處,謝謝合作!轉載授權請加我微信 frogoscar
喜歡本文的朋友,歡迎關注公衆号 程式員聯盟,收看更多精彩内容
點個[在看],是對我最大的支援!