終於可以做點有趣的事情了!
開心搓手
既然訂好了項目,當然就要開始逐步實踐。
先從抽牌開始!
實作步驟
22 張大阿爾克納,整整齊齊。
major_arcana = [
{"name": "Ⅰ 愚者 — The Fool", "position": "↑"},
{"name": "Ⅱ 魔術師 — The Magician", "position": "↑"},
{"name": "Ⅲ 女祭司 — The High Priestess", "position": "↑"},
{"name": "Ⅳ 皇后 — The Empress", "position": "↑"},
{"name": "Ⅴ 皇帝 — The Emperor", "position": "↑"},
{"name": "Ⅵ 教宗 — The Hierophant", "position": "↑"},
{"name": "Ⅶ 戀人 — The Lovers", "position": "↑"},
{"name": "Ⅷ 戰車 — The Chariot", "position": "↑"},
{"name": "Ⅸ 力量 — Strength", "position": "↑"},
{"name": "Ⅹ 隱者 — The Hermit", "position": "↑"},
{"name": "Ⅺ 命運之輪 — Wheel of Fortune", "position": "↑"},
{"name": "Ⅻ 正義 — Justice", "position": "↑"},
{"name": "ⅩⅢ 吊人 — The Hanged Man", "position": "↑"},
{"name": "ⅩⅣ 死神 — Death", "position": "↑"},
{"name": "ⅩⅤ 節制 — Temperance", "position": "↑"},
{"name": "ⅩⅥ 惡魔 — The Devil", "position": "↑"},
{"name": "ⅩⅦ 高塔 — The Tower", "position": "↑"},
{"name": "ⅩⅧ 星星 — The Star", "position": "↑"},
{"name": "ⅩⅨ 月亮 — The Moon", "position": "↑"},
{"name": "ⅩⅩ 太陽 — The Sun", "position": "↑"},
{"name": "ⅩⅪ 審判 — Judgement", "position": "↑"},
{"name": "ⅩⅫ 世界 — The World", "position": "↑"}
]
def shuffle_and_cut(self, deck):
random.shuffle(deck)
return deck
寫完了!
...才怪。
雖然直接用 random 就能達到類似的效果,但既然是要占卜,當然就要照塔羅的規矩來才有儀式感!
占卜師
┌──────────────┐
│ ███ ② │ ← 第 2 疊(從第 1 疊隨意抓取)
│ ███ ① │ ← 第 1 疊(原先放在桌上的)
│ ███ ③ │ ← 第 3 疊(從第 2 疊隨意抓取)
└──────────────┘
自己
預先洗牌
進行 3-5 次的洗牌+切牌
def shuffle_and_cut(self, deck):
"""
Shuffle the deck of cards in place.
"""
actions = ['shuffle', 'cut'] # 定義動作:洗牌或切牌
times = random.randint(3, 5) # 隨機決定要做幾次動作
random.shuffle(deck) # 先洗一次牌
for _ in range(times):
action = random.choice(actions) # 隨機決定要洗牌還是切牌
if action == 'shuffle':
random.shuffle(deck)
elif action == 'cut':
# 切牌位置 (避免太靠近兩端,這裡限制在 1/4 ~ 3/4 之間)
cut_point = random.randint(len(deck)//4, len(deck)*3//4)
# 切牌: 把上半段移到下半段之後
deck = deck[cut_point:] + deck[:cut_point]
return deck
將牌全部攤開在桌面,固定以順時鐘的方式或逆時鐘的方向洗牌,依直覺喊停
def spread_shuffle(self, deck):
"""
模擬攤牌洗牌
Args:
deck (list): 牌堆
Returns:
list: 洗好的牌堆
"""
direction = random.choice(["clockwise", "counterclockwise"]) # 順時鐘或逆時鐘
rounds = random.randint(5, 10) # 要轉幾圈
dq = deque(deck)
for _ in range(rounds):
# 每一圈隨機旋轉幾張方向
steps = random.randint(len(deck)//3, len(deck)//2)
for _ in range(steps):
if direction == "clockwise":
dq.rotate(steps) # 順時鐘
else:
dq.rotate(-steps) # 逆時鐘
# 模擬攪動時會有些隨機亂數
temp = list(dq) # 為處理先轉為 list
random.shuffle(temp)
# 模擬攪動時牌的方向會改變
n = random.randint(3, len(temp)//2) # 最少3張,最多整副牌的一半
result = random.sample(range(0, len(temp)), n)
for index in result:
temp[index]['position'] = self.__rotate_card(temp[index]['position'], direction)
dq = deque(temp) # 處理完還原回 dq
return list(dq)
def __rotate_card(self, position, direction):
"""
模擬塔羅牌方向變動
"""
# clockwise 順時針方向 (→ ↓ ← ↑)
# counterclockwise 逆時針方向 (← ↓ → ↑)
clockwise = { "→":"↓", "↓":"←", "←":"↑", "↑":"→"}
counterclockwise = { "←":"↓", "↓":"→", "→":"↑", "↑":"←"}
choice = clockwise if direction == 'clockwise' else counterclockwise
return choice[position]
from deck import DeckService
from cards import major_arcana
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}")
還沒洗牌的樣子:print(deck)
major_arcana = [
{"name": "Ⅰ 愚者 — The Fool", "position": "↑"},
{"name": "Ⅱ 魔術師 — The Magician", "position": "↑"},
{"name": "Ⅲ 女祭司 — The High Priestess", "position": "↑"},
{"name": "Ⅳ 皇后 — The Empress", "position": "↑"},
{"name": "Ⅴ 皇帝 — The Emperor", "position": "↑"},
{"name": "Ⅵ 教宗 — The Hierophant", "position": "↑"},
{"name": "Ⅶ 戀人 — The Lovers", "position": "↑"},
{"name": "Ⅷ 戰車 — The Chariot", "position": "↑"},
{"name": "Ⅸ 力量 — Strength", "position": "↑"},
{"name": "Ⅹ 隱者 — The Hermit", "position": "↑"},
{"name": "Ⅺ 命運之輪 — Wheel of Fortune", "position": "↑"},
{"name": "Ⅻ 正義 — Justice", "position": "↑"},
{"name": "ⅩⅢ 吊人 — The Hanged Man", "position": "↑"},
{"name": "ⅩⅣ 死神 — Death", "position": "↑"},
{"name": "ⅩⅤ 節制 — Temperance", "position": "↑"},
{"name": "ⅩⅥ 惡魔 — The Devil", "position": "↑"},
{"name": "ⅩⅦ 高塔 — The Tower", "position": "↑"},
{"name": "ⅩⅧ 星星 — The Star", "position": "↑"},
{"name": "ⅩⅨ 月亮 — The Moon", "position": "↑"},
{"name": "ⅩⅩ 太陽 — The Sun", "position": "↑"},
{"name": "ⅩⅪ 審判 — Judgement", "position": "↑"},
{"name": "ⅩⅫ 世界 — The World", "position": "↑"}
]
完成預先洗牌:print(f"shuffle_and_cut -> {deck}")
shuffle_and_cut -> [
{"name": "ⅩⅩ 太陽 — The Sun", "position": "↑"},
{"name": "ⅩⅨ 月亮 — The Moon", "position": "↑"},
{"name": "Ⅰ 愚者 — The Fool", "position": "↑"},
{"name": "ⅩⅪ 審判 — Judgement", "position": "↑"},
{"name": "ⅩⅫ 世界 — The World", "position": "↑"},
{"name": "ⅩⅥ 惡魔 — The Devil", "position": "↑"},
{"name": "Ⅺ 命運之輪 — Wheel of Fortune", "position": "↑"},
{"name": "ⅩⅣ 死神 — Death", "position": "↑"},
{"name": "ⅩⅢ 吊人 — The Hanged Man", "position": "↑"},
{"name": "ⅩⅦ 高塔 — The Tower", "position": "↑"},
{"name": "Ⅶ 戀人 — The Lovers", "position": "↑"},
{"name": "Ⅱ 魔術師 — The Magician", "position": "↑"},
{"name": "ⅩⅧ 星星 — The Star", "position": "↑"},
{"name": "Ⅴ 皇帝 — The Emperor", "position": "↑"},
{"name": "Ⅸ 力量 — Strength", "position": "↑"},
{"name": "Ⅷ 戰車 — The Chariot", "position": "↑"},
{"name": "Ⅵ 教宗 — The Hierophant", "position": "↑"},
{"name": "Ⅹ 隱者 — The Hermit", "position": "↑"},
{"name": "ⅩⅤ 節制 — Temperance", "position": "↑"},
{"name": "Ⅳ 皇后 — The Empress", "position": "↑"},
{"name": "Ⅲ 女祭司 — The High Priestess", "position": "↑"},
{"name": "Ⅻ 正義 — Justice", "position": "↑"}
]
完成攤牌洗牌:print(f"spread_shuffle -> {deck}")
spread_shuffle -> [
{"name": "Ⅹ 隱者 — The Hermit", "position": "→"},
{"name": "Ⅶ 戀人 — The Lovers", "position": "↑"},
{"name": "ⅩⅫ 世界 — The World", "position": "↓"},
{"name": "Ⅲ 女祭 司 — The High Priestess", "position": "→"},
{"name": "ⅩⅣ 死神 — Death", "position": "→"},
{"name": "ⅩⅨ 月亮 — The Moon", "position": "↑"},
{"name": "Ⅴ 皇帝 — The Emperor", "position": "↓"},
{"name": "Ⅻ 正義 — Justice", "position": "→"},
{"name": "ⅩⅪ 審判 — Judgement", "position": "→"},
{"name": "ⅩⅩ 太陽 — The Sun", "position": "←"},
{"name": "Ⅵ 教宗 — The Hierophant", "position": "↑"},
{"name": "Ⅳ 皇后 — The Empress", "position": "↑"},
{"name": "Ⅺ 命運之輪 — Wheel of Fortune", "position": "↑"},
{"name": "ⅩⅦ 高塔 — The Tower", "position": "↓"},
{"name": "Ⅷ 戰車 — The Chariot", "position": "↑"},
{"name": "Ⅱ 魔術師 — The Magician", "position": "→"},
{"name": "ⅩⅤ 節制 — Temperance", "position": "←"},
{"name": "Ⅸ 力量 — Strength", "position": "↓"},
{"name": "ⅩⅧ 星星 — The Star", "position": "←"},
{"name": "ⅩⅥ 惡魔 — The Devil", "position": "↑"},
{"name": "ⅩⅢ 吊人 — The Hanged Man", "position": "←"},
{"name": "Ⅰ 愚者 — The Fool", "position": "←"}
]
直接 random 冷冰冰的,一點溫度都沒有。
占卜就是要有點儀式感才浪漫啊!
其實也有理性的因素在。
如果真的 50:50 處理正位逆位,牌面會出現過多的逆位,導致解讀趨向過度負面,那就失去占卜的意義了。