确定扑克手


19

我一直在做德州扑克游戏,作为评估的一部分,并且我一直在思考如何检查7张可用的卡并确定是否存在手牌。

我能想到的唯一可能的方法是对卡片进行数字排序,然后检查每组5张卡片,并检查它们是否与每只可能的手的列表匹配。这将花费很长时间,这将仅适用于确定对,因为该诉讼是无关紧要的。

这些牌都是由数字/ a / j / q / k和一套西装(char)3(用小黑桃符号表示)组成的字符串。

有没有人有任何建议,公式或链接可用来帮助创建手部分析系统?

不必担心彼此之间排名靠前,那是另一回事。


1
只是指出这一点,但向量标记在其上下文中是关于线性代数的。不是容器。
2013年

@Sidar如果您认为标签不合适,请随时编辑。如果将鼠标悬停在标签上,然后会弹出“编辑标签”选项(至少对我来说?),并且您可以仅编辑标签而无需编辑问题。
MichaelHouse

@ Byte56我有这种怪异的习惯,想不知道自己是否对,所以我会通过评论被动地做它。这就是为什么我不编辑帖子。也许我错过了帖子中的某些内容,所以我正在等待他的回复。
2013年

几年前,Game Developer中还出现了一篇不错的文章:cowboyprogramming.com/2007/01/04/programming-poker-ai
celion

1
我在Java中做了一个有趣的实现,您可以在这里找到它:codereview.stackexchange.com/questions/10973/…。如果您查看PokerHand.java,则会找到测试每种手型的方法(例如isFullHouse)。它们仅在卡片首先被排序时才起作用。
bughi

Answers:


20

我认为您可以通过简单地对几张牌桌来找到大多数扑克手,每张牌中有多少张牌各具不同的花色。

换句话说,创建一个数组映射卡等级(数字和A / J / Q / K)到您手中该等级的卡数。如果玩家有一对或三个同类,则此数组中将有一个元素等于2或3,以此类推。如果有一个元素为2,另一个为3,则他们拥有一个满屋子。如果此数组中有五个等于1的连续元素。

同样,您可以对每套西服的牌数进行相似的排列,并使用它来检测同花。

一旦检测到特定手牌的存在,就很容易返回并找到该手牌中的特定牌,以便在UI或需要执行的操作中突出显示它们。

用伪代码:

int countByRank[13] = { 0 };        // Initialize counter to zero for each rank
for (cards in hand)
    countByRank[card.rank] += 1;    // Increment counter for this card's rank
if (countByRank.find(2))
    // There's a pair
else if (countByRank.find(3))
    // There's a three-of-a-kind
// etc...

17

这有点棘手,因为有很多组合。幸运的是,您拥有一个可以在很短的时间内检查大量组合的处理器。

您需要一些不同的策略来检测不同类型的手。幸运的是,一些不同的类型可以重叠策略。我会按手的顺序搜索。

  1. 直冲
  2. 同一样四个
  3. 客满
  4. 冲洗
  5. 直行
  6. 三种
  7. 两对
  8. 一对
  9. 高卡

23678都是简单的计数。使用从王牌到王牌的卡片列表,只需将每个值的编号放入列表中,对于找到的每张额外的卡片就递增。然后检查列表中是否有4,如果没有4,则说明您没有此类4。检查它是否为3s,如果没有3s,则说明您没有此类3。如果您有3,请检查2(表示满座)。等等...

对于15您可以使用相同的列表并查找序列,其中所有卡在列表中对于5张卡的序列都具有一个或多个条目。如果他们也有同样的衣服,那就是同花顺。

4可以具有相同的列表设置,但是这次您要数衣服。寻找5或更高的数字。

最后,9您拥有最高的卡,只需简单地查看上面列出的其中一个的最后一个最高值即可。

如果您按顺序搜索,找到匹配项后即可进行分类。如果要向用户提供所有信息,继续搜索并找到所有匹配项将是微不足道的。


本质上,您正在填充水桶。然后检查存储桶中的组合。为了显示:

在此处输入图片说明

从一个数组开始,每个卡都有一个存储桶,然后遍历卡并计算每个卡的实例。然后,您可以轻松地迭代数组并检查某些组合。在此示例中,很明显有一种类型为4,因为其中一个存储桶中有4个项目。


6

我曾经碰到过这种算法。它乘以质数来确定手,这是非常有趣的阅读。Cactus Kev的扑克手牌评估师


1
那个很有趣,但是我不确定您是否阅读了全部。该算法适用于5张卡。您可能在Google搜索中找到了与7张卡相关的内容,因为作者指出他们有7张卡的算法,但没有共享。请参阅页面顶部附近的灰色文本。因此,我不确定该算法对7张卡套有多大用处。
MichaelHouse

1
由于只有7张牌可以制作21张不同的5张牌,所以一种选择是在每张牌上使用5张评估器,然后选择最佳的。
亚当

@ Byte56你是对的,我记得我曾经用过7张卡,但是必须事先确定最好的5张卡,这有点降低了效率。
petervaz

此后,此答案上的链接已经烂掉了,垃圾邮件发送者接管了域名。我已将链接重定向到原始页面的存档,但是要使此答案更可靠,最好编辑答案以在答案本身的主体中包含所提议算法的摘要。
DMGregory

2

为了补充这个问题已经获得的出色答案,我认为一旦基本分类技术就位,提供一种最简单的手比较方法将很有帮助。首先,正如许多答案所建议的那样,您需要在手的上标记手-您的大多数比较是“手X是否比手Y好?” 然后可以通过比较两只手的类别并查看哪个类别更好来完成。对于其余部分,您实际上需要逐个卡片进行比较,事实证明,进行更多分类工作会使此操作变得容易。

作为基准情况,请考虑两只手都是“高牌”手的情况;在这种情况下,您将首先比较两个最高的牌,然后比较(如果匹配)接下来的两张牌,依此类推。如果您假设每个输入手从最高牌到最低牌进行排序,则此方法将导致代码看起来像这个:

int CompareHandsOfSameClass(Hand h1, Hand h2) {
  for ( int i = 0; i < 5; i++ ) {
    if ( h1[i].rank > h2[i].rank ) {
      return -1;
    } else if ( h1[i].rank < h2[i].rank ) {
      return 1;
    }
  }
  return 0;
}

现在,这是个好消息:事实证明,此字典顺序已适当调整,可用于比较两个任意位置的类别,只要它们的类别相同即可。例如,由于比较对的方式是先比较对,然后再比较其他三张牌,因此您可以排序以将对放在第一(甚至是对中的一张!)并进行相同的比较。(因此,例如,将A9772之类的手存储为77A92或更好的是7A927;该手A9972将存储为9A729,并与上述代码进行比较,首先将7与9并置,然后发现A9972赢得)。两对手将被存储,两对中的较高者首先被存储,然后是较低的,然后是“踢球者”(例如,A9977将被存储为97A97)。一种类型的三张将与三张卡中的一张卡一起存储,然后是踢脚卡,然后是其他卡(例如,A7772为7A277);一个满满的房子将被存储着它的三个之一,然后是它的两个之一(例如,99777将被存储为79779);顺子和同花都可以按“直接词典顺序”存储,因为它们的比较就像高手牌一样。这导致了一个直接的外部比较器函数,该函数可通过已给定的函数适用于所有类别的手:

// Compare two hands, returning -1/0/+1 as hand 1 is less than, equal to,
// or greater than hand 2. Note that this function assumes the hands have
// already been classified and sorted!
int CompareHands(Hand h1, Hand h2) {
  if ( h1.handClass > h2.handClass ) {
    return -1;
  } else if ( h1.handClass < h2.handClass ) {
    return 1;
  } else {
    return CompareHandsOfSameClass(h1, h2);
  }
}

希望这会有所帮助!


1

使用合适的卡表示形式和位旋转可以进行一些并行化。例如,此Java代码评估返回一张整数的7卡硬牌,该整数可用于比较两只手。它可以适用于以更用户友好的方式报告手的类型。核心思想来自于Cactus Kev在早期答案中引用的页面。

如果您仅对可能以各种语言命名手的实现感兴趣,而不是对效率和代码清晰度感兴趣,还可以 在codegolf.SE上查看“为扑克手命名”挑战。


1

首先,您需要知道所有牌的等级和适合度;琐碎但必要的。

然后,旋转这7张卡片并创建两个直方图;一个按等级(使用包含13个索引的数组,如果在发现具有该等级的手牌时将所有索引初始化为零,并递增1),另一个则一个(使用与等级相似构造的四个元素组成的数组) 。这些是线性运算,您只能为一组遍历对每张卡进行两种运算。

然后,您可以通过简单地检查每个直方图是否存在符合条件的存储桶和/或简单的后续测试来确定是否存在以下任何手:

  • 对:正好一个等级的存储桶的值是否正好为2,没有其他存储桶的值大于1并且没有刷新吗?
  • 两对:两个或多个等级存储桶的值是否恰好为2,没有存储桶的值大于2并且没有刷新?(显然三对不是一手牌,但是如果有七张牌,这是有可能的;最强的两对是玩家的手牌)
  • TOAK:正好一个存储桶的值正好为3,没有其他存储桶的值大于1并且没有刷新吗?
  • 直行:五个连续等级的存储桶是否具有等于或大于1的值且没有刷新?(别忘了Aces既高又低;您可以使用14个元素的等级直方图,并根据需要在两个存储桶中计算Ace)
  • 同花顺:任何西服桶有5张或更多张卡片吗?(如果是这样,请扫描该手以找到该西装的牌,然后选择前5名)
  • Full House:满满一桶的值是否等于3,其他满桶的值是否等于2(如果有7张牌,满满就不可能同花顺,除非您在Pinochle牌组上玩)
  • 四种:任何一个等级的值都为4吗?(不可能有其他组合)
  • 同花顺:直方图是否同时显示同花顺同花顺?如果是这样,指示的同花顺西装中是否至少有一张牌与指示的直牌的每个等级相匹配?(这可能是计算上最昂贵的,但是应该很容易地计算出有多少个连续等级,并且您只需要对这只手连续扫描5个连续等级,两次扫描6次,就3次扫描7次)

您自然可以合并以下几种检查:

  • 有同花顺吗?
  • 有直线吗?
    • 如果两者都有,是否是同花顺?
    • 如果是同花顺,是否同时拥有A和A?
  • 有四种吗?
  • 有多少种三种?(两个,这是一个完整的房子。一个,一对检查)
  • 有几对?(如果有两个或更多,那是两对。如果是三个,那是一整座房子,否则是一对。)
  • 以上都不是(高级卡)。

基本上,如果这些答案的答案出了任何手,则将所得的“手强度”值设置为找到的手的强度(如果该值尚未更高)。例如,如果您有一个满屋子,强度为7,强度为9,那么您也有一个强度为3的三个,强度为2,一对。

有几个快捷键和快速出局,但总体来说它不是真的昂贵的只是运行所有的检查。


0

您可以使用简单的迭代方法相对轻松地进行扑克操作。

对于每张卡,请检查是否有一个或两个或三个其他人的同一张脸,以检查一对还是三张/四张同一种。

满屋都是相似的。或者,如果您发现一对和三个都不相同,则标记为已满。

对于同花顺,请检查每套衣服,看是否有五套相同的衣服。

进行直接检查很容易,即使未排序也是如此。对于每张卡,请检查是否有更高的卡,然后重复直到找到五张连续的卡。

皇家同花顺和同花顺与同花顺相似。皇家同花顺有一些额外的条件,那就是较低价值的卡可以忽略。

是的,这种方法效率不高,但是对于大多数扑克游戏而言,这是无关紧要的。您每半分钟左右检查一小撮玩家,而不是每秒检查数千手。

存在更好的方法,但是可能需要花费更多的时间进行编码,并且您可能有更多重要的事情要花时间/金钱,从算法的角度来看,除了算法的灵巧性和效率之外,这将使更好的游戏成为可能。


0

查找对子,双人对,toaks,满满的房子,扑克等的简单方法如下:

在这样的嵌套循环中将每个卡彼此比较:

int matches=0;
for (int i=0;i<5;i++)
   for (int j=0;j<5;j++)
      if (i!=j && cardvalue(card[j])==cardvalue(card[i])) matches++;

比赛将包含以下内容:
2对2
4对2对
6 6对局
8 对整场比赛
12对扑克

为了加快优化速度:不需要将j循环运行到5,可以将其运行到i-1。然后可以删除比较“ i!= j”,然后将匹配值减半(1 =对,2 = 2对等)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.