- 案例目的
本案例通过一个21点扑克牌游戏的设计和实现,了解使用Python数据类型、控制流程和输入输出。
- 案例内容
21点又名黑杰克(Blackjack),起源于法国,已流传到世界各地,有着悠久的历史。现在在世界各地的赌场中都可以看到二十一点,随着互联网的发展,二十一点开始走向网络时代。该游戏由2到6个人玩,使用除大小王之外的52张牌,游戏者的目标是使手中的牌的点数之和不超过21点且尽量大。
- 实验环境
Pycharm、Anaconda
- 案例研究
4.1 游戏规则
开局时,庄家(dealer)给每个玩家(又称闲家)牌面向上发两张牌(明牌),再给庄家自己发两张牌,一张明牌,一张暗牌(牌面朝下)。
当所有的初始牌分发完毕后,如果玩家拿到的是A和T(无论顺序),就拥有黑杰克(Black Jack);若庄家的明牌为T,且暗牌为A,应直接翻开并拥有Black Jack;如果庄家的明牌为A,则玩家可以考虑买不买保险(Insurance),保险金额是赌注的一半且不退。此时,如果庄家的暗牌为10点牌(构成Black Jack),那么翻开此牌,购买保险的玩家得到1倍赌注;如果庄家没有Black Jack则保持暗牌,玩家继续游戏。若玩家为Black Jack且庄家为其他,玩家赢得1.5倍(或2倍,1赔2时)赌注;若庄家为Black Jack且玩家为其他,庄家赢得赌注;若庄家和玩家均为Black Jack,平局,玩家拿回自己的赌注。
接下来是正常的拿牌流程:首名非黑杰克玩家选择拿牌(Hit)、停牌(Stand)、加倍(Double)、分牌(Split,两牌相同时)或投降(Surrender,庄家赢得一半赌注);若选择拿牌,则后续只能选择拿牌或停牌。在发牌的过程中,如果玩家的牌点数的和超过21,玩家就输了——叫爆掉(Bust),庄家赢得赌注(无论庄家之后的点数是多少)。假如玩家没爆掉,又决定不再要牌了(停牌,或因加倍、投降而终止),则轮到下一名非黑杰克玩家选择。
当所有玩家停止拿牌后,庄家翻开暗牌,并持续拿牌直至点数不小于17(若有A,按最大而尽量不爆计算)。假如庄家爆掉了,那他就输了,玩家赢得1倍赌注;否则那么比点数大小,大为赢。点数相同为平局,玩家拿回自己的赌注。
4.2 设计思路
图 1 21点牌流程
4.3 设计代码
import random
def get_shuffled_deck():
"""初始化包括52张扑克牌的列表,并混排后返回,表示一副洗好的扑克牌"""
# 花色suits和序号
suits = {'♣', '♠', '♢', '♡'}
ranks = {'2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'}
deck = []
# 创建一副52张的扑克牌
for suit in suits:
for rank in ranks:
deck.append(rank + ' ' + suit)
random.shuffle(deck) # 混排,即洗牌
return deck
def deal_card(deck, participant):
"""发一张牌给参与者participant"""
card = deck.pop()
participant.append(card)
return card
def compute_total(hand):
"""计算并返回一首牌的点数和"""
values = {'2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8,
'9': 9, '1': 10, 'J': 10, 'Q': 10, 'K': 10, 'A': 11}
result = 0 # 初始化点数和为0
numAces = 0 # A的个数
# 计算点数和A的个数
for card in hand:
result += values[card[0]]
if card[0] == 'A':
numAces += 1
# 如果点数和>21,则尝试把A当做1来计算(即减去10),多个A循环减去10,直到点数<=21
while result > 21 and numAces > 0:
result -= 10
numAces -= 1
return result
def blackjack():
"""21点扑克牌游戏,计算机人工智能AI为庄家,用户为玩家"""
# 初始化一副洗好的扑克牌,初始化庄家和玩家手中的牌为空
deck = get_shuffled_deck()
house = [] # 庄家的牌
player = [] # 玩家的牌
# 依次给玩家和庄家各发两张牌
for i in range(2): # 开始发两轮牌
deal_card(deck, player) # 给玩家发一张牌
deal_card(deck, house) # 给庄家发一张牌
# 打印一首牌
print('庄家的牌:', house)
print('玩家的牌:', player)
# 询问玩家是否继续拿牌,如果是,继续给玩家发牌
answer = input('是否继续拿牌(y/n,缺省为y): ')
while answer in ('', 'y', 'Y'):
card = deal_card(deck, player)
print('玩家拿到的牌为:{0}, {1}'.format(card, player))
# 计算牌点
if compute_total(player) > 21: # 如果大于21点
print('爆掉 ... 玩家输牌!')
return
answer = input('是否继续拿牌(y/n,缺省为y): ') # 继续询问是否拿牌
# 庄家(计算机人工智能)按“庄家规则”确定是否拿牌
while compute_total(house) < 17:
card = deal_card(deck, house)
print('庄家拿到的牌为:{0}, {1}'.format(card, house))
# 计算牌点
if compute_total(house) > 21: # 如果大于21点
print('爆掉 ... 玩家赢牌!')
return
# 分别计算庄家和玩家的点数,比较点数大小,输出输赢结果信息
houseTotal, playerTotal = compute_total(house), compute_total(player)
if houseTotal > playerTotal:
print('庄家赢牌!')
elif houseTotal < playerTotal:
print('玩家赢牌!')
elif houseTotal == 21 and 2 == len(house) < len(player):
print('You loose.') # house wins with a blackjack
elif playerTotal == 21 and 2 == len(player) < len(house):
print('庄家赢牌!') # player wins with a blackjack
else:
print('平局!')
if __name__ == '__main__':
blackjack()
4.4 测试结果
图 2 21点牌多点测试结果