work in progress blackjack side bet analysis tool

Discussion in 'Skilled Play - Card Counting, Advanced Strategies' started by blazin22, Jul 25, 2009.

  1. blazin22

    blazin22 Active Member

    Hey guys,
    I am currently working on a 21+3 side bet analysis tool for blackjack. My code is only in a preliminary state, I am having a few problems replicating the odds listed in the casinos "house edge" booklet which shows a house edge of 3.29% which is consistent with the wizard of odds site which shows 3.24.
    assuming six decks.

    bash-3.2$ ./pairplus.exe
    pairplus <Number of Decks> <penetration>

    where penetration is an number between 0 and 311

    bash-3.2$ ./pairplus.exe 1000000 52
    deck penetration level 83.333333%
    rounds per shoe 87
    total shoes played 1000000
    hands won per shoe 8.418248
    winrate in percentage terms -3.585452%

    As you can see I get around 3.58 house edge, which is a huge difference from the that given in the booklet. I must be doing something wrong and I can't even begin to start analyzing any advantage obtainable by counting cards in this game until I can exactly replicate the house edge.

    Source code is available at (Dead link:

  2. I don't understand your computer language so I can't effectively debug, but the difference you get is around what I would expect for failing to consider both A23 and AKQ are straights.

    This sidebet will be tough to beat. The Royal Match sidebet on a SD game gets nearly all of its return from a two-card "flush", and counting all four suits independently only earns you a return of around 1.5% or so, not worth it alone in a SD game especially given a $100 limit on the sidebet. But with exactly one partner at the table and counting both BJ and the sidebet, it is worth it.

    The 21+3 sidebet takes less than 100% return from the flush and is offered on shoe games. You will probably need a team of 3 guys working together to do this.
  3. blazin22

    blazin22 Active Member

    Yes I initially made that mistake but have since fixed it, checking for a flush and three of a kind is very easy, the straight is relatively complex in comparison, I use the following algorithm,

    Because the three cards are not in order and I didn't want to use a sorting algorithm for speed purposes, I do the following,

    0 = A in my program so I first check if any of the three cards contain an 0 if they do I just add the other two numbers up and see if they equal 23, since Q = 11 and k= 12 so (A and QK) or (A and KQ) would be 0 and 23.

    for the other straights adding one to lowest number and subtracting 1 from the highest number and seeing if there is a match.

    The odds still don't match those given on wizzardofodds.

    I'm reconsidering it now, I don't have any play partners. I want to play alone. I'm thinking about converting my program into a simulation for the four deck blackjack ENHC game I have locally so I can study the return from my play style. I don't want to buy CVCX.
  4. UK-21

    UK-21 Well-Known Member

    Hold on . . . . I'll get some of my mates who work as suits and PBs in various casinos around the UK to get in touch. I'm sure they'll be able to help. What're your contact details . . . .? ? ? Can you send a photo just so they know who they're dealing with?
  5. Sonny

    Sonny Well-Known Member

    Thanks for including your C++ source code! We don’t see a lot of code around here so I’m always excited to see what other people are doing. It looks like you spent a lot of time on that project and I’m sure we can get it working properly with some tweaking.

    Unfortunately, the program needs a lot of work. Even if we found the bug, the program would still be waaaaay too slow. There is a lot of cleaning up we need to do before we start debugging it. You’ve got structures within structures, a very inefficient shuffle procedure, several functions that only perform one line of code and a lot of extra overhead that we can shed. Let’s simplify this program as much as we can first. I tried to use the same variable names from your program in the examples below.

    The first step is to dump the built-in RNG. It’s crap and it isn’t sufficient for simulations. Slap in the RNG below:

    #include <iostream>
    double u[98],c,cd,cm;  // make sure these are global variables
    int i97,j97;
    void initRNG(int ij,int kl) {
       int i,j,k,l,m,ii,jj;
       double s,t;
       if (ij<0 || ij>31328 || kl<0 || kl>30081) {
          cerr << "Seed1 must be between 0 and 31328" << endl;
          cerr << "Seed2 must be between 0 and 30081" << endl;
       } else {
          i = 2 + int(ij/177)%177;
          j = 2 + ij%177;
          k = 1 + int(kl/169)%178;
          l = kl%169;
          for (ii = 1; ii <= 97; ii++) {
             s = 0.0;
             t = 0.5;
             for (jj = 1; jj <= 24; jj++) {
                m = (k*((i*j)%179))%179;
                i = j;
                j = k;
                k = m;
                l = (53*l + 1)%169;
                if ((l*m)%64 >= 32) s = s + t;
                t = 0.5*t;
          u[ii] = s;
       c =    362436.0/16777216.0;
       cd =  7654321.0/16777216.0;
       cm = 16777213.0/16777216.0;
       i97 = 97;
       j97 = 33;
    double GetRND() {
       double uni;
       uni = u[i97] - u[j97];
       if (uni < 0.0) uni = uni + 1.0;
       u[i97] = uni;
       if (--i97 == 0) i97 = 97;
       if (--j97 == 0) j97 = 97;
       c = c - cd;
       if (c < 0.0) c = c + cm;
       uni = uni - c;
       if (uni < 0.0) uni = uni + 1.0;
       return uni;
    Initialize the RNG by calling initRNG(seed1,seed2) where seed1 is between 0 and 31328 and seed2 is between 0 and 30081. Then replace every rand() statement with GetRND(). That will improve the results.

    The shuffle procedure can be greatly condensed as well. Something simple like this should work:

    if (Offset > 233) {            // Past the virtual cut card?
       for (int i=311; i>0; i--) {
          a = int(GetRND() * i);   // Pick a random card and
          swap1 = Shoe[a];         // swap it with the current one
          Shoe[a] = Shoe[i];
          Shoe[i] = swap1;
       Offset = 1;                // burn card 0
    The shuffle procedure can be one of the most time-intensive (and frequently used) routines in a simulator so you want it to be as quick as possible. The code above also allows you to dump the cut_cards procedure completely. Once we get rid of the structures (see below) we can use the assembly instruction xchg to make things even easier.

    Next, dump all those structures. The only information you need is the card’s rank and suit. That can be held in a single byte (or short int). The lower four bits can hold the rank (decimal number between 0 and 12) and the upper four bits can hold the suit information. Each upper bit can represent a suit, so 0b1000 might be hearts, 0b0100 spades, 0b0010 clubs and 0b0001 diamonds. Packing everything into a single byte saves you a lot of space and overhead from the structures. Checking for flushes is now a simple bitwise operation:

    if (Shoe[Offset] & Shoe[Offset+1] & Shoe[Offset+2] & 0xf0) {
       // you’ve got a flush  
    } else {
       // no flush
    The algorithms you have (check_flush, check_straight, etc.) are fine, but lookup tables would be faster. There are 741 unique Three Card Poker hands so you just need an array that gives a rank to each one. For example, an A,Q,K flush would be ranked 1, a K,Q,J flush would be ranked 2…all the way down to an off-suit 5,3,2 with a value of 741. Checking for a winning hand would only require that all of the paying hands be at the top of the list (ranks 1-311 I believe, but I'm not sure about that yet) and you could check for ALL winning payouts like this:

    if (HandRank < 312) {          
       // Flush or better gets paid
    } else {                         
       // Otherwise you lose
    You can also dump a lot of those library files, but that’s just nitpicking. :)

    Those are just a few ideas I had off the top of my head. I’m working on a simulator for this side bet now so I’ll post the code when it’s done. I'm sure my code can be optimized quite a bit and maybe other members will have some suggestions.

  6. sagefr0g

    sagefr0g Well-Known Member

    is there valid source code available for like just making a player flat bet and play basic strategy over a bunch of deals? i mean with out all the bells and whistles, sort of stuff?

    i looked at power sim's code, but it has so many bells and whistles.

    i'd like to have a 'stripped' down sort of thing that i could then get to do stuff i wanted, with out all the other 'baggage'.
  7. blazin22

    blazin22 Active Member

    Hey Sonny, Thanks for the input, If I dump the use of nested structures and/or structures entirely I will have to recode 90% of the program. This is exactly what I am doing.

    I strongly suspect my use of rand() is the reason for the wrong output. I've been told I should look into using the time stamp counter within the CPU to generate a much more random value, RDTSC. This would obviously involve the use of some inline assembler which would break the potential for future portability or I could just use your random function.

    i'm going to spend some time on it this weekend see if I can recreate it all, I'd eventually like to create a full blackjack simulator but thats a long way off.
  8. Sonny

    Sonny Well-Known Member

    Some Corrections

    The 741 unique hands are only for a single-deck game. The 21+3 game uses six decks, which allows the possibility of suited pairs and even suited three-of-a-kinds. That brings the number of unique hands up to 911. Only the top 480 hands are winners, but using the lookup table makes that an easy fix. Just adjust the line if(HandRank < 312) to if(HandRank < 481) and you’re ready to roll. It’s much easier than using logic-based strategies that you have to rewrite every time you need to test a different strategy, which happens often.

    The lookup table also makes it easier to keep track of the frequencies of each hand type for debugging purposes. You can check for any hand type (or even a specific three-card hand) just by looking at the hand’s unique rank. Here’s how my program tallies the hand frequencies:

    if (PlayerRank < 1 || PlayerRank > 911)
       // On error, dump the hand contents to the output file
    else if (PlayerRank < 13) 
    else if (PlayerRank < 169)
    else if (PlayerRank < 182)
    else if (PlayerRank < 456) 
    else if (PlayerRank < 469)
    else if (PlayerRank < 481) 
    else if (PlayerRank < 638) 
    That way you can see right away which hands are creating a problem. It can save you a lot of time tracking down the bugs.

    If anyone is interested in other portions of my code (or the entire program) I'm happy to share. I'd also love to hear some tips/criticisms from other members. I know at least 4 of you write your own simulators! :)

  9. blazin22

    blazin22 Active Member

    Nice, I'm going to use a lookup table for my simulator. When I first started looking at advantage play in blackjack it was mostly your postings I'd read to further my understanding of card counting. I didn't think you were also a programmer. Counting/shuffle tracking/simulator coding, you're like the complete AP package :eek:. I'm going to finish my sim before I ask to see all of your code, I won't have the same sense of accomplishment if I just copy/paste all of yours :grin:.
  10. stophon

    stophon Well-Known Member

    I have a sim that generates an accurate house edge, but its far from efficient or intelligently programmed and it is written in vb.
  11. sagefr0g

    sagefr0g Well-Known Member

    thank you stophon, but that doesn't sound like what i'm looking for.

    i'm interested in a sim with code that doesn't calculate anything, just plays the game for a given number of rounds, like a player using basic strategy, flat betting against the dealer for what ever game rules.
  12. stophon

    stophon Well-Known Member

    Yeah, thats what it does and then afterwards it tells you how the player did. But like I said I doubt anyone could understand the code besides me.
  13. blazin22

    blazin22 Active Member

    found bug

    bug has been found, It was the way I was calculating the percentages. :cry:
    I'm now also using a much better PRNG.
  14. KenSmith

    KenSmith Administrator Staff Member

    Excellent. It's always nice to get your code to spit out accurate numbers. Even the simplest games present some interesting programming challenges. Good luck with your future efforts.
  15. stophon

    stophon Well-Known Member

    I find blackjack to be a difficult game to program to. It took me awhile to get my program to deal with pair splits correctly.
  16. Canceler

    Canceler Well-Known Member

    Handling split aces was enough to make me decide to use someone else's program, rather than finish my own. :(
  17. sagefr0g

    sagefr0g Well-Known Member

    are you referring to your "PlayBj" excel visual basic program?
  18. Canceler

    Canceler Well-Known Member

    No, I once tried to make my own simulator. PlayBJ was just a game, where the player can see that he has aces, can split them knowing he only gets one card on split aces, and can resplit if he gets another ace. Trying to make all that happen automatically with programming is just a nightmare.
  19. Sonny

    Sonny Well-Known Member

    Here’s a slow but intuitive way to do pair splits:

    if (hand is soft) {
       // Split to a new hand here (split cards and add another bet)
       do {
          // Look at the hit card
          if (hit card is another ace && allowed to re-split)
             // Re-split to another hand
          else {
             // Hit the current hand once
             // Move to next hand
       } while (currentHand <= totalHands)
    } else {
       if (BS says to split) {
          // Split to a new hand
          do {
             // Look at the hit card
             if (hit card is the same rank && still allowed to split)
                // Re-split to another hand
             else {
                // Hit the current hand once
                // Move to next hand
          } while (currentHand <= totalHands)
       // Handle double downs
       // Handle hits
    // Do payouts
    The code assumes that you will always split aces and that any hand you split will also be re-split, although that could easily be changed. The majority of the code will depend on the variables and structures that your program uses so all I can really give is a basic framework for the routines. I’m sure there are much better ways to do it, but that’s all I got for you.

  20. nightspirit

    nightspirit Well-Known Member

    Sonny, thanks for the great posts! And thanks Blazin for sharing the code!

    Hey Fr0g, maybe the short programs of T-Hopper are something for you:;read=1042 (Archive copy) Python is open source and free available here. Save the code in .py file and run it. But change the expression "whrandom" to "random" before you do so. Pay attention to the white space, that's VERY import in Python! ;) There are a lot more versions of T-Hopper's PyBJ. Use Google and search for:
    Code: PyBJ t-hopper

Share This Page