扑克下注回合的对象表示

发布于 2024-08-12 05:39:57 字数 1002 浏览 3 评论 0原文

我正在编写扑克牌的 HandConverter。这是我的第一个项目,我试图从一开始就做好它。

我已经获得了大部分内容,例如玩家列表、他们的位置、筹码大小、不同牌局的牌、正在玩的游戏等等,但我很难处理下注的表示,尤其是不同的加注、下注和倍数来自同一玩家的呼叫。

我发现在某些情况下,我的基于案例的天真解决方案不起作用,而且它非常复杂,我不喜欢它。由于它目前适用于 NL Hold'em,我认为如果我想实现 Stud、Razz 等游戏,尽管下注结构可能相同,我将有更多解决方法。

现在我使用这种表示形式,并且我想特别改进 RoundAction 类。你有什么建议给我吗?

public class HandHistory
{
    public GameInfo GameInfo;
    public TableInfo TableInfo;
    public List<Player> Players;
    public List<Round> Rounds;
    public string rawtext;
    public bool withHero;

}

public Round
{
    public List<Action> Action;
    public string Name;
    public decimal Potsize;
    public ulong Cards; //usually would have used a custom class, 
                        //but I need them in a ulong mask for some library I use
}

public class Action
{
    public Player Player;
    public string Type;
    public decimal Amount;
}

PS我还使用列表来存储不同的回合,是否有更好的方法,例如继承翻牌圈、转牌和河牌的回合类?

I'm writing a HandConverter of a poker hand. This is my first project and I'm trying to do it right from the beginning.

I got already the most parts, like lists of players, their position, stack sizes, cards for different boards, what game is being played and so on, but I struggle with the representation of the betting, especially the different raises, bets and multiple calls from the same player.

I found some cases where my naive case based solution does not work, and it's really complicated and I dislike it. As it currently works for NL Hold'em I think I'll have more workarounds to do if I want to implement games like Stud, Razz and so on altough the betting structure is likely the same.

For now I use this representation and I would like to improve especially the Round and Action classes. Do you have some suggestions for me?

public class HandHistory
{
    public GameInfo GameInfo;
    public TableInfo TableInfo;
    public List<Player> Players;
    public List<Round> Rounds;
    public string rawtext;
    public bool withHero;

}

public Round
{
    public List<Action> Action;
    public string Name;
    public decimal Potsize;
    public ulong Cards; //usually would have used a custom class, 
                        //but I need them in a ulong mask for some library I use
}

public class Action
{
    public Player Player;
    public string Type;
    public decimal Amount;
}

P.S. I'm also using a List to store the different rounds, is there better way like inheriting the round class for Flop, Turn and River e.g?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

一抹苦笑 2024-08-19 05:39:57

您可以使用枚举来代替 Action.Type 的字符串:

enum BettingAction
{
   Check,
   Bet,
   Call,
   Raise,
   Fold
}

Instead of a string for your Action.Type, you could use an enum:

enum BettingAction
{
   Check,
   Bet,
   Call,
   Raise,
   Fold
}
本王不退位尔等都是臣 2024-08-19 05:39:57

当你说第一个项目时,你的意思是什么?我猜您是学生或编程新手。

在这种假设下,我建议选择一些比扑克牌历史更简单的东西。就像在游戏编程中一样,在第一次编写游戏时就认为你正在创建最新的《使命召唤》,这是不合理的。你从突破开始,然后从那里向上移动。

如果你不希望开始规模比我建议的小,那么就不要开始编码。当你这样做的时候,你会花更多的时间在转动轮子上,而不是完成某件事。

例如,您应该首先花时间设计您的程序将做什么和不会做什么。尝试尽可能完整。这可以通过使用 UML 程序来完成,也可以通过笔和纸来完成。

我会告诉你你希望一只手如何进步。您想要跟踪的信息。一旦你真正理解了这一点,你的数据结构就会开始变得生动起来。

由于您是编程新手,我将开始编写概念验证代码。然后将其移至您的最终项目。我所说的概念验证是指您只是测试一个想法以了解其工作原理的代码。例如,手历史如何运作?您可以创建一些“模拟”历史记录并进行设置吗?理想情况下,您会进行单元测试,但让我们从较小的范围开始。

重要的是要知道您正在构建一个程序,就像建造一座房子一样。您需要知道您希望它做什么和不做什么(蓝图)。每一步是什么。然后你慢慢地在其他作品的基础上进行构建。这是一个需要时间的过程,但最终是值得的。

When you say first project what do you mean? I am guessing you are a student or new to programming.

Under that assumption I would suggest picking something simpler and than a poker hand history. As in game programming it is unreasonable to think on your first shot of programming a game you create the latest Call of Duty. You start with breakout and move up from there.

If you do not wish to start smaller than I suggest never jump into coding. When you do that you will spend more time just spinning your wheels rather than getting something done.

For instance you should first spend time designing what your program will do and what it will not do. Try to be as complete as possible. This can be done from something as complicated using a UML program or as simple as pen and paper.

I would flow out how you want a hand to progress. Information you want to track. Once you really understand this your data structures will start to come to life.

Since you are new to programming, I would start to write proof of concept code. Then move it to your final project. What I mean by proof of concept is code that you are just testing an idea to see how it works. For example, how would hand history work? Can you create some 'mock' history and set them up? Ideally you would unit test, but lets start a little smaller.

It is important to know that you are constructing a program, just like a house. You need to know what you want it to do and not do (blue prints). What each step is. And you build upon other pieces slowly. It is a process that takes time, but in the end is well worth it.

随波逐流 2024-08-19 05:39:57

卡牌可以有一个更好的名字,但我假设你指的是公共卡牌。我会将其设为一张牌列表,然后转牌和河牌子类列表中将只有一张牌。我还建议以对您有意义的方式表示卡片,然后在需要与库交互时进行转换。

Cards could have a better name, but I am assuming you mean the community cards. I would make it a list of Cards, then the turn and river subclasses would just only ever have one card in the list. I would also suggest representing the cards in a way that makes sense to you and then doing a conversion when you need to interface with the library.

何以畏孤独 2024-08-19 05:39:57

并不是真正与编程相关的答案;但 Razz 或 Stud 的投注方式在几个方面与 Hold 'em 有所不同。

1.) 没有百叶窗;相反
2.) 开局者可以带入或完成投注
3.) 有更多轮的下注

你有一个很好的开始。您可能想要创建一个 List,其中包含 List。否则,您将拥有大量回合,而无法分辨一只手牌何时开始/结束以及另一只手牌何时开始。

我认为您可能需要定义您的操作类型,然后事情可能会开始步入正轨。这是我想要的类型:

检查
赌注
折叠
致电
加注(本质上是跟注和下注)

可能还需要考虑在您的操作类中实施诸如“优先操作”之类的内容;因为每个玩家都对他们面前的动作做出反应。

您还需要解决游戏的一些细微差别;其中玩家 A 下注 500,玩家 B 全押 250;因为除此情况外,跟注需要与之前的下注相匹配。

Not really a programming related answer; but the betting styles for Razz or Stud is different from Hold 'em in several ways.

1.) There are no blinds; rather antes
2.) The person opening can either bring-in or complete the bet
3.) There are more rounds of betting

You have a pretty good start. You'd probably want to create a List<Hands> which has List<Rounds> inside it. Otherwise you'll have a huge list of rounds without being able to tell when one hand started/ended and another one began.

I think you probably need to define out your action types, and then things will probably start to fall into place. Here's what I would have for types:

Check
Bet
Fold
Call
Raise (essentially a call and bet)

Might also want to think about implementing something like "Prior Action" on your action class; as each player is reacting to the action before them.

You'd also want to address some nuances of the game; where player a bets 500 and player b goes all in for 250; since except in this instance, the call needs to match the prior bet.

人间☆小暴躁 2024-08-19 05:39:57

“回合”这个词有点含糊。 BettingRound 让这一点变得更加明显。
我不认为这里有必要有牌、名字和底池大小。底池大小是整个下注轮中的动作和变化的函数。
座位比玩家列表更能代表游戏,因为这可以让您更明显地代表游戏状态(堆栈大小等)。
我不认为有必要将翻牌圈、河牌牌明确分配给回合 - 只需使用牌列表和一些约定即可。例如,前三张牌=翻牌...第一轮下注=翻牌。使用一些扩展方法,方便参考德州扑克的翻牌。
当您需要使用卡片时,可以通过转换使用 ulong 版本的卡片,而不是弄乱您的域模型。

这就是我如何看待特定单个游戏的模型(即 1 次翻牌、河牌、转牌等)。仍然有很多工作要做来对所有游戏进行建模(例如,限制游戏使用小赌注/大赌注而不是盲注来定义赌注)。

公开课卡
{
公共西装 西装;
公开排名排名;
公共 ulong ToCardMask();
}

public enum Suit
{
    Clubs,
    Diamonds,
    Hearts,
    Spades
}

public enum Rank
{
    Ace,
    Deuce,
    Trey,
    //...
    Jack,
    Queen,
    King
}

public class Game
{
    public GameInfo GameInfo;
    public TableInfo TableInfo;
    public List<BettingRound> BettingRounds;
    public List<Card> CommunityCards;
    public string Rawtext;
    public bool WithHero; //??
}

public static class GameExtensions
{
    public static BettingRound Flop(this Game game)
    {
        return game.BettingRounds[0];
    }

    public static List<Card> FlopCards(this Game game)
    {
        return game.CommunityCards.Take(3).ToList();
    }
}

public class GameInfo
{
    public GameType GameType;
    public GameBettingStructure BettingStructure; // Limit, PotLimit, NoLimit
    public Stakes Stakes; // e.g. { $1, $2 }
    public long Id;
    public List<Seat> Seats;
}

enum GameType // prob change to a class for extensibility
{
    HoldEm,
    Razz,
    StudHi,
    StudHiLo,
    OmahaHi,
    OmahaHiLo
}

enum GameBettingStructure
{
    Limit,
    PotLimit,
    NoLimit
}

class Stakes // probably needs some tweeking for stud games (e.g. bring-in ...)
{
    public Money Ante;
    public List<Money> Blinds;
}

public class Seat
{
    public Player Player;
    public Money InitialStackAmount;
    public Money FinalStackAmount; // convienience field can be calculated
    public List<Card> Hand;
}

class Money
{
    public decimal Amount;
    public Unit Unit;
}

enum Unit
{
    USD,
    EUR,
    AUD,
    TournamentDollars
}

public class Player
{
    public string Name;
}

public class TableInfo
{
    public string Name;
}

public class BettingRound
{
    public List<BettingAction> BettingActions;
}

public class BettingAction
{
    public abstract Money PotSizeAfter();
    public byte SeatNumber;
}

public class Fold : BettingAction { }
public class Call : BettingAction { }
public class BringIn : BettingAction { }
public class Complete : BettingAction { }
public class Bet : BettingAction
{
    public Money Amount;
}

public class Raise : Bet { }

The term Round is a little ambiguous. BettingRound makes it more obvious.
I don't see the need to have cards, name and potsize here. Potsize is a function of the actions and changes throughout the betting round.
Seats represent the game a little better than a list of players does as this allows you to represent the game state (stack sizes etc.) a little more obviously.
I don't see the need to make the flop, river cards explicitly assigned to rounds - just use a list of cards and some conventions. e.g. first three cards = flop... first betting round = flop. Use some extension methods for convenience of referring to the flop for holdem.
Use the ulong version of cards via conversion when you need to use it rather than cluttering your domain model.

This is how I see the model of a particular individual Game (i.e. 1 flop, river, turn etc.). There's still a bunch of work to do to model all games (e.g. limit games use small bet / big bet instead of blinds to define the stakes).

public class Card
{
public Suit Suit;
public Rank Rank;
public ulong ToCardMask();
}

public enum Suit
{
    Clubs,
    Diamonds,
    Hearts,
    Spades
}

public enum Rank
{
    Ace,
    Deuce,
    Trey,
    //...
    Jack,
    Queen,
    King
}

public class Game
{
    public GameInfo GameInfo;
    public TableInfo TableInfo;
    public List<BettingRound> BettingRounds;
    public List<Card> CommunityCards;
    public string Rawtext;
    public bool WithHero; //??
}

public static class GameExtensions
{
    public static BettingRound Flop(this Game game)
    {
        return game.BettingRounds[0];
    }

    public static List<Card> FlopCards(this Game game)
    {
        return game.CommunityCards.Take(3).ToList();
    }
}

public class GameInfo
{
    public GameType GameType;
    public GameBettingStructure BettingStructure; // Limit, PotLimit, NoLimit
    public Stakes Stakes; // e.g. { $1, $2 }
    public long Id;
    public List<Seat> Seats;
}

enum GameType // prob change to a class for extensibility
{
    HoldEm,
    Razz,
    StudHi,
    StudHiLo,
    OmahaHi,
    OmahaHiLo
}

enum GameBettingStructure
{
    Limit,
    PotLimit,
    NoLimit
}

class Stakes // probably needs some tweeking for stud games (e.g. bring-in ...)
{
    public Money Ante;
    public List<Money> Blinds;
}

public class Seat
{
    public Player Player;
    public Money InitialStackAmount;
    public Money FinalStackAmount; // convienience field can be calculated
    public List<Card> Hand;
}

class Money
{
    public decimal Amount;
    public Unit Unit;
}

enum Unit
{
    USD,
    EUR,
    AUD,
    TournamentDollars
}

public class Player
{
    public string Name;
}

public class TableInfo
{
    public string Name;
}

public class BettingRound
{
    public List<BettingAction> BettingActions;
}

public class BettingAction
{
    public abstract Money PotSizeAfter();
    public byte SeatNumber;
}

public class Fold : BettingAction { }
public class Call : BettingAction { }
public class BringIn : BettingAction { }
public class Complete : BettingAction { }
public class Bet : BettingAction
{
    public Money Amount;
}

public class Raise : Bet { }
捶死心动 2024-08-19 05:39:57

我不会将 Round 子类化为 FlopRound TurnRound 等,而是会在 round 和 Action 中使用 Street 属性。

静态公共枚举 Street {PREFLOP、FLOP、TURN、RIVER};

...

instead of SubClassing Round into FlopRound TurnRound etc, I would use a Street attribute within round, and within Action as well.

static public enum Street {PREFLOP, FLOP, TURN, RIVER};

...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文