練習 - 將行為新增至遊戲
在猜拳遊戲中新增了類別和資料後,即可新增最後一個部分,即行為。 您會以方法的形式新增行為。 您要將方法新增至類別,並最終將獲得可運作的猜拳遊戲。
實作行為
您已學到使用物件導向程式設計 (OOP) 樣式建置程式,先建立您的模型,再撰寫程式碼。 模型產生了一份輸出表,其代表程式可能包含的物件、資料和行為。 這是同一份資料表。
階段 | 演員 | 行為 | 資料 |
---|---|---|---|
輸入 | 參與者 | 選擇符號 | Participant(choice) 上儲存為「選擇」的符號 |
加工業 | GameRound | 比較遊戲規則的選擇 | 已檢查的「結果」 |
加工業 | GameRound | 以結果值為基礎的獎勵積分 | 新增到獲勝 Participant(point) 的「積分」 |
加工業 | 遊戲 | 檢查後續解答 | 答案為真即繼續,否則結束 |
輸出 | 遊戲 | 新的遊戲回合或遊戲結束點數 |
這次要將焦點放在 Behavior
資料行上,並使用方法以新增至您的類別來填入資料行。 此外,您還要將程式碼新增至這些方法,使其依預期方式工作。
這是載至目前為止的程式碼。 讓我們在下列步驟中加以擴充:
您應該要有檔案
rock-paper-scissor.py
。 若沒有,請採取下列步驟:建立檔案
rock-paper-scissor
,開啟編輯器:touch rock-paper-scissor.py code .
並新增下列程式碼:
class Participant: def __init__(self): self.points = 0 self.choice = "" class GameRound: class Game: def __init__(self): self.endGame = False self.participant = Participant() self.secondParticipant = Participant()
開始遊戲
遊戲的第一個部分是設定,這表示要將遊戲具現化,並讓遊戲進入等待參與者採取行動的時間點。
將
rock-paper-scissor.py
內容取代為此程式碼並儲存檔案:class Participant: def __init__(self, name): self.name = name self.points = 0 self.choice = "" def choose(self): self.choice = input("{name}, select rock, paper or scissor: ".format(name= self.name)) print("{name} selects {choice}".format(name=self.name, choice = self.choice)) class GameRound: def __init__(self, p1, p2): p1.choose() p2.choose() def compareChoices(self): print("implement") def awardPoints(self): print("implement") class Game: def __init__(self): self.endGame = False self.participant = Participant("Spock") self.secondParticipant = Participant("Kirk") def start(self): game_round = GameRound(self.participant, self.secondParticipant) def checkEndCondition(self): print("implement") def determineWinner(self): print("implement") game = Game() game.start()
您已將資料表中的方法新增至每個物件。 您所做的變更可以資料表來表示,如此即可更輕鬆查看哪些行為會導致新增何種方法。
行為 方法 演員 選擇符號 choose() 參與者 比較選擇 compareChoices() GameRound 獎勵積分 awardPoints() GameRound 檢查後續解答 checkEndCondition() 遊戲 遊戲結束點數 determineWinner() 遊戲 上表中大部分行為都會對應至具有類似名稱的方法。 但遊戲結束的分數例外,這會變成
determineWinner()
。 因為在結束遊戲時,如果您可以查看誰是贏家並列印出贏家的資訊,那會相當實用。 您可以自行決定是否要重新命名此方法。叫用
python3
來執行程式碼:python3 rock-paper-scissor.py
程式會產生如下的輸出:
Spock, select rock, paper or scissor:
該輸出表示程式正在等候您採取動作。
選取
rock
,以確定程式運作正常,然後選取 [輸入]。 選取paper
,然後再次選取 [輸入]:您的輸出看起來會像這段文字:
Spock, select rock, paper or scissor: rock Spock selects rock Kirk, select rock, paper or scissor: paper Kirk selects paper
注意
您可以在開始遊戲 - 解決方案程式碼中找到本練習的解決方案。
實作規則
在問題描述中,您會看到某些選擇勝過其他選擇。 例如,石頭贏剪刀、剪刀贏布等等。 這會讓人很想撰寫如下所示的程式碼:
if choice1 == "rock" and choice2 == "scissor":
return 1
elif choice1 == "paper" and choice2 == "scissor":
return -1
else:
# something else
# and so on
但這會導致大量產生程式碼,並且帶來不便。 如果遊戲需要擴充規則集,這可能會讓程式碼更難維護,這時該怎麼辦?
幸好,還有更好的方法可達成此目的。 其中一種較佳的方法是將規則視為矩陣。 矩陣的使用概念用於表示某些組合勝過其他組合。 獲勝的動作會獲得 1
、平手會獲得 0
、而落敗的動作則會獲得 -1
。 下列為適用於「剪刀、石頭、布」的矩陣:
選擇 | 石頭 | 布 | 剪刀 |
---|---|---|---|
石頭 | 0 | -1 | 1 |
布 | 1 | 0 | -1 |
剪刀 | -1 | 1 | 0 |
您可以使用多維陣列,在 Python 中實作上表,如下所示:
rules = [
[0, -1, 1],
[1, 0, -1],
[-1, 1, 0]
]
rules[0][1] # Rock vs Paper = -1, Paper wins over Rock
找出
Participant
類別,並新增方法toNumericalChoice()
:def toNumericalChoice(self): switcher = { "rock": 0, "paper": 1, "scissor": 2 } return switcher[self.choice]
上述方法會將您的命令列字串輸入轉換成整數。 這可讓您更輕鬆地判斷每一回合的贏家。
提示
請務必定位游標以正確縮排程式碼。
找出
GameRound
類別,並更新方法compareChoices()
:def compareChoices(self, p1, p2): return self.rules[p1.toNumericalChoice()][p2.toNumericalChoice()]
這段程式碼可讓您比較兩個選擇,並判斷誰是贏家。
在相同的類別中,新增方法
getResultAsString()
:def getResultAsString(self, result): res = { 0: "draw", 1: "win", -1: "loss" } return res[result]
此方法有助於判斷結果,並在畫面上顯示容易理解的文字。
持續於相同的類別中,使用下列程式碼取代
__init__()
的內容:def __init__(self, p1, p2): self.rules = [ [0, -1, 1], [1, 0, -1], [-1, 1, 0] ] p1.choose() p2.choose() result = self.compareChoices(p1,p2) print("Round resulted in a {result}".format(result = self.getResultAsString(result) ))
上述程式碼會介紹欄位
rules
,其中包含「剪刀、石頭、布」的規則實作。 此外,呼叫self.compareChoices()
會比較進行的兩個選擇。 最後,畫面上會有一個資料列顯示讀者容易理解的結果。
注意
您可以在實作規則 - 解決方案程式碼中找到本練習的解決方案。
為遊戲計分
為遊戲計分是指在遊戲結束後將分數指派給正確的玩家。 結果分別為:獲勝玩家獲得一分、平手、落敗玩家則拿不到分數。
找出
Participant
類別,並新增方法incrementPoint()
:def incrementPoint(self): self.points += 1
提示
引進方法用於變更屬性是進行封裝的第一個步驟。 若要想要進行此步驟,請嘗試將
points
欄位變更為__points__
,以隱藏外部成員來練習封裝。找出
GameRound
類別,並將下列程式碼新增至__init()__
方法的結尾:if result > 0: p1.incrementPoint() elif result < 0: p2.incrementPoint()
注意
您可以在分數遊戲 - 解決方案程式碼中找到本練習的解決方案。
新增接續查詢
接續查詢是於遊戲回合結束時的問題,詢問玩家是否要繼續。 如果使用者選擇不繼續,其可輸出目前的結果和贏家 (如果有的話)。
找出
Game
類別,並更新方法determineWinner()
:def determineWinner(self): resultString = "It's a Draw" if self.participant.points > self.secondParticipant.points: resultString = "Winner is {name}".format(name=self.participant.name) elif self.participant.points < self.secondParticipant.points: resultString = "Winner is {name}".format(name=self.secondParticipant.name) print(resultString)
在同一個類別中,更新方法
checkEndCondition()
:def checkEndCondition(self): answer = input("Continue game y/n: ") if answer == 'y': GameRound(self.participant, self.secondParticipant) self.checkEndCondition() else: print("Game ended, {p1name} has {p1points}, and {p2name} has {p2points}".format(p1name = self.participant.name, p1points= self.participant.points, p2name=self.secondParticipant.name, p2points=self.secondParticipant.points)) self.determineWinner() self.endGame = True
在相同的類別中,使用下列程式碼取代
start()
方法:def start(self): while not self.endGame: GameRound(self.participant, self.secondParticipant) self.checkEndCondition()
儲存檔案。
執行命令
python3 rock-paper-scissor.py
,以測試您的程式:python3 rock-paper-scissor.py
選取
rock
和paper
作為輸入,然後在系統要求您繼續時輸入n
。您的輸出看起來會類似下列文字:
Spock, select rock, paper or scissor: rock Spock selects rock Kirk, select rock, paper or scissor: paper Kirk selects paper Round resulted in a loss Continue game y/n: n Game ended, Spock has 0, and Kirk has 1 Winner is Kirk
注意
您可以在接續查詢 - 解決方案程式碼中找到本練習的解決方案。
恭喜,您成功完成了! 您成功實作了一個 OOP 版本的「剪刀、石頭、布」遊戲。