Range parsing

The poker.hand.Range class parses human readable (text) ranges like "22+ 54s 76s 98s AQo+" to a set of Hands and hand Combos.

Can parse ranges and compose parsed ranges into human readable form again.
It’s very fault-tolerant, so it’s easy and fast to write ranges manually.
Can normalize unprecise human readable ranges into a precise human readable form, like "22+ AQo+ 33 AKo" –> "22+ AQo+"
Can tell how big a range is by Percentage or number of Combos.

Defining ranges

Atomic signs

X means “any card”
A K Q J T 9 8 7 6 5 4 3 2 Ace, King, Queen, Jack, Ten, 9, …, deuce
“s” or “o” after hands like AKo or 76s suited and offsuit. Pairs have no suit ('')
- hands worse, down to deuces
+ hands better, up to pairs

Available formats for defining ranges:

Format Parsed range
22 one pair
44+ all pairs better than 33
66- all pairs worse than 77
55-33 55, 44, 33

None of these below select pairs (for unambiguity):

AKo, J9o offsuit hands
AKs, 72s suited hands
AJo+ Q8o+ offsuit hands above this: AJo, AQo, AKo Q8o, Q9o, QTo, QJo
AJs+ same as offsuit
76s+ this is valid, although “+” is not neccessary, because there are no suited cards above 76s
A5o- offsuit hands; A5o-A2o
A5s- suited hands; A5s-A2s
K7 suited and offsuited version of hand; K7o, K7s
J8o-J4o J8o, J7o, J6o, J5o, J4o
76s-74s 76s, 75s, 74s
J8-J4 both ranges in suited an offsuited form; J8o, J7o, J6o, J5o, J4o, J8s, J7s, J6s, J5s, J4s
A5+ either suited or offsuited hands that contains an Ace and the other is bigger than 5. Same as “A5o+ A5s+”.
A5- downward, same as above
XX every hand (100% range) In this special case, pairs are also included (but only this)
AX Any hand that contains an ace either suited or offsuit (no pairs)
AXo Any offsuit hand that contains an Ace (equivalent to A2o+)
AXs Any suited hand that contains an Ace (equivalent to A2s+)
QX+ Any hand that contains a card bigger than a Jack; Q2+, K2+, A2+
5X- any hand that contains a card lower than 6
KXs+ Any suited hand that contains a card bigger than a Queen
KXo+ same as above with offsuit hands
7Xs- same as above
8Xo- same as above
2s2h, AsKc exact hand Combos

Note

“Q+” and “Q-” are invalid ranges, because in Hold’em, there are two hands to start with not one.

Ranges are case insensitive, so "AKs" and "aks" and "aKS" means the same. Also the order of the cards doesn’t matter. "AK" is the same as "KA". Hands can be separated by space (even multiple), comma, colon or semicolon, and Combo of them (multiple spaces, etc.).

Normalization

Ranges should be rearranged and parsed according to these rules:

  • hands separated with one space only in repr, with “, ” in str representation
  • in any given hand the first card is bigger than second (except pairs of course)
  • pairs first, if hyphened, bigger first
  • suited hands after pairs, descending by rank
  • offsuited hands at the end

Printing the range as an HTML table

Range has a method to_html(). When you print the result of that, you get a simple HTML table representation of it.

Range('XX').to_html() looks like this:

AAAKsAQsAJsATsA9sA8sA7sA6sA5sA4sA3sA2s
AKoKKKQsKJsKTsK9sK8sK7sK6sK5sK4sK3sK2s
AQoKQoQQQJsQTsQ9sQ8sQ7sQ6sQ5sQ4sQ3sQ2s
AJoKJoQJoJJJTsJ9sJ8sJ7sJ6sJ5sJ4sJ3sJ2s
AToKToQToJToTTT9sT8sT7sT6sT5sT4sT3sT2s
A9oK9oQ9oJ9oT9o9998s97s96s95s94s93s92s
A8oK8oQ8oJ8oT8o98o8887s86s85s84s83s82s
A7oK7oQ7oJ7oT7o97o87o7776s75s74s73s72s
A6oK6oQ6oJ6oT6o96o86o76o6665s64s63s62s
A5oK5oQ5oJ5oT5o95o85o75o65o5554s53s52s
A4oK4oQ4oJ4oT4o94o84o74o64o54o4443s42s
A3oK3oQ3oJ3oT3o93o83o73o63o53o43o3332s
A2oK2oQ2oJ2oT2o92o82o72o62o52o42o32o22

You can format it with CSS, you only need to define td.pair, td.offsuit and td.suited selectors. It’s easy to recreate PokerStove style colors:

<style>
   td {
      /* Make cells same width and height and centered */
      width: 30px;
      height: 30px;
      text-align: center;
      vertical-align: middle;
   }
   td.pair {
      background: #aaff9f;
   }
   td.offsuit {
      background: #bbced3;
   }
   td.suited {
      background: #e37f7d;
   }
</style>
AAAKsAQsAJsATsA9sA8sA7sA6sA5sA4sA3sA2s
AKoKKKQsKJsKTsK9sK8sK7sK6sK5sK4sK3sK2s
AQoKQoQQQJsQTsQ9sQ8sQ7sQ6sQ5sQ4sQ3sQ2s
AJoKJoQJoJJJTsJ9sJ8sJ7sJ6sJ5sJ4sJ3sJ2s
AToKToQToJToTTT9sT8sT7sT6sT5sT4sT3sT2s
A9oK9oQ9oJ9oT9o9998s97s96s95s94s93s92s
A8oK8oQ8oJ8oT8o98o8887s86s85s84s83s82s
A7oK7oQ7oJ7oT7o97o87o7776s75s74s73s72s
A6oK6oQ6oJ6oT6o96o86o76o6665s64s63s62s
A5oK5oQ5oJ5oT5o95o85o75o65o5554s53s52s
A4oK4oQ4oJ4oT4o94o84o74o64o54o4443s42s
A3oK3oQ3oJ3oT3o93o83o73o63o53o43o3332s
A2oK2oQ2oJ2oT2o92o82o72o62o52o42o32o22

Printing the range as an ASCII table

to_ascii() can print a nicely formatted ASCII table to the terminal:

>>> print(Range('22+ A2+ KT+ QJ+ 32 42 52 62 72').to_ascii())
AA  AKs AQs AJs ATs A9s A8s A7s A6s A5s A4s A3s A2s
AKo KK  KQs KJs KTs
AQo KQo QQ  QJs
AJo KJo QJo JJ
ATo KTo         TT
A9o                 99
A8o                     88
A7o                         77                  72s
A6o                             66              62s
A5o                                 55          52s
A4o                                     44      42s
A3o                                         33  32s
A2o                         72o 62o 52o 42o 32o 22