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 blocking your account with the withdrawal of all the money in the account. In this article there will be no ready-to-use code, so as not to make life easier for script-kiddisam, we will consider the basic principles and algorithms of the bot. A person familiar with programming, if desired, will still be able to write such a program. The existence of winning poker bots has always been questioned, some poker rooms claim that their software generally prevents the use of such programs. But anyone familiar with programming understands that it is easy to write the bot itself, and there will always be opposition to any protection. The most difficult (and therefore most important) problem is the decision-making algorithm. Indeed, to develop an algorithm that will bring plus is not so easy, but it is not necessary. Now with a lot of different bonuses, rakeback and other offers from the poker rooms, it is enough for the bot to play zero or a weak minus, which is quite possible for small limits.
')
In general, the very first bot (more precisely, the program that plays
poker ) 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-telzas - if the opponent thought for a long time, then his actions were more likely to be considered bluffing than if he acted quickly.
On the Internet it is not so difficult to find a lot of ready bots. From free elementary instances to bots with a large set of functions and the ability to play a team on several accounts at the cost of $ 200. And it is only in publicly available sources, it is not known what can be found on any specialized private hacker forums and sites. According to rumors a good winning bot costs from $ 1000, there are instances and $ 5000. Such programs are probably very good at hiding themselves and imitating human behavior to the maximum, it is quite possible that they use neural networks to make decisions. In this article we will not write a bot for $ 5k, we just try to make out the basic principles of the programs easier. And there is no limit to further improvement.
Bot basics
Main modules for bot operation:
- Getting information - getting information from the client, which is necessary for making a decision; includes - our cards, opponent's bets, stack sizes, button position, etc.
- Decision making is an algorithm for deciding on a bot action, rather a logical task and a separate topic, so we will look at it in the next article in this series.
- Simulation of user actions - imitation of user pressing buttons, imitation of mouse movements.
In this article we will talk about the 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, this is individual, but often the actions (cards handed out, the actions of the players) are recorded in a log file, which you can 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 text output. Almost all poker clients have a text element on the table, combining the functions of a chat and an information window. If necessary, you can display all the information on the distribution (player actions, your cards, table cards, etc.). It looks like this:

- Hand history - when the option is enabled in the client, the history of all hands 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 (stakes, player nicknames) and graphic information (maps, button position).
The log file usually does not contain all the information on the distribution, for example, from the Pokerstars client log file (C: \ Program Files \ PokerStars \ PokerStars.log.0) you can find out only the cards that we were given 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
11c, 11d are our cards (JcJd), and the dealer is in 3rd place.
The way with API messages is quite simple to implement and often it can be used to get all the necessary information. To implement it, you need to use the DLL injection into the poker client process. The embedded DLL may be useful for simulating keystrokes and other information output. The main disadvantage of the inject is that it is difficult to hide such an impact on the client if he tries 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. Implementation through the registry.
2. Using traps (hooks).
3. Deployment via remote stream
4. Write directly to memory using WriteProcessMemory (), you can read more
here .
We consider the easiest and most convenient approach - the use of traps. To do this, use the SetWindowsHookEx API function (idHook, lpfn, hMod, dwThreadId), where
idHook - defines the type of capture procedure, for global interception, you need to use WH_CBT (for intercepting keyboard messages, for example, you can use WH_KEYBOARD);
lpfn is a pointer to an interception procedure that will be called each time it is intercepted. In it, we will catch the messages we need and perform the necessary actions;
hMod is a DLL handle containing the lpfn procedure.
dwThreadId - identifier of the thread on which the interceptor is set (0 for global interception).
In our DLL, there must be a hook setting function and a function called when this hook 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 a global interception, the DLL is embedded in each process in the system, so as not to consume a lot of memory because of this, the loading process can be divided into two parts. First, a global DLL is implemented that takes up minimal memory and can only determine in which process it is loaded. For the desired process, it uses
LoadLibrary () to load the second DLL in which the necessary functionality is implemented (map reading, logic, etc.).
After interception, we can catch different API messages that are sent to the client. For example, when outputting to the Rich Edit element (can be used to organize a chat), the EM_STREAMIN message is used. And we can intercept it to get the text displayed in the chat, and with it the information on the distribution. For each room, the element for displaying text can be individual, but the procedure is the same. In general, it is very useful to use the Spy ++ program (most of you are familiar with it, it is included in the Visual Studio package) or an equivalent for researching API messages sent to the client. With Spy ++ you can find out the headers 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 ways of displaying information in them. In this case, you 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 just need to find where.
If it is impossible to arrange an interception (the client blocks such attempts) or we can’t find the information we need, we can use the screen capture and character recognition method. But this method is best left to the extreme case, because it is more laborious and requires more resources at work. Its main advantage is that this method will not be able to detect a poker client. You can even run a poker bot on another computer (although the part responsible for pressing the keys should be on the computer with the client, but for this part it is not necessary to use the DLL injection) to which the video from the computer screen with the poker client is transmitted. You can also run the client under the virtual machine, and the bot under the main OS. Obvious disadvantages of the screen capture approach - the number of tables played is limited by the screen resolution and dependence on the theme of the cards and the table used in the client.
Simulation of user actions
When the bot presses buttons and other actions that imitate the behavior of an ordinary player, we need to achieve maximum likelihood. In this case, you should use a random delay in the response of the bot, so as not to take all actions immediately after the start of the turn. From
the RNG article, you can find out that the PokerStars poker room uses the user's mouse movements to generate random numbers. At the same time, nothing prevents them from using this information to verify users (it is quite likely that other poker clients conduct such “surveillance” over their players). Therefore, it is important to make random mouse movements around the screen and moving the cursor to the point where the buttons are pressed. You can also make random clicks outside the poker client window (on the desktop, the taskbar).
Therefore, it will be optimal to work with the mouse programmatically directly. There is an option to find the handle of the desired buttons and send them a message using SendMessage (), but it is better to have minimal impact on the client itself, and do everything from the outside. It turns out 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 receive local click coordinates 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 randomly need to choose a point to click to simulate a person's behavior.
To control the mouse, we will use the
SendInput API function
(UINT nInputs, LPINPUT pInputs, int cbSize) . It is passed an array of
INPUT structures, which contains sequential actions with the mouse and keyboard. This is how the code moves the mouse to a specific position and presses 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 transfer a similar structure to KEYBDINPUT, although most often we will not need to use the keyboard.
Here we have analyzed input and output 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's logic; consider the various strategies that can be applied to our program.
Article
Pokeroff.ru specifically for Habrahabr