这个问题来自Daily Coding Problem这本书,是对this MIT视频中描述的问题的稍作修改。问题如下:
二十一点是一款两人制纸牌游戏,规则如下:
- 分别给玩家和发牌者两张牌。
- 然后玩家可以“击中”或任意要求额外的卡,以使他或她的总数不超过21。
- 如果发牌者的总分小于等于16,则必须命中,否则通过。
- 最后,两个比较总数,而总和不超过21的是获胜者。
对于此问题,我们将卡值简化为: 介于2到10之间的纸牌将被视为其面值,将面部纸牌被视为 10,ace数为1。
完全了解甲板上纸牌的顺序, 实施二十一点求解器以最大化玩家的得分(即 是,胜利减去损失)。
这本书具有以下代码:
import random
class Deck:
def __init__(self, seed=None):
self.cards = [i for i in range(1, 10)] * 4 + [10] * 16
random.seed(seed)
random.shuffle(self.cards)
def deal(self, start, n):
return self.cards[start:start + n]
class Player:
def __init__(self, hand):
self.hand = hand
self.total = 0
def deal(self, cards):
self.hand.extend(cards)
self.total = sum(self.hand)
def cmp(x, y):
return (x > y) - (x < y)
def play(deck, start, scores):
player = Player(deck.deal(start, 2))
dealer = Player(deck.deal(start + 2, 2))
results = []
for i in range(49 - start):
count = start + 4
player.deal(deck.deal(count, i))
count += i
if player.total > 21:
results.append((-1, count))
break
while dealer.total < 17 and count < 52:
dealer.deal(deck.deal(count, 1))
count += 1
if dealer.total > 21:
results.append((1, count))
else:
results.append((cmp(player.total, dealer.total), count))
options = []
for score, next_start in results:
options.append(score +
scores[next_start] if next_start <= 48 else score)
scores[start] = max(options)
def blackjack(seed=None):
deck = Deck(seed)
scores = [0 for _ in range(52)]
for start in range(48, -1, -1):
play(deck, start, scores)
return scores[0]
基本上,对于从卡牌n
开始的副牌后缀(1 <= n <= 52),代码尝试了玩家将0
带到52 - n
的所有可能性命中,直到他们破产,然后经销商只要分数为<= 16,就接受命中。
对于循环for i in range(49 - start)
的每次迭代,count
=“已使用的卡数”被重置为start + 4
,这是有道理的,因为我们正在模拟玩家采用不同的数字的点击次数比上次高。但是,困扰我的是玩家,发牌者的手未重置。在我看来,对于该循环的每次迭代,我们都应该从发给玩家和发牌人的两张牌开始,而不要保留最后一手。
我了解这个权利吗?