Android Open Source - pokerCCF Hand Evaluator






From Project

Back to project page pokerCCF.

License

The source code is released under:

Copyright (c) 2011-2014, Intel Corporation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redist...

If you think the Android project pokerCCF listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

// This file is part of the 'texasholdem' project, an open source
// Texas Hold'em poker application written in Java.
///* ww  w .j av a  2s  . c  om*/
// Copyright 2009 Oscar Stigter
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lo.wolo.pokerengine;

/**
 * Evaluator for calculating the value of a poker hand. <br />
 * <br />
 *
 * <b>NOTE:</b> This class is implemented with the focus on performance (instead of clean design).
 * 
 * @author Oscar Stigter
 */
public class HandEvaluator {
    
    /** The number of hand rankings. */
    private static final int NO_OF_RANKINGS  = 6;
    
    /** The maximum number of counting pairs. */
    private static final int MAX_NO_OF_PAIRS = 2;
    
    /** The ranking factors (powers of 13, the number of ranks). */
    private static final int[] RANKING_FACTORS = {371293, 28561, 2197, 169, 13, 1};
    
    /** The hand value type. */
    private HandValueType type;
    
    /** The hand value as integer number. */
    private int value = 0;
    
    /** The cards. */
    private final Card[] cards;
    
    /** The rank distribution (number of cards for each rank). */
    private int[] rankDist = new int[Card.NO_OF_RANKS];
    
    /** The suit distribution (number of cards for each suit). */
    private int[] suitDist = new int[Card.NO_OF_SUITS];
    
    /** The number of pairs. */
    private int noOfPairs = 0;
    
    /** The ranks of the pairs. */
    private int[] pairs = new int[MAX_NO_OF_PAIRS];
    
    /** The suit of the Flush. */
    private int flushSuit = -1;
    
    /** The rank of the Flush. */
    private int flushRank = -1;
    
    /** The rank of the Straight. */
    private int straightRank = -1;
    
    /** Whether we have a Straight with a wheeling Ace. */
    private boolean wheelingAce = false;
    
    /** The rank of the Three-of-a-Kind. */
    private int tripleRank = -1;
    
    /** The rank of the Four-of-a-Kind. */
    private int quadRank = -1;
    
    /** The weighed components of the hand value (highest first). */
    private int[] rankings = new int[NO_OF_RANKINGS];

    /**
     * Constructor.
     *
     * @param  hand  The hand to evaulate.
     */
    public HandEvaluator(Hand hand) {
        cards = hand.getCards();
        
        // Find patterns.
        calculateDistributions();
        findStraight();
        findFlush();
        findDuplicates();
        
        // Find special values.
        boolean isSpecialValue =
                (isStraightFlush() ||
                 isFourOfAKind()   ||
                 isFullHouse()     ||
                 isFlush()         ||
                 isStraight()      ||
                 isThreeOfAKind()  ||
                 isTwoPairs()      ||
                 isOnePair());
        if (!isSpecialValue) {
            calculateHighCard();
        }
        
        // Calculate value.
        for (int i = 0; i < NO_OF_RANKINGS; i++) {
            value += rankings[i] * RANKING_FACTORS[i];
        }
    }
    
    /**
     * Returns the hand value type.
     *
     * @return  the hand value type
     */
    public HandValueType getType() {
        return type;
    }
    
    /**
     * Returns the hand value as an integer.
     * 
     * This method should be used to compare hands.
     *
     * @return  the hand value
     */
    public int getValue() {
        return value;
    }
    
    /**
     * Calculates the rank and suit distributions.
     */
    private void calculateDistributions() {
        for (Card card : cards) {
            rankDist[card.getRank()]++;
            suitDist[card.getSuit()]++;
        }
    }
    
    /**
     * Looks for a flush, i.e. five cards with the same suit.
     */
    private void findFlush() {
        for (int i = 0; i < Card.NO_OF_SUITS; i++) {
            if (suitDist[i] >= 5) {
                flushSuit = i;
                for (Card card : cards) {
                    if (card.getSuit() == flushSuit) {
                        if (!wheelingAce || card.getRank() != Card.ACE) {
                            flushRank = card.getRank();
                            break;
                        }
                    }
                }
                break;
            }
        }
    }

    /**
     * Looks for a Straight, i.e. five cards with sequential ranks.
     * 
     * The Ace has the rank of One in case of a Five-high Straight (5-4-3-2-A).
     */
    private void findStraight() {
        boolean inStraight = false;
        int rank = -1;
        int count = 0;
        for (int i = Card.NO_OF_RANKS - 1; i >= 0 ; i--) {
            if (rankDist[i] == 0) {
                inStraight = false;
                count = 0;
            } else {
                if (!inStraight) {
                    // First card of the potential Straight.
                    inStraight = true;
                    rank = i;
                }
                count++;
                if (count >= 5) {
                    // Found a Straight!
                    straightRank = rank;
                    break;
                }
            }
        }
        // Special case for the 'Steel Wheel' (Five-high Straight with a 'wheeling Ace') .
        if ((count == 4) && (rank == Card.FIVE) && (rankDist[Card.ACE] > 0)) {
            wheelingAce = true;
            straightRank = rank;
        }
    }

    /**
     * Finds duplicates (pairs, triples and quads), i.e. two or more cards with
     * the same rank.
     */
    private void findDuplicates() {
        // Find quads, triples and pairs.
        for (int i = Card.NO_OF_RANKS - 1; i >= 0 ; i--) {
            if (rankDist[i] == 4) {
                quadRank = i;
            } else if (rankDist[i] == 3) {
                tripleRank = i;
            } else if (rankDist[i] == 2) {
                if (noOfPairs < MAX_NO_OF_PAIRS) {
                    pairs[noOfPairs++] = i;
                }
            }
        }
    }

    /**
     * Calculates the hand value based on the highest ranks.
     */
    private void calculateHighCard() {
        type = HandValueType.HIGH_CARD;
        rankings[0] = type.getValue();
        // Get the five highest ranks.
        int index = 1;
        for (Card card : cards) {
            rankings[index++] = card.getRank();
            if (index > 5) {
                break;
            }
        }
    }

    /**
     * Returns true if this hand contains One Pair.
     * 
     * The value of a One Pair is based on the rank of the pair.
     * The ranks of the remaining three cards are used as kickers.
     *
     * @return True if this hand contains One Pair.
     */
    private boolean isOnePair() {
        if (noOfPairs == 1) {
            type = HandValueType.ONE_PAIR;
            rankings[0] = type.getValue();
            // Get the rank of the pair.
            int pairRank = pairs[0];
            rankings[1] = pairRank;
            // Get the three kickers.
            int index = 2;
            for (Card card : cards) {
                int rank = card.getRank();
                if (rank != pairRank) {
                    rankings[index++] = rank;
                    if (index > 4) {
                        // We don't need any more kickers.
                        break;
                    }
                }
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns true if this hand contains Two Pairs.
     * 
     * The value of a Two Pairs is primarily based on the rank of the highest
     * pair, secondarily on the rank of the lowest pair and tertiarily on the
     * ranks of the remaining one kicker.
     *
     * @return True if this hand contains Two Pairs.
     */
    private boolean isTwoPairs() {
        if (noOfPairs == 2) {
            type = HandValueType.TWO_PAIRS;
            rankings[0] = type.getValue();
            // Get the value of the high and low pairs.
            int highRank = pairs[0];
            int lowRank  = pairs[1];
            rankings[1] = highRank;
            rankings[2] = lowRank;
            // Get the kicker card.
            for (Card card : cards) {
                int rank = card.getRank();
                if ((rank != highRank) && (rank != lowRank)) {
                    rankings[3] = rank;
                    break;
                }
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns true if this hand contains a Three of a Kind.
     * 
     * The value of a Three of a Kind is based on the rank of the triple.
     * The remaining two cards are used as kickers.
     *
     * @return True if this hand contains a Three of a Kind.
     */
    private boolean isThreeOfAKind() {
        if (tripleRank != -1) {
            type = HandValueType.THREE_OF_A_KIND;
            rankings[0] = type.getValue();
            rankings[1] = tripleRank;
            // Get the remaining two cards as kickers.
            int index = 2;
            for (Card card : cards) {
                int rank = card.getRank();
                if (rank != tripleRank) {
                    rankings[index++] = rank;
                    if (index > 3) {
                        // We don't need any more kickers.
                        break;
                    }
                }
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns true if this hand contains a Straight.
     * 
     * The value of a Straight is based on the rank of the highest card in the
     * straight.
     *
     * @return True if this hand contains a Straight.
     */
    private boolean isStraight() {
        if (straightRank != -1) {
            type = HandValueType.STRAIGHT;
            rankings[0] = type.getValue();
            rankings[1] = straightRank;
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns true if this hand contains a Flush.
     * 
     * The value of a Flush is based on the rank of the highest flushing card.
     * The remaining flushing cards are used as kickers.
     * 
     * @return True if this hand contains a Flush.
     */
    private boolean isFlush() {
        if (flushSuit != -1) {
            type = HandValueType.FLUSH;
            rankings[0] = type.getValue();
            int index = 1;
            for (Card card : cards) {
                if (card.getSuit() == flushSuit) {
                    int rank = card.getRank();
                    if (index == 1) {
                        flushRank = rank;
                    }
                    rankings[index++] = rank;
                    if (index > 5) {
                        // We don't need more kickers.
                        break;
                    }
                }
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns true if this hand contains a Full House.
     * 
     * The value of a Full House is primarily based on the rank of the triple
     * and secondarily on the rank of the pair. There are no kickers.
     *
     * @return True if this hand contains a Full House.
     */
    private boolean isFullHouse() {
        if ((tripleRank != -1) && (noOfPairs > 0)) {
            type = HandValueType.FULL_HOUSE;
            rankings[0] = type.getValue();
            rankings[1] = tripleRank;
            rankings[2] = pairs[0];
            return true;
        } else {
            return false;
        }
    }
    
    /**
     * Returns true if this hand contains a Four of a Kind.
     * 
     * The value of a Four of a Kind is primarily based on the rank of the
     * quad. There remaining card is used as kicker.
     *
     * @return True if this hand contains a Four of a Kind.
     */
    private boolean isFourOfAKind() {
        if (quadRank != -1) {
            type = HandValueType.FOUR_OF_A_KIND;
            rankings[0] = type.getValue();
            rankings[1] = quadRank;
            // Get the remaining card as kicker.
            int index = 2;
            for (Card card : cards) {
                int rank = card.getRank();
                if (rank != quadRank) {
                    rankings[index++] = rank;
                    break;
                }
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns true if this hand contains a Straight Flush.
     * 
     * An Ace-high Straight Flush is a Royal Flush, which has a fixed hand value
     * (no kickers).
     * 
     * The value of a (non-Royal Flush) Straight Flush is based on the rank of
     * the highest card of the Straight. There are no kickers.
     * 
     * @return True if this hand contains a Straight Flush.
     */
    private boolean isStraightFlush() {
        if (straightRank != -1 && flushRank == straightRank) {
            // Flush and Straight (possibly separate); check for Straight Flush.
            int straightRank2 = -1;
            int lastSuit = -1;
            int lastRank = -1;
            int inStraight = 1;
            int inFlush = 1;
            for (Card card : cards) {
                int rank = card.getRank();
                int suit = card.getSuit();
                if (lastRank != -1) {
                    int rankDiff = lastRank - rank;
                    if (rankDiff == 1) {
                        // Consecutive rank; possible straight!
                        inStraight++;
                        if (straightRank2 == -1) {
                            straightRank2 = lastRank;
                        }
                        if (suit == lastSuit) {
                            inFlush++;
                        } else {
                            inFlush = 1;
                        }
                        if (inStraight >= 5 && inFlush >= 5) {
                            // Straight!
                            break;
                        }
                    } else if (rankDiff == 0) {
                        // Duplicate rank; skip.
                    } else {
                        // Non-consecutive; reset.
                        straightRank2 = -1;
                        inStraight = 1;
                        inFlush = 1;
                    }
                }
                lastRank = rank;
                lastSuit = suit;
            }
            
            if (inStraight >= 5 && inFlush >= 5) {
                if (straightRank == Card.ACE) {
                    // Royal Flush.
                    type = HandValueType.ROYAL_FLUSH;
                    rankings[0] = type.getValue();
                    return true;
                } else {
                    // Straight Flush.
                    type = HandValueType.STRAIGHT_FLUSH;
                    rankings[0] = type.getValue();
                    rankings[1] = straightRank2;
                    return true;
                }
            } else if (wheelingAce && inStraight >= 4 && inFlush >= 4) {
                // Steel Wheel (Straight Flush with wheeling Ace).
                type = HandValueType.STRAIGHT_FLUSH;
                rankings[0] = type.getValue();
                rankings[1] = straightRank2;
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
    
}




Java Source Code List

com.intel.friend.invitation.FriendInvitationBase.java
com.intel.friend.invitation.FriendInvitationError.java
com.intel.friend.invitation.FriendInvitationMessage.java
com.intel.friend.invitation.FriendInvitationService.java
com.intel.friend.invitation.FriendReceiveInvitationActivity.java
com.intel.friend.invitation.FriendReceiveInvitationState.java
com.intel.friend.invitation.FriendSendInvitationActivity.java
com.intel.friend.invitation.FriendSendInvitationState.java
com.intel.friend.invitation.IDataStreamEventListener.java
com.intel.friend.invitation.IFriendInvitationEventListener.java
com.intel.friend.invitation.ReadEngine.java
com.intel.friend.invitation.SendInvitationDialogFragment.java
com.intel.friend.invitation.WriteEngine.java
com.intel.inproclib.user_details.UpdateUserDetailsActivity.java
com.intel.inproclib.user_details.UserSettingsFragment.java
com.intel.inproclib.utility.ImageViewNoLayoutRefresh.java
com.intel.inproclib.utility.InProcConstants.java
com.intel.inproclib.utility.InProc_ImageManager_Assets.java
com.intel.inproclib.utility.InProc_ListViewImageManager_FileSystem.java
com.intel.inproclib.utility.InProc_ListViewImageManager.java
com.intel.inproclib.utility.MaxLengthTextWatcher.java
com.intel.inproclib.utility.NoNewlineEditText.java
com.intel.startup.AvatarFragment.java
com.intel.startup.AvatarPickerFragment.java
com.intel.startup.CloudAuthorizationActivity.java
com.intel.startup.DeviceNameFragment.java
com.intel.startup.NewUnboxFragment.java
com.intel.startup.NewUnbox.java
com.intel.startup.StartupFragment.java
com.intel.startup.UserNameFragment.java
com.intel.ux.ImageUtilities.java
com.intel.ux.StcSessionListAdapter.java
lo.wolo.pokerccf.AbstractServiceUsingActivity.java
lo.wolo.pokerccf.CCFManager.java
lo.wolo.pokerccf.ChatAdapter.java
lo.wolo.pokerccf.Constants.java
lo.wolo.pokerccf.DiscoveryNodeActivity.java
lo.wolo.pokerccf.IServiceIOListener.java
lo.wolo.pokerccf.ISimpleDiscoveryListener.java
lo.wolo.pokerccf.MultiConnectRegisterApp.java
lo.wolo.pokerccf.NodeListAdapter.java
lo.wolo.pokerccf.NodeWrapper.java
lo.wolo.pokerccf.ReadEngine.java
lo.wolo.pokerccf.RemoteUser.java
lo.wolo.pokerccf.ServerController.java
lo.wolo.pokerccf.SessionAdapter.java
lo.wolo.pokerccf.WriteEngine.java
lo.wolo.pokerengine.Card.java
lo.wolo.pokerengine.ClientCCF.java
lo.wolo.pokerengine.Client.java
lo.wolo.pokerengine.Deck.java
lo.wolo.pokerengine.HandEvaluator.java
lo.wolo.pokerengine.HandValueType.java
lo.wolo.pokerengine.HandValue.java
lo.wolo.pokerengine.Hand.java
lo.wolo.pokerengine.Player.java
lo.wolo.pokerengine.Pot.java
lo.wolo.pokerengine.TableType.java
lo.wolo.pokerengine.Table.java
lo.wolo.pokerengine.actions.Action.java
lo.wolo.pokerengine.actions.AllInAction.java
lo.wolo.pokerengine.actions.BetAction.java
lo.wolo.pokerengine.actions.BigBlindAction.java
lo.wolo.pokerengine.actions.CallAction.java
lo.wolo.pokerengine.actions.CheckAction.java
lo.wolo.pokerengine.actions.ContinueAction.java
lo.wolo.pokerengine.actions.FoldAction.java
lo.wolo.pokerengine.actions.RaiseAction.java
lo.wolo.pokerengine.actions.SmallBlindAction.java
lo.wolo.pokerengine.bots.BasicBot.java
lo.wolo.pokerengine.bots.Bot.java
lo.wolo.pokerengine.bots.DummyBot.java
lo.wolo.pokerengine.util.PokerUtils.java