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!