Here's my solution to Project Euler #54. Click the link for the full project description (abbreviated here):
The file, poker.txt, contains one-thousand random hands dealt to two players. Each line of the file contains ten cards (separated by a single space): the first five are Player 1's cards and the last five are Player 2's cards. You can assume that all hands are valid (no invalid characters or repeated cards), each player's hand is in no specific order, and in each hand there is a clear winner.
How many hands does Player 1 win?
9 classes but most of them are short enums.
Note: You will need to put the data file poker.txt on your classpath.
Card.java
package problem54;
public class Card implements Comparable<Card> {
private final Value value;
private final Suit suit;
public Card(Value value, Suit suit) {
this.value = value;
this.suit = suit;
}
public Card(String s) {
this(Value.of(s.charAt(0)), Suit.of(s.charAt(1)));
}
public Value getValue() {
return value;
}
public Suit getSuit() {
return suit;
}
@Override
public int compareTo(Card o) {
return value.compareTo(o.value);
}
@Override
public String toString() {
return String.format("%s_%s", value.getChar(), suit.getChar());
}
}
Hand.java (the Heavy Lifting is here)
package problem54;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class Hand implements Comparable<Hand> {
private final List<Card> cards;
private final ValueRanking ranking;
public Hand(List<Card> cards) {
List<Card> temp = new ArrayList<>(cards);
temp.add(new Card(Value.NULL, Suit.NULL));
Collections.sort(temp);
this.cards = Collections.unmodifiableList(temp);
ValueRanking straightRanking = straightRanking();
if(straightRanking != null) {
ranking = straightRanking;
} else {
ranking = pairRanking();
}
}
public List<Card> getCards() {
return cards;
}
private ValueRanking straightRanking() {
if(isStraight() && isFlush()) {
return new ValueRanking(Ranking.STRAIGHT_FLUSH, cards.get(4).getValue(), (Value) null, Collections.<Value>emptyList());
} else if(isFlush()) {
List<Value> kickers = new LinkedList<Value>();
kickers.add(cards.get(2).getValue());
kickers.add(cards.get(1).getValue());
kickers.add(cards.get(0).getValue());
return new ValueRanking(Ranking.FLUSH, cards.get(4).getValue(), cards.get(3).getValue(), kickers);
} else if(isStraight()) {
return new ValueRanking(Ranking.STRAIGHT_FLUSH, cards.get(4).getValue(), (Value) null, Collections.<Value>emptyList());
} else {
return null;
}
}
public ValueRanking getRanking() {
return ranking;
}
private ValueRanking pairRanking() {
Value v = null;
int counter = 0;
int pair = 0, trips = 0, quads = 0;
Value primary = null;
Value secondary = null;
List<Value> kicker = new LinkedList<>();
for(Card c : cards) {
Value current = c.getValue();
if(v != current) {
switch(counter) {
case 2:
pair++;
if(primary == null) {
primary = v;
} else if(trips > 0 || primary.compareTo(current) > 0) {
secondary = current;
} else {
secondary = primary;
primary = v;
}
break;
case 3:
trips++;
secondary = primary;
primary = v;
break;
case 4:
quads++;
primary = v;
break;
default:
if(v != null) kicker.add(v);
}
v = current;
counter = 1;
} else {
counter++;
}
}
if(quads > 0) {
return new ValueRanking(Ranking.QUADS, primary, secondary, kicker);
} else if(trips > 0) {
if(pair > 0) return new ValueRanking(Ranking.FULL_HOUSE, primary, secondary, kicker);
else return new ValueRanking(Ranking.TRIPS, primary, secondary, kicker);
} else if(pair == 2) {
return new ValueRanking(Ranking.TWO_PAIR, primary, secondary, kicker);
} else if(pair == 1) {
return new ValueRanking(Ranking.PAIR, primary, secondary, kicker);
} else {
return new ValueRanking(Ranking.HIGH_CARD, primary, secondary, kicker);
}
}
private boolean isStraight() {
for(int i = 0; i < 4; i++) {
if(cards.get(i).getValue().ordinal() + 1 != cards.get(i+1).getValue().ordinal()) return false;
}
return true;
}
private boolean isFlush() {
for(int i = 0; i < 4; i++) {
if(cards.get(i).getSuit() != cards.get(i+1).getSuit()) return false;
}
return true;
}
@Override
public int compareTo(Hand o) {
return ranking.compareTo(o.ranking);
}
}
Parser.java
package problem54;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;
public class Parser implements Closeable {
private final BufferedReader reader;
public Parser(InputStream in) {
this.reader = new BufferedReader(new InputStreamReader(in));
}
public Round getNextRound() throws IOException {
String nextLine = reader.readLine();
String[] cardStrings = nextLine.split(" ");
List<Card> cardList = new LinkedList<>();
for(String cardString : cardStrings) {
cardList.add(new Card(cardString));
}
return new Round(cardList);
}
public void close() throws IOException {
reader.close();
}
public boolean hasNext() throws IOException {
return reader.ready();
}
}
Problem.java (Main method)
package problem54;
import java.io.IOException;
import java.io.InputStream;
public class Problem {
public static void main(String... args) throws IOException {
InputStream is = Problem.class.getResourceAsStream("/p054_poker.txt");
Parser p = new Parser(is);
int counter = 0;
while(p.hasNext()) {
Round r = p.getNextRound();
if(r.playerOneWins()) counter++;
}
System.out.println(counter);
p.close();
}
}
Ranking.java
public enum Ranking {
HIGH_CARD,
PAIR,
TWO_PAIR,
TRIPS,
STRAIGHT,
FLUSH,
FULL_HOUSE,
QUADS,
STRAIGHT_FLUSH;
}
Round.java
package problem54;
import java.util.List;
public class Round {
public final Hand playerOne;
public final Hand playerTwo;
public Round(List<Card> cardList) {
List<Card> firstCards = cardList.subList(0, 5);
this.playerOne = new Hand(firstCards);
List<Card> secondCards = cardList.subList(5, 10);
this.playerTwo = new Hand(secondCards);
}
public boolean playerOneWins() {
return playerOne.compareTo(playerTwo) > 0;
}
}
Suit.java
package problem54;
import java.util.HashMap;
import java.util.Map;
public enum Suit {
HEART ('H'),
DIAMOND ('D'),
SPADE ('S'),
CLUB ('C'),
NULL ('x');
private final char suitChar;
private static final Map<Character, Suit> valueMap = new HashMap<>();
static {
for(Suit c : Suit.values()) {
valueMap.put(c.getChar(), c);
}
}
private Suit(char cardString) {
this.suitChar = cardString;
}
public char getChar() {
return suitChar;
}
public static Suit of(char value) {
Suit s = valueMap.get(value);
if(s == null) return NULL;
return s;
}
}
Value.java
package problem54;
import java.util.HashMap;
import java.util.Map;
public enum Value {
TWO ('2'),
THREE ('3'),
FOUR ('4'),
FIVE ('5'),
SIX ('6'),
SEVEN ('7'),
EIGHT ('8'),
NINE ('9'),
TEN ('T'),
JACK ('J'),
QUEEN ('Q'),
KING ('K'),
ACE ('A'),
NULL ('x');
private final char valueChar;
private static final Map<Character, Value> valueMap = new HashMap<>();
static {
for(Value c : Value.values()) {
valueMap.put(c.getChar(), c);
}
}
private Value(char cardString) {
this.valueChar = cardString;
}
public char getChar() {
return valueChar;
}
public static Value of(char value) {
Value v = valueMap.get(value);
if(v == null) return NULL;
return v;
}
}
ValueRanking.java
package problem54;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ValueRanking implements Comparable<ValueRanking> {
private final Ranking rank;
private final Value primary;
private final Value secondary;
private final List<Value> kicker;
public ValueRanking(Ranking rank, Value primary, Value secondary, List<Value> kicker) {
this.rank = rank;
this.primary = primary == null ? Value.NULL : primary;
this.secondary = secondary == null ? Value.NULL : secondary;
List<Value> kickerTemp = new ArrayList<>(kicker);
try {
Collections.sort(kickerTemp);
} catch (NullPointerException e) {
System.out.println(kicker);
throw e;
}
Collections.reverse(kickerTemp);
this.kicker = Collections.unmodifiableList(kickerTemp);
}
public Ranking getRank() {
return rank;
}
public Value getPrimary() {
return primary;
}
public Value getSecondary() {
return secondary;
}
public List<Value> getKicker() {
return kicker;
}
@Override
public int compareTo(ValueRanking o) {
if(rank.compareTo(o.rank) != 0) return rank.compareTo(o.rank);
else if(primary.compareTo(o.primary) != 0) return primary.compareTo(o.primary);
else if(secondary.compareTo(o.secondary) != 0) return secondary.compareTo(o.secondary);
else {
for(int i = 0; i < kicker.size(); i++) {
if(kicker.get(i).compareTo(o.kicker.get(i)) != 0) return kicker.get(i).compareTo(o.kicker.get(i));
}
return 0;
}
}
@Override
public String toString() {
return String.format(
"[%s,%s,%s,%s]",
rank, primary, secondary, kicker);
}
}
Value.QUEEN
is primary,Value.TWO
is secondary, andValue.Seven
is a kicker. Similarly, for 2QQ22,Value.TWO
would be primary,Value.QUEEN
would be secondary. This is how I compare Q227Q to Q553Q, Q = Q, then 5 > 2. \$\endgroup\$ValueRanking
class \$\endgroup\$