1、第一部分第十一課:小游戲,猜單詞
2、第一部分第十二課預告:指針一出,誰與爭鋒
上一課《【C++探索之旅】第一部分第十課:文件讀寫,海闊憑魚躍》中我們學習了讀寫文件的知識。
第一部分的課程也快接近尾聲了,下一課我們會學習很重要的指針的知識。之後,我們就進入第二部分:面向對象編程了。哈哈哈,激動不?
目前為止,我們也學了不少內容:編譯基本原理,IDE,變量,函數,條件語句,循環語句,引用,數組,文件讀寫,等等。你應該為自己感到自豪。
俗語說得好:實踐是最好的老師。我們學了那麼多知識點,卻還沒有綜合地來應用一下,怎麼行呢?
這一課我們就用一個小游戲來鍛煉吧。
游戲規則很簡單:我們輸入一個單詞,然後把單詞裡的字母打亂,顯示給用戶看,讓用戶猜這個單詞是什麼。
准備工作和建議
這個小游戲看似簡單,但對於初學者的話還是有點難度的,因為需要用到我們前幾課的一些知識點,例如:
變量
string(字符串)
函數
控制流程,條件語句
數組
如果有知識點生疏了,可以在編寫這個游戲前回去復習。
[猜數字]游戲的主旨
我們要寫的小游戲的流程是這樣:
玩家1輸入一個單詞
程序將單詞打亂
玩家2嘗試從打亂的單詞猜出原始的單詞是什麼
下面是一個玩此游戲的一輪的例子:
輸入一個單詞 MYSTERY 原先的單詞是什麼?MSYRETY RYTEMSY 猜錯了! 原先的單詞是什麼?MSYRETY SERMYTY 猜錯了! 原先的單詞是什麼?MSYRETY MYSTERY 猜對了!好棒!
分析以上流程:
在這一輪中,玩家1選擇輸入MYSTERY(英語"神秘,秘密"的意思)作為要猜的單詞給玩家2來猜。
程序將MYSTERY這個單詞的字母順序打亂,然後請玩家2來猜打亂前的原始單詞是什麼。
玩家2嘗試猜測,他猜了3次才猜中:
RYTEMSY:顯示"猜錯了!"
SERMYTY:還是沒猜對
MYSTERY:終於猜對了,祝賀!
當然了,在上面演示的一輪游戲的例子中,玩家2可以看到玩家1輸入的原始單詞,暫時先這樣。到這課最後的改進部分我們會說怎麼避免。當然了,聰慧如你也許早就想到好幾個辦法了。
開始前的一些建議
當我們讓一個初學編程的朋友放手一搏,第一次自己寫一個比較完整的程序時,是有難度的。
小編也是過來人,以前還沒入門編程時感受頗深:看書,照著書上例子敲代碼,好像都懂啊。可是一上手自己寫比較復雜的代碼,就愣住了,不知該從何下手。
"從哪裡開始寫呢?","我該做什麼呢?","我該用什麼呢?",等等,都是初學的症候。不過誰都得過了這一階段,才能體驗到美好的入門感覺。
因此,絕對不要對自己灰心!編程麼,就是用一種新的思路來解決問題,只要你經過了編程思想的錘煉,你會發現其他很多事情都會顯得簡單了。
不過,善良如小編,還是不希望你自己過於迷失。畢竟只要稍加提示,你的思路就會更開闊啦。當然了,也許不必提示,你已經把程序寫出來了吧。
找到頭緒
這個程序的編寫其實只有三步:
輸入原始單詞;
打亂單詞的字母順序;
做循環:請求玩家2輸入猜測的單詞。只要玩家2沒猜到單詞,循環就繼續進行。
其實以上的三個步驟是相對獨立的,因此,與其一開始就很頭大地想要一下子把整個程序寫出來,為什麼不把程序分成三個部分一點點寫出來呢?
列出代碼的大綱
一開始,肯定是先寫main函數啦,畢竟是程序的入口嘛。我們可以先把程序寫成這樣:
intmain() { //1:請求用戶輸入單詞 //2:打亂輸入的單詞 //3:請求用戶猜單詞 return0; }
我們要做的,只是分別用代碼實現注釋裡的這三步而已。看吧,寫代碼之前先思考,然後做好規劃,對於寫出條理分明的代碼何等重要。
不過我建議大家先實現第1步,接著實現第3步,最後實現最難的第2步。
如何打亂單詞呢?
打亂單詞的這一步是最難的。需要學習一些基礎知識:
抽取隨機數
為了做到單詞的打亂是隨機的,我們需要隨機地抽取單詞中的字母,那怎麼達成這個目的呢?臣妾做不到啊。
不要緊,我們來學習隨機數的生成。
首先,須要引入兩個頭文件:ctime和cstdlib,因為我們要用到這兩個頭文件中的函數。
然後,我們需要調用函數srand(time(0))來初始化隨機數的"種子"(seed)。
然後,一旦"種子"准備好了,就可以用rand函數來生成隨機數了,例如:randomNb = rand() % 5; (5就限定了生成的隨機數是介於0到4之間)。
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或0),函數將只通過返回值返回現在的日歷時間。】
========================
如果我們在使用rand函數前沒有用srand函數制定seed的值,或者雖然用了srand函數,但是給它的參數是一個常量,比如srand(1),那麼每次程序運行rand產生的數字都是一樣的。只有用例如time()函數來給一個每次都不一樣的seed值,才能使得rand的返回值不一樣,才能做到“隨機”。
srand函數只需要在rand函數前面調用一次就夠了,也只能調用一次,之後你想要調用rand函數幾次都無所謂,但是每個程序中不能用兩次srand函數,切記。
下面演示一個程序,它會隨機生成一個整數,是介於0到4之間的:
#include#include //必須的頭文件 #include //必須的頭文件 usingnamespacestd; intmain() { intrandomNb(0); srand(time(0)); randomNb=rand()%5; return0; }
隨機抽取單詞中的字母
隨機生成一個數字是很不錯啊,但是我們的程序是要隨機挑選單詞裡的一個字母呢!
那還不是一樣的原理。
我們要隨機抽取單詞中的字母,只要用下標就可以了。還記得之前的課裡提到string很類似數組嗎?因此,只要 數組名[下標] 的形式,下標隨機就可以了。例如secretWord[2]就是單詞的第三個字母。
一旦我們知道如何生成隨機數了,那麼只要規定生成的隨機數的范圍就可以啦,范圍就是:0~單詞中字母數減1
例如:
#include#include #include #include usingnamespacestd; intmain() { stringsecretWord("MYSTERY"); srand(time(0)); intposition=rand()%secretWord.size(); cout<<"隨機抽取的字母是:"<
從字符串中刪除一個字符
那麼我們如何構建新的打亂的單詞呢?
我們可以用一個循環,從原始單詞中每次隨機抽取一個字符,然後添加到新的打亂的單詞中。
為了避免重復處理同一個字母,我們須要每次將抽取的字母從原始單詞中刪去。這樣,到最後,原始單詞的所有字母就會都"跑到"打亂的單詞中了。為了從字符串中刪除字符,我們可以使用erase這個方法:
secretWord.erase(4,1);//刪除原始單詞中的第五個字母
erase函數有兩個參數:
第一個參數表明從哪個字母開始刪除:上例中是4,就表明是從第五個元素開始刪除。畢竟數組是從0開始作為第一個下標的嘛。
第二個參數表明刪除幾個字符:上例中是1,因此只刪除從第五個字符開始的1個字符,也就是只刪除第五個字符。
創建函數
這一整個程序其實並沒有那麼復雜,不一定要創建新的函數。但是適時地使用函數絕對是好習慣。在我們的程序中,第二步的主要工作就可以交給一個自己創建的函數來做。
這樣,我們就可以在main函數中調用這個函數,如下:
mixedWord=mixLetters(secretWord);
我們將原始的單詞secretWord作為函數的參數傳遞給它,而函數的返回值則賦給打亂的單詞mixedWord。
好了,提示就到這裡了。
少年,放手一搏吧!
答案
相信你花了一些時間來思考,並且寫了程序。第一次你不一定都知道怎麼寫,但是沒關系,重點是思考和嘗試的過程。如果不思考直接來看答案,或嘗試了一下就放棄,那編程可是學不好的哦。
上面我們已經提過,此游戲的代碼可以分為三個部分來實現。第1步和第3步相對容易,只有第2步有些難度。
放上小編自己寫的版本。當然了,你可以寫得比我更好。我這個版本還是很基礎的。
翠花,上代碼:
=========================
#include#include #include #include usingnamespacestd; //用於打亂單詞的函數 stringmixLetters(stringword) { stringmixedWord; intposition(0); //只要還沒有抽完單詞裡的所有字母,就繼續while循環 while(word.size()!=0) ?{ ? //在單詞中隨機選擇一個字母 ? position=rand()%word.size(); ? ? //將此字母添加到新的單詞mixedWord中,也就是我們最後得到的打亂的單詞 ? mixedWord+=word[position]; ? ? //將此字母從字符串中刪去 ? //為了不重復處理同一個字母 ? word.erase(position,1); } //返回打亂後的單詞 returnmixedWord; } intmain() { stringsecretWord,mixedWord,userEnteredWord; //初始化偽隨機數的種子 srand(time(0)); //1:請求玩家1輸入原始單詞 cout<<"輸入一個單詞"< >secretWord; //2:用函數mixLetters將玩家1輸入的原始單詞打亂順序 mixedWord=mixLetters(secretWord); //3:請求玩家2猜單詞 do { cout< >userEnteredWord; if(userEnteredWord==secretWord) { cout<<"猜對了!好棒!"<
=========================
不要驚訝於此代碼的短小。也許只是你沒想到,看了代碼才發現,啊,原來這麼簡單,原來可以這麼寫。
下面對程序做分步解釋吧:
第一步:輸入單詞
這一步是最簡單的。cout來輸出一行提示消息。cin來獲取用戶輸入,存放到secretWord變量中。
第二步:打亂單詞
這一步是用一個函數mixLetters來實現的。
while(word.size()!=0) { position=rand()%word.size(); mixedWord+=word[position]; word.erase(position,1); }
每一次進入while循環體中,我們都隨機挑選單詞中的一個字母,然後將其放入新的打亂的單詞mixedWord中。最後將這個字母從原始的單詞中刪去。
一旦處理完原始單詞中所有的字母(也就是原始的單詞為空了),我們就跳出循環,函數會返回打亂後的單詞。
第三步:問用戶秘密單詞是什麼
這一步我們將使用一個do... while循環,以保證我們給用戶至少一次猜單詞的機會。
用cin來獲取用戶的輸入,然後與secretWord這個原始的單詞做比較,一旦兩者一致,則獲勝;否則就繼續循環。
}while(userEnteredWord!=secretWord);//只要沒猜對,就再進入while循環
每次都顯示一條消息:假如沒猜對,則顯示"猜錯了";假如猜到了,則顯示"猜對了!好棒!"
改進游戲
好了,我們的程序寫完了,不過始終可以改進。我們給出一些改進的建議:
在用戶1輸入單詞之後,想辦法"隱藏"單詞,不讓用戶2看到:可以使用加多個空行(endl);也可以有其他辦法。
使用戶可以一直玩:目前我們的游戲一輪就結束了。你可以在每一輪結束後顯示一行信息:"您還想繼續新一輪游戲嗎?(y/n)"。如果用戶輸入y,回車,則繼續新的一輪;如果用戶輸入n,回車,則退出游戲。
設置猜測次數上限:比如設置5次作為猜測的機會上限,那麼如果用戶5次沒猜中單詞的話,游戲也失敗。
計算玩家的平均分:每一輪的游戲所獲分數,可以設置比如1次就猜中的得10分,2次才猜中的9分,依次類推。然後把玩家所得總分除以所玩輪數,就是平均分啦
用一個文件儲存不少單詞,好像字典一樣,然後從中隨機選擇 : 這樣你就可以一個人玩了,不需要別人給你出題了。你的程序會從文件中隨機選中一個單詞,將其打亂,然後你自己來猜。下面是一個字典的例子:
FEATURE
MOTHER
ANSWER
CLOSE
GARAGE
DELETE
ENGINEER
ZERO
YESTERDAY
TOGETHER
JUICE
LAUGHOFTEN
PEOPLEKNOWLEDGE
當然,你可以自己填充英語單詞,或者去網上下載那種英文字典的txt文件,裡面有幾萬,幾十萬,甚至幾百萬個單詞,從中選擇的話就有猜單詞的樂趣和難度啦。
要讀取文件中的單詞,你可以用我們上一課學到的知識點:讀寫文件。
如果你自己還想到其他改進的點子,也可以去實現。你會發現通過這個游戲,自己進步了很多。
歡迎把改進後的游戲的代碼發給小編噢~
第一部分第十二課預告
今天的課就到這裡,一起加油吧!
下一課我們學習:指針一出,誰與爭鋒