Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[로또] 이예림 미션 제출합니다. #1346

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
97 changes: 97 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,98 @@
# java-lotto-precourse

### 🔑 한 줄 소개

1장에 1000원 짜리 로또를 구입한 금액에 맞춰 랜덤하고 중복이 없는 6자리 수를 발행해주면, 이를 당첨 번호와 보너스 번호에 기반하여 당첨 결과(당첨 등수, 수익률)을 알려준다.

<br/>

### 📑 구현 사항

#### 1. 기본 세팅
- jdk 21 버전 설정
- java style guide 설정

#### 2. 구입 금액 입력
- readLine() 으로 문자열 입력 받기
- 입력 값에 양의 정수 이외의 값이 오는 경우 IllegalArgumentException 발생 및 에러 메세지 형식에 따라 출력
- 입력 값이 1000 단위가 아닌 경우 IllegalArgumentException 발생 및 에러 메세지 형식에 따라 출력
- IlleagalArgumentException 발생 시, 재입력 받기

#### 3. 로또 장수 계산 및 출력
- 금액에 따라 로또 장수 계산
- 구매 장수 출력

#### 4. 로또 발행 및 출력
- 중복이 아닌 랜덤한 6개의 수 발급
- 구매 장수만큼 출력

#### 5. 로또 당첨 번호 및 보너스 번호 입력
- 당첨 번호 입력 받기
- 당첨 번호 입력 문자열에 6개 초과의 수를 입력한 경우 IllegalArgumentException 발생 및 에러 메세지 형식에 따라 출력
- 당첨 번호 입력 문자열에 중복된 수가 나오는 경우 IllegalArgumentException 발생 및 에러 메세지 형식에 따라 출력
- 당첨 번호 입력 문자열에 `,`와 숫자 이외의 값이 입력된 경우, IllegalArgumentException 발생 및 에러 메세지 형식에 따라 출력
- 당첨 번호 입력 문자열에 1 ~ 45 사이의 수가 아닌 수가 입력된 경우, `IllegalArgumentException` 발생 및 에러 메세지 형식에 따라 출력

- 보너스 번호 입력 받기
- 당첨 번호 입력 값이 당첨 번호 리스트 안에 있는 값인 경우(중복된 값이 있는 경우) IllegalArgumentException 발생 및 에러 메세지 형식에 따라 출력
- 당첨 번호 입력 문자열에 숫자 이외의 값이 입력된 경우, IllegalArgumentException 발생 및 에러 메세지 형식에 따라 출력
- 당첨 번호 입력 문자열에 1 ~ 45 사이의 수가 아닌 수가 입력된 경우, `IllegalArgumentException` 발생 및 에러 메세지 형식에 따라 출력
- IlleagalArgumentException 발생 시, 재입력 받기

#### 6. 당첨 통계 출력
- 발행한 로또 번호와 당첨 번호 일치 개수 확인
- 발행한 로또 번호 일치 개수 별 통계 출력

#### 7. 수익률 계산 및 출력
- (수익금 / 구매 금액) * 100 결과에서 소수점 둘째자리 반올림하여 수익률 출력

#### 8. 테스트 코드 작성
[성공]
- 8000 / 1,2,3,4,5,6 / 7 입력 시 예시 데이터에 따른 출력 결과 및 수익률 확인
- 7000 / 1,2,3,4,5,6 / 7 입력 시, 4개 일치 경우 1개 발생 시 수익률 출력 결과 2자리 수 반올림 확인

[실패-로또 가격]
- 로또 가격이 1000 원 단위가 아닌 경우, ERROR 메세지 포함 여부 검사
- 로또 가격에 양의 정수 이외의 값이 오는 경우, ERROR 메세지 포함 여부 검사

[실패-당첨 번호]
- 당첨 범호 입력 시 `,`와 숫자 이외의 값이 입력된 경우, ERROR 메세지 포함 여부 검사

[실패-보너스 번호]
- 당첨 범호 입력 시 숫자 이외의 값이 입력된 경우, ERROR 메세지 포함 여부 검사
- 보너스 번호 입력 시 당첨 번호와 중복된 수가 나오는 경우, ERROR 메세지 포함 여부 검사

[실패-로또]
- 6개 초과의 수를 입력한 경우, ERROR 메세지 포함 여부 검사
- 중복된 수가 나오는 경우, ERROR 메세지 포함 여부 검사
- 1 ~ 45 사이의 수가 아닌 수가 입력된 경우, ERROR 메세지 포함 여부 검사

<br/>

### ✅ 조건
- 로또 번호
- [ ] 로또 번호의 숫자 범위는 1 ~ 45 사이에만 존재함
- [ ] 로또 번호는 오름차순으로 정렬하여 보여줌
- [ ] 1개의 로또 발행(6개의 번호 추출) 시, 당첨번호 6개 추첨 시 중복된 숫자가 나오면 안됨
- 보너스 번호의 경우도 당첨번호와 중복된 숫자이면 안됨

- 로또 가격
- [ ] 로또 가격에 양의 정수 이외의 값이 오는 경우, `IllegalArgumentException` 발생 및 `[ERROR]` 로 시작하는 메세지 출력
- [ ] 로또 가격이 1000 단위가 아닐 경우, `IllegalArgumentException` 발생 및 `[ERROR]` 로 시작하는 메세지 출력

- 당첨 번호
- [ ] 당첨 번호는 `,`를 기준으로 구분됨
- [ ] 당첨 번호 입력 시 6개 초과의 수를 입력한 경우, `IllegalArgumentException` 발생 및 `[ERROR]` 로 시작하는 메세지 출력
- [ ] 당첨 번호 입력 시 중복된 수가 나오는 경우, `IllegalArgumentException` 발생 및 `[ERROR]` 로 시작하는 메세지 출력
- [ ] 당첨 번호 입력 시 `,`와 숫자 이외의 값이 입력된 경우, `IllegalArgumentException` 발생 및 `[ERROR]` 로 시작하는 메세지 출력
- [ ] 당첨 번호 입력 시 1 ~ 45 사이의 수가 아닌 수가 입력된 경우, `IllegalArgumentException` 발생 및 `[ERROR]` 로 시작하는 메세지 출력

- 보너스 번호
- [ ] 보너스 번호 입력 시 당첨 번호와 중복된 수가 나오는 경우, `IllegalArgumentException` 발생 및 `[ERROR]` 로 시작하는 메세지 출력
- [ ] 보너스 번호 입력 시 숫자 이외의 값이 입력된 경우, `IllegalArgumentException` 발생 및 `[ERROR]` 로 시작하는 메세지 출력
- [ ] 보너스 번호 입력 시 입력 시 1 ~ 45 사이의 수가 아닌 수가 입력된 경우, `IllegalArgumentException` 발생 및 `[ERROR]` 로 시작하는 메세지 출력

- 수익률
- [ ] 소수점 둘째 자리에서 반올림한 값으로 출력


110 changes: 109 additions & 1 deletion src/main/java/lotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,115 @@
package lotto;

import static lotto.InputUtil.checkInputIsNotInNumbers;
import static lotto.InputUtil.checkOverThanMinAndLessThanMax;
import static lotto.InputUtil.isClearedByNum;
import static lotto.InputUtil.isNumeric;
import static lotto.InputUtil.isNumericWithCommas;
import static lotto.InputUtil.splitByComma;
import static lotto.Lotto.LOTTO_BASIC_PRICE;
import static lotto.Lotto.LOTTO_MAX_NUM;
import static lotto.Lotto.LOTTO_MIN_NUM;
import static lotto.Lotto.checkLotto;
import static lotto.Lotto.makeRandomLotto;

import camp.nextstep.edu.missionutils.Console;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Application {

public static final String WINNING_NUMBERS_NAME = "당첨 번호";

public static void main(String[] args) {
// TODO: 프로그램 구현

// 구입 금액 입력 받고 유효성 검사
int purchasesCount;
while (true) {
try {
System.out.println("구입금액을 입력해 주세요.\n");
String price = Console.readLine();

isNumeric(price);
isClearedByNum(price, LOTTO_BASIC_PRICE); // 구입금액이 1000원 단위인지 확인

purchasesCount = Integer.parseInt(price) / LOTTO_BASIC_PRICE;
break;
} catch (IllegalArgumentException e) {
System.out.println("잘못된 입력입니다. 다시 입력해 주세요\n 예외 메세지: " + e.getMessage());
}
}

// 구매 개수 출력
System.out.println(purchasesCount + "개를 구매했습니다.\n");

// 구매한 로또 발행
List<Lotto> purchases = new ArrayList<>();
for (int i = 0; i < purchasesCount; i++) {
purchases.add(makeRandomLotto());
System.out.println(purchases.get(i).getNumbers());
}

// 당첨 로또 번호 입력 받기
Lotto winningLotto;
while (true) {
try {
System.out.println("당첨 번호를 입력해 주세요.\n");
String winningNumbers = Console.readLine();

isNumericWithCommas(winningNumbers);

// 당첨 번호로 로또 만들기
winningLotto = new Lotto(splitByComma(winningNumbers));
break;
} catch (IllegalArgumentException e) {
System.out.println("잘못된 입력입니다. 다시 입력해 주세요\n 예외 메세지: " + e.getMessage());
}
}

int bonusNumber;
// 보너스 번호 입력 받기
while (true) {
try {
System.out.println("보너스 번호를 입력해 주세요.\n");
String enteredBonusNumber = Console.readLine();

isNumeric(enteredBonusNumber);
checkOverThanMinAndLessThanMax(Integer.parseInt(enteredBonusNumber), LOTTO_MIN_NUM,
LOTTO_MAX_NUM);
checkInputIsNotInNumbers(winningLotto.getNumbers(), enteredBonusNumber,
WINNING_NUMBERS_NAME);

bonusNumber = Integer.parseInt(enteredBonusNumber);
break;
} catch (IllegalArgumentException e) {
System.out.println("잘못된 입력입니다. 다시 입력해 주세요\n 예외 메세지: " + e.getMessage());
}
}

ArrayList<Integer> statics = new ArrayList<Integer>(Arrays.asList(0, 0, 0, 0, 0));

// 당첨 내역 출력
for (Lotto lotto : purchases) {
int result = checkLotto(lotto.getNumbers(), winningLotto.getNumbers(), bonusNumber);
if (result >= 0 && result < 5) {
statics.set(result, statics.get(result) + 1);
}
}

System.out.println("3개 일치 (5,000원) - " + statics.get(0) + "개");
System.out.println("4개 일치 (50,000원) - " + statics.get(1) + "개");
System.out.println("5개 일치 (1,500,000원) - " + statics.get(2) + "개");
System.out.println("5개 일치, 보너스 볼 일치 (30,000,000원) - " + statics.get(3) + "개");
System.out.println("6개 일치 (2,000,000,000원) - " + statics.get(4) + "개");

// 수익률 계산 및 출력
int profits = statics.get(0)*5000 + statics.get(1)*50000 + statics.get(2)*1500000 + statics.get(3)*30000000 + statics.get(4)*2000000000;
double rateOfReturn = (profits / (double)(purchasesCount * LOTTO_BASIC_PRICE)) * 100;
double roundedRateOfReturn = Math.round(rateOfReturn * 100) / 100.0;
System.out.println("총 수익률은 " + roundedRateOfReturn + "%입니다.");


}

}
57 changes: 57 additions & 0 deletions src/main/java/lotto/InputUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package lotto;

import java.util.ArrayList;
import java.util.List;

public class InputUtil {
// 정수인지 확인
public static void isNumeric(String input) {
if (input == null || !input.matches("\\d+")) {
throw new IllegalArgumentException("[ERROR] 입력 값 내에 정수 외의 값이 있습니다.");
}
}

// 정수 및 , 인지 확인
public static void isNumericWithCommas(String input) {
if (input == null || !input.matches("[\\d,]+")) {
throw new IllegalArgumentException("[ERROR] 입력 값 내에 정수나 , 외의 값이 있습니다.");
}
}

// 단위 확인
public static void isClearedByNum(String input, int num) {
if ((Integer.parseInt(input) % num) != 0) {
throw new IllegalArgumentException("[ERROR] " + num + "단위의 값만 입력이 가능합니다.");
}
}

// 입력값이 min 이상, max 미만인지 확인하는
public static void checkOverThanMinAndLessThanMax(int i, int min, int max) {
if (!(i >= min && i <= max)) {
throw new IllegalArgumentException("[ERROR] 입력 값은 " + min + "과 " + max + " 사이여야 합니다.");
}
}

public static void checkInputIsNotInNumbers(List<Integer> numbers, String input, String numbersName) {
for (int i : numbers) {
if (i == Integer.parseInt(input)) {
throw new IllegalArgumentException("[ERROR] 해당 값이 " + numbersName + " 내에 존재하면 안됩니다.");
}
}
}

// 문자열 , 기준으로 쪼개기
public static List<Integer> splitByComma(String input) {
List<Integer> numbersList = new ArrayList<>();

if (input != null && !input.isEmpty()) {
String[] parts = input.split(",");
for (String part : parts) {
numbersList.add(Integer.parseInt(part.trim()));
}
}

return numbersList;
}

}
101 changes: 97 additions & 4 deletions src/main/java/lotto/Lotto.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,113 @@
package lotto;

import static lotto.InputUtil.checkOverThanMinAndLessThanMax;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.function.Supplier;

public class Lotto {
public static final int LOTTO_SIZE = 6;
public static final int LOTTO_MAX_NUM = 45;
public static final int LOTTO_MIN_NUM = 1;
public static final int LOTTO_BASIC_PRICE = 1000;

private final List<Integer> numbers;

public Lotto(List<Integer> numbers) {
validate(numbers);
validateSixCount(numbers);
validateNoDuplicates(numbers);
validateCoverage(numbers);
this.numbers = numbers;
}

private void validate(List<Integer> numbers) {
if (numbers.size() != 6) {
// 랜덤 로또 생성
public static Lotto makeRandomLotto(){
return new Lotto(generateUniqueRandomNumbers());
}

public List<Integer> getNumbers() {
return numbers;
}

// 6자리 숫자인지 확인
private void validateSixCount(List<Integer> numbers) {
if (numbers.size() != LOTTO_SIZE) {
throw new IllegalArgumentException("[ERROR] 로또 번호는 6개여야 합니다.");
}
}

// TODO: 추가 기능 구현
// 중복된 값이 있는지 확인
private void validateNoDuplicates(List<Integer> numbers) {
Set<Integer> uniqueNumbers = new HashSet<>(numbers);
if (uniqueNumbers.size() != numbers.size()) {
throw new IllegalArgumentException("[ERROR] 중복된 숫자가 있습니다.");
}
}

// 리스트 내의 모든 숫자가 범위 내인지 확인
private void validateCoverage(List<Integer> numbers) {
for (int i : numbers) {
checkOverThanMinAndLessThanMax(i, LOTTO_MIN_NUM, LOTTO_MAX_NUM);
}
}

// 랜덤 번호 생성
private static List<Integer> generateUniqueRandomNumbers() {
Set<Integer> uniqueNumbers = new HashSet<>();
Random random = new Random();

while (uniqueNumbers.size() < LOTTO_SIZE) {
int randomNumber = random.nextInt(LOTTO_MAX_NUM - LOTTO_MIN_NUM + 1) + LOTTO_MIN_NUM;
uniqueNumbers.add(randomNumber);
}

return new ArrayList<>(uniqueNumbers);
}

// 당첨 번호 비교
private static int countMatchingNumbers(List<Integer> uniqueNumbers, List<Integer> winningNumbers) {
Set<Integer> unique = new HashSet<>(uniqueNumbers);
Set<Integer> winning = new HashSet<>(winningNumbers);

unique.retainAll(winning);
return unique.size();
}

// 보너스 번호와 일치 비교
private static boolean isBonusMatched(List<Integer> uniqueNumbers, int bonusNumber) {
return uniqueNumbers.contains(bonusNumber);
}

// 일치 개수와 보너스 일치 여부에 따른 결과 반환
public static int getPrizeForMatchCount(int matchCount, boolean bonusMatch) {
if (matchCount == 6) {
return 4;
}
if (matchCount == 5 && bonusMatch) {
return 3;
}
if (matchCount == 5) {
return 2;
}
if (matchCount == 4) {
return 1;
}
if (matchCount == 3) {
return 0;
}
return -1;
}

// 최종 결과 계산 함수
public static int checkLotto(List<Integer> uniqueNumbers, List<Integer> winningNumbers, int bonusNumber) {
int matchCount = countMatchingNumbers(uniqueNumbers, winningNumbers);
boolean bonusMatch = isBonusMatched(uniqueNumbers, bonusNumber);
return getPrizeForMatchCount(matchCount, bonusMatch);
}


}
Loading