diff --git a/exercises/practice/darts/.approaches/introduction.md b/exercises/practice/darts/.approaches/introduction.md index b68d0ada6..8aa7f2591 100644 --- a/exercises/practice/darts/.approaches/introduction.md +++ b/exercises/practice/darts/.approaches/introduction.md @@ -39,19 +39,19 @@ import java.util.function.DoublePredicate; class Darts { - final private static double innerRing = 1.0; - final private static double middleRing = 5.0; - final private static double outerRing = 10.0; + private static final double INNER_RING = 1.0; + private static final double MIDDLE_RING = 5.0; + private static final double OUTER_RING = 10.0; int score(double x, double y) { var pointRadius = (Math.sqrt((x * x) + (y * y))); DoublePredicate thrownOutside = ring -> pointRadius > ring; - if (thrownOutside.test(outerRing)) + if (thrownOutside.test(OUTER_RING)) return 0; - if (thrownOutside.test(middleRing)) + if (thrownOutside.test(MIDDLE_RING)) return 1; - if (thrownOutside.test(innerRing)) + if (thrownOutside.test(INNER_RING)) return 5; return 10; } diff --git a/exercises/practice/poker/.meta/config.json b/exercises/practice/poker/.meta/config.json index d3e25334e..98bae76ac 100644 --- a/exercises/practice/poker/.meta/config.json +++ b/exercises/practice/poker/.meta/config.json @@ -6,10 +6,12 @@ "aadityakulkarni", "FridaTveit", "hgvanpariya", + "jagdish-15", "jmrunkle", "katmpatz", "kytrinyx", "lemoncurry", + "MartinDekanovsky", "mirkoperillo", "msomji", "muzimuzhi", @@ -27,8 +29,6 @@ "src/test/java/PokerTest.java" ], "example": [ - ".meta/src/reference/java/Card.java", - ".meta/src/reference/java/Hand.java", ".meta/src/reference/java/Poker.java" ], "invalidator": [ diff --git a/exercises/practice/poker/.meta/src/reference/java/Card.java b/exercises/practice/poker/.meta/src/reference/java/Card.java deleted file mode 100644 index e07c671d8..000000000 --- a/exercises/practice/poker/.meta/src/reference/java/Card.java +++ /dev/null @@ -1,28 +0,0 @@ -class Card { - private int rank; - private int suit; - - Card(String card) { - rank = parseRank(card); - suit = parseSuit(card); - } - - int getRank() { - return rank; - } - - int getSuit() { - return suit; - } - - private int parseRank(String card) { - if (card.substring(0, 2).equals("10")) { - return 10; - } - return "..23456789TJQKA".indexOf(card.charAt(0)); - } - - private int parseSuit(String card) { - return ".HSDC".indexOf(card.charAt(card.length() - 1)); - } -} diff --git a/exercises/practice/poker/.meta/src/reference/java/Hand.java b/exercises/practice/poker/.meta/src/reference/java/Hand.java deleted file mode 100644 index dc60b44a2..000000000 --- a/exercises/practice/poker/.meta/src/reference/java/Hand.java +++ /dev/null @@ -1,148 +0,0 @@ -import java.util.*; -import java.util.stream.Collectors; - -class Hand { - private String input; - private int score; - - Hand(String hand) { - this.input = hand; - this.score = scoreHand(parseCards(hand)); - } - - int getScore() { - return score; - } - - String getInput() { - return input; - } - - private List parseCards(String hand) { - ArrayList parsedCards = new ArrayList<>(); - String[] cardsToParse = hand.split(" "); - for (String cardToParse : cardsToParse) { - parsedCards.add(new Card(cardToParse)); - } - return parsedCards; - } - - private Map getFrequencyMap(List cards) { - Map frequencyMap = new HashMap<>(); - for (Card c : cards) { - if (frequencyMap.containsKey(c.getRank())) { - frequencyMap.put(c.getRank(), frequencyMap.get(c.getRank()) + 1); - } else { - frequencyMap.put(c.getRank(), 1); - } - } - return frequencyMap; - } - - private int scoreHand(List cards) { - List cardsByRank = cards.stream() - .sorted(Comparator.comparing(Card::getRank)) - .unordered() - .collect(Collectors.toList()); - - Map frequencyMap = getFrequencyMap(cards); - List ranks = frequencyMap - .entrySet() - .stream() - .map(Map.Entry::getKey) - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); - frequencyMap = frequencyMap - .entrySet() - .stream() - .sorted(Map.Entry.comparingByValue(Collections.reverseOrder())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); - List rankCounts = frequencyMap - .entrySet() - .stream() - .map(Map.Entry::getValue) - .collect(Collectors.toList()); - List suits = cards - .stream() - .map(Card::getSuit) - .collect(Collectors.toList()); - - return calculatedScore(frequencyMap, cardsByRank, ranks, rankCounts, suits); - } - - private int calculatedScore(Map frequencyMap, List cardsByRank, List ranks, - List rankCounts, List suits) { - if (ranks.equals(Arrays.asList(14, 5, 4, 3, 2))) { - ranks = Arrays.asList(5, 4, 3, 2, 1); - } - - boolean flush = suits - .stream() - .distinct() - .count() == 1; - boolean straight = ranks.stream().distinct().count() == 5 - && ranks.get(0) - ranks.get(4) == 4; - - Iterator iteratorOverFrequencies = frequencyMap.keySet().iterator(); - - int highestFrequency = iteratorOverFrequencies.next(); - - if (straight && flush) { - return 800 + highestFrequency; - } - if (rankCounts.equals(Arrays.asList(4, 1))) { - return 700 + cardsByRank.get(0).getRank(); - } - if (rankCounts.equals(Arrays.asList(3, 2))) { - int triplet = 0; - int pair = 0; - for (Integer key : frequencyMap.keySet()) { - if (frequencyMap.get(key) == 2) { - pair = (int) key; - } - if (frequencyMap.get(key) == 3) { - triplet = 3 * (int) key; - } - } - return 600 + 3 * triplet + pair; - } - if (flush) { - return 500 + highestFrequency; - } - if (straight) { - int maxValue = Collections.max(ranks); - return 400 + maxValue; - } - if (rankCounts.equals(Arrays.asList(3, 1, 1))) { - List uniqueCards = new ArrayList(); - int triplet = 0; - for (Integer key : frequencyMap.keySet()) { - if (frequencyMap.get(key) == 1) { - uniqueCards.add((int) key); - } - if (frequencyMap.get(key) == 3) { - triplet = 3 * (int) key; - } - } - return 300 + triplet + Collections.max(uniqueCards); - } - if (rankCounts.equals(Arrays.asList(2, 2, 1))) { - int productsOfFrequencyAndValue = 0; - for (Integer key : frequencyMap.keySet()) { - int frequencyKey = (int) key; - int frequencyValue = frequencyMap.get(key); - productsOfFrequencyAndValue += frequencyKey * frequencyValue; - } - return 200 + productsOfFrequencyAndValue + 2 * Math.max(highestFrequency, iteratorOverFrequencies.next()); - } - if (rankCounts.equals(Arrays.asList(2, 1, 1, 1))) { - return 100 + highestFrequency; - } - ranks.sort(Comparator.naturalOrder()); - int result = 0; - for (int i = 0; i < ranks.size(); i++) { - result += ranks.get(0) * (i + 1); - } - return result + ranks.get(ranks.size() - 1); - } -} diff --git a/exercises/practice/poker/.meta/src/reference/java/Poker.java b/exercises/practice/poker/.meta/src/reference/java/Poker.java index e80acbad6..e8181bf05 100644 --- a/exercises/practice/poker/.meta/src/reference/java/Poker.java +++ b/exercises/practice/poker/.meta/src/reference/java/Poker.java @@ -1,34 +1,132 @@ -import java.util.*; -import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; class Poker { - private final List bestHands; - Poker(final List hands) { - bestHands = bestHands(hands); + private List hands; + private List numericalValues; + private List counts; + + public Poker(List hands) { + this.hands = hands; } - List getBestHands() { + public List getBestHands() { + List bestHands = new ArrayList<>(); + bestHands.add(hands.get(0)); + + for (int i = 1; i < hands.size(); i++) { + if (getHandRank(hands.get(i)) > getHandRank(bestHands.get(0))) { + bestHands.set(0, hands.get(i)); + } else if (getHandRank(hands.get(i)) == getHandRank(bestHands.get(0))) { + getHandRank(bestHands.get(0)); + List firstHand = counts; + + getHandRank(hands.get(i)); + List secondHand = counts; + + if (firstHand.equals(secondHand)) { + bestHands.add(hands.get(i)); + } else { + for (int j = 4; j >= 2; j--) { + if (firstHand.contains(j) && secondHand.contains(j)) { + if (firstHand.lastIndexOf(j) < secondHand.lastIndexOf(j)) { + bestHands.set(0, hands.get(i)); + break; + } else if (firstHand.lastIndexOf(j) > secondHand.lastIndexOf(j)) { + break; + } else if (firstHand.lastIndexOf(j) == secondHand.lastIndexOf(j) && j == 2) { + if (firstHand.indexOf(j) < secondHand.indexOf(j)) { + bestHands.set(0, hands.get(i)); + } + } + } + } + for (int k = firstHand.size() - 1; k >= 0; k--) { + if (firstHand.get(k) <= 1 && secondHand.get(k) <= 1) { + if (firstHand.get(k) < secondHand.get(k)) { + bestHands.set(0, hands.get(i)); + break; + } else if (firstHand.get(k) > secondHand.get(k)) { + break; + } + } + } + } + } + } return bestHands; } - private List bestHands(final List hands) { - ArrayList scoredHands = new ArrayList<>(); + public int getHandRank(String hand) { + String[] cards = hand.split(" "); + List values = new ArrayList<>(); + List suits = new ArrayList<>(); + for (String card : cards) { + if (card.length() == 2) { + values.add(card.substring(0, 1)); + suits.add(card.substring(1)); + } else { + values.add(card.substring(0, 2)); + suits.add(card.substring(2)); + } + } + + for (int i = 0; i < values.size(); i++) { + switch (values.get(i)) { + case "J" -> values.set(i, "11"); + case "Q" -> values.set(i, "12"); + case "K" -> values.set(i, "13"); + case "A" -> { + if (values.contains("2") && values.contains("3") && values.contains("4") && values.contains("5")) { + values.set(i, "1"); + } else { + values.set(i, "14"); + } + } + } + } + + numericalValues = new ArrayList<>(); + for (String value : values) { + numericalValues.add(Integer.valueOf(value)); + } + Collections.sort(numericalValues); - for (String s : hands) { - scoredHands.add(new Hand(s)); + List possibleValues = new ArrayList<>(); + counts = new ArrayList<>(); + for (int i = 1; i <= 14; i++) { + counts.add(i - 1, countOccurrences(i)); + possibleValues.add(i); } - Optional maxScoreIfAny = scoredHands - .stream() - .map(Hand::getScore) - .max(Comparator.naturalOrder()); - return maxScoreIfAny - .map(maxScore -> scoredHands - .stream() - .filter(h -> h.getScore() == maxScore) - .map(Hand::getInput) - .collect(Collectors.toList())) - .orElseGet(Collections::emptyList); + boolean isStraight = Collections.indexOfSubList(possibleValues, numericalValues) != -1; + + boolean isFlush = suits.stream().distinct().count() == 1; + + if (isStraight & isFlush) { + return 8; // straight flush + } else if (counts.contains(4)) { + return 7; // four of a kind + } else if (counts.contains(3) && counts.contains(2)) { + return 6; // full house + } else if (isFlush) { + return 5; // flush + } else if (isStraight) { + return 4; // straight + } else if (counts.contains(3)) { + return 3; // three of a kind + } else if (counts.stream().filter(value -> value == 2).count() == 2) { + return 2; // two pair + } else if (counts.contains(2)) { + return 1; // pair + } else { + return 0; // high card + } } + + public int countOccurrences(int valueToFind) { + return (int) numericalValues.stream().filter(value -> value.equals(valueToFind)).count(); + } } diff --git a/exercises/practice/poker/.meta/tests.toml b/exercises/practice/poker/.meta/tests.toml index 194b314d6..2e654ef63 100644 --- a/exercises/practice/poker/.meta/tests.toml +++ b/exercises/practice/poker/.meta/tests.toml @@ -1,6 +1,13 @@ -# This is an auto-generated file. Regular comments will be removed when this -# file is regenerated. Regenerating will not touch any manually added keys, -# so comments can be added in a "comment" key. +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. [161f485e-39c2-4012-84cf-bec0c755b66c] description = "single hand always wins" @@ -14,12 +21,18 @@ description = "a tie has multiple winners" [61ed83a9-cfaa-40a5-942a-51f52f0a8725] description = "multiple hands with the same high cards, tie compares next highest ranked, down to last card" +[da01becd-f5b0-4342-b7f3-1318191d0580] +description = "winning high card hand also has the lowest card" + [f7175a89-34ff-44de-b3d7-f6fd97d1fca4] description = "one pair beats high card" [e114fd41-a301-4111-a9e7-5a7f72a76561] description = "highest pair wins" +[b3acd3a7-f9fa-4647-85ab-e0a9e07d1365] +description = "both hands have the same pair, high card wins" + [935bb4dc-a622-4400-97fa-86e7d06b1f76] description = "two pairs beats one pair" @@ -32,6 +45,12 @@ description = "both hands have two pairs, with the same highest ranked pair, tie [15a7a315-0577-47a3-9981-d6cf8e6f387b] description = "both hands have two identically ranked pairs, tie goes to remaining card (kicker)" +[f761e21b-2560-4774-a02a-b3e9366a51ce] +description = "both hands have two pairs that add to the same value, win goes to highest pair" + +[fc6277ac-94ac-4078-8d39-9d441bc7a79e] +description = "two pairs first ranked by largest pair" + [21e9f1e6-2d72-49a1-a930-228e5e0195dc] description = "three of a kind beats two pair" @@ -40,6 +59,11 @@ description = "both hands have three of a kind, tie goes to highest ranked tripl [eb856cc2-481c-4b0d-9835-4d75d07a5d9d] description = "with multiple decks, two players can have same three of a kind, ties go to highest remaining cards" +include = false + +[26a4a7d4-34a2-4f18-90b4-4a8dd35d2bb1] +description = "with multiple decks, two players can have same three of a kind, ties go to highest remaining cards" +reimplements = "eb856cc2-481c-4b0d-9835-4d75d07a5d9d" [a858c5d9-2f28-48e7-9980-b7fa04060a60] description = "a straight beats three of a kind" @@ -50,6 +74,9 @@ description = "aces can end a straight (10 J Q K A)" [76856b0d-35cd-49ce-a492-fe5db53abc02] description = "aces can start a straight (A 2 3 4 5)" +[e214b7df-dcba-45d3-a2e5-342d8c46c286] +description = "aces cannot be in the middle of a straight (Q K A 2 3)" + [6980c612-bbff-4914-b17a-b044e4e69ea1] description = "both hands with a straight, tie goes to highest ranked card" @@ -61,6 +88,11 @@ description = "flush beats a straight" [4d90261d-251c-49bd-a468-896bf10133de] description = "both hands have a flush, tie goes to high card, down to the last one if necessary" +include = false + +[e04137c5-c19a-4dfc-97a1-9dfe9baaa2ff] +description = "both hands have a flush, tie goes to high card, down to the last one if necessary" +reimplements = "4d90261d-251c-49bd-a468-896bf10133de" [3a19361d-8974-455c-82e5-f7152f5dba7c] description = "full house beats a flush" @@ -83,5 +115,17 @@ description = "with multiple decks, both hands with identical four of a kind, ti [923bd910-dc7b-4f7d-a330-8b42ec10a3ac] description = "straight flush beats four of a kind" +[d9629e22-c943-460b-a951-2134d1b43346] +description = "aces can end a straight flush (10 J Q K A)" + +[05d5ede9-64a5-4678-b8ae-cf4c595dc824] +description = "aces can start a straight flush (A 2 3 4 5)" + +[ad655466-6d04-49e8-a50c-0043c3ac18ff] +description = "aces cannot be in the middle of a straight flush (Q K A 2 3)" + [d0927f70-5aec-43db-aed8-1cbd1b6ee9ad] -description = "both hands have straight flush, tie goes to highest-ranked card" +description = "both hands have a straight flush, tie goes to highest-ranked card" + +[be620e09-0397-497b-ac37-d1d7a4464cfc] +description = "even though an ace is usually high, a 5-high straight flush is the lowest-scoring straight flush" diff --git a/exercises/practice/poker/src/test/java/PokerTest.java b/exercises/practice/poker/src/test/java/PokerTest.java index 4c4bd8529..016889229 100644 --- a/exercises/practice/poker/src/test/java/PokerTest.java +++ b/exercises/practice/poker/src/test/java/PokerTest.java @@ -44,6 +44,15 @@ public void sameHighCards() { .containsExactly(nextHighest3); } + @Disabled("Remove to run test") + @Test + public void winningWithLowestCard() { + String lowest2 = "2S 5H 6S 8D 7H"; + String lowest3 = "3S 4D 6D 8C 7S"; + assertThat(new Poker(Arrays.asList(lowest2, lowest3)).getBestHands()) + .containsExactly(lowest2); + } + @Disabled("Remove to run test") @Test public void nothingVsOnePair() { @@ -62,6 +71,15 @@ public void twoPairs() { .containsExactly(pairOf4); } + @Disabled("Remove to run test") + @Test + public void samePair() { + String pairOf4Lower = "4H 4S AH JC 3D"; + String pairOf4Higher = "4C 4D AS 5D 6C"; + assertThat(new Poker(Arrays.asList(pairOf4Lower, pairOf4Higher)).getBestHands()) + .containsExactly(pairOf4Lower); + } + @Disabled("Remove to run test") @Test public void onePairVsDoublePair() { @@ -98,6 +116,24 @@ public void identicallyRankedPairs() { .containsExactly(kicker8); } + @Disabled("Remove to run test") + @Test + public void twoPairsAddingToSameValue() { + String doublePair6And3 = "6S 6H 3S 3H AS"; + String doublePair7And2 = "7H 7S 2H 2S AC"; + assertThat(new Poker(Arrays.asList(doublePair6And3, doublePair7And2)).getBestHands()) + .containsExactly(doublePair7And2); + } + + @Disabled("Remove to run test") + @Test + public void rankedByLargestPair() { + String doublePairs5And4 = "5C 2S 5S 4H 4C"; + String doublePairs6And2 = "6S 2S 6H 7C 2C"; + assertThat(new Poker(Arrays.asList(doublePairs5And4, doublePairs6And2)).getBestHands()) + .containsExactly(doublePairs6And2); + } + @Disabled("Remove to run test") @Test public void doublePairVsThree() { @@ -119,7 +155,7 @@ public void twoThrees() { @Disabled("Remove to run test") @Test public void sameThreesMultipleDecks() { - String remainingCard7 = "4S AH AS 7C AD"; + String remainingCard7 = "5S AH AS 7C AD"; String remainingCard8 = "4S AH AS 8C AD"; assertThat(new Poker(Arrays.asList(remainingCard7, remainingCard8)).getBestHands()) .containsExactly(remainingCard8); @@ -152,6 +188,15 @@ public void acesCanStartAStraight() { .containsExactly(straightStartA); } + @Disabled("Remove to run test") + @Test + public void acesCannotBeInMiddleOfStraight() { + String hand = "2C 3D 7H 5H 2S"; + String straightMiddleA = "QS KH AC 2D 3S"; + assertThat(new Poker(Arrays.asList(hand, straightMiddleA)).getBestHands()) + .containsExactly(hand); + } + @Disabled("Remove to run test") @Test public void twoStraights() { @@ -181,11 +226,11 @@ public void straightVsFlush() { @Disabled("Remove to run test") @Test - public void twoFlushes() { - String flushTo8 = "4H 7H 8H 9H 6H"; - String flushTo7 = "2S 4S 5S 6S 7S"; - assertThat(new Poker(Arrays.asList(flushTo8, flushTo7)).getBestHands()) - .containsExactly(flushTo8); + public void twoFlushs() { + String flushTo9 = "2H 7H 8H 9H 6H"; + String flushTo7 = "3S 5S 6S 7S 8S"; + assertThat(new Poker(Arrays.asList(flushTo9, flushTo7)).getBestHands()) + .containsExactly(flushTo9); } @Disabled("Remove to run test") @@ -251,6 +296,33 @@ public void squareVsStraightFlush() { .containsExactly(straightFlushTo9); } + @Disabled("Remove to run test") + @Test + public void acesEndingStraightFlush() { + String hand = "KC AH AS AD AC"; + String straightFlushEndingWithA = "10C JC QC KC AC"; + assertThat(new Poker(Arrays.asList(hand, straightFlushEndingWithA)).getBestHands()) + .containsExactly(straightFlushEndingWithA); + } + + @Disabled("Remove to run test") + @Test + public void acesStartingStraightFlush() { + String straightFlushStartingWithA = "4H AH 3H 2H 5H"; + String hand = "KS AH AS AD AC"; + assertThat(new Poker(Arrays.asList(straightFlushStartingWithA, hand)).getBestHands()) + .containsExactly(straightFlushStartingWithA); + } + + @Disabled("Remove to run test") + @Test + public void acesCannotBeInMiddleOfStraightFlush() { + String straightFlushWithAInMiddle = "QH KH AH 2H 3H"; + String hand = "2C AC QC 10C KC"; + assertThat(new Poker(Arrays.asList(straightFlushWithAInMiddle, hand)).getBestHands()) + .containsExactly(hand); + } + @Disabled("Remove to run test") @Test public void twoStraightFlushes() { @@ -259,4 +331,13 @@ public void twoStraightFlushes() { assertThat(new Poker(Arrays.asList(straightFlushTo8, straightFlushTo9)).getBestHands()) .containsExactly(straightFlushTo9); } + + @Disabled("Remove to run test") + @Test + public void straightFlushTo5IsTheLowestScoring() { + String straightFlushTo6 = "2H 3H 4H 5H 6H"; + String straightFlushTo5 = "4D AD 3D 2D 5D"; + assertThat(new Poker(Arrays.asList(straightFlushTo6, straightFlushTo5)).getBestHands()) + .containsExactly(straightFlushTo6); + } }