# artemkorsakov's solution

## to Poker in the Java Track

Published at Feb 12 2019 · 0 comments
Instructions
Test suite
Solution

Pick the best hand(s) from a list of poker hands.

See wikipedia for an overview of poker hands.

# Running the tests

You can run all the tests for an exercise by entering

``````\$ gradle test
``````

## Source

Inspired by the training course from Udacity. https://www.udacity.com/course/viewer#!/c-cs212/

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

### PokerTest.java

``````import org.junit.Ignore;
import org.junit.Test;

import java.util.Arrays;
import java.util.Collections;

import static org.junit.Assert.assertEquals;

public class PokerTest {
@Test
public void oneHand() {
String hand = "4S 5S 7H 8D JC";
assertEquals(Collections.singletonList(hand), new Poker(Collections.singletonList(hand)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void highestCardWins() {
String highest8 = "4D 5S 6S 8D 3C";
String highest10 = "2S 4C 7S 9H 10H";
String highestJ = "3S 4S 5D 6H JH";
assertEquals(Collections.singletonList(highestJ),
new Poker(Arrays.asList(highest8, highest10, highestJ)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void tieHasMultipleWinners() {
String highest8 = "4D 5S 6S 8D 3C";
String highest10 = "2S 4C 7S 9H 10H";
String highestJh = "3S 4S 5D 6H JH";
String highestJd = "3H 4H 5C 6C JD";
assertEquals(Arrays.asList(highestJh, highestJd),
new Poker(Arrays.asList(highest8, highest10, highestJh, highestJd)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void sameHighCards() {
String nextHighest3 = "3S 5H 6S 8D 7H";
String nextHighest2 = "2S 5D 6D 8C 7S";
assertEquals(Collections.singletonList(nextHighest3),
new Poker(Arrays.asList(nextHighest3, nextHighest2)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void nothingVsOnePair() {
String nothing = "4S 5H 6C 8D KH";
String pairOf4 = "2S 4H 6S 4D JH";
assertEquals(Collections.singletonList(pairOf4),
new Poker(Arrays.asList(nothing, pairOf4)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void twoPairs() {
String pairOf2 = "4S 2H 6S 2D JH";
String pairOf4 = "2S 4H 6C 4D JD";
assertEquals(Collections.singletonList(pairOf4),
new Poker(Arrays.asList(pairOf2, pairOf4)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void onePairVsDoublePair() {
String pairOf8 = "2S 8H 6S 8D JH";
String doublePair = "4S 5H 4C 8C 5C";
assertEquals(Collections.singletonList(doublePair),
new Poker(Arrays.asList(pairOf8, doublePair)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void twoDoublePairs() {
String doublePair2And8 = "2S 8H 2D 8D 3H";
String doublePair4And5 = "4S 5H 4C 8S 5D";
assertEquals(Collections.singletonList(doublePair2And8),
new Poker(Arrays.asList(doublePair2And8, doublePair4And5)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void sameHighestPair() {
String doublePair2AndQ = "2S QS 2C QD JH";
String doublePairJAndQ = "JD QH JS 8D QC";
assertEquals(Collections.singletonList(doublePairJAndQ),
new Poker(Arrays.asList(doublePairJAndQ, doublePair2AndQ)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void identicallyRankedPairs() {
String kicker8 = "JD QH JS 8D QC";
String kicker2 = "JS QS JC 2D QD";
assertEquals(Collections.singletonList(kicker8), new Poker(Arrays.asList(kicker8, kicker2)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void doublePairVsThree() {
String doublePair2And8 = "2S 8H 2H 8D JH";
String threeOf4 = "4S 5H 4C 8S 4H";
assertEquals(Collections.singletonList(threeOf4),
new Poker(Arrays.asList(doublePair2And8, threeOf4)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void twoThrees() {
String threeOf2 = "2S 2H 2C 8D JH";
String threeOf1 = "4S AH AS 8C AD";
assertEquals(Collections.singletonList(threeOf1), new Poker(Arrays.asList(threeOf2, threeOf1)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void sameThreesMultipleDecks() {
String remainingCard7 = "4S AH AS 7C AD";
String remainingCard8 = "4S AH AS 8C AD";
assertEquals(Collections.singletonList(remainingCard8),
new Poker(Arrays.asList(remainingCard7, remainingCard8)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void threeVsStraight() {
String threeOf4 = "4S 5H 4C 8D 4H";
String straight = "3S 4D 2S 6D 5C";
assertEquals(Collections.singletonList(straight), new Poker(Arrays.asList(threeOf4, straight)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void acesCanEndAStraight() {
String hand = "4S 5H 4C 8D 4H";
String straightEndsA = "10D JH QS KD AC";
assertEquals(Collections.singletonList(straightEndsA),
new Poker(Arrays.asList(hand, straightEndsA)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void acesCanStartAStraight() {
String hand = "4S 5H 4C 8D 4H";
String straightStartA = "4D AH 3S 2D 5C";
assertEquals(Collections.singletonList(straightStartA),
new Poker(Arrays.asList(hand, straightStartA)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void twoStraights() {
String straightTo8 = "4S 6C 7S 8D 5H";
String straightTo9 = "5S 7H 8S 9D 6H";
assertEquals(Collections.singletonList(straightTo9),
new Poker(Arrays.asList(straightTo8, straightTo9)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void theLowestStraightStartsWithAce() {
String straight = "2H 3C 4D 5D 6H";
String straightStartA = "4S AH 3S 2D 5H";
assertEquals(Collections.singletonList(straight),
new Poker(Arrays.asList(straight, straightStartA)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void straightVsFlush() {
String straightTo8 = "4C 6H 7D 8D 5H";
String flushTo7 = "2S 4S 5S 6S 7S";
assertEquals(Collections.singletonList(flushTo7),
new Poker(Arrays.asList(straightTo8, flushTo7)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void twoFlushes() {
String flushTo8 = "4H 7H 8H 9H 6H";
String flushTo7 = "2S 4S 5S 6S 7S";
assertEquals(Collections.singletonList(flushTo8), new Poker(Arrays.asList(flushTo8, flushTo7)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void flushVsFull() {
String flushTo8 = "3H 6H 7H 8H 5H";
String full = "4S 5H 4C 5D 4H";
assertEquals(Collections.singletonList(full), new Poker(Arrays.asList(full, flushTo8)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void twoFulls() {
String fullOf4By9 = "4H 4S 4D 9S 9D";
String fullOf5By8 = "5H 5S 5D 8S 8D";
assertEquals(Collections.singletonList(fullOf5By8),
new Poker(Arrays.asList(fullOf4By9, fullOf5By8)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void twoFullssameThripletMultipleDecks() {
String fullOf5By9 = "5H 5S 5D 9S 9D";
String fullOf5By8 = "5H 5S 5D 8S 8D";
assertEquals(Collections.singletonList(fullOf5By9),
new Poker(Arrays.asList(fullOf5By9, fullOf5By8)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void fullVsSquare() {
String full = "4S 5H 4D 5D 4H";
String squareOf3 = "3S 3H 2S 3D 3C";
assertEquals(Collections.singletonList(squareOf3), new Poker(Arrays.asList(full, squareOf3)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void twoSquares() {
String squareOf2 = "2S 2H 2C 8D 2D";
String squareOf5 = "4S 5H 5S 5D 5C";
assertEquals(Collections.singletonList(squareOf5),
new Poker(Arrays.asList(squareOf2, squareOf5)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void sameSquaresMultipleDecks() {
String kicker2 = "3S 3H 2S 3D 3C";
String kicker4 = "3S 3H 4S 3D 3C";
assertEquals(Collections.singletonList(kicker4), new Poker(Arrays.asList(kicker2, kicker4)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void squareVsStraightFlush() {
String squareOf5 = "4S 5H 5S 5D 5C";
String straightFlushTo9 = "7S 8S 9S 6S 10S";
assertEquals(Collections.singletonList(straightFlushTo9),
new Poker(Arrays.asList(squareOf5, straightFlushTo9)).getBestHands());
}

@Ignore("Remove to run test")
@Test
public void twoStraightFlushes() {
String straightFlushTo8 = "4H 6H 7H 8H 5H";
String straightFlushTo9 = "5S 7S 8S 9S 6S";
assertEquals(Collections.singletonList(straightFlushTo9),
new Poker(Arrays.asList(straightFlushTo8, straightFlushTo9)).getBestHands());
}
}``````

### src/main/java/Poker.java

``````import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

class Poker {
private List<Hand> hands;

Poker(List<String> hands) {
this.hands = hands.stream().map(Hand::new).sorted(Comparator.comparing(o -> ((Hand) o))).collect(Collectors.toList());
}

List<String> getBestHands() {
return hands.stream().filter(h -> h.compareTo(hands.get(0)) == 0).map(Hand::getHand).collect(Collectors.toList());
}
}``````

### src/main/java/Hand.java

``````import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

class Hand implements Comparable<Hand>, Comparator<Hand> {
private String hand;
private List<Card> cards;
private PokerHands pokerHands = PokerHands.HIGH_CARD;
private List<PokerRank> ranks = Arrays.asList(PokerRank.TWO, PokerRank.TWO, PokerRank.TWO, PokerRank.TWO, PokerRank.TWO);

Hand(String hand) {
this.hand = hand;
String[] split = hand.split(" ");
if (split.length != 5) {
throw new IllegalArgumentException("Invalid hand");
}
cards = Arrays.stream(split).map(Card::new).collect(Collectors.toList());
calculate();
}

private void calculate() {
if (isFiveOfAKind()) {
calculateFiveOfAKind();
} else if (isStraightFlush()) {
calculateStraightFlush();
} else if (isFourOfAKind()) {
calculateFourOfAKind();
} else if (isFullHouse()) {
calculateFullHouse();
} else if (isFlush()) {
calculateFlush();
} else if (isStraight()) {
calculateStraight();
} else if (isThreeOfAKind()) {
calculateThreeOfAKind();
} else if (isTwoPair()) {
calculateTwoPair();
} else if (isOnePair()) {
calculateOnePair();
} else {
calculateHighCard();
}
}

String getHand() {
return hand;
}

@Override
public int compare(Hand o1, Hand o2) {
return o1.compareTo(o2);
}

@Override
public int compareTo(Hand o) {
int diff = pokerHands.compareTo(o.pokerHands);
return diff != 0 ? diff : IntStream.range(0, ranks.size()).map(i -> ranks.get(i).compareTo(o.ranks.get(i)))
.filter(r -> r != 0).findFirst().orElse(0);
}

private List<PokerRank> getSortedPokerRanks() {
if (sortedPokerRanks == null) {
sortedPokerRanks = cards.stream().map(Card::getPokerRank).sorted((o1, o2) -> o2.getRank() - o1.getRank()).collect(Collectors.toList());
}
return sortedPokerRanks;
}

private List<PokerRank> getSortedDistinctPokerRanks() {
if (sortedDistinctPokerRanks == null) {
sortedDistinctPokerRanks = getSortedPokerRanks().stream().distinct().collect(Collectors.toList());
}
return sortedDistinctPokerRanks;
}

private List<PokerRank> sortedDistinctPokerRanks = null;
private List<PokerRank> sortedPokerRanks = null;

private boolean isFiveOfAKind() {
return getSortedDistinctPokerRanks().size() == 1;
}

private void calculateFiveOfAKind() {
pokerHands = PokerHands.FIVE_OF_A_KIND;
ranks.set(0, cards.get(0).getPokerRank());
}

private boolean isStraightFlush() {
return isStraight() && isFlush();
}

private void calculateStraightFlush() {
pokerHands = PokerHands.STRAIGHT_FLUSH;
List<PokerRank> sortedRanks = getSortedPokerRanks();
PokerRank pokerRank = (sortedRanks.get(0) == PokerRank.A && sortedRanks.get(1) == PokerRank.FIVE) ? PokerRank.FIVE : sortedRanks.get(0);
ranks.set(0, pokerRank);
}

private boolean isStraight() {
List<PokerRank> ranks = getSortedPokerRanks();
if (ranks.containsAll(Arrays.asList(PokerRank.FIVE, PokerRank.FOUR, PokerRank.THREE, PokerRank.TWO, PokerRank.A))) {
return true;
}
for (int i = 0; i < ranks.size() - 1; i++) {
if (ranks.get(i).getRank() - ranks.get(i + 1).getRank() != 1) {
return false;
}
}
return true;
}

private void calculateStraight() {
pokerHands = PokerHands.STRAIGHT;
List<PokerRank> sortedRanks = getSortedPokerRanks();
PokerRank pokerRank = (sortedRanks.get(0) == PokerRank.A && sortedRanks.get(1) == PokerRank.FIVE) ? PokerRank.FIVE : sortedRanks.get(0);
ranks.set(0, pokerRank);
}

private boolean isFlush() {
return cards.stream().map(Card::getPokerSuit).distinct().count() == 1;
}

private void calculateFlush() {
ranks = getSortedPokerRanks();
pokerHands = PokerHands.FLUSH;
}

private boolean isFourOfAKind() {
long countOfRank = getSortedDistinctPokerRanks().size();
long countOfFirstCard = cards.stream().filter(c -> c.getPokerRank() == cards.get(0).getPokerRank()).count();
return countOfRank == 2 && (countOfFirstCard == 1 || countOfFirstCard == 4);
}

private void calculateFourOfAKind() {
List<PokerRank> sortedRanks = getSortedPokerRanks();
pokerHands = PokerHands.FOUR_OF_A_KIND;
boolean isFirstEqualSecond = sortedRanks.get(0).getRank() == sortedRanks.get(1).getRank();
ranks.set(0, isFirstEqualSecond ? sortedRanks.get(0) : sortedRanks.get(1));
ranks.set(1, isFirstEqualSecond ? sortedRanks.get(4) : sortedRanks.get(0));
}

private boolean isFullHouse() {
long countOfRank = getSortedDistinctPokerRanks().size();
long countOfFirstCard = cards.stream().filter(c -> c.getPokerRank() == cards.get(0).getPokerRank()).count();
return countOfRank == 2 && (countOfFirstCard == 2 || countOfFirstCard == 3);
}

private void calculateFullHouse() {
List<PokerRank> sortedRanks = getSortedPokerRanks();
pokerHands = PokerHands.FULL_HOUSE;
ranks.set(0, sortedRanks.get(2));
boolean isSecondEqualThird = sortedRanks.get(1).getRank() == sortedRanks.get(2).getRank();
ranks.set(1, isSecondEqualThird ? sortedRanks.get(3) : sortedRanks.get(1));
}

private boolean isThreeOfAKind() {
long countOfRank = getSortedDistinctPokerRanks().size();
if (countOfRank != 3) {
return false;
}
List<PokerRank> ranks = getSortedPokerRanks();
long countOfFirstCard = cards.stream().filter(c -> c.getPokerRank() == ranks.get(0)).count();
long countOfLastCard = cards.stream().filter(c -> c.getPokerRank() == ranks.get(ranks.size() - 1)).count();
return (countOfFirstCard == 1 && countOfLastCard == 1) ||
(countOfFirstCard == 3 && countOfLastCard == 1) ||
(countOfFirstCard == 1 && countOfLastCard == 3);
}

private void calculateThreeOfAKind() {
List<PokerRank> sortedRanks = getSortedPokerRanks();
pokerHands = PokerHands.THREE_OF_A_KIND;
int index = IntStream.range(0, sortedRanks.size() - 2)
.filter(i -> sortedRanks.get(i).getRank() == sortedRanks.get(i + 1).getRank()
&& sortedRanks.get(i + 1).getRank() == sortedRanks.get(i + 2).getRank())
.findFirst().orElse(0);
ranks.set(0, sortedRanks.get(2));
ranks.set(1, sortedRanks.get(index == 0 ? 3 : 0));
ranks.set(2, sortedRanks.get(index == 2 ? 1 : 4));
}

private boolean isTwoPair() {
long countOfRank = getSortedDistinctPokerRanks().size();
if (countOfRank != 3) {
return false;
}
List<PokerRank> ranks = getSortedPokerRanks();
long countOfFirstCard = cards.stream().filter(c -> c.getPokerRank() == ranks.get(0)).count();
long countOfLastCard = cards.stream().filter(c -> c.getPokerRank() == ranks.get(ranks.size() - 1)).count();
return (countOfFirstCard == 2 && countOfLastCard == 2) ||
(countOfFirstCard == 2 && countOfLastCard == 1) ||
(countOfFirstCard == 1 && countOfLastCard == 2);
}

private void calculateTwoPair() {
List<PokerRank> sortedRanks = getSortedPokerRanks();
pokerHands = PokerHands.TWO_PAIR;
boolean isFirstEqualSecond = sortedRanks.get(0).getRank() == sortedRanks.get(1).getRank();
boolean isThirdEqualFourth = sortedRanks.get(2).getRank() == sortedRanks.get(3).getRank();
ranks.set(0, sortedRanks.get(1));
ranks.set(1, sortedRanks.get(3));
int thirdIndex = isFirstEqualSecond && isThirdEqualFourth ? 4 : isFirstEqualSecond ? 2 : 0;
ranks.set(2, sortedRanks.get(thirdIndex));
}

private boolean isOnePair() {
return getSortedDistinctPokerRanks().size() == 4;
}

private void calculateOnePair() {
pokerHands = PokerHands.ONE_PAIR;
List<PokerRank> sortedRanks = getSortedPokerRanks();
int index = IntStream.range(0, sortedRanks.size() - 1)
.filter(i -> sortedRanks.get(i).getRank() == sortedRanks.get(i + 1).getRank())
.findFirst().orElse(0);
ranks.set(0, sortedRanks.get(index));
ranks.set(1, sortedRanks.get(index == 0 ? 2 : 0));
ranks.set(2, sortedRanks.get(index < 2 ? 3 : 1));
ranks.set(3, sortedRanks.get(index != 3 ? 4 : 2));
}

private void calculateHighCard() {
ranks = getSortedPokerRanks();
pokerHands = PokerHands.HIGH_CARD;
}
}``````

### src/main/java/PokerHands.java

``````enum PokerHands {
FIVE_OF_A_KIND(9),    // All cards are equal
STRAIGHT_FLUSH(8),    // A straight flush is a hand that contains five cards of sequential rank, all of the same suit
FOUR_OF_A_KIND(7),    // Four of a kind is a hand that contains four cards of one rank and one card of another rank
FULL_HOUSE(6),        // A full house is a hand that contains three cards of one rank and two cards of another rank
FLUSH(5),             // A flush is a hand that contains five cards all of the same suit, not all of sequential rank
STRAIGHT(4),          // A straight is a hand that contains five cards of sequential rank, not all of the same suit
THREE_OF_A_KIND(3),   // Three of a kind is a hand that contains three cards of one rank and two cards of two other ranks (the kickers)
TWO_PAIR(2),          // Two pair is a hand that contains two cards of one rank, two cards of another rank and one card of a third rank (the kicker)
ONE_PAIR(1),          // One pair is a hand that contains two cards of one rank and three cards of three other ranks (the kickers)
HIGH_CARD(0);          // High card is a hand that does not fall into any other category

private final int rank;

PokerHands(int rank) {
this.rank = rank;
}

int getRank() {
return rank;
}
}``````

### src/main/java/Card.java

``````class Card {
private PokerRank pokerRank;
private PokerSuit pokerSuit;

Card(String card) {
if (card.length() != 2 && card.length() != 3) {
throw new IllegalArgumentException("Invalid card");
}
pokerRank = PokerRank.getPokerRank(card.substring(0, card.length() - 1));
pokerSuit = PokerSuit.valueOf(card.substring(card.length() - 1));
}

PokerRank getPokerRank() {
return pokerRank;
}

PokerSuit getPokerSuit() {
return pokerSuit;
}
}``````

### src/main/java/PokerRank.java

``````enum PokerRank {
A(14), K(13), Q(12), J(11), TEN(10), NINE(9), EIGHT(8), SEVEN(7), SIX(6), FIVE(5), FOUR(4), THREE(3), TWO(2);

private final int rank;

PokerRank(int rank) {
this.rank = rank;
}

static PokerRank getPokerRank(String s) {
switch (s) {
case "A":
return A;
case "K":
return K;
case "Q":
return Q;
case "J":
return J;
case "10":
return TEN;
case "9":
return NINE;
case "8":
return EIGHT;
case "7":
return SEVEN;
case "6":
return SIX;
case "5":
return FIVE;
case "4":
return FOUR;
case "3":
return THREE;
case "2":
return TWO;
}
return null;
}

int getRank() {
return rank;
}
}``````

### src/main/java/PokerSuit.java

``````enum PokerSuit {
D, // Diamonds
C, // Clubs
H  // Hearts
}``````