【问题标题】:Coding a logical sub pot system for texas holdem poker为德州扑克编写逻辑子底池系统
【发布时间】:2023-03-12 18:40:01
【问题描述】:

编辑 看来我对游戏的实际运作方式得到了不同的反应,并且在阅读了官方规则后,与众多扑克伙伴交谈后,我想我自己也不知道规则.任何澄清将不胜感激。

我正在使用 MSVC++ 2010 Express 开发一个小型扑克游戏,并且一直在努力想出一种编写子底池系统的方法。出于某种原因,我无法理解它应该如何工作,并且想知道 SO 是否可以发布一些方法来解决它。以下是德州扑克游戏中可能发生并且很可能发生的特殊情况。

情况:

玩家 A 有 50 美元的筹码首先行动,并决定全押。玩家 B 加注到 150 美元。玩家 C 只有价值 70 美元的筹码并决定全押。玩家 D 只有 20 美元并全押。现在,我如何设计一个子底池机制来跟踪所有这些。

据我了解,会发生什么:

玩家 A 用 50 美元创造主底池。你把 B 和 C 的 50 美元加起来,使主底池变成 150 美元。然后,您将玩家 B 剩余的 100 美元分成 80 美元和 20 美元。然后你为玩家 B 和 C 做一个价值 40 美元的子底池(玩家 C 从 70 美元中剩下的),然后你将玩家 B 的 80 美元返还给玩家 B,因为没有人可以覆盖它。玩家 D 的 20 美元赌注进入玩家 B,Cs 的 40 美元子底池现在价值 60 美元。 *(或者这不会被添加?它不会被添加到任何投注中,因为它无法覆盖 50 美元的主底池,如果是这样,那么它们不会被添加到任何赌注中*

现在,当它进行评估时。如果玩家 A 赢了,他从玩家 A、B 和 C 那里赢得了 150 美元。接下来,玩家 B、C 和 D 用他们价值 60 美元的子底池继续比赛。

如果玩家 B 赢了,他就赢得了一切。

如果玩家 C 赢了,他从玩家 A、B 和 C 那里赢得 150 美元。然后他挑战玩家 B 和 D,赢得 60 美元。

玩家 D 只能赢得 60 美元,而当玩家 A、B 和 C 的底池跌到这么远时,有人已经赢得了玩家 A、B 和 C 的底池。 (取决于这是否被添加到 B 和 C 的底池,因为它不包括主要的 50 美元赌注)

一切都应该这样吗?我很难弄清楚如何跟踪每个赌注和子底池。任何想法或实现它的合乎逻辑的方法都会有很大帮助。感谢您的时间。 :-)

我在考虑让每个赌注都成为一个唯一的 id,或者每轮都有一个 id,然后将每个赌注添加到要评估的数组中,该数组还指向包含玩家信息的容器。我还必须考虑到一些玩家可能在子底池中并且已经在手牌并且弃牌,这意味着我也必须跟踪它。

【问题讨论】:

  • 先写最简单的情况,只有一个玩家。然后添加第二个玩家。然后添加许多玩家。如果需要,请向我们展示您的代码。
  • 这个问题好像已经回答过了——stackoverflow.com/questions/5462583/…

标签: c++ containers poker


【解决方案1】:

在这个例子中,主锅和副锅都计算错了。

规则:规则原则是每个玩家匹配对手剩余的赌注。

计算:

1) 首先,我们考虑筹码最少的玩家(全押)。在当前示例中,玩家 D 拥有 20 美元。

2) 接下来我们将每个玩家 (A,B,C,D) 的 20 美元相加,形成的主底池等于 80 美元,由所有玩家争夺。

3) 玩家剩余筹码 A – $30, B – $130, C – $50, D – $0

4) 接下来我们考虑第二小的筹码,在当前示例中,玩家 A 还剩 30 美元。边池 1 的形成等于 $30(A) + $30(B) +$30(C)= $90。玩家 D 没钱了,无法赢得这个边池。

5) 玩家剩余筹码 A – $0, B – $100, C – $20

6) 边池 2 的形成等于 $20(B) + $20(C)= $40。玩家 A 没钱了,无法赢得这个边池。

7) 玩家 B 还剩 80 美元,这笔钱退还给他。

所以我们终于得到了:

主底池 = 80 美元,由所有玩家 A、B、C、D 争夺

Side pot1 = $90,由 A,B,C 争夺

Side pot2 = $40,由 B,C 争夺

$80 返还给玩家 B

【讨论】:

【解决方案2】:

您的示例自言自语。在第一次下注或每次下注与初始下注不同时都会创建一个子彩池。有一些属性:

  • 一个赌注是一个单独的子底池
  • 如果新的数量不同,应该可以拆分现有的罐子
  • 相同数量的罐子可以合并

所以对于 Subpot 类的想法:

  • 一个子彩池由一个独特的金额和几个玩家组成
  • 一个赌注是一个单独的子底池
  • 两个(金额)相同的子罐可以合并成一个新的子罐(如套)
  • 一个subpot可以拆分成两个supbot,用amount = amount1 + amount2;
  • 每次添加新赌注时,首先按差额平分,然后合并相同数量的底池。 即

    //laughable attempt
    class Subpot{
      int amount; //oops i mean bet
      int pot; //actually function = amount x participants
      std::vector<Players*> participants;
    
      bool split(int amounta, int amountb, Subpot& a, Subpot& b);
      static bool merge(Subpot& a, Subpot& b , Subpot& dest);
    }
    

现在每次有新手牌时,您都会使用之前稳定的一组子池,然后创建下一代子池

  • 创建子底池手牌
  • 添加到子锅集(什么集?见下文)
  • 拿下注较小的那一个
  • 拆分所有存在于大小 betSmall 和下注 - betSmall 的底池中
  • 合并大小为 betSmall 的大小
  • 如果我没记错的话,你现在有一套稳定的 subPot

【讨论】:

    【解决方案3】:

    我这样做的方法是制作一个单独的容器,用于保存已下注的赌注,并记录下注者和下注金额。

    std::list<std::map<PlayerID, Chips>> wagers;
    unsigned n_raises;  // to keep track of number of raises, useful in limit holdem
    

    我假设您有一手活跃玩家的列表/数组。

    现在,如果玩家打电话,您只需将他的 ID 和筹码数量插入列表末尾的地图中即可。

    如果玩家加注,您在列表末尾的地图中插入一个 ID 和匹配的筹码数量,然后推回另一张地图。您在这个新的背面插入筹集的金额。相应地更新n_raisesto_call

    当插入玩家的赌注时,您需要从列表的开头进行迭代,并在您第一次遇到没有该玩家筹码的地图时开始插入。前面可能有多个加注,因此您显然不能总是只插入最后一张地图。

    棘手的情况是玩家没有足够的筹码来支付完整的跟注。在这种情况下,您会找到他用完筹码的地图,插入一张新地图,然后转移玩家在这张新地图中无法覆盖的所有筹码(其他玩家的)。 (在这种情况下,我们不会更新 n_raises)。

    这是 4 个玩家 A、B、C、D 的样子:

    Player A bet 100
    
        map0:  (A,100)    
    
    Player B calls:
    
        map0: (A, 100) (B,100)
    
    Player C raises to 300:
    
        map0: (A, 100) (B,100) (C,100)
        map1: (C, 200)
    
    Player D calls:
    
        map0: (A, 100) (B,100) (C,100) (D,100)
        map1: (C, 200) (D,200)
    
    Player A's turn, he folds.
    Player B calls, but he's got only 50 left:
    
        map0: (A, 100) (B,100) (C,100) D(100): 
        map1: (B, 50) (C, 50) (D,50)           <--- here we have split the map in two   
        map2: (C, 150) (D, 150)
    
    The betting round is over.
    

    您可以为每一轮下注设置一个单独的列表,也可以只创建一个并保留一个迭代器副本以开始新一轮。

    现在很容易分锅。您确定最好的手,然后开始弹出列表前面的地图,同时拥有最好的手的玩家参与地图。如果在那之后列表是空的,你就完成了。否则,确定第二好的手牌并重复。

    在上面的例子中,如果玩家 B 最终拿到最好的牌,你推给他 map0 和 map1。 map2 将属于 C 和 D 的获胜者。

    让我们试试你的例子:

    玩家 A 有 50 美元的筹码,决定全押。玩家 B 加注到 150 美元。玩家 C 只有价值 70 美元的筹码并决定全押。玩家 D 只有 20 美元并全押。

    map0: (A,50)
    ---------------------
    map0: (A,50) (B,50)
    map1: (B,100)
    ---------------------
    map0: (A,50) (B,50) (C, 50)
    map1: (B,20) (C,20)
    map2: (B,80)
    --------------------
    map0: (A,20) (B,20) (C,20) (D,20)
    map1: (A,30) (B,30) (C,30)
    map2: (B,20) (C,20)
    map3: (B,80)
    

    我认为这与我们在实际扑克桌上的操作方式非常相似,如果我要编写扑克游戏,我可能会采用这种方式。希望对你有帮助:)

    【讨论】:

    • 这似乎是最简单的。但是现在我很想如果我真的把规则写下来了——把所有的代码都写下来会很沮丧,而且因为规则不符合官方规则而不得不重做。
    • @Ohmages 我没有注意到您在问题中的计算错误。但是请注意我回答的最后一部分,它是正确的(就像在 Enterra 的回答中解释的那样):map0 是主要底池,所有玩家都参与其中,价值 80 美元。 map1 是第一边底池,价值 90 美元,由 A、B 和 C 竞争。map2 是第二边底池,价值 40 美元,供 B 或 C 使用,map3 是返回给 B 的剩余部分。
    • 哦,如果您想消除对扑克规则的任何疑问,请访问 [poker Stackexchange](poker.stackexchange.com)。
    【解决方案4】:

    抱歉,发布了 necro,但我在 C# 中编写了一个解决罐分布的解决方案: https://dotnetfiddle.net/P0wgR5

    这种算法的好处是,在支付时,您​​只需为每位玩家提供三条信息。他们投入底池的筹码数量、牌力以及是否弃牌。这不是立即直观的,因为这不是庄家处理边池的方式,但它会正确拆分底池,丢弃任何拆分的筹码(例如:100 拆分三路输掉一个筹码)。

    这是我在 C# 中编写的拆分罐的代码:

    using System;
    using System.Collections.Generic;
    
    public class Player
    {
        public ulong potCommitment;
        public uint handStrength;
        public ulong chipsRemaining;
        public bool folded = false;
        public Player(ulong pc, uint hs, ulong chipsBehind, bool isFolded): this(pc, hs, chipsBehind)
        {
            folded = isFolded;
        }
    
        public Player(ulong pc, uint hs, ulong chipsBehind)
        {
            potCommitment = pc;
            handStrength = hs;
            chipsRemaining = chipsBehind;
        }
    }
    
    public class Program
    {
        public static List<Player> winners = new List<Player>();
        public static List<Player> players = new List<Player>();
        public static void Main()
        {
            players.Add(new Player(50, 100, 0));
            players.Add(new Player(150, 80, 0));
            players.Add(new Player(70, 100, 0));
            players.Add(new Player(20, 150, 0));
            
            // Loop through players until no unclaimed chips in pot.
            while (PotChipsRemaining(players) > 0)
                PayOutWinners(CalculateAndSortWinners(players), players);
    
            // Refund folded players if remaining chips in pot
            foreach (var player in players)
            {
                player.chipsRemaining += player.potCommitment;
                player.potCommitment = 0;
            }
    
            Console.WriteLine($"***********************\nFinal results:");
            PotChipsRemaining(players);
        }
    
        // TODO: Split Pots
        public static List<Player> CalculateAndSortWinners(List<Player> playersInHand)
        {
            uint highHand = 0;
            // Get highHand, skipping folded players and those without any commitment left
            foreach (var player in players) if (player.potCommitment > 0 && !player.folded)
            {
                if (player.handStrength > highHand)
                {
                    winners.Clear();
                    highHand = player.handStrength;
                    winners.Add(player);
                }
                else if (player.handStrength == highHand)
                {
                    winners.Add(player);
                }
            }
    
            winners.Sort((x, y) => x.potCommitment.CompareTo(y.potCommitment));
            return winners;
        }
    
        public static void PayOutWinners(List<Player> winners, List<Player> playersInHand)
        {
            ulong collectedSidePot;
            ulong currentCommitment, collectionAmount;
            List<Player> paidWinners = new List<Player>();
            // for each playerPot in winners
            foreach (var playerPot in winners)
            {
                collectedSidePot = 0;
                currentCommitment = playerPot.potCommitment;
                // Collect it from all players who have money in pot
                foreach (var player in playersInHand) if (player.potCommitment > 0)
                    {
                        collectionAmount = Math.Min(currentCommitment, player.potCommitment);
                        player.potCommitment -= collectionAmount;
                        collectedSidePot += collectionAmount;
                    }
    
                int winnersToPay = 0;
                
                foreach (var player in winners) if (paidWinners.IndexOf(player) == -1) winnersToPay++;
    
                Console.WriteLine($"collectedSidePot: {collectedSidePot}  winnersToPay: {winnersToPay}");
                // Pay unpaid winners, tip dealer with remainder...
                foreach (var player in winners) if (paidWinners.IndexOf(player) == -1)
                {
                    player.chipsRemaining += collectedSidePot / (ulong)winnersToPay;
                    if (player.potCommitment <= 0)
                    {
                        paidWinners.Add(player);
                        Console.WriteLine($"Player {players.IndexOf(player)} paid out.");
                    }
                }
            }
            winners.Clear();
        }
    
        // Only count potchips for unfolded players. Also prints status to Console.
        public static ulong PotChipsRemaining(List<Player> playersInHand)
        {
            ulong tally = 0;
            foreach (var player in playersInHand) if (!player.folded)
                {
                    Console.WriteLine($"Player {players.IndexOf(player)} chips: {player.chipsRemaining}  Commitment: {player.potCommitment} \tHandStrength: {player.handStrength}\tFolded: {player.folded}");
                    tally += player.potCommitment;
                }
    
            foreach (var player in playersInHand) if (player.folded)
                    Console.WriteLine($"Player {players.IndexOf(player)} chips: {player.chipsRemaining}  Commitment: {player.potCommitment} \tHandStrength: {player.handStrength}\tFolded: {player.folded}");
    
            return tally;
        }
    }
    

    您的示例没有牌力,但这是您的结果的一种排列:

    Final results:
    Player A chips: 45    HandStrength: 100    Folded: False
    Player B chips: 80    HandStrength: 80    Folded: False
    Player C chips: 85    HandStrength: 100    Folded: False
    Player D chips: 80    HandStrength: 150    Folded: False
    

    (我正在更新这个问题,因为尽管它很古老,但没有编码答案,而且这个页面在搜索结果中仍然很高)

    【讨论】:

    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多