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]; 返回 r;}int HandIndex3(int c1, int c2, int c3){ int 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][1]-combin_array[52-c3][1];返回 r;}int HandIndex4(int c1, 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 有一个关于编程视频扑克的出色页面。