介紹
在本文中,我們展示了如何在Python中表示基本的撲克元素,例如「手」和「組合」,以及如何計算撲克賠率,即在無限額德州撲克中獲勝/平局/失敗的可能性。
我們根據《拉斯維加斯威尼斯之夜》中的真實故事提供實用的分析。
我們將使用poker包來表示手牌,連擊和範圍。 我已經擴展了來自Kevin Tseng的撲克賠率計算器,因此它除了能夠計算單個手牌之外,還可以基於範圍(可能的手牌)來計算撲克概率。
from poker import Rangefrom poker.hand import Comboimport holdem_calcimport holdem_functionsimport numpy as npimport pandas as pdimport matplotlib.pyplot as pltfrom IPython.core.display import display, HTMLhero_odds = []hero_range_odds = []
翻牌圈
我的手牌為K和J(KJ),我使用來自poker.hand的Combo類構造我的手牌。
# my hand = King of spades and Jack of clubshero_hand = Combo('KsJc')print(hero_hand)
我記不清翻牌前發生的事情以及我的位置。 但是,我確實記得翻牌前有加注,而翻牌後只剩下兩名選手:我和對方。
我們現在要注意。 翻牌圈出現梅花Q,紅桃10和梅花J。 是的,我翻到了順子!
讓我們假設沒有對方撲克的先驗知識來計算翻牌後的賠率,即在翻牌後,我們將計算出我的牌勝過隨機的一對牌的可能性。
flop = ["Qc", "Th", "9s"] # the flopboard = flop # the board equals the flopvillan_hand = None # no prior knowledge about the villanexact_calculation = True # calculates exactly by simulating the set of all possible handsverbose = True # returns odds of making a certain poker hand, e.g., quads, set, straightnum_sims = 1 # ignored by exact_calculation = Trueread_from_file = None # we are not reading hands from fileodds = holdem_calc.calculate_odds_villan(board, exact_calculation, num_sims, read_from_file , hero_hand, villan_hand, verbose, print_elapsed_time = True)
Holdemcalc中的函數calculateodds_villan可以計算出特定的德州撲克贏手的概率。 通過運行蒙特卡洛方法可以估算出該概率,也可以通過模擬所有可能的情況來準確地計算出該概率,快速計算翻牌後的確切賠率。因此在這裡我們不需要蒙特卡洛近似值。 這是我們的賠率:
odds[0]{'tie': 0.04138424018164999, 'win': 0.9308440557284221, 'lose': 0.027771704089927955}
此時,我感覺還不錯。 在隨機的情況下,我只有2.77%的機會輸,獲勝的機會超過93%。 這很樂觀。
考慮到翻牌前有加注,而只有我和對方在翻牌後才離開,所以對方有一些手牌,對吧? 我們稱這種可能的手為範圍。 這是我們根據幾個因素(包括對方的舉止,位置,下注大小等)做出的推論。該推論導致我們假設對方可能擁有一組手牌。 在這一點上,我認為對方有:
一對7或更好A /10或更好K/J或更好我們可以使用「類別範圍」來表示該範圍,如下所示:
villan_range = Range('77+, AT+, KJ+')display(HTML(villan_range.to_html()))print("#combo combinations:" + str(len(villan_range.combos)))
這使對方手牌組合從總共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"]]
{'tie': 0.11423324150596878, 'win': 0.8030711151923272, 'lose': 0.08269564330170391}
在假定的範圍內,我的獲勝機率從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.18498112437506367 Flush: 0.0040608101214161816 Full House: 0.04205693296602388 Four of a Kind: 0.004560759106213652 Straight Flush: 2.0406081012141617e-05 Royal Flush: 5.101520253035404e-05
如果我們繼續玩,對方很有可能做出一對(36%)或兩對(23%)。 他極有可能直接命中(18%)甚至打出盤(9.7%)或滿堂(4%)。 由於對方很有可能擁有合理的手牌,因此我決定下高注,大約底池的2/3。
轉牌
到轉牌了,是方片2(2)。 基本上,這是一張空白牌,也就是說,它對我們的遊戲沒有太大影響。
turn= ["2d"]board = flop + turnvillan_hand = Noneodds = 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])
{'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"]]
{'tie': 0.10123966942148759, 'win': 0.8615702479338843, 'lose': 0.0371900826446281}
河牌
是梅花K(K)。 這使對方更容易勝利。 所以這對我來說是個壞消息。
river = ["Kc"]board = flop + turn + river verbose = Truevillan_hand = Noneodds = 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])
{'tie': 0.11818181818181818, 'win': 0.8696969696969697, 'lose': 0.012121212121212121}
現在,我對隨機牌的獲勝機率從96%降至約87%。 但我仍然只以1.2%的極低概率輸掉。 好吧,那條壞的河牌不是那麼糟吧?
好吧,還有另外一個因素。 對方在翻牌圈和河牌圈都跟我有大賭注。 他可能比我想像的要好...對嗎? 然後,我應該調整我的假定範圍。
現在,我認為對方不再擁有77或88的一對,否則,鑑於我的高賭注,他不會跟下去。 我認為他可能有一對9或更好的一對,才能與99、10或QQ配對。 他可能還會有JJ從而導致平局。 或KK和AA,直到轉牌時都是頭對。 我決定保持10和K或更好的牌,因為有所謂的隱含賠率。 隱含賠率是對您打出的一筆錢可以從投注中贏取多少錢的估計。 因此,對方可能會等待中獎(他可能剛剛中了?)。 因此,我將對方的更新範圍定義如下:
villan_range = Range('99+, AT+, KJ+') display(HTML(villan_range.to_html()))print("#combo combinations:" + str(len(villan_range.combos)))
現在,對方的連擊數從144降低到了132。讓我們計算更新後的賠率。
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"]]
{'tie': 0.12, 'win': 0.72, 'lose': 0.16}
現在,我有72%的機會獲勝(從86%下降的),而我在轉牌時的失利機率從3.7%增加到16%。 我決定慎重一下,對方則全押,下注大約70%的彩池。
基本的河牌戰略可以告訴您以下內容:
用你最小的牌作為河牌利用您最強的資產押注以中等強度的攤牌值檢查手牌,以期達到攤牌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種類型:
虛張聲勢:他拿著{好牌,成對}的機率為60.66%中強度牌:他以{0.8}的機率拿著{Two Pair}價值下註:他以41.33%的機率持有{三種牌}對方的全押是有道理的,他持有好牌的概率太低而無法檢查。 所以在這裡我在想他要麼因為虛弱而虛張聲勢,要麼他發瘋了,這是一個有價值的選擇。 如果您的持牌量最差,那麼會虛張聲勢;如果您的牌很強,則進行價值下注的基本策略有時被稱為兩極分化下注。 那就是對方在這裡所做的。
回顧每種類型的概率(虛張聲勢,中等強度的手牌,價值下注),我基本上應該至少有60.66%的勝率,這是一個保守的衡量標準,因為對方可能會押注三分之一。 但是我應該跟進嗎?
這是另一個稱為底池賠率的概念。 底池賠率是指相對於底池大小進行下注的價格。 總而言之,如果我贏得底池的概率大於底池限注價格和底池大小之間的比率,我應該跟注。 讓我們做一些數學運算:
贏取機會≥60.66%(保守估計)底池價格= 0.7 *底池大小預測底池大小=(1 + 0.7 + 0.7)*底池大小底池賠率=底池價格/預測底池大小= 29%我獲勝的機會至少是底池賠率的兩倍。 因此,我繼續跟進。 結果呢? 對方轉過牌。 桌子一度安靜,卻凝視著桌子上的Ace Jack。
討論和結論
在本文中,我展示了如何表示基本的撲克元素(例如手牌和組合),以及如何在講述威尼斯人夜晚的故事的同時,假設Python中的隨機手牌和範圍來計算撲克賠率。
我們展示了撲克有多麼令人興奮(概率上很有趣)。 在下面,我展示了我的獲勝賠率是如何從翻牌到轉牌,然後是河牌的改變過程,假設對方的隨機牌以及推斷範圍。
我們觀察到,即使最終結果不利於我,我還是贏得這一單挑局的主要人選。 這就是為什麼撲克玩家說
您應該專注於做出決定,而不關注所取得的結果。
當然,本文中的所有分析都假設了一些範圍和基本的撲克策略,這些策略和基本的撲克策略構成了我在玩遊戲時的思維模型,並在本文中以Python實現。 我不是職業撲克玩家,還有很多方法。 我相信我犯了一些錯誤,例如,低估了對方在翻牌前加注時持有A和J的可能。
我很好奇,其他人將如何使用此處使用的Python框架來分析手牌。
作者:Thársis Souza, PhD
deephub翻譯組:孟翔傑