diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index d190922ba44..1831ded6219 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,7 +1,9 @@ package lotto; public class Application { - public static void main(String[] args) { + public static void main(String[] args) throws MyException { // TODO: 프로그램 구현 + Money money = new Money(); + money.showMain(); } } diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java index 519793d1f73..69c425dfc4f 100644 --- a/src/main/java/lotto/Lotto.java +++ b/src/main/java/lotto/Lotto.java @@ -1,20 +1,34 @@ package lotto; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class Lotto { private final List numbers; - public Lotto(List numbers) { + public Lotto(List numbers) throws MyException { validate(numbers); + checkRange(numbers); this.numbers = numbers; } - private void validate(List numbers) { + // 6자리인지 검사 + private void validate(List numbers) throws IllegalArgumentException { if (numbers.size() != 6) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("[ERROR] 로또 숫자는 6자리여야 합니다."); } } // TODO: 추가 기능 구현 + + // 범위 확인 + private void checkRange(List numbers) throws MyException { + for (Integer number : numbers) { + if (number < 1 || number > 45) { + throw new MyException("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다."); + } + } + } } diff --git a/src/main/java/lotto/Money.java b/src/main/java/lotto/Money.java new file mode 100644 index 00000000000..2e8a8905ec1 --- /dev/null +++ b/src/main/java/lotto/Money.java @@ -0,0 +1,70 @@ +package lotto; + +import camp.nextstep.edu.missionutils.Randoms; +import camp.nextstep.edu.missionutils.Console; +import org.assertj.core.api.ThrowableAssert; + +import java.util.*; + +public class Money { + public void showMain() { + getMoney(); + } + + // 구입금액 입력받기 + public ThrowableAssert.ThrowingCallable getMoney() { + try { + System.out.println("구입금액을 입력해주세요."); + int money = Integer.parseInt(Console.readLine()); + getModulo(money); + showNum(money / 1000); + } catch (MyException e) { // 예외처리 반복 https://woojin.tistory.com/74 참고 + System.out.println("[ERROR] 금액이 1000으로 나누어 떨어지지 않습니다."); + getMoney(); + } catch (NumberFormatException e) { // 숫자가 아닐 경우도 예외처리 + System.out.println("[ERROR] 숫자를 입력해주세요."); + getMoney(); + } + return null; + } + + // 1000으로 나누어 떨어지지 않으면 exception + public ThrowableAssert.ThrowingCallable getModulo(int money) throws MyException { + if (money % 1000 != 0) { + throw new MyException(); + } + return null; + } + + // 로또 번호 셀렉 + public ThrowableAssert.ThrowingCallable showNum(int money) throws MyException { + System.out.println(); + System.out.println(money + "개를 구매했습니다."); + List[] lotto = new List[money * 6]; + + for (int i = 0; i < money; i++) { + List list = selectNum(); + lotto[i] = list; + System.out.println(lotto[i]); + } + + WinNum win = new WinNum(); + win.showWin(lotto); + return null; + } + + // 랜덤 숫자 고르고 Lotto로 넘김 + private List selectNum() throws MyException { + // https://qh5944.tistory.com/152 참고 -, UnsupportedOperationException 해결 + List numbers = Randoms.pickUniqueNumbersInRange(1, 45, 6); + List result = new ArrayList<>(); + result.addAll(numbers); + Collections.sort(result); + try { + new Lotto(result); // Lotto에서 6개, 범위, 중복까지 확인 + } catch (MyException | IllegalArgumentException e) { + selectNum(); + } + return result; + } +} diff --git a/src/main/java/lotto/MyException.java b/src/main/java/lotto/MyException.java new file mode 100644 index 00000000000..ef61aea4557 --- /dev/null +++ b/src/main/java/lotto/MyException.java @@ -0,0 +1,10 @@ +package lotto; +// https://staticclass.tistory.com/72 참고 +public class MyException extends java.lang.Exception { + public MyException() { + } + + public MyException(String errMoney) { + super(errMoney); + } +} diff --git a/src/main/java/lotto/Result.java b/src/main/java/lotto/Result.java new file mode 100644 index 00000000000..69bf2517082 --- /dev/null +++ b/src/main/java/lotto/Result.java @@ -0,0 +1,101 @@ +package lotto; + +import org.assertj.core.api.ThrowableAssert; + +import java.util.*; + +public class Result { + public void showResult(List[] lotto, List win, int bonus) { + HashMap count = count(lotto, win, bonus); + HashMap result = result(count); + double percent = rate(count, lotto.length / 6); + printResult(result, percent); + } + + // 맞춘갯수끼리 map + private HashMap count(List[] lotto, List win, int bonus) { + HashMap map = new HashMap<>(); + for (int i = 0; i < lotto.length / 6; i++) { + int correct = count(lotto[i], win); + if (correct == 5 && countBonus(lotto[i], bonus)) { + correct = 7; // 보너스까지 맞았다면 7로 설정 + } + // mapping, https://gymdev.tistory.com/39 참고 + map.put(correct, map.getOrDefault(correct, 0) + 1); + } + return map; + } + + // 복권 한개당 일치하는 숫자 확인 + public int count(List lotto, List win) { + int correct = 0; + for (int i = 0; i < lotto.size(); i++) { + if(countBonus(lotto, win.get(i))){ + correct++; + } + } + return correct; + } + + // 5개+보너스숫자 확인 + private boolean countBonus(List lotto, int bonus) { + for (Integer integer : lotto) { + if (integer == bonus) { + return true; + } + } + return false; + } + + // 순서대로 결과 mapping, https://tosuccess.tistory.com/138 참고 + private HashMap result(HashMap count) { + LinkedHashMap result = new LinkedHashMap<>(); + for (PRICE price : PRICE.values()) { + result.put(price.getRank(), 0); + if (count.containsKey(price.getNumber())) { + result.replace(price.getRank(), count.get(price.getNumber())); + } + } + return result; + } + + // 수익률 구하기 + private double rate(HashMap result, int n) { + double avg = (double) (result.getOrDefault(3, 0) * 5 + result.getOrDefault(4, 0) * 50 + + result.getOrDefault(5, 0) * 1500 + result.getOrDefault(7, 0) * 30000 + + result.getOrDefault(6, 0) * 2000000) / n * 100; + return avg; + } + + // 결과 출력 + private void printResult(HashMap result, double percent) { + for (String key : result.keySet()) { + System.out.println(key + result.get(key) + "개"); + } + System.out.printf("총 수익률은 %3.1f%%입니다.", percent); + } +} + +enum PRICE { + FIFTH(3, "3개 일치 (5,000원) - "), + FOURTH(4, "4개 일치 (50,000원) - "), + THIRD(5, "5개 일치 (1,500,000원) - "), + SECOND(7, "5개 일치, 보너스 볼 일치 (30,000,000원) - "), + FIRST(6, "6개 일치 (2,000,000,000원) - "); + + final private int number; + final private String rank; + + public int getNumber() { + return number; + } + + public String getRank() { + return rank; + } + + private PRICE(int number, String text) { + this.number = number; + this.rank = text; + } +} diff --git a/src/main/java/lotto/WinNum.java b/src/main/java/lotto/WinNum.java new file mode 100644 index 00000000000..7b390c617be --- /dev/null +++ b/src/main/java/lotto/WinNum.java @@ -0,0 +1,68 @@ +package lotto; + +import camp.nextstep.edu.missionutils.Console; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.InputMismatchException; +import java.util.List; + +public class WinNum { + public void showWin(List[] lotto) { + List win = selectResult(); + int bonus = getBonus(); + + Result result = new Result(); + result.showResult(lotto, win, bonus); + } + + // 당첨 숫자 입력받기 + private List selectResult() { + List result = new ArrayList<>(); + try { + System.out.println("\n당첨 번호를 입력해 주세요."); + String[] num = Console.readLine().split(","); + result = toList(num); + new Lotto(result); + } catch (IllegalArgumentException | MyException e) { + System.out.println(e.getMessage()); + selectResult(); + } + return result; + } + + // 문자열 정수형으로 변환하며, Lotto에서 확인 + private List toList(String[] s) { + List result = new ArrayList<>(s.length); + try { + for (String string : s) { + result.add(Integer.parseInt(string)); + } + Collections.sort(result); + } catch (NumberFormatException e) { + System.out.println("[ERROR] 숫자가 아닙니다."); + selectResult(); + } + return result; + } + + // 보너스 번호 입력받기 + private int getBonus() { + try { + System.out.println("\n보너스 번호를 입력해 주세요."); + int num = Integer.parseInt(Console.readLine()); + validate(num); + return num; + } catch (InputMismatchException | IllegalArgumentException e) { + System.out.println("[ERROR] " + e.getMessage()); + getBonus(); + } + return 0; + } + + private void validate(int num) { + if (num < 1 || num > 45) { + throw new IllegalArgumentException("보너스 번호는 1~45 사이의 숫자여야 합니다."); + } + } +} diff --git a/src/test/java/lotto/ApplicationTest.java b/src/test/java/lotto/ApplicationTest.java index a15c7d1f522..b70200c8915 100644 --- a/src/test/java/lotto/ApplicationTest.java +++ b/src/test/java/lotto/ApplicationTest.java @@ -56,6 +56,10 @@ class ApplicationTest extends NsTest { @Override public void runMain() { - Application.main(new String[]{}); + try { + Application.main(new String[]{}); + } catch (MyException e) { + throw new RuntimeException(e); + } } } diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java index 9f5dfe7eb83..008583d63d9 100644 --- a/src/test/java/lotto/LottoTest.java +++ b/src/test/java/lotto/LottoTest.java @@ -24,4 +24,10 @@ void createLottoByDuplicatedNumber() { } // 아래에 추가 테스트 작성 가능 + @DisplayName("로또 번호가 1부터 45 사이의 숫자가 아니라면 예외 발생한다.") + @Test + void checkRange() { + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 46))) + .isInstanceOf(IllegalArgumentException.class); + } } \ No newline at end of file diff --git a/src/test/java/lotto/MoneyTest.java b/src/test/java/lotto/MoneyTest.java new file mode 100644 index 00000000000..2dd29946e37 --- /dev/null +++ b/src/test/java/lotto/MoneyTest.java @@ -0,0 +1,26 @@ +package lotto; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class MoneyTest { + @DisplayName("구입 금액 입력받기") + @Test + void nonNumericTest() throws MyException { + Money money = new Money(); + assertThatThrownBy(money.getModulo(50)) + .isInstanceOf(MyException.class); + } + + @DisplayName("랜덤숫자 보여주기") + @Test + void showRandomNumber() throws MyException { + Money money = new Money(); + assertThatThrownBy(money.showNum(8)) + .isInstanceOf(MyException.class); + } +}