diff --git a/README.md b/README.md index 15bb106b5..ca3657838 100644 --- a/README.md +++ b/README.md @@ -1 +1,80 @@ -# javascript-lotto-precourse +# 3주차 프리코스 과제 : 로또 +- 간단한 로또 발매기 구현 + +## 기본 기능 +- 구입 금액을 입력하단 해당 금액만큼 로또 발행 +- 6개의 당첨 번호와 보너스 번호 입력받아 당첨 통계 계산 +- 수익률 계산 후 출력 + +## 기능 요구사항 +1. 로또 구입 + - 사용자에게 구입 금액 입력 + - 구입 금액은 1,000원 단위의 정수 + - 구입 금액에 맞는 개수만큼 로또 발행 + - 한 장당 로또 번호는 1~45 사이의 중복되지 않은 6개의 숫자 + - 발행된 로또 번호를 오름차순으로 출력 +2. 당첨 번호 및 보너스 번호 입력 + - 당첨 번호 6개 입력(쉼표(,)로 구분) + - 당첨 번호는 1~45 사이의 중복되지 않은 숫자 + - 보너스 번호 1개를 추가로 입력 + - 보너스 번호는 당첨 번호와 중복되지 않아야 함 +3. 당첨 결과 + - 구매한 각 로또의 당첨 개수 계산 + - 당첨 기준에 따라 등수 판별 + - 각 등수별 당첨 횟수 출력 + +## 예외 처리 +- 구입 금액이 숫자가 아닐 경우 -> [ERROR] 구입 금액은 숫자여야 합니다. +- 구입 금액은 1,000원 단위가 아닐 경우 -> [ERROR] 구입 금액은 1,000원 단위여야 합니다 +- 당첨 번호와 보너스 번호가 숫자가 아닐 경우 -> [ERROR] 로또 번호는 숫자여야 합니다. +- 당첨 번호의 구분자가 쉼표가 아닐 경우 -> [ERROR] 당첨 번호의 구분자는 쉼표(,)여야 합니다. +- 당첨 번호와 보너스 번호는 1 ~ 45의 값 사이가 아닐 경우 -> [ERROR] 로또 번호는 1~45 사이의 값이어야 합니다. +- 당첨 번호가 중복될 경우 -> [ERROR] 중복된 번호가 있습니다. +- 보너스 번호가 당첨 번호와 중복된 경우 -> [ERROR] 보너스 번호는 당첨 번호와 중복될 수 없습니다. + +## 실행 예시 + ----------------------------------------- + 구입금액을 입력해 주세요. + 8000 + + 8개를 구매했습니다. + [8, 21, 23, 41, 42, 43] + [3, 5, 11, 16, 32, 38] + [7, 11, 16, 35, 36, 44] + [1, 8, 11, 31, 41, 42] + [13, 14, 16, 38, 42, 45] + [7, 11, 30, 40, 42, 43] + [2, 13, 22, 32, 38, 45] + [1, 3, 5, 14, 22, 45] + ----------------------------------------- + 당첨 번호를 입력해 주세요. + 1,2,3,4,5,6 + + 보너스 번호를 입력해 주세요. + 7 + ----------------------------------------- + 당첨 통계 + + 3개 일치 (5,000원) - 1개 + 4개 일치 (50,000원) - 0개 + 5개 일치 (1,500,000원) - 0개 + 5개 일치, 보너스 볼 일치 (30,000,000원) - 0개 + 6개 일치 (2,000,000,000원) - 0개 + 총 수익률은 62.5%입니다. + ----------------------------------------- + +## 구현 목록 +- [ ] 구입금액 입력 +- [ ] 구입 금액에 맞는 로또 수량 및 번호 출력 +- [ ] 당첨 번호와 보너스 번호 입력 +- [ ] 당첨 통계 출력 +- [ ] 예외처리 + +## 소감문 +이번 3주차 프리코스를 수행하면서 피드백을 최대한 반영하려고 노력했지만, 아직 부족한 부분이 많다는 것을 느꼈다. +먼저 README 파일을 작성할 때는 이전보다 조금 더 상세히 작성하려 노력했지만, 기능 구현 목록 작성과 기능 설명 능력이 아직 부족하다고 생각했다. +그래서 앞으로는 다른 사람들의 README 예시를 참고하면서 표현력과 구조를 발전시켜 나갈 계획이다. +또한 기능 목록을 주기적으로 업데이트하면서 진행했어야 했는데, 마지막 소감문을 작성하며 그 부분을 놓쳤다는 것을 깨달아 아쉬움이 남았다. +반면, 하드 코딩을 피하기 위해 의미 있는 상수 선언을 사용한 점은 스스로 칭찬하고 싶다. +이러한 과정을 통해 코드의 가독성과 유지보수성이 얼마나 달라지는지를 체감할 수 있었다. +앞으로는 README 파일을 다시 리뷰하며 더 구조적이고 이해하기 쉬운 문서로 다듬고, 값을 직접 입력하는 하드 코딩 대신 상수나 변수를 활용해 더 좋은 코드로 발전해 나가고자 한다. \ No newline at end of file diff --git a/src/App.js b/src/App.js index 091aa0a5d..2643a4ad8 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,135 @@ +import{Console,MissionUtils} from "@woowacourse/mission-utils"; +import Lotto from "./Lotto.js"; + class App { - async run() {} + async run() { + //5.예외처리 + try{ + const inputPrice = await this.getInputPrice(); + //1.1 구입금액에 맞는 개수만큼 로또 수 + const count = inputPrice/1000; + Console.print(`${count}개를 구매했습니다.`); + + //2.구입 금액에 맞는 로또 수량 및 번호 출력 + const lottos = this.lottoNum(count); + lottos.forEach((lotto) => Console.print(`[${lotto.join(", ")}]`)); + + //3.당첨 번호와 보너스 번호 입력 + const prizeNum = await this.getPrizeNum(); + const bonusNum = await this.getBonusNum(prizeNum); + + //4.당첨 통계 출력 + const prizeStats = this.printPrizeStats(lottos,prizeNum,bonusNum,inputPrice); + }catch(error){ + Console.print(error.message); + } + + } + + async getInputPrice(){ + //1.구입금액 입력받기 + Console.print("구입금액을 입력해 주세요."); + const input = await Console.readLineAsync(""); + if(isNaN(input)){ + throw new Error("[ERROR] 구입 금액은 숫자여야 합니다."); + } + const inputNumber = Number(input); + if(inputNumber <= 0){ + throw new Error("[ERROR] 구입 금액은 0원보다 커야 합니다."); + } + if(!(inputNumber % 1000 === 0)){ + throw new Error("[ERROR] 구입 금액은 1,000원 단위여야 합니다."); + } + return inputNumber; + } + + lottoNum(count){ + //2.1 금액에 맞는 로또 갯수만큼 로또 생성 후 랜덤값 삽입 + let lottoList = []; + + Array.from({length:count}, () => { + const lotto = MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6); + lotto.sort((a,b) => a - b); + lottoList.push(lotto); + }) + return lottoList; + } + + async getPrizeNum(){ + //3.1 당첨 번호 입력받기 + Console.print("당첨 번호를 입력해 주세요."); + const input = await Console.readLineAsync(""); + + if(!input.includes(",")){ + throw new Error("[ERROR] 당첨 번호는 쉼표(,)로 구분된 숫자여야 합니다."); + } + + const prizeNumbers = input.split(",").map(Number); + return prizeNumbers; + } + + async getBonusNum(prizeNum){ + //3.2 보너스 번호 입력받기 + Console.print("보너스 번호를 입력해 주세요."); + const input = await Console.readLineAsync(""); + const bonusNumber = Number(input); + if (isNaN(bonusNumber)) { + throw new Error("[ERROR] 보너스 번호는 숫자여야 합니다."); + } + if (bonusNumber < 1 || bonusNumber > 45) { + throw new Error("[ERROR] 보너스 번호는 1부터 45 사이여야 합니다."); + } + if (prizeNum.includes(bonusNumber)) { + throw new Error("[ERROR] 보너스 번호는 당첨 번호와 중복될 수 없습니다."); + } + return bonusNumber; + } + + printPrizeStats(lottos,prizeNum,bonusNum,inputPrice){ + const PRIZE_MONEY = { + 1:2000000000, + 2:30000000, + 3:1500000, + 4:50000, + 5:5000 + }; + let result = { + 1:0, + 2:0, + 3:0, + 4:0, + 5:0 + }; + + lottos.forEach((lotto) => { + const matchCount = lotto.filter((num) => prizeNum.includes(num)).length; + const hasBonus = lotto.includes(bonusNum); + + if(matchCount == 6) result[1]++; + else if(matchCount == 5 && hasBonus) result[2]++; + else if(matchCount == 5) result[3]++; + else if(matchCount == 4) result[4]++; + else if(matchCount == 3) result[5]++; + }); + + Console.print("당첨 통계\n---"); + Console.print(`3개 일치 (5,000원) - ${result[5]}개`); + Console.print(`4개 일치 (50,000원) - ${result[4]}개`); + Console.print(`5개 일치 (1,500,000원) - ${result[3]}개`); + Console.print(`5개 일치, 보너스 볼 일치 (30,000,000원) - ${result[2]}개`); + Console.print(`6개 일치 (2,000,000,000원) - ${result[1]}개`); + + const totalPrize = + PRIZE_MONEY[1] * result[1] + + PRIZE_MONEY[2] * result[2] + + PRIZE_MONEY[3] * result[3] + + PRIZE_MONEY[4] * result[4] + + PRIZE_MONEY[5] * result[5]; + + const rate = (totalPrize / inputPrice * 100).toFixed(1); + Console.print(`총 수익률은 ${rate}%입니다.`); + return rate; + } } export default App; diff --git a/src/Lotto.js b/src/Lotto.js index cb0b1527e..999ba4467 100644 --- a/src/Lotto.js +++ b/src/Lotto.js @@ -7,12 +7,31 @@ class Lotto { } #validate(numbers) { + if(!Array.isArray(numbers)){ + throw new Error("[ERROR] 로또 번호는 배열 형태여야 합니다"); + } if (numbers.length !== 6) { throw new Error("[ERROR] 로또 번호는 6개여야 합니다."); } + numbers.forEach((num) => { + if (typeof num !== "number" || isNaN(num)) { + throw new Error("[ERROR] 로또 번호는 숫자여야 합니다."); + } + if (num < 1 || num > 45) { + throw new Error("[ERROR] 로또 번호는 1부터 45 사이여야 합니다."); + } + }); + + const uniqueNumbers = new Set(numbers); + if (uniqueNumbers.size !== 6) { + throw new Error("[ERROR] 중복된 번호가 있습니다."); + } + } + + getNumbers() { + return this.#numbers; } - // TODO: 추가 기능 구현 } export default Lotto;