Back to project page pokerCCF.
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.
// 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; } } }