SkillAgentSearch skills...

CandyPoker

Poker Equity Calculator, and Game tree solver

Install / Use

/learn @sweeterthancandy/CandyPoker
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

CandyPoker

This is a C++ poker project authored by Gerry Candy, which aims to be a general C++ poker library. This project was started in 2017 with the original goals of,

  • Be a fast evaluation library
  • Solve three-player push-fold
  • Provide an framework for other poker software

The original goals have now been achived

The requirements are boost, and Eigen. Although Eigen is just used for Vector/Matrix multiplication.

Getting Started Windows

    REM you first need to clone https://gitlab.com/libeigen/eigen.git to somewhere
    REM also get some version of boost 
    git clone https://github.com/sweeterthancandy/CandyPoker.git
    cd CandyPoker
    mkdir deps
    cd deps
    git clone https://gitlab.com/libeigen/eigen.git
    git clone https://github.com/google/googletest.git
    git clone https://github.com/google/benchmark.git
    cd ..
    cmake -B build -DCMAKE_BUILD_TYPE=Release -DBoost_INCLUDE_DIR=C:\work\boost_1_77_0\boost_1_77_0
    REM open build\ps.sln and build
    

Getting Started Linux

    git@github.com:sweeterthancandy/CandyPoker.git
    cd CandyPoker
    mkdir deps
    cd deps
    git clone https://github.com/google/googletest.git
    cd ..
    cmake -G Ninja -DCMAKE_BUILD_TYPE=Release .
    ninja



    # simple equity evaluation
    ./candy-poker eval AA KK 
    |range| equity |  wins  |draw_1|draw equity| sigma  |
    +-----+--------+--------+------+-----------+--------+
    | AA  |81.9461%|50371344|285228|142614.00% |61642944|
    | KK  |18.0539%|10986372|285228|142614.00% |61642944|
    # create binary all in equity cache
    ./candy-poker read-cache --file Assets/PushFoldEquity.json

    # now create the cache
    ./candy-poker read-cache
    mv .cc.bin.stage .cc.bin

    # now create HU push-fold table
    ./candy-poker solver --solver quick-solver --eff-lower 1.0 --eff-upper 20.0 --eff-inc 1.0 --cum-table --print-seq

                           sb push/fold

        A    K    Q    J    T    9    8    7    6    5    4    3    2   
      +-----------------------------------------------------------------
    A |20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0
    K |20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 19.0 19.0
    Q |20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 16.0 13.0 12.0
    J |20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 18.0 17.0 13.0 10.0 8.0 
    T |20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 11.0 10.0 7.0  6.0 
    9 |20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 14.0 5.0  4.0  3.0 
    8 |20.0 18.0 13.0 13.0 16.0 20.0 20.0 20.0 20.0 18.0 8.0  2.0  2.0 
    7 |20.0 16.0 10.0 8.0  9.0  10.0 14.0 20.0 20.0 20.0 14.0 2.0  2.0 
    6 |20.0 15.0 9.0  6.0  5.0  4.0  7.0  10.0 20.0 20.0 16.0 7.0  2.0 
    5 |20.0 14.0 8.0  6.0  4.0  3.0  2.0  2.0  2.0  20.0 20.0 12.0 2.0 
    4 |20.0 13.0 8.0  5.0  3.0  2.0  2.0  2.0  2.0  2.0  20.0 9.0   1  
    3 |20.0 12.0 7.0  5.0  3.0  2.0   1    1    1    1    1   20.0  1  
    2 |20.0 11.0 7.0  4.0  2.0  2.0   1    1    1    1    1    1   20.0

                      bb call/fold, given bb push

        A    K    Q    J    T    9    8    7    6    5    4    3    2   
      +-----------------------------------------------------------------
    A |20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0 20.0
    K |20.0 20.0 20.0 20.0 20.0 20.0 17.0 15.0 14.0 13.0 12.0 11.0 10.0
    Q |20.0 20.0 20.0 20.0 20.0 16.0 13.0 10.0 10.0 8.0  8.0  7.0  7.0 
    J |20.0 20.0 19.0 20.0 18.0 13.0 10.0 8.0  7.0  6.0  6.0  5.0  5.0 
    T |20.0 20.0 14.0 12.0 20.0 11.0 9.0  7.0  6.0  5.0  5.0  4.0  4.0 
    9 |20.0 17.0 11.0 9.0  8.0  20.0 8.0  6.0  5.0  5.0  4.0  4.0  3.0 
    8 |20.0 13.0 9.0  7.0  6.0  6.0  20.0 6.0  5.0  4.0  4.0  3.0  3.0 
    7 |20.0 12.0 7.0  6.0  5.0  4.0  4.0  20.0 5.0  4.0  4.0  3.0  3.0 
    6 |20.0 11.0 7.0  5.0  4.0  4.0  4.0  4.0  20.0 4.0  4.0  3.0  3.0 
    5 |20.0 10.0 6.0  5.0  4.0  3.0  3.0  3.0  3.0  20.0 4.0  4.0  3.0 
    4 |18.0 9.0  6.0  4.0  3.0  3.0  3.0  3.0  3.0  3.0  20.0 3.0  3.0 
    3 |16.0 8.0  5.0  4.0  3.0  3.0  2.0  2.0  2.0  3.0  3.0  20.0 3.0 
    2 |15.0 8.0  5.0  4.0  3.0  3.0  2.0  2.0  2.0  2.0  2.0  2.0  15.0

Poker Evaluation

The poker evaulation doesn't support any boards, as this is mostly noise in the code as the preflop all-in with 5 cards to be dealt is the target case.

    ./candy-poker eval TT+,AQs+,AQo+ QQ+,AKs,AKo TT
    |    range    | equity |   wins   | draw_1  | draw_2 | draw equity |   sigma   |
    +-------------+--------+----------+---------+--------+-------------+-----------+
    |TT+,AQs+,AQo+|30.8450%|3213892992|566688312|41308668|297113712.00%|11382741216|
    | QQ+,AKs,AKo |43.0761%|4637673516|503604216|41308668|265571664.00%|11382741216|
    |     TT      |26.0789%|2923177728|63084096 |41308668|45311604.00% |11382741216|
     3.611110s wall, 3.580000s user + 0.010000s system = 3.590000s CPU (99.4%)

Two Player Push Fold EV

For solving push/fold games, a distinction has to be made between mixed solutions and minimally-mixed solutions. From a game theory perspective there should exist a GTO solution where each decision has only one holdem hand type mixed, meaning we can partition a strategy into {PUSH,FOLD,MIXED}, where MIXED has only one holdem hand type like 68s etc. What this means is that, it we are only looking for any solution, we can run a algorithm of

    Stategry FindAnyGTO(Stategry S){
            double factor = 0.05;
            for(; SomeCondition(S); ){
                    auto counter = CounterStrategy(S);
                    S = S * ( 1- factor ) + counter * factor;
            }
            return S;
    }

The above will converge to a GTO solution, if you where to program a computer to play poker, however we want to find a minimally mixed solution. This is a much hard problem, as there is no simple algorithm which would converge to a minially mixed solution because for any solution, there are going to be a subset of hands which are almost indifferent to being wither PUSH or FOLD, causing computation problems.

Another consideration when looking for solutions, is that although most solvers are compoutaitionally tangible for two player, three player push fold is much much slower, we have to create a suitable algorith.

The solution to these algorithms, was to chain several different algorithms together, with special metrics. For this I developed these concepts

Solution.Gamma

The Gamma for a solution is the number of hands, for which the counterstrategy is different. From the game theory we know that their exists a GTO mixed solution, and also we know that there exists a GTO solution with each player only having one mixed card (I think thats right). Gamma is an array of sets corresponding to each player. For example for HU, we might have a gamma of {{Q5s}, {64o,QJo}}, which would indicate that for the SB the hand Q5s is pretty indifferent to PUSH/FOLD, and also for BB 64o and QJo is indifferent to PUSH/FOLD.

Solution.Level, Solution.Total

The Level of a solution is the maximum number of Gamma cards for each player. So a gamma vector of {{Q5s}, {64o,QJo}} would correspond to (1,2), so the level of the solution is 2, and the total is 3.

Solution Sequence

Find GTO poker solutions is a stochastic process, as we are finding the find the best solution possible. This means that we are basically producing a sequence of candidate solutions {A0,A1,A2,...}, and from this we create a best to date sequence {B0,B1,B2,...}.

We have three solvers

  • simple-numeric
  • single-permutation
  • permutation

simple-numeric

This is the most basic solver, and is the basic FindAnyGTO() function that has been discussed. As an implementation detail we create an infinite loop, keeping taking the linear product of the two solutions, with a time to live variable of say 10, then if we have 10 iterations with a new best-to-date solution, we return that solution.

single-permutation

This is an algebraic solver, each takes the trail solution S, and creates a set with each gamma cards (a particilar card for with S is different from CounterStrategy(S)), with the trail solutions card replaced with either PUSH or FOLD, with all other cards the same. This means that with a gamma vector of {{Q5s}, {64o,QJo}} we would have 2^3 candidate solutions { S with Q5s for player 1 FOLD, S with Q5s for player 1 PUSH, S with 64o for player 2 PUSH, ...}. We then see if any of these new strategies is a "better" solution, if this is the case we restart the algorith. This algorithm would find the specific solution, depending on the path taken by the best-to-date sequence.

permutation

This is another algebraic solver, which tries to replace take the Gamma vector, and replace all but 1 card per decision with a mixed solution, and discretized the mixed solution with a grid.

For eaxmple with a Gamma vector of {{Q5s}, {64o,QJo}}, we would have gamma vectors of

            (m)(ff)
            (m)(pf)
            (m)(fp)
            (m)(pp)
            (f)(mf)
            (p)(mf)
            (f)(mp)
            (p)(mp)
            (f)(fm)
            (p)(fm)
            (f)(pm)
            (p)(pm),

where in the above, we replace each m with one of (0,1/n,2/m,..,(n-1)/n,n). We then take the best solution

Aggregate solvers

After much expreimentation I found that the best thing was a mix of different solvers, for which are described in a JSON file called .ps.solvers.json. Below is the description for the accurate-solver. Here we first apply the numeric solver with a factor of 0.4 (which means take 40% of the new solution), by considering a total

View on GitHub
GitHub Stars13
CategoryDevelopment
Updated10mo ago
Forks3

Languages

C++

Security Score

82/100

Audited on May 10, 2025

No findings