Doug's MATLAB Video Tutorials
June 24th, 2008
Puzzler: Six card poker
Update: This solution was found faster than expected. I have replaced the original with a stronger solution that I was holding in reserve. Congrats to DanK for solving the original ( found here in a zip). The contest is still running with the upgraded computerHand.p
I have been known to play the occasional hand of poker, so I have this poker-inspired challenge. I made up a simple poker type of game. The deck consists of 16 cards, four each of the values 1-4. Each player is dealt six cards.

Once you have the cards, you set three hands: High (3 cards), Middle (2 cards), Low (1 card). Each of these is scored differently. High hand is ranked by the sum of the cards. In the Middle hand largest pair wins. Pairs beat non-pairs, then high card wins in un-paired hands, ties broken by second card. In the Low hand, high card wins.

Lets look at two examples with the same cards. Notice the right hand player loses or draws depending on his strategy.


You should be able to see that the right hand player could have won also!
Each hand is worth 1 point for a win, 0.5 for a draw. Win more than 1.5 of the three total points per round for the win.

I coded up a good, but simple strategy. I am confident that someone can make a strategy that will consistently win. Free MATLAB t-shirt for the first to come up with it.
Modify humanHand.m and submit it in its entirety in the comments.
function [high, middle, low] = humanHand(hand)
% Make a valid, random hand for the human
% hand will consist of six random 'cards' selected from
% [1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4];
%
% Create three hands to be judged against the computer
% doing the smae with six other cards selected from the remainder
% of the above deck.
%
% HIGH hand consists for three cards. The value of
% the hand is the sum of the cards. Higher is better.
%
% MIDDLE hand consists of two cards. Any paired hand beats any non-paired hand.
% If the competeing hands are both paired, highest pair wins. If the
% competing hands are not paired, then the highest card in each hand
% determines winner. If highest cards are the same, second highest cards
% are compared.
%
% LOW hand consists of a single card. Highest card wins.
%
% This is a terrible plan that makes random hands.
randomIndex = randperm(numel(hand));
hand = hand(randomIndex);
high = hand(1:3);
middle = hand(4:5);
low = hand(6);
Here is the code to run the contest: (No need to modify this code)
function humanPerWins = main(numPlays)
numCardsInDeck = 16;
numCardsInHand = 6;
for i = 1:numPlays;
deck = ceil(randperm(numCardsInDeck)/4);
hand.computer = sort(deck(1:2:numCardsInHand*2 - 1));
hand.human = sort(deck(2:2:numCardsInHand*2 ));
hand.deck = sort(deck(numCardsInHand*2 + 1:numCardsInDeck));
[highC, middleC, lowC] = computerHand(hand.computer);
[highH, middleH, lowH] = humanHand(hand.human);
humanScore(3) = compareHigh ( highH , highC);
humanScore(2) = compareMiddle(middleH , middleC);
humanScore(1) = compareLow ( lowH , lowC);
humanFinal(i) = compareLow(sum(humanScore), 1.5);
end
clf
hist(humanFinal); ylim([0 numPlays])
clc
humanPerWins = sum(humanFinal)/numPlays * 100;
disp(['Your score against the computer: ' num2str(humanPerWins) '%.'])
function score = compareHigh(H, C)
if sum(H) > sum(C)
score = 1;
elseif sum(H) < sum(C)
score = 0;
else
score = 0.5;
end
function score = compareMiddle(H, C)
pairH = (H(1) == H(2));
pairC = (C(1) == C(2));
score = compareLow(pairH, pairC);
if score ~= 0.5 %is pair vs non-pair
return
else %is non-pairs or pairs
H = sort(H);
C = sort(C);
highH = H(2);
highC = C(2);
score = compareLow(highH, highC);
if score ~= 0.5
return
else
lowH = H(1);
lowC = C(1);
score = compareLow(lowH, lowC);
end
end
function score = compareLow(H, C)
if H > C
score = 1;
elseif H < C
score = 0;
else
score = 0.5;
end
Remember to use the <pre><code> and </code></pre> tags around your code in the comments.
The p-coded file that dictates the computer strategy is: computerHand.p
You can find all three files here.
Some interesting notes: My strategy against itself is about 50% win rate (of course). I tried several strategies, and used the best one overall. However, for any given "hand vs hand" one of the "lesser" strategies would beat mine. I think a blended strategy that chooses from many strategies based on the cards will win.
13:55 UTC |
Posted in Topic: Puzzler |
Permalink |
22 Comments »
You can follow any responses to this entry through the RSS 2.0 feed.
You can skip to the end and leave a response. Pinging is currently not allowed.
Leave a Reply
|
Well, I can get the winning odds up to 55%:
First: Here’s my guess at what is in computerHand.p (gives almost exactly 50% odds:
My solution is:
With a winning percentage of 57.5%
Dan
Dan,
You got a good solution, I figured anything that routinely got 55% or above was a winner. I have updated the computerHand.p to do something slightly smarter. You get the first T-shirt. Send me your address and shirt size off-line.
The contest is still going with the now harder solution. Dan, you up for the next challenge, too? We are at about 50-50 against the new solution.
-Doug
OK last change: I can squeak out an extra 0.25% by adding a slight change.
There aren’t that many possibilities, so I decided to use a “brute force” strategy: Pick the hand that has the best expected score against random hands. I used a couple of persistent variables to store some relatively expensive calculations (namely, the probabilities for high, middle, and low hands; and the permutations for a 6-card hand).
This strategy beats the computer about 60% of the time.
It would be nice to take a look at what hands this strategy acually picks, but I’ve already spent too much time. :-)
Also, I used the profiler to compare timing of the strategies. My strategy is about 15% slower than the computer strategy. Interesting, the profiler reveals something about how the computer strategy’s p-code works (is this a bug or a feature????): The computerHand function has a strategy subfunction that calls bestLow, bestMiddle, and bestHigh subfunctions. bestMiddle takes about 75% of the time in strategy, and most of that time (about 80%) is spent in hist.
*ouch* The updated computerHand.p really changed things for me! My “brute force” approach went from winning ~60% of the time to winning ~49% of the time!
I get the following error
“??? File “/home/timur/puzzler/puzzlerPoker/computerHand.p” not recognizable as a P-file.”
I’m running MATLAB 7.4.0 (R2007a) on Kubuntu 8.04. I ‘ve tried both the original and the updated computerHand.p.
Something I’ve forgot to do?
Arthur,
Interesting solution. I made this problem small enough that brute force would be possible. Here is a thought. The computer is “not playing with a full deck”. Your init function to make the look-up table does not take into account that the cards in the human hand are *not* available for the computer to use.
Would this make for a better solution?
Doug
Timur,
I just download the new set of files, they are still working on my machine. I don’t know what to tell you.
Doug
With a few alterations, I get a ~70% win-percentage over the original of DanK.
Unfortunately, I cannot test it on the P-file, due to unknown errors.
On a ~80% over DanK’s solution mentioned in reply #4:
Timur,
You do win *a lot*. The problem is if we were playing in the Wild West someone would accuse you of having a few extra cards up your sleeve! Take a look at the hand that is given and the hands returned by the algorithm:
hand =
1 1 1 1 3 4
high =
4 3 4
middle =
1 1
low =
4
Two of those ones became fours by the time the algorithm finished. “Know when to walk away, know when to RUN!” -The Gambler!
-Doug
Sorry, I accidently altered the hand I was given in the code.
Is there a version problem here?
People without 2008A can’t seem to run the p-code. Any chance of a 2007-compatible computerHand.p?
Pete,
Thank you for that. I have updated all the files and confirmed they work from 2007a forward.
Doug
you should pre-allocate your humanFinal array.
If I can ever do better than 45% I’ll post a result :-/
Adam,
Yes, I should have pre-allocated that array. Pre-allocation does help speed when you can do it. The effects are most noticeable on larger vectors and arrays.
Good catch,
Doug
[Jim's code is extensive, it will be easier to download it from here: http://blogs.mathworks.com/images/pick/humanHand.m (copying from the code below might not work) -Doug]
Here’s my approach. It is a brute force method as well. Using the computers input (knowing their hand) about the best one can expect to do is 65%. I replicate the computer strategy in creating the persistent variable (the look up table). Then I figure out for every possible hand of mine (68 total) what high, middle, and low to play that will beat the computer most often. In the end the code that runs every time just looks up what hand I got, then plays the one most likely to win.
On average I get between 54 to 55% correct. I tried for a few hours to consistently get above 55% but it just wasn’t happening. The only idea which I didn’t implement as it would mean losing a lot of the up front calculations would have been to build some history into my hands so that whatever hands I’ve had recently, I figure the computer won’t have. Although even that is tricky as there are so many ways to get the same hand. My attempt at building in a probability as to which hands were more or less likely for the current hand like 111144 being less likely then 112233 ended up hurting my predictions.
[Jim's code is extensive, it will be easier to download it from here: http://blogs.mathworks.com/images/pick/humanHand.m (copying from the code below might not work) -Doug]
Oops, sorry for the second long attachment, I correctly implemented the probability model to get about 55.1 – 55.2%, also some code at the end was cut off.
[Jim's code is extensive, it will be easier to download it from here: http://blogs.mathworks.com/images/pick/humanHand.m (copying from the code below might not work) -Doug]
Here’s the last function, which keeps on getting chopped off. The 3 functions used to determine the score for high, medium, and low are also needed.
[Jim's code is extensive, it will be easier to download it from here: http://blogs.mathworks.com/images/pick/humanHand.m -Doug]
Any thoughts? I think this (55.1 – 55.2%) may be the best you can do. Thanks.
Jim,
This is the kind of strategy that I expected would be needed to win against my computerHand. I noted that no matter what strategy is picked by the computer there is almost always a strategy that will win by humanHand. So, playing against an omniscient player would guarantee a landslide.
Your code does the best to be that omniscient player by generating all possible hands and figuring a strategy based on that. There is hidden knowledge as to what cards are available and what the computer strategy is, so your 55% is very respectable, and the best I have seen.
The computer strategy that finally got used was this:
Make the best possible one card hand, then the best possible two card hand.
There are six possible “simple, greedy” strategies, basically make the best N card hand, then the best possible M card hand. Of these six “simple greedy” strategies, [1 2 3] is the best against the others. [2 1 3] is the second best. I started the contest with [2 1 3] and then moved to [1 2 3] once DanK bested me.
-Thanks for playing!
Doug