Tuesday, April 14, 2015

Creating a PokerBot using a C# WinForms Application Part4

Hello again. In this part we will work with the image windows we captured last time, and loading and comparing bitmaps of the 52 cards to see which cards are in play/your hand. I'm also going to include a few bitmaps that will help save time searching shown below:
 







For the community cards, by searching for the tops of the cards within the community cards window its possible to determine the amount of cards that are in play (3,4, or 5). This will make searching faster because the program will know how many cards to look for.

The fourth image is searched for within your hand window to determine if you have any cards in your hand at the current time. This will save a search through 52 cards if you are currently folded or out of play.

I've included all the bitmaps for the 52 cards and the four images above in the zip file below:
Bitmaps.zip

Since the program will be searching for these Bitmaps quite often, it will be beneficial to load them into memory at program start up. To do this, you're going to need to put that unzipped Bitmaps Folder directly into your C# Project's Debug Directory
Default:(C:\Users\YOURNAME\Documents\Visual Studio 201X\Projects\YOUR_PROJECT_NAME\YOUR_PROJECT_NAME\bin\Debug)

As we import the bitmaps we want to keep the filenames attached with them for later identification.
IE) So you know that if the S7.bmp was found within your hand region window on the screen then you know the Seven of Spades was one of the cards in your hand.

To do this, in the CardDetection.cs we will add a new Struct called NamedBitmap:

public struct NamedBitmap
{
    public Bitmap bitmap;
    public string name;
}

Now we will add two seperate Lists of NamedBitmaps to the CardDetection.cs. One list for the Card Bitmaps, and another List for the other four bitmaps shown above:

private static List<namedbitmap> cardPics = new List<namedbitmap>();
private static List<namedbitmap> tablePics = new List<namedbitmap>();

When the Program starts we want to populate those lists with all the Bitmap and filename data, so in the CardDetection.cs class, we will create an Initialize function. This function will only be called once from the main Form's On_Load Event to get all the Bitmap information into memory for quick comparison later. Here is the Initialize function:

public static void Initialize()
{
    //Set Path of the Bitmap folder
    string bitmapsPath = Application.StartupPath + "\\Bitmaps\\";            

    //Load the Bitmaps into carPics, and tablePics Lists
    foreach (string s in Directory.GetFiles(bitmapsPath))
    {
        if (s.ToUpper().EndsWith(".BMP"))
        {
            string[] splitter = s.Split('\\');
            string fileName = splitter[splitter.Length - 1];

            string[] splitter2 = fileName.Split('.');

            //if filename Length is 2 or 3.
            if (splitter2[0].Length == 2 || splitter2[0].Length == 3)
            {
                //File name was 2 or 3 chars long, its a card bitmap
                NamedBitmap b = new NamedBitmap();
                b.name = splitter2[0];
                b.bitmap = new Bitmap(s);
                cardPics.Add(b);
            }
            else
            {
                //File name was not 2 chars long, its a table bitmap
                NamedBitmap b = new NamedBitmap();
                b.name = splitter2[0];
                b.bitmap = new Bitmap(s);
                tablePics.Add(b);
            }
        }                
    }
}

Basically it searches through the Bitmaps folder, and puts any files that have filenames 2 or 3 characters long into the cardPics list, and anything else into the tablePics list.

Now we will create a basic function to fetch a bitmap out of the cardList or tableList with a given name. You'll see why we're creating this function shortly:

private static Bitmap RetrieveBitmap(string name)
{
    //Check cardPics List
    foreach (NamedBitmap nb in cardPics)
    {
        if (nb.name.ToUpper() == name.ToUpper())
        {
            return nb.bitmap;
        }
    }

    //Check tablePics List
    foreach (NamedBitmap nb in tablePics)
    {
        if (nb.name.ToUpper() == name.ToUpper())
        {
            return nb.bitmap;
        }
    }

    return null;
}

Also let's create a few functions that allow us to convert from the card filename strings (S7, DQ, H2, etc....) into the Suit, and Rank enums that we defined in Part 3, because that is the more common form of card data that we will store throughout the rest of the program. Here are two functions for doing that:

// Converts a string "S", "C", "H", or "D" into the coresponding Suit Enum
private static Suit ConvertStringToSuit(string stringSuit)
{
    Suit cardSuit;

    //Figure out which suit the found card was
    switch (stringSuit)
    {
        case "S":
            cardSuit = Suit.Spades;
            break;
        case "C":
            cardSuit = Suit.Clubs;
            break;
        case "H":
            cardSuit = Suit.Hearts;
            break;
        case "D":
            cardSuit = Suit.Diamonds;
            break;
        default:
            cardSuit = Suit.NotFound;
            break;
    }

    return cardSuit;
}


// Converts a string "2", "3", "4"... "J", "Q", "K", or "A"  into the coresponding Rank Enum
private static Rank ConvertStringToRank(string stringRank)
{
    Rank cardRank;

    //Figure out which rank the found card was
    switch (stringRank)
    {
        case "2":
            cardRank = Rank.Two;
            break;
        case "3":
            cardRank = Rank.Three;
            break;
        case "4":
            cardRank = Rank.Four;
            break;
        case "5":
            cardRank = Rank.Five;
            break;
        case "6":
            cardRank = Rank.Six;
            break;
        case "7":
            cardRank = Rank.Seven;
            break;
        case "8":
            cardRank = Rank.Eight;
            break;
        case "9":
            cardRank = Rank.Nine;
            break;
        case "10":
            cardRank = Rank.Ten;
            break;
        case "J":
            cardRank = Rank.Jack;
            break;
        case "Q":
            cardRank = Rank.Queen;
            break;
        case "K":
            cardRank = Rank.King;
            break;
        case "A":
            cardRank = Rank.Ace;
            break;
        default:
            cardRank = Rank.NotFound;
            break;
    }

    return cardRank;
}

Now, finally coming around to the main goal we set out to do for this portion of the code. Lets make one function that will return a Card[] array of two Cards that were found in your hand, and another function that will return a Card[] array of either 3, 4 or 5 cards that are community cards. Both functions will return null if no cards were found in those places.

The function for returning the Card[] in your hand is here:
public static Card[] RetieveYourHand()
{
    Bitmap currentHand = CaptureYourHandImage();

    //First check to see if there are no cards in your hand to avoid unnessicary searching.
    if (SearchBitmap(currentHand, RetrieveBitmap("NoHand")))
    {
        return null;
    }
    else
    {
        int foundCount = 0;
        int cardsToSearch = 2;
        Card[] hand = new Card[2];

        foreach (NamedBitmap cardImage in cardPics)
        {
            if (SearchBitmap(currentHand, cardImage.bitmap))
            {
                //Split the Bitmap filenames up into two seperate strings. EX) filename == S7, then stringSuit = "S", and stringRank = "7"
                string stringSuit = cardImage.name.Substring(0, 1);
                string stringRank = cardImage.name.Substring(1);

                //Get the Bitmap filenames into Suit and Rank Enums. EX) filename == S7, then cardSuit = Suit.Spades, and cardRank = Rank.Seven
                Suit cardSuit = ConvertStringToSuit(stringSuit);
                Rank cardRank = ConvertStringToRank(stringRank);

                hand[foundCount] = new Card(cardSuit, cardRank);
                foundCount++;

                //If you found all the cards, stop searching
                if (foundCount == cardsToSearch)
                {
                    break;
                }
            }
        }
        return hand;
    }
}


The function for returning the Card[] array for community cards is here:
public static Card[] RetieveCommunityCards()
{
    Bitmap currentCommunityCards = CaptureCommunityCardsImage();

    int foundCount = 0;
    int cardsToSearch;
    Card[] hand;

    //Check to see how many cards you need to find, if no cards are on the table return null.
    if (SearchBitmap(currentCommunityCards, RetrieveBitmap("3cards")))
    {
        cardsToSearch = 3;
        hand = new Card[3];
    }
    else if (SearchBitmap(currentCommunityCards, RetrieveBitmap("4cards")))
    {
        cardsToSearch = 4;
        hand = new Card[4];
    }
    else if (SearchBitmap(currentCommunityCards, RetrieveBitmap("5cards")))
    {
        cardsToSearch = 5;
        hand = new Card[5];
    }
    else
    {
        return null;
    }

    foreach (NamedBitmap cardImage in cardPics)
    {
        if (SearchBitmap(currentCommunityCards, cardImage.bitmap))
        {
            //Split the Bitmap filenames up into two seperate strings. EX) filename == S7, then stringSuit = "S", and stringRank = "7"
            string stringSuit = cardImage.name.Substring(0, 1);
            string stringRank = cardImage.name.Substring(1);

            //Get the Bitmap filenames into Suit and Rank Enums. EX) filename == S7, then cardSuit = Suit.Spades, and cardRank = Rank.Seven
            Suit cardSuit = ConvertStringToSuit(stringSuit);
            Rank cardRank = ConvertStringToRank(stringRank);

            hand[foundCount] = new Card(cardSuit, cardRank);
            foundCount++;

            //If you found all the cards, stop searching
            if (foundCount == cardsToSearch)
            {
                break;
            }
        }
    }
    return hand;
}

These methods are very similar. Basically they are putting together everything that we've done up to this point into two neat functions that can be used later on. The functions begin by determining how many cards / if any at all are on the table. Then they loop through the cardPics list that we filled with our card bitmaps searching the specified card window (using the CaptureYourHandImage(), and CaptureCommunityCardsImage() functions we made in Part3)  for each card until it finds the amount of cards its looking for. It converts the Bitmap filenames which were located into Suits and Ranks that are turned into Card objects which are passed out of the function as a Card[] array.

To test that these functions are working properly you can add some quick test code on a button in the main Form. Also, don't forget to call the CardDetection.Initiailize() function in your Form's onLoad event so that the bitMap Lists populate:

private void Form1_Load(object sender, EventArgs e)
{
    CardDetection.Initialize();
} 

private void button1_Click(object sender, EventArgs e)
{
    Card[] hand = CardDetection.RetieveYourHand();
    Card[] comm = CardDetection.RetieveCommunityCards();            
}

Add a breakpoint to the end of the button click function, and you can see whats inside the hand, and comm Card[] arrays. For example, I clicked the button when this was showing on the Pokerstars Screen:



and everything worked because I saw this in the Visual Studio Debug local variables window, when the code was finished and hit the breakpoint I placed at the end:


That pretty much wraps things up for this portion of the code. Now our bot has some eyes, no brains yet, but eyes are good. We may come back to some more image detection later on to get things like the pot and player chip counts to help our bots make more informed decisions, but for now this is a good starting point and in the next posts we can start to touch on the AI side of things and start building our brain.

Sunday, April 5, 2015

Creating a PokerBot using a C# WinForms Application Part3

Before we start diving into too much more code I want to take a step back and give the code a little more structure. Building a good foundation will make it easier as things move forward and the bot becomes more complex.

I'll start by creating a new class called CardDetection.cs. I'm going to put any functions and variables related to the bot's Card Detection into this class. This class is going to be static because there is never going to be a need to create multiple instances of Card Detection. I'm going to move the SearchBitmap and CaptureApplication functions that were shown earlier into this class. So far in the CardDetection.cs class we have:


static class CardDetection
{
     public static Bitmap CaptureApplication(string procName)

     public static bool SearchBitmap(Bitmap bigBmp, Bitmap smallBmp)

     private class User32      //Used by the Capture Application Function
}

My ultimate goal for this class is to have functions that can be called by the main Form to return the information of what cards are currently in play. To reduce all the complexity of capturing bitmaps, searching through them, and matching appropriate cards to their comparisons to a single line of code that simply returns the cards that are on the table.

So first off, I'd like to create a Card.cs class. This will be a simple class that contains a Suit and a Rank variable, but will be used to pass Card Information around the program whenever it is needed. Unlike the CardDetection class, this class will not be static, as there will need to be multiple card variables in use by the program. In addition to the Card class, I'll create some Enums:

public enum Suit
{
    NotFound = 0,
    Diamonds = 1,
    Hearts = 2,
    Clubs = 3,
    Spades = 4,
}

public enum Rank
{
    NotFound = 0,
    Two = 2,
    Three = 3,
    Four = 4,
    Five = 5,
    Six = 6,
    Seven = 7,
    Eight = 8,
    Nine = 9,
    Ten = 10,
    Jack = 11,
    Queen = 12,
    King = 13,
    Ace = 14,
}

The Card.cs Class looks like this:

class Card
{
    public Suit Suit { get; private set; }
    public Rank Rank { get; private set; }

    public Card(Suit suit, Rank rank)
    {
        Suit = suit;
        Rank = rank;
    }
}

Here is an example of how the Card variable can now be used in the program:

//Define your hand, an array of 2 Cards.
Card[] YourHand = new Card[2];

//Define the first Card in your hand, the Ace of Spaces
YourHand[0] = new Card(Suit.Spades, Rank.Ace);

//Define the second Card in your hand, the Ace of Diamonds
YourHand[1] = new Card(Suit.Diamonds, Rank.Ace);

Now that we have the Card class setup, we can continue working on the CardDetection class. I mentioned earilier it will be better to narrow down the areas on the table where the program is looking for cards to make the searching quicker and more efficient. To do this we will put a function in to return a Bitmap of a partial area of the Poker Stars screen defined by a rectagle. The function is shown below:

private static Bitmap CopyPartialBitmap(Bitmap srcBitmap, Rectangle section)
{
     // Create the new bitmap and associated graphics object
     Bitmap bmp = new Bitmap(section.Width, section.Height);
     Graphics g = Graphics.FromImage(bmp);

     // Draw the specified section of the source bitmap to the new one
     g.DrawImage(srcBitmap, 0, 0, section, GraphicsUnit.Pixel);

     // Clean up
     g.Dispose();

     // Return the bitmap
     return bmp;
}

Now we would need to define some rectangles on the Pokerstars window to narrow where we are going to search for cards. The picture below shows on orange rectangle defining where to look for your cards, and a red rectangle defining where to look for community cards:

I created two functions in the CardDetection class that capture a bitmap of the two rectangles above:

private static Bitmap CaptureYourHandImage()
{
    return CopyPartialBitmap(CaptureApplication("PokerStars"), new Rectangle(352, 365, 90, 53));
}

private static Bitmap CaptureCommunityCardsImage()
{
    return CopyPartialBitmap(CaptureApplication("PokerStars"), new Rectangle(262, 204, 282, 74));
}


Next post we will look at combining the elements laid out in this post and create functions that simply return Card[] by searching through the newly defined card windows against all 52 card bitmaps to determine which cards (if any) are there.

Sunday, March 29, 2015

Creating a PokerBot using a C# WinForms Application Part2

Hello again.
Last time we managed to get a screenshot of the PokerStars window, now its a matter of figuring out whether there's any Cards on the Table. For this we need a function that can determine whether a smaller bitmap is contained in a larger bitmap.

I found just what I needed here: Finding a Bitmap contained inside another Bitmap.
Note, before you can use the author's code you need to allow Unsafe Code in your application because the author used pointers, which are considered unsafe in C#. To do this, right click on your project in Visual Studio, and go to properties:









Now on the Build Tab, Click the "Allow Unsafe Code" checkbox, then save-all, and rebuild your project by going Build > Build Solution.


Now you should be able to use the author's code. I modifyed the author's function slightly because I want the function to return a bool rather than a position, and I want to always use 0% tolerance because I'm searching for exact images. The function is shown below:

private bool SearchBitmap(Bitmap bigBmp, Bitmap smallBmp)
        {
            double tolerance = 0d;
            BitmapData smallData =
              smallBmp.LockBits(new Rectangle(0, 0, smallBmp.Width, smallBmp.Height),
                       System.Drawing.Imaging.ImageLockMode.ReadOnly,
                       System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            BitmapData bigData =
              bigBmp.LockBits(new Rectangle(0, 0, bigBmp.Width, bigBmp.Height),
                       System.Drawing.Imaging.ImageLockMode.ReadOnly,
                       System.Drawing.Imaging.PixelFormat.Format24bppRgb);

            int smallStride = smallData.Stride;
            int bigStride = bigData.Stride;

            int bigWidth = bigBmp.Width;
            int bigHeight = bigBmp.Height - smallBmp.Height + 1;
            int smallWidth = smallBmp.Width * 3;
            int smallHeight = smallBmp.Height;

            Rectangle location = Rectangle.Empty;
            int margin = Convert.ToInt32(255.0 * tolerance);

            unsafe
            {
                byte* pSmall = (byte*)(void*)smallData.Scan0;
                byte* pBig = (byte*)(void*)bigData.Scan0;

                int smallOffset = smallStride - smallBmp.Width * 3;
                int bigOffset = bigStride - bigBmp.Width * 3;

                bool matchFound = true;

                for (int y = 0; y < bigHeight; y++)
                {
                    for (int x = 0; x < bigWidth; x++)
                    {
                        byte* pBigBackup = pBig;
                        byte* pSmallBackup = pSmall;

                        //Look for the small picture.
                        for (int i = 0; i < smallHeight; i++)
                        {
                            int j = 0;
                            matchFound = true;
                            for (j = 0; j < smallWidth; j++)
                            {
                                //With tolerance: pSmall value should be between margins.
                                int inf = pBig[0] - margin;
                                int sup = pBig[0] + margin;
                                if (sup < pSmall[0] || inf > pSmall[0])
                                {
                                    matchFound = false;
                                    break;
                                }

                                pBig++;
                                pSmall++;
                            }

                            if (!matchFound) break;

                            //We restore the pointers.
                            pSmall = pSmallBackup;
                            pBig = pBigBackup;

                            //Next rows of the small and big pictures.
                            pSmall += smallStride * (1 + i);
                            pBig += bigStride * (1 + i);
                        }

                        //If match found, we return.
                        if (matchFound)
                        {
                            location.X = x;
                            location.Y = y;
                            location.Width = smallBmp.Width;
                            location.Height = smallBmp.Height;
                            break;
                        }
                        //If no match found, we restore the pointers and continue.
                        else
                        {
                            pBig = pBigBackup;
                            pSmall = pSmallBackup;
                            pBig += 3;
                        }
                    }

                    if (matchFound) break;

                    pBig += bigOffset;
                }
            }

            bigBmp.UnlockBits(bigData);
            smallBmp.UnlockBits(smallData);

            if (location.Width != 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
To use this function, pass in larger bitmap you are trying to search through, and the smaller bitmap you are trying to find, and the function will return true if the smaller Bitmap was found within the larger one, or false if it was not found.

To Test whether the code is working properly we will use this table image PokerTable.bmp, and run two different tests. The first Test will check if a seven of spades exists on the table, and the second test will check if the seven of clubs exists on the table. To run the image comparisons, you'll need the bitmaps of the individual cards I attached in the last post: 52CardBitmaps.zip

For these tests, I'm going to put my PokerTable.bmp, S7.bmp, and C7.bmp into the Debug folder in my C# Project.

PokerTable.bmp
C7.bmp
S7.bmp




Now in Visual studio, I'll create a new button to run these tests, and in the buttons Click Event Code put this:
private void button2_Click(object sender, EventArgs e)
        {
            //Define Bitmap Images from filepaths
            Bitmap PokerTable = new Bitmap(Application.StartupPath + "//PokerTable.bmp");
            Bitmap S7 = new Bitmap(Application.StartupPath + "//S7.bmp");
            Bitmap C7 = new Bitmap(Application.StartupPath + "//C7.bmp");

            //Check for Seven of Spades on the table
            if (SearchBitmap(PokerTable, S7))
            {
                Console.WriteLine("Seven of Spades was found on the table.");
            }
            else
            {
                Console.WriteLine("Seven of Spades was not found on the table.");
            }

            //Check for Seven of Clubs on the table
            if (SearchBitmap(PokerTable, C7))
            {
                Console.WriteLine("Seven of Clubs was found on the table.");
            }
            else
            {
                Console.WriteLine("Seven of Clubs was not found on the table.");
            }            
        }

When you click this button, you should see the following Console output:

This verifys that the search Code is working properly. The program matched up the Bitmap of the Seven of spades with the one on the table, and failed to see the seven of clubs.

In the next Post we will shrink down the area where the program is looking for cards to make the search more effiecient. Also it will give the program the ability to tell the difference between cards in your hand, and shared cards.




Saturday, March 28, 2015

Creating a PokerBot using a C# WinForms Application Part1


Welcome to a C# development series about creating a poker bot to compete against players online for play money. This will be partly an experimental post, trying different techniques to make the bot's AI, as well as an instructional series showing different snippits of Code Iv'e put together to make the bot work.

The first thing I've decided to start with is to setup how the bot can determine which cards are in play. This seems like a logical place to start because a bot playing blind wouln't be too great :P This can be done by searching through a library of 52 bitmaps of each card to see which exists within a certain area of the game screen. These bitmaps were obtained by manually taking screenshots of multiple games in a PartyPoker table, I've attached them here so you don't have to go through the work:
52CardBitmaps.zip

Here's a bit of code to take a screenshot of the current game screen. I was using PokerStars in this example, but you could pretty much do the same for any game:
using System.Diagnostics;
using System.Runtime.InteropServices;

private class User32
        {
            [StructLayout(LayoutKind.Sequential)]
            public struct Rect
            {
                public int left;
                public int top;
                public int right;
                public int bottom;
            }

            [DllImport("user32.dll")]
            public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);
            [DllImport("user32.dll")]
            public static extern bool SetForegroundWindow(IntPtr hWnd);  
            
        }

public Bitmap CaptureApplication(string procName)
        {
            var proc = Process.GetProcessesByName(procName)[0];
            
            var rect = new User32.Rect();
            User32.GetWindowRect(proc.MainWindowHandle, ref rect);

            int width = rect.right - rect.left;
            int height = rect.bottom - rect.top;

            var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
            Graphics graphics = Graphics.FromImage(bmp);
            User32.SetForegroundWindow(proc.MainWindowHandle);
            graphics.CopyFromScreen(rect.left, rect.top, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);

            return bmp;            
        }

I started with this function that captures a screenshot of a windows application and returns it as a Bitmap. This code basically gets the height, width, and location of the Pokerstars window and captures that area of the screen using Windows CopyFromScreen() method. the process name string that you need can be found in task manager:

So to call the CaptureApplication for the PokerStars Process method the code would be:
CaptureApplication("PokerStars")
To test whether its working properly you can make a quick Form with a PictureBox and a button. In the buttons Click event code put this:
private void button1_Click(object sender, EventArgs e)
        {
            Bitmap PokerStarsWindow = CaptureApplication("PokerStars");
            pictureBox1.Width = PokerStarsWindow.Width;
            pictureBox1.Height = PokerStarsWindow.Height;
            pictureBox1.BackgroundImage = PokerStarsWindow;
        }

With the PokerStars window open, if you click the button you should see a live screenshot of the pokerstars window come up in your application like this:


Good start! Next time we will narrow down the windows in which the program is searching for cards, and show how to make a function that tells whether a smaller bitmap is contained in a larger bitmap for card identification.

Any comments are appriciated!