RS
RS
  • Threads: 62
  • Posts: 8626
Joined: Feb 11, 2014
May 27th, 2015 at 6:15:01 PM permalink
So let's say I want to write a function (or several functions) to incorporate proper VP strategy. I'm not too sure how to write the code (efficiently) to check for each possible draw.

I can think of a few ways to go about doing this, but, none of them seem to be too efficient, even with a simple strategy like 9/6 JOB. I am not trying to generate a strategy.


Sure, I could first check for a dealt royal flush. Then check for a dealt straight flush, then dealt 4-of-a-kind and dealt FH. Then check for 4-to-royal. Then keep going down the list.


Any ideas or help? Thanks.
JB
Administrator
JB
  • Threads: 334
  • Posts: 2089
Joined: Oct 14, 2009
May 27th, 2015 at 6:55:09 PM permalink
Is it for a specific game and paytable which will never change? Or is it for an arbitrary game and paytable, requiring an accurate, generic function?

Is it for a simulation?
Wizard
Administrator
Wizard 
  • Threads: 1491
  • Posts: 26436
Joined: Oct 14, 2009
May 27th, 2015 at 6:56:04 PM permalink
I wrote a whole page about it: My Methodology for Video Poker Analysis.
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
RS
RS
  • Threads: 62
  • Posts: 8626
Joined: Feb 11, 2014
May 27th, 2015 at 6:57:21 PM permalink
Quote: JB

Is it for a specific game and paytable which will never change? Or is it for an arbitrary game and paytable, requiring an accurate, generic function?

Is it for a simulation?



That's part of the problem. Right now I'm doing it for a simulation on 9/6 JOB. But I'm hoping to include other paytables/strategies in the future like DW and DDB primarily.


Quote: Wizard

I wrote a whole page about it: My Methodology for Video Poker Analysis.



Yes, I read that (partially). But if I'm not mistaken, that was for generating a strategy and iterating through each possible dealt hand to determine the best hold.

I already know the best hold. I don't know how to code the best hold strategy, other than hard-coding it. ie:

Quote:


ten = 0;
jack = 0;
queen =0;
king = 0;
ace = 0;

spade = 0;
club = 0;
heart = 0;
diamond = 0;

for (i = 0; i < 5; i++) {
if (rank == "T")
ten++;
elseif (rank == "J")
jack++;
....etc

if (suite == "s")
spade++;
elseif (suite == "d")
diamond++;
}

if (spade == 5 or diamond == 5 or heart == 5 or club == 5)
if (ten == 1 and jack == 1 and queen == 1 and king == 1 and ace == 1)
echo "Dealt royal flush!\n";




that's a lot of code for a fairly simple hand.

other hands get more complex (haven't gotten or attempted those yet). i'd assume hands like 3-card straight flush draws would be a total b****.


edit edit:

the [i] in my code/quote above don't show up properly. click "edit/quote" on my post to see the [i ] properly.
JB
Administrator
JB
  • Threads: 334
  • Posts: 2089
Joined: Oct 14, 2009
May 27th, 2015 at 7:16:23 PM permalink
Quote: RS

That's part of the problem. Right now I'm doing it for a simulation on 9/6 JOB. But I'm hoping to include other paytables/strategies in the future like DW and DDB primarily.


The reason I ask is because you don't need to go through much trouble at all for a simulation.

1) Create a static array holding the probability of each outcome when optimal strategy is used.

2) For the simulation itself, repeatedly generate a random double r such that 0 <= r < 1 and determine which outcome it corresponds to. Use the outcome however you need (such as adding its payoff to a running total, etc.)

The following code snippets are in C# but you should be able to adapt the code to whatever language you use:

    static Random RNG = new Random();

static int[] Paytable = { 800, 50, 25, 9, 6, 4, 3, 2, 1, 0 };

static double[] ProbabilityTable =
{
0.0000247582680375947, // Royal Flush
0.000109309090371472, // Straight Flush
0.00236254568587687, // Four of a Kind
0.0115122073362865, // Full House
0.0110145109680315, // Flush
0.0112293672413438, // Straight
0.0744486985711364, // Three of a Kind
0.129278902480178, // Two Pair
0.214585031125644, // Jacks or Better
0.545434669233094 // All Other
};

// the probabilities don't quite sum to exactly 1 but are close enough

//////////////////////////////////////////////////////////////////////

while (true)
{
int outcome = 0;

double r = RNG.NextDouble();

for (int x = 0; x < 10; x++)
{
double p = ProbabilityTable[x];

if (r < p)
{
outcome = x;
break;
}

r -= p;
}

int prize = Paytable[outcome];

// use the outcome or prize however you need
}


This will also let you run many more trials per second than if you actually shuffled a deck, dealt a hand, decoded it, applied optimal strategy, drew replacement cards, and scored the final hand for each trial.
RS
RS
  • Threads: 62
  • Posts: 8626
Joined: Feb 11, 2014
May 27th, 2015 at 7:32:58 PM permalink
Quote: JB

The reason I ask is because you don't need to go through much trouble at all for a simulation.

1) Create a static array holding the probability of each outcome when optimal strategy is used.

2) For the simulation itself, repeatedly generate a random double r such that 0 <= r < 1 and determine which outcome it corresponds to. Use the outcome however you need (such as adding its payoff to a running total, etc.)

The following code snippets are in C# but you should be able to adapt the code to whatever language you use:

    static Random RNG = new Random();

static int[] Paytable = { 800, 50, 25, 9, 6, 4, 3, 2, 1, 0 };

static double[] ProbabilityTable =
{
0.0000247582680375947, // Royal Flush
0.000109309090371472, // Straight Flush
0.00236254568587687, // Four of a Kind
0.0115122073362865, // Full House
0.0110145109680315, // Flush
0.0112293672413438, // Straight
0.0744486985711364, // Three of a Kind
0.129278902480178, // Two Pair
0.214585031125644, // Jacks or Better
0.545434669233094 // All Other
};

// the probabilities don't quite sum to exactly 1 but are close enough

//////////////////////////////////////////////////////////////////////

while (true)
{
int outcome = 0;

double r = RNG.NextDouble();

for (int x = 0; x < 10; x++)
{
double p = ProbabilityTable[x];

if (r < p)
{
outcome = x;
break;
}

r -= p;
}

int prize = Paytable[outcome];

// use the outcome or prize however you need
}


This will also let you run many more trials per second than if you actually shuffled a deck, dealt a hand, decoded it, applied optimal strategy, drew replacement cards, and scored the final hand for each trial.



I've already done this in single-line (what you described).

But now I'm working on multi-line. As far as I know, you can't do the same thing, can you?

I'm looking at the variance (ie: bankroll requirement to play X coin in)...not so much as for the EV.
JB
Administrator
JB
  • Threads: 334
  • Posts: 2089
Joined: Oct 14, 2009
May 27th, 2015 at 7:33:39 PM permalink
Ah. In that case, the simple approach would not work.

Stay tuned, another idea is coming forth...
JB
Administrator
JB
  • Threads: 334
  • Posts: 2089
Joined: Oct 14, 2009
May 27th, 2015 at 7:47:36 PM permalink
What language are you using?
Wizard
Administrator
Wizard 
  • Threads: 1491
  • Posts: 26436
Joined: Oct 14, 2009
May 27th, 2015 at 7:55:39 PM permalink
Putting video poker strategy into code is both tedious and error prone. I avoid it as much as I can. Fortunately, JB is really good at it.
"For with much wisdom comes much sorrow." -- Ecclesiastes 1:18 (NIV)
MathExtremist
MathExtremist
  • Threads: 88
  • Posts: 6526
Joined: Aug 31, 2010
May 27th, 2015 at 8:08:52 PM permalink
Quote: RS

I already know the best hold. I don't know how to code the best hold strategy, other than hard-coding it.


It doesn't matter whether *you* know the best hold, your software has to know the best hold. That means you either enter it or let the computer figure it out -- those are the only two options, really. If you don't want to let your computer figure it out then you've got to commit to at least entering in 134459 hold instructions (that's 52c5, discounting suit-coloring differences).

How to represent the hold may hinge on how you represent the cards in the first place. If you use a 64-bit int with only 5 bits set (representing the cards in the hand) then you could just represent the hold with only the bits to keep, from 0 to all 5 bits. If you have the hand's cards in an array, I'd first sort them and then use a 5-bit number to indicate which cards to hold. Binary 11111 (0x1F) would be hold everything, etc. As with many such problems, the data structures and algorithms you use are tightly intertwined.
"In my own case, when it seemed to me after a long illness that death was close at hand, I found no little solace in playing constantly at dice." -- Girolamo Cardano, 1563
RS
RS
  • Threads: 62
  • Posts: 8626
Joined: Feb 11, 2014
May 27th, 2015 at 9:12:05 PM permalink
JB, I started it in PHP. I dont have C++ or Java on the computer I'm using, although I can install it. (But I hate installing stuff on Linux, I'm a total noob at it and I always mess it up or it takes forever.) Thinking C++ or Java would be faster, though, too.

Wizard, that's what I was thinking that it'd be error prone and tedious.

ME, indeed. I hadn't even thought about ordering the cards.
JB
Administrator
JB
  • Threads: 334
  • Posts: 2089
Joined: Oct 14, 2009
May 27th, 2015 at 9:32:19 PM permalink
Quote: RS

JB, I started it in PHP.


Wow, that's... brave.
RS
RS
  • Threads: 62
  • Posts: 8626
Joined: Feb 11, 2014
May 27th, 2015 at 10:29:30 PM permalink
Quote: JB

Wow, that's... brave.



Not sure if brave is the right word.. :)

But that's all I have/had on that comp. But likely gonna just do it in C++ or Java.
MathExtremist
MathExtremist
  • Threads: 88
  • Posts: 6526
Joined: Aug 31, 2010
May 28th, 2015 at 4:33:11 AM permalink
Quote: RS

ME, indeed. I hadn't even thought about ordering the cards.


First, definitely use C++. If you're on linux, you probably do have some version of the gnu compiler on there, but you probably don't have a proper IDE (which I'd use if you're not an emacs hacker).

As for the ordering, that approach will break games that pay on things like sequential royals or whatnot. Those are rare but it's still an edge case. Actually both techniques I mentioned are order-agnostic so if you really want to be safe for a generic case of "order matters" then you'll need to intentionally check for that. I'm unaware of any other games that involve different pays based on hand ordering though, and I haven't looked at whether the order has an actual impact on the sequential royal strategy (i.e. do you hold an A in the first position more than in the 2nd).

But if you don't care about those, an order-agnostic treatment is definitely less work.
"In my own case, when it seemed to me after a long illness that death was close at hand, I found no little solace in playing constantly at dice." -- Girolamo Cardano, 1563
RS
RS
  • Threads: 62
  • Posts: 8626
Joined: Feb 11, 2014
May 29th, 2015 at 8:15:46 PM permalink
How bad would it be (difficult, efficient/innefficient, imrpoper, whatever) to do something like....find probability of each possible dealt hand. Then find probability of the ending hand.


X% chance of getting dealt a low pair. From there, I get an A% chance to improve to 2-pair, B% chance to improve to 3-of-a-kind, C% chance to improve to full-house, D% chance to improve to four-of-a-kind, and an E% chance (A+B+C+D+E = 1.00) of ending with a non-winning hand.


If this is a good idea, I'm assuming I should be looking more into Wizard's "My Methodology" page. ??
tringlomane
tringlomane
  • Threads: 8
  • Posts: 6281
Joined: Aug 25, 2012
May 29th, 2015 at 10:38:07 PM permalink
Quote: MathExtremist

First, definitely use C++. If you're on linux, you probably do have some version of the gnu compiler on there, but you probably don't have a proper IDE (which I'd use if you're not an emacs hacker).

As for the ordering, that approach will break games that pay on things like sequential royals or whatnot. Those are rare but it's still an edge case. Actually both techniques I mentioned are order-agnostic so if you really want to be safe for a generic case of "order matters" then you'll need to intentionally check for that. I'm unaware of any other games that involve different pays based on hand ordering though, and I haven't looked at whether the order has an actual impact on the sequential royal strategy (i.e. do you hold an A in the first position more than in the 2nd).

But if you don't care about those, an order-agnostic treatment is definitely less work.



ACE$ Bonus Poker gives you a royal if the four aces, each of which have a gold A,C,,E, and $ spell out ACE$ with no gaps. Very few strategy changes though from regular 8/5 Bonus. One obvious one, break Aces Full if they are in order. And one wacky one, AQ suited is better than 89T suited if ACE$ is still eligible.

I will be watching this thread closely. I wouldn't mind setting up a multiline simulator either.
RS
RS
  • Threads: 62
  • Posts: 8626
Joined: Feb 11, 2014
May 29th, 2015 at 11:19:15 PM permalink
This is what I'm thinking.......


First of all, I'm going to continue it on PHP, hard-coded mostly, for 9/6 JOB. Only variables are things like coin-in, denom, lines, and payouts (ie: 9/5 or 8/6 JOB). But strategy is constant.


I think the way I'm going to attack the strategy thing is kind of.....different.


First of all, it'd be a bunch of if...elseif...elseif...elseif... statements. So, if I'm checking for a hand like an outside straight draw, I don't have to check for low pairs, high pairs, flush draws, 3 or 4 to a royal, etc. Since those hands are already checked before. (The only exception is TJQK is listed before low pair, then outside Straight.)

It'll check for hands that are ALWAYS held. Hands like 2-pair, 3 of a kind, full house, and 4 of a kind.

Next it checks for hands like dealt RF and dealt SF, also hands that are never discarded.

Then four to a flush, then TJQK (outside straight), then low pair, then outside straights.


That's where I left off (I got hungry). But next I think is going after the 3-card SF hands. Those will likely be a total b**** and a half, since there are so many different 3-card SF holds. Off the top of my head:

[2 high 0 gap is not possible, since that'd be TJQ]
2 high 1 gap
2 high 2 gap
1 high 2 gap
1 high 1 gap
1 high 0 gap
Ace-low (A23, A45, etc. are all the same)
0 high:
0 gap
1 gap
1 gap with straight penalty (ie: 346 of hearts, 7 of spades, Ten of non-hearts)
2 gap
2 gap with straight penalty (ie: 347 of hearts, 5 of spades, Ten of non-hearts)
0 gap with straight penalty (ie: 456 of hearts, 8 of spades, Ten of non-hearts)



Some hands require a "special deck" to be drawn from (ie: SF draws). Other hands don't need a special deck (ie: 2-pair, since you just have 4/47 chance of getting a FH, and 43/47 chance of ending with a 2-pair). Or a hand like a pair....it doesn't matter what the 3 cards are that you discard. If I have 3,3,J,Q,A versus 3,3,5,7,9....the cards I threw away (JQA or 579) don't hurt or help me one way or the other. If I draw 2 high cards (which is equally likely as drawing 2 low cards), then I get 2-pair. I can't draw at a straight, flush, or "jacks or better" [since drawing at Jacks or Better would land me with a 2-pair, as said previously].

4 to an outside straight wouldn't need a special deck, since you can just do 8/47 chance of a straight [assuming no high cards in the 4-held-cards]. And if there are high cards in the straight, you can (easily?) incorporate that too. ie: TJQK outside straight has 8/47 chance of getting an outside straight and 9/47 chance of getting a high pair (3 jacks, 3 queens, 3 kings remaining).



If this all works, then I'm thinking I'll translate it to C++ or Java. I'm still not too sure what the best way is to do this for other games (DW or DDB)....other than hard-coding a strategy. Perhaps I'll be able to have it generate a strategy using the paytable, then use that strategy when simulating the game. But, that seems like quite a chore, at this point.
charliepatrick
charliepatrick
  • Threads: 39
  • Posts: 2946
Joined: Jun 17, 2011
May 30th, 2015 at 5:39:13 AM permalink
Just a thought - it might be easier to have an array with the decision but let the computer work it out once.

The first program works it out and puts the output somewhere (in my case I use the screen and drag and drop it into a spreadsheet then generate the code to initialise the array). I use the same technique by having a spreadsheet for my wanted list of Abba records using web pages ( http://www.capatrick.freeserve.co.uk/abrx-forlabel.html?labelnum=1 ), the code (actually one line for each record) is created using CONCATENATE("n=n+1", ...) and using a variable for any quotes.
The entry for 4-track Dancing Queen
n=n+1;
picname[n]="images/abbarecs/a4track - Tai - DQ EP.jpg";
pictitle[n]="Dancing Queen (EP)";
piclabel[n]="4 - track";
piccountry[n]="Thailand";
picdemo[n]="";
picgot[n]=""
other entries...
n=n+1;
picname[n]="images/abbarecs/a4track - Tai - D (EP) (2892).jpg";
.....
other labels...
endofset[nooflabels]=n; nooflabels=nooflabels+1; startofset[nooflabels]=n+1;
MangoJ
MangoJ
  • Threads: 10
  • Posts: 905
Joined: Mar 12, 2011
May 30th, 2015 at 7:43:38 AM permalink
Here is my approach for a similar game I wrote a few years ago. It combines a simulation and a lookup part.

1. Write a routine which calculates the probability ending in any of the ranking hands based on (a) a few cards you hold, and (b) the cards remaining.
(this is the major time your program will spend, so optimize your data structures for this).

2. Shuffle and deal a hand. Calculate the ranking probabilities for each of the 32 hold patterns, use the one with the highest EV. Alternatively, chose the hold pattern based on whatever strategy you use.

3. With a 100-hand play (or an N-hand play), based on your (exact) probabilities and the specific paytable - create the payout probability distribution.
(an efficient method is by convolution).

4. Repeat step 2. for different hands (either random or iterate over all 5-card deals (modulo suits and position).


For step 1 (the difficult job here), first check if your holded cards are suited. If they are, find the number or remaining cards of your suit and you have the probability of a flush. Same with sequences in rank, there are only really a few tens of patterns in rank to give any straights.
If straights and flushes are figured out, check if your hold cards are pairs/trips/quads/2pair/fullhouse. For each (few) of these situations, find the number of cards which gives you the ranks in the paytable. Then take care of the 1 card hold, this will be pretty simple.

With this you exploit the fact that you can't have both a straight and pairs/trips/quads/2pair/fullhouse, and likewise pairs/trips/quads/2pair/fullhouse and flushes. Of course you can have straights and flushes, this you can take into account.

The code is rather lengthy (and boring) to write, with a lot of if-branches and bit manipulations - but it runs reasonably *fast*. I think step 1 was like 10 million evaluations per second on my crappy laptop. The key point (as mentioned before) is the internal card representation, with an efficient access to ranks and suits.
I think I used some bit-like representation for each card, i.e. 23456789TJQKA00c00h00s00d, i.e. Ace of Spades was (binary) 0000000000001000000001000.
With this you can do tricks like numerically add up (up to 7) hold cards, and the lowest resulting bits of the sum gives you the number of hold cards for each suit. If you OR all hold cards together, you can AND with a mask to check for various kinds of straight fragments, i.e. the mask 0011110000000000000000000 gives you the open end 4-7 straight draw.
  • Jump to: