7张扑克牌手牌评估器

40

有没有人知道一种快速算法来评估七张扑克牌的手牌?这种算法比从七张牌中检查每个21个五张手牌组合更有效率。

谢谢,

Pete


好问题,但我认为你最好只是检查21种组合,而不是试图找出一些疯狂的诀窍来捷径。 - David M
8
实际上,与事实相反。人们已经付出了很多努力来编写快速的七张牌评估器。当您想要每秒处理数百万手时,速度变得非常重要。 - Nick Johnson
@David:有更好的方法,更易于阅读,而且根本不需要排序。它们在速度上也快了几个数量级。一个例子是——咳咳——在这里找到:http://code.google.com/p/specialkpokereval/。 - Ken
我为了练习编程而用Java写了一个自己的版本(我想为了练习而重新发明轮子)。它有文档,而且正好符合你所描述的。虽然你已经得到了很多答案,但如果你正在使用Java进行编程,这可能更相关。回复此评论,我将创建一个新的答案并链接它。如果不是Java,我猜对于你来说,它可能不会比任何已链接的答案更有趣。 - DPM
我写了一个 Java 程序,在 5 秒内检查了所有的 1.33 亿个 7 张牌手牌:http://www.codeproject.com/Articles/1068755/Quick-card-poker-ranking-in-Java 这是一个正在进行中的项目,所以我希望它会变得更快。 - Albert Hendriks
9个回答

33

我用JavaScript写了一个程序。核心评估方法只使用位操作,因此非常快速。考虑到这一点,查看21个组合仍然非常快。唯一需要深入的时候是发生平局的情况。当这种情况发生时,我们需要更详细地查看哪个5张牌的组合实际上是最好的。以下是我想出的解决方案:

hands=["4 of a Kind", "Straight Flush", "Straight", "Flush", "High Card",
       "1 Pair", "2 Pair", "Royal Flush", "3 of a Kind", "Full House" ];
var A=14, K=13, Q=12, J=11, _ = { "♠":1, "♣":2, "♥":4, "♦":8 };

//Calculates the Rank of a 5 card Poker hand using bit manipulations.
function rankPokerHand(cs,ss) {
  var v, i, o, s = 1<<cs[0]|1<<cs[1]|1<<cs[2]|1<<cs[3]|1<<cs[4];
  for (i=-1, v=o=0; i<5; i++, o=Math.pow(2,cs[i]*4)) {v += o*((v/o&15)+1);}
  v = v % 15 - ((s/(s&-s) == 31) || (s == 0x403c) ? 3 : 1);
  v -= (ss[0] == (ss[1]|ss[2]|ss[3]|ss[4])) * ((s == 0x7c00) ? -5 : 1);

  document.write("Hand: "+hands[v]+((s == 0x403c)?" (Ace low)":"")+"<br/>");
}

//Royal Flush   
rankPokerHand( [ 10, J, Q, K, A],  [ _["♠"], _["♠"], _["♠"], _["♠"], _["♠"] ] ); 

这里解释
这里演示


这个项目投入了很多努力!+1 :) - Tom Tu
他是个巫师!太棒了的文章。 - Evan Knowles
4
这段代码中存在一个错误 - 如果你有一张以A为最小牌的顺子(例如A♣ 2♦ 3♦ 4♠ 5♥ 6♦ K♥),那么它会错误地选择更低的顺子。在这个例子中,6♦高顺应该是最好的。这是因为它给予Ace额外的权重,但在这种情况下这样做是错误的,因为它不是高顺而是低顺。请参见 https://imgur.com/a/e9wGR。 - wal

14
这个网站列出了一堆扑克牌手牌评估库,并对每个库进行了一些详细介绍。其中大多数是针对5张牌的,但至少有一个是针对7张牌的,名为The Snezee7 Evaluator。此外,该网站还提供了关于分析扑克牌手快速的不同技术和算法的概述。
我在几个不同的扑克项目中使用了Keith Rule C# Port of the Pokersource Evaluator,并认为它是一个非常优秀的库。你可以使用许多聪明的技巧来制作真正快速的手牌评估器,但编写代码需要很多工作,我强烈建议使用现有的库。

请注意,Snezee7需要一个266 MB的查找表。 - David M
2
它确实需要做大量的预计算,但这就是获得快速手牌评估器的方法。Two Plus Two评估器使用类似的方法来评估7张牌的手牌,使用大小为123 MB的查找表。对于5张牌的手牌,查找表要小得多。 - Greg Bray
不要介意我自己的宣传 - 虽然速度不如上面提到的一些快,但我有一个完全不使用表格就能做到约70M手/秒的程序在这里:https://github.com/ashelly/ACE_eval。 - AShelly
@polarysekt 很遗憾,编码博客网站已经关闭了几个月。现在还不确定它是否会回来,但是您可以在 WayBack Machine 上找到这两个链接的详细信息。链接为:https://web.archive.org/web/20140625212722/http://codingthewheel.com/archives/poker-hand-evaluator-roundup/。 - Greg Bray
另外一个插件:http://lcrocker.github.io/onejoker/cardlib/ 有几种语言的绑定。它的五张牌评估器比以前的任何一个都要快,而且它的七张牌评估器与最好的相当,并且只需要1Mb的表格。 - Lee Daniel Crocker

8

很高兴您提出了问题 :) 是的,这里有一个全新的解决方案可能正是您需要的:

代码: http://code.google.com/p/specialkpokereval/
博客: http://specialk-coding.blogspot.com/2010/04/texas-holdem-7-card-evaluator_23.html

这个评估器的商业级演变版本可以通过iTunes Store购买iPhone/iPod Touch。它被称为“Poker Ace”。

关于各种解决方案的优秀总结以及链接,可以在James Devlin的博客“Coding The Wheel”中找到。

那里还没有讨论的一个评估器是Klaatu's

祝你好运!


我已将你的评估器添加到我的汇总中。我会非常感激任何反馈。 - AShelly
1
@AShelly,你的ACE评估器真是太棒了。我多年来一直梦想将其移植到GPU上...但可惜我不再玩扑克了! - étale-cohomology

5
我开发了一种算法,用于评估7张牌的手牌而不需要迭代所有21种组合。
基本上,它将7张牌分为两类:同花和非同花。如果是同花,只需在8192个条目的表中查找值即可。如果不是同花,则将使用动态规划技术运行哈希函数,然后在49205个条目的哈希表中查找该值。
如果您感兴趣,请在github上查看我的工作。 https://github.com/HenryRLee/PokerHandEvaluator

如果在检查flush之后计算五元组,而不是在之前,您的评估器可以大大提高性能。此外,由于len始终为13,k始终为7,将循环展平平均可以再节省15%的时间。 - Siddharth Chabra
@SiddharthChabra 好主意。感谢你的建议。如果你愿意,欢迎提交一个 pull request。 - HenryLee

2
我认为你应该做出21种组合,然后使用某种7462表格。第一步:任意7张牌有21种不同的5张牌组合。第二步:每种可能的最终扑克牌手(2,598,960)代表着7462种不同类型的牌手。所以,这很容易。
你只需要查看你的每21种组合,对于每一种组合,查看7462个排名表的排名即可。http://www.sendspace.com/file/pet0dd 然后,对于每七张牌,你将从我制作的这个7462表中获得21个不同的排名。最高的21个组合排名就是你想知道的。
要理解这个表格:在每一行中,你都有一个5张牌的手牌(Z表示同花,Y表示非同花),以及它的排名。这是你所需要的。我给你表格和一个示例算法。这并不是真正的代码。它是Visual Basic格式,我现在写的。可能不起作用,但你应该能够理解。代码大致如下:
'############### 1st: Define your hand, for example "2c2d2h2s3c3h3s" #############################################################################################

Dim mycard As New ArrayList

mycard(1).Add("2c")
mycard(2).Add("2d")
mycard(3).Add("2h")
mycard(4).Add("2s")
mycard(5).Add("3c")
mycard(6).Add("3h")
mycard(7).Add("3s")
mycard.Sort() '################# you need to sort in alphabeticall order to match it later with 7462 table #############################################



' ################## 2nd: Let´s transform it to every kind of 5 cards combinations (21). It will also preserve the alphabeticall order ##################################

Dim myHand5 As String = ""
Dim suited as String = ""
Dim ranking as Integer = 0
Dim myranking as Integer = 7462
Dim mystring as String = ""

For cicle1 = 0 to 2
     For cicle2 = cicle1 + 1 to 3
          For cicle3 = cicle3 + 1 to 4
               For cicle4 = cicle3 + 1 to 5
                    For cicle5 = cicle4 + 1 to 6
                         myhand5 = left(mycard(cicle1),1) & left(mycard(cicle2),1) & left(mycard(cicle3),1) & left(mycard(cicle4),1)  & left(mycard(cicle5),1)
                         suited = left(mycard(cicle1),2) & left(mycard(cicle2),2) & left(mycard(cicle3),2) & left(mycard(cicle4),2)  & left(mycard(cicle5),2)
                         if suited = "ccccc" or suited = "ddddd" or suited = "hhhhh" or suited = "sssss" then myhand5 = myhand5 & "Z" Else myhand5 = myhand5 & "Y"  
                          ranking = 0                              
                          FileOpen (1, "7462.txt", Input)
                          Do
                               ranking = ranking + 1
                               Input(1, mystring)
                               Input(1, ranking)
                               If mystring = myhand5 Then 
                                    If ranking < myranking then myrankin = ranking
                               End If
                          Loop Until EOF(1)
                          FileClose(1)
                    Next cicle5
               Next cicle4
          Next cicle3
     Next cicle2
Next cicle1

最终排名是我的排名变量。你应该在不到一秒钟内知道自己的手牌。而且与其他手牌进行比较也很好,因为你有排名值而不是名称。如果你想做一些关于扑克算法的事情,这就是你应该开始的地方。用排名值,一切都快速简单。
注意:我不是程序员。我只是个想成为程序员的人。我了解一些Visual Basic函数。我希望我知道如何制作真正的程序。如果算法有效,请留下评论。如果你希望它非常非常快,我不知道该怎么做。我希望我有一个超快的算法,可以让我在游戏的每个阶段实时检查我的胜率并与任何对手进行比较。我尝试了很多算法来计算我在翻牌前的赔率,但最快的时间是30秒。现在,我可以在3秒钟内计算出我在翻牌前的赔率,但我使用了一个150吉字节的数据库,其中包含了许多预先计算的内容。如果你想实时知道自己的赔率,你应该预先计算很多东西。这就是我做的方式。

2
我已经在C语言中创建了一个扑克评估器的测试平台,链接在这里。在我测试的评估器中,poker-eval库是胜者。 Steve Brecher's Holdem Showdown也非常快速,并且需要的内存要少得多。我的ACE_Eval也表现不错。
如果您能帮助添加其他评估器或提供来自其他机器的测试结果,我将不胜感激。

我仔细研究了Steve Brecher的评估器,但是我很难从他的0x0V0RRRRR评估结果中提取卡牌值。你能否在基准测试的BHS部分添加更详细的解码器? - JSON
我会尝试解决这个问题,但同时:有5个“R”字节,其中0..C代表“2”..‘A’。它们表示组成手牌等级的牌的值。末尾的0将被忽略。您需要查看等级以确定要拉出多少个值。因此,对于带有7牌踢出的4个A,您将拥有C5000。对于Kings over 3s的Full House,您获得B1000。两对,8和4带有Queen kiker是62A00。依此类推。一个高牌手将使用所有5个字节。ACE_Eval.h使用类似的表示。请参见ACE_decode()以获取获胜卡片的一种方法。 - AShelly

0
我开发了一个德州扑克模拟器,在开发过程中,我发现在翻牌圈上有7462种独特的组合(52-5/5张牌)。接着,在转牌圈上这个数字降至6075(5/6),最后在河牌圈上为4824(5/7)。这是因为在分类扑克手牌时,1或2张牌是无关紧要的。例如:76543QK = 7654332 是一个顺子(3到7)。
我的模拟器名为Easy Poker,可在我的网站http://crvltda.webs.com上下载。
参考资料:Pokersoftware.com/forum

0

0

当然,如果你想要做得非常快。我之前提供的算法太慢了。

table7462应该是一个数组,而不是文件中。

然后,你应该预先计算每一种不同的7张牌的组合,并将其存储到数据库中。共有133,784,560种不同的7张牌组合。

你应该使用这个格式(按字母顺序):

"2c2d2h2s3c3d3h" 并排名

存储每一个133,784,560种不同的组合。你可以做52C7次循环,对它们进行排名,并将它们存储在数据库中。 也许几天后你就可以准备好了。 当你准备好了,你就不再需要21种组合,只需将手牌按字母顺序排序,然后在你的数据库中搜索即可。

如果你这样做,你会发现你可以随时实时计算出你与对手之间的胜率。

相信我。我不是程序员,但我可以做到。我知道三秒内翻牌后我的胜率。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接