WOO logo

在這一頁

我的視訊撲克分析方法

我經常被問到的一個問題是,我是如何讓我的視訊撲克程式在一分鐘內評估賠率表的。本頁將嘗試解答這個問題。

我最初的程序用一種蠻力法循環遍歷所有 2,598,960 種起手牌,然後嘗試所有 32 種可能的棄牌方式,包括在棄掉所有五張牌時循環遍歷所有 1,533,939 張替換牌。這大約是 1998 年的事了。當時我在電腦上估計這個程式需要一年以上才能完成。而今天,這樣的程序只需要一個月左右。然而,使用兩個快捷鍵,你可以將時間從大約一個月縮短到大約三秒鐘。以下是操作方法。

為了將運行時間縮短到幾天,您可以避免分析發牌時類似的牌型。例如,如果起手牌是四張 A 和一張 K,那麼 K 的花色就無關緊要了。將任意花色分配給 K,然後將結果乘以四,可以節省一些時間。使用相同的邏輯,不同類型的起手牌數量可以從 2,598,960 種減少到 134,459 種。下表顯示了按等級排列每類牌型的花色及其權重的方法。

五個單身漢

循環遍歷所有 combin(13,5)=1,287 種可能的組合,從 13 種組合中選出 5 個不同的等級。對於每種等級組合,設定花色(編號為 1 到 4)和權重,如下所示。例如,第一行將每個單例等級設定為花色 1。由於有四種可能的花色,因此與其循環四次,不如循環一次,然後將結果乘以權重 4。

五個獨特的等級

class="data-heading">重量
唱1唱歌2唱歌3唱歌4唱歌。5
1 1 1 1 1 4
2 1 1 1 1 12
1 2 1 1 1 12
1 1 2 1 1 12
1 1 1 2 1 12
1 1 1 1 2 12
2 2 1 1 1 12
2 1 2 1 1 12
2 1 1 2 1 12
2 1 1 1 2 12
1 2 2 1 1 12
1 2 1 2 1 12
1 2 1 1 2 12
1 1 2 2 1 12
1 1 2 1 2 12
1 1 1 2 2 12
2 3 1 1 1 24
2 1 3 1 1 24
2 1 1 3 1 24
2 1 1 1 3 24
1 2 3 1 1 24
1 2 1 3 1 24
1 2 1 1 3 24
1 1 2 3 1 24
1 1 2 1 3 24
1 1 1 2 3 24
1 1 2 2 3 24
1 2 1 2 3 24
1 2 2 1 3 24
1 1 2 3 2 24
1 2 1 3 2 24
1 2 2 3 1 24
1 1 3 2 2 24
1 2 3 1 2 24
1 2 3 2 1 24
1 3 1 2 2 24
1 3 2 1 2 24
1 3 2 2 1 24
3 1 1 2 2 24
3 1 2 1 2 24
3 1 2 2 1 24
4 4 1 2 3 24
4 1 4 2 3 24
4 2 3 4 1 24
4 1 2 3 4 24
1 4 4 2 3 24
1 4 2 4 3 24
1 4 2 3 4 24
2 3 4 4 1 24
2 3 4 1 4 24
1 2 3 4 4 24

一對

循環遍歷所有 13×combin(12,3)=2,860 種可能的方式,為對子選擇一個等級,並從剩餘的 12 種方式中為三個單張牌選擇一個等級。對於每種等級組合,設定花色(編號為 1 到 4)和權重如下。例如,第一行將對子的花色設為 1 和 2,單張牌的花色全部設為 1。有 combin(4,2)=6 種方式來選擇對子的花色,有 2 種方式為單張牌選擇一個與對子花色相同的花色,權重為 6×2=12。

一對

第 1 對第 2 對唱1唱歌2唱歌3重量
1 2 1 1 1 12
1 2 1 1 2 12
1 2 1 2 1 12
1 2 2 1 1 12
1 2 1 1 3 24
1 2 1 3 1 24
1 2 3 1 1 24
1 2 1 3 3 24
1 2 3 1 3 24
1 2 3 3 1 24
1 2 3 3 3 12
1 2 1 2 3 24
1 2 1 3 2 24
1 2 3 1 2 24
1 2 3 4 4 12
1 2 4 3 4 12
1 2 4 4 3 12
1 2 1 3 4 24
1 2 3 1 4 24
1 2 3 4 1 24

兩對

循環遍歷所有 combin(13,2)×11=858 種可能的方式,為兩對牌從 13 種花色中選出兩種,為單張牌從剩下的 11 種花色中選出一種。對於每種花色組合,設定花色(編號為 1 到 4)和權重如下。例如,第一行將第一對牌的花色設定為 1 和 2,第二對牌的花色設定為 3 和 4,單張牌的花色設定為 1。有 combin(4,2)=6 種方式可以為第一對牌選出花色。第二對牌有另外兩種花色,因此只有一個 1 可供選擇。單張牌可以是第一對牌中的任一花色,因此有兩種可能性。因此第一行的權重為 6×1×2=12。

兩對

第 1 對
卡 1
第 1 對
卡 2
第 2 對
卡 1
第 2 對
卡 2
唱1重量
1 2 3 4 1 12
1 2 3 4 3 12
1 2 1 3 1 24
1 2 1 3 2 24
1 2 1 3 3 24
1 2 1 3 4 24
1 2 1 2 1 12
1 2 1 2 3 12

三張同點牌

循環遍歷所有 13×combin(12,2)=858 種可能的方式,從三張牌的 13 種花色中選出一種,從另外 12 種花色中選出兩種單張牌,共計 66 種方式。對於每種花色組合,設定花色(編號為 1 到 4)和權重如下。例如,第一行將三張牌的花色設定為 1、2 和 3,將兩個單張牌的花色設定為三張牌所代表的三種花色中的兩種。從 4 種花色中選出三張牌的 3 種方式共有 combin(4,3)=4 種,從這三種花色中選出第一張單張牌的花色有 3 種,從這三種花色中選出第二張單張牌的花色有 2 種。因此,第一行的權重為 4×3×2=24。

三張同點牌

3種
卡 1
3種
卡 2
3種
卡 3
唱1唱歌2重量
1 2 3 1 2 24
1 2 3 1 4 12
1 2 3 4 1 12
1 2 3 1 1 12
1 2 3 4 4 4

客滿

循環遍歷所有 13×12=156 種可能的方式,從 13 種花色中為三張牌選擇一個等級,並有 12 種方式為對子選擇一個等級。對於每種等級組合,設定花色(編號為 1 到 4)和權重如下。例如,第一行將對子的花色設為 1 和 2,三張牌的花色設為 1、2 和 3。那麼,對子共有 combin(4,2)=6 種花色選擇方式。三張牌使用了對子的兩種花色,以及另外兩種花色中的一種。因此,第一行的權重為 6×2×2=12。

客滿

一對
卡 1
一對
卡 2
3種
卡 1
3種
卡 2
3種
卡 3
重量
1 2 1 2 3 12
1 4 1 2 3 12

四條

循環遍歷四條的所有 13×12=156 種可能的方式,從 13 種花色中選出一種,以及單張牌的所有 12 種花色。對於每種花色組合,設定花色(編號為 1 到 4)和權重如下。例如,第一行將四條的花色設定為 1、2、3 和 4,單張牌的花色設定為 1。三條只有一種花色可以從 4 種花色中選出 4 種,而單張牌有 4 種花色可以從 4 種花色中選出一種。因此,第一行的權重為 1×4×2=4。

四條

4種
卡 1
4種
卡 2
4種
卡 3
4種
卡 4
唱1重量
1 2 3 4 1 4

上述步驟將減少 95% 的計算時間,但如果循環遍歷 1,533,939 種可能的替換牌組合,仍然需要幾個小時。三秒程序的秘訣在於不在抽牌步驟循環。具體方法如下:

  1. 初始化以下數組:
    • 數組 1:大小 2,598,960
    • 數組 2:大小為 270,725 x 16
    • 數組 3:大小為 22100 x 16
    • 數組 4:大小為 1326 x 16
    • 數組 5:大小為 52 x 16
    • 數組 6:大小 16
    16 指的是抽牌時賠付牌型的最大數量。您可以根據需要進行調整。我從未見過任何視訊撲克遊戲的賠付表包含超過 16 種元素。
  2. 循環遍歷 52 張牌中所有 2,598,960 種 5 張牌的組合。此時請勿使用 134,459 手牌快捷方式。對每手牌執行以下操作:
    • 根據撲克牌的點數進行評分。
    • 將分數放入數組 0 中。將第一手牌放入陣列的元素 0 中,每拿一手牌,分數加 1。
    • 對於從 5 張牌中選擇 4 張的 5 種方法中的每一種,將這 4 張牌轉換為從 0 到 270,724 的索引號(我將在稍後解釋如何執行此操作),並將數組 1 的元素 [索引號][手牌分數] 增加 1。
    • 對於從 5 張牌中選擇 3 張的 10 種方法中的每一種,將這 3 張牌轉換為從 0 到 22,099 的索引號,並將 array2 的元素 [索引號][手牌分數] 增加 1。
    • 對於從 5 張牌中選擇 2 張的 10 種方法中的每一種,將這兩張牌轉換為從 0 到 1,325 的索引號,並將 array3 的元素 [索引號][手牌分數] 增加 1。
    • 對於從發牌的 5 張牌中選擇 1 張的 5 種方法中的每一種,將牌轉換為從 0 到 51 的索引號,並將 array4 的元素 [索引號][手牌分數] 增加 1。
    • 將陣列 5 的元素 [手牌分數] 增加 1。
    此時,你將得到一個數組,用來表示在發牌時持有任何一組牌的可能結果,不包括懲罰牌(你棄掉的牌)。陣列 0 表示棄掉 0 張牌的結果,陣列 1 表示棄掉 1 張牌的結果,以此類推。
  3. 接下來,循環遍歷上面解釋的 134,459 類手。
  4. 若要確定保留全部五張牌的價值,請將五張牌轉換為索引號,然後在陣列 0 中尋找撲克牌價值。
  5. 要確定持有任意四張牌的價值,請將這四張牌轉換為索引號,並在 array1 的相應元素中查找抽牌的可能結果。但是,這將包括獲得您在發牌時丟棄的牌。因此,您應該從數組中與持有所有五張牌的撲克價值相關的元素中減去一。例如,如果您持有 J♣、Q♣、K♣、A♣ 並丟棄2♥,則有 1 種方法可以獲得皇家牌,8 種方法可以獲得同花,3 種方法可以獲得順子,12 種方法可以獲得一對 J 或更好的牌,以及 23 種方法可以獲得輸牌。但是,array1 會顯示有 24 種方法可以獲得輸牌,包括抽牌時獲得2♥ 。因此,您需要從持有 4 張牌的 5 種可能結果中減去持有所有牌的結果。
  6. 確保你理解上述步驟的邏輯,因為在這一步驟中我們將更進一步。對於任三張牌的 10 種持有方式,你需要在陣列 2 中找到可能的結果。然後,從陣列 1 中減去你手中的三張牌和每張要棄掉的牌的可能結果。例如,對於保留 2♣ 2♥ 2♠ 並棄掉4♥和 J♠ 的值,首先從數組 2 中 2♣ 2♥ 2&spades 的值開始,然後減去 2♣ 2♥ 2♠ 4♥和 2♣ 2♥ 2♠ J♠ 的值。但是,這會導致持有所有五張牌的結果減去兩倍。所以你需要將持有所有五張牌的結果加回去。
  7. 按照相同的邏輯,對於保存任意 2 張卡片,從 array3 中的值開始,從 array2 中減去相應的卡片組,從 array1 中添加回相應的卡片組,然後從 array0 中減去用於保存所有內容的元素。
  8. 對於持有 1 張卡,從 array4 中的關聯值開始,從 array3 中減去對應的值,從 array2 中加回對應的值,從 array1 中減去對應的值,然後從 array0 中加回對應的值。
  9. 若要丟棄所有內容,請從 array5 中的值開始,然後從 array4 中減去適當的值,從 array3 中加入適當的值,從 array2 中減去適當的值,從 array1 中加入適當的值,然後從 array0 中減去適當的值。
  10. 現在你應該知道了這手牌所有 32 種玩法的所有可能結果的組合數。確定每種玩法的預期值。對於產生最大預期值的玩法,將其可能的結果添加到包含遊戲所有可能結果的陣列中。記得要乘以該手牌的相應權重。
  11. 循環遍歷所有 134,459 種起手牌後,你應該知道每種牌型在抽牌時有多少種組合方式。用這個陣列來決定遊戲的整體回報。

以下是四個子程序,用於翻譯 2 到 5 張卡片(編號為 0 到 51)並傳回索引值。

int HandIndex2(int c1, int c2){ int r; r=combin_array[52][2]-combin_array[52-c1][2]; r+=combin_array[51-c1][1]-combin_array[52-c2][1]; 傳回 rint} 傳回r; r=combin_array[52][3]-combin_array[52-c1][3]; r+=combin_array[51-c1][2]-combin_array[52-c2][2]; r+=combin_array[51-c2][52-c2][2]; r+=combin_array[51-c2][52-c2][2]; r+=combin_array[51-c2][1]-com01][2]; int c2, int c3, int c4){ int r; r=combin_array[52][4]-combin_array[52-c1][4]; r+=combin_array[51-c1][3]-combin_array[52-c2][3]; r+=combin_array[51-c2][2]-combin_array[52-c3][2]; r+=combin_array[51-c3][1]-combin_array[52-c4][1]; 傳回 r;}int HandIndex5(int CardIndex[]){ int r; r=combin_array[52][5]-combin_array[52-CardIndex[0]][5]; r+=combin_array[51-CardIndex[0]][4]-combin_array[52-CardIndex[1]][4]; r+=combin_array[51-CardIndex[1]][3]-combin_array[52-CardIndex[2]][3]; r+=combin_array[51-CardIndex[2]][2]-combin_array[52-CardIndex[3]][2]]; r+=combin_array[51-CardIndex[3]][1]-combin_array[52-CardIndex[4]][1]; 回傳 r;}

連結

該網站解釋了作者如何將他的視訊撲克分析器的速度從一年縮短到七秒。

VP Genius 有一個關於編程視訊撲克的出色頁面。