编译:1+1=6
1
介绍
在今天的推文中,公众号将向大家展示如何在Python中表示基本的扑克元素,例如“手牌”和“组合牌”(Hands、Combos),以及如何计算扑克赔率,即在无限注德州扑克中获胜/平局/失败的可能性。我们将使用Poker包来表示手牌、组合和区间。
https://poker.readthedocs.io/en/latest/index.html
首先导入相关库文件:
from poker import Range
from poker.hand import Combo
import holdem_calc
import holdem_functions
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.core.display import display, HTML
hero_odds = []
hero_range_odds = []
2
翻牌(Flop)
K♠、J♣
我们将使用poker.hand来构造我们的手牌:
hero_hand = Combo('KsJc')
print(hero_hand)
我们不清楚翻牌前发生的事情以及我们的位置。我们只知道翻牌前有加注,而翻牌后只剩下两名玩家:我们自己和另外一个人。
我们现在领先了。翻牌出现了:
Q♣、10♠、J♠
是的,顺子!
假设没我们有事先了解对方的牌型,让我们计算翻牌后的赔率:
flop = ["Qc", "Th", "9s"]
board = flop
villan_hand = None
exact_calculation = True
verbose = True
num_sims = 1
read_from_file = None
odds = holdem_calc.calculate_odds_villan(board, exact_calculation,
num_sims, read_from_file ,
hero_hand, villan_hand,
verbose, print_elapsed_time = True)
Holdem_calc中的函数calculate_odds_villan计算出特定的德州扑克某手牌获胜的概率。通过运行蒙特卡罗方法可以估算出该概率,也可以通过模拟所有可能的手牌来精准计算。快速计算翻牌后的精准赔率,因此在这里我们不需要蒙特卡罗近似值。这是我们的赔率:
hero_odds.append(odds[0]['win'])
odds[0]
{'tie': 0.04138424018164999,
'win': 0.9308440557284221,
'lose': 0.027771704089927955}
此时,我们感觉还不错。与随机牌相比,我们只有2.77%的机会输,获胜的机会超过93%,这很乐观。
考虑到翻牌前有加注,对反很可能会有一些东西。我们称这种可能的手牌为一个区间。这是我们根据几个因素(包括对反的举止、位置、赌注大小等)做出的推论。该推论导致我们假设对反可能拥有一组手牌:
我们可以使用Class Range来表示该区间,如下所示:
villan_range = Range('77+, AT+, KJ+')
display(HTML(villan_range.to_html()))
print("#combo combinations:" + str(len(villan_range.combos)))
144
这使我们对手的牌组合从 51*52–1=2651 减少到 144 种组合。现在假设对手的区间来计算我们的赔率。
items = [holdem_calc.calculate_odds_villan(board, exact_calculation,
num_sims, read_from_file ,
hero_hand, villan_hand,
verbose, print_elapsed_time = False) for villan_hand in villan_range.combos]
odds = {}
[odds.update({odd_type: np.mean([res[0][odd_type] for res in items if res])}) for odd_type in ["tie", "win", "lose"]]
hero_range_odds.append(odds['win'])
odds
{'tie': 0.11423324150596878,
'win': 0.8030711151923272,
'lose': 0.08269564330170392}
在给定假定的区间内,我们的获胜几率从93%下降至80%。但是,损失8.2%的可能性仍然很低,但是我们应该打赌吗?我们绝对希望对手继续比赛并且不弃牌。但是他在翻牌后有一手好牌的可能性有多大?让我们看看如果我们继续玩到最后,他下一手牌的几率有多大。
for hand_ranking in holdem_functions.hand_rankings:
print(hand_ranking +": " + str(np.mean([res[1][1][hand_ranking] for res in items if res])))
High Card: 0.06978879706152433
Pair: 0.3662891541679421
Two Pair: 0.23085399449035812
Three of a Kind: 0.09733700642791548
Straight: 0.18498112437506373
Flush: 0.0040608101214161816
Full House: 0.04205693296602388
Four of a Kind: 0.004560759106213652
Straight Flush: 2.0406081012141617e-05
Royal Flush: 5.101520253035404e-05
如果我们继续玩到河牌(第五张公共牌),对反就有很好的机会做出:
他极有可能命中:
由于对反很有可能拥有合理的手牌,因此我们决定下高注,大约底池的2/3。
3
转牌(Turn)
回合了来:
2♦
平局!
turn= ["2d"]
board = flop + turn
villan_hand = None
odds = holdem_calc.calculate_odds_villan(board, exact_calculation,
num_sims, read_from_file ,
hero_hand, villan_hand,
verbose, print_elapsed_time = True)
hero_odds.append(odds[0]['win'])
print(odds[0])
Time elapsed: 0.5410661697387695
{'tie': 0.0233201581027668, 'win': 0.9677206851119895, 'lose': 0.008959156785243741}
假设对手的牌是随机的,那么我们现在有96%的获胜几率。
但是,考虑到我们假定的对手区间,我们的获胜几率现在从翻牌时的80%上升到86%。我们再次下注,对手跟注,河牌来了。
items = [holdem_calc.calculate_odds_villan(board, exact_calculation,
num_sims, read_from_file ,
hero_hand, villan_hand,
verbose, print_elapsed_time = False) for villan_hand in villan_range.combos]
odds = {}
[odds.update({odd_type: np.mean([res[0][odd_type] for res in items if res])}) for odd_type in ["tie", "win", "lose"]]
hero_range_odds.append(odds['win'])
odds
{'tie': 0.10123966942148759,
'win': 0.8615702479338843,
'lose': 0.0371900826446281}
4
河牌(River)
K♣
河牌的梅花K使顺子更容易被对手抓住。所以这对我们来说是个坏消息。
现在,我们对随机牌的胜算从96%降至约87%。但是我仍然只是以1.2% 的极低概率输掉了比赛。
好吧,还有另外一个因素。对手在翻牌和河牌都跟我们下了大赌注。他可能有比我们想像的更好的牌。那么,我们应该调整我们的假设区间。
现在,我们认为对手不再拥有77或88的组合,否则,鉴于我们的高赌注,他不会走那么远。我们认为他可能要搭配99或更好的一对,才能与99、10或QQ配对。他可能还会有JJ、KK或者AA。由于所谓的隐含赔率,我们决定保留Ace 10或更好和King Jack或更好的组合。隐含赔率是对如果你打出一笔钱可以从投注中赢取多少钱的估计。因此,对手可能希望打一场平局。因此,我们将对手的更新区间定义如下:
villan_range = Range('99+, AT+, KJ+')
display(HTML(villan_range.to_html()))
print("#combo combinations:" + str(len(villan_range.combos)))
现在,对手的组合数从144降低了132。让我们计算更新后的赔率。
tems = [holdem_calc.calculate_odds_villan(board, exact_calculation,
num_sims, read_from_file ,
hero_hand, villan_hand,
verbose, print_elapsed_time = False) for villan_hand in villan_range.combos]
odds = {}
[odds.update({odd_type: np.mean([res[0][odd_type] for res in items if res])}) for odd_type in ["tie", "win", "lose"]]
hero_range_odds.append(odds['win'])
odds
{'tie': 0.12, 'win': 0.72, 'lose': 0.16}
现在,我们有72%的胜算(从86%下降),失利几率从3.7%增加到16%。我们决定检查一下,对手全押,下注大约底池的70%。
一个基本和标准的河牌策略:
1、用你最薄弱的资产作为河牌虚张声势。
2、用你最强的资产作为价值押注(Value Bet)。
3、用中的摊牌价值(Show down value,简称SDV)检查牌局,以期达到摊牌。
for hand_ranking in holdem_functions.hand_rankings:
print(hand_ranking +": " + str(np.mean([res[1][1][hand_ranking] for res in items if res])))
High Card: 0.0
Pair: 0.5066666666666667
Two Pair: 0.08
Three of a Kind: 0.13333333333333333
Straight: 0.28
Flush: 0.0
Full House: 0.0
Four of a Kind: 0.0
Straight Flush: 0.0
Royal Flush: 0.0
根据赔率直方图,我们可以将对手的可能手分为3种类型:
1、虚张声势:他持有(高牌,对子)的几率为60.66%。
2、SDV:他持有(两对)的几率为0.8%。
3、最强持牌:他持有(三张相同的牌-顺子)的几率为41.33%。
对手的全押是有道理的。所以在这里,所以我认为他要么是在虚张声势,比如因为错过了一场平局,要么就是在胡说八道,这是一个价值押注。虚张声势或价值押注的基本策略有时被称为两极化押注。 这就是对手在做的事。
回顾每种类型的概率(虚张声势、SDV、价值押注),我们基本上应该至少赢得60.66%,这是一个保守的衡量标准,因为对手可能会价值押注三倍。但是我们应该跟注吗?
这是另一个称为底池赔率(Pot Odds)的概念。你可能会经常听大家常说到。跟注的成败比是3:1,遭遇对手1.5:1的全下,Pot Odds 在德州扑克里都是以比例的形式出现。
池赔率是指相对于底池大小进行下注的价格,它是期望回报与面临风险的比例的数学表达式。数学公式就是:
风险 : 回报
举个例子:底池Pot 40,此时的Pot Odds就是40:(40+80)=3:1,意思是你要跟注40你才能赢得
总而言之,如果我们赢得底池的概率大于底池限注价格和底池大小之间的比率,我们应该跟注。
那么对于这把牌局:
1、胜算 ≥ 60.66%(保守)
2、跟注价格 = 0.7 * 底池大小
3、跟注后底池大小 =(1 + 0.7 + 0.7)* 底池大小
4、底池赔率 = 跟注价格 跟注后底池大小 = 29%
我们获胜的机会至少是底池赔率的两倍。因此,我们继续跟注。结果如何?对手是:
A、J
呵呵!
4
结语
下面,我们向读者展示了我们的获胜几率是如何从翻牌变成转牌,然后是河牌(假设对手玩家随机出牌)。
df_odds = pd.DataFrame(
{"board" : ["flop","turn", "river"],
"hero_odds" : hero_odds,
"hero_range_odds" : hero_range_odds,
}
index = [1, 2, 3])
In [22]:
ax = plt.gca()
df_odds.plot(kind='line',x='board',y='hero_odds', marker='o', ax=ax)
df_odds.plot(kind='line',x='board',y='hero_range_odds',color='red' ,marker='o', ax=ax)
plt.show()
我们观察到,即使最终结果不利于我们,我们还是最有可能赢得这一平手牌。这就是为什么扑克玩家说:
你应该专注于做出的决定,而不是所取得的结果!
当然,本文中的所有分析都假设了一些区间和基本的扑克策略,这些策略构成了我们在玩游戏时的思维模型,并用Python实现。这手牌有很多玩法。我们也犯了一些错误。例如,在翻牌前加注的情况下,低估了对反持有A、J的机会。
希望大家在牌桌上有更好的表现!
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有