昨天 coding 得很開心,今天繼續完成洗牌功能!
實作步驟
前一篇做了洗牌的實作,接下來要將洗好的牌給收攏。
抽牌實作:Day 12. 只是隨機一點都不浪漫
占卜師
┌──────────────┐
│ ███ ② │ ← 第 2 疊(從第 1 疊隨意抓取)
│ ███ ① │ ← 第 1 疊(原先放在桌上的)
│ ███ ③ │ ← 第 3 疊(從第 2 疊隨意抓取)
└──────────────┘
自己
[
{"name": "Ⅴ 皇帝 — The Emperor", "position": "→"},
{"name": "ⅩⅧ 星星 — The Star", "position": "→"},
{"name": "ⅩⅣ 死神 — Death", "position": "↓"},
{"name": "Ⅰ 愚者 — The Fool", "position": "←"},
{"name": "Ⅳ 皇后 — The Empress", "position": "←"},
{"name": "ⅩⅢ 吊人 — The Hanged Man", "position": "↑"},
{"name": "Ⅺ 命運之輪 — Wheel of Fortune", "position": "↓"},
{"name": "Ⅱ 魔術師 — The Magician", "position": "←"},
{"name": "ⅩⅫ 世界 — The World", "position": "↓"},
{"name": "Ⅷ 戰車 — The Chariot", "position": "↓"},
{"name": "ⅩⅨ 月亮 — The Moon", "position": "←"},
{"name": "ⅩⅤ 節制 — Temperance", "position": "→"},
{"name": "ⅩⅦ 高塔 — The Tower", "position": "←"},
{"name": "ⅩⅥ 惡魔 — The Devil", "position": "↑"},
{"name": "Ⅲ 女祭司 — The High Priestess", "position": "↑"},
{"name": "Ⅻ 正義 — Justice", "position": "↓"},
{"name": "Ⅹ 隱者 — The Hermit", "position": "→"},
{"name": "Ⅵ 教宗 — The Hierophant", "position": "↓"},
{"name": "Ⅸ 力量 — Strength", "position": "←"},
{"name": "ⅩⅩ 太陽 — The Sun", "position": "→"},
{"name": "ⅩⅪ 審判 — Judgement", "position": "↓"},
{"name": "Ⅶ 戀人 — The Lovers", "position": "↑"}
]
資料已經是 List 了,還需要收攏成疊嗎?
雖然已經是 List,但牌裡面有方向啊!
所以這一步,就是要將洗亂的牌收成橫放的樣子,牌的方向也只會剩下左跟右。
def gather_deck(self, deck):
"""
把整副牌 deck 收攏
模擬雙手在牌面上整理的狀態,先決定理牌的是左還是右手。
如果使用左手往內收,朝上會變成向右,朝下會變成向左;反之亦然
"""
lefthand = { "→":"→", "↑":"→", "←":"←", "↓":"←"}
righthand = { "→":"→", "↑":"←", "←":"←", "↓":"→"}
for card in deck:
hand_map = random.choice([lefthand, righthand])
card['position'] = hand_map[card['position']]
return deck
[
{"name": "Ⅴ 皇帝 — The Emperor", "position": "→"},
{"name": "ⅩⅧ 星星 — The Star", "position": "→"},
{"name": "ⅩⅣ 死神 — Death", "position": "→"},
{"name": "Ⅰ 愚者 — The Fool", "position": "←"},
{"name": "Ⅳ 皇后 — The Empress", "position": "←"},
{"name": "ⅩⅢ 吊人 — The Hanged Man", "position": "←"},
{"name": "Ⅺ 命運之輪 — Wheel of Fortune", "position": "→"},
{"name": "Ⅱ 魔術師 — The Magician", "position": "←"},
{"name": "ⅩⅫ 世界 — The World", "position": "←"},
{"name": "Ⅷ 戰車 — The Chariot", "position": "→"},
{"name": "ⅩⅨ 月亮 — The Moon", "position": "←"},
{"name": "ⅩⅤ 節制 — Temperance", "position": "→"},
{"name": "ⅩⅦ 高塔 — The Tower", "position": "←"},
{"name": "ⅩⅥ 惡魔 — The Devil", "position": "→"},
{"name": "Ⅲ 女祭司 — The High Priestess", "position": "→"},
{"name": "Ⅻ 正義 — Justice", "position": "→"},
{"name": "Ⅹ 隱者 — The Hermit", "position": "→"},
{"name": "Ⅵ 教宗 — The Hierophant", "position": "←"},
{"name": "Ⅸ 力量 — Strength", "position": "←"},
{"name": "ⅩⅩ 太陽 — The Sun", "position": "→"},
{"name": "ⅩⅪ 審判 — Judgement", "position": "←"},
{"name": "Ⅶ 戀人 — The Lovers", "position": "←"}
]
雖說是將牌分成三疊,但也要考慮到實際抓牌的時候不會真的抓到很邊緣,所以會先設定區間。(這個方式昨天實作洗牌的時候也有類似的作法)
def split_and_merge(self, deck):
"""
把整副牌 deck 分成三疊 (①, ②, ③)
從第一疊抓取一部分 → 第二疊,再從第二疊抓取 → 形成第三疊。
每疊至少保留 5 張。
→ 設定牌的順序 0 是最下面的一張
"""
n = len(deck)
min_size = (n // 5) # 每疊最少要有的張數 (總牌數的 1/5)
print(f"min_size={min_size}")
# 從第一疊取出第二疊
cut_1 = random.randint(min_size, n - 2 * min_size)
pile1 = deck[:cut_1]
pile2 = deck[cut_1:]
print(f"cut_1={cut_1}")
print(f"pile1:{pile1}")
# 從第二疊取出第三疊
cut_2 = random.randint(min_size, len(pile2) - min_size)
pile3 = pile2[cut_2:]
pile2 = pile2[:cut_2]
print(f"cut_2={cut_2}")
print(f"pile2:{pile2}")
print(f"pile3:{pile3}")
return pile3 + pile2 + pile1
結果
先上圖:
第一次切牌:切在第 7 張
# 第一疊,cut_1=7
[
{"name": "Ⅴ 皇帝 — The Emperor", "position": "→"},
{"name": "ⅩⅧ 星星 — The Star", "position": "→"},
{"name": "ⅩⅣ 死神 — Death", "position": "→"},
{"name": "Ⅰ 愚者 — The Fool", "position": "←"},
{"name": "Ⅳ 皇后 — The Empress", "position": "←"},
{"name": "ⅩⅢ 吊人 — The Hanged Man", "position": "←"},
{"name": "Ⅺ 命運之輪 — Wheel of Fortune", "position": "→"}
]
第二次切牌,切在第 6 張
# 第二疊,cut_2=6
[
{"name": "Ⅱ 魔術師 — The Magician", "position": "←"},
{"name": "ⅩⅫ 世界 — The World", "position": "←"},
{"name": "Ⅷ 戰車 — The Chariot", "position": "→"},
{"name": "ⅩⅨ 月亮 — The Moon", "position": "←"},
{"name": "ⅩⅤ 節制 — Temperance", "position": "→"},
{"name": "ⅩⅦ 高塔 — The Tower", "position": "←"}
]
# 第三疊
[
{"name": "ⅩⅥ 惡魔 — The Devil", "position": "→"},
{"name": "Ⅲ 女祭司 — The High Priestess", "position": "→"},
{"name": "Ⅻ 正義 — Justice", "position": "→"},
{"name": "Ⅹ 隱者 — The Hermit", "position": "→"},
{"name": "Ⅵ 教宗 — The Hierophant", "position": "←"},
{"name": "Ⅸ 力量 — Strength", "position": "←"},
{"name": "ⅩⅩ 太陽 — The Sun", "position": "→"},
{"name": "ⅩⅪ 審判 — Judgement", "position": "←"},
{"name": "Ⅶ 戀人 — The Lovers", "position": "←"}
]
最後把它疊回去
split_and_merge ->
[
{"name": "ⅩⅥ 惡魔 — The Devil", "position": "→"},
{"name": "Ⅲ 女祭司 — The High Priestess", "position": "→"},
{"name": "Ⅻ 正義 — Justice", "position": "→"},
{"name": "Ⅹ 隱者 — The Hermit", "position": "→"},
{"name": "Ⅵ 教宗 — The Hierophant", "position": "←"},
{"name": "Ⅸ 力量 — Strength", "position": "←"},
{"name": "ⅩⅩ 太陽 — The Sun", "position": "→"},
{"name": "ⅩⅪ 審判 — Judgement", "position": "←"},
{"name": "Ⅶ 戀人 — The Lovers", "position": "←"},
{"name": "Ⅱ 魔術師 — The Magician", "position": "←"},
{"name": "ⅩⅫ 世界 — The World", "position": "←"},
{"name": "Ⅷ 戰車 — The Chariot", "position": "→"},
{"name": "ⅩⅨ 月亮 — The Moon", "position": "←"},
{"name": "ⅩⅤ 節制 — Temperance", "position": "→"},
{"name": "ⅩⅦ 高塔 — The Tower", "position": "←"},
{"name": "Ⅴ 皇帝 — The Emperor", "position": "→"},
{"name": "ⅩⅧ 星星 — The Star", "position": "→"},
{"name": "ⅩⅣ 死神 — Death", "position": "→"},
{"name": "Ⅰ 愚者 — The Fool", "position": "←"},
{"name": "Ⅳ 皇后 — The Empress", "position": "←"},
{"name": "ⅩⅢ 吊人 — The Hanged Man", "position": "←"},
{"name": "Ⅺ 命運之輪 — Wheel of Fortune", "position": "→"}
]
def turn_counterclockwise(self, deck):
"""
將整副牌逆時針轉向
"""
counterclockwise = { "→":"↑", "←":"↓"}
for card in deck:
card['position'] = counterclockwise[card['position']]
return deck
[
{"name": "ⅩⅥ 惡魔 — The Devil", "position": "↑"},
{"name": "Ⅲ 女祭司 — The High Priestess", "position": "↑"},
{"name": "Ⅻ 正義 — Justice", "position": "↑"},
{"name": "Ⅹ 隱者 — The Hermit", "position": "↑"},
{"name": "Ⅵ 教宗 — The Hierophant", "position": "↓"},
{"name": "Ⅸ 力量 — Strength", "position": "↓"},
{"name": "ⅩⅩ 太陽 — The Sun", "position": "↑"},
{"name": "ⅩⅪ 審判 — Judgement", "position": "↓"},
{"name": "Ⅶ 戀人 — The Lovers", "position": "↓"},
{"name": "Ⅱ 魔術師 — The Magician", "position": "↓"},
{"name": "ⅩⅫ 世界 — The World", "position": "↓"},
{"name": "Ⅷ 戰車 — The Chariot", "position": "↑"},
{"name": "ⅩⅨ 月亮 — The Moon", "position": "↓"},
{"name": "ⅩⅤ 節制 — Temperance", "position": "↑"},
{"name": "ⅩⅦ 高塔 — The Tower", "position": "↓"},
{"name": "Ⅴ 皇帝 — The Emperor", "position": "↑"},
{"name": "ⅩⅧ 星星 — The Star", "position": "↑"},
{"name": "ⅩⅣ 死神 — Death", "position": "↑"},
{"name": "Ⅰ 愚者 — The Fool", "position": "↓"},
{"name": "Ⅳ 皇后 — The Empress", "position": "↓"},
{"name": "ⅩⅢ 吊人 — The Hanged Man", "position": "↓"},
{"name": "Ⅺ 命運之輪 — Wheel of Fortune", "position": "↑"}
]
最後一步其實放在步驟 3. 將牌收攏成疊 也可以吧?
是沒錯啦...雖然結果是一樣的,但是就感覺很沒溫度...反正是練習專案,會想選擇自己喜歡的方式也很合理。
在實做過程中一度想把方向從4個改成8個甚至12個,但時間上來不及了...也不是這次題目的重點。
還是先往下進行後續的功能比較重要!
對了,最後的 main.py
長這個樣子:
if __name__ == "__main__":
# 準備一副牌
deck = major_arcana.copy()
print(deck)
deckService = DeckService()
# 預先洗牌
deck = deckService.shuffle_and_cut(deck)
print(f"shuffle_and_cut -> {deck}")
# 攤牌洗牌
deck = deckService.spread_shuffle(deck)
print(f"spread_shuffle -> {deck}")
# 將牌收攏
deck = deckService.gather_deck(deck)
print(f"gather_deck -> {deck}")
# 切牌
deck = deckService.split_and_merge(deck)
print(f"split_and_merge -> {deck}")
# 逆時針調整牌的方向
deck = deckService.turn_counterclockwise(deck)
print(f"counterclockwise -> {deck}")
print(deck)