iT邦幫忙

2025 iThome 鐵人賽

DAY 19
0
自我挑戰組

順著感覺走!從零開始的 Python & Vibe Coding 遊戲創作系列 第 19

第19天: 牌組的流轉:抽牌、棄牌與手牌管理的藝術

  • 分享至 

  • xImage
  •  

嗨,各位程式碼冒險家!歡迎來到我的 「順著感覺走!從零開始的 Python & Vibe Coding 遊戲創作」第十九天。在任何卡牌遊戲中,卡牌的順暢流動都是維繫遊戲節奏與策略深度的命脈。今天,我們將深入 GameManager 的內部,揭示《奇幻卡牌競技場》是如何透過一系列精密的方法,管理從牌組(Deck)手牌(Hand)桌面牌堆(Center Pile)棄牌堆(Discard Pile) 的完整生命週期。

這些機制不僅確保了遊戲的公平性與不可預測性,更是實現回合推進、資源補充與策略平衡的程式碼基石。


一、創世紀:牌組的初始化與初始發牌

遊戲的起點,始於一個完整且經過洗牌的牌組。這個任務由 initialize_game_elements 方法負責,它確保了每場對局的獨特性與公平性。

程式碼實現 (gamemanager.py)
def initialize_game_elements(self):
    GameManager.play_battle_music()  # 播放戰鬥音樂

    # 1. 建立完整的 54 張牌組
    for card_type in CardType:
        for _ in range(6):  # 每種職業卡牌各有 6 張
            self.full_deck.append(Card(card_type))
    
    # 2. 隨機洗牌
    random.shuffle(self.full_deck)
    self.remaining_deck = list(self.full_deck)

    # 3. 初始發牌給玩家
    initial_deal_size = min(10, self.max_hand_size)
    for player in self.players:
        for _ in range(initial_deal_size):
            if self.remaining_deck:
                player.hand.append(self.remaining_deck.pop())
    
    # 4. 補充初始桌面牌堆
    self._refill_center_pile_initial(8) # 初始放置 8 張牌
    
    # ... 更新遊戲狀態
    self.turn_count = 1
    self.game_state = "PLAYER_TURN"
技術細節解析
  • 牌組構成:程式使用一個 for 迴圈遍歷 CardType 這個 Enum(列舉) 的所有成員,並為每種職業類型建立 6 張 Card 物件,最終構成一副 54 張的完整牌組(full_deck)。
  • 隨機性random.shuffle() 函式是實現遊戲不可預測性的關鍵。它會原地打亂 self.full_deck 清單中的所有卡牌順序。
  • 初始資源:遊戲開始時,每位玩家會從剩餘牌組(remaining_deck)中抽取 10 張手牌,同時桌面牌堆(center_pile)會被補充至 8 張。這些初始設定為整場遊戲奠定了戰術基礎。

二、牌流核心:回合結束時的精密調控 (advance_turn)

advance_turn 方法是回合轉換之間的總管,它在每個回合結束後被呼叫,負責處理所有與卡牌資源相關的結算與補充,是維持遊戲持續進行的關鍵。

程式碼實現 (gamemanager.py)
def advance_turn(self):
    current_player = self.get_current_player()

    # 1. 手牌上限處理 (強制棄牌)
    while len(current_player.hand) > self.max_hand_size:
        if current_player.hand:
            discarded = current_player.hand.pop(random.randint(0, len(current_player.hand)-1))
            self.discard_piles[current_player].append(discarded)
    
    # 2. 手牌補充
    cards_to_draw_for_hand = 0
    if len(current_player.hand) < 5:
        cards_to_draw_for_hand = 2
    for _ in range(cards_to_draw_for_hand):
        if self.remaining_deck:
            drawn_card = self.remaining_deck.pop()
            current_player.hand.append(drawn_card)

    # 3. 桌面牌堆補充
    cards_to_draw_for_center = self.min_center_cards - len(self.center_pile)
    if cards_to_draw_for_center > 0:
        for _ in range(cards_to_draw_for_center):
            drawn_card = self.draw_card_to_center()
            if not drawn_card:
                break # 牌組已空,停止補充
    
    # ... 切換玩家、更新回合數等
技術細節解析
  1. 手牌上限處理(強制棄牌)

    • 觸發條件:當玩家回合結束時,若其手牌數量超過 self.max_hand_size(預設為 10 張)。
    • 執行邏輯:程式會進入一個 while 迴圈,使用 random.randint 隨機選取一張超出的手牌,並透過 pop() 方法將其從玩家手牌 (current_player.hand) 移除,再加入到該玩家的棄牌堆 (self.discard_piles) 中。這個機制有效地防止了玩家囤積過多手牌,迫使其進行策略性出牌。
  2. 手牌補充

    • 觸發條件:根據遊戲規則,當玩家手牌少於 5 張時,會自動補充 2 張。
    • 執行邏輯:程式會檢查 current_player.hand 的長度。若小於 5,則從 self.remaining_deckpop() 兩張卡牌並 append() 到玩家手牌中。這確保了玩家在下個回合總有足夠的戰術選擇,避免因無牌可出而陷入僵局。
  3. 桌面牌堆補充

    • 觸發條件:當桌面牌堆 (self.center_pile) 的卡牌數量低於 self.min_center_cards(預設為 5 張)時。
    • 執行邏輯:程式會計算需要補充的數量,並多次呼叫 draw_card_to_center 方法,從牌組中翻牌到桌面,直到滿足最低數量為止。這保證了戰場中央始終有足夠的卡牌供雙方進行配對,維持遊戲的互動性。

透過 initialize_game_elements 的開局設定與 advance_turn 在每回合間的精準調控,GameManager 成功地建立了一套穩定而動態的卡牌生態系統。從洗牌、發牌,到回合結束時的強制棄牌與自動補充,每一個環節都經過精心設計,確保了遊戲體驗的流暢性與策略深度。

明天,我們將聚焦於遊戲中最核心的互動——玩家如何進行卡牌配對,以及那激動人心的「自動連擊」是如何在程式碼中實現的。敬請期待!


上一篇
第18天: 觸發強力效果:卡牌技能的程式碼實現深度解析
系列文
順著感覺走!從零開始的 Python & Vibe Coding 遊戲創作19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言