From 54db27f3fa4340c0c392ed14dfb880f131e193eb Mon Sep 17 00:00:00 2001 From: SEMIN-97 Date: Sun, 7 Sep 2025 00:59:39 +0900 Subject: [PATCH 01/12] =?UTF-8?q?docs:=20=EC=BD=98=EC=86=94=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=20=EB=A1=9C=EB=98=90=20=EA=B2=8C=EC=9E=84=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=9A=94=EA=B5=AC=20=EC=82=AC=ED=95=AD=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/REQUIREMENTS.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/docs/REQUIREMENTS.md diff --git a/src/docs/REQUIREMENTS.md b/src/docs/REQUIREMENTS.md new file mode 100644 index 000000000..1a0369d59 --- /dev/null +++ b/src/docs/REQUIREMENTS.md @@ -0,0 +1,29 @@ +# 기능 요구 사항 + +## 1단계 - 콘솔 기반 로또 게임 + +### 입출력 +-[ ] 사용자로부터 구입 금액을 입력받는다. +-[ ] 사용자로부터 당첨 번호(6개)와 보너스 번호(1개)를 입력받는다. +-[ ] 발행된 로또 번호들을 출력한다. +-[ ] 등수별 당첨 개수를 출력한다. + - `3개 일치 (5,000원) - 1개` 의 형식으로 출력한다. +-[ ] 총 수익률을 출력한다. + +### 로또 +-[ ] 로또 1장의 가격은 1,000원이다. +-[ ] 구입 금액에 따라 구입 금액에 해당하는 만큼 로또를 발행해야 한다. +-[ ] 로또 1장은 1~45 범위의 중복되지 않은 6개의 숫자로 구성된다. + +### 로또 게임 +-[ ] 입력받은 당첨 번호와 보너스 번호를 검증한다. +-[ ] 사용자가 구매한 로또 번호와 당첨 번호를 비교해 등수를 판정한다. +-[ ] 당첨 기준 및 상금은 다음과 같다: + - 1등: 6개 번호 일치 / 2,000,000,000원 + - 2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원 + - 3등: 5개 번호 일치 / 1,500,000원 + - 4등: 4개 번호 일치 / 50,000원 + - 5등: 3개 번호 일치 / 5,000원 +-[ ] 전체 당첨 결과를 집계한다. +-[ ] 총 수익률을 계산한다. (총 당첨금 ÷ 구입 금액 * 100) + - 수익률은 소수점 셋째 자리에서 반올림하여 둘째 자리까지만 유지한다. From 6424440c8040a4415c740691296b51c5152bebf4 Mon Sep 17 00:00:00 2001 From: SEMIN-97 Date: Sun, 7 Sep 2025 16:24:06 +0900 Subject: [PATCH 02/12] =?UTF-8?q?feat:=20=EA=B5=AC=EC=9E=85=20=EA=B8=88?= =?UTF-8?q?=EC=95=A1=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EA=B5=AC=EC=9E=85=20?= =?UTF-8?q?=EA=B8=88=EC=95=A1=EC=97=90=20=ED=95=B4=EB=8B=B9=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=A7=8C=ED=81=BC=20=EB=A1=9C=EB=98=90=EB=A5=BC=20?= =?UTF-8?q?=EB=B0=9C=ED=96=89=20=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __tests__/lotto.test.js | 26 ++++++++++++++++++++++++++ src/docs/REQUIREMENTS.md | 4 ++-- src/errors/lottoError.js | 7 +++++++ src/lotto.js | 11 +++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 __tests__/lotto.test.js create mode 100644 src/errors/lottoError.js create mode 100644 src/lotto.js diff --git a/__tests__/lotto.test.js b/__tests__/lotto.test.js new file mode 100644 index 000000000..06029a703 --- /dev/null +++ b/__tests__/lotto.test.js @@ -0,0 +1,26 @@ +import { getTicket } from '../src/lotto.js' +import { LottoError } from '../src/errors/lottoError.js' + +describe('getTicket 함수 테스트', () => { + it('1,000원을 입력하면 1장을 발급한다', () => { + const money = 1000 + + expect(getTicket(money)).toBe(1) + }) + + it('1,000원 단위로 티켓을 발급하며, 2,500원을 입력하면 2장을 발급한다', () => { + const money = 2500 + + expect(getTicket(money)).toBe(2) + }) + + it('1,000원 미만은 에러를 발생시킨다', () => { + const money = 900 + + expect(() => getTicket(money)).toThrow(LottoError.TicketPriceTooLow()) + }) + + it('금액을 입력하지 않으면 에러를 발생시킨다', () => { + expect(() => getTicket()).toThrow(LottoError.TicketPriceTooLow()) + }) +}) \ No newline at end of file diff --git a/src/docs/REQUIREMENTS.md b/src/docs/REQUIREMENTS.md index 1a0369d59..bf72fc448 100644 --- a/src/docs/REQUIREMENTS.md +++ b/src/docs/REQUIREMENTS.md @@ -11,8 +11,8 @@ -[ ] 총 수익률을 출력한다. ### 로또 --[ ] 로또 1장의 가격은 1,000원이다. --[ ] 구입 금액에 따라 구입 금액에 해당하는 만큼 로또를 발행해야 한다. +-[x] 로또 1장의 가격은 1,000원이다. +-[x] 구입 금액에 따라 구입 금액에 해당하는 만큼 로또를 발행해야 한다. -[ ] 로또 1장은 1~45 범위의 중복되지 않은 6개의 숫자로 구성된다. ### 로또 게임 diff --git a/src/errors/lottoError.js b/src/errors/lottoError.js new file mode 100644 index 000000000..624511638 --- /dev/null +++ b/src/errors/lottoError.js @@ -0,0 +1,7 @@ +function ticketPriceTooLow() { + return new Error('구매 금액은 최소 1,000원 이상이어야 합니다.') +} + +export const LottoError = { + TicketPriceTooLow: ticketPriceTooLow, +} \ No newline at end of file diff --git a/src/lotto.js b/src/lotto.js new file mode 100644 index 000000000..9cbe9e8df --- /dev/null +++ b/src/lotto.js @@ -0,0 +1,11 @@ +import { LottoError } from './errors/lottoError.js' + +const LOTTO_PRICE = 1000 + +export function getTicket(money) { + if (!money || money < LOTTO_PRICE) { + throw LottoError.TicketPriceTooLow() + } + + return Math.floor(money / LOTTO_PRICE) +} \ No newline at end of file From 95fe914fccbc72778406594bd091c05b8757de8f Mon Sep 17 00:00:00 2001 From: SEMIN-97 Date: Sun, 7 Sep 2025 16:33:03 +0900 Subject: [PATCH 03/12] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=201=EC=9E=A5?= =?UTF-8?q?=EC=9D=80=201~45=20=EB=B2=94=EC=9C=84=EC=9D=98=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=EB=90=98=EC=A7=80=20=EC=95=8A=EC=9D=80=206=EA=B0=9C?= =?UTF-8?q?=EC=9D=98=20=EC=88=AB=EC=9E=90=EB=A1=9C=20=EA=B5=AC=EC=84=B1?= =?UTF-8?q?=EB=90=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __tests__/lotto.test.js | 25 ++++++++++++++++++++++++- src/lotto.js | 12 ++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/__tests__/lotto.test.js b/__tests__/lotto.test.js index 06029a703..39a9e0c83 100644 --- a/__tests__/lotto.test.js +++ b/__tests__/lotto.test.js @@ -1,4 +1,4 @@ -import { getTicket } from '../src/lotto.js' +import { generateLottoNumber, getTicket } from '../src/lotto.js' import { LottoError } from '../src/errors/lottoError.js' describe('getTicket 함수 테스트', () => { @@ -23,4 +23,27 @@ describe('getTicket 함수 테스트', () => { it('금액을 입력하지 않으면 에러를 발생시킨다', () => { expect(() => getTicket()).toThrow(LottoError.TicketPriceTooLow()) }) +}) + +describe('generateLottoNumber 함수 테스트', () => { + it('6개의 숫자로 구성된다', () => { + const numbers = generateLottoNumber() + + expect(numbers).toHaveLength(6) + }) + + it('6개의 숫자는 중복되지 않는다', () => { + const numbers = new Set(generateLottoNumber()) + + expect(numbers.size).toBe(6) + }) + + it('1~45 범위의 숫자들로 구성된다', () => { + const numbers = generateLottoNumber() + + numbers.forEach(number => { + expect(number).toBeGreaterThanOrEqual(1) + expect(number).toBeLessThanOrEqual(45) + }) + }) }) \ No newline at end of file diff --git a/src/lotto.js b/src/lotto.js index 9cbe9e8df..3d4765221 100644 --- a/src/lotto.js +++ b/src/lotto.js @@ -1,6 +1,7 @@ import { LottoError } from './errors/lottoError.js' const LOTTO_PRICE = 1000 +const LOTTO_NUMBERS_LENGTH = 6 export function getTicket(money) { if (!money || money < LOTTO_PRICE) { @@ -8,4 +9,15 @@ export function getTicket(money) { } return Math.floor(money / LOTTO_PRICE) +} + +export function generateLottoNumber() { + const numbers = new Set() + + while (numbers.size < LOTTO_NUMBERS_LENGTH) { + const num = Math.floor(Math.random() * 45) + 1 + numbers.add(num) + } + + return Array.from(numbers) } \ No newline at end of file From c2c32c9a25c7dd7e08c00900a12577fb66c71cbd Mon Sep 17 00:00:00 2001 From: SEMIN-97 Date: Sun, 7 Sep 2025 16:38:27 +0900 Subject: [PATCH 04/12] =?UTF-8?q?feat:=20getTicket=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=EA=B0=92=EC=9D=84=20=EB=A1=9C=EB=98=90=20=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EB=B0=B0=EC=97=B4=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __tests__/lotto.test.js | 4 ++-- src/docs/REQUIREMENTS.md | 2 +- src/lotto.js | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/__tests__/lotto.test.js b/__tests__/lotto.test.js index 39a9e0c83..206c6caec 100644 --- a/__tests__/lotto.test.js +++ b/__tests__/lotto.test.js @@ -5,13 +5,13 @@ describe('getTicket 함수 테스트', () => { it('1,000원을 입력하면 1장을 발급한다', () => { const money = 1000 - expect(getTicket(money)).toBe(1) + expect(getTicket(money)).toHaveLength(1) }) it('1,000원 단위로 티켓을 발급하며, 2,500원을 입력하면 2장을 발급한다', () => { const money = 2500 - expect(getTicket(money)).toBe(2) + expect(getTicket(money)).toHaveLength(2) }) it('1,000원 미만은 에러를 발생시킨다', () => { diff --git a/src/docs/REQUIREMENTS.md b/src/docs/REQUIREMENTS.md index bf72fc448..683a7710e 100644 --- a/src/docs/REQUIREMENTS.md +++ b/src/docs/REQUIREMENTS.md @@ -13,7 +13,7 @@ ### 로또 -[x] 로또 1장의 가격은 1,000원이다. -[x] 구입 금액에 따라 구입 금액에 해당하는 만큼 로또를 발행해야 한다. --[ ] 로또 1장은 1~45 범위의 중복되지 않은 6개의 숫자로 구성된다. +-[x] 로또 1장은 1~45 범위의 중복되지 않은 6개의 숫자로 구성된다. ### 로또 게임 -[ ] 입력받은 당첨 번호와 보너스 번호를 검증한다. diff --git a/src/lotto.js b/src/lotto.js index 3d4765221..2483785ee 100644 --- a/src/lotto.js +++ b/src/lotto.js @@ -8,7 +8,8 @@ export function getTicket(money) { throw LottoError.TicketPriceTooLow() } - return Math.floor(money / LOTTO_PRICE) + const ticketCount = Math.floor(money / LOTTO_PRICE) + return Array.from({ length: ticketCount }, () => generateLottoNumber()) } export function generateLottoNumber() { From 5e8718d6ba72d2272e9767dacb8ae364557cc11e Mon Sep 17 00:00:00 2001 From: SEMIN-97 Date: Sun, 7 Sep 2025 17:45:40 +0900 Subject: [PATCH 05/12] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EA=B0=80=20=EA=B5=AC=EB=A7=A4=ED=95=9C=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=EC=99=80=20=EB=8B=B9=EC=B2=A8=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=EB=A5=BC=20=EB=B9=84=EA=B5=90=ED=95=B4=20=EB=93=B1?= =?UTF-8?q?=EC=88=98=EB=A5=BC=20=ED=8C=90=EC=A0=95=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __tests__/lottoGame.test.js | 39 +++++++++++++++++++++++++++++++++++++ src/docs/REQUIREMENTS.md | 6 +++--- src/lottoGame.js | 20 +++++++++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 __tests__/lottoGame.test.js create mode 100644 src/lottoGame.js diff --git a/__tests__/lottoGame.test.js b/__tests__/lottoGame.test.js new file mode 100644 index 000000000..91a7a6cb4 --- /dev/null +++ b/__tests__/lottoGame.test.js @@ -0,0 +1,39 @@ +import { getRank } from '../src/lottoGame.js' + +describe('getRank 함수 테스트', () => { + let winningNumbers + let winningBonusNumber + + beforeEach(() => { + winningNumbers = [1, 2, 3, 4, 5, 6] + winningBonusNumber = 7 + }) + + it('숫자 3개가 일치하면 5,000원을 받는다', () => { + const ticketNumbers = [1, 2, 3, 14, 15, 16] + const rank = getRank(ticketNumbers, winningNumbers, winningBonusNumber) + + expect(rank.price).toBe(5_000) + }) + + it('보너스 번호 제외 숫자 5개가 일치하면 1,500,000원을 받는다', () => { + const ticketNumbers = [1, 2, 3, 4, 5, 16] + const rank = getRank(ticketNumbers, winningNumbers, winningBonusNumber) + + expect(rank.price).toBe(1_500_000) + }) + + it('보너스 번호 포함 숫자 6개가 일치하면 30,000,000원을 받는다', () => { + const ticketNumbers = [1, 2, 3, 4, 5, 7] + const rank = getRank(ticketNumbers, winningNumbers, winningBonusNumber) + + expect(rank.price).toBe(30_000_000) + }) + + it('숫자 6개가 전부 일치하면 2,000,000,000원을 받는다', () => { + const ticketNumbers = [1, 2, 3, 4, 5, 6] + const rank = getRank(ticketNumbers, winningNumbers, winningBonusNumber) + + expect(rank.price).toBe(2_000_000_000) + }) +}) \ No newline at end of file diff --git a/src/docs/REQUIREMENTS.md b/src/docs/REQUIREMENTS.md index 683a7710e..74b5da64f 100644 --- a/src/docs/REQUIREMENTS.md +++ b/src/docs/REQUIREMENTS.md @@ -16,9 +16,9 @@ -[x] 로또 1장은 1~45 범위의 중복되지 않은 6개의 숫자로 구성된다. ### 로또 게임 --[ ] 입력받은 당첨 번호와 보너스 번호를 검증한다. --[ ] 사용자가 구매한 로또 번호와 당첨 번호를 비교해 등수를 판정한다. --[ ] 당첨 기준 및 상금은 다음과 같다: +-[x] 입력받은 당첨 번호와 보너스 번호를 검증한다. +-[x] 사용자가 구매한 로또 번호와 당첨 번호를 비교해 등수를 판정한다. +-[x] 당첨 기준 및 상금은 다음과 같다: - 1등: 6개 번호 일치 / 2,000,000,000원 - 2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원 - 3등: 5개 번호 일치 / 1,500,000원 diff --git a/src/lottoGame.js b/src/lottoGame.js new file mode 100644 index 000000000..0a8a1f2a4 --- /dev/null +++ b/src/lottoGame.js @@ -0,0 +1,20 @@ +const LOTTO_RANK_TABLE = [ + { rank: 1, matchCount: 6, price: 2_000_000_000 }, + { rank: 2, matchCount: 5, price: 30_000_000, bonus: true }, + { rank: 3, matchCount: 5, price: 1_500_000 }, + { rank: 4, matchCount: 4, price: 50_000 }, + { rank: 5, matchCount: 3, price: 5_000 }, +] + +export function getRank(ticketNumbers, winningNumbers, winningBonusNumber) { + const matchCount = ticketNumbers.filter(num => winningNumbers.includes(num)).length; + const hasBonusNumber = ticketNumbers.includes(winningBonusNumber) + + return LOTTO_RANK_TABLE.find(rank => { + if (rank.bonus) { + return hasBonusNumber && rank.matchCount === matchCount + } + + return rank.matchCount === matchCount + }) +} \ No newline at end of file From fe9692f0e18ce5ca16643a8bda5dbb9fb6ef5bd9 Mon Sep 17 00:00:00 2001 From: SEMIN-97 Date: Sun, 7 Sep 2025 19:11:24 +0900 Subject: [PATCH 06/12] =?UTF-8?q?feat:=20=EA=B5=AC=EB=A7=A4=20=EA=B8=88?= =?UTF-8?q?=EC=95=A1=EA=B3=BC=20=EB=8B=B9=EC=B2=A8=EA=B8=88=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=B4=9D=20=EC=88=98=EC=9D=B5=EB=A5=A0=EC=9D=84=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __tests__/lottoGame.test.js | 18 +++++++++++++++++- src/docs/REQUIREMENTS.md | 3 +-- src/lottoGame.js | 8 +++++++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/__tests__/lottoGame.test.js b/__tests__/lottoGame.test.js index 91a7a6cb4..0a6b26e52 100644 --- a/__tests__/lottoGame.test.js +++ b/__tests__/lottoGame.test.js @@ -1,4 +1,4 @@ -import { getRank } from '../src/lottoGame.js' +import { calculateProfitRate, getRank } from '../src/lottoGame.js' describe('getRank 함수 테스트', () => { let winningNumbers @@ -36,4 +36,20 @@ describe('getRank 함수 테스트', () => { expect(rank.price).toBe(2_000_000_000) }) +}) + +describe('calculateProfitRate 함수 테스트', () => { + it('구매 금액과 당첨금으로 총 수익률을 계산한다', () => { + const purchaseAmount = 10_000 + const totalWinnings = 50_000 + + expect(calculateProfitRate(purchaseAmount, totalWinnings)).toBe(500) + }) + + it('수익률은 소수점 셋째 자리에서 반올림하여 둘째 자리까지만 유지한다', () => { + const purchaseAmount = 30_000 + const totalWinnings = 5_000 + + expect(calculateProfitRate(purchaseAmount, totalWinnings)).toBe(16.67) + }) }) \ No newline at end of file diff --git a/src/docs/REQUIREMENTS.md b/src/docs/REQUIREMENTS.md index 74b5da64f..13aba2708 100644 --- a/src/docs/REQUIREMENTS.md +++ b/src/docs/REQUIREMENTS.md @@ -24,6 +24,5 @@ - 3등: 5개 번호 일치 / 1,500,000원 - 4등: 4개 번호 일치 / 50,000원 - 5등: 3개 번호 일치 / 5,000원 --[ ] 전체 당첨 결과를 집계한다. --[ ] 총 수익률을 계산한다. (총 당첨금 ÷ 구입 금액 * 100) +-[x] 총 수익률을 계산한다. (총 당첨금 ÷ 구입 금액 * 100) - 수익률은 소수점 셋째 자리에서 반올림하여 둘째 자리까지만 유지한다. diff --git a/src/lottoGame.js b/src/lottoGame.js index 0a8a1f2a4..c353b9573 100644 --- a/src/lottoGame.js +++ b/src/lottoGame.js @@ -7,7 +7,7 @@ const LOTTO_RANK_TABLE = [ ] export function getRank(ticketNumbers, winningNumbers, winningBonusNumber) { - const matchCount = ticketNumbers.filter(num => winningNumbers.includes(num)).length; + const matchCount = ticketNumbers.filter(num => winningNumbers.includes(num)).length const hasBonusNumber = ticketNumbers.includes(winningBonusNumber) return LOTTO_RANK_TABLE.find(rank => { @@ -17,4 +17,10 @@ export function getRank(ticketNumbers, winningNumbers, winningBonusNumber) { return rank.matchCount === matchCount }) +} + +export function calculateProfitRate(purchaseAmount, totalWinnings) { + const rate = (totalWinnings / purchaseAmount) * 100 + + return Math.round(rate * 100) / 100 } \ No newline at end of file From c8486c88ac1e5fdcdc8f4d421b19e3cce1b9ebfd Mon Sep 17 00:00:00 2001 From: SEMIN-97 Date: Sun, 7 Sep 2025 20:00:22 +0900 Subject: [PATCH 07/12] =?UTF-8?q?feat:=20=EC=BD=98=EC=86=94=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=EC=9D=84=20=EC=B2=98=EB=A6=AC=ED=95=98=EB=8A=94=20rea?= =?UTF-8?q?dlineAsync=20=EC=9C=A0=ED=8B=B8=20=ED=95=A8=EC=88=98=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 --- src/utils/readlineAsync.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/utils/readlineAsync.js diff --git a/src/utils/readlineAsync.js b/src/utils/readlineAsync.js new file mode 100644 index 000000000..69f55c940 --- /dev/null +++ b/src/utils/readlineAsync.js @@ -0,0 +1,23 @@ +import readline from 'readline'; + +export function readLineAsync(query) { + return new Promise((resolve, reject) => { + if (arguments.length !== 1) { + reject(new Error('arguments must be 1')); + } + + if (typeof query !== 'string') { + reject(new Error('query must be string')); + } + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + rl.question(query, (input) => { + rl.close(); + resolve(input); + }); + }); +} \ No newline at end of file From 730d0a647f2c366ea9266b37d1d5c941cf4b42a5 Mon Sep 17 00:00:00 2001 From: SEMIN-97 Date: Sun, 7 Sep 2025 20:15:48 +0900 Subject: [PATCH 08/12] =?UTF-8?q?refactor:=20LOTTO=5FRANK=5FTABLE=EC=9D=98?= =?UTF-8?q?=20price=EB=A5=BC=20prize=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __tests__/lottoGame.test.js | 8 ++++---- src/lottoGame.js | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/__tests__/lottoGame.test.js b/__tests__/lottoGame.test.js index 0a6b26e52..be76f11de 100644 --- a/__tests__/lottoGame.test.js +++ b/__tests__/lottoGame.test.js @@ -13,28 +13,28 @@ describe('getRank 함수 테스트', () => { const ticketNumbers = [1, 2, 3, 14, 15, 16] const rank = getRank(ticketNumbers, winningNumbers, winningBonusNumber) - expect(rank.price).toBe(5_000) + expect(rank.prize).toBe(5_000) }) it('보너스 번호 제외 숫자 5개가 일치하면 1,500,000원을 받는다', () => { const ticketNumbers = [1, 2, 3, 4, 5, 16] const rank = getRank(ticketNumbers, winningNumbers, winningBonusNumber) - expect(rank.price).toBe(1_500_000) + expect(rank.prize).toBe(1_500_000) }) it('보너스 번호 포함 숫자 6개가 일치하면 30,000,000원을 받는다', () => { const ticketNumbers = [1, 2, 3, 4, 5, 7] const rank = getRank(ticketNumbers, winningNumbers, winningBonusNumber) - expect(rank.price).toBe(30_000_000) + expect(rank.prize).toBe(30_000_000) }) it('숫자 6개가 전부 일치하면 2,000,000,000원을 받는다', () => { const ticketNumbers = [1, 2, 3, 4, 5, 6] const rank = getRank(ticketNumbers, winningNumbers, winningBonusNumber) - expect(rank.price).toBe(2_000_000_000) + expect(rank.prize).toBe(2_000_000_000) }) }) diff --git a/src/lottoGame.js b/src/lottoGame.js index c353b9573..842ed50b4 100644 --- a/src/lottoGame.js +++ b/src/lottoGame.js @@ -1,9 +1,9 @@ const LOTTO_RANK_TABLE = [ - { rank: 1, matchCount: 6, price: 2_000_000_000 }, - { rank: 2, matchCount: 5, price: 30_000_000, bonus: true }, - { rank: 3, matchCount: 5, price: 1_500_000 }, - { rank: 4, matchCount: 4, price: 50_000 }, - { rank: 5, matchCount: 3, price: 5_000 }, + { rank: 1, matchCount: 6, prize: 2_000_000_000 }, + { rank: 2, matchCount: 5, prize: 30_000_000, bonus: true }, + { rank: 3, matchCount: 5, prize: 1_500_000 }, + { rank: 4, matchCount: 4, prize: 50_000 }, + { rank: 5, matchCount: 3, prize: 5_000 }, ] export function getRank(ticketNumbers, winningNumbers, winningBonusNumber) { From 5dc4e3b9e1c3402bada3e0b6dbc4f94785a1f41c Mon Sep 17 00:00:00 2001 From: SEMIN-97 Date: Sun, 7 Sep 2025 20:55:31 +0900 Subject: [PATCH 09/12] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=20=EC=9E=85=EC=B6=9C=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/REQUIREMENTS.md | 10 +++--- src/lotto.js | 2 +- src/lottoGame.js | 2 +- src/step1-index.js | 68 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/docs/REQUIREMENTS.md b/src/docs/REQUIREMENTS.md index 13aba2708..7aecd8fbd 100644 --- a/src/docs/REQUIREMENTS.md +++ b/src/docs/REQUIREMENTS.md @@ -3,12 +3,12 @@ ## 1단계 - 콘솔 기반 로또 게임 ### 입출력 --[ ] 사용자로부터 구입 금액을 입력받는다. --[ ] 사용자로부터 당첨 번호(6개)와 보너스 번호(1개)를 입력받는다. --[ ] 발행된 로또 번호들을 출력한다. --[ ] 등수별 당첨 개수를 출력한다. +-[x] 사용자로부터 구입 금액을 입력받는다. +-[x] 사용자로부터 당첨 번호(6개)와 보너스 번호(1개)를 입력받는다. +-[x] 발행된 로또 번호들을 출력한다. +-[x] 등수별 당첨 개수를 출력한다. - `3개 일치 (5,000원) - 1개` 의 형식으로 출력한다. --[ ] 총 수익률을 출력한다. +-[x] 총 수익률을 출력한다. ### 로또 -[x] 로또 1장의 가격은 1,000원이다. diff --git a/src/lotto.js b/src/lotto.js index 2483785ee..7a5a9af4b 100644 --- a/src/lotto.js +++ b/src/lotto.js @@ -1,6 +1,6 @@ import { LottoError } from './errors/lottoError.js' -const LOTTO_PRICE = 1000 +export const LOTTO_PRICE = 1000 const LOTTO_NUMBERS_LENGTH = 6 export function getTicket(money) { diff --git a/src/lottoGame.js b/src/lottoGame.js index 842ed50b4..25f934777 100644 --- a/src/lottoGame.js +++ b/src/lottoGame.js @@ -1,4 +1,4 @@ -const LOTTO_RANK_TABLE = [ +export const LOTTO_RANK_TABLE = [ { rank: 1, matchCount: 6, prize: 2_000_000_000 }, { rank: 2, matchCount: 5, prize: 30_000_000, bonus: true }, { rank: 3, matchCount: 5, prize: 1_500_000 }, diff --git a/src/step1-index.js b/src/step1-index.js index 44313b450..9b16dd2e1 100644 --- a/src/step1-index.js +++ b/src/step1-index.js @@ -1,4 +1,64 @@ -/** - * step 1의 시작점이 되는 파일입니다. - * 브라우저 환경에서 사용하는 css 파일 등을 불러올 경우 정상적으로 빌드할 수 없습니다. - */ +import { readLineAsync } from './utils/readlineAsync.js' +import { getTicket, LOTTO_PRICE } from './lotto.js' +import { calculateProfitRate, getRank, LOTTO_RANK_TABLE } from './lottoGame.js' + +async function start() { + const purchaseAmount = await readLineAsync('구입 금액을 입력해 주세요. ') + const tickets = getTicket(purchaseAmount) + + printTicket(tickets) + + let winningNumbers = await readLineAsync('당첨 번호를 입력해 주세요. ') + winningNumbers = winningNumbers.split(',').map(num => Number(num.trim())) + const bonusNumber = await readLineAsync('보너스 번호를 입력해 주세요. ') + + printResults(tickets, winningNumbers, bonusNumber) +} + +function printTicket(tickets) { + console.log(`${tickets.length}개를 구매했습니다.`) + tickets.forEach(ticket => console.log(ticket)) +} + +function printResults(tickets, winningNumbers, bonusNumber) { + const results = tickets.map(ticket => getRank(ticket, winningNumbers, bonusNumber)).filter(Boolean) + + if (!results?.length) { + console.log('낙첨되었습니다.') + return + } + + const stats = getRankStatistics(results) + + console.log('당첨 통계') + console.log('--------------------') + + let totalWinnings = 0 + stats.forEach(({ matchCount, prize, bonus, count }) => { + totalWinnings += prize * count + console.log(`${matchCount}개 일치${bonus ? ', 보너스 볼 일치': ''} (${prize.toLocaleString()}원) - ${count.toLocaleString()}개`) + }) + + console.log(`총 수익률은 ${calculateProfitRate(tickets.length * LOTTO_PRICE, totalWinnings)}% 입니다.`) +} + +function getRankStatistics(results) { + const stats = LOTTO_RANK_TABLE.map(rank => ({ + ...rank, + count: 0, + })) + + results.forEach(result => { + const rankIndex = stats.findIndex(rank => + rank.matchCount === result.matchCount && rank.bonus === result.bonus + ) + + if (rankIndex !== -1) { + stats[rankIndex].count += 1 + } + }) + + return stats.reverse() +} + +await start() \ No newline at end of file From bb8cb74eeeaa64da6cc2ad482b7dbb12aef0040d Mon Sep 17 00:00:00 2001 From: SEMIN-97 Date: Sun, 7 Sep 2025 22:36:48 +0900 Subject: [PATCH 10/12] =?UTF-8?q?refactor:=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=EC=9E=85=EC=B6=9C=EB=A0=A5=20=EB=8B=A8?= =?UTF-8?q?=EA=B3=84=EB=A5=BC=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A1=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=ED=95=98=EA=B3=A0=20=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=84=20lottoPrint.js=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lottoPrint.js | 22 +++++++++++++++++++++ src/step1-index.js | 49 +++++++++++++++++++++------------------------- 2 files changed, 44 insertions(+), 27 deletions(-) create mode 100644 src/lottoPrint.js diff --git a/src/lottoPrint.js b/src/lottoPrint.js new file mode 100644 index 000000000..4ce8cb80a --- /dev/null +++ b/src/lottoPrint.js @@ -0,0 +1,22 @@ +export function printTicket(tickets) { + console.log(`${tickets.length}개를 구매했습니다.`) + tickets.forEach(ticket => console.log(ticket)) +} + +export function printResults(stats, totalWinnings) { + if (!totalWinnings) { + console.log('낙첨되었습니다.') + return + } + + console.log('당첨 통계') + console.log('--------------------') + + stats.forEach(({ matchCount, prize, bonus, count }) => { + console.log(`${matchCount}개 일치${bonus ? ', 보너스 볼 일치': ''} (${prize.toLocaleString()}원) - ${count.toLocaleString()}개`) + }) +} + +export function printProfit(profitRate) { + console.log(`총 수익률은 ${profitRate.toLocaleString()}% 입니다.`) +} \ No newline at end of file diff --git a/src/step1-index.js b/src/step1-index.js index 9b16dd2e1..c0184e2ab 100644 --- a/src/step1-index.js +++ b/src/step1-index.js @@ -1,54 +1,49 @@ import { readLineAsync } from './utils/readlineAsync.js' import { getTicket, LOTTO_PRICE } from './lotto.js' import { calculateProfitRate, getRank, LOTTO_RANK_TABLE } from './lottoGame.js' +import { printProfit, printResults, printTicket } from './lottoPrint.js' async function start() { - const purchaseAmount = await readLineAsync('구입 금액을 입력해 주세요. ') - const tickets = getTicket(purchaseAmount) - + const tickets = await getTickets() printTicket(tickets) - let winningNumbers = await readLineAsync('당첨 번호를 입력해 주세요. ') - winningNumbers = winningNumbers.split(',').map(num => Number(num.trim())) - const bonusNumber = await readLineAsync('보너스 번호를 입력해 주세요. ') + const { winningNumbers, winningBonusNumber } = await getWinningNumber() + const { stats, totalWinnings } = getStatistics(tickets, winningNumbers, winningBonusNumber) + printResults(stats, totalWinnings) - printResults(tickets, winningNumbers, bonusNumber) + const profitRate = calculateProfitRate(tickets.length * LOTTO_PRICE, totalWinnings) + printProfit(profitRate) } -function printTicket(tickets) { - console.log(`${tickets.length}개를 구매했습니다.`) - tickets.forEach(ticket => console.log(ticket)) +async function getTickets() { + const purchaseAmount = await readLineAsync('구입 금액을 입력해 주세요. ') + return getTicket(purchaseAmount) } -function printResults(tickets, winningNumbers, bonusNumber) { - const results = tickets.map(ticket => getRank(ticket, winningNumbers, bonusNumber)).filter(Boolean) - - if (!results?.length) { - console.log('낙첨되었습니다.') - return - } +async function getWinningNumber() { + let winningNumbers = await readLineAsync('당첨 번호를 입력해 주세요. ') + winningNumbers = winningNumbers.split(',').map(num => Number(num.trim())) - const stats = getRankStatistics(results) + const winningBonusNumber = await readLineAsync('보너스 번호를 입력해 주세요. ') - console.log('당첨 통계') - console.log('--------------------') + return { winningNumbers, winningBonusNumber: Number(winningBonusNumber) } +} - let totalWinnings = 0 - stats.forEach(({ matchCount, prize, bonus, count }) => { - totalWinnings += prize * count - console.log(`${matchCount}개 일치${bonus ? ', 보너스 볼 일치': ''} (${prize.toLocaleString()}원) - ${count.toLocaleString()}개`) - }) +function getStatistics(tickets, winningNumbers, winningBonusNumber) { + const results = tickets.map(ticket => getRank(ticket, winningNumbers, winningBonusNumber)).filter(Boolean) - console.log(`총 수익률은 ${calculateProfitRate(tickets.length * LOTTO_PRICE, totalWinnings)}% 입니다.`) + return getRankStatistics(results) } function getRankStatistics(results) { + let totalWinnings = 0 const stats = LOTTO_RANK_TABLE.map(rank => ({ ...rank, count: 0, })) results.forEach(result => { + totalWinnings += result.prize const rankIndex = stats.findIndex(rank => rank.matchCount === result.matchCount && rank.bonus === result.bonus ) @@ -58,7 +53,7 @@ function getRankStatistics(results) { } }) - return stats.reverse() + return { stats: stats.reverse(), totalWinnings } } await start() \ No newline at end of file From 08345b5210dbae7a5982bc0b62a0a1d103cac4d5 Mon Sep 17 00:00:00 2001 From: SEMIN-97 Date: Sun, 7 Sep 2025 22:38:43 +0900 Subject: [PATCH 11/12] =?UTF-8?q?refactor:=20getRankStatistics=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=EB=A5=BC=20lottoGame.js=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lottoGame.js | 25 +++++++++++++++++++++++-- src/step1-index.js | 24 +----------------------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/lottoGame.js b/src/lottoGame.js index 25f934777..67bb9fff5 100644 --- a/src/lottoGame.js +++ b/src/lottoGame.js @@ -1,4 +1,4 @@ -export const LOTTO_RANK_TABLE = [ +const LOTTO_RANK_TABLE = [ { rank: 1, matchCount: 6, prize: 2_000_000_000 }, { rank: 2, matchCount: 5, prize: 30_000_000, bonus: true }, { rank: 3, matchCount: 5, prize: 1_500_000 }, @@ -23,4 +23,25 @@ export function calculateProfitRate(purchaseAmount, totalWinnings) { const rate = (totalWinnings / purchaseAmount) * 100 return Math.round(rate * 100) / 100 -} \ No newline at end of file +} + +export function getRankStatistics(results) { + let totalWinnings = 0 + const stats = LOTTO_RANK_TABLE.map(rank => ({ + ...rank, + count: 0, + })) + + results.forEach(result => { + totalWinnings += result.prize + const rankIndex = stats.findIndex(rank => + rank.matchCount === result.matchCount && rank.bonus === result.bonus + ) + + if (rankIndex !== -1) { + stats[rankIndex].count += 1 + } + }) + + return { stats: stats.reverse(), totalWinnings } +} diff --git a/src/step1-index.js b/src/step1-index.js index c0184e2ab..7420aef72 100644 --- a/src/step1-index.js +++ b/src/step1-index.js @@ -1,6 +1,6 @@ import { readLineAsync } from './utils/readlineAsync.js' import { getTicket, LOTTO_PRICE } from './lotto.js' -import { calculateProfitRate, getRank, LOTTO_RANK_TABLE } from './lottoGame.js' +import { calculateProfitRate, getRank, getRankStatistics } from './lottoGame.js' import { printProfit, printResults, printTicket } from './lottoPrint.js' async function start() { @@ -31,29 +31,7 @@ async function getWinningNumber() { function getStatistics(tickets, winningNumbers, winningBonusNumber) { const results = tickets.map(ticket => getRank(ticket, winningNumbers, winningBonusNumber)).filter(Boolean) - return getRankStatistics(results) } -function getRankStatistics(results) { - let totalWinnings = 0 - const stats = LOTTO_RANK_TABLE.map(rank => ({ - ...rank, - count: 0, - })) - - results.forEach(result => { - totalWinnings += result.prize - const rankIndex = stats.findIndex(rank => - rank.matchCount === result.matchCount && rank.bonus === result.bonus - ) - - if (rankIndex !== -1) { - stats[rankIndex].count += 1 - } - }) - - return { stats: stats.reverse(), totalWinnings } -} - await start() \ No newline at end of file From 75de2374ae3b767ab1acaa0076b05f801177d375 Mon Sep 17 00:00:00 2001 From: SEMIN-97 Date: Sun, 7 Sep 2025 22:39:47 +0900 Subject: [PATCH 12/12] =?UTF-8?q?fix:=20LOTTO=5FRANK=5FTABLE=202=EB=93=B1?= =?UTF-8?q?=20=EC=A1=B0=EA=B1=B4=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lottoGame.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lottoGame.js b/src/lottoGame.js index 67bb9fff5..735ffe2a0 100644 --- a/src/lottoGame.js +++ b/src/lottoGame.js @@ -1,6 +1,6 @@ const LOTTO_RANK_TABLE = [ { rank: 1, matchCount: 6, prize: 2_000_000_000 }, - { rank: 2, matchCount: 5, prize: 30_000_000, bonus: true }, + { rank: 2, matchCount: 6, prize: 30_000_000, bonus: true }, { rank: 3, matchCount: 5, prize: 1_500_000 }, { rank: 4, matchCount: 4, prize: 50_000 }, { rank: 5, matchCount: 3, prize: 5_000 },