練習 - 將行為新增至遊戲

已完成

在猜拳遊戲中新增了類別和資料後,即可新增最後一個部分,即行為。 您會以方法的形式新增行為。 您要將方法新增至類別,並最終將獲得可運作的猜拳遊戲。

實作行為

您已學到使用物件導向程式設計 (OOP) 樣式建置程式,先建立您的模型,再撰寫程式碼。 模型產生了一份輸出表,其代表程式可能包含的物件、資料和行為。 這是同一份資料表。

階段 演員 行為 資料
輸入 參與者 選擇符號 Participant(choice) 上儲存為「選擇」的符號
加工業 GameRound 比較遊戲規則的選擇 已檢查的「結果」
加工業 GameRound 以結果值為基礎的獎勵積分 新增到獲勝 Participant(point) 的「積分」
加工業 遊戲 檢查後續解答 答案為真即繼續,否則結束
輸出 遊戲 新的遊戲回合或遊戲結束點數

這次要將焦點放在 Behavior 資料行上,並使用方法以新增至您的類別來填入資料行。 此外,您還要將程式碼新增至這些方法,使其依預期方式工作。

這是載至目前為止的程式碼。 讓我們在下列步驟中加以擴充:

  1. 您應該要有檔案 rock-paper-scissor.py。 若沒有,請採取下列步驟:

  2. 建立檔案 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()
    

開始遊戲

遊戲的第一個部分是設定,這表示要將遊戲具現化,並讓遊戲進入等待參與者採取行動的時間點。

  1. 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()。 因為在結束遊戲時,如果您可以查看誰是贏家並列印出贏家的資訊,那會相當實用。 您可以自行決定是否要重新命名此方法。

  2. 叫用 python3 來執行程式碼:

    python3 rock-paper-scissor.py
    

    程式會產生如下的輸出:

    Spock, select rock, paper or scissor:
    

    該輸出表示程式正在等候您採取動作。

  3. 選取 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
  1. 找出 Participant 類別,並新增方法 toNumericalChoice()

        def toNumericalChoice(self):
            switcher = {
                "rock": 0,
                "paper": 1,
                "scissor": 2
            }
            return switcher[self.choice]
    

    上述方法會將您的命令列字串輸入轉換成整數。 這可讓您更輕鬆地判斷每一回合的贏家。

    提示

    請務必定位游標以正確縮排程式碼。

  2. 找出 GameRound 類別,並更新方法 compareChoices()

        def compareChoices(self, p1, p2):
            return self.rules[p1.toNumericalChoice()][p2.toNumericalChoice()]
    

    這段程式碼可讓您比較兩個選擇,並判斷誰是贏家。

  3. 在相同的類別中,新增方法 getResultAsString()

        def getResultAsString(self, result):
            res = {
                0: "draw",
                1: "win",
                -1: "loss"
            }       
            return res[result]
    

    此方法有助於判斷結果,並在畫面上顯示容易理解的文字。

  4. 持續於相同的類別中,使用下列程式碼取代 __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()比較所做的兩個選擇。 最後,畫面上會有一個資料列顯示讀者容易理解的結果。

注意

您可以在實作規則 - 解決方案程式碼中找到本練習的解決方案。

為遊戲計分

為遊戲計分是指在遊戲結束後將分數指派給正確的玩家。 結果分別為:獲勝玩家獲得一分、平手、落敗玩家則拿不到分數。

  1. 找出 Participant 類別,並新增方法 incrementPoint()

        def incrementPoint(self):
            self.points += 1
    

    提示

    引進方法用於變更屬性是進行封裝的第一個步驟。 若要想要進行此步驟,請嘗試將 points 欄位變更為 __points__,以隱藏外部成員來練習封裝。

  2. 找出 GameRound 類別,並將下列程式碼新增至 __init()__ 方法的結尾:

            if result > 0:
               p1.incrementPoint()
            elif result < 0:
               p2.incrementPoint()
    

注意

您可以在分數遊戲 - 解決方案程式碼中找到本練習的解決方案。

新增接續查詢

接續查詢是於遊戲回合結束時的問題,詢問玩家是否要繼續。 如果使用者選擇不繼續,其可輸出目前的結果和贏家 (如果有的話)。

  1. 找出 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)
    
  2. 在同一個類別中,更新方法 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
    
  3. 在相同的類別中,使用下列程式碼取代 start() 方法:

        def start(self):
            while not self.endGame:
                GameRound(self.participant, self.secondParticipant)
                self.checkEndCondition()
    
  4. 儲存檔案。

  5. 執行命令 python3 rock-paper-scissor.py,以測試您的程式:

    python3 rock-paper-scissor.py
    

    選取 rockpaper 作為輸入,然後在系統要求您繼續時輸入 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 版本的「剪刀、石頭、布」遊戲。