none
Good way to model a poker player? RRS feed

  • Question

  • I'm creating a poker application as a hobby projects. In it I will need to model poker players as objects. I don't know what a good way to do this. So I'm looking for advice on how to do this.

    Some of the properties that the poker player will have are:
    Cards (cardrank1, cardsuit1, cardrank2, cardsuit2, shortHandFormat, longHandFromat)
    Actions (raise, bet, fold, call, check and the $ amount of the action)
    Position (positions with regards to the raiser, with regards to the button, with regards to the big blind)

    Should I set all these "small" properties I have within paranthesises as simple (int, double, string) properties? Or should I create internal classes or something for "Cards", "Actions" and "Positions"?

    The strange thing is that I've read about five programming books and I still haven't picked this up. I've now just begun reading "Code Complete" after a recommendation in these forums. I hope it is the right book for me and will help me with these things.

    Any suggestions about how to structure my Player class or classes in general are welcome! As are suggestions on how I can learn to become better at how to structure my code.

    Thanks
    Sunday, September 16, 2007 8:10 PM

Answers

  • Good question.  I will address the three examples you list separately.

    Cards: Make a class Card that represents a single card (suit -- which would be of type Suit, an enum; and rank -- perhaps a struct where you could put methods to compare ranks).  Then I would make a class called Cards, which is a collection of Card objects.  I've only programmed in .NET 1.1, without generic lists, but I think that for this you will want to either inherit Cards from List<Card>, or make Cards contain a List<Card> (with the latter you could better limit the operations that could be done on the cards).  By making a Cards class instead of just using a List<Card> directly in the Player, it gives you a good place to put methods related to the set of cards as a collection (perhaps calculations of how good a hand is, comparing it to other hands, etc.)  Actually, you may want to name the class Hand instead of Cards since you wouldn't use it for all collections of cards (like those in the deck, on the table if applicable, etc.).

    The Player class should expose a Hand property of type Hand (it's okay to have the same name as long as it's not confusing).

    Actions:  It doesn't make sense for these to be properties.  I'm not quite sure how you want the application architecture to allow for actions to be done.  They need to be done in order (each player needs to check, bet, etc.), and they depend on what the other players have done.  It seems like the best way to structure this operation is by having the controller (maybe in a Game class) know the rules for who can bet when, and call a single method of the Player.  Maybe a method like:

    protected abstract BetAction GetBetAction(BetState state);

    Where BetState would encapsulate things like how much the player has to bet to stay in, what actions are allowed, etc.  BetAction would be a class that you should make look a lot like an enum except that Bet and Raise would have to be associated with an amount.

    Note that I made GetBetAction abstract -- this is using the assumption that you will want both a HumanPlayer and a ComputerPlayer.  GetBetAction for a human player would block (or at least wait a reasonable amount of time) until the person actually makes a decision using the UI -- you'll need to use threading to keep the UI responsive while Game waits for a response.

    Position:  These aren't instinsic properties of the player so much as they are properties of the player with respect to the current game state.  So I would put these types of computations into the Game class (if Game starts getting too big, you could make a Table class to hold these).  Game or Table will be able to know this because that is where the List<Player> is actually stored (assuming the list index corresponds with the table position).


    There are still a lot of decisions to make regarding the higher-level structure of the Game class.  If you would like any more opinions about making this, let us know.
    Sunday, September 16, 2007 10:53 PM

All replies

  • Good question.  I will address the three examples you list separately.

    Cards: Make a class Card that represents a single card (suit -- which would be of type Suit, an enum; and rank -- perhaps a struct where you could put methods to compare ranks).  Then I would make a class called Cards, which is a collection of Card objects.  I've only programmed in .NET 1.1, without generic lists, but I think that for this you will want to either inherit Cards from List<Card>, or make Cards contain a List<Card> (with the latter you could better limit the operations that could be done on the cards).  By making a Cards class instead of just using a List<Card> directly in the Player, it gives you a good place to put methods related to the set of cards as a collection (perhaps calculations of how good a hand is, comparing it to other hands, etc.)  Actually, you may want to name the class Hand instead of Cards since you wouldn't use it for all collections of cards (like those in the deck, on the table if applicable, etc.).

    The Player class should expose a Hand property of type Hand (it's okay to have the same name as long as it's not confusing).

    Actions:  It doesn't make sense for these to be properties.  I'm not quite sure how you want the application architecture to allow for actions to be done.  They need to be done in order (each player needs to check, bet, etc.), and they depend on what the other players have done.  It seems like the best way to structure this operation is by having the controller (maybe in a Game class) know the rules for who can bet when, and call a single method of the Player.  Maybe a method like:

    protected abstract BetAction GetBetAction(BetState state);

    Where BetState would encapsulate things like how much the player has to bet to stay in, what actions are allowed, etc.  BetAction would be a class that you should make look a lot like an enum except that Bet and Raise would have to be associated with an amount.

    Note that I made GetBetAction abstract -- this is using the assumption that you will want both a HumanPlayer and a ComputerPlayer.  GetBetAction for a human player would block (or at least wait a reasonable amount of time) until the person actually makes a decision using the UI -- you'll need to use threading to keep the UI responsive while Game waits for a response.

    Position:  These aren't instinsic properties of the player so much as they are properties of the player with respect to the current game state.  So I would put these types of computations into the Game class (if Game starts getting too big, you could make a Table class to hold these).  Game or Table will be able to know this because that is where the List<Player> is actually stored (assuming the list index corresponds with the table position).


    There are still a lot of decisions to make regarding the higher-level structure of the Game class.  If you would like any more opinions about making this, let us know.
    Sunday, September 16, 2007 10:53 PM
  • I have been working on writting a simple poker game in C#.net.  When the player clicks on the "Deal" button an object called Hand is created which is basically a random list of  5 Card objects.  Each card object in the hand object has two property fields (like any normal card) a suit and a faceValue. 

     

    To score a poker hand you need to go through the hand object and compare each card with each other card to check for pairs, three of a kinds, two pairs, full houses, etc.  Right now I'm using class variables (int type) starting at ace and going to king.  Then Im calling a method to go through the hand and whatever the faceValue of the card is the class variable that matches gets a 1 added.  (i.e.

     

    int Aces;

     

    exampleMethod()

    if (card.faceValue == "Ace")

    Aces += 1;

    }

     

     

    then Im calling another method to go through all of the class variables to check their values (i.e

     

    exampleMethod2()

    if (Aces == 2)

    text = "A Pair"

    else if (Aces == 3)

    text = "Three of a kind."

    etc.....

     

    Here is my question:

    Is there a better way to do these card comparisons?  Ive been trying all kinds of different things and I can't figure it out.

    Thanks in advance.

    Monday, December 24, 2007 4:45 PM
  • You might enjoy checking out work that others have done on evaluating poker hands. The most often used poker evaluator is poker-eval. This source code is not for the newcomer.  It makes heavy use of lookup tables and bitwise masking. But if you want to learn some serious power programming, check it out.

    http://pokersource.sourceforge.net/

     

    Here is another work which is based on poker-eval and is written in C#:

    http://www.codeproject.com/KB/game/pokerhandevaldoc.aspx?df=100&forumid=239484&exp=0&select=1856619

     

    It is also heavy on bitwise operations and table use, but if you want to just drop it into your application, you could write a simple converter to convert from your application's form of "hand" into the bitmap used by the evaluator.

     

    Tuesday, December 25, 2007 1:39 AM
  • Below is a fairly simple evaluator for evaluating 5-card poker.

     

    The evaluation code is not mine, I stole it from somewhere (long ago) and just converted it to C#. I didn't do any serious testing of it, but the C# changes were very minimal (basically I renamed some constants).

     

    I wrote the objects: Card, Hand, and Dealer to demonstrate how to use it.

     


    Code Block

    using System;

    public enum CardSuit
    {
        Club, Diamond, Heart, Spade
    }

    public struct Card : IComparable<Card>
    {
        private int index; // 0 .. 51

        public Card(int index)
        {
            if (index < 0 || index > 51)
                throw new ArgumentOutOfRangeException("index", "index must be 0 .. 51");
            this.index = index;
        }

        // 0 (Club) .. 3 (Spade)
        public CardSuit Suit { get { return (CardSuit)(index / 13); } }
        public int SuitIndex { get { return index / 13; } }
        public string SuitString { get { return Suit.ToString().Substring(0, 1); } }

        // 0 (2) .. 12 (A)
        public int RankIndex { get { return index % 13; } }
        public string RankString { get { return RankStrings[RankIndex]; } }

        public int Value { get { return index; } }

        public override bool Equals(object obj) { return obj.Equals(this.index); }

        public override int GetHashCode() { return this.index.GetHashCode(); }

        private static string[] RankStrings = new string[13] {
            "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"
        };

        public override string ToString() { return RankString + SuitString; }

        public int CompareTo(Card other) { return -this.RankIndex.CompareTo(other.RankIndex); }
    }

    public class Hand : IComparable<Hand>
    {
        private Card[] cards = new Card[5];

        public Hand(Dealer dealer)
        {
            for (int i = 0; i < 5; i++)
                cards[i] = dealer.GetCard();
        }

        public void Sort()
        {
            Array.Sort(cards);
        }

        public uint Score()
        {
            uint suitMask = PokerEvaluator.CardSuit[cards[0].Value]
                | PokerEvaluator.CardSuit[cards[1].Value]
                | PokerEvaluator.CardSuit[cards[2].Value]
                | PokerEvaluator.CardSuit[cards[3].Value]
                | PokerEvaluator.CardSuit[cards[4].Value];
            uint c0 = PokerEvaluator.CardMask[cards[0].Value];
            uint c1 = PokerEvaluator.CardMask[cards[1].Value];
            uint c2 = PokerEvaluator.CardMask[cards[2].Value];
            uint c3 = PokerEvaluator.CardMask[cards[3].Value];
            uint c4 = PokerEvaluator.CardMask[cards[4].Value];
            return PokerEvaluator.Score(c0, c1, c2, c3, c4, suitMask);
        }

        public int CompareTo(Hand other)
        {
            return Math.Sign((long)this.Score() - (long)other.Score());
        }

        public override string ToString()
        {
            return String.Format("{0} {1} {2} {3} {4}", cards[0], cards[1], cards[2], cards[3], cards[4]);
        }
    }

    public class Dealer
    {
        private int[] cards = new int[52];
        private int cardCount = 0;
        private static Random rand = new Random();

        public int CardCount { get { return cardCount; } }
        public Card GetCard()
        {
            if (cardCount == 0)
                throw new InvalidOperationException("Dealer has no more cards.");
            return new Card(cards[--cardCount]);
        }

        public Dealer()
        {
            int i;
            for (i = 0; i < 52; i++)
                cards[i] = i;
            // Knuth Shuffle
            for (i--; i >= 0; i--)
            {
                int rnd = rand.Next(i);
                int tmp = cards[i];
                cards[i] = cards[rnd];
                cards[rnd] = tmp;
            }
            cardCount = 52;
        }
    }

    public enum PokerRank
    {
        None = 0,
        Pair = 1,
        TwoPair = 2,
        ThreeKind = 3,
        Straight = 4,
        Flush = 5,
        FullHouse = 6,
        FourKind = 7,
        StraightFlush = 8,
        RoyalFlush = 9,
    }

    public static class PokerEvaluator
    {
        private const int ScoreShift = 27;
        private const int SubScoreShift = 13;

        private const uint StraightFlushScore = (8U << ScoreShift);
        private const uint FourKindScore = (7U << ScoreShift);
        private const uint FullHouseScore = (6U << ScoreShift);
        private const uint FlushScore = (5U << ScoreShift);
        private const uint StraightScore = (4U << ScoreShift);
        private const uint ThreeKindScore = (3U << ScoreShift);
        private const uint TwoPairScore = (2U << ScoreShift);
        private const uint TwoKindScore = (1U << ScoreShift);
        private const uint OnePairScore = TwoKindScore;

        public static PokerRank GetPokerRank(uint score)
        {
            // is it royal?
            if (score == 0x40001F00)
                return PokerRank.RoyalFlush;
            return (PokerRank)(score >> ScoreShift);
        }

        public static uint[] CardSuit = new uint[52] {
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
         4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
         8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
        };

        public static uint[] CardMask = new uint[52] {
         1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000,
         1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000,
         1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000,
         1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000
        };

        public static uint Score(uint c0, uint c1, uint c2, uint c3, uint c4, uint suitMask)
        {
            // Each card must be in the form of the power of two CardMask (above)

            // suitMask contains a bit set for each suit that appears in the hand
            //  this algorithm doesn't care which bit represents which suit

            suitMask = suitMask & (suitMask - 1);

            // suitMask now contains a zero if all cards are same suit, or non-zero if mixed suits are present

            // Build masks of 1, 2, 3, and 4 of a kind.

            uint m1, m2, m3, m4;

            m1 = c0 | c1;
            m2 = c1 & c0;

            m2 |= c2 & m1;
            m1 |= c2;

            m2 |= c3 & m1;
            m1 |= c3;

            m2 |= c4 & m1;
            m1 |= c4;

            // m1 = c0 | c1 | c2 | c3 | c4
            //   in other words: m1 has a bit set for each rank that appears in the hand
            // m2 = (c0 & c1) | ((c0|c1) & c2) | ((c0|c1|c2) & c3) | ((c0|c1|c2|c3) & c4)
            //   in other words: m2 has a bit set for each rank that appears more than once in the hand

            if (m2 == 0) // there are no duplicate ranks
            {
                // Deal with the bicycle/wheel 5,4,3,2,Ace straight
                if (m1 == 0x100F)
                {
                    if (suitMask != 0)
                        return StraightScore + 0xF;
                    return StraightFlushScore + 0xF;
                }

                // Is the mask a sequence of 1 bits?

                uint z = m1 ^ (m1 & (m1 - 1));
                uint y = (z << 5) - z;

                if (y == m1)
                {
                    if (suitMask != 0)
                        return StraightScore + m1;
                    return StraightFlushScore + m1;
                }

                if (suitMask != 0)
                    return m1; // Nothing
                return FlushScore + m1;
            }

            m1 = c0 | c1;
            m2 = c1 & c0;

            m3 = c2 & m2;
            m2 |= c2 & m1;
            m1 |= c2;

            m4 = c3 & m3;
            m3 |= c3 & m2;
            m2 |= c3 & m1;
            m1 |= c3;

            m4 |= c4 & m3;
            m3 |= c4 & m2;
            m2 |= c4 & m1;
            m1 |= c4;

            m1 &= ~m2;

            // m3 = mask of 3 or 4 of a kind.
            // m4 = mask of 4 of a kind.

            if (m3 == 0)
            {
                if ((m2 & (m2 - 1)) == 0)
                    return TwoKindScore + (m2 << SubScoreShift) + m1;
                return TwoPairScore + (m2 << SubScoreShift) + m1;
            }

            m2 &= ~m3;

            if (m4 == 0)
            {
                if (m2 == 0)
                    return ThreeKindScore + (m3 << SubScoreShift) + m1;
                return FullHouseScore + (m3 << SubScoreShift) + m2;
            }

            return FourKindScore + (m4 << SubScoreShift) + m1;
        }
    }

     

     

     

    Tuesday, December 25, 2007 7:35 AM
  • Here's a simple form to demonstrate it:

     

    Create a form with a button and three text boxes on it. Name the text boxes: textBoxHand1, textBoxScore1, textBoxScoreHex1 (The "1" is left on the names because I created four sets of the three and dealt out four hands at a time, but you can do however many you like).

     

    Code Block

    using System;
    using System.Windows.Forms;

    namespace WindowsFormsApplication7
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }

            private void button1_Click(object sender, EventArgs e)
            {
                Dealer dealer = new Dealer();
                Hand hand1 = new Hand(dealer);
                hand1.Sort();
                uint score1 = hand1.Score();
                textBoxHand1.Text = hand1.ToString();
                textBoxScore1.Text = score1.ToString();
                textBoxScoreHex1.Text = "0x" + score1.ToString("X8");
            }
        }
    }

     

     

    Tuesday, December 25, 2007 8:01 AM
  • listboxs

    using System;

    using System.Data;

    using System.Configuration;

    using System.Web;

    using System.Web.Security;

    using System.Web.UI;

    using System.Web.UI.WebControls;

    using System.Web.UI.WebControls.WebParts;

    using System.Web.UI.HtmlControls;

    public partial class _Default : System.Web.UI.Page

    {

    protected void Page_Load(object sender, EventArgs e)

    {

    }

    protected void Btn_add_Click(object sender, EventArgs e)

    {

    lstData.Items.Add(new ListItem(txtNewText.Text,txtNewValue.Text));

    }

    protected void btn_clear_Click(object sender, EventArgs e)

    {

    txtNewText.Text = "";

    txtNewValue.Text = "";

    lstData.Items.Clear();

    }

    protected void Button1_Click(object sender, EventArgs e)

    {

    lstData.Items.RemoveAt(lstData.SelectedIndex);

    }

    }

     

    update

    using System;

    using System.Data;

    using System.Configuration;

    using System.Collections;

    using System.Web;

    using System.Web.Security;

    using System.Web.UI;

    using System.Web.UI.WebControls;

    using System.Web.UI.WebControls.WebParts;

    using System.Web.UI.HtmlControls;

    using System.Data.SqlClient;

    public partial class default : System.Web.UI.Page

    {

    protected void Page_Load(object sender, EventArgs e)

    {

    }

    protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)

    {

    GridViewRow gr = GridView1.SelectedRow;

    txtcode.Text = gr.Cells[1].Text;

    txtdesc.Text = gr.Cells[2].Text;

    txtprice.Text = gr.Cells[3].Text;

    }

    protected void btnUpdate_Click(object sender, EventArgs e)

    {

    try

    {

    SqlConnection con = new SqlConnection("server");

    con.Open();

    SqlCommand cmd = new SqlCommand();

    cmd.Connection = con;

    cmd.CommandType = CommandType.Text;

    cmd.CommandText = "Update exam set price = @price where product_code = @code";

    cmd.Parameters.add(new SqlParameter("@code", SqlDbType.Int)).value = (txtcode.Text);

    cmd.Parameters.add(new SqlParameter("@price", SqlDbType.Decimal)).value = (Decimal)double.parse(txtprice.Text);

    cmd.EndExecuteNonQuery();

    con.Close();

    }

    catch(Exception ee)

    {

    lblmsg.Text = Exception ee;

    }

    }

    }

    payrole

    using System;

    using System.Data;

    using System.Configuration;

    using System.Collections;

    using System.Web;

    using System.Web.Security;

    using System.Web.UI;

    using System.Web.UI.WebControls;

    using System.Web.UI.WebControls.WebParts;

    using System.Web.UI.HtmlControls;

    public partial class Page6__Payroll_ : System.Web.UI.Page

    {

    protected void Page_Load(object sender, EventArgs e)

    {

    }

    protected void btnCalc_Click(object sender, EventArgs e)

    {

    double hrs, prate, result,taxrate,taxdue, npay ;

    hrs = Double.Parse(txtHours.Text);

    prate = Double.Parse(txtPayRate.Text);

    result = hrs * prate;

    lblGrossPay.Text = result.ToString();

    taxrate = Double.Parse(txTaxRate.Text);

    taxdue = result / taxrate;

    npay = taxdue - result;

    txTaxDue.Text = taxdue.ToString();

    txtNetPay.Text = npay.ToString();

    }

    }

    payrole2

    using System;

    using System.Data;

    using System.Configuration;

    using System.Collections;

    using System.Web;

    using System.Web.Security;

    using System.Web.UI;

    using System.Web.UI.WebControls;

    using System.Web.UI.WebControls.WebParts;

    using System.Web.UI.HtmlControls;

    public partial class Page7_Payroll2_ : System.Web.UI.Page

    {

    protected void Page_Load(object sender, EventArgs e)

    {

    }

    protected void btnCalc_Click(object sender, EventArgs e)

    {

    String name;

    Double rate, normal, otime, dtime, expenses, taxrate, taxcredit, gpay, npay,taxdue;

     

    name = txtName.Text;

    rate = Double.Parse(txtRate.Text);

    normal = Double.Parse(txtNormal.Text);

    otime = Double.Parse(txtOTime.Text);

    dtime = Double.Parse(txtDTime.Text);

    expenses = Double.Parse(txtExpenses.Text);

    taxrate = Double.Parse(txtRate.Text);

    taxcredit = Double.Parse(txtTaxCredit.Text);

    otime = normal * .5;

    dtime = normal * 2;

    gpay = normal + otime + dtime - taxrate - taxcredit;

    lblGrossPay.Text = gpay.ToString();

    taxdue = gpay - taxrate - taxcredit;

    lblTaxDue.Text = taxdue.ToString();

    npay = gpay - taxdue;

    lblNetPay.Text = npay.ToString();

     

    }

    }

     

     

    Sunday, May 18, 2008 8:46 PM