Poker Bot Principles

    image


    Attention:
    Do not take this article as a guide to action, remember that the use of bots is prohibited in all poker rooms and entails the blocking of the account with the withdrawal of all money in the account. This article will not have ready-to-use code, so as not to make life easier for script kiddies, we will consider the basic principles and algorithms of the bot. A person familiar with programming, if desired, can still write such a program.

    The existence of winning poker bots has always been questioned; some poker rooms claim that their software generally prevents the possibility of using such programs. But anyone familiar with programming understands that writing the bot itself is not difficult, and there will always be a reaction against any protection. The most difficult (and therefore the most important) problem is the decision-making algorithm. Indeed, developing an algorithm that will bring a plus is not so simple, but it is not necessary. Now with a lot of different bonuses, rakeback and other offers from poker rooms, it’s enough for a bot to play zero or a weak minus, which is quite realistic for small limits.

    In general, the very first bot (more precisely, a poker program)) is considered "Orac", which was developed in the early 80s by the famous poker player Mike Caro, the author of the book "Sign Language". One of the features of the program was the ability to use timing tools - if the opponent thought for a long time, then his actions were more likely to be considered a bluff than if he acted quickly.

    It's not so difficult to find many ready-made bots on the Internet. From the simplest free instances, to bots with a large set of functions and the possibility of team play on several accounts at the price of $ 200. And this is only in public sources, it is not known what can be found on any specialized closed hacker forums and sites. According to rumors, a good winning bot costs from $ 1000, there are copies and $ 5000 each. Such programs are probably very good at hiding themselves and imitate human behavior as much as possible, it is quite possible to use neural networks to make decisions. In this article, we will not write a bot for $ 5k, we just try to understand the basic principles of how programs work easier. And there is no limit to further improvement.

    Bot Basics


    The main modules for the bot:
    • Receiving information - receiving information from a client, which is necessary for making a decision; includes - our cards, opponents' bets, stack sizes, button position, etc.
    • Decision making is an algorithm for deciding on the action of the bot, rather a logical task and a separate topic, so we will consider it in the next article in this series.
    • Simulation of user actions - imitation of user clicking buttons, imitation of mouse movements.

    In this article we will talk about input / output of information - the main software modules of the poker bot.

    Receiving the information


    Here are our main sources of information:
    • Log files - for each client it is individual, but often actions (dealt cards, player actions) are recorded in the log file, which can be constantly re-read from the disk and instantly receive the necessary information.
    • API messages - here you can find a lot of useful information, the most interesting part is the output of the text. Almost all poker clients have a text element on the table that combines the functions of a chat and an information window. If necessary, you can display all the information on the distribution (players' actions, their cards, table cards, etc.). It looks something like this:
      image
    • Hand history - when the option is enabled in the client, the history of all distributions in which the player participates will be recorded in a separate file. The problem is that this information can only be obtained after the fact, because it is recorded only after the end of the distribution itself.
    • Client screenshot - can be used to recognize both text (bets, players nicknames) and graphic information (cards, button position).

    Usually, all distribution information doesn’t fall into the log file, for example, from the Pokerstars client’s log file (C: \ Program Files \ PokerStars \ PokerStars.log.0) you can only find the cards that were given to us and the dealer’s position:
    MSG_TABLE_SUBSCR_ACTION
    MSG_TABLE_SUBSCR_DEALPLAYERCARDS
    sit0
    nCards=2
    sit1
    nCards=2
    sit2
    nCards=2
    sit3
    nCards=2
    sit4
    nCards=2
    sit5
    nCards=2
    dealerPos=3
    TableAnimation::dealPlayerCards
    MSG_TABLE_PLAYERCARDS 000C0878
    ::: 11c
    ::: 11d

    11s, 11d - our cards (JcJd), and the dealer is in 3rd place.

    The method with API messages is quite simple to implement and often with it you can get all the necessary information. To implement it, you need to use the DLL injection into the poker client process. The implemented DLL may be useful for us to simulate keystrokes and other information output. The main disadvantage of the injection is that it is difficult to hide such an impact on the client if he is trying to catch such attempts. But the program cannot perceive all implementations as hacking, because these methods use quite honest programs, for example, the well-known Punto Switcher.

    There are several ways to implement a DLL:
    1. Deployment through the registry.
    2. Using traps (hooks).
    3. Deployment using a remote stream
    4. Writing directly to memory using WriteProcessMemory (), more details can be found here .

    We will consider the simplest and most convenient approach - using traps. To do this, use the SetWindowsHookEx API function (idHook, lpfn, hMod, dwThreadId), where

    idHook - defines the type of capture procedure, for global interception it is necessary to use WH_CBT (to intercept keyboard messages, for example, you can use WH_KEYBOARD);

    lpfn - pointer to the interception procedure, which will be called every time during interception. In it we will catch the messages we need and perform the necessary actions;

    hMod - a handle to the DLL that contains the lpfn procedure.

    dwThreadId - thread identifier on which the interceptor is installed (0 for global interception).

    Our DLL must have a function for setting a trap and a function called when this trap is triggered:
    BOOL WINAPI SetHook() {
      g_hook = SetWindowsHookEx(WH_CBT, (HOOKPROC) CBTProc, g_hinstDll, 0);
      return (g_hook != NULL);
    }

    LRESULT WINAPI CBTProc(int nCode, WPARAM wParam, LPARAM lParam) {

      if (nCode < 0)
        return CallNextHookEx(g_hHook, nCode, wParam, lParam);
     
      if (nCode == HCBT_ACTIVATE)
      {
        //Что-нибудь сделать при активации окна
        //..
      }
      else if (nCode == HCBT_DESTROYWND)
      {
        //Что-нибудь сделать при закрытии окна
        //..
      }
      else if (nCode == HCBT_SETFOCUS)
      {
        //Что-нибудь сделать при получении фокуса
        //..
      }
     
      //Передаем управление следующим ловушкам в цепочке
      return(CallNextHookEx(g_hook, nCode, wParam, lParam));
    }

    * This source code was highlighted with Source Code Highlighter.

    When installing global interception, the DLL is embedded in every process in the system, so as not to occupy a lot of memory, you can split the boot process into two parts. First, a global DLL is introduced, which takes up a minimum of memory and can only determine in which process it is loaded. Using the LoadLibrary () function, it loads the second DLL in which the necessary functionality is implemented (card reading, logic, etc.).

    After interception, we can catch various API messages that are sent to the client. For example, when outputting to a Rich Edit element (can be used to organize a chat), the EM_STREAMIN message is used. And we can intercept it to receive the text displayed in the chat, and with it the distribution information. For each room, the element for text output may be individual, but the procedure is the same. In general, it is very useful to use the Spy ++ program to study the API messages sent to the client (most of you are familiar with it, it is included in the Visual Studio package) or an analogue. Using Spy ++, you can find out the titles of the windows we need and find out which API messages we need to intercept.

    Everything becomes complicated if the client uses some non-standard visual elements or non-standard methods for displaying information in them. In this case, you already need to use reverse engineering and look for this data in the process memory. Because all the same, all textual information is stored somewhere in memory in the form of strings, we only need to find where.

    If it is impossible to arrange an interception (the client blocks such attempts) or does not manage to find the information we need, we can use the method of capturing the screen and recognizing characters from it. But this method is better left as a last resort, because it is more time-consuming and requires more resources when working. Its main advantage is that this method will not be able to detect a poker client. You can generally run the poker bot on another computer (although the part responsible for keystrokes must be on the computer with the client, but for this part it is not necessary to use the DLL implementation), where the video from the computer screen with the poker client is transferred. You can still run the client under the virtual machine, and the bot under the main OS.

    Simulation of user actions


    When the bot presses buttons and other actions simulating the behavior of an ordinary player, we need to achieve maximum credibility. In this case, you need to use a random delay in the response of the bot, so as not to do all the actions immediately after the start of the move. From an article on RNG, you can find out that the poker room PokerStars uses user mouse movements to generate random numbers. At the same time, nothing prevents them from using this information to verify users (it is likely that other poker clients conduct such “surveillance” of their players). Therefore, it is important to make random mouse movements on the screen and move the cursor to the point where the buttons are pressed. You can still make random clicks outside the poker client window (on the desktop, taskbar).

    Therefore, it will optimally work with the mouse programmatically directly. There is an option to find the handle of the necessary buttons and send them a message using SendMessage (), but it is better to minimize the impact on the client itself, and do everything from the outside. It turns out that you need to find the local coordinates of the buttons in the window, for this you can use the same Spy ++. If you configure it to catch mouse messages in the poker client, then when you click on the desired area, we will get the local coordinates of the click in the window. Like that:
    <00227> 00120644 P WM_LBUTTONDOWN fWKeys:MK_BUTTON xPos:840 yPos:103
    So you can find the coordinates of the rectangle inside the button, from which you need to randomly select a point to click in order to simulate human behavior.

    To control the mouse, we will use the SendInput API function (UINT nInputs, LPINPUT pInputs, int cbSize) . An array of INPUT structures is passed to it , which contains sequential actions with the mouse and keyboard. This is the code for moving the mouse to a specific position and pressing its left button:
    //Координаты в окне клиента
    POINT coords;
    coords.x = 840;
    coords.y = 103;

    //Конвертируем в координаты экрана
    ClientToScreen(hWND, &coords);

    //Получаем разрешение экрана
    HDC hdc = GetDC(NULL);
    int screenWidth = GetDeviceCaps(hdc, HORZRES);
    int screenHeight= GetDeviceCaps(hdc, VERTRES);
    ReleaseDC(NULL, hdc);

    //Конвертируем координаты в глобальные
    double worldCoords = 65535 * coords.x;
    double buttonX = worldCoords / screenWidth;
    worldCoords = 65535 * coords.y;
    double buttonY = worldCoords / screenHeight;

    // Создаем массив структур INPUT
    INPUT input[3];
    MOUSEINPUT mouseInput;

    // Двигаем мышь к кнопке
    input[0].type=INPUT_MOUSE;
    mouseInput.dx = (int)buttonX;
    mouseInput.dy = (int)buttonY;
    mouseInput.mouseData = NULL;
    mouseInput.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
    mouseInput.time = 0; //Здесь можно использовать случайное время 1-2с.
    mouseInput.dwExtraInfo = 1001;
    input[0].mi = mouseInput;

    // Нажимаем левую кнопку мыши
    input[1].type=INPUT_MOUSE;
    mouseInput.dx = (int)buttonX;
    mouseInput.dy = (int)buttonY;
    mouseInput.mouseData = NULL;
    mouseInput.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE;
    mouseInput.time = 0; //Здесь можно использовать случайное время 1-2с.
    mouseInput.dwExtraInfo = 1001;
    input[1].mi = mouseInput;

    // И отжимаем...
    input[2].type=INPUT_MOUSE;
    mouseInput.dx = (int)buttonX;
    mouseInput.dy = (int)buttonY;
    mouseInput.mouseData = NULL;
    mouseInput.dwFlags = MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE;
    mouseInput.time = 0; //Здесь можно использовать случайное время 1-2с.
    mouseInput.dwExtraInfo = 1001;
    input[2].mi = mouseInput;

    int numberOfInputs = 2;

    // Посылаем наш INPUT
    SendInput(numberOfInputs, input, sizeof(INPUT));

    * This source code was highlighted with Source Code Highlighter.

    This function can be used not only for any movement and pressing of buttons, but also for working with the keyboard. To do this, you need to pass a similar KEYBDINPUT structure, although most often we do not need to use the keyboard.

    Here we examined the input and output of information, which are the basis for all program actions of the bot. In the next part, we will analyze the decision-making module - the basis of the bot logic, consider various strategies that can be applied to our program. Pokeroff.ru

    article specially for Habrahabr

    Also popular now: