Poker

Goal

For each decision situation, calculate the best move and find a way to quickly
estimate it in-game, either using mental calculus, or using precomputed charts.

Definitions

A standard 52-card pack comprises 13 ranks in each of the four French suits:
clubs (trèfle), diamonds (carreaux), hearts (coeur) and spades (pique).

A hand is a set of raw hands. A raw hand is a set of two distinct cards.

There are seventy eight different offsuit hands, one of the seventy eight
different suited hands, or one of the thirteen pairs.

App to calculate equities: Holdem Lab

10p seats: sb, bb, utg, utg+1, utg+2, mp, lj, hj, co, but

A street is either pre-flop, flop, turn or river.

Facing a bet

https://upswingpoker.com/pot-odds-step-by-step/
https://openpokertools.com/range_equity.html
https://upswingpoker.com/poker-ranges-strategy/

Suppose I need to decide whether to call my opponent.

I want to calculate the equity required for the call to be profitable.

My expected profit at some time t is the amount of chips I can expect to gain,
compared to before my call, counting the pot as not mine.

A call is profitable if the amount of money I can expect to win by calling is
greater than the size of the call, i.e. if my expected profit is positive.

p: size of the pot before call
c: size of the call
s: my showdown equity
o: pot odds
v: my expected profit
t: showdown equity required for the call to be profitable

o := p:c
u := c/p

v = sp - (1 - s)c

    tp - (1 - t)c = 0
=>  tp = (1 - t)c
=>  tp = c - tc
=>  t(p + c) = c
=>  t = c/(p + c)
=>  t = u/(1 + u)

t = u/(1 + u)

With non-zero implied odds

https://upswingpoker.com/implied-odds-poker-strategy/
https://www.thepokerbank.com/strategy/mathematics/implied-odds/

p: size of the pot before call
c: size of the call
s: prob of hitting on the next street
o: pot odds
x: expected profit on the next streets if I hit on the next one
v: expected profit
t: prob of hitting on the next street required for the call to be profitable
x': required x for my call to be profitable

o := p:c

v = s(p + x) - (1 - s)c

    t(p + x) - (1 - t)c = 0
=>  t(p + x) = (1 - t)c
=>  t(p + x) = c - tc
=>  t(p + x + c) = c
=>  t = c/(p + x + c)

t = c/(p + x + c)

    s(p + x') - (1 - s)c = 0
=>  sp + sx' = (1 - s)c
=>  sx' = (1 - s)c - sp
=>  x' = (1 - s)/s*c - p

=>  x' = c/s - p - c

How much should I bet?

https://poker.fandom.com/wiki/Fold_equity
https://en.wikipedia.org/wiki/Fold_equity
https://redchippoker.com/fold-equity-calculator/
https://floattheturn.com/wp_php/wp_spread_sheet.php
https://poker.stackexchange.com/questions/78/how-do-i-calculate-expected-value-of-shoving-including-fold-equity-in-heads-up
https://dwarrior.wordpress.com/tag/fold-equity/

Suppose I need to decide how much to bet on the river. Of course, if I am not on
the river, the fact that I will be able to bet on the next streets changes
things. So we rule that out for the moment.

My expected profit is the amount of money I can expect to gain, compared to
before my bet, counting the pot as not mine.

I want to calculate the bet size that maximizes my expected profit.

Summary

p: size of the pot before my bet
b: size of my bet
u := b/p
k: min(my stack in % of pot before the bet, stack in % of pot of opponent)
s: my showdown equity
r: range vs range equity of my opponent
h: hand vs range equity of my opponent
q: probability that the opponent folds if I bet infinitely high.
f: probability that the opponent folds
v: my expected profit if opponent calls
y: my expected profit
um: bet size in % of pot that maximizes my expected profit

v = s(p + b) - (1 - s)b
q = P(h < 1/2)
f = Min(u, q)
y = fp + (1 - f)v

if s > 1/2:
    if y(k) > y(Min(s/(4*s - 2), q)):
        um = k
    else:
        um = Min(s/(4*s - 2), q)
else:
    um = q

f = b(1 - r)/(b + rp)

Finding f

I want:

r ≠ 0 ∧ r ≠ 1 => lim_{b -> +∞}(f) = P(h < 0.5)

Let us say that P(h > 0.5) = 1 - r.

I need an expression of f such that:
-   r ↑ => f ↓
-   b ↑ => f ↑
-   b = 0 => f = 0
-   b ≠ 0 ∧ r = 0 => f = 1
-   b ≠ 0 ∧ r = 1 => f = 0
-   r ≠ 0 ∧ r ≠ 1 => lim_{b -> 0}(f) = 0
-   r ≠ 0 ∧ r ≠ 1 => lim_{b -> +∞}(f) = 1 - r
-   b ≠ 0 => lim_{r -> 0}(f) = 1
-   b ≠ 0 => lim_{r -> 1}(f) = 0

f = b(1 - r)/(b + ar)
Finding a
h: hand vs range equity of my opponent

Suppose r << 0.5. Then h is probably below 0.5.

Hence the opponent will call only if:

    h(p + b) - (1 - h)b > 0
<=> b < ph/(1 - 2h)

Hence if r << 0.5:

f = P(h/(1 - 2h) < b/p)

Let g be the function defined on [0; 0.5[, such that g(h) = h/(1 - 2h).

from sympy import *
from sympy.plotting import *
h = symbols('h')
g = h/(1 - 2*h)
plot(g, (h, 0, 0.45))

We have:
-   g is increasing
-   Im(g) ⊂ [0; +∞[
-   g(1/3) = 1

We could say:

b/p = r/(1 - 2r) => f = 0.5

from sympy import *
from sympy.plotting import *
p, r, a = symbols('p r a')
b = p*r/(1 - 2*r)
f = b*(1 - r)/(b + a*r)
solve(f - 0.5, a)
> p

Thus:

f = b(1 - r)/(b + pr)
Plotting f
from sympy import *
from sympy.plotting import *
b, r = symbols('b r')
f = b*(1 - r)/(b + r)
plot3d(f, (b, 0, 3), (r, 0.05, 1))

f = b(1 - r)/(b + pr)

Plotting y

f = b(1 - r)/(b + rp)
y = ((2s - 1)rb² + (1 + (3s - 2)r)pb + rsp²)/(b + rp)

call factor := (2s - 1)rb² + (1 + (3s - 2)r)pb + rsp²
fold factor := b + rp

from sympy import *
from sympy.plotting import *
p, b, s, r = symbols('p b s r')
v = s*(p + b) - (1 - s)*b
f = b*(1 - r)/(b + p*r)
y = f*p + (1 - f)*v
plot3d(y.subs([(p, 100), (b, 100)]), (s, 0, 1), (r, 0.05, 0.95))
plot3d(y.subs([(p, 100), (s, 1)]), (b, 0, 300), (r, 0, 0.95))

Optimal bet size

Let us calculate some common optimal bet sizes.

yd = diff(y, b)
bm = solve(yd, b)[1]
plot3d(bm.subs([(p, 100), (b, 100)]), (s, 0, 1), (r, 0, 0.95))

from sympy import *
from sympy.plotting import *
p, b, s, r = symbols('p b s r')
v = s*(p + b) - (1 - s)*b
f = b*(1 - r)/(b + p*r)
y = f*p + (1 - f)*v
for sx in range(0, 11, 2):
    sx = sx/10
    for rx in range(0, 10, 2):
        rx = rx/10
        ys = y.subs([(p, 100), (s, sx), (r, rx)])
        ysd = diff(ys, b)
        sol = solve(ysd, b)
        sol = [round(x) for x in sol if not(im(x)) and x > 0]
        if sol:
            action = "bet"
            amount = sol[0]
        else:
            coef = round(ysd.subs([(b, 50)]), 2)
            if coef > 0:
                action = "all-in"
            else:
                action = "check"
            amount = ""
        print(f"{sx}|{rx}|{action}|{amount}")

s   | r   | decision | bet size
0.0 | 0.0 | check    |
0.0 | 0.2 | bet      | 60
0.0 | 0.4 | bet      | 20
0.0 | 0.6 | check    |
0.0 | 0.8 | check    |
0.2 | 0.0 | check    |
0.2 | 0.2 | bet      | 75
0.2 | 0.4 | bet      | 35
0.2 | 0.6 | check    |
0.2 | 0.8 | check    |
0.4 | 0.0 | check    |
0.4 | 0.2 | bet      | 130
0.4 | 0.4 | bet      | 85
0.4 | 0.6 | bet      | 38
0.4 | 0.8 | check    |
0.6 | 0.0 | check    |
0.6 | 0.2 | all-in   |
0.6 | 0.4 | all-in   |
0.6 | 0.6 | all-in   |
0.6 | 0.8 | all-in   |
0.8 | 0.0 | check    |
0.8 | 0.2 | all-in   |
0.8 | 0.4 | all-in   |
0.8 | 0.6 | all-in   |
0.8 | 0.8 | all-in   |
1.0 | 0.0 | check    |
1.0 | 0.2 | all-in   |
1.0 | 0.4 | all-in   |
1.0 | 0.6 | all-in   |
1.0 | 0.8 | all-in   |

Case s = 1

When s = 1, we have:

v = p + b
f = b(1 - r)/(b + pr)
y = fp + (1 - f)v

1 - f = (b + pr - b + br)/(b + pr)
      = r(p + b)/(b + pr)

y = fp + (1 - f)(p + b)
   = (bp(1 - r) + r(p + b)²)/(b + pr)
   = (bp - rbp + r(p² + 2pb + b²)/(b + pr)
   = (bp - rbp + rp² + 2rpb + rb²)/(b + pr)
   = (bp + rbp + rp² + rb²)/(b + pr)
   = (rb² + (1 + r)pb + rp²)/(b + pr)

from sympy import *
from sympy.plotting import *
p, b, s, r = symbols('p b s r')
v = s*(p + b) - (1 - s)*b
f = b*(1 - r)/(b + p*r)
y = f*p + (1 - f)*v
plot(y.subs([(p, 100), (s, 1), (r, 0.1)])/100, f.subs([(p, 100), (r, 0.1)]), (b, 0, 300))

plot = plot3d(y.subs([(p, 100), (s, 1)])/100, f.subs(p, 100), (b, 0, 300), (r, 0.05, 0.95), show = False)
plot.show()

When s = 1, the model shows a monotonic increasing y(b). Hence, regardless of
my stack, I should go all-in. Why? Shouldn't I bet not too much so that my
opponent calls?

It is a compromise. Betting more increases the probability that the opponent
folds, but also the profit in case he calls.

Model says that I should always bet more because even if r is very low, betting
more increases the call factor more than the fold factor.

Problem. f does not seem realistic. Why would f' be that much higher at low bets
than at high bets. Maybe we need a more linear f.

f = P(h < b/(p + 2b))

We assume that the opponent plays optimally. The opponent will fold if and only
if his expected profit v is negative. Hence:

f = P(v < 0)
  = P(h(p + b) - (1 - h)b < 0)

h(p + b) - (1 - h)b = hp + hb - b + hb
                    = hp + 2hb - b
                    = hp + (2h - 1)b
                    = h(p + 2b) - b

Hence:

f = P(h < b/(p + 2b))
  = |{hdo ∈ rgo ; h(hdo) < b/(p + 2b)}| / |rgo|

Using normal distribution for h

https://en.wikipedia.org/wiki/Truncated_normal_distribution

Let us assume that h is a truncated continuous normal random variable with
values in [0, 1], centered on r, and with standard deviation σ.

How to choose σ? No idea, but 0.2 seems to yield something balanced.

We have:

f = P(h < b/(p + 2b))
  = cdf(h)(b/(p + 2b))

from sympy import *
from sympy.plotting import *
from sympy.stats import Normal, density, cdf
import numpy as np
r, s, x, b, p = symbols('r s x b p')
N = Normal("N", 0, 1)
s = 0.2
k = (x - r)/s
l = (0 - r)/s
t = (1 - r)/s
Z = cdf(N)(t) - cdf(N)(l)
pdf = 1/s*density(N)(k)/Z
cdf = (cdf(N)(k) - cdf(N)(l))/Z
plot(cdf.subs(r, 0.3), (x, 0, 1))
f = cdf.subs(x, b/(p + 2*b))
simplify(f)
plot3d(f.subs(p, 100), (b, 0, 300), (r, 0.05, 0.95))
p = plot(show = false)
plt = plot(f.subs([(p, 100), (r, 0.1)]), (b, 0, 300))

plot3d fails, no idea why.

cdf(h)(x) = (erf(√2/2*r/σ) - erf(√2/2*(r - x)/σ))/(erf(√2/2*r/σ) - erf(√2/2*(r - 1)/σ))

f = (erf(2.5*sqrt(2)*r) + erf(2.5*sqrt(2)*(b - r*(2*b + p))/(2*b + p)))/(erf(2.5*sqrt(2)*r) - erf(2.5*sqrt(2)*(r - 1)))

Using uniform distribution for h

u := b/p

Suppose that we know nothing about the hand of the opponent. Then h is a
continuous uniform random variable over [0, 1] and we have:

f = P(h < b/(p + 2b))
  = b/(p + 2b)
  = u/(1 + 2u)

q: probability that the opponent folds if I bet infinitely high.

We could take:

f = u/(1 + u/q)

For now we assume that q = 1/2.

from sympy import *
from sympy.plotting import *
import numpy as np
u, s, q = symbols('u s q')
q = 1/2
v = s*(1 + u) - (1 - s)*u
f = u/(1 + u/q)
y = f + (1 - f)*v
yd = diff(y, u)
uo = solve(yd, u)[0]

I get:

uo = (1 - 2s - 1.41√(0.5 - s))/(4s - 2)

plt = plot(show = False)
for sx in np.linspace(0, 1, 11):
    plt.append(plot(y.subs(s, sx), (u, 0, 2), show = False)[0])
plt.show()

plot3d(y, (u, 0, 2), (s, 0, 1))
plot(uo, (s, 0, 0.49))
Varying q
Let us make q vary.

from sympy import *
from sympy.plotting import *
import numpy as np
u, s, q = symbols('u s q')
v = s*(1 + u) - (1 - s)*u
f = u/(1 + u/q)
y = f + (1 - f)*v
yd = diff(y, u)
uo = solve(yd, u)[1]
plot3d(uo, (s, 0, 1), (q, 0, 0.99))

plot3d(f, (u, 0, 2), (q, 0, 1))

from sympy import *
from sympy.plotting import *
import numpy as np
u, s, q = symbols('u s q')
s = 1
q = 1
v = s*(1 + u) - (1 - s)*u
f = u/(1 + u/q)
y = f + (1 - f)*v
plt = plot(legend = true, show = False)
plt.append(plot(f, (u, 0, 2), label = "f", line_color = "blue", show = False)[0])
plt.append(plot(v, (u, 0, 2), label = "v", line_color = "red", show = False)[0])
plt.append(plot(y, (u, 0, 2), label = "y", line_color = "green", show = False)[0])
plt[0].label = "f"
plt.show()

f = Min(u, q)

I could take:

f = Min(u, 1)

Using that, when s = 1, it is no longer the best solution to bet infinitely high.

Let us take:

f = Min(u, q)

y = (1 - Min(q, u))*(s*(u + 1) - u*(1 - s)) + Min(q, u)
  = -(s*(u + 1) + u*(s - 1))*(Min(q, u) - 1) + Min(q, u)

from sympy import *
from sympy.plotting import *
import numpy as np
u, s, q = symbols('u s q')
v = s*(1 + u) - (1 - s)*u
f = Min(u, q)
y = f + (1 - f)*v

plot(y.subs([(s, 1), (q, 0.5)]), (u, 0, 1))

plt = plot(show = False)
for sx in np.linspace(0.1, 0.9, 5):
    for qx in np.linspace(0, 1, 11):
        plt.append(plot(y.subs([(q, qx), (s, sx)]), (u, 0, 1), line_color = (sx, 0, 1 - sx), show = False)[0])
plt.show()

plot3d(y.subs(s, 1), (u, 0, 1), (q, 0, 1))
This fails with:
TypeError: only integer scalar arrays can be converted to a scalar index
No idea why.

u > q

y = (1 - Min(q, u))*(s*(u + 1) - u*(1 - s)) + Min(q, u)
  = (1 - q)*(s*(u + 1) - u*(1 - s)) + q
  = -2*q*s*u - q*s + q*u + q + 2*s*u + s - u
  = (q - 2*q*s + 2*s - 1)*u - q*s + q + s
  = (2*s - 1)*(1 - q)*u - q*s + q + s

We assume that k > 1.

If s > 1/2, um = k. If s < 1/2, um = q.

u < q

y = (1 - Min(q, u))*(s*(u + 1) - u*(1 - s)) + Min(q, u)
  = (1 - u)*(s*(u + 1) - u*(1 - s)) + u
  = -2*s*u**2 + s*u + s + u**2
  = (1 - 2*s)*u**2 + s*u + s

dy/du = 2*(1 - 2*s)*u + s

    2*(1 - 2*s)*u + s = 0
<=> u = s/(4*s - 2)
s > 1/2
Then s/(4*s - 2) > 0.

If s/(4*s - 2) > q, then y(u) is increasing on [0, q] and um = q.

If s/(4*s - 2) < q, then um = s/(4*s - 2).

Hence:

um = Min(s/(4*s - 2), q)
s < 1/2
We have s/(4*s - 2) < 0.

Hence y(u) is increasing on [0, q], so um = q.

um

yh: y for high values of u (u > q)
yl: y for low values of u (u < q)

if s > 1/2:
    if y(k) > y(Min(s/(4*s - 2), q)):
        um = k
    else:
        um = Min(s/(4*s - 2), q)
else:
    um = q
Other formulation
if s > 1/2:
    if q > s/(4*s - 2):
        if yh(k) > yl(s/(4*s - 2)):
            um = k
        else:
            um = s/(4*s - 2)
    else:
        if yh(k) > yl(q):
            um = k
        else:
            um = q
else:
    um = q
s > 1/2 ∧ q > s/(4*s - 2)
Suppose s > 1/2 ∧ q > s/(4*s - 2).

    y(k) - y(Min(s/(4*s - 2), q))
=   yh(k) - yl(s/(4*s - 2))

from sympy import *
from sympy.plotting import *
import numpy as np
u, s, q, k = symbols('u s q k')
v = s*(1 + u) - (1 - s)*u
yh = q + (1 - q)*v
yl = u + (1 - u)*v
simplify(yh.subs(u, k) - yl.subs(u, s/(4*s - 2)))
Computing some values
def calc_y(u, s, q, k):
    v = s*(1 + u) - (1 - s)*u
    f = min(u, q)
    y = f + (1 - f)*v
    return y

def calc_um(s, q, k):
    if s > 1/2:
        if calc_y(k, s, q, k) > calc_y(min(s/(4*s - 2), q), s, q, k):
            um = k
        else:
            um = min(s/(4*s - 2), q)
    else:
        um = q
    return um

k = 5
print(f"k = {k}")
print("s|q|um")
for sx in [x/10 for x in range(0, 11, 2)]:
    for qx in [x/10 for x in range(0, 11, 2)]:
        um = round(calc_um(sx, qx, k), 1)
        print(f"{sx:g}|{qx:g}|{um:g}")
k = 5
s   | 1 - q | um
0   | 0     | 1
0   | 0.2   | 0.8
0   | 0.4   | 0.6
0   | 0.6   | 0.4
0   | 0.8   | 0.2
0   | 1     | 0
0.2 | 0     | 1
0.2 | 0.2   | 0.8
0.2 | 0.4   | 0.6
0.2 | 0.6   | 0.4
0.2 | 0.8   | 0.2
0.2 | 1     | 0
0.4 | 0     | 1
0.4 | 0.2   | 0.8
0.4 | 0.4   | 0.6
0.4 | 0.6   | 0.4
0.4 | 0.8   | 0.2
0.4 | 1     | 0
0.6 | 0     | 1
0.6 | 0.2   | 5
0.6 | 0.4   | 5
0.6 | 0.6   | 5
0.6 | 0.8   | 5
0.6 | 1     | 5
0.8 | 0     | 0.7
0.8 | 0.2   | 5
0.8 | 0.4   | 5
0.8 | 0.6   | 5
0.8 | 0.8   | 5
0.8 | 1     | 5
1   | 0     | 0.5
1   | 0.2   | 5
1   | 0.4   | 5
1   | 0.6   | 5
1   | 0.8   | 5
1   | 1     | 5
s = 1
k = 1

s | 1 - q | um
1 | 0     | 0.5
1 | 0.1   | 0.5
1 | 0.2   | 0.5
1 | 0.3   | 1
1 | 0.4   | 1
1 | 0.5   | 1
1 | 0.6   | 1
1 | 0.7   | 1
1 | 0.8   | 1
1 | 0.9   | 1
1 | 1     | 1

Betting so that my opponent calls

For the opponent to call I need to bet b such that:

   h(p + b) - (1 - h)b > 0
=> hp + hb - b + hb > 0
=> (2h - 1)b + hp > 0
=> (1 - 2h)b - hp < 0
=> (1 - 2h)b < hp

If h > 0.5, then my opponent will always call.

If h < 0.5, then I need:

b < h/(1 - 2h)*p

Let g be the function defined on [0; 0.5[, such that g(h) = h/(1 - 2h).

We have: g(1/3) = 1.

Hence, to make my opponent call, I should bet the pot when my opponent has a
hand vs range equity of 1/3.

Calculating q

Facing my bet, the opponent will call if and only if:

    h*(p + b) - (1 - h)*b > 0
<=> h > b/(2*b + p)

Hence: q = P(h < 1/2).

Equities

po(hd): probability that the opponent has hand hd
pm(hd): probability that I have hand hd
pw(hd1, hd2): probability of hand hd1 winning against hand hd2
rgo: range of opponent
rgm: my range
|hd|: number of raw hands in hand hd
|rg|: number of raw hands in range rg
s: my equity
h(hdo): equity of opponent given he has hand hdo
r: range vs range equity of opponent

|TT| = 2 parmi 4 = 6
|T3s| = |{Tc3c, Ts3s, Td3d, Th3h}| = 4
|T3o| = 4 * 3 = 12

|rg| = ∑_{hd ∈ rg}(|hd|)

s = ∑_{hdo ∈ rgo}(pw(hdm, hdo) * po(hdo))
  = ∑_{hdo ∈ rgo}(pw(hdm, hdo) * |hdo|) / |rgo|

h(hdo) = ∑_{hdm ∈ rgm}(pw(hdo, hdm) * |hdm|) / |rgm|

r = ∑_{hdo ∈ rgo}(h(hdo) * |hdo|) / |rgo|
  = ∑_{(hdo, hdm) ∈ rgo x rgm}(pw(hdo, hdm) * |hdo| * |hdm|) / (|rgo| * |rgm|)

Knowing my range vs range equity is useful to see what how opponent rates my
hand on average. It helps me understand his actions and hence allows me to
better narrow down his range.

How to estimate my equity faster?

Example:
My range: TT
His range: 22+, ATs+, ATo+
s = ?

First, I identify subranges of similar equities:
-   JJ+: s = 20%
-   AJ-AK: s = 55%
-   22-99: s = 80%

For simplicity, I do not consider hands that share a card with me.

Then I count the weight of each subrange:
-   JJ+: 4*6 = 24
-   AJ-AK: 3*16 = 48
-   22-99: 8*6 = 48

Then I estimate the weighted average. Here the equities of the lower pairs
compensate double the equities of the higher pairs, so we can think of a 60%/70%
equity. The AJ-AK subrange pulls the equity towards 50%, so I estimate an equity
of 60%.

Equities of common scenarios

Preflop

In the table below, all hands are offsuit.

Scenario | Equities
AA, K8   | 85, 15
AA, KK   | 80, 20
A9, 83   | 65, 35
A9, T5   | 65, 35
A7, K8   | 60, 40
A9, 88   | 45, 55
A9, KK   | 30, 70

Scenario      | Equities
22, KJo, QTo  | 30, 35, 30
AA, KJo, QTo  | 75, 10, 15

Scenario      | Equities
A3o, A2o, T4o | 31, 27, 42
A3o, K2o, T4o | 40, 28, 32
36s, 98o, A4o | 27, 38, 34

In the A3o, A2o, T4o scenario, T4o is in its normal 60/40 situation. However,
A3o and A2o are given each half of the 60%. Why? Note that they have 13% chances
of making a tie.

In the A3o, K2o, T4o scenario, why does T4o have a better equity than K2o? Note
that all three hands have an almost 0% chance of making a tie.

Postflop

Use the rule of 2s and 4s.

Ranges

http://www.pokerprofessor.com/university/how-to-win-at-poker/types-poker-hands
https://www.cardplayer.com/poker-tools/odds-and-outs

-   6p-utg: 18%. 44+, A2s+, K9s+, Q9s+, J9s+, T9s, 98s, 87s, 76s, ATo+, KJo+
-   6p-but: 46%. 22+, A2s+, K2s+, Q4s+, J6s+, T6s+, 95s+, 85s+, 74s+, 63s+,
    53s+, 43s, 32s, A2o+, K7o+, Q9o+, J9o+, T9o, 98o
-   9p-utg: 10%. 77+, A5s, ATs+, KTs+, QTs+, J9s+, T9s, 98s, AQo+
-   9p-but: same as 6p-but

-   10%: 77+, A9s+, KTs+, QJs, AJo+, KQo
-   20%: 55+, A3s+, K7s+, Q9s+, JTs, A8o+, KTo+, QJo
-   30%: 44+, A2s+, K3s+, Q7s+, J8s+, T8s+, A5o+, K9o+, Q9o+, JTo
-   40%: 33+, A2s+, K2s+, Q5s+, J7s+, T7s+, 98s, A2o+, K7o+, Q8o+, J9o+, T9o
-   50%: 22+, A2s+, K2s+, Q2s+, J3s+, T6s+, 96s+, 86s+, 76s, A2o+, K4o+, Q7o+,
    J8o+, T8o+, 98o

-   all-in preflop: 22+, ATs+, ATo+

Identify types of preflop hands, to simplify range estimation.

Types of preflop hands:
-   high pairs (hp): TT+
-   high cards (hc): AT+, KT+, QT+, JT
-   low pairs (lp): 22-99
-   suited connectors (sc): 32s-T9s
-   suited ace (sa): A2s-A9s

High pair. We want to be playing against a maximum of 2 opponents. On the flop,
we are looking for cards that are lower than our pair. Range size = 5 * 6 = 30.

High cards. We want to grow the pot a bit to prepare our top pair. On the flop,
we are looking to hit top pair. Range size = 10 * 16 = 160.

Low pair. We want a cheap, multi-way pot. On the flop, we are looking to hit a
third card, which happens 1/8 of the time. Range size = 8 * 6 = 48 ≈ 50.

Suited connectors. We want a cheap, multi-way pot. On the flop, we are looking
for flush and straight draws. Range size = 7 * 4 = 28 ≈ 30.

Suited ace. We want a cheap, multi-way pot. On the flop, we are looking for
flush draws. Range size = 8 * 4 = 32 ≈ 30.

Decisions:
-   raise: act for an expensive pot with few people. Includes raising and
    calling a raise.
-   call: act for a cheap, multi-way pot. Includes calling bb and calling a
    raise where many people are in.

range                  | decision | looking for | Hit frequency | range size
high pair (hp)         | raise    | lower cards |               | 30
high cards (hc)        | raise    | top pair    |               | 160
low pair (lp)          | call     | set         | 1/8           | 50
suited connectors (sc) | call     | draws       |               | 30
suited ace (sa)        | call     | flush draw  | 1/7           | 30

decision | ranges
raise    | hp, hc
call     | lp, sc, sa

Later, I will need to identify smaller ranges and consider position in my
charts:
-   position, range -> decision
-   position, decision -> ranges

Positions:
-   early
-   late

I could also find the decisions and calculate the correct raise sizes using my
equity formulae.

Probabilities

https://en.wikipedia.org/wiki/Poker_probability
https://stattrek.com/poker/poker-probability.aspx?tutorial=prob

The probability of drawing a given hand is calculated by dividing the number of
ways of drawing the hand (frequency) by the total number of 5-card hands (the
sample space; C(52,2).

It is possible to create any type of poker hand through a concise series of
independent choices. The number of ways to produce any type of poker hand is
equal to the product of the number of ways to make each independent choice.

from math import comb

Full house

A full house has three cards of one rank, and two cards of another rank. Each of
the three matching cards is a different suit. And each of the two matching cards
is a different suit.

It requires the following four independent choices to produce a full house:
-   Choose the rank of cards in the hand. A playing card can have a rank of 2,
    3, 4, 5, 6, 7, 8, 9, 10, jack, queen, king, or ace. For a full house, we
    choose 2 ranks from a set of 13 ranks. The number of ways to do this is
    13C2.
-   Choose the rank of the three-card set. From the two ranks chosen in Step 1,
    we choose one rank for the three-card set. The number of ways to do this is
    2C1.
-   Choose a suit for each card in the three-card set. There are four suits,
    from which we choose three. The number of ways to do this is 4C3.
-   Choose a suit for each card in the two-card set. There are four suits, from
    which we choose two. The number of ways to do this is 4C2.

Four of a kind

Independent choices:
-   Choose the rank of the card that appears four times in the hand
-   Choose a rank for the fifth card
-   Choose a suit for the fifth card

Flopping a flush draw

I hold suited cards. Probability of flopping a flush draw, including a flush.

The sample space is C(50,3), assuming an unordered flop.

Independent choices:
-   Choose the ranks of the two suited cards of the flop
-   Choose another card

p = C(11,2)C(48,1)/C(50,3)
  = 13%
  = 1/7

Flopping a three of a kind

I hold a pair. Probability of flopping at least a three of a kind of the same
rank as my pair.

The sample space is C(50,3), assuming an unordered flop.

Independent choices:
-   Choose the suit of the third card of the three of a kind
-   Choose two other cards

p = C(2,1)C(49,2)/C(50,3)
  = 12%
  = 1/8

Examples

sm: showdown equity of me
sv: showdown equity of villain

6p. Me: 50bb on but. Villain: 50bb on bb. Villain is a LAG. Me: TT. utg folds,
hj folds, co folds, I raise to 3bb, sb folds, villain goes all-in. Me: ?

o ≈ 1:1
t = c/(p + c) = 47/(53.5 + 47) ≈ 0.5

My range: 20%. 55+, A3s+, K7s+, Q9s+, JTs, A8o+, KTo+, QJo 
His range: 22+, ATs+, ATo+
sm = 40
sv = 55

This explains why he raised.

My range: TT
His range: 22+, ATs+, ATo+
sm = 60
sv = 40

sm ≈ (48*55% + 24*20% + 48*80%)/120
   = 58%

My range: TT
His range: 88+, ATs+, ATo+
sm = 50
sv = 50

I should have called.

Suppose you are out of position (OOP) in the big blind as the preflop caller
holding J♦ 7♦.

You’ve just called a c-bet on the flop.

On the turn, the board is K♦ T♦ 3♠ 2♥, and you face a $50 bet into a $67 pot
from the player on the button, who has $117 behind (you cover).

Opponent range pre-flop: hp, hc
Opponent range flop: hp, hc (let's say his c-bet could be a (semi-)bluff)
Opponent range turn: AA, KK, TT, A♦Q♦, A♦J♦, Q♦J♦, AK, KQ, KJ, KT

My range pre-flop: hc (I would have raised with hp)
My range flop: A♦Q♦, A♦J♦, Q♦J♦, AK, KQ, KJ (I would have raised with KT)
My range turn if I call: A♦Q♦, A♦J♦, Q♦J♦, AK, KQ, KJ
My range river if I donk bet: A♦Q♦, A♦J♦, Q♦J♦, AK, KQ, KJ

s = 18% (I have 18% against almost all of his hands)
o = 117:50

Suppose I hit the flush. I have 4 flush hands in my 36 hands range, but my
opponent has sets and double pairs in his range, which win against all my 32
left hands. Hence the range vs range equity of my opponent is pretty high, let's
say 70%.

Guessing x and deducing t

If I donk bet, my opponent will fear the flush and might not follow a very high
bet.

If I check, since my range has only few flush hands, my opponent might bet, e.g.
60% of the time. In this case, I will likely win the rest of his stack, since he
will not have much left to call. Hence:

x = 0.6 * 117 ≈ 70

We have:

t = c/(p + x + c)
   = 21%

Hence I should not call, but it is close.

Guessing s and deducing x'

x' = c/s - p - c
   = 50/0.18 - 117 - 50
   = 110

Since the opponent has only 117 left, this means that I would need to win his
stack 100% of the time where I hit at the river. This is not very realistic. So
I must fold.

6p. Me (stack: 30bb, pos: bb, hand: 22). utg (stack: 20bb) all-in. but (stack:
20bb) calls. Me ?

p = 40
b = 20
u = 0.5
t = u/(1 + u) = 0.33
s = 0.32

s < t so I should fold.

If I call 32% of the time I win 40, and 68% of the time I loose 20. Hence if I
call I can expect to win 12 and to loose 13.6. Hence I should fold.