diff --git a/README.md b/README.md index 0af2dba..a2449b6 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,9 @@ The following categories exists: - [x] Ones, Twos, Threes, Fours, Fives, Sixes: The player scores the sum of the dice that reads one, two, three, four, five or six, respectively. For example, 1, 1, 2, 4, 4 placed on “fours” gives 8 points. -- [ ] Pair: The player scores the sum of the two highest matching dice. For example, 3, 3, 3, 4, 4 placed on “pair” +- [x] Pair: The player scores the sum of the two highest matching dice. For example, 3, 3, 3, 4, 4 placed on “pair” gives 8. -- [ ] Two pairs: If there are two pairs of dice with the same number, the player scores the sum of these dice. If not, +- [x] Two pairs: If there are two pairs of dice with the same number, the player scores the sum of these dice. If not, the player scores 0. For example, 1, 1, 2, 3, 3 placed on “two pairs” gives 8. - [x] Three of a kind: If there are three dice with the same number, the player scores the sum of these dice. Otherwise, the player scores 0. For example, 3, 3, 3, 4, 5 places on “three of a kind” gives 9. diff --git a/src/main/java/io/github/vbetsch/yams/CategoryEnum.java b/src/main/java/io/github/vbetsch/yams/CategoryEnum.java index 3ac7db2..0738fcc 100644 --- a/src/main/java/io/github/vbetsch/yams/CategoryEnum.java +++ b/src/main/java/io/github/vbetsch/yams/CategoryEnum.java @@ -1,12 +1,17 @@ package io.github.vbetsch.yams; public enum CategoryEnum { + // --- TOP PART --- ACES, TWOS, THREES, FOURS, FIVES, SIXES, + // --- BONUS PART (kata) --- + PAIR, + TWO_PAIRS, + // --- BOTTOM PART --- THREE_OF_A_KIND, FOUR_OF_A_KIND, // FULL_HOUSE, diff --git a/src/main/java/io/github/vbetsch/yams/CategoryNotAuthorizedForThisRollError.java b/src/main/java/io/github/vbetsch/yams/CategoryNotAuthorizedForThisRollError.java deleted file mode 100644 index 6b01ff4..0000000 --- a/src/main/java/io/github/vbetsch/yams/CategoryNotAuthorizedForThisRollError.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.vbetsch.yams; - -public class CategoryNotAuthorizedForThisRollError extends IllegalArgumentException { - public CategoryNotAuthorizedForThisRollError(CategoryEnum category) { - super("We cannot compute score with category " + category + " for this roll"); - } -} diff --git a/src/main/java/io/github/vbetsch/yams/Yams.java b/src/main/java/io/github/vbetsch/yams/Yams.java index 765f539..e1e904f 100644 --- a/src/main/java/io/github/vbetsch/yams/Yams.java +++ b/src/main/java/io/github/vbetsch/yams/Yams.java @@ -1,11 +1,33 @@ package io.github.vbetsch.yams; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.IntStream; +import java.util.stream.Stream; public class Yams { static final int DICES_COUNT = 5; + // Utils + private Map getOccurrencesByValue(List roll) { + Map occurrencesByValue = new HashMap<>(); + for (int index = 0; index < roll.size(); index++) { + int diceValue = roll.get(index); + occurrencesByValue.merge(diceValue, 1, Integer::sum); + } + IO.println(occurrencesByValue); + return occurrencesByValue; + } + + private boolean containsAtLeastXDuplicatesOnAtLeastYDifferentDices(Map occurrencesByValue, int duplicatesNumber, int differentDices) { + Stream> matchingOccurrences = occurrencesByValue + .entrySet() + .stream() + .filter(occurrenceByValue -> occurrenceByValue.getValue() >= duplicatesNumber); + return matchingOccurrences.count() >= differentDices; + } + private int calculateSumOfDices(List roll) { return roll .stream() @@ -13,6 +35,14 @@ private int calculateSumOfDices(List roll) { .sum(); } + private Stream> getAllPairsOccurrences(Map occurrencesByValue) { + return occurrencesByValue + .entrySet() + .stream() + .filter(occurrenceByValue -> occurrenceByValue.getValue() >= 2); + } + + // Handlers private int handleChanceScore(List roll) { return this.calculateSumOfDices(roll); } @@ -38,30 +68,47 @@ private int handleTopPartScores(List roll, int target) { .sum(); } - private boolean containsDuplicatesNth(List roll, int duplicatesNumber) { - IntStream reducedRoll = roll - .stream() + private int handlePairScore(List roll) throws IllegalArgumentException { + Map occurrencesByValue = this.getOccurrencesByValue(roll); + if (!this.containsAtLeastXDuplicatesOnAtLeastYDifferentDices(occurrencesByValue, 2, 1)) { + return 0; + } + return this.getAllPairsOccurrences(occurrencesByValue) + .map(Map.Entry::getKey) + .max(Integer::compare) + .orElse(0) * 2; + } + + private int handleTwoPairsScore(List roll) throws IllegalArgumentException { + Map occurrencesByValue = this.getOccurrencesByValue(roll); + if (!this.containsAtLeastXDuplicatesOnAtLeastYDifferentDices(occurrencesByValue, 2, 2)) { + return 0; + } + return this.getAllPairsOccurrences(occurrencesByValue) + .map(occurrenceByValue -> occurrenceByValue.getKey() * 2) .mapToInt(Integer::intValue) - .distinct(); - return reducedRoll.count() == (Yams.DICES_COUNT - duplicatesNumber + 1); + .sum(); } private int handleThreeOfAKindScore(List roll) throws IllegalArgumentException { - if (!this.containsDuplicatesNth(roll, 3)) { - throw new CategoryNotAuthorizedForThisRollError(CategoryEnum.THREE_OF_A_KIND); + Map occurrencesByValue = this.getOccurrencesByValue(roll); + if (!this.containsAtLeastXDuplicatesOnAtLeastYDifferentDices(occurrencesByValue, 3, 1)) { + return 0; } return this.calculateSumOfDices(roll); } private int handleFourOfAKindScore(List roll) throws IllegalArgumentException { - if (!this.containsDuplicatesNth(roll, 4)) { - throw new CategoryNotAuthorizedForThisRollError(CategoryEnum.FOUR_OF_A_KIND); + Map occurrencesByValue = this.getOccurrencesByValue(roll); + if (!this.containsAtLeastXDuplicatesOnAtLeastYDifferentDices(occurrencesByValue, 4, 1)) { + return 0; } return this.calculateSumOfDices(roll); } + // Public method public int computeScore(List roll, CategoryEnum category) throws InvalidRollSizeError { - if(roll.size() != Yams.DICES_COUNT) { + if (roll.size() != Yams.DICES_COUNT) { throw new InvalidRollSizeError(roll.size()); } return switch (category) { @@ -73,6 +120,8 @@ public int computeScore(List roll, CategoryEnum category) throws Invali case CategoryEnum.FOURS -> this.handleTopPartScores(roll, 4); case CategoryEnum.FIVES -> this.handleTopPartScores(roll, 5); case CategoryEnum.SIXES -> this.handleTopPartScores(roll, 6); + case CategoryEnum.PAIR -> this.handlePairScore(roll); + case CategoryEnum.TWO_PAIRS -> this.handleTwoPairsScore(roll); case CategoryEnum.THREE_OF_A_KIND -> this.handleThreeOfAKindScore(roll); case CategoryEnum.FOUR_OF_A_KIND -> this.handleFourOfAKindScore(roll); default -> 1000; diff --git a/src/test/java/io/github/vbetsch/yams/YamsTest.java b/src/test/java/io/github/vbetsch/yams/YamsTest.java index 9683c49..44a175d 100644 --- a/src/test/java/io/github/vbetsch/yams/YamsTest.java +++ b/src/test/java/io/github/vbetsch/yams/YamsTest.java @@ -8,8 +8,9 @@ import static org.junit.jupiter.api.Assertions.assertThrows; class YamsTest { + // --- CHANCE --- @Test - void chance_return15Points_whenGivenLargeStraight() { + void chance_returnPoints_whenGivenLargeStraight() { // Arrange Yams yams = new Yams(); @@ -21,7 +22,7 @@ void chance_return15Points_whenGivenLargeStraight() { } @Test - void chance_return16Points_whenGivenSmallStraight() { + void chance_returnPoints_whenGivenSmallStraight() { // Arrange Yams yams = new Yams(); @@ -32,8 +33,9 @@ void chance_return16Points_whenGivenSmallStraight() { assertEquals(16, result); } + // --- YAMS --- @Test - void yams_return50Points_whenGivenFiveDicesSix() { + void yams_returnPoints_whenGivenFiveDicesSix() { // Arrange Yams yams = new Yams(); @@ -45,7 +47,7 @@ void yams_return50Points_whenGivenFiveDicesSix() { } @Test - void yams_return50Points_whenGivenFiveDicesThree() { + void yams_returnPoints_whenGivenFiveDicesThree() { // Arrange Yams yams = new Yams(); @@ -57,7 +59,7 @@ void yams_return50Points_whenGivenFiveDicesThree() { } @Test - void yams_return0Points_whenGivenRollWithDuplicates() { + void yams_returnZeroPoints_whenGivenRollWithDuplicates() { // Arrange Yams yams = new Yams(); @@ -69,7 +71,7 @@ void yams_return0Points_whenGivenRollWithDuplicates() { } @Test - void yams_return0Points_whenGivenRollWithoutDuplicates() { + void yams_returnZeroPoints_whenGivenRollWithoutDuplicates() { // Arrange Yams yams = new Yams(); @@ -80,8 +82,9 @@ void yams_return0Points_whenGivenRollWithoutDuplicates() { assertEquals(0, result); } + // --- ACES --- @Test - void aces_return3Points_whenGivenRollWithThreeDicesAces() { + void aces_returnPoints_whenGivenRollWithThreeDicesAces() { // Arrange Yams yams = new Yams(); @@ -92,8 +95,9 @@ void aces_return3Points_whenGivenRollWithThreeDicesAces() { assertEquals(3, result); } + // --- TOP PARTS --- @Test - void twos_return6Points_whenGivenRollWithThreeDicesTwos() { + void twos_returnPoints_whenGivenRollWithThreeDicesTwos() { // Arrange Yams yams = new Yams(); @@ -105,7 +109,7 @@ void twos_return6Points_whenGivenRollWithThreeDicesTwos() { } @Test - void three_return9Points_whenGivenRollWithThreeDicesThrees() { + void three_returnPoints_whenGivenRollWithThreeDicesThrees() { // Arrange Yams yams = new Yams(); @@ -117,7 +121,7 @@ void three_return9Points_whenGivenRollWithThreeDicesThrees() { } @Test - void four_return12Points_whenGivenRollWithThreeDicesFours() { + void four_returnPoints_whenGivenRollWithThreeDicesFours() { // Arrange Yams yams = new Yams(); @@ -129,7 +133,7 @@ void four_return12Points_whenGivenRollWithThreeDicesFours() { } @Test - void five_return15Points_whenGivenRollWithThreeDicesFives() { + void five_returnPoints_whenGivenRollWithThreeDicesFives() { // Arrange Yams yams = new Yams(); @@ -141,7 +145,7 @@ void five_return15Points_whenGivenRollWithThreeDicesFives() { } @Test - void sixes_return18Points_whenGivenRollWithThreeDicesSixes() { + void sixes_returnPoints_whenGivenRollWithThreeDicesSixes() { // Arrange Yams yams = new Yams(); @@ -152,8 +156,131 @@ void sixes_return18Points_whenGivenRollWithThreeDicesSixes() { assertEquals(18, result); } + // --- PAIRS --- @Test - void threeOfAKind_return3Points_whenGivenThreeDicesOnes() { + void pair_returnZeroPoints_whenGivenNotPair() { + // Arrange + Yams yams = new Yams(); + + // Act + int result = yams.computeScore(List.of(1, 2, 3, 4, 5), CategoryEnum.PAIR); + + // Assert + assertEquals(0, result); + } + + @Test + void pair_returnPoints_whenGivenRollWithOnlyOnePairOfTwo() { + // Arrange + Yams yams = new Yams(); + + // Act + int result = yams.computeScore(List.of(2, 2, 3, 4, 5), CategoryEnum.PAIR); + + // Assert + assertEquals(4, result); + } + + @Test + void pair_returnPoints_whenGivenRollWithOnlyOnePairOfThree() { + // Arrange + Yams yams = new Yams(); + + // Act + int result = yams.computeScore(List.of(3, 3, 4, 5, 1), CategoryEnum.PAIR); + + // Assert + assertEquals(6, result); + } + + @Test + void pair_returnPoints_whenGivenRollWithMoreThanTwoDices() { + // Arrange + Yams yams = new Yams(); + + // Act + int result = yams.computeScore(List.of(3, 3, 3, 5, 1), CategoryEnum.PAIR); + + // Assert + assertEquals(6, result); + } + + @Test + void pair_returnPoints_whenGivenRollWithTwoPairs() { + // Arrange + Yams yams = new Yams(); + + // Act + int result = yams.computeScore(List.of(3, 3, 4, 4, 1), CategoryEnum.PAIR); + + // Assert + assertEquals(8, result); + } + + // --- TWO PAIRS --- + @Test + void two_pairs_returnZeroPoints_whenGivenNotPair() { + // Arrange + Yams yams = new Yams(); + + // Act + int result = yams.computeScore(List.of(1, 2, 3, 4, 5), CategoryEnum.TWO_PAIRS); + + // Assert + assertEquals(0, result); + } + + @Test + void two_pairs_returnZeroPoints_whenGivenRollWithOnlyOnePair() { + // Arrange + Yams yams = new Yams(); + + // Act + int result = yams.computeScore(List.of(2, 2, 3, 4, 5), CategoryEnum.TWO_PAIRS); + + // Assert + assertEquals(0, result); + } + + @Test + void two_pairs_returnPoints_whenGivenRollWithTwoPairsOfThreeAndFour() { + // Arrange + Yams yams = new Yams(); + + // Act + int result = yams.computeScore(List.of(3, 3, 4, 4, 1), CategoryEnum.TWO_PAIRS); + + // Assert + assertEquals(14, result); + } + + @Test + void two_pairs_returnPoints_whenGivenRollWithTwoPairsOfFourAndFive() { + // Arrange + Yams yams = new Yams(); + + // Act + int result = yams.computeScore(List.of(4, 4, 5, 5, 1), CategoryEnum.TWO_PAIRS); + + // Assert + assertEquals(18, result); + } + + @Test + void two_pairs_returnPoints_whenGivenRollWithFull() { + // Arrange + Yams yams = new Yams(); + + // Act + int result = yams.computeScore(List.of(3, 3, 4, 4, 4), CategoryEnum.TWO_PAIRS); + + // Assert + assertEquals(14, result); + } + + // --- THREE OF A KIND --- + @Test + void threeOfAKind_returnPoints_whenGivenThreeDicesOnes() { // Arrange Yams yams = new Yams(); @@ -165,7 +292,7 @@ void threeOfAKind_return3Points_whenGivenThreeDicesOnes() { } @Test - void threeOfAKind_return10Points_whenGivenThreeDicesTwos() { + void threeOfAKind_returnPoints_whenGivenThreeDicesTwos() { // Arrange Yams yams = new Yams(); @@ -177,20 +304,32 @@ void threeOfAKind_return10Points_whenGivenThreeDicesTwos() { } @Test - void threeOfAKind_throwIllegalArgumentException_whenGivenLargeStraight() { + void threeOfAKind_returnPoints_whenGivenMoreThanThreeDicesTwos() { // Arrange Yams yams = new Yams(); - // Act & Assert - assertThrows( - IllegalArgumentException.class, - () -> yams.computeScore(List.of(1, 2, 3, 4, 5), CategoryEnum.THREE_OF_A_KIND), - "We cannot compute score with category ThreeOfAKind for this roll" - ); + // Act + int result = yams.computeScore(List.of(2, 2, 2, 2, 3), CategoryEnum.THREE_OF_A_KIND); + + // Assert + assertEquals(11, result); } @Test - void fourOfAKind_return7Points_whenGivenFourDicesOnes() { + void threeOfAKind_returnZeroPoints_whenGivenLargeStraight() { + // Arrange + Yams yams = new Yams(); + + // Act + int result = yams.computeScore(List.of(1, 2, 3, 4, 5), CategoryEnum.THREE_OF_A_KIND); + + // Assert + assertEquals(0, result); + } + + // --- FOUR OF A KIND --- + @Test + void fourOfAKind_returnPoints_whenGivenFourDicesOnes() { // Arrange Yams yams = new Yams(); @@ -202,7 +341,7 @@ void fourOfAKind_return7Points_whenGivenFourDicesOnes() { } @Test - void fourOfAKind_return11Points_whenGivenFourDicesTwos() { + void fourOfAKind_returnPoints_whenGivenFourDicesTwos() { // Arrange Yams yams = new Yams(); @@ -214,18 +353,30 @@ void fourOfAKind_return11Points_whenGivenFourDicesTwos() { } @Test - void fourOfAKind_throwIllegalArgumentException_whenGivenLargeStraight() { + void fourOfAKind_returnPoints_whenGivenMoreThanFourDicesTwos() { // Arrange Yams yams = new Yams(); - // Act & Assert - assertThrows( - IllegalArgumentException.class, - () -> yams.computeScore(List.of(1, 2, 3, 4, 5), CategoryEnum.FOUR_OF_A_KIND), - "We cannot compute score with category FourOfAKind for this roll" - ); + // Act + int result = yams.computeScore(List.of(2, 2, 2, 2, 2), CategoryEnum.FOUR_OF_A_KIND); + + // Assert + assertEquals(10, result); } + @Test + void fourOfAKind_returnZeroPoints_whenGivenLargeStraight() { + // Arrange + Yams yams = new Yams(); + + // Act + int result = yams.computeScore(List.of(1, 2, 3, 4, 5), CategoryEnum.FOUR_OF_A_KIND); + + // Assert + assertEquals(0, result); + } + + // --- CHECKS --- @Test void throwIllegalArgumentException_whenGivenLessThanFiveDices() { // Arrange @@ -233,7 +384,7 @@ void throwIllegalArgumentException_whenGivenLessThanFiveDices() { // Act & Assert assertThrows( - InvalidRollSizeError.class, + IllegalArgumentException.class, () -> yams.computeScore(List.of(1, 2, 3, 4), CategoryEnum.CHANCE), "Roll must contain exactly 5 dices" ); @@ -246,7 +397,7 @@ void throwIllegalArgumentException_whenGivenMoreThanFiveDices() { // Act & Assert assertThrows( - InvalidRollSizeError.class, + IllegalArgumentException.class, () -> yams.computeScore(List.of(1, 2, 3, 4, 5, 6), CategoryEnum.CHANCE), "Roll must contain exactly 5 dices" ); @@ -259,7 +410,7 @@ void throwIllegalArgumentException_whenGivenNoDice() { // Act & Assert assertThrows( - InvalidRollSizeError.class, + IllegalArgumentException.class, () -> yams.computeScore(List.of(), CategoryEnum.CHANCE), "Roll must contain exactly 5 dices" );