diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..77028b5349 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,18 @@ +## 기능 구현 목록 +--- + +1. 로또 게임 + - [ ] 로또 금액을 입력하면 그에 해당하는 로또를 발행하기 + - [ ] 당첨 번호와 보너스 번호 입력 받기 + - [ ] 로또 번호를 오름차순으로 정렬 + - [ ] 사용자가 구매한 로또 번호와 당첨 번호를 비교하기 + - [ ] 당첨 내역과 수익률을 출력하기 + + +2. 예외 처리 + - [ ] 입력한 로또 금액이 천원 단위인지 확인 + - [ ] 입력한 로또 금액이 숫자인지 확인 + - [ ] 입력 받은 당첨 번호가 쉼표로 구분되는지 확인 + - [ ] 입력 번호의 로또 범위가 1~45 사이인지 확인 + - [ ] 로또 번호를 발행할 때, 중복되는지 확인 + - [ ] 보너스 번호가 6개의 로또 번호와 중복되는지 확인 diff --git a/src/App.js b/src/App.js index c38b30d5b2..7b5a6c9a8a 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,21 @@ +import LottoController from "./Controller/LottoController"; +import { Console } from "@woowacourse/mission-utils"; + class App { - async play() {} + #lotto; + constructor() { + this.#lotto = new LottoController(); + } + + async play() { + try { + await this.#lotto.LottoStart(); + } catch(error) { + Console.print(error.message); + } + + } + } export default App; diff --git a/src/Constant/ErrorMessage.js b/src/Constant/ErrorMessage.js new file mode 100644 index 0000000000..ebac543fcb --- /dev/null +++ b/src/Constant/ErrorMessage.js @@ -0,0 +1,20 @@ +const ERROR_MESSAGE = Object.freeze({ + PURCHASE_AMOUNT_NUMBER: "[ERROR] 구입 금액은 숫자만 입력 가능합니다.", + PURCHASE_AMOUNT_UNDER: + "[ERROR] 구입 금액은 1000원 이상부터 로또 구입이 구입 가능합니다.", + PURCHASE_AMOUNT_UNIT: + "[ERROR] 구입 금액은 1000원 단위로 로또를 구입해야 합니다.", + PURCHASE_AMOUNT_ZERO: "[ERROR] 구입 금액은 0원이 될 수 없습니다.", + + LOTTO_NUMBER: "[ERROR] 로또 번호는 숫자로만 구성되어야 합니다.", + LOTTO_DUPLICATE: "[ERROR] 로또 번호는 서로 중복될 수 없습니다.", + LOTTO_COUNT: "[ERROR] 로또 번호는 6개로 구성되어야 합니다.", + LOTTO_RANGE: "[ERROR] 로또 번호는 1과 45 사이의 값이어야 합니다", + + WINNING_NUMBER: "[ERROR] 당첨 번호는 숫자만 입력 가능합니다.", + WINNING_DUPLICATE: "[ERROR] 당첨 번호는 서로 중복될 수 없습니다.", + WINNING_COUNT: "[ERROR] 당첨 번호는 6개로 구성되어야 합니다.", + WINNING_RANGE: "[ERROR] 당첨 번호는 1과 45 사이의 값이어야 합니다.", +}); + + export default ERROR_MESSAGE; \ No newline at end of file diff --git a/src/Constant/PrintMessage.js b/src/Constant/PrintMessage.js new file mode 100644 index 0000000000..f5f8c4a803 --- /dev/null +++ b/src/Constant/PrintMessage.js @@ -0,0 +1,8 @@ +const PRINT_MESSAGE = Object.freeze({ + REQUEST_PURCHASE_AMOUNT: "구입금액을 입력해 주세요.\n", + LOTTO_COUNT: "개를 구매했습니다.", + REQUEST_WINNING_NUMBERS: "\n당첨 번호를 입력해 주세요.\n", + REQUEST_BONUS_NUMBER: "\n보너스 번호를 입력해 주세요.\n", +}); + + export default PRINT_MESSAGE; \ No newline at end of file diff --git a/src/Constant/StaticNumber.js b/src/Constant/StaticNumber.js new file mode 100644 index 0000000000..fdc84bc62d --- /dev/null +++ b/src/Constant/StaticNumber.js @@ -0,0 +1,8 @@ +const STATIC_NUMBER = Object.freeze({ + PURCHASE_AMOUNT_SPLIT: 1_000, + LOTTO_START_NUMBER: 1, + LOTTO_END_NUMBER: 45, + LOTTO_COUNT: 6, + }); + + export default STATIC_NUMBER; \ No newline at end of file diff --git a/src/Controller/LottoController.js b/src/Controller/LottoController.js new file mode 100644 index 0000000000..9b086b7dbf --- /dev/null +++ b/src/Controller/LottoController.js @@ -0,0 +1,49 @@ +import { Console } from "@woowacourse/mission-utils"; +import Input from "../Input/Input"; +import LottoCount from "../Main/LottoCount"; +import Output from "../Output/Output"; +import Lottos from "../Main/Lottos"; +import WinningNumber from "../Main/WinningNumber"; +import BonusNumber from "../domain/BonusNumber"; + +class LottoController { + #lottoCount; + #lottos; + #winningNumber; + + constructor() {} + + async LottoStart() { + await this.getLottoPrice(); + } + + async getLottoPrice() { + const LOTTO_PRICE = await Input.lottoPrice(); + await this.getLottoCount(LOTTO_PRICE); + } + + async getLottoCount(input) { + this.#lottoCount = new LottoCount(input); + + Output.printLottoCount(this.#lottoCount.getLottoCount()); + + this.#lottos = new Lottos(this.#lottoCount.getLottoCount()); + Output.printLottos(this.#lottos.getLottos()); + + await this.requestWinningNumber(); + } + + async requestWinningNumber() { + const winningNumber = await Input.lottoWinningNumber(); + this.#winningNumber = new WinningNumber(winningNumber); + Console.print(this.#winningNumber.getWinningNumber()); + } + + async requestBonusNumber() { + const bonusNumber = await Input.lottoBonusNumber(); + this.#winningNumber = new BonusNumber(bonusNumber); + Console.print(this.#winningNumber.getBonusNumber()); + } +} + +export default LottoController; \ No newline at end of file diff --git a/src/Input/Input.js b/src/Input/Input.js new file mode 100644 index 0000000000..85144a1ed9 --- /dev/null +++ b/src/Input/Input.js @@ -0,0 +1,30 @@ +import { Console } from "@woowacourse/mission-utils"; +import PRINT_MESSAGE from "../Constant/PrintMessage"; + +const Input = { + async lottoPrice() { + const purchaseAmount = await Console.readLineAsync( + PRINT_MESSAGE.REQUEST_PURCHASE_AMOUNT + ); + + Validator.InputPurchaseAmount(purchaseAmount); + return purchaseAmount; + }, + + async lottoWinningNumber() { + const winningNumber = await Console.readLineAsync( + PRINT_MESSAGE.REQUEST_WINNING_NUMBERS + ); + Validator.InputWinningNumber(winningNumber); + return winningNumber; + }, + + async lottoBonusNumber(winningNumber) { + const bonusNumber = await Console.readLineAsync( + PRINT_MESSAGE.REQUEST_BONUS_NUMBER + ); + return bonusNumber; + }, +}; + +export default Input; \ No newline at end of file diff --git a/src/Lotto.js b/src/Lotto.js deleted file mode 100644 index cb0b1527e9..0000000000 --- a/src/Lotto.js +++ /dev/null @@ -1,18 +0,0 @@ -class Lotto { - #numbers; - - constructor(numbers) { - this.#validate(numbers); - this.#numbers = numbers; - } - - #validate(numbers) { - if (numbers.length !== 6) { - throw new Error("[ERROR] 로또 번호는 6개여야 합니다."); - } - } - - // TODO: 추가 기능 구현 -} - -export default Lotto; diff --git a/src/Main/BonusNumber.js b/src/Main/BonusNumber.js new file mode 100644 index 0000000000..675c3c2429 --- /dev/null +++ b/src/Main/BonusNumber.js @@ -0,0 +1,13 @@ +class BonusNumber { + #bonusNumber; + + constructor(bonusNumber) { + this.#bonusNumber = bonusNumber; + } + + getBonusNumber() { + return this.#bonusNumber; + } + } + + export default BonusNumber; \ No newline at end of file diff --git a/src/Main/Lotto.js b/src/Main/Lotto.js new file mode 100644 index 0000000000..35afd39d0b --- /dev/null +++ b/src/Main/Lotto.js @@ -0,0 +1,20 @@ +import Validator from "../utils/Validator"; + +class Lotto { + #numbers; + + constructor(numbers) { + this.#validate(numbers); + this.#numbers = numbers.sort((a, b) => a - b); + } + + #validate(numbers) { + Validator.lottoNumber(numbers); + } + + getLotto() { + return this.#numbers; + } +} + +export default Lotto; diff --git a/src/Main/LottoCount.js b/src/Main/LottoCount.js new file mode 100644 index 0000000000..7aaf54af45 --- /dev/null +++ b/src/Main/LottoCount.js @@ -0,0 +1,15 @@ +import STATIC_NUMBER from "../Constant/StaticNumber"; + +class LottoCount { + #lottoCount; + + constructor(inputPurchaseAmount) { + this.#lottoCount = Number(inputPurchaseAmount) / STATIC_NUMBER.PURCHASE_AMOUNT_SPLIT; + } + + getLottoCount() { + return this.#lottoCount; + } + } + + export default LottoCount; \ No newline at end of file diff --git a/src/Main/Lottos.js b/src/Main/Lottos.js new file mode 100644 index 0000000000..ca4a9b85c2 --- /dev/null +++ b/src/Main/Lottos.js @@ -0,0 +1,21 @@ +import Lotto from "./Lotto"; +import LottoRandomNumberGenerator from "../utils/LottoRandomNumberGenerator"; + +class Lottos { + #lottos = []; + + /** 로또 번호 생성 **/ + constructor(lottoCount) { + for (let i = 0; i < lottoCount; i += 1) { + this.#lottos.push( + new Lotto(LottoRandomNumberGenerator.generate()).getLotto() + ); + } + } + + getLottos() { + return this.#lottos; + } +} + +export default Lottos; \ No newline at end of file diff --git a/src/Main/WinningNumber.js b/src/Main/WinningNumber.js new file mode 100644 index 0000000000..86bc7ef2b2 --- /dev/null +++ b/src/Main/WinningNumber.js @@ -0,0 +1,13 @@ +class WinningNumber { + #winningNumber; + + constructor(winningNumber) { + this.#winningNumber = winningNumber.split(","); + } + + getWinningNumber() { + return this.#winningNumber; + } + } + + export default WinningNumber; \ No newline at end of file diff --git a/src/Output/Output.js b/src/Output/Output.js new file mode 100644 index 0000000000..845739e279 --- /dev/null +++ b/src/Output/Output.js @@ -0,0 +1,16 @@ +import { Console } from "@woowacourse/mission-utils"; +import PRINT_MESSAGE from "../Constant/PrintMessage"; + +const Output = { + printLottoCount(lottoCount) { + Console.print(`\n${lottoCount}${PRINT_MESSAGE.LOTTO_COUNT}`); + }, + + printLottos(lottos) { + for (let i = 0; i < lottos.length; i++) { + Console.print(`[${lottos[i].join(", ")}]`); + } + }, +}; + +export default Output; \ No newline at end of file diff --git a/src/utils/LottoRandomNumberGenerator.js b/src/utils/LottoRandomNumberGenerator.js new file mode 100644 index 0000000000..c9f2a242ce --- /dev/null +++ b/src/utils/LottoRandomNumberGenerator.js @@ -0,0 +1,14 @@ +import { Random } from "@woowacourse/mission-utils"; +import STATIC_NUMBER from "../Constant/StaticNumber"; + +const LottoRandomNumberGenerator = { + generate() { + return Random.pickUniqueNumbersInRange( + STATIC_NUMBER.LOTTO_START_NUMBER, + STATIC_NUMBER.LOTTO_END_NUMBER, + STATIC_NUMBER.LOTTO_COUNT + ); + }, +}; + +export default LottoRandomNumberGenerator; \ No newline at end of file diff --git a/src/utils/RangeTest.js b/src/utils/RangeTest.js new file mode 100644 index 0000000000..ae712a5a6f --- /dev/null +++ b/src/utils/RangeTest.js @@ -0,0 +1,12 @@ +import STATIC_NUMBER from "../Constant/StaticNumber"; + +function RangeTest(inputNumbers) { + return inputNumbers.some((number) => { + return ( + number < STATIC_NUMBER.LOTTO_START_NUMBER || + number > STATIC_NUMBER.LOTTO_END_NUMBER + ); + }); + } + + export default RangeTest; \ No newline at end of file diff --git a/src/utils/Validator.js b/src/utils/Validator.js new file mode 100644 index 0000000000..637cdc1439 --- /dev/null +++ b/src/utils/Validator.js @@ -0,0 +1,48 @@ +import ERROR_MESSAGE from "../Constant/ErrorMessage"; +import RangeTest from "./RangeTest"; + +const Validator = { + InputPurchaseAmount(purchaseAmount) { + if (purchaseAmount.replace(/\d/g, "").length > 0) + throw new Error(ERROR_MESSAGE.PURCHASE_AMOUNT_NUMBER); + + if (purchaseAmount < 1000) + throw new Error(ERROR_MESSAGE.PURCHASE_AMOUNT_UNDER); + + if (purchaseAmount % 1000 !== 0) + throw new Error(ERROR_MESSAGE.PURCHASE_AMOUNT_UNIT); + + if (purchaseAmount.replace(/0/g, "").length === 0) + throw new Error(ERROR_MESSAGE.PURCHASE_AMOUNT_ZERO); + + }, + + lottoNumber(numbers) { + const number = numbers.join(""); + if (number.replace(/\d/g, "").length > 0) + throw new Error(ERROR_MESSAGE.LOTTO_NUMBER); + if (numbers.length !== new Set(numbers).size) + throw new Error(ERROR_MESSAGE.LOTTO_DUPLICATE); + if (numbers.length !== 6) + throw new Error(ERROR_MESSAGE.LOTTO_COUNT); + if (RangeTest(numbers)) + throw new Error(ERROR_MESSAGE.LOTTO_RANGE); + }, + + InputWinningNumber(input) { + const inputWinninNumbers = input.split(","); + if (input.replace(/\d|\,/g, "").length > 0) + throw new Error(ERROR_MESSAGE.WINNING_NUMBER); + + if (inputWinninNumbers.length !== new Set(inputWinninNumbers).size) + throw new Error(ERROR_MESSAGE.LOTTO_DUPLICATE); + + if (inputWinninNumbers.length !== 6) + throw new Error(ERROR_MESSAGE.WINNING_COUNT); + + if (RangeTest(inputWinninNumbers)) + throw new Error(ERROR_MESSAGE.WINNING_RANGE); + }, + }; + + export default Validator; \ No newline at end of file