From d18b4ccc4bcd8f6551acb20d9ef0a6ee8ed7cb3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 10:08:24 +0000 Subject: [PATCH 01/17] chore: setting eslint config --- eslint.config.js | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 eslint.config.js diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 000000000..cf10a04e9 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,57 @@ +import babelParser from '@babel/eslint-parser'; +import { FlatCompat } from '@eslint/eslintrc'; +import pluginJs from '@eslint/js'; +import prettier from 'eslint-config-prettier'; +import globals from 'globals'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const filename = fileURLToPath(import.meta.url); +const dirname = path.dirname(filename); + +const compat = new FlatCompat({ + baseDirectory: dirname, +}); + +export default [ + { + languageOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + parser: babelParser, + parserOptions: { + requireConfigFile: false, + }, + globals: { + ...globals.node, + ...globals.jest, + }, + }, + }, + ...compat.extends('eslint-config-airbnb-base'), + prettier, + { + files: ['/src//.js', '/tests//.js', '/tests//*.js'], + rules: { + 'max-depth': ['error', 2], + 'max-params': ['error', 3], + 'max-lines-per-function': ['error', { max: 15 }], + 'import/extensions': ['error', 'ignorePackages'], + 'import/prefer-default-export': 'off', + 'class-methods-use-this': 'off', + 'no-plusplus': ['error', { allowForLoopAfterthoughts: true }], + 'no-console': 'off', + 'no-unused-vars': 'error', + 'prefer-const': 'error', + 'no-var': 'error', + eqeqeq: ['error', 'always'], + }, + }, + { + files: ['eslint.config.js'], + rules: { + 'no-underscore-dangle': 'off', + 'import/no-extraneous-dependencies': ['error', { packageDir: __dirname }], + }, + }, +]; \ No newline at end of file From c4efb34612eaa24ccea4f821cabbcd3ee7c4ec42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 10:56:33 +0000 Subject: [PATCH 02/17] =?UTF-8?q?feat:=20App=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 32 +++++++++++++++++++++++++++++++- src/Lotto.js | 6 +++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/App.js b/src/App.js index 091aa0a5d..8405f19b2 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,35 @@ class App { - async run() {} + async run() { + + // 로또구입금액입력() + // 구입금액 검사() + // 로또 번호 발행() + // 로또 번호 검사() + + // 발행한 로또 수량 및 번호 오름차순 출력() + + // 당첨번호 입력() + // 당첨 번호 검사() + // 보너스번호 입력() + // 보너스 번호 검사() + + // 당첨내역 출력() + // 총 수익률 출력() + + } +} + +class LottoVendingMachine { + #lottos + + purchaseLottoAmount() { + this.#validateLottoAmount() + } + + #validateLottoAmount(number) { + + } + } export default App; diff --git a/src/Lotto.js b/src/Lotto.js index cb0b1527e..95d8bdf61 100644 --- a/src/Lotto.js +++ b/src/Lotto.js @@ -10,9 +10,13 @@ class Lotto { if (numbers.length !== 6) { throw new Error("[ERROR] 로또 번호는 6개여야 합니다."); } + + // TODO: 로또 숫자 validate 숫자 범위 + // 숫자 중복 검사 + } // TODO: 추가 기능 구현 } -export default Lotto; +export default Lotto; \ No newline at end of file From c8a1350a2d92365d948dfab4d0ac8a0ed5457511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 10:56:47 +0000 Subject: [PATCH 03/17] =?UTF-8?q?feat:=20MESSAGES=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/App.js b/src/App.js index 8405f19b2..0df560bc0 100644 --- a/src/App.js +++ b/src/App.js @@ -1,3 +1,17 @@ +import { Console } from "@woowacourse/mission-utils"; +const MESSAGES = Object.freeze({ + INPUT: { + LOTTO_AMOUT: "구입금액을 입력해 주세요.\n", + + }, + OUTPUT: { + + }, + ERROR: { + + } +}); + class App { async run() { From de299f3bfcb284e9b613103a889b7867e12f55f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 11:15:52 +0000 Subject: [PATCH 04/17] =?UTF-8?q?feat:=20MESSAGES=20=EC=9E=85=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5=20=EB=A9=94=EC=84=B8=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/App.js b/src/App.js index 0df560bc0..6a3372791 100644 --- a/src/App.js +++ b/src/App.js @@ -1,14 +1,23 @@ import { Console } from "@woowacourse/mission-utils"; + const MESSAGES = Object.freeze({ INPUT: { - LOTTO_AMOUT: "구입금액을 입력해 주세요.\n", - + PURCHASE_LOTTO_MONEY: "구입금액을 입력해 주세요.\n", + WIN_NUMBERS: "당첨 번호를 입력해 주세요.\n", + BOUNS_NUMBER: "보너스 번호를 입력해 주세요.\n", }, OUTPUT: { - + PURCHASE_LOTTO_NUMBER: (n) => `${n}개를 구매했습니다.`, + WIN_RATE: "당첨 통계\n---\n", + WIN_DETAIL_5th: (n) => `3개 일치 (5,000원) - ${n}개\n`, + WIN_DETAIL_4th: (n) => `4개 일치 (50,000원) - ${n}개\n`, + WIN_DETAIL_3rd: (n) => `5개 일치 (1,500,000원) - ${n}개\n`, + WIN_DETAIL_2nd: (n) => `5개 일치, 보너스 볼 일치 (30,000,000원) - ${n}개\n`, + WIN_DETAIL_1st: (n) => `6개 일치 (2,000,000,000원) - ${n}개`, + PROFIT_RATE: (profitRate) => `총 수익률은 ${profitRate}%입니다.\n` }, ERROR: { - + PREFIX: "[ERROR] ", } }); From 8dd01c5792af96defe542159c960731e760ea3a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 11:21:43 +0000 Subject: [PATCH 05/17] docs: add readme --- README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.md b/README.md index 15bb106b5..06e70daa0 100644 --- a/README.md +++ b/README.md @@ -1 +1,30 @@ # javascript-lotto-precourse + +## 기능 목록 - turtlehwan + +- [ ] 로또 구입 금액 입력 기능 + - [ ] 예외 : 1,000 단위가 아닌 경우 +- [ ] 로또 번호 발행 기능 + - [ ] 1~45 정수 숫자 범위 + - [ ] 중복되지 않는 6개 숫자 발행 +- [ ] 발행한 로또 수량 및 번호 출력 (오름차순) +- [ ] 당첨 번호 입력 기능 + - [ ] 중복되지 않는 6개의 숫자 입력 + - [ ] 중복되지 않는 1개의 보너스 번호 입력 +- [ ] 당첨 확인(등수 판별) 기능 +- [ ] 당첨 내역 확인 및 출력 +- [ ] 수익률 계산 및 출력 + - [ ] 소수점 둘째 자리에서 반올림 + +## 중점 사항 + +- 값을 하드코딩하지 않고, 상수로 빼내서 정리하기 +- 메서드가 한 가지 기능을 하는지 확인하는 기준 세우기 + - 여러 메서드에서 중복되는 코드가 있으면 별도의 메서드로 분리 + - 해당 메서드를 변경할 때 서로 다른 이유로 변경하고 commit하는지 살펴보기 + - 안내 문구 출력, 사용자 입력 처리, 유효값 검증 등의 작업을 각기 다른 함수로 분리 +- class의 private 필드 잘 이용하기 (# 문법) +- JavaScript에서 객체를 만드는 다양한 방법을 이해하고 사용하기 +- 기능별 단위 테스트 만들기 + - 테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리 + - 처음부터 큰 단위의 테스트를 만들지 않기 From 967e55f140a91b030a5082e886d97f70e622f709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 11:56:30 +0000 Subject: [PATCH 06/17] =?UTF-8?q?=EB=A1=9C=EB=98=90=20=EA=B5=AC=EC=9E=85?= =?UTF-8?q?=20=EA=B8=88=EC=95=A1=20=EC=9E=85=EB=A0=A5=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 ++++++---- src/App.js | 43 +++++++++++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 06e70daa0..e2efe8cef 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,10 @@ ## 기능 목록 - turtlehwan -- [ ] 로또 구입 금액 입력 기능 - - [ ] 예외 : 1,000 단위가 아닌 경우 +- [x] 로또 구입 금액 입력 기능 + - [x] 예외 : 정수가 아닌 숫자입니다. + - [x] 예외 : 정수가 1,000 미만입니다. + - [x] 예외 : 입력 받은 수가 1,000 단위가 아닙니다. - [ ] 로또 번호 발행 기능 - [ ] 1~45 정수 숫자 범위 - [ ] 중복되지 않는 6개 숫자 발행 @@ -26,5 +28,5 @@ - class의 private 필드 잘 이용하기 (# 문법) - JavaScript에서 객체를 만드는 다양한 방법을 이해하고 사용하기 - 기능별 단위 테스트 만들기 - - 테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리 - - 처음부터 큰 단위의 테스트를 만들지 않기 + - 테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리 + - 처음부터 큰 단위의 테스트를 만들지 않기 diff --git a/src/App.js b/src/App.js index 6a3372791..1bebd6078 100644 --- a/src/App.js +++ b/src/App.js @@ -7,52 +7,67 @@ const MESSAGES = Object.freeze({ BOUNS_NUMBER: "보너스 번호를 입력해 주세요.\n", }, OUTPUT: { - PURCHASE_LOTTO_NUMBER: (n) => `${n}개를 구매했습니다.`, + PURCHASE_LOTTO_NUMBER: (n) => `${n}개를 구매했습니다.\n`, WIN_RATE: "당첨 통계\n---\n", WIN_DETAIL_5th: (n) => `3개 일치 (5,000원) - ${n}개\n`, WIN_DETAIL_4th: (n) => `4개 일치 (50,000원) - ${n}개\n`, WIN_DETAIL_3rd: (n) => `5개 일치 (1,500,000원) - ${n}개\n`, WIN_DETAIL_2nd: (n) => `5개 일치, 보너스 볼 일치 (30,000,000원) - ${n}개\n`, - WIN_DETAIL_1st: (n) => `6개 일치 (2,000,000,000원) - ${n}개`, - PROFIT_RATE: (profitRate) => `총 수익률은 ${profitRate}%입니다.\n` + WIN_DETAIL_1st: (n) => `6개 일치 (2,000,000,000원) - ${n}개\n`, + PROFIT_RATE: (profitRate) => `총 수익률은 ${profitRate}%입니다.\n`, }, ERROR: { - PREFIX: "[ERROR] ", - } + PREFIX: `[ERROR] `, + NOT_INT: `정수가 아닌 숫자입니다.\n`, + SMALL_INT: `정수가 1,000 미만입니다.\n`, + NOT_UNIT_INT: `입력 받은 수가 1,000 단위가 아닙니다.\n`, + }, }); class App { async run() { + const LottoVM = new LottoVendingMachine(); + LottoVM.purchaseLottoAmount(); - // 로또구입금액입력() - // 구입금액 검사() // 로또 번호 발행() // 로또 번호 검사() - // 발행한 로또 수량 및 번호 오름차순 출력() - // 당첨번호 입력() // 당첨 번호 검사() // 보너스번호 입력() // 보너스 번호 검사() - // 당첨내역 출력() // 총 수익률 출력() - } } class LottoVendingMachine { - #lottos + #lottos; - purchaseLottoAmount() { - this.#validateLottoAmount() + async purchaseLottoAmount() { + let money = await Console.readLineAsync( + MESSAGES.INPUT.PURCHASE_LOTTO_MONEY + ); + money = this.#validateLottoAmount(money); } #validateLottoAmount(number) { + if (!Number.isInteger(Number(number))) { + throw new Error(MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.NOT_INT); + } + if (Number(number) < 1000) { + throw new Error(MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.SMALL_INT); + } + if (number.slice(-3) !== "000") { + throw new Error(MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.NOT_UNIT_INT); + } + return Number(number); } +} +class ValidateCommon { + validate; } export default App; From 3a900532e3123d5b757af4d6ede00f596455b974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 12:42:11 +0000 Subject: [PATCH 07/17] =?UTF-8?q?feat:=20message=20const=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 41 ++++++++++++--------------------------- src/constants/messages.js | 23 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 src/constants/messages.js diff --git a/src/App.js b/src/App.js index 1bebd6078..b5e19818f 100644 --- a/src/App.js +++ b/src/App.js @@ -1,28 +1,5 @@ import { Console } from "@woowacourse/mission-utils"; - -const MESSAGES = Object.freeze({ - INPUT: { - PURCHASE_LOTTO_MONEY: "구입금액을 입력해 주세요.\n", - WIN_NUMBERS: "당첨 번호를 입력해 주세요.\n", - BOUNS_NUMBER: "보너스 번호를 입력해 주세요.\n", - }, - OUTPUT: { - PURCHASE_LOTTO_NUMBER: (n) => `${n}개를 구매했습니다.\n`, - WIN_RATE: "당첨 통계\n---\n", - WIN_DETAIL_5th: (n) => `3개 일치 (5,000원) - ${n}개\n`, - WIN_DETAIL_4th: (n) => `4개 일치 (50,000원) - ${n}개\n`, - WIN_DETAIL_3rd: (n) => `5개 일치 (1,500,000원) - ${n}개\n`, - WIN_DETAIL_2nd: (n) => `5개 일치, 보너스 볼 일치 (30,000,000원) - ${n}개\n`, - WIN_DETAIL_1st: (n) => `6개 일치 (2,000,000,000원) - ${n}개\n`, - PROFIT_RATE: (profitRate) => `총 수익률은 ${profitRate}%입니다.\n`, - }, - ERROR: { - PREFIX: `[ERROR] `, - NOT_INT: `정수가 아닌 숫자입니다.\n`, - SMALL_INT: `정수가 1,000 미만입니다.\n`, - NOT_UNIT_INT: `입력 받은 수가 1,000 단위가 아닙니다.\n`, - }, -}); +import { MESSAGES } from "./constants/messages.js"; class App { async run() { @@ -41,14 +18,20 @@ class App { } } -class LottoVendingMachine { +export class LottoVendingMachine { #lottos; async purchaseLottoAmount() { - let money = await Console.readLineAsync( - MESSAGES.INPUT.PURCHASE_LOTTO_MONEY - ); - money = this.#validateLottoAmount(money); + while (true) { + try { + let money = await Console.readLineAsync( + MESSAGES.INPUT.PURCHASE_LOTTO_MONEY + ); + return this.#validateLottoAmount(money); + } catch (e) { + Console.print(e.message); + } + } } #validateLottoAmount(number) { diff --git a/src/constants/messages.js b/src/constants/messages.js new file mode 100644 index 000000000..cc9689072 --- /dev/null +++ b/src/constants/messages.js @@ -0,0 +1,23 @@ +export const MESSAGES = Object.freeze({ + INPUT: { + PURCHASE_LOTTO_MONEY: "구입금액을 입력해 주세요.\n", + WIN_NUMBERS: "당첨 번호를 입력해 주세요.\n", + BOUNS_NUMBER: "보너스 번호를 입력해 주세요.\n", + }, + OUTPUT: { + PURCHASE_LOTTO_NUMBER: (n) => `${n}개를 구매했습니다.\n`, + WIN_RATE: "당첨 통계\n---\n", + WIN_DETAIL_5th: (n) => `3개 일치 (5,000원) - ${n}개\n`, + WIN_DETAIL_4th: (n) => `4개 일치 (50,000원) - ${n}개\n`, + WIN_DETAIL_3rd: (n) => `5개 일치 (1,500,000원) - ${n}개\n`, + WIN_DETAIL_2nd: (n) => `5개 일치, 보너스 볼 일치 (30,000,000원) - ${n}개\n`, + WIN_DETAIL_1st: (n) => `6개 일치 (2,000,000,000원) - ${n}개\n`, + PROFIT_RATE: (profitRate) => `총 수익률은 ${profitRate}%입니다.\n`, + }, + ERROR: { + PREFIX: `[ERROR] `, + NOT_INT: `정수가 아닌 숫자입니다.\n`, + SMALL_INT: `정수가 1,000 미만입니다.\n`, + NOT_UNIT_INT: `입력 받은 수가 1,000 단위가 아닙니다.\n`, + }, +}); From 42f32047aaaa341ad0b8ba23ce7b684f0fe0e591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 12:42:26 +0000 Subject: [PATCH 08/17] =?UTF-8?q?test:=20LottoVendingMachine=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __tests__/LottoVendingMachineTest.js | 52 ++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 __tests__/LottoVendingMachineTest.js diff --git a/__tests__/LottoVendingMachineTest.js b/__tests__/LottoVendingMachineTest.js new file mode 100644 index 000000000..6b143d32f --- /dev/null +++ b/__tests__/LottoVendingMachineTest.js @@ -0,0 +1,52 @@ +import { MissionUtils } from "@woowacourse/mission-utils"; +import App, { LottoVendingMachine } from "../src/App.js"; +import { MESSAGES } from "../src/constants/messages.js"; + +const mockQuestions = (inputs) => { + MissionUtils.Console.readLineAsync = jest.fn(); + + MissionUtils.Console.readLineAsync.mockImplementation(() => { + const input = inputs.shift(); + + return Promise.resolve(input); + }); +}; + +const getLogSpy = () => { + const logSpy = jest.spyOn(MissionUtils.Console, "print"); + logSpy.mockClear(); + return logSpy; +}; + +const runException = async (input) => { + // given + const logSpy = getLogSpy(); + + const INPUT_NUMBERS_TO_END = ["1000", "1,2,3,4,5,6", "7"]; + mockQuestions([input, ...INPUT_NUMBERS_TO_END]); + + // when + const LottoVM = new LottoVendingMachine(); + await LottoVM.purchaseLottoAmount(); + + // then + expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("[ERROR]")); +}; + +describe("LottoVendingMachine 클래스 테스트", () => { + beforeEach(() => { + jest.restoreAllMocks(); + }); + + test("금액이 정수가 아니면 예외가 발생한다.", async () => { + await runException("1000.5"); + }); + + test("금액이 1000원 미만이면 예외가 발생한다.", async () => { + await runException("500"); + }); + + test("금액이 1000원 단위가 아니면 예외가 발생한다.", async () => { + await runException("1521"); + }); +}); From 0f17e7971c0b9e188aa9878521e97579539b1c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 12:56:43 +0000 Subject: [PATCH 09/17] =?UTF-8?q?feat:=20LottoVendingMachine=20class=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __tests__/LottoVendingMachineTest.js | 3 +- src/App.js | 50 +------------------------- src/LottoVendingMachine.js | 52 ++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 51 deletions(-) create mode 100644 src/LottoVendingMachine.js diff --git a/__tests__/LottoVendingMachineTest.js b/__tests__/LottoVendingMachineTest.js index 6b143d32f..8d56b2741 100644 --- a/__tests__/LottoVendingMachineTest.js +++ b/__tests__/LottoVendingMachineTest.js @@ -1,6 +1,5 @@ import { MissionUtils } from "@woowacourse/mission-utils"; -import App, { LottoVendingMachine } from "../src/App.js"; -import { MESSAGES } from "../src/constants/messages.js"; +import LottoVendingMachine from "../src/LottoVendingMachine.js"; const mockQuestions = (inputs) => { MissionUtils.Console.readLineAsync = jest.fn(); diff --git a/src/App.js b/src/App.js index b5e19818f..3049eb26f 100644 --- a/src/App.js +++ b/src/App.js @@ -1,56 +1,8 @@ -import { Console } from "@woowacourse/mission-utils"; -import { MESSAGES } from "./constants/messages.js"; - class App { async run() { const LottoVM = new LottoVendingMachine(); - LottoVM.purchaseLottoAmount(); - - // 로또 번호 발행() - // 로또 번호 검사() - // 발행한 로또 수량 및 번호 오름차순 출력() - // 당첨번호 입력() - // 당첨 번호 검사() - // 보너스번호 입력() - // 보너스 번호 검사() - // 당첨내역 출력() - // 총 수익률 출력() - } -} - -export class LottoVendingMachine { - #lottos; - - async purchaseLottoAmount() { - while (true) { - try { - let money = await Console.readLineAsync( - MESSAGES.INPUT.PURCHASE_LOTTO_MONEY - ); - return this.#validateLottoAmount(money); - } catch (e) { - Console.print(e.message); - } - } + LottoVM.run(); } - - #validateLottoAmount(number) { - if (!Number.isInteger(Number(number))) { - throw new Error(MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.NOT_INT); - } - if (Number(number) < 1000) { - throw new Error(MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.SMALL_INT); - } - if (number.slice(-3) !== "000") { - throw new Error(MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.NOT_UNIT_INT); - } - - return Number(number); - } -} - -class ValidateCommon { - validate; } export default App; diff --git a/src/LottoVendingMachine.js b/src/LottoVendingMachine.js new file mode 100644 index 000000000..13cbba606 --- /dev/null +++ b/src/LottoVendingMachine.js @@ -0,0 +1,52 @@ +import { Console } from "@woowacourse/mission-utils"; +import { MESSAGES } from "./constants/messages.js"; + +class LottoVendingMachine { + #lottos; + + async run() { + const money = await this.purchaseLottoAmount(); + this.issueLottos(money); + + // 로또 번호 발행() + // 로또 번호 검사() + // 발행한 로또 수량 및 번호 오름차순 출력() + // 당첨번호 입력() + // 당첨 번호 검사() + // 보너스번호 입력() + // 보너스 번호 검사() + // 당첨내역 출력() + // 총 수익률 출력() + } + + async purchaseLottoAmount() { + while (true) { + try { + let money = await Console.readLineAsync( + MESSAGES.INPUT.PURCHASE_LOTTO_MONEY + ); + return this.#validateLottoAmount(money); + } catch (e) { + Console.print(e.message); + } + } + } + + issueLottos(money) {} + + #validateLottoAmount(number) { + if (!Number.isInteger(Number(number))) { + throw new Error(MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.NOT_INT); + } + if (Number(number) < 1000) { + throw new Error(MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.SMALL_INT); + } + if (number.slice(-3) !== "000") { + throw new Error(MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.NOT_UNIT_INT); + } + + return Number(number); + } +} + +export default LottoVendingMachine; From a2bb8d52a35b6f9b8be3f6adbf0165cd5efdfd15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 13:22:45 +0000 Subject: [PATCH 10/17] =?UTF-8?q?feat:=20lotto=20=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EB=B0=9C=ED=96=89=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/App.js | 2 ++ src/LottoVendingMachine.js | 38 +++++++++++++++++++++++++++++++------- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e2efe8cef..02eaa9e56 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ - [x] 예외 : 정수가 아닌 숫자입니다. - [x] 예외 : 정수가 1,000 미만입니다. - [x] 예외 : 입력 받은 수가 1,000 단위가 아닙니다. -- [ ] 로또 번호 발행 기능 +- [x] 로또 번호 발행 기능 - [ ] 1~45 정수 숫자 범위 - [ ] 중복되지 않는 6개 숫자 발행 - [ ] 발행한 로또 수량 및 번호 출력 (오름차순) diff --git a/src/App.js b/src/App.js index 3049eb26f..803f99790 100644 --- a/src/App.js +++ b/src/App.js @@ -1,3 +1,5 @@ +import LottoVendingMachine from "./LottoVendingMachine.js"; + class App { async run() { const LottoVM = new LottoVendingMachine(); diff --git a/src/LottoVendingMachine.js b/src/LottoVendingMachine.js index 13cbba606..74d7721b2 100644 --- a/src/LottoVendingMachine.js +++ b/src/LottoVendingMachine.js @@ -1,15 +1,18 @@ -import { Console } from "@woowacourse/mission-utils"; +import { Console, MissionUtils } from "@woowacourse/mission-utils"; import { MESSAGES } from "./constants/messages.js"; +import { MAGIC_NUMBER } from "./constants/magicNumber.js"; class LottoVendingMachine { - #lottos; + #lottoAmount; + #lottos = []; async run() { - const money = await this.purchaseLottoAmount(); - this.issueLottos(money); + await this.purchaseLottoAmount(); // 로또 번호 발행() // 로또 번호 검사() + this.issueLottos(); + // 발행한 로또 수량 및 번호 오름차순 출력() // 당첨번호 입력() // 당첨 번호 검사() @@ -25,15 +28,16 @@ class LottoVendingMachine { let money = await Console.readLineAsync( MESSAGES.INPUT.PURCHASE_LOTTO_MONEY ); - return this.#validateLottoAmount(money); + money = this.#validateLottoAmount(money); + this.#lottoAmount = money / 1000; + + return this.#lottoAmount; } catch (e) { Console.print(e.message); } } } - issueLottos(money) {} - #validateLottoAmount(number) { if (!Number.isInteger(Number(number))) { throw new Error(MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.NOT_INT); @@ -47,6 +51,26 @@ class LottoVendingMachine { return Number(number); } + + issueLottos() { + while (this.#lottos.length < this.#lottoAmount) { + this.#lottos.push(this.#pickRandomLotto()); + } + } + + #pickRandomLotto() { + return MissionUtils.Random.pickUniqueNumbersInRange( + MAGIC_NUMBER.LOTTO_MIN_NUM, + MAGIC_NUMBER.LOTTO_MAX_NUM, + MAGIC_NUMBER.LOTTO_PICK_NUM + ); + } + + printIssueLottosInfo() { + Console.print(""); + Console.print(MESSAGES.OUTPUT.PURCHASE_LOTTO_NUMBER(this.#lottoAmount)); + this.#lottos.forEach((lotto) => Console.print(lotto)); + } } export default LottoVendingMachine; From e842ce406d9ffaf45375629603e13b00ec554801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 13:23:45 +0000 Subject: [PATCH 11/17] =?UTF-8?q?feat:=20=EB=B0=9C=ED=96=89=ED=95=9C=20?= =?UTF-8?q?=EB=A1=9C=EB=98=90=20=EC=88=98=EB=9F=89=20=EB=B0=8F=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- src/LottoVendingMachine.js | 2 ++ src/constants/magicNumber.js | 5 +++++ src/constants/messages.js | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 src/constants/magicNumber.js diff --git a/README.md b/README.md index 02eaa9e56..d3a34b19a 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ - [x] 예외 : 정수가 1,000 미만입니다. - [x] 예외 : 입력 받은 수가 1,000 단위가 아닙니다. - [x] 로또 번호 발행 기능 - - [ ] 1~45 정수 숫자 범위 - - [ ] 중복되지 않는 6개 숫자 발행 -- [ ] 발행한 로또 수량 및 번호 출력 (오름차순) + - [x] 1~45 정수 숫자 범위 -> MissionUtils + - [x] 중복되지 않는 6개 숫자 발행 -> MissionUtils +- [x] 발행한 로또 수량 및 번호 출력 (오름차순) - [ ] 당첨 번호 입력 기능 - [ ] 중복되지 않는 6개의 숫자 입력 - [ ] 중복되지 않는 1개의 보너스 번호 입력 diff --git a/src/LottoVendingMachine.js b/src/LottoVendingMachine.js index 74d7721b2..e3d8b072b 100644 --- a/src/LottoVendingMachine.js +++ b/src/LottoVendingMachine.js @@ -14,6 +14,8 @@ class LottoVendingMachine { this.issueLottos(); // 발행한 로또 수량 및 번호 오름차순 출력() + this.printIssueLottosInfo(); + // 당첨번호 입력() // 당첨 번호 검사() // 보너스번호 입력() diff --git a/src/constants/magicNumber.js b/src/constants/magicNumber.js new file mode 100644 index 000000000..a99ed8bd6 --- /dev/null +++ b/src/constants/magicNumber.js @@ -0,0 +1,5 @@ +export const MAGIC_NUMBER = Object.freeze({ + LOTTO_MIN_NUM: 1, + LOTTO_MAX_NUM: 45, + LOTTO_PICK_NUM: 6, +}); diff --git a/src/constants/messages.js b/src/constants/messages.js index cc9689072..3f1aa2dfc 100644 --- a/src/constants/messages.js +++ b/src/constants/messages.js @@ -5,7 +5,7 @@ export const MESSAGES = Object.freeze({ BOUNS_NUMBER: "보너스 번호를 입력해 주세요.\n", }, OUTPUT: { - PURCHASE_LOTTO_NUMBER: (n) => `${n}개를 구매했습니다.\n`, + PURCHASE_LOTTO_NUMBER: (n) => `${n}개를 구매했습니다.`, WIN_RATE: "당첨 통계\n---\n", WIN_DETAIL_5th: (n) => `3개 일치 (5,000원) - ${n}개\n`, WIN_DETAIL_4th: (n) => `4개 일치 (50,000원) - ${n}개\n`, From 4d1dab0675d0bb09adbe8f8ebb3d4fba7dfa7436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 13:31:50 +0000 Subject: [PATCH 12/17] =?UTF-8?q?feat:=20=EB=8B=B9=EC=B2=A8=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=9E=85=EB=A0=A5=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/LottoVendingMachine.js | 32 +++++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d3a34b19a..b9492f603 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ - [x] 1~45 정수 숫자 범위 -> MissionUtils - [x] 중복되지 않는 6개 숫자 발행 -> MissionUtils - [x] 발행한 로또 수량 및 번호 출력 (오름차순) -- [ ] 당첨 번호 입력 기능 +- [x] 당첨 번호 입력 기능 - [ ] 중복되지 않는 6개의 숫자 입력 - [ ] 중복되지 않는 1개의 보너스 번호 입력 - [ ] 당첨 확인(등수 판별) 기능 diff --git a/src/LottoVendingMachine.js b/src/LottoVendingMachine.js index e3d8b072b..7c4e5bf57 100644 --- a/src/LottoVendingMachine.js +++ b/src/LottoVendingMachine.js @@ -1,13 +1,16 @@ import { Console, MissionUtils } from "@woowacourse/mission-utils"; import { MESSAGES } from "./constants/messages.js"; import { MAGIC_NUMBER } from "./constants/magicNumber.js"; +import Lotto from "./Lotto.js"; class LottoVendingMachine { - #lottoAmount; + #lottoAmount = 0; #lottos = []; + #winNumbers = []; + #bounsNumber = 0; async run() { - await this.purchaseLottoAmount(); + await this.purchaseLottoAmountInput(); // 로또 번호 발행() // 로또 번호 검사() @@ -18,13 +21,17 @@ class LottoVendingMachine { // 당첨번호 입력() // 당첨 번호 검사() + await this.winNumbersInput(); + // 보너스번호 입력() // 보너스 번호 검사() + await this.bounsNumberInput(); + // 당첨내역 출력() // 총 수익률 출력() } - async purchaseLottoAmount() { + async purchaseLottoAmountInput() { while (true) { try { let money = await Console.readLineAsync( @@ -73,6 +80,25 @@ class LottoVendingMachine { Console.print(MESSAGES.OUTPUT.PURCHASE_LOTTO_NUMBER(this.#lottoAmount)); this.#lottos.forEach((lotto) => Console.print(lotto)); } + + async winNumbersInput() { + while (true) { + try { + let winNumbers = await Console.readLineAsync( + MESSAGES.INPUT.WIN_NUMBERS + ); + winNumbers = winNumbers.split(",").map((n) => Number(n)); + const lotto = new Lotto(winNumbers); + this.#winNumbers = winNumbers; + + return this.#winNumbers; + } catch (e) { + Console.print(e.message); + } + } + } + + bounsNumberInput() {} } export default LottoVendingMachine; From edb884efa05d14f7bd980c8d3e841b31aaaee4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 13:48:23 +0000 Subject: [PATCH 13/17] =?UTF-8?q?feat:=20Lotto=20=EB=8B=B9=EC=B2=A8=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=ED=8C=90=EB=B3=84=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +++- src/Lotto.js | 42 ++++++++++++++++++++++++++++++++++----- src/constants/messages.js | 4 ++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b9492f603..2a71a7415 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,9 @@ - [x] 중복되지 않는 6개 숫자 발행 -> MissionUtils - [x] 발행한 로또 수량 및 번호 출력 (오름차순) - [x] 당첨 번호 입력 기능 - - [ ] 중복되지 않는 6개의 숫자 입력 + - [x] 양의 정수 판별 + - [x] 1~45 숫자 범위 + - [x] 중복되지 않는 6개의 숫자 입력 - [ ] 중복되지 않는 1개의 보너스 번호 입력 - [ ] 당첨 확인(등수 판별) 기능 - [ ] 당첨 내역 확인 및 출력 diff --git a/src/Lotto.js b/src/Lotto.js index 95d8bdf61..9650e2a61 100644 --- a/src/Lotto.js +++ b/src/Lotto.js @@ -1,22 +1,54 @@ +import { MissionUtils } from "@woowacourse/mission-utils"; +import { MAGIC_NUMBER } from "./constants/magicNumber.js"; +import { MESSAGES } from "./constants/messages.js"; + class Lotto { #numbers; constructor(numbers) { this.#validate(numbers); + this.#validateDuplicate(numbers); this.#numbers = numbers; } #validate(numbers) { if (numbers.length !== 6) { - throw new Error("[ERROR] 로또 번호는 6개여야 합니다."); + throw new Error( + MESSAGES.ERROR.PREFIX + + MESSAGES.ERROR.LOTTO_PICK_NUM(MAGIC_NUMBER.LOTTO_PICK_NUM) + ); } - // TODO: 로또 숫자 validate 숫자 범위 - // 숫자 중복 검사 + numbers.forEach((number) => { + if (!Number.isInteger(Number(number))) { + throw new Error(MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.NOT_INT); + } + this.#validateIntsRange( + number, + MAGIC_NUMBER.LOTTO_MIN_NUM, + MAGIC_NUMBER.LOTTO_MAX_NUM + ); + }); + } + #validateIntsRange(number, min, max) { + if (Number(number) < min || Number(number) > max) { + throw new Error( + MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.RANGE_INT(min, max) + ); + } } - // TODO: 추가 기능 구현 + #validateDuplicate(numbers) { + const set = new Set(numbers); + if (numbers.length !== set.size) { + throw new Error(MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.DUPLICATE_INT); + } + } + + getLottoNumbers() { + return this.#numbers; + } } -export default Lotto; \ No newline at end of file +export default Lotto; diff --git a/src/constants/messages.js b/src/constants/messages.js index 3f1aa2dfc..b0ff0d43f 100644 --- a/src/constants/messages.js +++ b/src/constants/messages.js @@ -19,5 +19,9 @@ export const MESSAGES = Object.freeze({ NOT_INT: `정수가 아닌 숫자입니다.\n`, SMALL_INT: `정수가 1,000 미만입니다.\n`, NOT_UNIT_INT: `입력 받은 수가 1,000 단위가 아닙니다.\n`, + RANGE_INT: (min, max) => + `${min}에서 ${max} 사이의 정수를 입력해야 합니다.\n`, + LOTTO_PICK_NUM: (pickNum) => `로또 번호는 ${pickNum}개여야 합니다.\n`, + DUPLICATE_INT: `중복된 숫자가 있습니다.\n`, }, }); From 1d2a34bdbebbb10abda111378b306b16c264b6af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 14:10:52 +0000 Subject: [PATCH 14/17] =?UTF-8?q?feat:=20=EB=B3=B4=EB=84=88=EC=8A=A4=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=9E=85=EB=A0=A5=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 ++- __tests__/LottoVendingMachineTest.js | 2 +- src/Lotto.js | 1 - src/LottoVendingMachine.js | 53 +++++++++++++++++++++------- src/constants/magicNumber.js | 1 + 5 files changed, 47 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 2a71a7415..9b0fc35e2 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,10 @@ - [x] 양의 정수 판별 - [x] 1~45 숫자 범위 - [x] 중복되지 않는 6개의 숫자 입력 - - [ ] 중복되지 않는 1개의 보너스 번호 입력 +- [x] 보너스 번호 입력 기능 + - [x] 양의 정수 판별 + - [x] 1~45 숫자 범위 + - [x] 중복되지 않는 6개의 숫자 입력 - [ ] 당첨 확인(등수 판별) 기능 - [ ] 당첨 내역 확인 및 출력 - [ ] 수익률 계산 및 출력 diff --git a/__tests__/LottoVendingMachineTest.js b/__tests__/LottoVendingMachineTest.js index 8d56b2741..3b3ec7b62 100644 --- a/__tests__/LottoVendingMachineTest.js +++ b/__tests__/LottoVendingMachineTest.js @@ -26,7 +26,7 @@ const runException = async (input) => { // when const LottoVM = new LottoVendingMachine(); - await LottoVM.purchaseLottoAmount(); + await LottoVM.purchaseLottoAmountInput(); // then expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("[ERROR]")); diff --git a/src/Lotto.js b/src/Lotto.js index 9650e2a61..ae8820a4f 100644 --- a/src/Lotto.js +++ b/src/Lotto.js @@ -1,4 +1,3 @@ -import { MissionUtils } from "@woowacourse/mission-utils"; import { MAGIC_NUMBER } from "./constants/magicNumber.js"; import { MESSAGES } from "./constants/messages.js"; diff --git a/src/LottoVendingMachine.js b/src/LottoVendingMachine.js index 7c4e5bf57..79a35063e 100644 --- a/src/LottoVendingMachine.js +++ b/src/LottoVendingMachine.js @@ -12,19 +12,12 @@ class LottoVendingMachine { async run() { await this.purchaseLottoAmountInput(); - // 로또 번호 발행() - // 로또 번호 검사() this.issueLottos(); - // 발행한 로또 수량 및 번호 오름차순 출력() this.printIssueLottosInfo(); - // 당첨번호 입력() - // 당첨 번호 검사() await this.winNumbersInput(); - // 보너스번호 입력() - // 보너스 번호 검사() await this.bounsNumberInput(); // 당첨내역 출력() @@ -63,15 +56,15 @@ class LottoVendingMachine { issueLottos() { while (this.#lottos.length < this.#lottoAmount) { - this.#lottos.push(this.#pickRandomLotto()); + this.#lottos.push(this.#pickRandomLotto(MAGIC_NUMBER.LOTTO_PICK_NUM)); } } - #pickRandomLotto() { + #pickRandomLotto(pickNum) { return MissionUtils.Random.pickUniqueNumbersInRange( MAGIC_NUMBER.LOTTO_MIN_NUM, MAGIC_NUMBER.LOTTO_MAX_NUM, - MAGIC_NUMBER.LOTTO_PICK_NUM + pickNum ); } @@ -84,12 +77,13 @@ class LottoVendingMachine { async winNumbersInput() { while (true) { try { + Console.print(""); let winNumbers = await Console.readLineAsync( MESSAGES.INPUT.WIN_NUMBERS ); winNumbers = winNumbers.split(",").map((n) => Number(n)); const lotto = new Lotto(winNumbers); - this.#winNumbers = winNumbers; + this.#winNumbers = lotto.getLottoNumbers(); return this.#winNumbers; } catch (e) { @@ -98,7 +92,42 @@ class LottoVendingMachine { } } - bounsNumberInput() {} + async bounsNumberInput() { + while (true) { + try { + Console.print(""); + let bonusNumber = await Console.readLineAsync( + MESSAGES.INPUT.BOUNS_NUMBER + ); + + if (!Number.isInteger(Number(bonusNumber))) { + throw new Error(MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.NOT_INT); + } + this.#validateIntsRange( + bonusNumber, + MAGIC_NUMBER.LOTTO_MIN_NUM, + MAGIC_NUMBER.LOTTO_MAX_NUM + ); + + if (this.#winNumbers.includes(Number(bonusNumber))) { + throw new Error(MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.DUPLICATE_INT); + } + this.#bounsNumber = bonusNumber; + + return this.#bounsNumber; + } catch (e) { + Console.print(e.message); + } + } + } + + #validateIntsRange(number, min, max) { + if (Number(number) < min || Number(number) > max) { + throw new Error( + MESSAGES.ERROR.PREFIX + MESSAGES.ERROR.RANGE_INT(min, max) + ); + } + } } export default LottoVendingMachine; diff --git a/src/constants/magicNumber.js b/src/constants/magicNumber.js index a99ed8bd6..8728c533e 100644 --- a/src/constants/magicNumber.js +++ b/src/constants/magicNumber.js @@ -2,4 +2,5 @@ export const MAGIC_NUMBER = Object.freeze({ LOTTO_MIN_NUM: 1, LOTTO_MAX_NUM: 45, LOTTO_PICK_NUM: 6, + LOTTO_BONUS_NUM_COUNT: 1, }); From a77ef2917553bd19cac2654a93e917bb2122f024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 14:47:53 +0000 Subject: [PATCH 15/17] =?UTF-8?q?feat:=20=EC=88=98=EC=9D=B5=EB=A5=A0=20?= =?UTF-8?q?=EB=B0=8F=20=EB=A7=9E=EC=B6=98=20=EA=B0=9C=EC=88=98=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/LottoVendingMachine.js | 59 ++++++++++++++++++++++++++++++++++++++ src/constants/messages.js | 14 ++++----- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/src/LottoVendingMachine.js b/src/LottoVendingMachine.js index 79a35063e..4b21a71dd 100644 --- a/src/LottoVendingMachine.js +++ b/src/LottoVendingMachine.js @@ -8,6 +8,7 @@ class LottoVendingMachine { #lottos = []; #winNumbers = []; #bounsNumber = 0; + #winCount = {}; async run() { await this.purchaseLottoAmountInput(); @@ -21,6 +22,7 @@ class LottoVendingMachine { await this.bounsNumberInput(); // 당첨내역 출력() + this.printWinInfo(); // 총 수익률 출력() } @@ -128,6 +130,63 @@ class LottoVendingMachine { ); } } + + printWinInfo() { + const winCount = this.#calcWin(); + + Console.print(""); + Console.print(MESSAGES.OUTPUT.WIN_RATE); + Console.print(MESSAGES.OUTPUT.WIN_DETAIL_5th(winCount["3"] ?? 0)); + Console.print(MESSAGES.OUTPUT.WIN_DETAIL_4th(winCount["4"] ?? 0)); + Console.print(MESSAGES.OUTPUT.WIN_DETAIL_3rd(winCount["5"] ?? 0)); + Console.print(MESSAGES.OUTPUT.WIN_DETAIL_2nd(winCount["5.1"] ?? 0)); + Console.print(MESSAGES.OUTPUT.WIN_DETAIL_1st(winCount["6"] ?? 0)); + Console.print(MESSAGES.OUTPUT.PROFIT_RATE(this.#calcProfitRate())); + } + + #calcWin() { + const hasSameNumArray = this.#lottos.map((lotto) => { + const sameNum = lotto.reduce( + (acc, cur) => acc + Number(this.#winNumbers.includes(cur)), + 0 + ); + + if (sameNum === 5 && lotto.includes(this.#bounsNumber)) { + return 5.1; + } + return sameNum; + }); + + const winCount = {}; + + hasSameNumArray.forEach((num) => { + winCount[num] = (winCount[num] ?? 0) + 1; + }); + + this.#winCount = winCount; + return winCount; + } + + #calcProfitRate() { + const winCount = this.#winCount; + const totalPrize = + (winCount["3"] ?? 0) * 5000 + + (winCount["4"] ?? 0) * 50000 + + (winCount["5"] ?? 0) * 1500000 + + (winCount["5.1"] ?? 0) * 30000000 + + (winCount["6"] ?? 0) * 2000000000; + const totalMoney = this.#lottoAmount * 1000; + + return this.#formatProfitRate( + Math.round((totalPrize / totalMoney) * 10000) / 100 + ); + } + + #formatProfitRate(rate) { + const parts = rate.toFixed(1).split("."); + const integerPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); + return `${integerPart}.${parts[1]}`; + } } export default LottoVendingMachine; diff --git a/src/constants/messages.js b/src/constants/messages.js index b0ff0d43f..40e0e2a5e 100644 --- a/src/constants/messages.js +++ b/src/constants/messages.js @@ -6,13 +6,13 @@ export const MESSAGES = Object.freeze({ }, OUTPUT: { PURCHASE_LOTTO_NUMBER: (n) => `${n}개를 구매했습니다.`, - WIN_RATE: "당첨 통계\n---\n", - WIN_DETAIL_5th: (n) => `3개 일치 (5,000원) - ${n}개\n`, - WIN_DETAIL_4th: (n) => `4개 일치 (50,000원) - ${n}개\n`, - WIN_DETAIL_3rd: (n) => `5개 일치 (1,500,000원) - ${n}개\n`, - WIN_DETAIL_2nd: (n) => `5개 일치, 보너스 볼 일치 (30,000,000원) - ${n}개\n`, - WIN_DETAIL_1st: (n) => `6개 일치 (2,000,000,000원) - ${n}개\n`, - PROFIT_RATE: (profitRate) => `총 수익률은 ${profitRate}%입니다.\n`, + WIN_RATE: "당첨 통계\n---", + WIN_DETAIL_5th: (n) => `3개 일치 (5,000원) - ${n}개`, + WIN_DETAIL_4th: (n) => `4개 일치 (50,000원) - ${n}개`, + WIN_DETAIL_3rd: (n) => `5개 일치 (1,500,000원) - ${n}개`, + WIN_DETAIL_2nd: (n) => `5개 일치, 보너스 볼 일치 (30,000,000원) - ${n}개`, + WIN_DETAIL_1st: (n) => `6개 일치 (2,000,000,000원) - ${n}개`, + PROFIT_RATE: (profitRate) => `총 수익률은 ${profitRate}%입니다.`, }, ERROR: { PREFIX: `[ERROR] `, From d0894767dd39d7cfaa0adf114f96ce47288c8da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 14:48:03 +0000 Subject: [PATCH 16/17] =?UTF-8?q?feat:=20=EC=88=98=EC=9D=B5=EB=A5=A0=20?= =?UTF-8?q?=EB=B0=8F=20=EB=A7=9E=EC=B6=98=20=EA=B0=9C=EC=88=98=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9b0fc35e2..8910d87ed 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,10 @@ - [x] 양의 정수 판별 - [x] 1~45 숫자 범위 - [x] 중복되지 않는 6개의 숫자 입력 -- [ ] 당첨 확인(등수 판별) 기능 -- [ ] 당첨 내역 확인 및 출력 -- [ ] 수익률 계산 및 출력 - - [ ] 소수점 둘째 자리에서 반올림 +- [x] 당첨 확인(등수 판별) 기능 +- [x] 당첨 내역 확인 및 출력 +- [x] 수익률 계산 및 출력 + - [x] 소수점 둘째 자리에서 반올림 ## 중점 사항 From 7a221c35eda1fac1a73d27ae7d117906b572d554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=80=ED=99=98=20=28Jihwan=20Kim=29?= Date: Mon, 4 Nov 2024 14:48:20 +0000 Subject: [PATCH 17/17] fix: add await --- src/App.js | 2 +- src/LottoVendingMachine.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/App.js b/src/App.js index 803f99790..c1672118a 100644 --- a/src/App.js +++ b/src/App.js @@ -3,7 +3,7 @@ import LottoVendingMachine from "./LottoVendingMachine.js"; class App { async run() { const LottoVM = new LottoVendingMachine(); - LottoVM.run(); + await LottoVM.run(); } } diff --git a/src/LottoVendingMachine.js b/src/LottoVendingMachine.js index 4b21a71dd..58df6c321 100644 --- a/src/LottoVendingMachine.js +++ b/src/LottoVendingMachine.js @@ -71,9 +71,8 @@ class LottoVendingMachine { } printIssueLottosInfo() { - Console.print(""); Console.print(MESSAGES.OUTPUT.PURCHASE_LOTTO_NUMBER(this.#lottoAmount)); - this.#lottos.forEach((lotto) => Console.print(lotto)); + this.#lottos.forEach((lotto) => Console.print(`[${lotto.join(", ")}]`)); } async winNumbersInput() {