views:

323

answers:

6

Hi, 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?

+2  A: 

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

enum BettingAction
{
   Check,
   Bet,
   Call,
   Raise,
   Fold
}
msergeant
+1  A: 

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.

David Basarab
A: 

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.

unholysampler
A: 

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.

Jim B
Each Hand is represented by that object, Rounds is the representation of Preflop, Flop, Turn and River..
BeatMe
A: 

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 { }
Joshka
A: 

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};

...

Alix Martin