diff --git a/README.md b/README.md index 9594b6c872..ae6fd62db1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,32 @@ # java-lotto - 로또 미션 저장소 - # [미션 리드미](https://github.com/talmood/private-mission-README/tree/main/%EB%AF%B8%EC%85%98%203%20-%20%EB%A1%9C%EB%98%90) + + +# 기능 목록 정리 + +## 입력 +* 값을 입력받을 수 있도록 한다. + * 구매 금액, 당첨 번호, 보너스 볼 + +## 출력 +* 값을 출력하도록 한다. + +## 로또 생성 +* 구매 금액을 받아서 몇 개의 로또를 생성할지 계산한다. +* 로또 번호를 생성한다. + * 1부터 45중 6개로 구성 + +## 당첨 조회 +* 당첨 여부를 조회한다. + + +## 당첨 통계 +* 일치하는 번호의 수 별로 통계를 구한다. + * 3개~6개까지 일치하는 경우, 5개 + 보너스 볼 일치 +* 수익률 계산 + * 3개 일치 5000원 + * 4개 일치 5000원 + * 5개 일치 150000원 + * 5개+보너스볼 30000000원 + * 6개 일치 2000000000원 diff --git a/build.gradle b/build.gradle index a0a012f579..35b3babf16 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,11 @@ repositories { dependencies { testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2' + // Mockito Dependency + testImplementation 'org.mockito:mockito-core:3.9.0' + + // Mockito JUnit 5 Integration + testImplementation 'org.mockito:mockito-junit-jupiter:3.9.0' } java { diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 0000000000..b3c1347c4b --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,6 @@ +public class Application { + public static void main(String[] args) { + LottoProcessor lottoProcessor = new LottoProcessor(new InputView(), new ResultView()); + lottoProcessor.processGame(); + } +} diff --git a/src/main/java/GameCountDecider.java b/src/main/java/GameCountDecider.java new file mode 100644 index 0000000000..3f27c26971 --- /dev/null +++ b/src/main/java/GameCountDecider.java @@ -0,0 +1,7 @@ +public class GameCountDecider { + + static final Integer LOTTO_PRICE = 1000; + public Integer calculateGameForPrice(Integer price) { + return price/LOTTO_PRICE; + } +} diff --git a/src/main/java/InputView.java b/src/main/java/InputView.java new file mode 100644 index 0000000000..5f7166c785 --- /dev/null +++ b/src/main/java/InputView.java @@ -0,0 +1,31 @@ +import java.util.Scanner; + +public class InputView { + Scanner scanner = new Scanner(System.in); + + public void guideToPutPurchasePrice() { + System.out.println("구매금액을 입력해주세요"); + } + public void guideToPutWinningNumbers() { + System.out.println("지난 주 당첨 번호를 입력해 주세요."); + } + + public void guideToPutBonusNumber() { + System.out.println("보너스 볼을 입력해 주세요."); + } + + public String acceptInput() { + return scanner.nextLine(); + } + + public Integer validatePurchasePrice(String input) { + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + System.out.println("숫자를 입력해주세요"); + return null; + } + } + + +} diff --git a/src/main/java/Lotto.java b/src/main/java/Lotto.java new file mode 100644 index 0000000000..222e8f0a7c --- /dev/null +++ b/src/main/java/Lotto.java @@ -0,0 +1,40 @@ +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Lotto { + + public void setLottoGames(List lottoGames) { + LottoGames = lottoGames; + } + + List LottoGames; + + public List getLottoGames() { + return new ArrayList<>(LottoGames); + } + + public Lotto(Integer gameCount) { + this.LottoGames = new ArrayList<>(); + for (int i = 0; i < gameCount; i++) { + LottoGame lottoGame = createLottoGame(); + this.LottoGames.add(lottoGame); + } + } + + private LottoGame createLottoGame() { + int minimumLottoValue = 1; + int maximumLottoValue = 46; + int lottoNumberCount = 6; + List potentialLottoNumbers = new ArrayList<>(); + for (int i = minimumLottoValue; i <= maximumLottoValue; i++) { + potentialLottoNumbers.add(i); + } + Collections.shuffle(potentialLottoNumbers); + List gameNumbers = new ArrayList<>(potentialLottoNumbers.subList(0, lottoNumberCount)); + Collections.sort(gameNumbers); + + return new LottoGame(gameNumbers); + } + +} diff --git a/src/main/java/LottoGame.java b/src/main/java/LottoGame.java new file mode 100644 index 0000000000..9ad90b321a --- /dev/null +++ b/src/main/java/LottoGame.java @@ -0,0 +1,20 @@ +import java.util.ArrayList; +import java.util.List; + +public class LottoGame { + private final List gameNumbers; + + public LottoGame(List gameNumbers) { + this.gameNumbers = new ArrayList<>(gameNumbers); + } + + @Override + public String toString() { + return gameNumbers.toString(); + } + + public List getGameNumbers() { + return gameNumbers; + } + +} diff --git a/src/main/java/LottoProcessor.java b/src/main/java/LottoProcessor.java new file mode 100644 index 0000000000..a969925f31 --- /dev/null +++ b/src/main/java/LottoProcessor.java @@ -0,0 +1,76 @@ +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class LottoProcessor { + + private final InputView inputView; + + private final ResultView resultView; + + public LottoProcessor(InputView inputView, ResultView resultView) { + this.inputView = inputView; + this.resultView = resultView; + } + + public void processGame() { + + Integer price = acceptPriceForLottoGame(); + Integer gameCount = purchaseGamesByPrice(price); + Lotto lotto = createLottoByGameCount(gameCount); + WinningLottoNumber winningLottoNumber = acceptWinningNumbers(); + Winners winners = selectWinners(lotto, winningLottoNumber); + showWinnerStatistics(winners, price); + } + + private void showWinnerStatistics(Winners winners, Integer price) { + resultView.printWinnerStatisticsMessage(); + ProfitCalculator profitCalculator = new ProfitCalculator(winners, price); + resultView.printWinnerStatistics(winners, profitCalculator); + } + + + public Lotto createLottoByGameCount(Integer gameCount) { + Lotto lotto = new Lotto(gameCount); + resultView.printLottoGameNumbers(lotto); + return lotto; + } + + public Integer purchaseGamesByPrice(Integer price) { + GameCountDecider gameCountDecider = new GameCountDecider(); + Integer gameCount = gameCountDecider.calculateGameForPrice(price); + resultView.printPurchasedGameCount(gameCount); + + return gameCount; + } + + private int acceptPriceForLottoGame() { + inputView.guideToPutPurchasePrice(); + Integer price; + + do { + String stringInput = inputView.acceptInput(); + price = inputView.validatePurchasePrice(stringInput); + } while (price == null); + + return price; + } + + private WinningLottoNumber acceptWinningNumbers() { + inputView.guideToPutWinningNumbers(); + String stringWinningNumbers = inputView.acceptInput(); + List winningNumbers = Arrays.stream(stringWinningNumbers.split(",")) + .map(String::trim) + .map(Integer::parseInt).collect(Collectors.toList()); + + inputView.guideToPutBonusNumber(); + int bonusNumber = Integer.parseInt(inputView.acceptInput()); + + return new WinningLottoNumber(winningNumbers, bonusNumber); + } + + public Winners selectWinners(Lotto lotto, WinningLottoNumber winningLottoNumber) { + WinnerSelector winnerSelector = new WinnerSelector(lotto, winningLottoNumber); + return winnerSelector.getWinners(); + } +} diff --git a/src/main/java/ProfitCalculator.java b/src/main/java/ProfitCalculator.java new file mode 100644 index 0000000000..eeb122b90f --- /dev/null +++ b/src/main/java/ProfitCalculator.java @@ -0,0 +1,25 @@ +public class ProfitCalculator { + + final static Integer FIRST_PRIZE = 2000000000; + final static Integer SECOND_PRIZE = 30000000; + final static Integer THIRD_PRIZE = 1500000; + final static Integer FOURTH_PRIZE = 50000; + final static Integer FIFTH_PRIZE = 5000; + + private float profit = 0; + + public ProfitCalculator(Winners winners, Integer investMoney) { + Integer totalWinningMoney = + winners.getFirstWinner() * FIRST_PRIZE + + winners.getSecondWinner() * SECOND_PRIZE + + winners.getThirdWinner() * THIRD_PRIZE + + winners.getFourthWinner() * FOURTH_PRIZE + + winners.getFifthWinner() * FIFTH_PRIZE; + + this.profit = totalWinningMoney.floatValue() / investMoney.floatValue(); + } + + public float getProfit() { + return profit; + } +} diff --git a/src/main/java/ResultView.java b/src/main/java/ResultView.java new file mode 100644 index 0000000000..e050b6906a --- /dev/null +++ b/src/main/java/ResultView.java @@ -0,0 +1,31 @@ +import java.util.List; + +public class ResultView { + public void printPurchasedGameCount(Integer gameCount) { + System.out.println(gameCount + "개를 구매했습니다."); + } + + public void printLottoGameNumbers(Lotto lotto) { + List lottoGames = lotto.getLottoGames(); + for (int i = 0; i < lottoGames .size(); i++) { + System.out.println(lottoGames.get(i)); + } + } + + public void printWinnerStatisticsMessage() { + System.out.println("당첨 통계"); + System.out.println("---------"); + } + + public void printWinnerStatistics(Winners winners, ProfitCalculator profitCalculator) { + float profit = profitCalculator.getProfit(); + + System.out.println("3개 일치 (" + profitCalculator.FIFTH_PRIZE + "원)- " + winners.getFifthWinner()+"개"); + System.out.println("4개 일치 (" + profitCalculator.FOURTH_PRIZE + "원)- " + winners.getFourthWinner()+"개"); + System.out.println("5개 일치 (" + profitCalculator.THIRD_PRIZE + "원)- " + winners.getThirdWinner()+"개"); + System.out.println("5개 일치, 보너스 볼 일치 (" + profitCalculator.SECOND_PRIZE + "원)- " + winners.getSecondWinner()+"개"); + System.out.println("6개 일치 (" + profitCalculator.FIRST_PRIZE + "원)- " + winners.getFirstWinner()+"개"); + System.out.println("profit = " + profit); + System.out.println(String.format("총 수익률은 %.2f 입니다.", profit)); + } +} diff --git a/src/main/java/WinnerSelector.java b/src/main/java/WinnerSelector.java new file mode 100644 index 0000000000..4f40da4743 --- /dev/null +++ b/src/main/java/WinnerSelector.java @@ -0,0 +1,89 @@ +import java.util.List; + +public class WinnerSelector { + private final Winners winners; + + public Winners getWinners() { + return winners; + } + + public WinnerSelector(Lotto lotto, WinningLottoNumber winningLottoNumber) { + int firstWinner = 0; + int secondWinner = 0; + int thirdWinner = 0; + int fourthWinner = 0; + int fifthWinner = 0; + + List winningNumbers = winningLottoNumber.getWinningNumbers(); + Integer bonusNumber = winningLottoNumber.getBonusNumber(); + + for (LottoGame lottoGame : lotto.getLottoGames()) { + Integer matchCount = 0; + + List gameNumbers = lottoGame.getGameNumbers(); + matchCount = findWinningNumberInGameNumber(matchCount, winningNumbers, gameNumbers); + + firstWinner = getFirstWinner(firstWinner, matchCount); + secondWinner = getSecondWinner(secondWinner, bonusNumber, matchCount, gameNumbers); + thirdWinner = getThirdWinner(thirdWinner, bonusNumber, matchCount, gameNumbers); + fourthWinner = getFourthWinner(fourthWinner, matchCount); + fifthWinner = getFifthWinner(fifthWinner, matchCount); + } + this.winners = new Winners(firstWinner, secondWinner, thirdWinner, fourthWinner, fifthWinner); + } + + private int getFifthWinner(int fifthWinner, Integer matchCount) { + if (matchCount == 3) { + fifthWinner++; + } + return fifthWinner; + } + + private int getFourthWinner(int fourthWinner, Integer matchCount) { + if (matchCount == 4) { + fourthWinner++; + } + return fourthWinner; + } + + private int getThirdWinner(int thirdWinner, Integer bonusNumber, Integer matchCount, List gameNumbers) { + if (matchCount == 5 && !isBonusNumberMatch(gameNumbers, bonusNumber)) { + thirdWinner++; + } + return thirdWinner; + } + + private int getSecondWinner(int secondWinner, Integer bonusNumber, Integer matchCount, List gameNumbers) { + if (matchCount == 5 && isBonusNumberMatch(gameNumbers, bonusNumber)) { + secondWinner++; + } + return secondWinner; + } + + private int getFirstWinner(int firstWinner, Integer matchCount) { + if (matchCount == 6) { + firstWinner++; + } + return firstWinner; + } + + + private boolean isBonusNumberMatch(List gameNumbers, Integer bonusNumber) { + return gameNumbers.contains(bonusNumber); + } + + private Integer findWinningNumberInGameNumber(Integer matchCount, List winningNumbers, List gameNumbers) { + for (Integer number : gameNumbers) { + matchCount = increaseMatchCountIfIncludeGameNumber(matchCount, winningNumbers, number); + } + return matchCount; + } + + private Integer increaseMatchCountIfIncludeGameNumber(Integer matchCount, List winningNumbers, Integer number) { + if (winningNumbers.contains(number)) { + matchCount++; + } + return matchCount; + } + +} diff --git a/src/main/java/Winners.java b/src/main/java/Winners.java new file mode 100644 index 0000000000..b7731bd598 --- /dev/null +++ b/src/main/java/Winners.java @@ -0,0 +1,35 @@ +public class Winners { + private final Integer firstWinner; + private final Integer secondWinner; + private final Integer thirdWinner; + private final Integer fourthWinner; + private final Integer fifthWinner; + + public Winners(Integer firstWinner, Integer secondWinner, Integer thirdWinner, Integer fourthWinner, Integer fifthWinner) { + this.firstWinner = firstWinner; + this.secondWinner = secondWinner; + this.thirdWinner = thirdWinner; + this.fourthWinner = fourthWinner; + this.fifthWinner = fifthWinner; + } + + public Integer getFirstWinner() { + return firstWinner; + } + + public Integer getSecondWinner() { + return secondWinner; + } + + public Integer getThirdWinner() { + return thirdWinner; + } + + public Integer getFourthWinner() { + return fourthWinner; + } + + public Integer getFifthWinner() { + return fifthWinner; + } +} diff --git a/src/main/java/WinningLottoNumber.java b/src/main/java/WinningLottoNumber.java new file mode 100644 index 0000000000..986d8cb23f --- /dev/null +++ b/src/main/java/WinningLottoNumber.java @@ -0,0 +1,20 @@ +import java.util.ArrayList; +import java.util.List; + +public class WinningLottoNumber { + private final List winningNumbers; + private final Integer bonusNumber; + + public WinningLottoNumber(List winningNumbers, Integer bonusNumber) { + this.winningNumbers = new ArrayList<>(winningNumbers); + this.bonusNumber = bonusNumber; + } + + public List getWinningNumbers() { + return winningNumbers; + } + + public Integer getBonusNumber() { + return bonusNumber; + } +} diff --git a/src/test/java/LottoProcessorTest.java b/src/test/java/LottoProcessorTest.java new file mode 100644 index 0000000000..bd5378b04b --- /dev/null +++ b/src/test/java/LottoProcessorTest.java @@ -0,0 +1,73 @@ +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.verify; + + +class LottoProcessorTest { + + private ResultView resultView; + private InputView inputView; + private LottoProcessor lottoProcessor; + + @BeforeEach + void setUp() { + resultView = Mockito.mock(ResultView.class); + inputView = Mockito.mock(InputView.class); + lottoProcessor = new LottoProcessor(inputView, resultView); + } + + + @Test + void purchaseGamesByPrice() { + + Integer price = 14000; + Integer gameCount = lottoProcessor.purchaseGamesByPrice(price); + assertEquals(14, gameCount); + + verify(resultView).printPurchasedGameCount(gameCount); + } + + @Test + void createLottoByGameCount() { + Integer gameCount = 14; + + Lotto lotto = lottoProcessor.createLottoByGameCount(gameCount); + assertEquals(gameCount, lotto.getLottoGames().size()); + + verify(resultView).printLottoGameNumbers(lotto); + } + + @Test + void selectWinners() { + Integer gameCount = 5; + + Lotto lotto = new Lotto(gameCount); + + List lottoGames = List.of( + new LottoGame(List.of(1, 2, 3, 4, 5, 6)), + new LottoGame(List.of(1, 2, 3, 4, 5, 7)), + new LottoGame(List.of(1, 2, 3, 4, 5, 16)), + new LottoGame(List.of(1, 2, 3, 4, 15, 16)), + new LottoGame(List.of(1, 2, 3, 14, 15, 16)) + ); + lotto.setLottoGames(lottoGames); + List winningNumbers = List.of(1, 2, 3, 4, 5, 6); + Integer bonusNumber = 7; + + WinningLottoNumber winningLottoNumber = new WinningLottoNumber(winningNumbers, bonusNumber); + Winners winners = lottoProcessor.selectWinners(lotto, winningLottoNumber); + + assertEquals(winners.getFirstWinner(), 1); + assertEquals(winners.getSecondWinner(), 1); + assertEquals(winners.getThirdWinner(), 1); + assertEquals(winners.getFourthWinner(), 1); + assertEquals(winners.getFifthWinner(), 1); + + + } +} \ No newline at end of file