image thumbnail
from Texas Holdem Win Probabilities by Oliver Rice
Calculates the probability of each player winning a round of Texas Holdem

CardAnalyze( Hand, Table, nPlayers, nIter )
function [ WinProb ] = CardAnalyze( Hand, Table, nPlayers, nIter )
%% Licence Information
%  
%  Author    : Oliver Rice
%  Employer  : University College London
%  Title     : PhD Candidate Financial Computing
%  Date      : May 5, 2012

%  Licence   : Free for academic use only

%% Instructions/Explanation/Example
%
% This function estimates the probability of each player winning
% a Round of Texas Holdem
% Hand     = Matrix of Known Cards (KnownPlayers X 2)
% Table    = An Array of Cards Presently on the Table
% nPlayers = Total Number of Players Sitting at the Table
% nIter    = Number of Monte Carlo Iters to Estimate Probabilities

% Each Card is designated by the whole number (e.g. 5.2 = 5)
% The suit of each card is represented by the decimal (e.g. 8.1 = .1)

% When size(Hand,2)==nPlayers the function assumes perfect information
%   meaning true probabilities are estimated
%
% To estimate the probability of an individual winning under normal
%   circumstances, 'Hand' is a 2 x 1 matrix of the individual's cards
%
% To estimate probability of an individual winning when nCollude players
% are colluding, 'Hand' is a 2 x nCollude matrix of the colluder's cards
%
% Example Inputs
% Hand     = [2.3, 14.3, 11.4, 9.3; 11.1, 10.1, 13.1, 10.2];
% Table    = [9.1, 6.3, 5.2];
% nPlayers = 6;
% nIter    = 8000;
%
% Yields P(Win) for each player when 3 players' cards are known
% (i.e. colluding) against an additional 3 unknown players with 3 cards on
% the table.

%%--------------PRE-PROCESSING-------------------------------------------
%% Calculate Which Cards Have Not Been Used
% Create a Deck of Cards
%   Whole number = Card Number
%   Decimal      = Card Suit
Cards = 2:.1:15;
Cards(mod(Cards,1)>.41 | mod(Cards,1)<.09)=[];

% Find Used Cards
UsedCards = horzcat(reshape(Hand,1,size(Hand,1)*size(Hand,2)), Table);

% Calculate Remaining Cards by Removing Used Cards from Full Deck
Remaining = Cards;
Remaining(ismember(Remaining,UsedCards))=[];
clear UsedCards Cards

%% Generate Samples from Remaining Cards to Estimate Probabilities
%Create Function to Randomly Sample from Remaining Cards
% Randomly Select Pocket Cards For Unknown Players & Table Cards for All
PermutRemain = zeros(nIter,(5)+2*(nPlayers-size(Hand,2)));
parfor i = 1:nIter
    PermutRemain(i,:) = randsample(Remaining,(5)+2*(nPlayers-size(Hand,2)));
end
clear SampleRandomly Remaining
%% Generate Cards Available to Known Players for Each Iteration
% Pre-allocate size for efficiency
PlayerHands = zeros(nPlayers,nIter,7);
for i = 1:size(Hand,2)
    PlayerHands(i,1:nIter,1:2)=repmat(transpose(Hand(:,i)),nIter,1);
    PlayerHands(i,1:nIter,3:7)=reshape(PermutRemain(:,end-4:end),...
        1,nIter,5);
    if ~isempty(Table)
        PlayerHands(i,1:nIter,3:3+length(Table)-1)=...
            repmat(Table,nIter,1);
    end
end

%% Generate Cards Available to Unknown Players for Each Iteration
% Pre-allocate size for efficiency
UnknownPlayerHands = zeros(nPlayers-size(Hand,2),nIter,7);
if size(Hand,2)<nPlayers
    for i = 1:nPlayers-size(Hand,2)
        UnknownPlayerHands(i,1:nIter,1:2) = ...
            reshape(PermutRemain(:,i*2-1:i*2),1,nIter,2);
        UnknownPlayerHands(i,1:nIter,3:7) = ...
            reshape(PermutRemain(:,end-4:end),1,nIter,5);
        if ~isempty(Table)
            UnknownPlayerHands(i,1:nIter,3:3+length(Table)-1)=...
                repmat(Table,nIter,1);
        end
    end
end

%% Concatenate Known and Unknown Player's Cards to Single Matrix
PlayerHands(size(Hand,2)+1:nPlayers,:,:)=...
    UnknownPlayerHands(:,:,:);
clear UnknownPlayerHands PermutRemain
%----------END-PRE-PROCESSING-------------------------------------------

%----------HAND-EVALUATION-LOGIC----------------------------------------

% Used Later to Calcualte Straights
Straight = horzcat(11:14, 2:14);

for i = 1:size(PlayerHands,1)
    
    % Create Matrix to Hold Best Possible Hand
    BestHand = zeros(nIter, 6); 
    
    % Reduce Dimension of Player Hands for Ease of Use
    CurrentPlayerHand=reshape(PlayerHands(i,:,:),nIter,7);   
        
    % Find Any Multiples of A Card Number
    FreqMatrix = histc(CurrentPlayerHand,.5:1:15,2);
    
    % Logical Matrix for Each Frequency
    oKind4 = FreqMatrix==4;
    oKind3 = FreqMatrix==3;
    oKind2 = FreqMatrix==2; 
    oKind1 = FreqMatrix==1;
    
    % Since the Max Function Returns the First Value of a Given Max Freq
    % The FreqMatrix is flipped left to right to make sure the highest
    % Card value for the given max frequency is returned
    
    % Calculate High Card
    Index = sum(oKind1>0,2)==7;
    if sum(Index)>=1
        [~,Loc]=max(fliplr(FreqMatrix(Index,:)==1),[],2); % Find 2ofK
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number 
        BestHand(Index,1)=1; % High Card Index
        BestHand(Index,2)=Loc; % Card Number
        FreqMatrixW=FreqMatrix(Index,:); % Working Copy
        for j = 1:length(Loc) % Cant figure out how to get rid of this
            FreqMatrixW(j,Loc(j))=0;
        end
        clear Loc
        [~,Loc] = max(fliplr(FreqMatrixW==1),[],2);
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,3)=Loc; % Card Num of 2nd Highest Remaining Card 
        for j = 1:length(Loc) % Cant figure out how to get rid of this
            FreqMatrixW(j,Loc(j))=0;
        end
        clear Loc
        [~,Loc] = max(fliplr(FreqMatrixW==1),[],2);
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,4)=Loc; % Card Num of 2nd Highest Remaining Card
        for j = 1:length(Loc) % Cant figure out how to get rid of this
            FreqMatrixW(j,Loc(j))=0;
        end
        clear Loc
        [~,Loc] = max(fliplr(FreqMatrixW==1),[],2);
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,5)=Loc; % Card Num of 2nd Highest Remaining Card
        for j = 1:length(Loc) % Cant figure out how to get rid of this
            FreqMatrixW(j,Loc(j))=0;
        end
        clear Loc
        [~,Loc] = max(fliplr(FreqMatrixW==1),[],2);
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,6)=Loc; % Card Num of 2nd Highest Remaining Card
        clear Loc FreqMatrixW
    end
    clear Index  
    
    % Calculate Pair
    Index = sum(oKind2>0,2)==1 & sum(oKind3>0,2)==0 & sum(oKind4>0,2)==0;
    if sum(Index)>=1
        [~,Loc]=max(fliplr(FreqMatrix(Index,:)==2),[],2); % Find 2ofK
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number 
        BestHand(Index,1)=2; % Pair Index
        BestHand(Index,2)=Loc; % Card Number
        clear Loc
        FreqMatrixW=FreqMatrix(Index,:); % Working Copy
        [~,Loc] = max(fliplr(FreqMatrixW==1),[],2);
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,3)=Loc; % Card Num of Highest Remaining Card
        for j = 1:length(Loc) % Cant figure out how to get rid of this
            FreqMatrixW(j,Loc(j))=0;
        end
        clear Loc
        [~,Loc] = max(fliplr(FreqMatrixW==1),[],2);
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,4)=Loc; % Card Num of 2nd Highest Remaining Card
        for j = 1:length(Loc) % Cant figure out how to get rid of this
            FreqMatrixW(j,Loc(j))=0;
        end
        clear Loc
        [~,Loc] = max(fliplr(FreqMatrixW==1),[],2);
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,4)=Loc; % Card Num of Last Remaining Card
        BestHand(Index,5:6)=0;
        clear Loc FreqMatrixW
    end
    clear Index
    
    
    
    % Calculate 2 Pair
    Index = sum(oKind2>0,2)>=2 & sum(oKind3>0,2)==0 & sum(oKind4>0,2)==0;
    if sum(Index)>=1
        [~,Loc]=max(fliplr(FreqMatrix(Index,:)==2),[],2); % Find Best 2ofK
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number 
        BestHand(Index,1)=3; % 2 Pair Index
        BestHand(Index,2)=Loc; % Card Number
        FreqMatrixW=FreqMatrix(Index,:); % Working Copy
        FreqMatrixW(FreqMatrixW==3)=0; % Ensure Max Card Isn't 2oK Card
        for j = 1:length(Loc) % Cant figure out how to get rid of this
            FreqMatrixW(j,Loc(j))=0;
        end
        clear Loc
        [~,Loc] = max(fliplr(FreqMatrixW==2),[],2);
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,3)=Loc; % Card Num 2nd Highest Pair
        clear Loc
        [~,Loc] = max(fliplr(FreqMatrixW==1),[],2);
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,4)=Loc; % Card Num of Remaining Card
        BestHand(Index,5:6)=0;
        clear Loc FreqMatrixW
    end
    clear Index
    
    % Calculate Straights
    for j = 1:13
        Index = sum(FreqMatrix(:,Straight(j:j+4))>0,2)==5;
        if sum(Index)>=1
            BestHand(Index,1)= 5; % Straight Index
            BestHand(Index,2)= Straight(j+4); % Highest Card In Straight
            BestHand(Index,3:6)=0;
        end
        clear Index
    end    
    clear j
    
    % Calculate Flush
    SuitFreq = histc(mod(CurrentPlayerHand,1),...
        .05:.1:.45,2); % Find Frequencies of Each Suit
    for j = 1:4
        Index = sum(SuitFreq(:,j),2)>=5;
        if sum(Index)>=1
            CPH = CurrentPlayerHand; % Working Copy
            CPH(round(mod(CPH,1)*10)~=j)=0; % Ignore Other Suits
            FreqMatrixBySuit = histc(CPH,.5:1:15,2); % New FreqMat by Suit
                        
            BestHand(Index,1)= 6; % Flush Index
            [~,Loc]=max(fliplr(FreqMatrixBySuit(Index,:)>0),[],2);
            Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
            BestHand(Index,2)=Loc; % Card Num Highest
            for k = 1:length(Loc) % Cant figure out how to get rid of this
                FreqMatrixBySuit(k,Loc(k))=0;
            end
            clear Loc
            [~,Loc]=max(fliplr(FreqMatrixBySuit(Index,:)>0),[],2);
            Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
            BestHand(Index,3)=Loc; % Card Num Highest
            for k = 1:length(Loc) % Cant figure out how to get rid of this
                FreqMatrixBySuit(k,Loc(k))=0;
            end
            clear Loc            
            [~,Loc]=max(fliplr(FreqMatrixBySuit(Index,:)>0),[],2);
            Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
            BestHand(Index,4)=Loc; % Card Num Highest
            for k = 1:length(Loc) % Cant figure out how to get rid of this
                FreqMatrixBySuit(k,Loc(k))=0;
            end
            clear Loc    
            [~,Loc]=max(fliplr(FreqMatrixBySuit(Index,:)>0),[],2);
            Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
            BestHand(Index,5)=Loc; % Card Num Highest
            for k = 1:length(Loc) % Cant figure out how to get rid of this
                FreqMatrixBySuit(k,Loc(k))=0;
            end
            clear Loc
            [~,Loc]=max(fliplr(FreqMatrixBySuit(Index,:)>0),[],2);
            Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
            BestHand(Index,6)=Loc; % Card Num Highest
            for k = 1:length(Loc) % Cant figure out how to get rid of this
                FreqMatrixBySuit(k,Loc(k))=0;
            end
            clear Loc FreqMatrixBySuit CPH
        end
        clear Index
    end

    % Calculate 3 of a Kind
    Index = sum(oKind3>0,2)==1 & sum(oKind2>0,2)==0 & sum(oKind4>0,2)==0;
    if sum(Index)>=1        
        [~,Loc]=max(fliplr(FreqMatrix(Index,:)==3),[],2); % Find Best 3ofK
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number 
        BestHand(Index,1)=4; % 3 of a Kind Index
        BestHand(Index,2)=Loc; % Card Number
        clear Loc
        FreqMatrixW=FreqMatrix(Index,:); % Working Copy
        FreqMatrixW(FreqMatrixW==3)=0; % Ensure Max Card Isn't 3oK Card
        [~,Loc] = max(fliplr(FreqMatrixW>0),[],2);
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,3)=Loc; % Card Num Highest Remainder
        for j = 1:length(Loc) % Cant figure out how to get rid of this
            FreqMatrixW(j,Loc(j))=0;
        end
        clear Loc
        [~,Loc] = max(fliplr(FreqMatrixW>0),[],2);
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,4)=Loc; % Card Num 2nd Highest Remainder
        BestHand(Index,5:6)=0;
        clear Loc Index FreqMatrixW
    end
    clear Index
    
    % Calculate Full House
    Index = sum(oKind3,2)==2 | (sum(oKind3,2)==1 & sum(oKind2,2)>0);
    if sum(Index)>=1
        BestHand(Index,1)=7; % Full House Index   
        [~,Loc]=max(fliplr(FreqMatrix(Index,:)==3),[],2); % Find Best 3oK
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,2)= Loc; %Save Card Num     
        FreqMatrixW = FreqMatrix(Index,:);
        for j = 1:length(Loc) % Cant figure out how to get rid of this
            FreqMatrixW(j,Loc(j))=0;
        end
        clear Loc
        [~,Loc]=max(fliplr(FreqMatrixW(:,:)>=2),[],2); % Find Best 2oKind    
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,3)= Loc; %Save Card Num
        clear Loc
    end
    clear Index
        
    % Calculate 4 of a Kind
    Index = sum(oKind4,2)>0;
    if sum(Index)>=1
        [~,Loc]=max(fliplr(FreqMatrix(Index,:)==4)...
            ,[],2); % Find Best 4 of A Kind
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,1)=8; % 4 of a Kind Index
        BestHand(Index,2)=Loc; % Card Number    
        clear Loc
        FreqMatrixW=FreqMatrix(Index,:); % Working Copy
        FreqMatrixW(FreqMatrixW==4)=0; % Ensure Max Card Isn't 4oK Card
        
        [~,Loc] = max(fliplr(FreqMatrixW>0),[],2);
        Loc = size(FreqMatrix,2)-Loc+1; % Get Card Number
        BestHand(Index,3)=Loc; % Card Number of Highest Remainder
        BestHand(Index,4:6)=0;
        clear Loc FreqMatrixW
    end
    clear Index
    
    % Calculate Straight Flush
    for j = 1:13
        Index = sum(FreqMatrix(:,Straight(j:j+4))>0,2)==5;
        if sum(Index)>=1
            for q = 1:4
                if sum(round(CurrentPlayerHand(Index,:)*10)/10==...
                    Straight(j)+q/10)+...
                    sum(round(CurrentPlayerHand(Index,:)*10)/10==...
                    Straight(j+1)+q/10)+...
                    sum(round(CurrentPlayerHand(Index,:)*10)/10==...
                    Straight(j+2)+q/10)+...
                    sum(round(CurrentPlayerHand(Index,:)*10)/10==...
                    Straight(j+3)+q/10)+...
                    sum(round(CurrentPlayerHand(Index,:)*10)/10==...
                    Straight(j+4)+q/10)==5
                BestHand(Index,1)= 9; % Straight Flush Index
                BestHand(Index,2)= Straight(j+4); % Highest Card In Straight
                BestHand(Index,3:6)=0;
                end
            end
        end
        clear Index
    end    
    clear j
  
% Score Each Hand as Single Value For Direct Comparison    
ScoreHands(i,:) = BestHand(:,1)*10000000000+BestHand(:,2)*...
    100000000+BestHand(:,3)*1000000+BestHand(:,4)*10000+...
    BestHand(:,5)*100+BestHand(:,6)*1;
clear CurrentPlayerHand FreqMatrix BestHand oKind1 oKind2 oKind3 oKind4
end

%% Compute Winner of Each Iteration
% Create Function Handle to Avoid Loop
RetrieveMax = @(x) max(ScoreHands(:,x));
% Retrieve Best Score Per Hand
[HandMax,~] = RetrieveMax(1:nIter);

% Create nPlayer Rows of Hand Max for Logic Matrix Comparison
HandMax = repmat(HandMax,nPlayers,1);
% Create Logical Index of Winners By Score
WinnerMatrix = HandMax==ScoreHands;
% Calculate the Probability of Each Player Winning Adjusted for Ties
WinProb = WinnerMatrix/sum(WinnerMatrix,1);

    
end

Contact us