📜 ⬆️ ⬇️

Writing a poker bot

Attention material is purely informational in nature, and the author is not responsible for the closure of accounts poker rooms. According to the laws of the countries, the creation and use of bots is not prohibited, but according to the rules of the poker rooms they are prohibited.

In this article, there will be no complete source codes, only theoretically what and how can you use, and some pieces of functions. If you are interested in this, you will not be able to put everything in a single picture and write your own. Also, I will not teach the strategies of the game, about terms or strategies, as well as the rules, you can find a lot of information on the Internet.

A bit of history.


He started playing about 5 years ago, during which time more than 1 million hands were played. This is mainly No Limit Holdem (non-limit Hold'em) at short tables (from 2 to 6 people at the table), there is an experience of playing Omaha, HU NL Holdem. The game is kind of like a hobby and leisure. And just like the idea to write a bot appeared with friends, the first idea was to write under the limit of hold'em, and when the bot was written halfway, they passed a law prohibiting Americans from playing poker, and ended up with a bot. The first version was written on a neural network with pattern recognition, what it means - they trained the bot to recognize maps from screen shots, but you yourself understand the inaccurate method, but with fairly good training, recognized with an accuracy of 98-99%. Further versions of bots already worked on direct with the windows of the rooms, using their resources., as well as in tandem with programs for analyzing and collecting game statistics.
')
Poker rooms do not sleep.


Virtually all poker rooms have this or that protection and detection system of poker bots. Consider some of them. Long play - when a person plays for too long, it becomes suspicious, so in some rooms windows may appear with questions. As protection not to do long sessions of the game. Actions at the table - pressing buttons, selecting windows, actions should not occur without moving the mouse, in a minimized window. It is better to set the trajectories of the mouse a little nonlinearly, pressing the buttons to different places, the speed of movement is also not instant. Scans of running processes and screenshots of the screen - protection as mentioned above, do not play minimized windows, call the bot process not a poker bot, and the like, do not leave the bot window maximized on the screen, change the process name after some time (for example, x minutes restart bot with new process name).

Scheme poker bot.


The scheme can be divided into 3 parts:

Block 1 is a block of interaction with a client for playing poker.
Block 2 is a decision block.
Block 3 is a statistics collection block, in my opinion it is better to use third-party software, such as PokerTracker3, while turning off statistics on the screen.
Next, we consider each block in more detail.

Block 1 is a block of interaction with a client for playing poker.


This block is used to collect information on the gaming table and transfer it to the decision block, as well as receiving an answer with the decision to perform an action like Fold, Raise, Call or All-In. Now consider this block. Part of this part is the interaction with the main window of the program, these are actions like choosing a limit, choosing the table at which we will play, this is also a big part, but we will not dwell on it. Consider in more detail the part already with the table. First we need to find the handles of all game open tables (windows), we can do this using the EnumWindows function.
The EnumWindows function enumerates all top-level windows on the screen, passing a handle to each window, in turn, to the callback function defined by the program. EnumWindows is valid until the last top-level window is listed, or until the callback function returns FALSE.

Syntax:
BOOL EnumWindows ( WNDENUMPROC lpEnumFunc, //      LPARAM lParam //    ); 

Options:
lpEnumFunc - points to a program-defined callback function. For more information, see the callback function of EnumWindowsProc.
lParam - sets the 32-bit program-defined value to be passed to the callback function.
Return Values: If the function succeeds, a nonzero value is returned. If the function fails, the return value is zero.

If the function has completed successfully, we can get the window name GetWindowText, and having analyzed it to understand the window you need or not, usually the nickname, the name of the poker room and the limit are in the window title. Further, it would not hurt to save the handles of all found windows, for further use, without re-search, the search will need to be done only in the case of opening a new window.

Now that we have a handle to the game window, we can pull out information about the table. Most of the information is stored in the dealer window. From it we can find out all the participants at the table, who came in, who left the table, who did what action, the cards on the table and their cards. To do this, we need a text parser, how to write it I will not tell, this is a separate topic, the main thing is that the idea is clear J. But before the parser we need to first find the elements of the window, and among them to find a dealer window. Very often, the dealer window is a class derived from Internet Explorer_Server, to find it, we use the EnumChildWindows function, and then GetClassName.

EnumChildWindows enumerates the child windows that belong to a particular parent window, in turn, passing the handle of each child window to the callback function defined by the program. The EnumChildWindows function works until the last child window is listed or the callback function returns FALSE.

Syntax

 BOOL EnumChildWindows ( HWND hWndParent, //    WNDENUMPROC lpEnumFunc, //      LPARAM lParam // ,   ); 


Options:
hWndParent - identifies the parent window whose child windows should be listed.
lpEnumFunc - points to a program-defined callback function. For more information on the callback function, see the callback function EnumChildProc.
lParam - sets the 32-bit program-defined value to be passed to the callback function.
Return Values: If the function succeeds, a nonzero value is returned. If the function fails, the return value is zero.

Now you can proceed to parsing the text. Our course or not can be analyzed on the basis of which buttons are available to us at the moment. This can be done both through resources and simply by points on the screen in this window. Further, if our move, then we transfer the known data to the decision block. When we receive a response, we do the corresponding action In this part, it has not yet been considered how to press the buttons, but think about it yourself.

Block 2 - decision block


This block, the heart of the bot, because it depends on him how successful he is in the game, how right he makes decisions. In Hacker 137, dated 06.2010, an example was considered which is completely based on probability theory and this is good, but in order to get a sufficiently reliable result, you need to perform quite a lot of iterations. To play confidently enough against an opponent, we need to know his range and how he plays (this piece is tied to statistics), there are also certain strategies of the game, depending on the amount of money you have, on the number of players at the table, and the strategy of the game. The starting range of hands depends on all this, and actions on subsequent streets, for example, a person playing short stacks strategy (SSS) rarely makes any moves on the river (when 5 cards are laid out on the table), because by this time he is often already in Allina (went to all-in). Assessment of situations also goes into counting outs, outs is the number of cards that will improve our position on subsequent streets, hence combinations such as straight draw, straight in hole, flush draw, etc. A draw is also a combination, and depending on the number of outs has a price. And having a certain combination, even at the moment not winning, we are already building a game line. Thanks to these additional data, we can already make decisions depending on the chosen strategy, if we add more player statistics over them, they will be even more accurate, and by adding a full miscalculation, this sample can be supplemented. I hope I brought my thought about the reduction of samples and decision making, all this is based on the personal experience of the game and decision making, because in a few sentences you cannot tell the theory of poker games, which is published in many volumes, about different situations, which are countless, although you can also identify them by major groups ...

First we need to know the situation on the table, and find out what combination we have. Here is an example of a combination definition code (rewritten more clearly, but may not be optimal):

 //     ,     int CardToNumber(char Card) { if ((Card>'1')&&(Card<='9'))return (Card-48); if (Card=='T')return 10; if (Card=='J')return 11; if (Card=='Q')return 12; if (Card=='K')return 13; if (Card=='A')return 14; return -1; } //    ,    int MastToNumber(char mast) { if (mast=='h')return 1; if (mast=='d')return 2; if (mast=='c')return 4; if (mast=='s')return 8; return 0; } //    int Hand(char *MyHand_,char *CardsTable_) { int flush_,flush; //0- 1- 2-  3- 4-  (nothing cards) int cards[15][4]; //i- j1--  j2- bits 1,2,3,4-h,d,c,s j3-0/1 - 1-  0- ( ) int straight; //0-, 1-, 2- (2- ) 3- 4-    5-   int readyhand; //0-  , 1-  int i,j, maxi,flag_,flag,flmax,top,pair_,pair,three,four,over,set_,treeps,quad,num,kicker; //         for(i=1;i<=14;i++) for(j=1;j<=3;j++)cards[i][j]=0; cards[CardToNumber(MyHand_[0])][1]++; cards[CardToNumber(MyHand_[0])][2]=cards[CardToNumber(MyHand_[0])][2]| 2*MastToNumber(MyHand_[1])); cards[CardToNumber(MyHand_[0])][2]=cards[CardToNumber(MyHand_[0])][2]|(32*MastToNumber(MyHand_[1])); cards[CardToNumber(MyHand_[0])][3]=1; cards[CardToNumber(MyHand_[3])][1]++; cards[CardToNumber(MyHand_[3])][2]=cards[CardToNumber(MyHand_[3])][2]|(2*MastToNumber(MyHand_[4])); cards[CardToNumber(MyHand_[3])][2]=cards[CardToNumber(MyHand_[3])][2]|(32*MastToNumber(MyHand_[4])); cards[CardToNumber(MyHand_[3])][3]=cards[CardToNumber(MyHand_[3])][3]+1; i=0; while(i<strlen(CardsTable_)) { cards[CardToNumber(CardsTable_[i])][1]++; cards[CardToNumber(CardsTable_[i])][2]=cards[CardToNumber(CardsTable_[i])][2]|(2*MastToNumber(CardsTable_[i+1])); i=i+3; } //  ,      num=0; i=1; maxi=0; flag=0; flmax=0; while(i<15) { if((cards[i][1]>0)&&(i>1)) { num++; if (cards[i][3]==1)flag=i; } else { if (maxi<num) { maxi=num; flmax=flag; } num=0; flag=0; } if(i==1){ if (cards[14][1]>0) { num++; if (cards[i][3]==1)flag=i; } else { if (maxi<num) { maxi=num; flmax=flag; } num=0; flag=0; } } i++; } if(maxi<num){maxi=num;flmax=flag;} num=0; i=0; flag=0; if(flmax>=maxi) { while(i<maxi) { if((cards[flmax-i][3]>0)&&(flmax-i!=1))flag++; if((cards[14][3]>0)&&((flmax-i)==1))flag++; i++; } } straight=0; if((maxi>=5)&&(flmax==14)&&(flag==0)) straight=4; if((maxi>=5)&&(flmax<14)&&(flag==0)) straight=5; if((maxi>=5)&&(flag>0)) straight=1; if((maxi==4)&&(flag>0)&&(flmax<14)) straight=2; if((maxi==4)&&(flag>0)&&(flmax==14)) straight=3; if((maxi==4)&&(flag>0)&&(flmax==4)) straight=3; //   flush_=0; j=2; while(j<=16) { flush=0; flag=0; i=2; num=0; flmax=0; flag_=0; while(i<15){ if ((cards[i][2]& j)==j) { flmax=i;num++;} if ((cards[i][2]& (j*16))==j*16) { flag_++;flag=i;} i++; } if((num==5)&&(flag==0)) flush=4; if((num>=5)&&(flmax==14)&&(flag>=13)) flush=1; if((num>=5)&&(flmax==14)&&((cards[13][2]& j)==j)&&(flag==12)) flush=1; if((num>=5)&&(flmax==14)&&((cards[13][2]& j)==j)&&((cards[12][2]& j)==j)&&(flag==11)) flush=1; if((num>=5)&&(flmax==14)&&((cards[13][2]& j)==j)&&((cards[12][2]& j)==j)&&((cards[11][2]& j)==j)&&(flag==10)) flush=1; if((num>=5)&&(flmax==14)&&((cards[13][2]& j)==j)&&((cards[12][2]& j)==j)&&((cards[11][2]& j)==j)&&((cards[10][2]& j)==j)) flush=1; if((num==5)&&(flag_==2)) flush=1; if((num==4)&&(flag>0)) flush=2; if((num==3)&&(flag==2)) flush=3; if((flush>flush_)&&(flush!=0))flush_=flush; j*=2; } flush=flush_; //  ,  , , , ,  pair=0;three=0;four=0; top=0;over=0;set_=0;treeps=0;quad=0;pair_=0; i=2; while(i<15) { if ((cards[i][1]>=1)&&(cards[i][3]==0)){ top=0;over=0;} if ((cards[i][1]==1)&&(cards[i][3]==0)){ top=0;over=0;} if ((cards[i][1]==2)&&(cards[i][3]==0)){ pair++;} if ((cards[i][1]==2)&&(cards[i][3]==1)){ pair_++;pair++;top=1;} if ((cards[i][1]==2)&&(cards[i][3]==2)){ pair_++;pair++;over=1;} if (cards[i][1]==3){ three++;} if (cards[i][1]==4){ four++;} if ((cards[i][1]==3)&&(cards[i][3]==1)) treeps=1; if ((cards[i][1]==3)&&(cards[i][3]==2)) set_=1; if ((cards[i][1]==4)&&(cards[i][3]>0)) quad=1; if ((cards[i][1]==4)&&(cards[14][3]>0)) quad=1; i++; } kicker=0; if (top>0) for(i=2;i<=14;i++){ if ((cards[i][1]==1)&&(cards[i][3]==1)) kicker=i; } // ':   '- straight // ':   '- flush // '- : '- pair // '-   : '- pair_ // ' : '- top // '' - kicker // ' : '- over // '3 : '- three // ':(2    1  ) '- treeps // ': (2   1  )'- Set_ // '4 : '- four // ': '- quad ……… } 


As a result, in the corresponding variables we have data on the current state of affairs. At what notice concepts as full houses are not present, it is a derivative from steam and thrips or a set. Here you also need a piece that determines the readiness of the hand, i.e. ready, not ready, half cooked. For example, the following code:

...

// hand ready
  readyhand=0; if ((pair==1)&&((top==1)||(over==1))) readyhand=1; if ((pair==1)&&(top==1)&&(kicker<11)&&(over==0)) readyhand=0; if ((pair>=2)&&((top==1)||(over==1))) readyhand=1; if ((pair==2)&&(top==1)&&(kicker<11)&&(over==0)) readyhand=0; if ((pair==2)&&((top==0)||(over==0))&&(pair_==2)) readyhand=1; if ((pair>2)&&((top==0)||(over==0))) readyhand=0; if ((three==1)&&(top==0)&&(over==0)&&(pair_==0)) readyhand=0; if (treeps==1) readyhand=1; if (set_==1) readyhand=1; if (straight==5) readyhand=0; if (straight==4) readyhand=1; if ((straight==2)&&(readyhand==0)) readyhand=2; if (straight==1) readyhand=1; if (flush==4) readyhand=0; if ((flush==2)&&(readyhand==0)) readyhand=2; if (flush==1) readyhand=1; if ((three==1)&&((top==1)||(over==1))) readyhand=1; if ((three==1)&&(top==0)&&(over==0)&&(pair_>0)) readyhand=1; if ((three==1)&&(top==0)&&(over==0)&&(pair_==0)&&(pair>0)) readyhand=0; //   if ((treeps==1)&&(pair>0)) readyhand=1; if ((set_==1)&&(pair>0)) readyhand=1; if (quad==1) readyhand=1; 

...

Based on these data, we go to the part that receives statistics about the opponents, calculations about the chances of the bank and other necessary ones, decide on the action and proceed to the first block - to perform the action.

Block 3 - statistics collection block


This is an important block that helps in making decisions; you can collect statistics on how to use third-party programs. I will describe an example of a request to PokerTracker3, a program for collecting and analyzing game statistics, parameters in the program are more than 100, but at least a few basic ones are enough for us, at least for example. In PokerTracker3 database is stored in the Postges database. Request example:

 SELECT sites.site_abbrev, p.player_name, COUNT(hhps.id_player) AS hands, AVG (CASE WHEN flg_vpip THEN 1 ELSE 0 END)*100 AS vpip, AVG (CASE WHEN cnt_p_raise >= 1 THEN 1 ELSE 0 END)*100 AS pfr, AVG (CASE WHEN flg_steal_att THEN 1 WHEN flg_steal_opp THEN 0 END)*100 AS ats, AVG (CASE WHEN flg_sb_steal_fold THEN 1 WHEN flg_blind_def_opp and flg_blind_s THEN 0 END)*100 AS fsbtos, AVG (CASE WHEN flg_bb_steal_fold THEN 1 WHEN flg_blind_def_opp and flg_blind_b THEN 0 END)*100 AS fbbtos, AVG (CASE WHEN enum_f_cbet_action='F' THEN 1 WHEN flg_f_cbet_def_opp THEN 0 END)*100 AS fcbetf FROM lookup_sites AS sites INNER JOIN ( player AS p INNER JOIN holdem_hand_player_statistics AS hhps ON (p.player_name = ' ')and(p.id_player = hhps.id_player) ) ON p.id_site = sites.id_site GROUP BY sites.site_abbrev,p.player_name ORDER BY sites.site_abbrev DESC; 


At the exit we get a sample for a certain player, sites on which such a nickname is in the database, you can reduce on our site only, the number of your opponent’s hands (needed for accurate readings), VPIP (% of the hands with which comes into the game), PFR (preflop raise - preflop raise), ATS (attemptto steel - how many blinds steal preflop), CBET (c-bet on flop - bet continued on the flop), BBS (big blind steel –the big blind), SBS (small blind steel - theft of small the blind). These parameters are mainly used in the extended strategy of short stacks, for full stacks there are few of these parameters, but as an example of how to get them is enough. Why all these parameters are needed and how to use them I advise you to read the relevant resources.

After we received additional statistics, we proceed to the second block and adjust our decision on the game.

Finally.


Writing articles from Hacker 137 magazine dated 06.2010 “We are tightening network poker rooms” and Hacker 139 dated 08.2010 “Simulating a poker orgasm” were written on writing this article. Since I do not fully agree with the author and consider the topic as completely unrevealed, I wanted to share my opinion.

Here are some screenshots from what you can do:


imageimage
imageimage
imageimage

Source: https://habr.com/ru/post/130805/


All Articles