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.

cards1.JPG

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.

cards2.JPG

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

cards4.JPG

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.
cards5.JPG

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.

22 Responses to “Puzzler: Six card poker”

  1. DanK replied on :

    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:

    
    low = hand(end);
    high = hand(3:5);
    middle = hand(1:2);
    
  2. DanK replied on :

    My solution is:

    
    squareHand = repmat(hand(:),1,4);
    counts = [sum(hand==1) sum(hand==2) sum(hand==3) sum(hand==4)]; % How many of each do I have?
    low = hand(end); %Try to always win the low card
    counts(low)=counts(low)-1; % Eliminate that card so I don't reuse it
    largePair = find(counts>1,1,'last'); %find the largest pair
    middle = [largePair largePair];
    counts(middle)=counts(middle)-2;  %Eliminate Them
    for n = 1:3
    	high(n)=find(counts,1,'first');  % High is whatever is left
    	counts(high(n))=counts(high(n))-1;
    end
    

    With a winning percentage of 57.5%
    Dan

  3. Doug replied on :

    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

  4. DanK replied on :

    OK last change: I can squeak out an extra 0.25% by adding a slight change.

    
    squareHand = repmat(hand(:),1,4);
    counts = [sum(hand==1) sum(hand==2) sum(hand==3) sum(hand==4)]; % How many of each do I have?
    low = hand(end); %Try to always win the low card
    counts(low)=counts(low)-1; % Eliminate that card so I don't reuse it
    largePair = find(counts>1,1,'last'); %find the largest pair
    if largePair == 4 && counts(3)>1 % Then I have three 4s, so computer doesn't have a pair of 4s
    	largePair =3;
    end
    middle = [largePair largePair];
    counts(middle)=counts(middle)-2;  %Eliminate Them
    high = zeros(1,3);
    for n = 1:3
    	high(n)=find(counts,1,'first');  % High is whatever is left
    	counts(high(n))=counts(high(n))-1;
    end
    
  5. ArthurG replied on :

    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.

    
    function [high, middle, low] = humanHand(hand)
    % Pick hand that has best expected score when compared to random hands
    
    %% Precompute probability table & permutations for speed
    persistent table iPerm
    if isempty(table)
        table = init;
        iPerm = perms(1:6);
    end
    %% Generate all possible permuations of my hand
    allHands = hand(iPerm);
    %% Score low, middle, and high parts of the hand
    scores = zeros(size(allHands,1), 3);
    scores(:,1) = calcScore(table(1), allHands(:,1));
    scores(:,2) = calcScore(table(2), allHands(:,2:3));
    scores(:,3) = calcScore(table(3), sum(allHands(:,4:6),2));
    %% Calculate the total score
    score = sum(scores,2);
    %% Find the permuation with the best score
    [maxScore, iMax] = max(score);
    %% Assign the cards in the best permutation
    low = allHands(iMax,1);
    middle = allHands(iMax, 2:3);
    high = allHands(iMax, 4:6);
    end % function myhand
    
    function score = calcScore(table, subs)
    % function that scores a hand based on probabilities in the table
    %% Convert the subscripts to vector indices
    if size(subs,2) == 1
        index = subs;
    else % size(subs,2) == 2
        index = sub2ind(size(table.prob), subs(:,1), subs(:,2));
    end
    %% Calculate the score (0.5 for a tie, 1 for a win)
    score = 0.5*table.prob(index) + 1*table.cumprob(index);
    end % function calcScore
    
    function table = init()
    % Calculate tables of probabilities
    % * table.prob = probability of a tie
    % * table.cumprob = probility of a win
    %
    % table(i) is for a hand of i cards (i=1:3)
    % The table of 3-card hands is indexed by sum of values
    % The tables of 1- & 2-card hands are indexed by the hands
    %% Initialize the table
    table = struct('prob', [], 'cumprob', []);
    %% Create a dec of cards
    deck = repmat(1:4, [1 4]);
    n = numel(deck);
    %% Create matrix of all possible hands of i cards
    hand3 = zeros(n*(n-1)*(n-2),3);
    hand2 = zeros(n*(n-1),2);
    hand1 = [1:4]';
    idx3 = 0;
    idx2 = 0;
    for i=1:16
        for j=1:16
            if i==j
                continue
            end
            idx2 = idx2 + 1;
            hand2(idx2,:) = deck([i j]);
            for k=1:16
                if i==k || j==k
                    continue
                end
                idx3 = idx3 + 1;
                hand3(idx3, :) = deck([i j k]);
            end
        end
    end
    %% Calculate the probabilities for 3-card hands
    h3 = hist(sum(hand3,2), 1:12);
    table(3).prob = h3(:) ./ size(hand3,1);
    table(3).cumprob = mycumsum(table(3).prob);
    %% Calculate the probabilities for 2-card hands
    % Note: sort the hands so (3,4) also counts as (4,3)
    h2 = hist3(sort(hand2,2), {1:4,1:4});
    prob2 = h2 ./ size(hand2,1);
    % The hands are ranked (in increasing order) as:
    ivec2 = ...
    [   7   1   2   4
        0   8   3   5
        0   0   9   6
        0   0   0   10  ];
    [dummy,iSort] = sort(ivec2(:));
    prob2vec = prob2(iSort);
    cumprob2vec = mycumsum(prob2vec);
    cumprob2 = zeros(size(prob2));
    cumprob2(iSort) = cumprob2vec;
    % Make 2-card prob and cumprob symmetric so I can also look up (4,3) as (3,4)
    table(2).prob = prob2 + triu(prob2,1)';
    table(2).cumprob = cumprob2 + triu(cumprob2,1)';
    %% Calculate the probability of 1-card hands
    table(1).prob = [1 1 1 1]'/4;
    table(1).cumprob = mycumsum(table(1).prob);
    end % function init
    
    function s = mycumsum(x)
    % calculate the probability of winning using cumsum
    x = x(:);
    s = cumsum([0; x(1:end-1)]);
    end % function mycumsum
    
  6. ArthurG replied on :

    *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!

  7. Timur replied on :

    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?

  8. Doug replied on :

    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

  9. Doug replied on :

    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

  10. Timur replied on :

    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.

    function [high, middle, low] = humanHand(hand)
    
    counts = [sum(hand==1) sum(hand==2) sum(hand==3) sum(hand==4)]; % How many of each do I have?
    largePair = find(counts>1,1,'last');
    middle = [largePair largePair];
    counts(middle)=counts(middle)-2;
    
    high = zeros(1,3);
    for n = 1:3
    	high(n)=find(counts,1,'last');  % High is whatever is left
    	counts(high(n))=counts(high(n))-1;
    end
    
    low = hand(end);
    counts(low)=counts(low)-1;
    

  11. Timur replied on :

    On a ~80% over DanK’s solution mentioned in reply #4:

    
    function [high, middle, low] = humanHand(hand)
    
    counts = [sum(hand==1) sum(hand==2) sum(hand==3) sum(hand==4)]; % How many of each do I have?
    
    largePair = find(counts>1,1,'last');
    middle = [largePair largePair];
    counts(middle)=counts(middle)-2;
    
    high = zeros(1,3);
    for n = 1:3
    	high(n)=find(counts,1,'last');  % High is whatever is left
    	counts(high(n))=counts(high(n))-1;
        if n==2
            low = hand(end);
            counts(low)=counts(low)-1;
        end
    end
    

  12. Doug replied on :

    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

  13. Timur replied on :

    Sorry, I accidently altered the hand I was given in the code.

  14. pete replied on :

    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?

  15. Doug replied on :

    Pete,

    Thank you for that. I have updated all the files and confirmed they work from 2007a forward.

    Doug

  16. Adam replied on :

    you should pre-allocate your humanFinal array.

    If I can ever do better than 45% I’ll post a result :-/

  17. Doug replied on :

    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

  18. Jim Hokanson replied on :

    [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.

    
    function [high, middle, low] = humanHand(hand)
    
    persistent strategy
    handInput = hand; %a bit sloppy, I overwrote this in the persistent code
    
    if isempty(strategy)
        %Generation of all possible hands
        %===================================
        numCardsInDeck = 16;
        uniqueFound = 0;
            possHands = [];
        while uniqueFound ~= 68
            runSize = 1000;
            tempPossHands = zeros(runSize,6);
            for i = 1:runSize %Not sure what the best way of dividing this up is
                deck = ceil(randperm(numCardsInDeck)/4);
                tempPossHands(i,:) = sort(deck(1:6));
            end
            tempPossHands = unique(tempPossHands,'rows');
            possHands = [possHands; tempPossHands]; %#ok
            possHands = unique(possHands,'rows');
            uniqueFound = size(possHands,1);
        end
    
        %calculating the probability -> I didn't actually use this
        %========================================
        totalP = zeros(1,size(possHands,1));
        for i = 1:size(possHands,1)
           count = [4 4 4 4];
           p = 1;
           for j = 1:6
               index = possHands(i,j);
               p = p*count(index)/sum(count);
               count(index) = count(index) - 1;
           end
           totalP(i) = p;
        end
    
        %The computer strategy, I think
        %========================================
        cS = possHands; %cs -> computer Strategy
        totalCS = zeros(size(cS,1),12);
        for i = 1:size(cS,1)
           hand = cS(i,:); %Note these values are already sorted
           L = hand(end);
           hand(end) = [];
           N = hist(hand,[1 2 3 4]);
           I = find(N >= 2); %You'll always get a pair by the nature of the game
           I = I(end); %Grab the highest valued set
           M = [I I]; %Note, there will always be at least 2 pairs, so this always works
           I = find(hand == I,2); %find 2 of these values and delete them
           hand(I) = [];
           H = hand;
           totalCS(i,:) = [cS(i,:) H M L]; %Note going back to cS because we've deleted from hand
        end
    
        %AT THIS POINT WE HAVE THE FULL STRATEGY OF THE OPPONENT NOW TO SEE WHAT WE SHOULD DO
        %==========================================================================
    
        allCount = 0;
        for i = 1:size(possHands,1)
            for j = 1:size(possHands,1)
                BothHands = [possHands(i,:) possHands(j,:)];
                N = hist(BothHands,[1 2 3 4]);
                if isempty(find(N > 4,1))
                    [Total] = genAllHands(possHands(j,:));
                    for k = 1:size(Total,1)
                        wins = determineWin(Total(k,1:3),Total(k,4:5),Total(k,6),totalCS(i,7:9),totalCS(i,10:11),totalCS(i,12));
                        allCount = allCount + 1;
                        allPossible(allCount,:) = [possHands(i,:) possHands(j,:) Total(k,:) wins(4) totalP(i)];   %#ok
                    end
    
                end
            end
        end
    
        %Form of allPossible
        %==========================================================================
        %cols 1-6 -> his cards, 7-12 -> my cards,  13-18 -> an approach with my cards H M L
        %19 whether or not I would win,lose,tie
        %20 -> probability of that hand
    
        %NOW, KNOWING EVERYTHING I CAN PLAY AGAINST THE COMPUTER
        %I DETERMINE THE BEST STRATEGY, FOR WHATEVER HAND I GET
    
        strategy = zeros(size(possHands,1),12);
        for i = 1:length(possHands)
            hand = possHands(i,:);
            badRows = ismember(allPossible(:,1:6),hand,'rows'); %The computer won't have my hand!
            rowsMine = ismember(allPossible(:,7:12),hand,'rows');
            rowsMine(badRows == 1) = 0;
            remainder = allPossible(rowsMine,13:end);
    
            %This was some combining of code from different files
            %I had possHands here originally, but not I have possHands from above
            %so I made this one possHands2
            [possHands2,I,J] = unique(remainder(:,1:6),'rows'); %get all the computers hands & what I can play against them
    
            scoresTot = zeros(size(possHands2,1),1);
            scoresTot2 = zeros(size(possHands2,1),1);
            for j = 1:size(possHands2,1)
                temp = remainder(J == j,7);
                scoresTot(j) = sum(temp);
                scoresTot2(j) = scoresTot(j)/length(temp);
            end
            [junk,I] = max(scoresTot);
            [junk2,I2] = max(scoresTot2);
            if I ~= I2
                keyboard
            end
    
            high   = possHands2(I,1:3);
            middle = possHands2(I,4:5);
            low    = possHands2(I,6);
            strategy(i,:) = [hand high middle low];
        end
    
    end %if isempty (strategy)
    
        %=========================================
        %The code that runs every trial
        %=========================================
        I = ismember(strategy(:,1:6),handInput,'rows');
        rowToUse = strategy(I,7:12);
    
        high   = rowToUse(1:3);
        middle = rowToUse(4:5);
        low    = rowToUse(6);
    
    %=======================================================================
    %EXTRA FUNCTIONS
    %=====================================================================
    
    function wins = determineWin(highH,middleH,lowH,highC,middleC,lowC)
        humanScore(3) = compareHigh  (  highH ,   highC);
        humanScore(2) = compareMiddle(middleH , middleC);
        humanScore(1) = compareLow   (   lowH ,    lowC);
        win = compareLow(sum(humanScore), 1.5);
        wins = [humanScore(3),humanScore(2),humanScore(1),win];
    
    function score = compareHigh(H, C)
        if sum(H) > sum(C)
            score = 1;
        elseif sum(H)  C
            score = 1;
        elseif H  n x 6 of High,Middle,Low
    %all unique combinations with the rules i.e 2 2 2 3 4 4
    %is the same as 2 2 2 4 3 4
        persistent perm5
        if isempty(perm5)
            perm5 = perms(1:5);
        end
    
        uniqNums = unique(hand);
    
        count = 0;
        for i = 1:length(uniqNums)
            tempHand = hand;
            I = find(hand == uniqNums(i),1);
            tempHand(I) = [];
            for j = 1:120
                count = count + 1;
                H(count,:) = sort(tempHand(perm5(j,1:3)));
                M(count,:) = sort(tempHand(perm5(j,4:5)));
                L(count,:) = uniqNums(i);
            end
        end
            Total = unique([H M L],'rows');
  19. Jim Hokanson replied on :

    [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.

    
    function [high, middle, low] = humanHand(hand)
    persistent strategy
    handInput = hand; %a bit sloppy, I overwrote this in the persistent code
    if isempty(strategy)
        %Generation of all possible hands
        %===================================
        numCardsInDeck = 16;
        uniqueFound = 0;
            possHands = [];
        while uniqueFound ~= 68
            runSize = 1000;
            tempPossHands = zeros(runSize,6);
            for i = 1:runSize %Not sure what the best way of dividing this up is
                deck = ceil(randperm(numCardsInDeck)/4);
                tempPossHands(i,:) = sort(deck(1:6));
            end
            tempPossHands = unique(tempPossHands,'rows');
            possHands = [possHands; tempPossHands]; %#ok
            possHands = unique(possHands,'rows');
            uniqueFound = size(possHands,1);
        end
    
        %The computer strategy, I think
        %========================================
        cS = possHands; %cs -> computer Strategy
        totalCS = zeros(size(cS,1),12);
        for i = 1:size(cS,1)
           hand = cS(i,:); %Note these values are already sorted
           L = hand(end);
           hand(end) = [];
           N = hist(hand,[1 2 3 4]);
           I = find(N >= 2); %You'll always get a pair by the nature of the game
           I = I(end); %Grab the highest valued set
           M = [I I]; %Note, there will always be at least 2 pairs, so this always works
           I = find(hand == I,2); %find 2 of these values and delete them
           hand(I) = [];
           H = hand;
           totalCS(i,:) = [cS(i,:) H M L]; %Note going back to cS because we've deleted from hand
        end
    
        %AT THIS POINT WE HAVE THE FULL STRATEGY OF THE OPPONENT NOW TO SEE WHAT WE SHOULD DO
        %======================================================================
        allCount = 0;
        for i = 1:size(possHands,1)
            for j = 1:size(possHands,1)
                BothHands = [possHands(i,:) possHands(j,:)];
                N = hist(BothHands,[1 2 3 4]);
                if isempty(find(N > 4,1))
    
                    %His hand -> possHands(i,:)
                    %My hand -> possHands(j,:)
                    %with my cards removed, what is his probability of getting his hand
                    counts = [4 4 4 4];
                    mH = possHands(j,:);
                    counts = counts - hist(mH,[1 2 3 4]);
                    counts2 = hist(possHands(i,:),[1 2 3 4]);
    
                    p = 1;
                    for k = 1:4
                        if counts(k) == 0 && counts2(k) ~= 0
                            p = 0; %This should never occur with if statement above
                        else
                            p = p*nchoosek(counts(k),counts2(k));
                        end
                    end
    
                    [Total] = genAllHands(possHands(j,:));
                    for k = 1:size(Total,1)
                        wins = determineWin(Total(k,1:3),Total(k,4:5),Total(k,6),totalCS(i,7:9),totalCS(i,10:11),totalCS(i,12));
                        allCount = allCount + 1;
                        allPossible(allCount,:) = [possHands(i,:) possHands(j,:) Total(k,:) wins(4) p];   %#ok
                    end
                end
            end
        end
    
        %Form of allPossible
        %==========================================================================
        %cols 1-6 -> his cards, 7-12 -> my cards,  13-18 -> an approach with my cards H M L
        %19 whether or not I would win,lose,tie
        %20 -> probability of that hand (really ways to get hand with my hand being what it is)
    
        %NOW, KNOWING EVERYTHING I CAN PLAY AGAINST THE COMPUTER I DETERMINE THE BEST STRATEGY, FOR WHATEVER HAND I GET
        strategy = zeros(size(possHands,1),12);
        for i = 1:length(possHands)
            hand = possHands(i,:);
            badRows = ismember(allPossible(:,1:6),hand,'rows'); %The computer won't have my hand!
            rowsMine = ismember(allPossible(:,7:12),hand,'rows');
            rowsMine(badRows == 1) = 0;
            remainder = allPossible(rowsMine,13:end);
    
            %This was some combining of code from different files
            %I had possHands here originally, but not I have possHands from above
            %so I made this one possHands2
            [possHands2,I,J] = unique(remainder(:,1:6),'rows'); %get all the computers hands & what I can play against them
    
            scoresTot = zeros(size(possHands2,1),1);
            scoresTot2 = zeros(size(possHands2,1),1);
            for j = 1:size(possHands2,1)
                temp = remainder(J == j,7);
                p = remainder(J == j,8);
                scoresTot(j) = sum(temp.*p);
                scoresTot2(j) = scoresTot(j)/length(temp);
            end
            [junk,I] = max(scoresTot);
            [junk2,I2] = max(scoresTot2);
            if I ~= I2
                keyboard
            end
    
            high   = possHands2(I,1:3);
            middle = possHands2(I,4:5);
            low    = possHands2(I,6);
            strategy(i,:) = [hand high middle low];
        end
    
    end %if isempty (strategy)
    
        %=========================================
        %The code that runs every trial
        %=========================================
        I = ismember(strategy(:,1:6),handInput,'rows');
        rowToUse = strategy(I,7:12);
    
        high   = rowToUse(1:3);
        middle = rowToUse(4:5);
        low    = rowToUse(6);
    
    %=======================================================================
    %EXTRA FUNCTIONS
    %=====================================================================
    
    function wins = determineWin(highH,middleH,lowH,highC,middleC,lowC)
        humanScore(3) = compareHigh  (  highH ,   highC);
        humanScore(2) = compareMiddle(middleH , middleC);
        humanScore(1) = compareLow   (   lowH ,    lowC);
        win = compareLow(sum(humanScore), 1.5);
        wins = [humanScore(3),humanScore(2),humanScore(1),win];
    
    function score = compareHigh(H, C)
        if sum(H) > sum(C)
            score = 1;
        elseif sum(H)  C
            score = 1;
        elseif H  n x 6 of High,Middle,Low
    %all unique combinations with the card game rules i.e 2 2 2 3 4 4
    %is the same as 2 2 2 4 3 4 but not as 2 2 3 2 4 4
        persistent perm5
        if isempty(perm5)
            perm5 = perms(1:5);
        end
        uniqNums = unique(hand);
        count = 0;
        for i = 1:length(uniqNums)
            tempHand = hand;
            I = find(hand == uniqNums(i),1);
            tempHand(I) = [];
            for j = 1:120
                count = count + 1;
                H(count,:) = sort(tempHand(perm5(j,1:3)));
                M(count,:) = sort(tempHand(perm5(j,4:5)));
                L(count,:) = uniqNums(i);
            end
        end
            Total = unique([H M L],'rows');
  20. Jim Hokanson replied on :

    [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.

    
    function Total = genAllHands(hand)
    %Total -> n x 6 of High,Middle,Low
    %all unique combinations with the card game rules i.e 2 2 2 3 4 4
    %is the same as 2 2 2 4 3 4 but not as 2 2 3 2 4 4
        persistent perm5
        if isempty(perm5)
            perm5 = perms(1:5);
        end
        uniqNums = unique(hand);
        count = 0;
        for i = 1:length(uniqNums)
            tempHand = hand;
            I = find(hand == uniqNums(i),1);
            tempHand(I) = [];
            for j = 1:120
                count = count + 1;
                H(count,:) = sort(tempHand(perm5(j,1:3)));
                M(count,:) = sort(tempHand(perm5(j,4:5)));
                L(count,:) = uniqNums(i);
            end
        end
            Total = unique([H M L],'rows');
    
  21. Jim Hokanson replied on :

    [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.

  22. Doug replied on :

    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

Leave a Reply

Wrap code fragments inside <pre> tags, like this:

<pre class="code">
a = magic(3);
sum(a)
</pre>

If you have a "<" character in your code, either follow it with a space or replace it with "&lt;" (including the semicolon).


MathWorks

Doug Hull is a proud MathWorker who is on a mission to help you with MATLAB.

Doug's picture

These postings are the author's and don't necessarily represent the opinions of The MathWorks.