Poker Bot Preflop Strategy Explained

This is post #19 in an ongoing series of articles about my work as a poker bot developer.

When I first attempted to build a poker bot, I tried long and hard to come up with a single algorithm that given your two hole cards and the situation would spit out call, raise, or fold for every situation that the bot could face. Turns out its not a trivial task. There are a lot of factors that should affect your decision: your hole cards, your image, your opponent’s style, your position, the stack sizes relative to the blinds, the recent hand history, the stack to pot ratio once the flop hits and, and the history so far in the hand (in the case of three and four bets). To quantify all of this information – let alone weigh it in an intelligent way – is difficult, to say the least. After several weeks of fruitless attempts, I abandoned it in favor of a simpler, albeit less elegant solution.

At its core, my solution boils down to conditional statements using a simplified version of the current situation. Consider this:

“If your effective stack sizes is 55 big blinds and you’re the small blind and you’re first to act and your opponent is loose aggressive and you have 8h 6h then you should raise three times the big blind.”

It would be impossible to enumerate every possible combination this way, but if you take some shortcuts, you reduce it to a manageable number of factors.

Stack Size

In order to work with stack sizes, I grouped them together based on the effective stack size (effective stack size uses the smaller of your and your opponent’s stack size because the rest is not in play):

0 – 10bb: Shortstacked

10 – 15bb: Danger Zone

15 – 22bb: Red

22 – 35bb: Orange

35 – 50bb: Yellow

50 – 75bb: Green

(The maximum effective stack size you can have on PokerStars’ Heads Up SNGs is 1500/20 = 75 bb.)

These groups were based on my observations watching the bot and on the postflop stack to pot ratios.

Previous Actions

Like stack sizes, it would be impossible to list out every possible combination of actions leading up to current situation, so I used a shorthand:

1 = You’re small blind

2 = You’re big blind

All previous actions are reduced to C (call) or R (raise) and the size of the raises are ignored completely.


– You’re small blind and just dealt: 1

– You’re big blind and your opponent raises: 2R

– You’re small blind, you limp and your opponent raises: 1CR

– You’re big blind, your opponent raises, you three bet, and he four bets: 2RRR

Every preflop decision can be represented this way.

Hole Cards

It’s generally not a good idea to group holecards beyond the standard 9s 9h = 99, 9s 8h = 98o, etc. There are times when 88, 99, and TT should be treated the same and other times when TT should be treated differently, so it won’t do you much good to create a “Mid Pocket Pair” group and treat everything in it the same way.

That being said, specifying an action for all 169 simplified hole card combinations for every situation would would be a tedious task to do by hand. In an effort to manage it, I created a seperate program specifically for creating ranges and assigning actions to the holecards. I called it Range Maker:

(If you’ve seen ALL IN Expert, the poker calculator that I developed after the poker bot, Range Maker is where the idea came from.)

There are two versions of Range Maker shown in the screenshot above. Below the grid on the left is the older version; on the left is newer version. The grid serves both.

The older version lets me assign a fold, call, raise, or push action to every hole card combination in the selected range.

In the example below, orange represents call, maroon a 3x raise, cyan push, and gray/pink fold:

As time went on I expanded it to include additional options:

The numbers on the right represent the distribution of raise/call/fold for that color. The randomness was necessary to fool observant opponents and PokerStars security, in case that was something they looked at. The decision to push or raise 3x was later delegated to software, which figured out what the stack to pot ratio would be postflop if the opponent called and pushed if it was too high.

Here’s an example of what the range looks like for the small blind opening range vs an aggressive opponent when the stack size was in the red zone (15 – 22 bb):

And here’s the same spot vs a call station:

It took a long time to get these ranges down. I would sit there and watch the bot play for hours, making pages of notes on what I wanted to adjust when the session was over.

For those of you looking for a bit more, here are the range files I used with each version:

These include virtually every range I ever worked with, so if you want to use these you’ll have to spend some time analyzing them to pick out the gems.

Putting it All Together

Here’s roughly how it went in an actual game:

1. Analyze the current situation: hole cards, stack size, previous action, and opponent style and group them.

2. Look up the range based on these factors

3. Act

Example: PokerStars 25/50 blinds, you are small blind with 2100 chips, call station opponent is big blind with 900 and you’re dealt Ks 5h.

Your effective size is 900/50 = 18 big blinds, putting you in the red zone and since you’re small blind and first to act, the previous action is simply “1”. Now with three layers of conditional statements (stack size, previous action, and opponent style) you can look up the range (see the screenshot above) and determine that K5o is 80% raise, 20% call, and 0% fold. Roll a die (in this case the random number generator) and pick an action based on that distribution.

Here’s the corresponding code for step 2:

'  Name    : PreflopActionFromFile
'  Purpose : Given a specific situation, this'll look up my range in
'            the Hero Ranges.txt file and see what I want to do with this hand
Public Function PreflopActionFromFile(sHoleCards As String, sRangeName As String) As String

 Dim sRangeFile_Old As String                ' old range file
 Dim sRangeFile_New As String                ' advanced ranging (mixed ranges)
 Dim sNewINIData As String                   ' data from the advanced file
 Dim iDataPos As Integer                     ' start of the distribution
 Dim sDistribution As String                 ' data from that file
 Dim sDistributionSplit() As String          ' split it up

 Dim sSimpleHand As String                   ' AA, AQs...
 Dim dRndNum As Double                       ' a random #
 Dim dRaisePerc As Double
 Dim dCallPerc As Double
 Dim sINIVal As String

 sSimpleHand = SimpleHand(sHoleCards)

 sRangeFile_Old = App.Path & "ResourcesHero Ranges.ini"
 sRangeFile_New = App.Path & "ResourcesHero Advanced.txt"
 sNewINIData = GetFromINI("Range Data", sRangeName, sRangeFile_New)

 If sNewINIData <> vbNullString Then
 ' There is an entry in the advanced range file

 iDataPos = InStr(sNewINIData, sSimpleHand)
 sDistribution = Mid(sNewINIData, iDataPos + Len(sSimpleHand) + Len(" ("), InStr(iDataPos, sNewINIData, ")") - iDataPos - Len(sSimpleHand) - Len(" ("))
 sDistributionSplit = Split(sDistribution, "/")

 ' Make an update
 Update "Mixed Distribution: " & sSimpleHand & " (" & sDistribution & ")", 2, eDecision

 dRndNum = Rnd
 dRaisePerc = Val(sDistributionSplit(0)) / 100
 dCallPerc = Val(sDistributionSplit(1)) / 100

 ' Make a decision...
 If dRndNum <= dRaisePerc Then
 PreflopActionFromFile = "R"
 ElseIf dRndNum <= (dRaisePerc + dCallPerc) Then
 PreflopActionFromFile = "C"
 PreflopActionFromFile = "F"
 End If

 Update "Action Chosen: " & PreflopActionFromFile, 3, eDecision


 Update "Hero Range: " & sRangeName, 1

 sINIVal = GetFromINI("Ranges", sRangeName & " (R)", sRangeFile_Old)
 If InStr(sINIVal, SimpleHand(sHoleCards)) > 0 Then
 PreflopActionFromFile = "R"
 Exit Function
 End If

 sINIVal = GetFromINI("Ranges", sRangeName & " (C)", sRangeFile_Old)
 If InStr(sINIVal, SimpleHand(sHoleCards)) > 0 Then
 PreflopActionFromFile = "C"
 Exit Function
 End If

 sINIVal = GetFromINI("Ranges", sRangeName & " (P)", sRangeFile_Old)
 If InStr(sINIVal, SimpleHand(sHoleCards)) > 0 Then
 PreflopActionFromFile = "P"
 Exit Function
 End If

 PreflopActionFromFile = "F"
 End If

End Function

Final Thoughts

This approach covered about 90% of all the preflop situations the bot faced in the course of a game, but about 10% had to be handled with special cases. Consider, for example, an opponent who pushes every time with 75bb. You can’t just treat this as a 2R situation and call with the standard range. There are also a lot of situations where you should shove simply based on its expected value, which you have to figure out in spots that warrant it (which require you to estimate your opponent’s raising and calling range).

If you’re an aspiring botter and have questions about any of this, feel free to shoot me an email. And good luck.


5 thoughts on “Poker Bot Preflop Strategy Explained

  1. Interesting piece. I spend a lot of time writing PPL code for the Shanky bot. The code itself is simple but the situations you can define are limitless. I always found that the problem with writing a PPL profile, it’s never finished. If you don’t know what PPL is there are links to lots of free PPL text files on

Leave a Reply to Paul M. Cancel reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s