diff --git a/README.md b/README.md index 15bb106b5..b41b73c78 100644 --- a/README.md +++ b/README.md @@ -1 +1,43 @@ # javascript-lotto-precourse + +### 기능 구현 목록 + +1. 입력 + + 1-1. 로또 구입 금액 입력 + + - 1,000원 단위로 입력 받으며 1,000원으로 나누어 떨어지지 않는 경우 예외 처리 + + 1-2. 당첨 번호 입력 + + - 6개의 당첨 번호, 쉼표(,)를 기준으로 구분 + + 1-3. 보너스 번호 입력 + +2. 기능 + + 2-1. 발행해야 할 로또 개수 구하기 + + 2-2. 로또 숫자 뽑기 + + - 중복되지 않는 6개의 숫자 + + - 6개의 숫자와 중복되지 않는 보너스 번호 1개 뽑기 + + 2-3. 로또 당첨 여부 확인 + + - 4개 일치하는 경우, 보너스 볼과 일치하는지 확인 + +3. 출력 + + 3-1. 발행한 로또 수량 및 번호 출력 + + - 오름차순으로 정렬하여 출력 + + 3-2. 당첨 내역 출력 + + 3-3. 수익률 출력 + + - 소수점 둘째 자리에서 반올림 + + 3-4. 로또 게임 종료 diff --git a/src/App.js b/src/App.js index 091aa0a5d..196156fd6 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,60 @@ +import { Console, MissionUtils } from '@woowacourse/mission-utils'; +import { numOfLotto, chkSelectedNum } from '../src/InputComponent.js'; +import { verifyLotto, showLottoStatistics, CalculateRate } from '../src/VerifyLotto.js'; +import Lotto from './Lotto.js'; + class App { - async run() {} + async run() { + try { + // 로또 구입금액 입력 + Console.print('구입금액을 입력해 주세요.'); + const price = await Console.readLineAsync(''); + const lotto = numOfLotto(price); // 로또 개수 + if (lotto == 0) this.throwError('로또 구입 금액 입력 오류'); + + // 로또 구매 + const lottoArr = []; + Console.print(`\n${lotto}개를 구매했습니다.`); + for (let i = 0; i < lotto; i++) { + const sortedNumbers = MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6).sort((a, b) => a - b); + const lottoInstance = new Lotto(sortedNumbers); + lottoArr.push(lottoInstance.getNumbers()); + } + for (let i = 0; i < lotto; i++) { + Console.print(lottoArr[i]); + } + + // 당첨 번호 입력 + Console.print('\n당첨 번호를 입력해 주세요.'); + const numbers = (await Console.readLineAsync('')).split(','); + let selectedNum = chkSelectedNum(numbers); + if (!selectedNum) this.throwError('당첨 번호 입력 오류'); + const selectedNumInstance = new Lotto(selectedNum); + selectedNumInstance.validateRange(); + selectedNum = selectedNumInstance.getNumbers(); + + // 보너스 번호 입력 + Console.print('\n보너스 번호를 입력해 주세요.'); + const bonusNum = parseInt(await Console.readLineAsync('')); + + // 당첨 통계 출력 + Console.print('\n당첨 통계'); + Console.print('---'); + const result = verifyLotto(lottoArr, selectedNum, bonusNum); + showLottoStatistics(result); + + // 수익률 계산 및 출력 + const gainRate = CalculateRate(parseInt(price), result); + Console.print(`총 수익률은 ${gainRate}%입니다.`); + } catch (error) { + Console.print(error.message); + throw error; + } + } + + throwError(message) { + throw new Error(`[ERROR] ${message}`); + } } export default App; diff --git a/src/InputComponent.js b/src/InputComponent.js new file mode 100644 index 000000000..fa4398acf --- /dev/null +++ b/src/InputComponent.js @@ -0,0 +1,15 @@ +import { Console } from '@woowacourse/mission-utils'; + +export function numOfLotto(price) { + if (price % 1000 != 0) return 0; + return price / 1000; +} + +export function chkSelectedNum(num) { + const result = []; + for (let i = 0; i < num.length; i++) { + let trimmed = num[i].trim(); + if (trimmed !== '') result.push(trimmed); + } + if (result.length == 6) return result; +} diff --git a/src/Lotto.js b/src/Lotto.js index cb0b1527e..76033890b 100644 --- a/src/Lotto.js +++ b/src/Lotto.js @@ -1,18 +1,44 @@ class Lotto { - #numbers; + #numbers; - constructor(numbers) { - this.#validate(numbers); - this.#numbers = numbers; - } + constructor(numbers) { + this.#validate(numbers); + this.#numbers = numbers; + } + + #validate(numbers) { + if (numbers.length !== 6) { + throw new Error('[ERROR] 로또 번호는 6개여야 합니다.'); + } + this.validateDuplicate(numbers); + } + + // TODO: 추가 기능 구현 + // 로또 번호가 1부터 45 사이의 숫자가 아닌 경우 + validateRange() { + for (let i = 0; i < 6; i++) { + if (this.#numbers[i] < 1 || this.#numbers[i] > 45) { + } + } + } + + // 중복되는 숫자가 있는 경우 + validateDuplicate(numbers) { + const uniqueNumbers = new Set(numbers); + if (uniqueNumbers.size !== numbers.length) { + throw new Error('[ERROR] 로또 번호는 중복되지 않는 숫자여야 합니다.'); + } + } - #validate(numbers) { - if (numbers.length !== 6) { - throw new Error("[ERROR] 로또 번호는 6개여야 합니다."); + // 정수형으로 변환 + #changeToInt() { + this.#numbers = this.#numbers.map((num) => parseInt(num, 10)); } - } - // TODO: 추가 기능 구현 + getNumbers() { + this.#changeToInt(this.#numbers); + return this.#numbers; + } } export default Lotto; diff --git a/src/VerifyLotto.js b/src/VerifyLotto.js new file mode 100644 index 000000000..cda11c2b2 --- /dev/null +++ b/src/VerifyLotto.js @@ -0,0 +1,49 @@ +import { Console } from '@woowacourse/mission-utils'; + +export function verifyLotto(lottoArr, correctNum, bonus) { + const result = Array(7).fill(0); + + for (let i = 0; i < lottoArr.length; i++) { + let outcome = numOfCorrected(lottoArr[i], correctNum); + // 보너스 번호를 포함하는 경우 + if (outcome == 5 && chkBonusLotto(lottoArr[i], bonus)) { + result[2] += 1; + continue; + } + if (outcome > 2) result[outcome] += 1; + } + return result; +} + +function numOfCorrected(nums, correctNum) { + let cnt = 0; + for (let i = 0; i < 6; i++) { + if (nums.includes(correctNum[i])) cnt++; + } + return cnt; +} + +function chkBonusLotto(nums, bonus) { + for (let i = 0; i < 6; i++) { + if (nums[i] == bonus) return true; + } + return false; +} + +export function showLottoStatistics(arr) { + Console.print(`3개 일치 (5,000원) - ${arr[3]}`); + Console.print(`4개 일치 (50,000원) - ${arr[4]}`); + Console.print(`5개 일치 (1,500,000원) - ${arr[5]}`); + Console.print(`5개 일치, 보너스 볼 일치 (30,000,000원) - ${arr[2]}`); + Console.print(`6개 일치 (2,000,000,000원) - ${arr[6]}`); +} + +export function CalculateRate(pay, arr) { + let total = 0; + if (arr[2] != 0) total += 30000000 * arr[2]; + if (arr[3] != 0) total += 5000 * arr[3]; + if (arr[4] != 0) total += 50000 * arr[4]; + if (arr[5] != 0) total += 1500000 * arr[5]; + if (arr[6] != 0) total += 2000000000 * arr[6]; + return total / pay; +}