Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

A 13x13 Grid Poker Hand Chart Windows Forms User Control

4.60/5 (5 votes)
18 Oct 2010LGPL36 min read 33.1K   510  
A simple .NET control to display the 'standard' 13x13 grid for two card poker hands.

Introduction

I have been playing poker and writing home brew tools for a while, and recently started writing some for friends, mostly educational and analytical. I soon found there was need for a control that would allow me to display information about specific hands, in the standard 13x13 card chart.

And since this is actually pretty easy to code (much easier than I first thought), and I will use it lots, it made sense to code a generic hand chart that I could do pretty much whatever I wanted to. It occurred to me that others might find uses for such a control also, so here I present it.

It is a standard .NET Windows Forms control, written in C#. I find using it at the source level is extremely easy, and described below (basically, cut and paste into your project). But as it is not added to the toolbox (I don't have that much experience with .NET user controls; if there is a way, I would love to know), a few lines need to be added manually to the form you want the chart to appear in. I have also provided a Class Library DLL containing only the control, precompiled.

This is not meant to be an example of best-practices for control development, just a useful control to share.

Background

Properties of different Hold'em starting hands are easily and compactly displayed on a 13x13 grid, such as implemented in this control.

PokerChartDemoEmpty.jpg

PokerChartDemo.jpg

The top image shows a blank chart, as drawn at design time; the bottom one shows the chart with each pocket pair coloured Gold, with an X displayed.

Most poker players are familiar with these grids; for an example of specific usage, see the HU Nash Equilibrium Push/Fold charts here.

All cells above the diagonal represent suited hands, and those below unsuited.

Using the Code

Without the control in the Toolbox, adding a chart to a form (or other container) is a simple three step process!

  1. Create a Windows Forms application.
  2. Reference the DLL, or Copy and paste the two files PokerHandChart.cs and PokerHandChart.Designer.cs into your project (in the Solution Explorer).
    • If you copy the code, you may need to add a reference to System.Drawing.
  3. Add the following code to your form's .Designer.cs file - Yes, some need to go in the region marked 'Windows Form Designer generated code', which is generally not a good idea, but it is the only way to place the control on the form - well, without all the installation, etc. (I wrote this in VS 2008 Express) - which doesn't support User Control projects, but this solution is trivial. So, here is the code:
  4. Declare a member variable of type PokerHandChart at the class level, and place the remainder in the InitializeComponent() method, under the existing code:

C#
private PokerHandChartControl.PokerHandChart chart = null;

this.SuspendLayout();
// 1st and last line may already be there, 
// so just place next 4 lines between
            
this.chart = new PokerHandChartControl.PokerHandChart();
this.chart.Location = new System.Drawing.Point(10, 120);
this.chart.Size = new System.Drawing.Size(200, 200);   // (200, 100) wouldn't matter
this.Controls.Add(this.chart);
            
this.ResumeLayout(false);  

Note: The exact dimensions of size aren't important as the control automatically makes itself into a square - you can now move/resize the control in the Form Designer.

Also, adding this code will not interfere with adding other 'standard' controls placed on the form in the future. This code you entered will change (and be added to) when you resize/move the control on the form - this is just to get an instance on the form. The control draws an empty 13x13 grid with each row/col labelled with a card... Once this is done, switch to the Design tab for whichever form you just added the PokerHandChart to, and you will see an empty chart. You can now move and resize the chart control, and notice it always stays a square.

Note: Using the chart is about as easy as it possibly could be.

Each cell in the grid exposes properties to allow the display of the cell to be controlled. Each cell has a foreground and background colour, and 'DisplayValue' (a simple string - but it should fit within the cell!) which you can also change the font of (Note, name only, not size in this version, maybe later ;-)). Also, each cell has a 'Selected' tag, for use in selecting hands for your application (i.e., a Range) - at least flagging hands as having been selected, by the user or the control. The headers listing the cards also have a configurable colour and font (again, not font size).

Font sizes for both header row/column and each cell is a fixed fraction of the cell size, and as such will scale with the overall size of the chart.

Each cell value is displayed centered both horizontally and vertically in the chart.

Access to these properties is via a GridCell class exposing the properties.

One 'feature' I feel makes this chart control especially easy to work with is the indexer style accessor (get only) for each cell. There are three different indexer methods available.

The simplest is to use a string containing the hand, so for example, to get the cell representing AKs, we could use this["AKs"] - what could be easier! Well, often, we want to access many cells, often in a loop, and this is where the second indexer is very useful. It takes three parameters, being the two cards (as chars) and a boolean value to indicate suitedness. So again, to access AKs, we would use this['A', 'K', true]. Obviously, the bool is ignored for pocket pairs. Also, coding this['K', 'A', true] will return the very same GridCell object as before, so the class 'normalizes' the hands. So for specially local access to many cells, a this accessor combined with a loop (or two) is the easiest way. In fact, every pocket pair and suited hand cell can be accessed in a few lines of code (and the array/enumeration 'ranks' help with this).

C#
foreach (char r1 in Hand.ranks)
  foreach (char r2 in Hands.ranks)
        if ( Hands.indexFor(r1) >= Hands.indexFor(r2) ) {
            // So AA, AK but not KA, 
            //even though that would work it would
            //visit most cells more than once, wasteful...
            GridCell c = chart[r1, r2, suited]; 
            c.DisplayValue = "11.5"; 
            c.Foreground = Brushes.Blue; 
            c.CellFont = "Arial";
        }

(Make a cell display '11.5' with a blue background.) [Hands.indexFor(card) is a method to allow comparison of cards by rank.]

And finally, the cells can be accessed by standard numerical coordinates, with AA being (0,0), AKs (1, 0), etc. This is there for (semi)completeness; I haven't used it yet.

Each cell is created and maintained within the class automatically. All the developer needs to do is provide it with display data like colours, and some text or number to place in a cell.

The control also exports a Click style event, fired when the user clicks on one of the cells. The actual GridCell object is passed to any subscribed event listeners.

C#
chart.HandClickedEvent += HandClicked;

protected void HandClicked(object sender, GridCell hit_cell) { ... }

So the control can either be used to display static data, or be used to allow the user to select and choose hands/ranges.

There are a few enumerations for example, for all pocket pairs, and all broadway that make selecting these groups very easy:

C#
foreach (string s in HandEnumerations.PocketPairs)
    GridCell c = chart[s];    // Do something to each of these cells...

Note: After any changes are made to the chart, 'UPDATE()' must be invoked - either on the chart itself or the containing form/container!

The only enumeration factory methods which accept a parameter are the SuitedConnector/SuitedGapper, where you provide a lower bound for the hand collection (as in all SCs > 4?) and the distance of the gap.

Each cell can also be accessed sequentially using the enumerator, and some utility classes (i.e., HandEnumerations) are provided for convenience.

Enjoy!

History

  • Version 0.1.0 - None yet, but I plan to extend some of the 'helper' classes and add any features I think of ;-)

I wanted to see if there was much of a response to this idea before putting too much effort (other than what I require) into polishing this control, but there is plenty I can do (some refactoring/renaming/extending) if enough interest is shown... (for example, a 'Clear()' method is obviously missing now). So do look back for improvements!

Perhaps you could leave feature requests as comments?

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)