既然討論的是文件,為什麼要在這一章建立一個遊戲呢?嗯,Hangman 遊戲之所以有趣,原因在於它有一個龐大的詞彙表,可以從這個詞彙表中選擇題目。要做到這一點,最容易的辦法就是從文件中讀取。我們仍然使用 PythonCard 來完成這個遊戲,也想由此說明並非只能使用 Pygame 建立圖形化遊戲。
我不打算像介紹其他程序那樣詳細地解釋這個程序。現在你應該已經會看代碼了,相信你能自己搞清楚其中大部分代碼的作用。我只會稍稍給你一點指導,幫助你順利讀懂代碼。
Hangman GUI
我們的 Hangman 程序的主 GUI 是像下圖這樣的:
這裡顯示了上吊小人的各個部分,不過程序運行時,我們首先會隱藏小人的所有部分。玩家猜錯一個字母時,就會顯示這個小人的下一部分。如果整個小人都顯示出來,玩家可以再猜一次,然後遊戲結束!
玩家猜一個字母時,程序會查看這個字母是否在秘密詞中。如果確實是秘密詞中的字母,就把這個字母顯示出來。在窗口下方,玩家可以看見到目前為止他猜過的所有字母。玩家什麼時候都可以嘗試猜詞。
下面概括一下這個程序的工作原理。
開始時,程序完成以下工作:
從文件加載詞彙表;
從每行的末尾去除換行符;
讓小人的所有部分都不可見;
從詞彙表隨機選擇一個詞;
根據秘密詞中的字母個數顯示相同數目的橫線。
玩家點擊 Guess! 按鈕時,程序做以下工作。
檢查猜的是一個字母還是一個詞。
如果是一個字母:
檢查秘密詞,查看是否包含這個字母。
如果玩家猜對了,用這個字母取代橫線,顯示這個字母出現在什麼位置。
如果玩家猜錯了,顯示小人的另一部分。
把猜出的字母增加到 Previous guesses 顯示區。
查看玩家是否已經猜出單詞(猜出所有字母)。
如果是一個詞:
檢查玩家猜得正確與否。
如果正確,顯示一個對話框,指出 You Won!(你贏了!)並開始新遊戲。
查看玩家是不是已經沒有機會了,如果是,顯示一個對話框,指出 You Lost(你失敗了),並顯示這個秘密詞到底是什麼。
從詞彙表得到單詞
這一章討論的是文件,所以下面來看程序中得到詞彙表的部分。相應代碼如下:
words.txt 文件只是一個文本文件,所以可以使用 readlines
讀這個文件。為了從詞彙表中選擇一個詞,我們使用了 random.choice
函數,如下:
self.currentword = random.choice(self.lines)
顯示小人
要跟蹤已經顯示了小人的哪些部分,另外下一步要顯示哪一部分,這有很多方法。我們決定使用一個循環,代碼如下:
def wrong(self): self.pieces_shown += 1 for i in range(self.pieces_shown): self.pieces[i].setHidden(False) if self.pieces_shown == len(self.pieces): message = \"You lose. The word was \" + self.currentword QtGui.QMessageBox.warning(self,\"Hangman\",message) self.new_game
我們使用 self.pieces_shown
來跟蹤上吊小人顯示了多少部分。如果所有的部分都顯示出來了,我們就使用一個對話框來告知玩家他輸了。
檢查猜到的字母
這個程序最難的一部分就是檢查玩家猜到的字母,看它是否出現在秘密詞中。這個工作之所以困難,是因為字母可能在一個單詞中出現多次。例如,如果秘密詞是 lever,玩家猜到了 e,就必須把第 2 個和第 4 個字母都顯示出來,因為它們都是 e。
我們有幾個函數來完成這項工作。find_letters
函數會查找某個字母在單詞中出現的所有位置,並返回一個包含這些位置的列表。例如,對於字母 e 和單詞 lever,這個函數會返回 [1, 3],因為字母 e 出現在這個字符串的索引 1 和索引 3 的位置上。(記住,索引從 0 開始。)代碼如下:
replace_letters
函數從 find_letters
得到列表,用正確的字母替換這些位置上的橫線。在我們的例子中(lever 中的字母 e),它會用 -e-e-
替換 -----
。這就向玩家顯示出猜對的字母出現在單詞的什麼位置,其餘仍然為橫線。代碼如下:
def replace_letters(string, locations, letter): new_string = \'\' for i in range (0, len(string)):if i in locations: new_string = new_string + letterelse: new_string = new_string + string[i] return new_string
玩家猜一個字母時,我們使用剛才定義的兩個函數 find_letters
和 replace_letters
:
整個程序大約 95 行代碼,另外我還加入了一些空行,讓代碼看起來更美觀。代碼清單 22-8 給出了整個程序,這裡加了對各個不同部分做的一些解釋。如果使用本書的安裝程序,你的計算機上的 ExamplesHangman 文件夾中應該已經有這個代碼了,另外也可以在網站上找到這個代碼,包括 hangman.py、hangman.ui 和 words.txt。切記,正如我們在第 20 章提到的,如果你使用的是 Mac,則需要在 Qt Designer 中打開 hangman.ui,並勾選掉 menubar
對象的 nativeMenuBar
屬性。
代碼清單 22-8 完整的 hangman.py 程序
為了簡化起見,我們的 Hangman 程序只使用了小寫字母。我們提供的單詞表只有小寫字母,用戶也必須將其猜測的內容以小寫形式輸入。
新遊戲開始時,處的 dashes
函數使用橫槓替換了字母。但它並沒有替換標點符號,比如撇號。所以如果單詞是 doesn\'t,玩家將會看到 -----\'-
。
建議你自己創建這個程序。可以用 Qt Designer 構建 GUI,即使看上去與這裡的版本不完全一樣也沒有關係。不過一定要仔細查看代碼,看看組件分別使用什麼名字。代碼中的名字必須與 .ui 文件中的名字一致。
盡可能自己鍵入代碼。運行程序,看看結果怎麼樣。如果你想做些不同的嘗試,那就放手去做!充分嘗試,大膽試驗,並享受其中的快樂。這正是編程最有意思也最有收穫的地方,大多數知識都是通過這個途徑獲得的。
你學到了什麼
在這一章,你學到了以下內容。
什麼是文件。
如何打開和關閉文件。
打開文件的不同方式:讀、寫和追加。
寫文件的不同方法:
write
或print >>
。如何使用
pickle
在文件中保存列表和對像(以及其他 Python 數據結構)。很多與文件夾(也稱為目錄)、文件位置和路徑相關的內容。
我們還建立了一個 Hangman 遊戲,這個遊戲使用來自文件的數據得到一個詞彙表。
測試題
1. Python 中用來處理文件的對象稱為 ________。
2. 如何創建一個文件對像?
3. 文件對像和文件名之間有什麼區別?
4. 完成文件讀寫時應該對文件做什麼操作?
5. 如果用追加模式打開一個文件然後在文件中寫入內容會怎樣?
6. 如果用寫模式打開一個文件然後在文件中寫入內容會怎樣?
7. 讀過文件的一部分之後如何從文件起始位置開始讀?
8. 將一個 Python 對像保存到文件中要使用哪個 pickle
函數?
9. 要「還原」一個對像 ( 從 pickle
文件得到對像 , 並放回到 Python 變量中 ),應當使用哪個 pickle
方法?
動手試一試
1. 建立程序造一些滑稽句子。每個句子應該至少有 4 部分,類似於:
The ____________ _______________ ______________ _________________
(形容詞) ( 名詞 ) ( 動詞短語 )( 副詞短語 )
例如:
這個程序會隨機選擇一個形容詞、一個名詞、一個動詞短語和一個副詞短語來創建句子。這些單詞都存儲在文件中,可以使用「記事本」創建這些單詞。要完成這個程序,最簡單的方法是為這 4 組單詞分別創建一個文件,不過也可以採用你希望的任何方式創建文件。下面給出一些點子來啟發一下你,不過,我相信你也能提出自己的想法。
形容詞:crazed, silly, shy, goofy, angry, lazy, obstinate, purple
名詞:monkey, elephant, cyclist, teacher, author, hockey player
動詞短語:played a ukulele, danced a jig, combed his hair, flapped her ears
副詞短語:on the table, at the grocery store, in the shower, after breakfast,with a broom
再給出一個示例輸出:「The lazy author combed his hair with a broom」。
2. 編寫一個程序,讓用戶輸入名字、年齡、最喜歡的顏色和最喜歡的食物。程序要把所有這 4 項保存在一個文本文件中,每一項分別放在單獨的一行上。
3. 完成第 2 題的任務,不過使用 pickle
將數據保存到一個文件。(提示:如果先把數據放在一個列表中就會很容易做到。)