diff --git a/src/main/java/calculator/App.java b/src/main/java/calculator/App.java deleted file mode 100644 index 2549f3a2b3..0000000000 --- a/src/main/java/calculator/App.java +++ /dev/null @@ -1,13 +0,0 @@ -package calculator; - -import lotto.LottoController; -import lotto.LottoMachine; -import lotto.ShuffleNumberGenerator; - -public class Application { - public static void main(String[] args) { - LottoMachine machine = new LottoMachine(new ShuffleNumberGenerator()); - LottoController controller = new LottoController(new InputView(), new ResultView(), machine); - controller.run(); - } -} diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index 8a8546a8b9..31f5b45df5 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,8 +1,5 @@ package lotto; -import lotto.InputView; -import lotto.ResultView; - public class Application { public static void main(String[] args) { LottoMachine machine = new LottoMachine(new ShuffleNumberGenerator()); diff --git a/src/main/java/lotto/InputView.java b/src/main/java/lotto/InputView.java index 7eb3a7ad3e..ac8db7d376 100644 --- a/src/main/java/lotto/InputView.java +++ b/src/main/java/lotto/InputView.java @@ -20,4 +20,9 @@ public List readWinningNumbers() { .map(Integer::parseInt) .collect(Collectors.toList()); } + + public int readBonusNumber() { + System.out.println("보너스 볼을 입력해 주세요."); + return Integer.parseInt(scanner.nextLine().trim()); + } } diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java index 3b56c1c7cf..5816f18847 100644 --- a/src/main/java/lotto/Lotto.java +++ b/src/main/java/lotto/Lotto.java @@ -1,66 +1,55 @@ package lotto; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class Lotto { - private static final int SIZE = 6; - private static final int MIN = 1; - private static final int MAX = 45; + private static final int LOTTO_SIZE = 6; - private final Set numbers; + private final Set numbers; public Lotto(List numbers) { - validateSize(numbers); - validateRange(numbers); - validateUnique(numbers); - this.numbers = Set.copyOf(numbers); - } - - public Lotto(int... numbers) { - this(Arrays.stream(numbers).boxed().collect(Collectors.toList())); - } - - public List numbers() { - return numbers.stream() - .sorted() - .collect(Collectors.toList()); - } - - public boolean contains(int number) { - return numbers.contains(number); + this(toLottoNumbers(numbers)); } - public int matchCount(Lotto winning) { - return (int) numbers.stream() - .filter(winning::contains) - .count(); + private Lotto(Set numbers) { + validate(numbers); + this.numbers = Set.copyOf(numbers); } - private void validateSize(List numbers) { - if (numbers.size() != SIZE) { - throw new IllegalArgumentException("로또 번호는 6개여야 한다."); + private static Set toLottoNumbers(List numbers) { + Set result = new HashSet<>(); + for (int n : numbers) { + result.add(LottoNumber.from(n)); } + return result; } - private void validateRange(List numbers) { - if (numbers.stream().anyMatch(this::isOutOfRange)) { - throw new IllegalArgumentException("로또 번호는 1~45 범위여야 한다."); + private void validate(Set numbers) { + if (numbers.size() != LOTTO_SIZE) { + throw new IllegalArgumentException("로또 번호는 중복 없이 " + LOTTO_SIZE + "개여야 합니다."); } } - private boolean isOutOfRange(int number) { - return number < MIN || number > MAX; + public boolean contains(LottoNumber number) { + return numbers.contains(number); } - private void validateUnique(List numbers) { - if (new HashSet<>(numbers).size() != SIZE) { - throw new IllegalArgumentException("로또 번호는 중복될 수 없다."); + public int matchCount(Lotto other) { + int count = 0; + for (LottoNumber n : numbers) { + if (other.contains(n)) count++; } + return count; + } + + public List numbers() { + return numbers.stream() + .map(LottoNumber::value) + .sorted() + .collect(Collectors.toList()); } } diff --git a/src/main/java/lotto/LottoController.java b/src/main/java/lotto/LottoController.java index 99f3fe8310..d90aa4cec7 100644 --- a/src/main/java/lotto/LottoController.java +++ b/src/main/java/lotto/LottoController.java @@ -1,7 +1,6 @@ package lotto; -import lotto.InputView; -import lotto.ResultView; + import java.util.List; public class LottoController { @@ -18,14 +17,13 @@ public LottoController(InputView inputView, ResultView resultView, LottoMachine public void run() { Money money = Money.of(inputView.readPurchaseAmount()); - List lottos = lottoMachine.issue(money); - resultView.printLottos(lottos); + Lottos tickets = lottoMachine.issue(money); + resultView.printLottos(tickets); Lotto winning = new Lotto(inputView.readWinningNumbers()); + WinningNumbers winningNumbers = new WinningNumbers(winning, inputView.readBonusNumber()); - WinningStatistics stats = new WinningStatistics(); - stats.accumulate(lottos, winning); - + WinningStatistics stats = winningNumbers.match(tickets); resultView.printStatistics(stats, money); } } diff --git a/src/main/java/lotto/LottoMachine.java b/src/main/java/lotto/LottoMachine.java index 42f2764120..e8becac43a 100644 --- a/src/main/java/lotto/LottoMachine.java +++ b/src/main/java/lotto/LottoMachine.java @@ -10,11 +10,11 @@ public LottoMachine(LottoNumberGenerator generator) { this.generator = generator; } - public List issue(Money money) { + public Lottos issue(Money money) { List lottos = new ArrayList<>(); for (int i = 0; i < money.ticketCount(); i++) { lottos.add(new Lotto(generator.generate())); } - return List.copyOf(lottos); + return new Lottos(lottos); } } diff --git a/src/main/java/lotto/LottoNumber.java b/src/main/java/lotto/LottoNumber.java new file mode 100644 index 0000000000..d5063c33b8 --- /dev/null +++ b/src/main/java/lotto/LottoNumber.java @@ -0,0 +1,50 @@ +package lotto; + +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +public class LottoNumber { + private static final int MIN = 1; + private static final int MAX = 45; + + private static final Map CACHE = new ConcurrentHashMap<>(); + + private final int value; + + private LottoNumber(int value) { + this.value = value; + } + + public static LottoNumber from(int value) { + validateRange(value); + return CACHE.computeIfAbsent(value, LottoNumber::new); + } + + private static void validateRange(int value) { + if (value < MIN || value > MAX) { + throw new IllegalArgumentException("로또 번호는 " + MIN + " ~ " + MAX + " 사이여야 합니다. value=" + value); + } + } + + public int value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof LottoNumber)) return false; + return value == ((LottoNumber) o).value; + } + + @Override + public int hashCode() { + return Integer.hashCode(value); + } + + @Override + public String toString() { + return String.valueOf(value); + } +} diff --git a/src/main/java/lotto/Lottos.java b/src/main/java/lotto/Lottos.java new file mode 100644 index 0000000000..bf7d7aec26 --- /dev/null +++ b/src/main/java/lotto/Lottos.java @@ -0,0 +1,21 @@ +package lotto; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class Lottos { + private final List values; + + public Lottos(List values) { + this.values = List.copyOf(values); + } + + public List values() { + return values; + } + + public int size() { + return values.size(); + } +} diff --git a/src/main/java/lotto/MatchResult.java b/src/main/java/lotto/MatchResult.java new file mode 100644 index 0000000000..e42b00e6a1 --- /dev/null +++ b/src/main/java/lotto/MatchResult.java @@ -0,0 +1,19 @@ +package lotto; + +public class MatchResult { + private final int matchCount; + private final boolean bonusMatched; + + public MatchResult(int matchCount, boolean bonusMatched) { + this.matchCount = matchCount; + this.bonusMatched = bonusMatched; + } + + public int matchCount() { + return matchCount; + } + + public boolean bonusMatched() { + return bonusMatched; + } +} diff --git a/src/main/java/lotto/Rank.java b/src/main/java/lotto/Rank.java index d79d2d4e31..712d9aea54 100644 --- a/src/main/java/lotto/Rank.java +++ b/src/main/java/lotto/Rank.java @@ -1,29 +1,42 @@ package lotto; -import java.util.Arrays; + +import lotto.MatchResult; public enum Rank { - FIRST(6, 2_000_000_000, "6개 일치"), - THIRD(5, 1_500_000, "5개 일치"), - FOURTH(4, 50_000, "4개 일치"), - FIFTH(3, 5_000, "3개 일치"), - MISS(0, 0, ""); + FIRST(6, false, 2_000_000_000, "6개 일치", 5), + SECOND(5, true, 30_000_000, "5개 일치, 보너스 볼 일치", 4), + THIRD(5, false, 1_500_000, "5개 일치", 3), + FOURTH(4, false, 50_000, "4개 일치", 2), + FIFTH(3, false, 5_000, "3개 일치", 1), + MISS(0, false, 0, "", 0); private final int matchCount; - private final long prize; + private final boolean bonusNeeded; + private final int prize; private final String description; + private final int displayOrder; - Rank(int matchCount, long prize, String description) { + Rank(int matchCount, boolean bonusNeeded, int prize, String description, int displayOrder) { this.matchCount = matchCount; + this.bonusNeeded = bonusNeeded; this.prize = prize; this.description = description; + this.displayOrder = displayOrder; } - public int matchCount() { - return matchCount; + public static Rank of(MatchResult result) { + int matchCount = result.matchCount(); + boolean bonusMatched = result.bonusMatched(); + + if (matchCount == 6) return FIRST; + if (matchCount == 5) return bonusMatched ? SECOND : THIRD; + if (matchCount == 4) return FOURTH; + if (matchCount == 3) return FIFTH; + return MISS; } - public long prize() { + public int prize() { return prize; } @@ -31,20 +44,11 @@ public String description() { return description; } - public boolean isWinning() { - return this != MISS; - } - - public static Rank of(int matchCount) { - return Arrays.stream(values()) - .filter(rank -> rank.matchCount == matchCount) - .findFirst() - .orElse(MISS); + public int displayOrder() { + return displayOrder; } public static Rank[] winningRanks() { - return Arrays.stream(values()) - .filter(Rank::isWinning) - .toArray(Rank[]::new); + return new Rank[]{FIFTH, FOURTH, THIRD, SECOND, FIRST}; } } diff --git a/src/main/java/lotto/ResultView.java b/src/main/java/lotto/ResultView.java index 8d66b83726..6f14fb820d 100644 --- a/src/main/java/lotto/ResultView.java +++ b/src/main/java/lotto/ResultView.java @@ -1,11 +1,13 @@ package lotto; +import java.util.Arrays; +import java.util.Comparator; import java.util.List; public class ResultView { - public void printLottos(List lottos) { + public void printLottos(Lottos lottos) { System.out.println(lottos.size() + "개를 구매했습니다."); - lottos.forEach(lotto -> System.out.println(lotto.numbers())); + lottos.values().forEach(lotto -> System.out.println(lotto.numbers())); } public void printStatistics(WinningStatistics stats, Money money) { @@ -13,11 +15,14 @@ public void printStatistics(WinningStatistics stats, Money money) { System.out.println("당첨 통계"); System.out.println("---------"); - for (Rank rank : Rank.winningRanks()) { - System.out.println(rank.description() + " (" + rank.prize() + "원)- " - + stats.countOf(rank) + "개"); - } + Arrays.stream(Rank.winningRanks()) + .sorted(Comparator.comparingInt(Rank::displayOrder)) + .forEach(rank -> printRank(stats, rank)); System.out.println("총 수익률은 " + stats.profitRate(money) + "입니다."); } + + private void printRank(WinningStatistics stats, Rank rank) { + System.out.println(rank.description() + " (" + rank.prize() + "원)- " + stats.countOf(rank) + "개"); + } } diff --git a/src/main/java/lotto/WinningNumbers.java b/src/main/java/lotto/WinningNumbers.java new file mode 100644 index 0000000000..51d630a1bc --- /dev/null +++ b/src/main/java/lotto/WinningNumbers.java @@ -0,0 +1,39 @@ +package lotto; + + +import lotto.MatchResult; + +public class WinningNumbers { + private final Lotto winning; + private final LottoNumber bonus; + + public WinningNumbers(Lotto winning, int bonus) { + this(winning, LottoNumber.from(bonus)); + } + + public WinningNumbers(Lotto winning, LottoNumber bonus) { + validateBonusNotDuplicated(winning, bonus); + this.winning = winning; + this.bonus = bonus; + } + + private void validateBonusNotDuplicated(Lotto winning, LottoNumber bonus) { + if (winning.contains(bonus)) { + throw new IllegalArgumentException("보너스 번호는 당첨 번호와 중복될 수 없습니다. bonus=" + bonus.value()); + } + } + + public Rank match(Lotto ticket) { + int matchCount = ticket.matchCount(winning); + boolean bonusMatched = ticket.contains(bonus); + return Rank.of(new MatchResult(matchCount, bonusMatched)); + } + + public WinningStatistics match(Lottos tickets) { + WinningStatistics stats = new WinningStatistics(); + for (Lotto ticket : tickets.values()) { + stats.accumulate(match(ticket)); + } + return stats; + } +} diff --git a/src/main/java/lotto/WinningStatistics.java b/src/main/java/lotto/WinningStatistics.java index 0e361bd8f1..2af77bc588 100644 --- a/src/main/java/lotto/WinningStatistics.java +++ b/src/main/java/lotto/WinningStatistics.java @@ -1,45 +1,28 @@ package lotto; import java.util.EnumMap; -import java.util.List; import java.util.Map; -public class WinningStatistics { +public final class WinningStatistics { private final Map counts = new EnumMap<>(Rank.class); - public WinningStatistics() { - initCounts(); - } - - public void accumulate(List lottos, Lotto winning) { - lottos.forEach(lotto -> accumulateOne(lotto, winning)); + public void accumulate(Rank rank) { + counts.merge(rank, 1, Integer::sum); } public int countOf(Rank rank) { - return counts.get(rank); + return counts.getOrDefault(rank, 0); } public long totalPrize() { - return counts.entrySet().stream() - .mapToLong(e -> e.getKey().prize() * e.getValue()) - .sum(); + long sum = 0L; + for (Map.Entry e : counts.entrySet()) { + sum += (long) e.getKey().prize() * e.getValue(); + } + return sum; } public double profitRate(Money purchase) { return (double) totalPrize() / purchase.amount(); } - - private void initCounts() { - for (Rank rank : Rank.winningRanks()) { - counts.put(rank, 0); - } - } - - private void accumulateOne(Lotto lotto, Lotto winning) { - Rank rank = Rank.of(lotto.matchCount(winning)); - if (!rank.isWinning()) { - return; - } - counts.put(rank, counts.get(rank) + 1); - } } diff --git a/src/test/java/lotto/LottoMachineTest.java b/src/test/java/lotto/LottoMachineTest.java index 74ceb633af..c2a4a50347 100644 --- a/src/test/java/lotto/LottoMachineTest.java +++ b/src/test/java/lotto/LottoMachineTest.java @@ -16,8 +16,8 @@ void issueLottosByMoney() { )); LottoMachine machine = new LottoMachine(generator); - List lottos = machine.issue(Money.of(2000)); + Lottos lottos = machine.issue(Money.of(2000)); - assertThat(lottos).hasSize(2); + assertThat(lottos.size()).isEqualTo(2); } } diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java index e2e34c19c5..894bd2aeb5 100644 --- a/src/test/java/lotto/LottoTest.java +++ b/src/test/java/lotto/LottoTest.java @@ -11,37 +11,41 @@ public class LottoTest { @Test @DisplayName("로또 번호는 반드시 6개여야 한다") void lottoMustHaveSixNumbers() { - assertThatThrownBy(() -> new Lotto(List.of(1,2,3,4,5))) + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5))) .isInstanceOf(IllegalArgumentException.class); } @Test @DisplayName("로또 번호는 중복될 수 없다") void lottoNumbersMustBeUnique() { - assertThatThrownBy(() -> new Lotto(List.of(1,1,2,3,4,5))) + assertThatThrownBy(() -> new Lotto(List.of(1, 1, 2, 3, 4, 5))) .isInstanceOf(IllegalArgumentException.class); } @Test - @DisplayName("로또 번호는 1부터 45 사이여야 한다") + @DisplayName("로또 번호는 1~45 범위여야 한다") void lottoNumbersMustBeInRange() { - assertThatThrownBy(() -> new Lotto(List.of(0,1,2,3,4,5))) + assertThatThrownBy(() -> new Lotto(List.of(0, 2, 3, 4, 5, 6))) + .isInstanceOf(IllegalArgumentException.class); + + assertThatThrownBy(() -> new Lotto(List.of(46, 2, 3, 4, 5, 6))) .isInstanceOf(IllegalArgumentException.class); } @Test - @DisplayName("로또 번호는 오름차순으로 정렬된다") + @DisplayName("로또 번호 목록은 오름차순으로 반환된다") void lottoNumbersAreSorted() { - Lotto lotto = new Lotto(List.of(8, 21, 23, 41, 42, 43)); - assertThat(lotto.numbers()) - .containsExactly(8, 21, 23, 41, 42, 43); + Lotto lotto = new Lotto(List.of(43, 21, 8, 42, 41, 23)); + + assertThat(lotto.numbers()).containsExactly(8, 21, 23, 41, 42, 43); } @Test @DisplayName("당첨 번호와 일치하는 개수를 계산한다") void matchCountCalculation() { - Lotto lotto = new Lotto(List.of(1,2,3,10,11,12)); + Lotto ticket = new Lotto(1, 2, 3, 10, 11, 12); + Lotto winning = new Lotto(1, 2, 3, 4, 5, 6); - assertThat(lotto.matchCount(lotto)).isEqualTo(3); + assertThat(ticket.matchCount(winning)).isEqualTo(3); } } diff --git a/src/test/java/lotto/LottosTest.java b/src/test/java/lotto/LottosTest.java new file mode 100644 index 0000000000..cfda6385a2 --- /dev/null +++ b/src/test/java/lotto/LottosTest.java @@ -0,0 +1,25 @@ +package lotto; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class LottosTest { + @Test + @DisplayName("Lottos는 외부 리스트 변경으로부터 안전해야 한다(방어적 복사)") + void defensiveCopy() { + List input = new ArrayList<>(); + input.add(new Lotto(List.of(1,2,3,4,5,6))); + + Lottos lottos = new Lottos(input); + + input.add(new Lotto(List.of(7,8,9,10,11,12))); // 외부 변경 + + assertThatThrownBy(() -> lottos.values().add(new Lotto(List.of(13,14,15,16,17,18)))) + .isInstanceOf(UnsupportedOperationException.class); + } +} diff --git a/src/test/java/lotto/RankTest.java b/src/test/java/lotto/RankTest.java index c6000a40cf..756244c790 100644 --- a/src/test/java/lotto/RankTest.java +++ b/src/test/java/lotto/RankTest.java @@ -1,31 +1,44 @@ package lotto; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; public class RankTest { @Test - @DisplayName("일치 개수로 등수를 찾는다 (6개=1등, 5개=3등, 4개=4등, 3개=5등)") - void rankOfMatchCount() { - assertThat(Rank.of(6)).isEqualTo(Rank.FIRST); - assertThat(Rank.of(5)).isEqualTo(Rank.THIRD); - assertThat(Rank.of(4)).isEqualTo(Rank.FOURTH); - assertThat(Rank.of(3)).isEqualTo(Rank.FIFTH); + @DisplayName("6개 일치이면 1등이다") + void firstRank() { + assertThat(Rank.of(new MatchResult(6, false))).isEqualTo(Rank.FIRST); } @Test - @DisplayName("당첨이 아닌 개수는 MISS로 반환한다") - void nonWinningIsMiss() { - assertThat(Rank.of(2)).isEqualTo(Rank.MISS); - assertThat(Rank.of(0)).isEqualTo(Rank.MISS); + @DisplayName("5개 일치 + 보너스 일치이면 2등이다") + void secondRank() { + assertThat(Rank.of(new MatchResult(5, true))).isEqualTo(Rank.SECOND); } @Test - @DisplayName("winningRanks는 MISS를 제외한 당첨 등수만 반환한다") - void winningRanksExcludesMiss() { - assertThat(Rank.winningRanks()).doesNotContain(Rank.MISS); + @DisplayName("5개 일치 + 보너스 불일치이면 3등이다") + void thirdRank() { + assertThat(Rank.of(new MatchResult(5, false))).isEqualTo(Rank.THIRD); + } + + @Test + @DisplayName("4개 일치이면 4등이다") + void fourthRank() { + assertThat(Rank.of(new MatchResult(4, false))).isEqualTo(Rank.FOURTH); + } + + @Test + @DisplayName("3개 일치이면 5등이다") + void fifthRank() { + assertThat(Rank.of(new MatchResult(3, false))).isEqualTo(Rank.FIFTH); + } + + @Test + @DisplayName("2개 이하 일치이면 MISS이다") + void missRank() { + assertThat(Rank.of(new MatchResult(2, false))).isEqualTo(Rank.MISS); } } diff --git a/src/test/java/lotto/WinningNumbersTest.java b/src/test/java/lotto/WinningNumbersTest.java new file mode 100644 index 0000000000..70ec2a800a --- /dev/null +++ b/src/test/java/lotto/WinningNumbersTest.java @@ -0,0 +1,46 @@ +package lotto; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class WinningNumbersTest { + + @Test + @DisplayName("보너스 번호는 당첨 번호와 중복될 수 없다") + void bonusMustNotDuplicateWinningNumbers() { + Lotto winning = new Lotto(List.of(1, 2, 3, 4, 5, 6)); + + assertThatThrownBy(() -> new WinningNumbers(winning, 6)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("WinningNumbers.match(ticket)은 Rank를 반환한다") + void matchReturnsRank() { + WinningNumbers winningNumbers = new WinningNumbers(new Lotto(List.of(1,2,3,4,5,6)), 7); + + Rank rank = winningNumbers.match(new Lotto(List.of(1,2,3,4,5,7))); + + assertThat(rank).isEqualTo(Rank.SECOND); + } + + @Test + @DisplayName("WinningNumbers.match(lottos)은 통계를 만들어 반환한다") + void matchLottosReturnsStatistics() { + WinningNumbers winningNumbers = new WinningNumbers(new Lotto(List.of(1,2,3,4,5,6)), 7); + + Lottos tickets = new Lottos(List.of( + new Lotto(List.of(1,2,3,4,5,7)), // SECOND + new Lotto(List.of(1,2,3,4,5,10)) // THIRD + )); + + WinningStatistics stats = winningNumbers.match(tickets); + + assertThat(stats.countOf(Rank.SECOND)).isEqualTo(1); + assertThat(stats.countOf(Rank.THIRD)).isEqualTo(1); + } +} diff --git a/src/test/java/lotto/WinningStatisticsTest.java b/src/test/java/lotto/WinningStatisticsTest.java index 939b7a9724..fc43e69a30 100644 --- a/src/test/java/lotto/WinningStatisticsTest.java +++ b/src/test/java/lotto/WinningStatisticsTest.java @@ -7,36 +7,37 @@ import org.junit.jupiter.api.Test; public class WinningStatisticsTest { + @Test - @DisplayName("로또 결과를 등수별로 집계한다") - void accumulateStatistics() { - Lotto winning = new Lotto(1, 2, 3, 4, 5, 6); - List lottos = List.of( - new Lotto(1, 2, 3, 10, 11, 12), - new Lotto(1, 2, 3, 4, 11, 12) - ); - - WinningStatistics stats = new WinningStatistics(); - stats.accumulate(lottos, winning); - - assertThat(stats.countOf(Rank.FIFTH)).isEqualTo(1); - assertThat(stats.countOf(Rank.FOURTH)).isEqualTo(1); - assertThat(stats.countOf(Rank.THIRD)).isEqualTo(0); - assertThat(stats.countOf(Rank.FIRST)).isEqualTo(0); + @DisplayName("총 당첨금은 등수별 상금 * 당첨횟수의 합이다") + void totalPrize() { + WinningNumbers winningNumbers = new WinningNumbers(new Lotto(List.of(1,2,3,4,5,6)), 7); + + Lottos tickets = new Lottos(List.of( + new Lotto(List.of(1,2,3,4,5,7)), // SECOND + new Lotto(List.of(1,2,3,4,5,10)) // THIRD + )); + + WinningStatistics stats = winningNumbers.match(tickets); + + long expected = (long) Rank.SECOND.prize() + (long) Rank.THIRD.prize(); + assertThat(stats.totalPrize()).isEqualTo(expected); } @Test - @DisplayName("총 당첨금은 등수별 상금의 합이다") - void totalPrizeCalculation() { - Lotto winning = new Lotto(1, 2, 3, 4, 5, 6); - List lottos = List.of( - new Lotto(1, 2, 3, 10, 11, 12), - new Lotto(1, 2, 3, 4, 11, 12) - ); - - WinningStatistics stats = new WinningStatistics(); - stats.accumulate(lottos, winning); - - assertThat(stats.totalPrize()).isEqualTo(55_000); + @DisplayName("수익률은 총 당첨금을 구입 금액으로 나눈 값이다") + void profitRate() { + WinningNumbers winningNumbers = new WinningNumbers(new Lotto(List.of(1,2,3,4,5,6)), 7); + + Lottos tickets = new Lottos(List.of( + new Lotto(List.of(1,2,3,4,5,7)) // SECOND + )); + + WinningStatistics stats = winningNumbers.match(tickets); + + Money purchase = Money.of(10_000); + double expected = (double) Rank.SECOND.prize() / purchase.amount(); + + assertThat(stats.profitRate(purchase)).isEqualTo(expected); } }