Preflop Odds – Building a Better Bingo Bot

In my last post I showcased some of the first results of my poker experiment. The automated logic running my poker bot has been set to wait for specific pocket cards in the preflop, and if a match is found, simply go all in:

NSString *allInCardsStr = @"AA KK QQ JJ AJ AK TT 99 88 77 AQ KQ";
NSSet *allInCardsSet = [NSSet setWithArray:[allInCardsStr componentsSeparatedByString:@" "]];

NSLog(@"checking if cards (%@) are found in set: %@",pocketCards, allInCardsStr);
shouldAllIn = [allInCardsSet containsObject:pocketCards];

This kind of “bingo botting” has potential, and has yielded results so far, but there is still too much luck and too much risk involved for my liking. Probabilities of winning change depending on the number of players, and this logic doesn’t account for that. This something that can benefit from refined control.

These are the odds involved in the preflop for some of these cards of interest:

preflop odds:
 	2       3       4       5       6       7       8       9
AA 	0.851	0.733	0.634	0.557	0.489	0.431	0.384	0.343
KK 	0.822	0.688	0.581	0.495	0.426	0.372	0.326	0.290
QQ 	0.796	0.644	0.533	0.443	0.375	0.322	0.279	0.246
JJ 	0.771	0.608	0.489	0.399	0.333	0.282	0.243	0.213
TT 	0.748	0.573	0.448	0.360	0.294	0.246	0.213	0.186
99 	0.716	0.534	0.409	0.322	0.262	0.222	0.191	0.168
88 	0.689	0.497	0.373	0.292	0.235	0.198	0.175	0.154
77 	0.657	0.461	0.338	0.264	0.214	0.182	0.160	0.145

Suited
AK 	0.663	0.499	0.406	0.345	0.302	0.267	0.241	0.217
AQ 	0.663	0.483	0.388	0.323	0.282	0.249	0.221	0.200
AJ 	0.664	0.472	0.372	0.310	0.267	0.235	0.210	0.188
KQ 	0.625	0.462	0.373	0.316	0.275	0.243	0.215	0.196
KJ 	0.615	0.446	0.358	0.298	0.259	0.227	0.204	0.182
KT 	0.605	0.435	0.341	0.288	0.247	0.214	0.191	0.172

Unsuited
AK 	0.643	0.475	0.376	0.316	0.269	0.235	0.206	0.185
AQ 	0.645	0.457	0.357	0.293	0.249	0.215	0.186	0.165
AJ 	0.625	0.442	0.339	0.276	0.232	0.198	0.171	0.150
KQ 	0.603	0.433	0.342	0.283	0.241	0.208	0.181	0.159
KJ 	0.593	0.419	0.325	0.263	0.223	0.189	0.165	0.146
KT 	0.584	0.404	0.309	0.252	0.210	0.179	0.154	0.135

Note the sharp variations depending on not just the number of players involved, but also in whether or not your cards have the same suit. These numbers were calculated using the same probability simulator used within the poker bot, with 200000 simulations based on the number of players available and the cards currently visible, assuming no one ever folds. The results can also be replicated online with various browser based odds simulators, and should approximately fix these measurements.

Reviewing the table above we’ll gain a clearer picture why it might not be a good idea to go all in with an unsuited AK with 9 people in play, compared to 2 or 3 people in play. With 2 people in play, chances are you’ll win more often than you lose. These odds decrease slightly with a larger number of players, where with 9 players you can expect to win less than 20% of your attempts.

This is the resulting preflop logic for the current bingo-based poker bot, tweaked based on experimentation:

switch(self.playerCount){
    case 2:
        if(self.winningOdds > 0.58){
        }else{
            [self foldAction];
        }
        break;
        
    case 3:
        if(self.winningOdds > 0.435){
            [self allInAction];
            
        }else{
            [self foldAction];
            
        }
        break;
        
    case 4:
        if(self.winningOdds > 0.340){
            [self allInAction];
        }else{
            [self foldAction];
            
        }
        break;
        
    case 5:
        if(self.winningOdds > 0.250){
            [self allInAction];
            
        }else{
            [self foldAction];
            
        }
        break;
        
    case 6:
        if(self.winningOdds > 0.246){//includes AQ unsuited (0.247)
            [self allInAction];
            
        }else{
            [self foldAction];
            
        }
        break;
        
    case 7:
        if(self.winningOdds > 0.200){
            [self allInAction];
            
        }else{
            [self foldAction];
            
        }
        break;
        
    case 8:
        if(self.winningOdds > 0.200){
            [self allInAction];
            
        }else{
            [self foldAction];
            
        }
        break;
        
    case 9:
        if(self.winningOdds > 0.18){
            [self allInAction];
            
        }else{
            [self foldAction];
            
        }
        break;
}

The decision-making logic is still extremely simple, but the tighter odds-driven control means more low-risk hands are played (e.g. when less people are playing), and less higher-risk hands are creating a loss when more people are involved. That’s what we’re interested in. We can’t win every hand, but we can try to win more than we lose, and part of that is keeping probabilities in our favour as much as we can.

Results are already flowing in, with my week-old account having grown from roughly $450k to $7million with just a few days of random play. Basic CSV logging has now been added, so hopefully I’ll soon have more concrete data to support my observations so far.

What’s next?

Of the many things the bot is reading to stay aware of the current game state, what it still does not see is the money in the pot (or side pots, nor how many people have called or raised (and how much) in the current round. This is the next priority, since I’ll try to use that to build more risk/reward driven logic to play more than just the preflop.

Another challenge is the image recognition – cards and buttons are recognised through comparison to known datasets of matching images, but the volume of noise (chips & cards flying across the screen) is generating tons of new images that need to be classified. An hour of play alone generates at least 100 new images that need to be manually classified. A few sessions of neglect, and this can easily build up. I’m in the process of training an image classification model through deep learning, which I can then use to automate a big chunk of this classification process. It’s too slow to make a part of the normal bot workflow, but reliable enough to be used as a parallel/secondary process. Recognition so far is encouraging, but it takes quite a bit of time to tune.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.