Combinatorial Analysis Programming

assume_R

Well-Known Member
#1
Hey all. I'm working on a CA program, and I'd appreciate some input (probably from k_c since I know he's created one).

Thus far, I have the exact same output as http://www.bjstrat.net/cgi-bin/cdca_web.exe for Dealer Probabilities, which means I'm onto a good start.

So now I have to actually do the player probabilities, but I'm having some trouble understanding the way to do bonuses (such as BJ, or 5-card bonuses), and eventually I'll have to incorporate splits and doubling and surrendering (right now I will start with just H/S).

Here's my understanding of how to do it:
Let's say I have a 16v10, and b.s. says to hit. So I give myself each card, and every result will either be a +100% (win), 0% (push), -100% (loss), or +150% (for example for a 5-card 21 bonus if I get a 5, and my 16 was made up of 4 cards). I take that EV (is that actually EV?) and multiply by the probability of that card being dealt to me, and then I add that all up for all possibilities.

Correct? Thanks in advance to everybody.
 

FLASH1296

Well-Known Member
#2

Obviously, in relative terms, I am an innumerate proto-simian; unable to polish your (mathematical) boots; but isn't what you are deriving a weighted average of all possible outcomes for a continually modified shoe; Viz 48 x Z decks, minus the cards incorporated into the hand. That multitude of hand matchups need to be Composition-Dependent as player hands can be constructed in numerous ways. I cannot imagine the impressive array of multi-card hands that need to be considered for a (virtually) accurate result.

I am unable to grasp how many combinations of cards there are to create a hard 17 when working with 32 Aces, 32 Deuces, etc. That short-circuits my measly little underpowered brain. Weighing those would not fluster me as the probability of all of the particular 7 or 8 or 9 card stiff (hard) hand is computable, (from a finite shoe); but it would need to be computed for HOW MANY different arrays ?

I am too old to plow through it via brute force like The original "Four Horseman" (Baldwin, McDermott, Cantey, and Maisel) did in the mid 1950's.
 

London Colin

Well-Known Member
#3
assume_R said:
Here's my understanding of how to do it:
Let's say I have a 16v10, and b.s. says to hit. So I give myself each card, and every result will either be a +100% (win), 0% (push), -100% (loss), or +150% (for example for a 5-card 21 bonus if I get a 5, and my 16 was made up of 4 cards). I take that EV (is that actually EV?) and multiply by the probability of that card being dealt to me, and then I add that all up for all possibilities.

Correct? Thanks in advance to everybody.
Sounds like you've got the gist of it.

But your CA is going to determine what BS is for any given set of rules, including whatever bonuses exist. So you always calculate the EV of both standing and hitting, and compare the two.

E.g., for a particular 4-card 16v10 you might find that with a +100% payoff, standing has the higher EV, but with the 5-card 21 bonus, hitting is better.

You will pursue every hand to its bitter end, hitting until you get to a total of 21. Thus you will have the EV of standing on every possible hand that can be reached.

Then you work backwards through the hands with totals of 20, 19, 18, etc, calculating the EV of hitting those hands. E.g., if you hit 20, the EV is the prob of receiving an ace, multiplied by the EV of the resulting hand of 21, minus the prob of busting. You assign the higher of the two EVs to the hand, along with the corresponding action.

Now when you consider the 19s, you have the probability of reaching 20 or 21 to consider, and you have the EVs of the hands with those totals handily already calculated.

And so on...

[This method derives the optimal, composition-dependent strategy. Surprisingly, it seems to be rather more difficult to come up with a mechanism which guarantees the best total-dependent strategy.]
 

assume_R

Well-Known Member
#4
London Colin said:
Now when you consider the 19s, you have the probability of reaching 20 or 21 to consider, and you have the EVs of the hands with those totals handily already calculated.
Thanks, London Colin. I have just gotten a result exactly the same as one of the elements in k_c's 12v2 value, so another proof of concept.

However, there is a slight problem with what you just described, as per my understanding. Your old EV of the 20 was taking into account only 2 10's, for example, missing from the deck. But now if you have a 19, you presumably have another 10 gone from the deck and now a 9 gone from the deck, which means you can't just use the old EV for your 20. Obviously this will only make a significantly difference in single deck games, but still.

And thanks, I will be using this in part to try out different indices and I have realized that the order that I generate the indices will matter slightly, since as you pointed out your 19 decision depends on your 20 decision, and your 12v3 decision, for example, will depend on your index (or decision) for 13v3 and 14v3, etc.

I am confident I am on the right track, as I was able to get the same # as k_c, but now I may need some help once I start dealing with splitting...
 

London Colin

Well-Known Member
#5
assume_R said:
Your old EV of the 20 was taking into account only 2 10's, for example, missing from the deck. But now if you have a 19, you presumably have another 10 gone from the deck and now a 9 gone from the deck, which means you can't just use the old EV for your 20. Obviously this will only make a significantly difference in single deck games, but still.
The idea is not to calculate a single EV for 20. You generate separate EVs for every possible hand with any given total.

Step one is to generate a tree of every possible hand of blackjack. The end points of that tree are the hands with a total of 21.

You work backwards from those, filling in the missing EVs as you go.

So when you consider a particular holding of 19, drawing an ace or a deuce will take you to particular holdings of 20 and 21, whose EVs you know precisely.
 

assume_R

Well-Known Member
#6
London Colin said:
So when you consider a particular holding of 19, drawing an ace or a deuce will take you to particular holdings of 20 and 21, whose EVs you know precisely.
The way I programmed it currently is recursive, but I don't have the resulting tree saved...

So you're saying that when I already generated the total EV of a 20, I had considered a 10-9-A. What I was saying is that when I have a 10-9, and I take the probability of drawing an Ace (which would be 4/49), I should only be considering the branch of the tree derived from a 10-9-A, (because of the effect of removal of those specific cards). I shouldn't also be including the EV's of the 10-10 branch.

But again, maybe we have different styles of programming, but why do you say the 21's are the nodes of the tree? Anytime you go over 21 that should also be a node (with an EV of -100%), for example, right?
 

MangoJ

Well-Known Member
#7
My CA works the other way around, it calculates the probability of the dealer standing on 17-21 (and busting), for a given upcard and remaining deck composition. This is my "endgame library", for SD play it is 10 x 1GB of data, and consumed about a week for computation on my netbook.

For player strategy, I just traverse a combinatorial tree for every possible hand until I bust, and on each node I multiply my hand's total with the correct entry from the "endgame library" - selected on the remaining deck composition (and upcard). This is the stand-EV. The hit-EV is then just the hand-EV of the 10 child-hands with their probability of drawing. The hand-EV itself is then (for best strategy) the maximum of stand-EV and hit-EV.

If you have stand-EV and hit-EV, a double-EV is simple: it is just the stand-EV of all child-hands (twice).

This would be the same as a logic of a "simple" chess computer, i.e. work from both ends of the game. Unlike chess however the middle part can be resolved exactly (as there are no cyclic Blackjack hands, and only a "few" possible anyway).

Personally I'm happy with my CA, as I can easily modify for other rules (i.e. Charlie), although it only works on single deck. Although it is far from the best algorithm (a better way would be to generate all valid player hand + dealer hand combinations, and apply a simple payout table for stand-EV). It served my curiosity well enough as a hobby programmer.

Colin, I always assumed that a total-dependent strategy is just the strategy for an infinite deck - which is pretty easy to implement as drawing probabilities are always fixed (1/13 or 4/13). Can be written in a spreadsheet in just an hour.
 

London Colin

Well-Known Member
#8
assume_R said:
The way I programmed it currently is recursive, but I don't have the resulting tree saved...
Yes, recursion is almost certainly the way to go. The CA with which I have some familiarity, originally written by Eric Farmer, recursively generates a tree-like data structure and then invokes various recursive functions that manipulate that tree. (If I recall correctly:))


assume_R said:
So you're saying that when I already generated the total EV of a 20, I had considered a 10-9-A. What I was saying is that when I have a 10-9, and I take the probability of drawing an Ace (which would be 4/49), I should only be considering the branch of the tree derived from a 10-9-A, (because of the effect of removal of those specific cards). I shouldn't also be including the EV's of the 10-10 branch.
I think we are saying the same thing. Sounds like you may have slightly misunderstood me. (It's not a subject that I find it terribly easy to talk about in a clear fashion. :grin:)

To re-iterate, there's no reason to generate an overall total EV for 20. Every possible hand composition has the following attributes:
  • a total
  • an EV
  • a set of precursor hands from which this hand can be reached by drawing a card.
So when considering hitting T,9, all we have to worry about is the three possible outcomes: BUST, T,9,A and T,9,2.
Bust has an EV of -1.
T,9,A and T,9,2 have EVs which have already been calculated.

assume_R said:
But again, maybe we have different styles of programming, but why do you say the 21's are the nodes of the tree? Anytime you go over 21 that should also be a node (with an EV of -100%), for example, right?
Well its a notional tree, which might or might not have a concrete representation as a data structure in your code.

I'm sure there are any number of variations possible, when it comes to the details of actual implementation. I was trying, as much as possible, to gloss over the details and just talk about the essentials of the algorithm.

You could certainly say that the bust hands from part of the tree, but they are not a very useful part, and you certainly wouldn't want to incorporate them into any data structures.

If our purpose is to fill out the missing information (the EVs and the corresponding actions that give rise to those EVs.) E.g., STAND on T,T vs 5 for an EV of whatever it is, then there is no missing information for the bust hands: the EV is -1, and there are no more actions to be taken.
 

London Colin

Well-Known Member
#9
MangoJ said:
My CA works the other way around, it calculates the probability of the dealer standing on 17-21 (and busting), for a given upcard and remaining deck composition. This is my "endgame library", for SD play it is 10 x 1GB of data, and consumed about a week for computation on my netbook.
Repeatedly calculating the dealer probabilities is by far the most time-consuming part of the whole process. I once profiled Eric's library; I think I found it was spending something like 98% of its time in the function BJDealer::computeProbabilities().

So your week of computation might be time well spent, if it has produced results once that can be reused again and again. (Presuming looking them up in such a large data file doesn't take longer than generating them on demand.)

MangoJ said:
For player strategy, I just traverse a combinatorial tree for every possible hand until I bust, and on each node I multiply my hand's total with the correct entry from the "endgame library" - selected on the remaining deck composition (and upcard). This is the stand-EV. The hit-EV is then just the hand-EV of the 10 child-hands with their probability of drawing. The hand-EV itself is then (for best strategy) the maximum of stand-EV and hit-EV.
Presumably you work backwards from 21 here, as I described, so that your child hands' EVs are known. (They could be stand EVs or hit EVs.)


MangoJ said:
If you have stand-EV and hit-EV, a double-EV is simple: it is just the stand-EV of all child-hands (twice).This would be the same as a logic of a "simple" chess computer, i.e. work from both ends of the game. Unlike chess however the middle part can be resolved exactly (as there are no cyclic Blackjack hands, and only a "few" possible anyway).

Personally I'm happy with my CA, as I can easily modify for other rules (i.e. Charlie), although it only works on single deck. Although it is far from the best algorithm (a better way would be to generate all valid player hand + dealer hand combinations, and apply a simple payout table for stand-EV). It served my curiosity well enough as a hobby programmer.
You (and assume_R) have my admiration. I found it difficult enough to gain enough understanding of Eric's code to be able to make my own modifications to it. The thought of starting from scratch on such a project fills me with dread! :grin:

MangoJ said:
Colin, I always assumed that a total-dependent strategy is just the strategy for an infinite deck - which is pretty easy to implement as drawing probabilities are always fixed (1/13 or 4/13). Can be written in a spreadsheet in just an hour.
I don't think so. (At least not in theory. As a practical matter that might be OK; I don't know.)

I suspect there are various ways to tackle the problem, but I don't know of one that is guaranteed to be optimal, at least not without making some assumptions.

We know that the total-dependent strategy for 16vs10 is to hit, while the actual compositions that can form the total of 16 vary, some are hit and some are stand.

So how do we arrive at the decision to hit every hand of 16vs10? One would think it must derive from the frequency with which the various compositions will arise. If hit-worthy 16s are more common that stand-worthy ones, then the overall decision should be to hit all 16s.

[Or more precisely, we need to sum the hit EVs of all the hand compositions totalling 16 which we may reach, weighted by the probability of reaching those hands, do the same for the stand EVs, and adopt whichever is higher as our overall action for 16vs10.]

But how do we know the frequencies of all the different compositions? They themselves ought to be a product of the strategy for the lower totals. The more totals below 16 we hit, the more multicard 16s we will generate.

We can't decide what to do with the lower totals until we have worked out the EVs of all the higher totals which we might reach by hitting them, and we can't calculate the EVs of the higher totals until we know what strategy we will be employing with the lower totals. A chicken-and-egg conundrum.:grin:

I gather the practical solution is to start out with the perfect-play EVs and hand frequencies, and use those as the basis for generating the total-dependent strategy, but I don't know how we can say with certainty that such an approach is giving us all the right answers.
 

MangoJ

Well-Known Member
#10
Ok, here is a better approach for total-dependent strategy.

Eric Farmers code allows to measure the EV of any kind of strategy.

a) I would start with a total-dependent strategy with most obvious decisions already "guessed". Leaving the marginal decisions open. Should be only a few.
(say 20)

b) Iterate through all those open 20 decision configurations (typically 2^20), evaluate EV each, and pick the marginal decision set with maximal EV.

c) Iterate through all decisions, alter them one-by-one (so only ~200 evaluations). If on any those modifications a EV can get better, take this decision, and start back at (b).

When it converges, this is proved to be the best total-dependent strategy.
 

assume_R

Well-Known Member
#11
Thanks again for all the input; I am on my way to having this work well. I am simply going to allow the user to select which cards are comprised of each hand, and to calculate the EV give a strategy the user selects. Yes, it can become a chicken and egg conundrum indeed, but the marginal plays will probably not change too much. Either way, I am almost always agreeing with k_c's output right now, and will be using that as a baseline.

Here's an algorithmic question: how do you generate all possible permutations of hands that total 16? I have some ideas, but I feel like this is probably already a solved problem. Because let's say we are generating an index for 16v10. We don't really want the user to have to select 10,6 vs. 10. The program should ideally be able to use all possible permutations of 16 vs. 10.
 
#12
assume_R said:
Thanks again for all the input; I am on my way to having this work well. I am simply going to allow the user to select which cards are comprised of each hand, and to calculate the EV give a strategy the user selects. Yes, it can become a chicken and egg conundrum indeed, but the marginal plays will probably not change too much. Either way, I am almost always agreeing with k_c's output right now, and will be using that as a baseline.

Here's an algorithmic question: how do you generate all possible permutations of hands that total 16? I have some ideas, but I feel like this is probably already a solved problem. Because let's say we are generating an index for 16v10. We don't really want the user to have to select 10,6 vs. 10. The program should ideally be able to use all possible permutations of 16 vs. 10.
In a true CA, for one player vs. one dealer in a 8D game you would start with 416^22 combinations, assuming a hand can go up to 11 cards. Each combination starts with a 1/(416^22) chance of occurring. Subtract out all the combinations that are banned by exclusivity (card #345 can only occur once etc.) and there's your denominator. Everything else you do will be based on this exhaustive list of hands.

Where it gets tricky is where it makes sense to allow for strategy, e.g., hands like A,9,6 vs. 10 can't be considered in a real-world approach because realistically you won't be drawing on A,9 vs. 10.

This is how I would approach your problem, as a guy who wrote his first programs to 8 inch floppy disks when problems involving 416^22 combinations were done on hardware that was immersed in liquid nitrogen. Probabilities. People like CA's because they are flexible and have elegantly simple code. But to solve for 16 vs. 10 I would just directly calculate all the ways the hand can be 16 vs. 10:

10,6, vs. 10: (128/416)*(32/415)*(127/414)

10,2,2,2 vs. 10: (128/416)*(32/415)*(31/414)*(30/413)*(127/412)

The code for generating the probabilities is also elegant and compact, you just decrement the denominator for each card and the numerator for each occurrence within a rank. If you are suit-aware your code is more complicated and your numerators smaller. The programming labor is to generate all the different ways the player can have 16, and then you have the option of excluding all the ones that would be the result of an unreasonable playing strategy such as A,9,6.

Hope this can of worms was useful! :laugh:
 

assume_R

Well-Known Member
#13
Automatic Monkey said:
But to solve for 16 vs. 10 I would just directly calculate all the ways the hand can be 16 vs. 10:

10,6, vs. 10: (128/416)*(32/415)*(127/414)

10,2,2,2 vs. 10: (128/416)*(32/415)*(31/414)*(30/413)*(127/412)

The code for generating the probabilities is also elegant and compact, you just decrement the denominator for each card and the numerator for each occurrence within a rank. If you are suit-aware your code is more complicated and your numerators smaller. The programming labor is to generate all the different ways the player can have 16, and then you have the option of excluding all the ones that would be the result of an unreasonable playing strategy such as A,9,6.
Thanks, Monkey, and this is indeed how I programmed my CA thus far. However, I was more asking about the part in bold, which I am still unsure how to best do.
 

iCountNTrack

Well-Known Member
#14
Recursion is really not the most efficient way to day things, the best thing is to generate all the possible sequences for the dealer and the player and store them because you will keep on using them. The best way to do it is by brute force enumeration using loops. The following is a partial list of the card sequences for dealer's if he stands on soft 17.

Code:
1  10  10  20
2  10  9  19
3  10  8  18
4  10  7  17
5  10  6  10  26
6  10  6  9  25
7  10  6  8  24
8  10  6  7  23
9  10  6  6  22
10  10  6  5  21
11  10  6  4  20
12  10  6  3  19
13  10  6  2  18
14  10  6  1  17
15  10  5  10  25
16  10  5  9  24
17  10  5  8  23
18  10  5  7  22
19  10  5  6  21
20  10  5  5  20
21  10  5  4  19
22  10  5  3  18
23  10  5  2  17
24  10  5  1  10  26
25  10  5  1  9  25
26  10  5  1  8  24
27  10  5  1  7  23
28  10  5  1  6  22
29  10  5  1  5  21
30  10  5  1  4  20
31  10  5  1  3  19
32  10  5  1  2  18
33  10  5  1  1  17
34  10  4  10  24
35  10  4  9  23
36  10  4  8  22
37  10  4  7  21
38  10  4  6  20
39  10  4  5  19
40  10  4  4  18
41  10  4  3  17
42  10  4  2  10  26
43  10  4  2  9  25
44  10  4  2  8  24
45  10  4  2  7  23
46  10  4  2  6  22
47  10  4  2  5  21
48  10  4  2  4  20
49  10  4  2  3  19
50  10  4  2  2  18
51  10  4  2  1  17
52  10  4  1  10  25
53  10  4  1  9  24
54  10  4  1  8  23
55  10  4  1  7  22
56  10  4  1  6  21
57  10  4  1  5  20
58  10  4  1  4  19
59  10  4  1  3  18
60  10  4  1  2  17
61  10  4  1  1  10  26
62  10  4  1  1  9  25
63  10  4  1  1  8  24
64  10  4  1  1  7  23
65  10  4  1  1  6  22
66  10  4  1  1  5  21
67  10  4  1  1  4  20
68  10  4  1  1  3  19
69  10  4  1  1  2  18
70  10  4  1  1  1  17
71  10  3  10  23
72  10  3  9  22
73  10  3  8  21
74  10  3  7  20
75  10  3  6  19
76  10  3  5  18
77  10  3  4  17
78  10  3  3  10  26
79  10  3  3  9  25
80  10  3  3  8  24
81  10  3  3  7  23
82  10  3  3  6  22
83  10  3  3  5  21
84  10  3  3  4  20
85  10  3  3  3  19
86  10  3  3  2  18
87  10  3  3  1  17
88  10  3  2  10  25
89  10  3  2  9  24
90  10  3  2  8  23
91  10  3  2  7  22
92  10  3  2  6  21
93  10  3  2  5  20
94  10  3  2  4  19
95  10  3  2  3  18
96  10  3  2  2  17
97  10  3  2  1  10  26
98  10  3  2  1  9  25
99  10  3  2  1  8  24
100  10  3  2  1  7  23
101  10  3  2  1  6  22
102  10  3  2  1  5  21
103  10  3  2  1  4  20
104  10  3  2  1  3  19
105  10  3  2  1  2  18
106  10  3  2  1  1  17
107  10  3  1  10  24
108  10  3  1  9  23
109  10  3  1  8  22
110  10  3  1  7  21
111  10  3  1  6  20
112  10  3  1  5  19
113  10  3  1  4  18
114  10  3  1  3  17
115  10  3  1  2  10  26
116  10  3  1  2  9  25
117  10  3  1  2  8  24
118  10  3  1  2  7  23
119  10  3  1  2  6  22
120  10  3  1  2  5  21
121  10  3  1  2  4  20
122  10  3  1  2  3  19
123  10  3  1  2  2  18
124  10  3  1  2  1  17
125  10  3  1  1  10  25
126  10  3  1  1  9  24
127  10  3  1  1  8  23
128  10  3  1  1  7  22
129  10  3  1  1  6  21
130  10  3  1  1  5  20
131  10  3  1  1  4  19
132  10  3  1  1  3  18
133  10  3  1  1  2  17
134  10  3  1  1  1  10  26
135  10  3  1  1  1  9  25
136  10  3  1  1  1  8  24
137  10  3  1  1  1  7  23
138  10  3  1  1  1  6  22
139  10  3  1  1  1  5  21
140  10  3  1  1  1  4  20
141  10  3  1  1  1  3  19
142  10  3  1  1  1  2  18
143  10  3  1  1  1  1  17
144  10  2  10  22
145  10  2  9  21
146  10  2  8  20
147  10  2  7  19
148  10  2  6  18
149  10  2  5  17
150  10  2  4  10  26
151  10  2  4  9  25
152  10  2  4  8  24
153  10  2  4  7  23
154  10  2  4  6  22
155  10  2  4  5  21
156  10  2  4  4  20
157  10  2  4  3  19
158  10  2  4  2  18
159  10  2  4  1  17
160  10  2  3  10  25
161  10  2  3  9  24
162  10  2  3  8  23
163  10  2  3  7  22
164  10  2  3  6  21
165  10  2  3  5  20
166  10  2  3  4  19
167  10  2  3  3  18
168  10  2  3  2  17
169  10  2  3  1  10  26
170  10  2  3  1  9  25
171  10  2  3  1  8  24
172  10  2  3  1  7  23
173  10  2  3  1  6  22
174  10  2  3  1  5  21
175  10  2  3  1  4  20
176  10  2  3  1  3  19
177  10  2  3  1  2  18
178  10  2  3  1  1  17
179  10  2  2  10  24
180  10  2  2  9  23
181  10  2  2  8  22
182  10  2  2  7  21
183  10  2  2  6  20
184  10  2  2  5  19
185  10  2  2  4  18
186  10  2  2  3  17
187  10  2  2  2  10  26
188  10  2  2  2  9  25
189  10  2  2  2  8  24
190  10  2  2  2  7  23
191  10  2  2  2  6  22
192  10  2  2  2  5  21
193  10  2  2  2  4  20
194  10  2  2  2  3  19
195  10  2  2  2  2  18
196  10  2  2  2  1  17
197  10  2  2  1  10  25
198  10  2  2  1  9  24
199  10  2  2  1  8  23
200  10  2  2  1  7  22
201  10  2  2  1  6  21
202  10  2  2  1  5  20
203  10  2  2  1  4  19
204  10  2  2  1  3  18
205  10  2  2  1  2  17
206  10  2  2  1  1  10  26
207  10  2  2  1  1  9  25
208  10  2  2  1  1  8  24
209  10  2  2  1  1  7  23
210  10  2  2  1  1  6  22
211  10  2  2  1  1  5  21
212  10  2  2  1  1  4  20
213  10  2  2  1  1  3  19
214  10  2  2  1  1  2  18
215  10  2  2  1  1  1  17
216  10  2  1  10  23
217  10  2  1  9  22
218  10  2  1  8  21
219  10  2  1  7  20
220  10  2  1  6  19
221  10  2  1  5  18
222  10  2  1  4  17
223  10  2  1  3  10  26
224  10  2  1  3  9  25
225  10  2  1  3  8  24
226  10  2  1  3  7  23
227  10  2  1  3  6  22
228  10  2  1  3  5  21
229  10  2  1  3  4  20
230  10  2  1  3  3  19
231  10  2  1  3  2  18
232  10  2  1  3  1  17
233  10  2  1  2  10  25
234  10  2  1  2  9  24
235  10  2  1  2  8  23
236  10  2  1  2  7  22
237  10  2  1  2  6  21
238  10  2  1  2  5  20
239  10  2  1  2  4  19
240  10  2  1  2  3  18
241  10  2  1  2  2  17
242  10  2  1  2  1  10  26
243  10  2  1  2  1  9  25
244  10  2  1  2  1  8  24
245  10  2  1  2  1  7  23
246  10  2  1  2  1  6  22
247  10  2  1  2  1  5  21
248  10  2  1  2  1  4  20
249  10  2  1  2  1  3  19
250  10  2  1  2  1  2  18
251  10  2  1  2  1  1  17
252  10  2  1  1  10  24
253  10  2  1  1  9  23
254  10  2  1  1  8  22
255  10  2  1  1  7  21
256  10  2  1  1  6  20
257  10  2  1  1  5  19
258  10  2  1  1  4  18
259  10  2  1  1  3  17
260  10  2  1  1  2  10  26
261  10  2  1  1  2  9  25
262  10  2  1  1  2  8  24
263  10  2  1  1  2  7  23
264  10  2  1  1  2  6  22
265  10  2  1  1  2  5  21
266  10  2  1  1  2  4  20
267  10  2  1  1  2  3  19
268  10  2  1  1  2  2  18
269  10  2  1  1  2  1  17
270  10  2  1  1  1  10  25
271  10  2  1  1  1  9  24
272  10  2  1  1  1  8  23
273  10  2  1  1  1  7  22
274  10  2  1  1  1  6  21
275  10  2  1  1  1  5  20
276  10  2  1  1  1  4  19
277  10  2  1  1  1  3  18
278  10  2  1  1  1  2  17
279  10  2  1  1  1  1  10  26
280  10  2  1  1  1  1  9  25
281  10  2  1  1  1  1  8  24
282  10  2  1  1  1  1  7  23
283  10  2  1  1  1  1  6  22
284  10  2  1  1  1  1  5  21
285  10  2  1  1  1  1  4  20
286  10  2  1  1  1  1  3  19
287  10  2  1  1  1  1  2  18
288  10  2  1  1  1  1  1  17
289  10  1  21
290  9  10  19
291  9  9  18
292  9  8  17
293  9  7  10  26
294  9  7  9  25
295  9  7  8  24
296  9  7  7  23
297  9  7  6  22
298  9  7  5  21
299  9  7  4  20
...
First column is element number, last column on the right is hand total.
 

iCountNTrack

Well-Known Member
#15
Automatic Monkey said:
In a true CA, for one player vs. one dealer in a 8D game you would start with 416^22 combinations, assuming a hand can go up to 11 cards. Each combination starts with a 1/(416^22) chance of occurring. Subtract out all the combinations that are banned by exclusivity (card #345 can only occur once etc.) and there's your denominator. Everything else you do will be based on this exhaustive list of hands.

Where it gets tricky is where it makes sense to allow for strategy, e.g., hands like A,9,6 vs. 10 can't be considered in a real-world approach because realistically you won't be drawing on A,9 vs. 10.

This is how I would approach your problem, as a guy who wrote his first programs to 8 inch floppy disks when problems involving 416^22 combinations were done on hardware that was immersed in liquid nitrogen. Probabilities. People like CA's because they are flexible and have elegantly simple code. But to solve for 16 vs. 10 I would just directly calculate all the ways the hand can be 16 vs. 10:

10,6, vs. 10: (128/416)*(32/415)*(127/414)

10,2,2,2 vs. 10: (128/416)*(32/415)*(31/414)*(30/413)*(127/412)

The code for generating the probabilities is also elegant and compact, you just decrement the denominator for each card and the numerator for each occurrence within a rank. If you are suit-aware your code is more complicated and your numerators smaller. The programming labor is to generate all the different ways the player can have 16, and then you have the option of excluding all the ones that would be the result of an unreasonable playing strategy such as A,9,6.

Hope this can of worms was useful! :laugh:
I am sorry but I am afraid this is not true, when you do a combinatorial analysis you will have to enlist ALL the possible hands including the ones that a that might appear absurd, like hitting a hard 20. The properly weighing the EV for each decision with its respective frequency, will take care of everything.
 
#16
assume_R said:
Thanks, Monkey, and this is indeed how I programmed my CA thus far. However, I was more asking about the part in bold, which I am still unsure how to best do.
I'd do that like this: it seems that the player can have up to 10 cards to reasonably get a hand of 16. {2,2,2,2,2,2,A,A,A,A} There are unreasonable ways to do it using more, but I call them unreasonable because they would involve hitting on soft 19-21.

So you can go through 10 loops of 10, see which sums hit 16. But you should include a line to tell you if your hand is "unreasonable" in the context of blackjack, to see if the sum ever hit soft 19-21 before it hit hard 16. There's no reason to corrupt your final analysis with things that don't happen in the real world.

(By the way, if you're programming that way, it's not a strict CA, more of a hybrid, which is a more sensible way to go with 416 elements. ;))
 

assume_R

Well-Known Member
#17
iCountNTrack said:
Recursion is really not the most efficient way to day things, the best thing is to generate all the possible sequences for the dealer and the player and store them because you will keep on using them. The best way to do it is by brute force enumeration using loops. The following is a partial list of the card sequences for dealer's if he stands on soft 17.

Code:
1  10  10  20
2  10  9  19
3  10  8  18
...
First column is element number, last column on the right is hand total.
Right - that's essentially what I do, but I have been calculating these results on the fly, because I haven't come up with an efficient way to recall the correct ones.

For example, here's an example of a dealer hand for player 10,6 vs. 4 for single deck:
4,10,2,5: P = 1.0 * 15/49 * 4/48 * 4/47 = .0021711
4,10,2,6: P = 1.0 * 15/49 * 4/48 * 3/47 = .0016283

But if I had 9,7 vs. 4 it would be:
4,10,2,5: P = 1.0 * 16/49 * 4/48 * 4/47 = .0023158
4,10,2,6: P = 1.0 * 16/49 * 4/48 * 4/47 = .0023158

So I'd have to recalculate them each time. I'm not sure how much more efficient storing them would be.
 
#18
assume_R said:
... Because let's say we are generating an index for 16v10. We don't really want the user to have to select 10,6 vs. 10. The program should ideally be able to use all possible permutations of 16 vs. 10.
One other thing I thought of- it's probably not worthwhile to worry about any kind of composition dependence for an index play unless it is specifically a comp-dependent index play (e.g., 77 vs 10 in a SD game.)

The reason is by the time you are using an index play an undetermined number and composition of cards have already been dealt out, and it's of no value to worry about the composition of the hand in front of you when 2 decks of a shoe have already been dealt out and all you have is count data to make a decision on.
 

k_c

Well-Known Member
#19
assume_R said:
Hey all. I'm working on a CA program, and I'd appreciate some input (probably from k_c since I know he's created one).

Thus far, I have the exact same output as http://www.bjstrat.net/cgi-bin/cdca_web.exe for Dealer Probabilities, which means I'm onto a good start.

So now I have to actually do the player probabilities, but I'm having some trouble understanding the way to do bonuses (such as BJ, or 5-card bonuses), and eventually I'll have to incorporate splits and doubling and surrendering (right now I will start with just H/S).

Here's my understanding of how to do it:
Let's say I have a 16v10, and b.s. says to hit. So I give myself each card, and every result will either be a +100% (win), 0% (push), -100% (loss), or +150% (for example for a 5-card 21 bonus if I get a 5, and my 16 was made up of 4 cards). I take that EV (is that actually EV?) and multiply by the probability of that card being dealt to me, and then I add that all up for all possibilities.

Correct? Thanks in advance to everybody.
London Colin has pretty much described a good approach to an efficient CA. My approach is similar. The challenge for me was to write code that would execute fast enough on my old computer to be at least somewhat practical. Basically I started from scratch, figured out what was logical, and then looked for ways to improve execution speed.

Right now I don't have the time to work on much. I have a new version of my comp dependent CA that has a couple of additional rules options and also computes EVs for knowledge of 0, 1, or 2 dealer cards. Also I have a .dll library of the old version. It can compute all of the EVs in the .exe version and additionally has some sim capabilities using computed values. The .dll could be used by a Windows programmer.

Also I have some partially finished projects that I don't work on any more, including the new version of the comp dependent CA.

It sounds like you are on a reasonable path. It takes time.
 

assume_R

Well-Known Member
#20
k_c said:
London Colin has pretty much described a good approach to an efficient CA. My approach is similar. The challenge for me was to write code that would execute fast enough on my old computer to be at least somewhat practical. Basically I started from scratch, figured out what was logical, and then looked for ways to improve execution speed.

Right now I don't have the time to work on much. I have a new version of my comp dependent CA that has a couple of additional rules options and also computes EVs for knowledge of 0, 1, or 2 dealer cards. Also I have a .dll library of the old version. It can compute all of the EVs in the .exe version and additionally has some sim capabilities using computed values. The .dll could be used by a Windows programmer.

Also I have some partially finished projects that I don't work on any more, including the new version of the comp dependent CA.

It sounds like you are on a reasonable path. It takes time.
Thanks, k_c, I was hoping you'd chime in on this thread. Thus far, for hitting and standing my results exactly agree with your website's results, except for the Player EV for cards vs. an A, which disagree by only a few percentage points (possibly implementation differences?).

Now I will be implementing splitting, which I am a bit unsure of. So let's say it's 7,7 vs. 2. I then basically just up the EV's of a 1-card 7 vs. 2 and another 1-card 7 vs. 2 assuming both 1-card 7's have to hit?
 
Top