Skip to content

Instantly share code, notes, and snippets.

@snarkbait
Last active July 17, 2017 14:24
Show Gist options
  • Save snarkbait/f6852088447d5d4215ce to your computer and use it in GitHub Desktop.
Save snarkbait/f6852088447d5d4215ce to your computer and use it in GitHub Desktop.
Poker Hand Scoring example for /r/javaexamples
package enumexample;
import java.util.*;
/**
* Cards class for playing cards. Uses simple integer-based system
* where the card face value is n mod 13 and the suit is n mod 4
* @author /u/Philboyd_Studge on 3/26/2016.
*/
public class Cards {
private static final int DECK_SIZE = 52;
private static final int FACES = 13;
private static final String[] FACE_NAMES = { "Ace", "Two", "Three", "Four",
"Five", "Six", "Seven", "Eight",
"Nine", "Ten", "Jack", "Queen", "King"};
private static final String[] SUIT_NAMES = { "Diamonds", "Clubs", "Hearts", "Spades"};
Random rand = new Random();
private int[] deck; // deck itself
private int pointer; // current position in deck
/**
* Constructor
*/
public Cards() {
deck = new int[DECK_SIZE];
// initialize deck to values 0 - 51
for (int i = 0; i < DECK_SIZE; i++) {
deck[i] = i;
}
}
/**
* Get card integer value at position n
* @param n position in deck
* @return integer card value
*/
public int getCard(int n) {
return deck[n];
}
/**
* Fisher-yates shuffle
*/
public void shuffle() {
pointer = 0;
for (int i = DECK_SIZE - 1; i > 0; i--) {
int j = rand.nextInt(i);
if (j != i) {
swap(j, i);
}
}
}
private void swap(int first, int second) {
int temp = deck[first];
deck[first] = deck[second];
deck[second] = temp;
}
/**
* Get face value 0 - 12 for card
* note: off by one compared to actual card face
* @param card integer value of card
* @return face value
*/
public static int getFaceValue(int card) {
return card % FACES;
}
/**
* Get suit 0 - 3 for card
* '& 3' a bitwise way of saying '% 4'
* @param card integer value of card
* @return suit
*/
public static int getSuit(int card) {
return card & 3;
}
/**
* Get string representation of card
* @param card integer value of card
* @return string of card name
*/
public static String getCardName(int card) {
return FACE_NAMES[getFaceValue(card)] + " of " + SUIT_NAMES[getSuit(card)];
}
/**
* Deal n cards from the deck
* re-shuffles if not enough cards left for a full hand
* @param n number of cards to deal
* @return Linked list of integers representing the hand
*/
public List<Integer> deal(int n) {
if (n < 1) return null;
// don't reshuffle in the middle of a hand
if (DECK_SIZE - pointer < n) shuffle();
List<Integer> hand = new LinkedList<>();
for (int i = 0; i < n; i++) {
hand.add(dealCard());
}
return hand;
}
/**
* deal single card and increment pointer
* @return
*/
private int dealCard() {
return deck[pointer++];
}
}
package enumexample;
/**
* enum for ranking a poker hand
* @author /u/Philboyd_Studge on 3/26/2016.
*/
public enum PokerHand {
HIGH_CARD("High Card"),
ONE_PAIR("One Pair"),
TWO_PAIR("Two Pairs"),
THREE_OF_A_KIND("Three of a Kind"),
STRAIGHT("Straight"),
ROYAL_STRAIGHT("Royal Straight"),
FLUSH("Flush"),
FULL_HOUSE("Full House"),
FOUR_OF_A_KIND("Four of a Kind"),
STRAIGHT_FLUSH("Straight Flush"),
ROYAL_FLUSH("Royal Flush");
private String name;
PokerHand(String name) {
this.name = name;
}
public String getName() { return name; }
}
package enumexample;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author /u/Philboyd_Studge on 3/27/2016.
*/
public class PokerHandTester {
public static void main(String[] args) {
Cards deck = new Cards();
deck.shuffle();
Map<PokerHand, Integer> frequencyMap = new HashMap<>();
int total = 1000000;
for (int i = 0; i < total; i++) {
List<Integer> hand = deck.deal(5);
PokerHand temp = new ScoreHand(hand).getRank();
frequencyMap.put(temp, frequencyMap.getOrDefault(temp, 0) + 1);
}
for (PokerHand each : PokerHand.values()) {
int count = frequencyMap.getOrDefault(each, 0);
System.out.println(each.getName() + " : " + count);
System.out.printf("%.4f%%%n", (( count / (double) total) * 100));
}
}
}
package enumexample;
import java.util.Arrays;
import java.util.List;
/**
* Score a hand of poker
* for 5 cards, no wild cards.
* @author /u/Philboyd_Studge on 3/26/2016.
*/
public class ScoreHand {
// constants for evaluating pairs
public static final int ONE_PAIR = 7;
public static final int TWO_PAIR = 9;
public static final int THREE_OF_A_KIND = 11;
public static final int FULL_HOUSE = 13;
public static final int FOUR_OF_A_KIND = 17;
List<Integer> hand; // the hand
int[] faceFrequency = new int[13]; // frequency table for face values
int[] suitFrequency = new int[4]; // frequency table for suits
boolean hasAce; // hand contains at least one Ace
boolean isRoyal; // hand contains a straight that is 'royal' i.e. A-10-J-Q-K
PokerHand rank; // calculated rank of the hand
int highCard; // highest card in hand for tiebreakers
/**
* Constructor
* @param hand list of integers in range 0 - 51, must have only 5 elements
*/
public ScoreHand(List<Integer> hand) {
if (hand.size() != 5) {
throw new IllegalArgumentException("Hand incorrect size");
}
this.hand = hand;
getFrequencies();
rank = PokerHand.HIGH_CARD;
findHighCard();
rankHand();
}
public PokerHand getRank() {
return rank;
}
public int getHighCard() {
return highCard;
}
private void rankHand() {
// find all possibilities of straights first
if (isStraight()) {
if (isFlush()) {
rank = isRoyal ? PokerHand.ROYAL_FLUSH : PokerHand.STRAIGHT_FLUSH;
} else {
rank = isRoyal ? PokerHand.ROYAL_STRAIGHT : PokerHand.STRAIGHT;
}
} else {
if (isFlush()) rank = PokerHand.FLUSH;
}
// now find pairs/other multiples
int pairs = getPairSum();
switch (pairs) {
case ONE_PAIR:
if (rank.compareTo(PokerHand.ONE_PAIR) < 0) rank = PokerHand.ONE_PAIR;
break;
case TWO_PAIR:
if (rank.compareTo(PokerHand.TWO_PAIR) < 0) rank = PokerHand.TWO_PAIR;
break;
case THREE_OF_A_KIND:
if (rank.compareTo(PokerHand.THREE_OF_A_KIND) < 0) rank = PokerHand.THREE_OF_A_KIND;
break;
case FULL_HOUSE:
if (rank.compareTo(PokerHand.FULL_HOUSE) < 0) rank = PokerHand.FULL_HOUSE;
break;
case FOUR_OF_A_KIND:
if (rank.compareTo(PokerHand.FOUR_OF_A_KIND) < 0) rank = PokerHand.FOUR_OF_A_KIND;
break;
default:
}
}
/**
* fill frequency tables with hand data
*/
private void getFrequencies() {
for (int each : hand) {
// kill three birds with one for-loop
if (Cards.getFaceValue(each) == 0) hasAce = true;
faceFrequency[Cards.getFaceValue(each)]++;
suitFrequency[Cards.getSuit(each)]++;
}
}
/**
* use frequency of suits table to find a flush
* @return true if all cards the same suit
*/
private boolean isFlush() {
for (int each : suitFrequency) {
if (each == 5) return true;
}
return false;
}
/**
* get a sorted int array for use in finding straights
* @return sorted array of card face values
*/
private int[] getSortedArray() {
int[] sorted = new int[5];
int i = 0;
for (int each : hand) {
sorted[i++] = Cards.getFaceValue(each);
}
Arrays.sort(sorted);
return sorted;
}
/**
* check for straights
* @return true if a 5-card run exists
*/
private boolean isStraight() {
int[] sorted = getSortedArray();
// ugly but why not
if (hasAce && sorted[1] > 1) {
if (sorted[1]==9 && sorted[2]==10 &&
sorted[3]==11 && sorted[4]== 12) {
isRoyal = true;
return true;
}
} else {
for (int i = 1; i < 5; i++) {
if (sorted[i] - sorted[i - 1] > 1) return false;
}
return true;
}
return false;
}
/**
* use the face value frequency table to get a
* unique number for the different possibilities
* @return pair sum number that will correspond to the constants above
*/
private int getPairSum() {
int sum = 0;
for (int each : faceFrequency) {
sum += each * each;
}
return sum;
}
/**
* find and set the high card for tiebreaker purposes
*/
private void findHighCard() {
if (hasAce) {
for (int each : hand) {
if (Cards.getFaceValue(each) == 0) highCard = each;
}
} else {
int max = -1;
for (int each : hand) {
if (Cards.getFaceValue(each) > max) {
max = Cards.getFaceValue(each);
highCard = each;
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment