diff --git a/README.md b/README.md index 15bb106b5..1becf96b0 100644 --- a/README.md +++ b/README.md @@ -1 +1,27 @@ # javascript-lotto-precourse + +우아한 테크코스 3주차 + +## 기능 요구 사항 + +### 1. 사용자 입력 + +- [x] 로또 구입 금액을 입력받는다. + - [ ] 구입 금액은 1,000원 단위로 입력 받으며 1,000원으로 나누어 떨어지지 않는 경우 예외 처리한다. +- [x] 당첨 번호를 쉼표(,)를 기준으로 구분하여 입력 받는다. +- [x] 보너스 번호를 입력 받는다. + +### 2. 로또 실행 + +- [x] 로또 번호의 숫자 범위는 1~45까지이다. +- [ ] 로또 범위의 숫자 범위 내에서 중복되지 않는 6개의 숫자를 뽑는다. +- [x] 보너스 번호 1개의 숫자를 뽑는다. +- [x] 구입 금액에 해당하는 만큼 로또를 발행한다. + +### 3. 출력 + +- [x] 발행한 로또 수량 및 번호를 출력한다. +- [x] 로또 번호는 오름차순으로 정렬하여 보여준다. +- [x] 당첨 내역을 출력한다. +- [x] 수익률을 계산하여 출력한다. 수익률은 소수점 둘째 자리에서 반올림한다. (ex. 100.0%, 51.5%, 1,000,000.0%) +- [ ] 예외 상황 시 에러 문구를 출력해야 한다. 단, 에러 문구는 "[ERROR]"로 시작해야 한다. diff --git a/src/App.js b/src/App.js index 091aa0a5d..322a45d93 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,9 @@ +import { startLotto } from "./LottoProgram.js"; + class App { - async run() {} + async run() { + await startLotto(); + } } export default App; diff --git a/src/Constant.js b/src/Constant.js new file mode 100644 index 000000000..94f9706cd --- /dev/null +++ b/src/Constant.js @@ -0,0 +1,16 @@ +export const PROGRAM_MESSAGES = { + PRICE_PROMPT: "구입금액을 입력해 주세요.", + TRY_NUM_PROMPT: "개를 구매했습니다.", + WINNING_NUMBER_PROMPT: "\n" + "당첨 번호를 입력해 주세요.", + BONUS_NUMBER_PROMPT: "\n" + "보너스 번호를 입력해 주세요.", +}; + +export const STATICS_MESSAGE = { + RESULT_MESSAGE: "\n" + "당첨 통계" + "\n" + "---" + "\n", + THREE_MATCHES: "3개 일치 (5,000원) - ", + FOUR_MATCHES: "4개 일치 (50,000원) - ", + FIVE_MATCHES: "5개 일치 (1,500,000원) - ", + BONUS_MATCHES: "5개 일치, 보너스 볼 일치 (30,000,000원) - ", + SIX_MATCHES: "6개 일치 (2,000,000,000원) - ", + RATE_OF_RETURN: (rate) => `총 수익률은 ${rate}%입니다.`, +}; diff --git a/src/InputHandler.js b/src/InputHandler.js new file mode 100644 index 000000000..12a9420f4 --- /dev/null +++ b/src/InputHandler.js @@ -0,0 +1,14 @@ +import { Console } from "@woowacourse/mission-utils"; + +class InputHandler { + static async input(message) { + const price = await this.#readInput(message + "\n"); + return price; + } + + static #readInput(message) { + return Console.readLineAsync(message); + } +} + +export default InputHandler; diff --git a/src/Lotto.js b/src/Lotto.js index cb0b1527e..d31ae3b5c 100644 --- a/src/Lotto.js +++ b/src/Lotto.js @@ -1,3 +1,5 @@ +import OutputHandler from "./OutputHandler.js"; + class Lotto { #numbers; @@ -13,6 +15,17 @@ class Lotto { } // TODO: 추가 기능 구현 -} + // 로또 출력 함수 + displayNumbers() { + OutputHandler.output("[" + this.#numbers.join(", ") + "]"); + } + + countMatchNumber(winningNumbers) { + const matchNum = this.#numbers.filter((num) => + winningNumbers.includes(num) + ); + return matchNum.length; + } +} export default Lotto; diff --git a/src/LottoProgram.js b/src/LottoProgram.js new file mode 100644 index 000000000..443ace7e7 --- /dev/null +++ b/src/LottoProgram.js @@ -0,0 +1,109 @@ +import InputHandler from "./InputHandler.js"; +import OutputHandler from "./OutputHandler.js"; +import { PROGRAM_MESSAGES, STATICS_MESSAGE } from "./Constant.js"; +import { validatePrice } from "./Validation.js"; +import { MissionUtils } from "@woowacourse/mission-utils"; + +import Lotto from "./Lotto.js"; + +export const startLotto = async () => { + //로또 구입 금액을 입력받는다. + const price = await InputHandler.input(PROGRAM_MESSAGES.PRICE_PROMPT); + const tryNum = price / 1000; + // 구입 금액은 1,000원 단위로 입력 받으며 1,000원으로 나누어 떨어지지 않는 경우 예외 처리한다. + validatePrice(tryNum); + + // 로또 몇장인지 출력 + OutputHandler.output("\n" + tryNum + PROGRAM_MESSAGES.TRY_NUM_PROMPT); + //tryNum 만큼의 로또를 발행 + const lottoNumbers = getLottoNumbers(tryNum); + + // 당첨 번호를 쉼표(,)를 기준으로 구분하여 입력 받는다. + let winningNumbers = await InputHandler.input( + PROGRAM_MESSAGES.WINNING_NUMBER_PROMPT + ); + winningNumbers = winningNumbers.split(",").map((num) => num.trim()); + + //로또 번호 검증 ( 6개인지 ) + const validateLotto = new Lotto(winningNumbers); + + //보너스 번호 입력 + const bonusNumber = await InputHandler.input( + PROGRAM_MESSAGES.BONUS_NUMBER_PROMPT + ); + + //당첨통계 + OutputHandler.output(STATICS_MESSAGE.RESULT_MESSAGE); + getStatics(lottoNumbers, winningNumbers, bonusNumber, price); +}; + +export const getLottoNumbers = (tryNum) => { + const lottoNumbersArray = []; + for (let i = 0; i < tryNum; i++) { + const numbers = MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6); + //오름차순 정렬 + const lotto = new Lotto(numbers.sort((a, b) => a - b)); + lotto.displayNumbers(); + lottoNumbersArray.push(lotto); + } + return lottoNumbersArray; +}; + +export const getStatics = ( + lottoNumbers, + winningNumbers, + bonusNumber, + price +) => { + const result = { + threeMatches: 0, + fourMatches: 0, + fiveMatches: 0, + fiveBonusMatches: 0, + sixMatches: 0, + }; + let income = 0; + let rate = 0; + lottoNumbers.forEach((num) => { + const matchCount = num.countMatchNumber(winningNumbers); + + if (matchCount === 3) { + result.threeMatches += 1; + income += 5000; + } else if (matchCount === 4) { + result.fourMatches += 1; + income += 50000; + } else if (matchCount === 5) { + if (num.numbers.includes(bonusNumber)) { + result.fiveBonusMatches += 1; + income += 30000000; + } else { + result.fiveMatches += 1; + income += 1500000; + } + } else if (matchCount === 6) { + result.sixMatches += 1; + income += 2000000000; + } + }); + + OutputHandler.output( + `${STATICS_MESSAGE.THREE_MATCHES} ${result.threeMatches}개` + ); + OutputHandler.output( + `${STATICS_MESSAGE.FOUR_MATCHES} ${result.fourMatches}개` + ); + OutputHandler.output( + `${STATICS_MESSAGE.FIVE_MATCHES} ${result.fiveMatches}개` + ); + OutputHandler.output( + `${STATICS_MESSAGE.BONUS_MATCHES} ${result.fiveBonusMatches}개` + ); + OutputHandler.output(`${STATICS_MESSAGE.SIX_MATCHES} ${result.sixMatches}개`); + + if (income !== 0) { + rate = (income / price) * 100; + } + + OutputHandler.output(STATICS_MESSAGE.RATE_OF_RETURN(rate.toFixed(2))); +}; diff --git a/src/OutputHandler.js b/src/OutputHandler.js new file mode 100644 index 000000000..6b3bf8217 --- /dev/null +++ b/src/OutputHandler.js @@ -0,0 +1,12 @@ +import { Console } from "@woowacourse/mission-utils"; + +class OutputHandler { + static async output(message) { + Console.print(message); + } + static async staticOutput(matchNum, winners, message) { + Console.print(matchNum + message + winners); + } +} + +export default OutputHandler; diff --git a/src/Validation.js b/src/Validation.js new file mode 100644 index 000000000..9b1c51628 --- /dev/null +++ b/src/Validation.js @@ -0,0 +1,5 @@ +export const validatePrice = (try_num) => { + if (!Number.isInteger(try_num)) { + throw Error("[ERROR] 입력 금액이 1,000원 단위가 아닙니다."); + } +};