diff --git a/README.md b/README.md index 15bb106b5..0daf7b9c5 100644 --- a/README.md +++ b/README.md @@ -1 +1,78 @@ -# javascript-lotto-precourse +# 우테코 프리코스 3주차 [로또] +## 과제 수행 체크리스트 +#### 개발 과정 준수 사항 +- [ ] 코드 포매팅 사용 +- [ ] README.MD 기능 목록 수시로 업데이트 +- [ ] 자바스크립트 코드 컨벤션 준수 +- [ ] 코드의 목적이 들어나는 변수명 고려 + +#### 코드 준수 사항 +- [ ] let 선언 줄이기, const 사용 +- [ ] 접근제어자 표시 +- [ ] 값 하드 코딩 금지, 상수화 +- [ ] 들여쓰기 depth 2이내 +- [ ] else 문 지양 +- [ ] 함수 길이 15라인 이내 유지 (한 가지 기능) +- [ ] 구현 순서 준수(클래스 : 필드, 생성자, 매서드 순) +- [ ] 메서드가 한 가지 기능을 담당하는지 확인하는 기준 세우기 (ex: 15줄 이내) +- [ ] 예외 처리 (validation) 를 따로 분류하여 관리하기 + +#### 테스트 코드 준수 사항 +- [ ] 테스트를 작성하는 이유에 대해 생각하기 +- [ ] Jest 이용 테스트코드 +- [ ] 기능의 단위 테스트 작성 (UI(System.out, System.in, Scanner) 로직은 제외한다.) + +## 기능 구현 목록 +### 입력 +- #### 사용자 입력 + - [x] 로또 구입 금액을 입력한다. + - [x] 당첨번호를 입력한다. + - [x] 보너스 번호를 입력한다. +- #### 입력 처리 + - [x] 로또 구입 금액을 통해 로또 구입 개수 계산한다. + - [x] 당첨 번호 입력을 구분자로 구분한다. + - [x] 사용자의 구입 금액을 저장한다. + - [x] 사용자의 당첨 번호를 저장한다. + - [x] 사용자의 보너스 번호를 저장한다. + +- #### 입력 조건 + - 로또 구입 금액은 1000원 단위로 입력 받는다. + - 당첨 번호는 쉼표를 기준으로 구분한다. + - 로또 번호는 1에서 45사이의 숫자여야 한다. + - 로또 번호의 중복이 있으면 안된다. (보너스도 마찬가지) + - 로또 번호는 6자리이다. + - 보너스 번호는 숫자 하나이다. + +- #### 입력 예외 처리 목록 + - 구입 금액 + - [x] 로또 구입 금액이 숫자가 아닌 값이 입력된 경우. + - [x] 로또 구입 금액이 1000원 단위가 아닐 경우. + - [x] 로또 구입 금액이 소수인 경우. + - 로또 번호 + - [x] 로또 번호 중 숫자가 아닌 특수문자가 있을 경우. 로또 번호 입력형식에 맞지 않은 경우. + - [x] 로또 번호가 1에서 45사이의 숫자가 아닐 경우. + - [x] 로또 번호에 중복이 있을 경우. + 보너스 번호 포함. + - [x] 로또 번호가 보너스 제외하고 6자리가 아닐 경우. + - [x] 로또 번호가 소수일 경우 + - 보너스 번호 + - [ ] 보너스 번호가 숫자가 아닌 특수문자일 경우. + - [x] 보너스 번호가 1에서 45사이의 숫자가 아닐 경우. + - [ ] 보너스 번호가 중복일 경우 + - [x] 보너스 번호가 소수일 경우 + +### 로또 게임 진행 +- #### 당첨 로또 번호 생성 + - [x] 랜덤한 당첨 번호를 생성한다. +- #### 사용자의 로또 당첨 여부 확인 + - [x] 사용자의 당첨 번호와 보너스 번호를 비교했을 때 일치하는 개수를 구한다. +- #### 사용자의 로또 게임 결과 판단 + - [x] 사용자의 로또가 몇 등 당첨인지 판단한다. + - [x] 당첨 금액 및 투입 금액에 따른 사용자의 수익률을 계산한다. + +### 출력 +- #### 안내 메시지 + - [x] 구매한 로또의 개수를 출력한다. + - [x] 당첨 통계 안내 메시지를 출력한다. +- #### 로또 결과 출력 + - [x] 정해진 당첨 등수를 모두 출력한다. 그와 함께 사용자가 각 등수에 당첨된 개수를 출력한다. + - [x] 사용자의 수익률을 출력한다. diff --git a/package-lock.json b/package-lock.json index 3243a564f..6253c0aab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2459,12 +2459,13 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -2909,10 +2910,11 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -3146,6 +3148,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -4023,12 +4026,13 @@ "dev": true }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -4666,6 +4670,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -6600,12 +6605,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "browserslist": { @@ -6903,9 +6908,9 @@ } }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -7741,12 +7746,12 @@ "dev": true }, "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" } }, diff --git a/src/App.js b/src/App.js index 091aa0a5d..f166398af 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,22 @@ +import {Console} from '@woowacourse/mission-utils' +import LottoGameController from './LottoGameController.js' +import LottoGameView from './LottoGameView.js' +import LottoGameService from './LottoGameService.js' +import Lotto from './Lotto.js' + class App { - async run() {} -} + controller; + + constructor(){ + this.controller = new LottoGameController( + new LottoGameView(), + new LottoGameService() + ) + } + + async run() { + await this.controller.run(); + } +} -export default App; +export default App; \ No newline at end of file diff --git a/src/Lotto.js b/src/Lotto.js index cb0b1527e..8b83b122a 100644 --- a/src/Lotto.js +++ b/src/Lotto.js @@ -1,18 +1,22 @@ +import {lottoNumberValidatePipe} from './lottoNumberValidatePipe.js' +import { validateLottoNumberAmount, validateLottoOverlap } from "./validate.js" + class Lotto { #numbers; constructor(numbers) { this.#validate(numbers); - this.#numbers = numbers; + this.#numbers = numbers.map(Number).sort((a, b) => a - b); } #validate(numbers) { - if (numbers.length !== 6) { - throw new Error("[ERROR] 로또 번호는 6개여야 합니다."); - } + validateLottoNumberAmount(numbers); + validateLottoOverlap(numbers); + lottoNumberValidatePipe(numbers); + } + getNumbers() { + return this.#numbers; } - - // TODO: 추가 기능 구현 } export default Lotto; diff --git a/src/Lotto/AnswerLotto.js b/src/Lotto/AnswerLotto.js new file mode 100644 index 000000000..b1d6de183 --- /dev/null +++ b/src/Lotto/AnswerLotto.js @@ -0,0 +1,40 @@ +import Lotto from '../Lotto.js' + +import {bonusNumberValidatePipe} from '../bonusNumberValidatePipe.js' +import {validateBonusOverlap} from '../validate.js' + +class AnswerLotto extends Lotto { + #bonus; + #lottoTable; + + constructor(numbers, bonus){ + super(numbers); + + this.#bonus = bonus; + this.applyLottoToTable(numbers); + this.#validateAnswerLotto(bonus); + } + + static create(numbers, bonus) { + return new AnswerLotto(numbers, bonus); + } + #validateAnswerLotto(bonus){ + bonusNumberValidatePipe(bonus); + validateBonusOverlap(this.#lottoTable, bonus); + } + getBonusNumber(){ + return this.#bonus; + } + #setInitialStateLottoTable(){ + this.#lottoTable = Array.from({ length: 45 }, () => 0); + } + applyLottoToTable(numbers) { + this.#setInitialStateLottoTable(); + numbers.forEach((element) => { + this.#lottoTable[element - 1] += 1; + }); + } + +} + +export default AnswerLotto \ No newline at end of file diff --git a/src/Lotto/IssuedLotto.js b/src/Lotto/IssuedLotto.js new file mode 100644 index 000000000..eac534d24 --- /dev/null +++ b/src/Lotto/IssuedLotto.js @@ -0,0 +1,73 @@ +import Lotto from '../Lotto.js' +import {random} from '../utils/random.js' + +class IssuedLotto extends Lotto { + #lottoTable; + #matchNumber; + #winningGrade; + + constructor(numbers) { + super(numbers); + this.#winningGrade = '꼴등'; + this.#matchNumber = 0; + } + + static create(amount) { + return Array.from({length: amount}, () => new IssuedLotto(random.makeUniqueNumbers(1, 45, 6))); + } + getWinningGrade(){ + return this.#winningGrade; + } + #setInitialStateLottoTable(){ + this.#lottoTable = Array.from({ length: 45 }, () => 0); + } + applyLottoToTable(numbers) { + this.#setInitialStateLottoTable(); + numbers.forEach((element) => { + this.#lottoTable[element - 1] += 1; + }); + } + checkWinningIssuedLotto(answer){ + this.#setWinningGrade(answer); + } + #setMatchNumber(answer) { + this.applyLottoToTable(super.getNumbers()); + answer.getNumbers().forEach((element) => { + this.#matchNumber += this.#lottoTable[element-1]; + }); + } + #setWinningGrade(answer) { + this.#setMatchNumber(answer); + if(this.#matchNumber === 6){ + this.#winningGrade = '1등'; + return; + } + if(this.#matchNumber === 5 && this.isMatchedBonus(answer.getBonusNumber())){ + this.#winningGrade = '보너스'; + return; + } + if(this.#matchNumber === 5){ + this.#winningGrade = '2등'; + return; + } + if(this.#matchNumber === 4){ + this.#winningGrade = '3등'; + return; + } + if(this.#matchNumber === 3){ + this.#winningGrade = '4등'; + return; + } + this.#winningGrade = '꼴등'; + return; + } + + isMatchedBonus(bonus){ + return this.#lottoTable[bonus-1] === 1; + } + getLottoTable(){ + return this.#lottoTable; + } +} + +export default IssuedLotto \ No newline at end of file diff --git a/src/LottoGameController.js b/src/LottoGameController.js new file mode 100644 index 000000000..87024db54 --- /dev/null +++ b/src/LottoGameController.js @@ -0,0 +1,78 @@ +import {validateAnswerNumberForm} from './validate.js' +import {purchaseValidatePipe} from './purchaseValidatePipe.js' + +import parser from './utils/parser.js' + +class LottoGameController{ + constructor(view, service){ + this.view = view; + this.service = service + } + + static onGetPurchaseQuantity(purchaseAmount){ + purchaseValidatePipe(purchaseAmount); + return parser.divideThousands(purchaseAmount); + } + static onGetAnswerNumber(answerNumberString){ + validateAnswerNumberForm(answerNumberString); + const answerNumbers = parser.separateString(answerNumberString, ','); + return answerNumbers; + } + + async run(){ + let purchaseQuantity; + let answerNumbers; + let bonusNumber; + + while (true) { + purchaseQuantity = await this.getPurchaseQuantitySafely(); + if (purchaseQuantity !== null) break; + } + while (true) { + answerNumbers = await this.getAnswerNumberSafely(); + if (answerNumbers !== null) break; + } + while (true) { + bonusNumber = await this.getBonusNumberSafely(); + if (bonusNumber !== null) break; + } + + const purchaseAmount = purchaseQuantity * 1000; + this.service.inSetting( + purchaseAmount, + purchaseQuantity, + this.onMakeIssuedLotto.bind(this) + ); + + + this.service.start(answerNumbers, bonusNumber); + const result = this.service.getResult(); + this.view.showLottoGameResult(result); + } + async getPurchaseQuantitySafely() { + try { + return await this.view.getPurchaseQuantity(); + } catch (error) { + return null; + } + } + async getAnswerNumberSafely() { + try { + return await this.view.getAnswerNumber(); + } catch (error) { + return null; + } + } + async getBonusNumberSafely() { + try { + return await this.view.getBonusNumber(); + } catch (error) { + return null; + } + } + onMakeIssuedLotto(purchaseQuantity, issuedLottos){ + this.view.showPurchaseResult(purchaseQuantity, issuedLottos); + } +} + +export default LottoGameController \ No newline at end of file diff --git a/src/LottoGameService.js b/src/LottoGameService.js new file mode 100644 index 000000000..080e3b283 --- /dev/null +++ b/src/LottoGameService.js @@ -0,0 +1,72 @@ +import IssuedLotto from './Lotto/IssuedLotto.js' +import AnswerLotto from './Lotto/AnswerLotto.js' + +const WINNING_GRADE_PRIZE = { + '1등': 2000000000, + '2등': 1500000, + '3등': 50000, + '4등': 5000, + '보너스': 30000000, + '꼴등': 0, +}; + +class LottoGameService{ + #issuedLottos; + #answerLotto; + #winningGradeQuantity; + #purchaseAmount; + #purchaseQuantity; + #totalIncome; + #incomeRate; + + constructor(){ + this.#winningGradeQuantity = { + '1등': 0, + '2등': 0, + '3등': 0, + '4등': 0, + '보너스': 0, + '꼴등': 0, + }; + this.#purchaseAmount = 0; + + } + inSetting(purchaseAmount, purchaseQuantity, onMakeIssuedLotto){ + this.#purchaseAmount = purchaseAmount; + this.#purchaseQuantity= purchaseQuantity; + this.#issuedLottos = IssuedLotto.create(purchaseQuantity); + onMakeIssuedLotto(this.#purchaseQuantity, this.#issuedLottos); + } + start(answerNumbers, bonusNumber){ + this.#answerLotto = AnswerLotto.create(answerNumbers, bonusNumber); + this.#issuedLottos.forEach((issuedLotto) => { + issuedLotto.checkWinningIssuedLotto(this.#answerLotto); + this.#countWinningLotto(issuedLotto); + }); + this.#getTotalIncome(); + this.#calculateIncomeRate(); + } + + #countWinningLotto(issuedLotto){ + this.#winningGradeQuantity[issuedLotto.getWinningGrade()]++; + } + #calculateIncomeRate(){ + this.#incomeRate = this.#totalIncome / this.#purchaseAmount * 100; + } + #getTotalIncome(){ + this.#totalIncome = 0; + Object.keys(this.#winningGradeQuantity).forEach((key) => { + const gradePrize = WINNING_GRADE_PRIZE[key]; + const gradeQuantity = this.#winningGradeQuantity[key]; + this.#totalIncome += gradePrize * gradeQuantity; + }); + } + getResult(){ + return { + gradeQuantity : this.#winningGradeQuantity, + incomeRate : this.#incomeRate + }; + } +} + +export default LottoGameService \ No newline at end of file diff --git a/src/LottoGameView.js b/src/LottoGameView.js new file mode 100644 index 000000000..043c905cb --- /dev/null +++ b/src/LottoGameView.js @@ -0,0 +1,38 @@ +import {Console} from '@woowacourse/mission-utils' +import LottoGameController from './LottoGameController.js' + +class LottoGameView{ + async getPurchaseQuantity(){ + const input = await Console.readLineAsync('구입금액을 입력해 주세요.'); + return LottoGameController.onGetPurchaseQuantity(input); + } + + async getAnswerNumber(){ + const input = await Console.readLineAsync('당첨 번호를 입력해 주세요.'); + return LottoGameController.onGetAnswerNumber(input); + } + + async getBonusNumber(){ + const input = await Console.readLineAsync('보너스 번호를 입력해 주세요.'); + return input; + } + showPurchaseResult(purchaseQuantity, issuedLottos){ + Console.print(`${purchaseQuantity}개를 구매했습니다.`); + issuedLottos.forEach((lotto) => { + Console.print(`[${lotto.getNumbers().join(', ')}]`); + }); + } + showLottoGameResult(result){ + let outputMessages = []; + outputMessages.push('당첨 통계\n---\n'); + outputMessages.push(`3개 일치 (5,000원) - ${result.gradeQuantity['4등']}개\n`); + outputMessages.push(`4개 일치 (50,000원) - ${result.gradeQuantity['3등']}개\n`); + outputMessages.push(`5개 일치 (1,500,000원) - ${result.gradeQuantity['2등']}개\n`); + outputMessages.push(`5개 일치, 보너스 볼 일치 (30,000,000원) - ${result.gradeQuantity['보너스']}개\n`); + outputMessages.push(`6개 일치 (2,000,000,000원) - ${result.gradeQuantity['1등']}개\n`); + outputMessages.push(`총 수익률은 ${result.incomeRate}%입니다.\n`); + const output = outputMessages.join(''); + Console.print(output); + } +} +export default LottoGameView diff --git a/src/bonusNumberValidatePipe.js b/src/bonusNumberValidatePipe.js new file mode 100644 index 000000000..753beefab --- /dev/null +++ b/src/bonusNumberValidatePipe.js @@ -0,0 +1,8 @@ +import {validateBonusNumberOutOfBounds, + validateBonusNumberDecimal, + validateLottoOverlap} from './validate.js' + +export const bonusNumberValidatePipe = (bonusNumber) => { + validateBonusNumberOutOfBounds(bonusNumber); + validateBonusNumberDecimal(bonusNumber); +} \ No newline at end of file diff --git a/src/lottoNumberValidatePipe.js b/src/lottoNumberValidatePipe.js new file mode 100644 index 000000000..fd9ed5181 --- /dev/null +++ b/src/lottoNumberValidatePipe.js @@ -0,0 +1,7 @@ +import {validateLottoNumberOutOfBounds, + validateLottoNumberDecimal} from './validate.js' + +export const lottoNumberValidatePipe = (winningNumbers) => { + validateLottoNumberOutOfBounds(winningNumbers); + validateLottoNumberDecimal(winningNumbers); +} \ No newline at end of file diff --git a/src/purchaseValidatePipe.js b/src/purchaseValidatePipe.js new file mode 100644 index 000000000..aadbe14e6 --- /dev/null +++ b/src/purchaseValidatePipe.js @@ -0,0 +1,9 @@ +import {validateNotNumber, + validateNotThousandUnits, + validatePayDecimalNumber} from './validate.js' + +export const purchaseValidatePipe = (purchaseAmount) => { + validateNotNumber(purchaseAmount); + validateNotThousandUnits(purchaseAmount); + validatePayDecimalNumber(purchaseAmount); +} diff --git a/src/utils/parser.js b/src/utils/parser.js new file mode 100644 index 000000000..205071ee9 --- /dev/null +++ b/src/utils/parser.js @@ -0,0 +1,14 @@ +const divideThousands = (element) => { + return element / 1000; +} + +const separateString = (element, separator) => { + return element.split(separator); +} + +const parser = { + divideThousands, + separateString, +} + +export default parser; \ No newline at end of file diff --git a/src/utils/random.js b/src/utils/random.js new file mode 100644 index 000000000..83f98edfd --- /dev/null +++ b/src/utils/random.js @@ -0,0 +1,10 @@ +import {MissionUtils} from '@woowacourse/mission-utils' + +const makeUniqueNumbers = (minNumber, maxNumber, amount) => { + return MissionUtils.Random.pickUniqueNumbersInRange(minNumber, maxNumber, amount); +}; + + +export const random = { + makeUniqueNumbers, +}; diff --git a/src/validate.js b/src/validate.js new file mode 100644 index 000000000..1241a62c3 --- /dev/null +++ b/src/validate.js @@ -0,0 +1,77 @@ +import {Console} from '@woowacourse/mission-utils' + +export const validateNotNumber = (inputValue) => { + const notNumber = new RegExp('[^0-9]+'); + if(notNumber.test(inputValue)){ + Console.print('[ERROR]'); + throw new Error('[ERROR]'); + } +} +export const validatePayDecimalNumber = (inputValue) => { + const decimalNumber = new RegExp('[.]+'); + if(decimalNumber.test(inputValue)){ + Console.print('[ERROR]'); + throw new Error('[ERROR]입력 에러, 금액 입력 시 정수의 숫자만 입력해주세요.'); + } +} +export const validateNotThousandUnits = (inputValue) => { + if(inputValue%1000 !== 0){ + Console.print('[ERROR]'); + throw new Error('[ERROR]구입 금액 에러, 1000원 단위 금액을 입력해주세요.') + } +} +export const validateAnswerNumberForm = (inputValue) => { + const matchWinningNumberFormat = new RegExp('(\\d\\s*,\\s*,\\s*\\d|,\\s*,|,\\s*$|[^\\d,\\s])'); + if(matchWinningNumberFormat.test(inputValue)){ + Console.print('[ERROR]'); + throw new Error('[ERROR]당첨 번호 에러, 입력한 당첨번호의 형식이 맞지 않습니다.'); + } +} +export const validateLottoNumberOutOfBounds = (inputValue) => { + inputValue.forEach((element) => { + if(element > 45 || element < 1){ + Console.print('[ERROR]'); + throw new Error('[ERROR]당첨 번호 에러, 로또 번호는 1에서 45사이의 숫자입니다.'); + } + }); +} +export const validateLottoOverlap = (numbers) => { + if(new Set(numbers).size !== numbers.length){ + Console.print('[ERROR]'); + throw new Error('[ERROR]'); + } +} +export const validateBonusOverlap = (lottoTable, bonus) => { + if(lottoTable[bonus-1] === 1){ + Console.print('[ERROR]'); + throw new Error('[ERROR]보너스 번호 에러, 보너스 번호에 중복이 있습니다.'); + } +} +export const validateLottoNumberAmount = (inputValue) => { + if(inputValue.length > 6 || inputValue.length < 6){ + Console.print('[ERROR]'); + throw new Error('[ERROR]당첨 번호 에러, 로또 번호는 6개의 숫자를 입력해야합니다.'); + } +} +export const validateLottoNumberDecimal = (inputValue) => { + const decimalNumber = new RegExp('[.]+'); + inputValue.forEach((element) =>{ + if(decimalNumber.test(element)){ + Console.print('[ERROR]'); + throw new Error('[ERROR]당첨 번호 에러, 당첨 번호는 정수의 숫자만 입력해주세요.'); + } + }); +} +export const validateBonusNumberOutOfBounds = (inputValue) => { + if(inputValue > 45 || inputValue < 1){ + Console.print('[ERROR]'); + throw new Error('[ERROR]보너스 번호 에러, 보너스 번호는 1에서 45사이의 숫자입니다.'); + } +} +export const validateBonusNumberDecimal = (inputValue) => { + const decimalNumber = new RegExp('[.]+'); + if(decimalNumber.test(inputValue)){ + Console.print('[ERROR]'); + throw new Error('[ERROR]보너스 번호 에러, 보너스 번호는 정수의 숫자만 입력해주세요.'); + } +} \ No newline at end of file