diff --git a/README.md b/README.md index 15bb106b5..e70e1f775 100644 --- a/README.md +++ b/README.md @@ -1 +1,49 @@ # javascript-lotto-precourse + +## ๐Ÿ“œ๊ธฐ๋Šฅ ์ •๋ฆฌ + +### ๋กœ๋˜ ๊ตฌ์ž… ์ž…๋ ฅ ๊ธฐ๋Šฅ + +- [x] **๊ตฌ์ž… ๊ธˆ์•ก** ์ž…๋ ฅ์„ ๋ฐ›๋Š” ๊ธฐ๋Šฅ - `Console.readLineAsync()` +- [x] ์ž…๋ ฅ์ด ์กด์žฌํ•˜๋Š”์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ์ž…๋ ฅ์ด ์–‘์˜ ์ •์ˆ˜์ธ์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ์ž…๋ ฅ์ด 1000๋‹จ์œ„๋กœ ๋‚˜๋ˆ„์–ด ๋–จ์–ด์ง€๋Š”์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๊ธฐ๋Šฅ + +### ๋กœ๋˜๋ฅผ ๋ฐœํ–‰ํ•˜๋Š” ๊ธฐ๋Šฅ + +- [x] ๊ตฌ์ž… ๊ธˆ์•ก์„ 1000์œผ๋กœ ๋‚˜๋ˆ„์–ด **๋กœ๋˜ ๋ฐœํ–‰ ์ˆ˜**๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ๋กœ๋˜ ๋ฐœํ–‰ ์ˆ˜๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๊ธฐ๋Šฅ - `Console.print()` +- [x] 1 ~ 45 ์ด๋‚ด์˜ ์ค‘๋ณต๋˜์ง€ ์•Š๋Š” 6๊ฐœ์˜ ์ˆซ์ž๋กœ ์ด๋ฃจ์–ด์ง„ **๋กœ๋˜ ๋ฒˆํ˜ธ ๋ฐฐ์—ด**์„ ์ƒ์„ฑํ•˜๋Š” ๊ธฐ๋Šฅ - `MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6)` +- [x] ์ƒ์„ฑํ•œ ๋กœ๋˜ ๋ฒˆํ˜ธ ๋ฐฐ์—ด ์˜ค๋ฆ„์ฐจ ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ๋ฐœํ–‰ ์ˆ˜๋งŒํผ ๋กœ๋˜ ๋ฒˆํ˜ธ ๋ฐฐ์—ด ์ƒ์„ฑ์„ ๋ฐ˜๋ณตํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ์ƒ์„ฑ๋œ ๋ชจ๋“  ๋กœ๋˜ ๋ฒˆํ˜ธ๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๊ธฐ๋Šฅ - `Console.print()` + +### ๋‹น์ฒจ ๋ฒˆํ˜ธ ์ž…๋ ฅ ๊ธฐ๋Šฅ + +- [x] **๋‹น์ฒจ ๋ฒˆํ˜ธ** ์ž…๋ ฅ์„ ๋ฐ›๋Š” ๊ธฐ๋Šฅ - `Console.readLineAsync()` +- [x] ์ž…๋ ฅ์ด ์žˆ๋Š”์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ์‰ผํ‘œ(,)๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ž…๋ ฅ์„ ๋ถ„๋ฆฌ(spliit)ํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ์‰ผํ‘œ๋กœ ๋ถ„๋ฆฌ๋œ ์ž…๋ ฅ์ด ์–‘์˜ ์ •์ˆ˜์ธ์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ์‰ผํ‘œ๋กœ ๋ถ„๋ฆฌ๋œ ์ž…๋ ฅ์ด 1 ~ 45 ์ด๋‚ด์˜ ์ˆซ์ž์ธ์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ์‰ผํ‘œ๋กœ ๋ถ„๋ฆฌ๋œ ์ž…๋ ฅ์— ์ค‘๋ณต๋œ ์ˆซ์ž๊ฐ€ ์žˆ๋Š”์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๊ธฐ๋Šฅ + +### ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ ์ž…๋ ฅ ๊ธฐ๋Šฅ + +- [x] **๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ** ์ž…๋ ฅ์„ ๋ฐ›๋Š” ๊ธฐ๋Šฅ - `Console.readLineAsync()` +- [x] ์ž…๋ ฅ์ด ์žˆ๋Š”์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ์ž…๋ ฅ์ด ์–‘์˜ ์ •์ˆ˜์ธ์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ์ž…๋ ฅ์ด 1 ~ 45 ์ด๋‚ด์˜ ์ˆซ์ž์ธ์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ์ž…๋ ฅ์ด ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ์ค‘๋ณต๋˜๋Š”์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๊ธฐ๋Šฅ + +### ๋‹น์ฒจ ๋‚ด์—ญ์„ ์ถœ๋ ฅํ•˜๋Š” ๊ธฐ๋Šฅ + +- [x] โ€œ๋‹น์ฒจ ํ†ต๊ณ„โ€ ๋ฉ”์„ธ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๊ธฐ๋Šฅ - `Console.print()` +- [x] ๋กœ๋˜ ๋ฒˆํ˜ธ ๋ฐฐ์—ด์„ ์ˆœํšŒํ•˜๋ฉฐ ์ผ์น˜ํ•˜๋Š” ์ˆซ์ž๋ฅผ ์ฐพ์•„ ๋‹น์ฒจ ์ •๋ณด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ๋ฐœํ–‰ ์ˆ˜๋งŒํผ ๋‹น์ฒจ ์ •๋ณด ์ƒ์„ฑ์„ ๋ฐ˜๋ณตํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ๋‹น์ฒจ ์ •๋ณด๋ฅผ ๋ฐ›์•„ ๋‹น์ฒจ ๋‚ด์—ญ์„ ์ถœ๋ ฅํ•˜๋Š” ๊ธฐ๋Šฅ - `Console.print()` + +### ์ด ์ˆ˜์ต๋ฅ ์„ ์ถœ๋ ฅํ•˜๋Š” ๊ธฐ๋Šฅ + +- [x] ๋‹น์ฒจ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ˆ˜์ต๋ฅ ์„ ๊ณ„์‚ฐํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ์ˆ˜์ต๋ฅ ์„ ์†Œ์ˆ˜์  ๋‘˜์งธ ์ž๋ฆฌ์—์„œ ๋ฐ˜์˜ฌ๋ฆผ ํ•˜๋Š” ๊ธฐ๋Šฅ +- [x] ์ˆ˜์ต๋ฅ  ์ •๋ณด๋ฅผ ๋ฐ›์•„ ์ด ์ˆ˜์ต๋ฅ ์„ ์ถœ๋ ฅํ•˜๋Š” ๊ธฐ๋Šฅ - `Console.print()` diff --git a/__tests__/LottoMachineTest.js b/__tests__/LottoMachineTest.js new file mode 100644 index 000000000..9e1713583 --- /dev/null +++ b/__tests__/LottoMachineTest.js @@ -0,0 +1,310 @@ +import { + validatePurchasePrice, + calcPurchaseCount, + createLottoNumbers, + validateWinningNumbers, + validateBonusNumber, + incrementCount, + calcWinningResult, + calcTotalYield, +} from "../src/lottoMachine.js"; +import { MissionUtils } from "@woowacourse/mission-utils"; +import Lotto from "../src/Lotto.js"; +import { BONUS_PRIZE_INDEX, winningInfo } from "../src/const.js"; + +describe("validatePurchasePrice ํ…Œ์ŠคํŠธ", () => { + test("์ •์ƒ์ ์ธ ๊ตฌ๋งค ๊ธˆ์•ก์ด๋ฉด ์ˆซ์ž๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.", () => { + const result = validatePurchasePrice("1000"); + expect(result).toBe(1000); + }); + + test("๊ณต๋ฐฑ์ด ํฌํ•จ๋œ ๊ตฌ๋งค ๊ธˆ์•ก์ด๋ฉด trim ํ›„ ๊ฒ€์ฆํ•œ๋‹ค.", () => { + const result = validatePurchasePrice(" 2000 "); + expect(result).toBe(2000); + }); + + test("๋นˆ ๋ฌธ์ž์—ด์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validatePurchasePrice(""); + }).toThrow("[ERROR]"); + }); + + test("๊ณต๋ฐฑ๋งŒ ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validatePurchasePrice(" "); + }).toThrow("[ERROR]"); + }); + + test("1000๋‹จ์œ„๊ฐ€ ์•„๋‹ˆ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validatePurchasePrice("1500"); + }).toThrow("[ERROR]"); + }); + + test("์ˆซ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validatePurchasePrice("abc"); + }).toThrow("[ERROR]"); + }); + + test("์–‘์˜ ์ •์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validatePurchasePrice("-1000"); + }).toThrow("[ERROR]"); + }); +}); + +describe("calcPurchaseCount ํ…Œ์ŠคํŠธ", () => { + test("1000์›์ด๋ฉด 1๊ฐœ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.", () => { + const result = calcPurchaseCount(1000); + expect(result).toBe(1); + }); + + test("5000์›์ด๋ฉด 5๊ฐœ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.", () => { + const result = calcPurchaseCount(5000); + expect(result).toBe(5); + }); +}); + +describe("createLottoNumbers ํ…Œ์ŠคํŠธ", () => { + beforeEach(() => { + jest.restoreAllMocks(); + }); + + test("๊ตฌ๋งค ๊ฐœ์ˆ˜๋งŒํผ ๋กœ๋˜๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.", () => { + const mockNumbers = [1, 2, 3, 4, 5, 6]; + MissionUtils.Random.pickUniqueNumbersInRange = jest + .fn() + .mockReturnValue(mockNumbers); + + const result = createLottoNumbers(3); + + expect(result).toHaveLength(3); + expect(MissionUtils.Random.pickUniqueNumbersInRange).toHaveBeenCalledTimes( + 3 + ); + }); + + test("์ƒ์„ฑ๋œ ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” ์ •๋ ฌ๋˜์–ด ์žˆ๋‹ค.", () => { + const mockNumbers = [6, 1, 5, 2, 4, 3]; + MissionUtils.Random.pickUniqueNumbersInRange = jest + .fn() + .mockReturnValue(mockNumbers); + + const result = createLottoNumbers(1); + + expect(result[0].numbers).toEqual([1, 2, 3, 4, 5, 6]); + }); + + test("์ƒ์„ฑ๋œ ๋กœ๋˜๋Š” Lotto ์ธ์Šคํ„ด์Šค์ด๋‹ค.", () => { + const mockNumbers = [1, 2, 3, 4, 5, 6]; + MissionUtils.Random.pickUniqueNumbersInRange = jest + .fn() + .mockReturnValue(mockNumbers); + + const result = createLottoNumbers(1); + + expect(result[0]).toBeInstanceOf(Lotto); + }); +}); + +describe("validateWinningNumbers ํ…Œ์ŠคํŠธ", () => { + test("์ •์ƒ์ ์ธ ๋‹น์ฒจ ๋ฒˆํ˜ธ์ด๋ฉด ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.", () => { + const result = validateWinningNumbers("1,2,3,4,5,6"); + expect(result).toEqual([1, 2, 3, 4, 5, 6]); + }); + + test("๊ณต๋ฐฑ์ด ํฌํ•จ๋œ ๋‹น์ฒจ ๋ฒˆํ˜ธ์ด๋ฉด trim ํ›„ ๊ฒ€์ฆํ•œ๋‹ค.", () => { + const result = validateWinningNumbers(" 1, 2, 3, 4, 5, 6 "); + expect(result).toEqual([1, 2, 3, 4, 5, 6]); + }); + + test("๋นˆ ๋ฌธ์ž์—ด์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateWinningNumbers(""); + }).toThrow("[ERROR]"); + }); + + test("6๊ฐœ๊ฐ€ ์•„๋‹ˆ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateWinningNumbers("1,2,3,4,5"); + }).toThrow("[ERROR]"); + }); + + test("์ค‘๋ณต๋œ ๋ฒˆํ˜ธ๊ฐ€ ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateWinningNumbers("1,2,3,4,5,5"); + }).toThrow("[ERROR]"); + }); + + test("๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚œ ๋ฒˆํ˜ธ๊ฐ€ ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateWinningNumbers("1,2,3,4,5,46"); + }).toThrow("[ERROR]"); + }); + + test("์ˆซ์ž๊ฐ€ ์•„๋‹Œ ๊ฐ’์ด ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateWinningNumbers("1,2,3,4,5,abc"); + }).toThrow("[ERROR]"); + }); + + test("์–‘์˜ ์ •์ˆ˜๊ฐ€ ์•„๋‹Œ ๊ฐ’์ด ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateWinningNumbers("1,2,3,4,5,abc"); + }).toThrow("[ERROR]"); + }); +}); + +describe("validateBonusNumber ํ…Œ์ŠคํŠธ", () => { + const winningNumbers = [1, 2, 3, 4, 5, 6]; + + test("์ •์ƒ์ ์ธ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ์ด๋ฉด ์ˆซ์ž๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.", () => { + const result = validateBonusNumber("7", winningNumbers); + expect(result).toBe(7); + }); + + test("๊ณต๋ฐฑ์ด ํฌํ•จ๋œ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ์ด๋ฉด trim ํ›„ ๊ฒ€์ฆํ•œ๋‹ค.", () => { + const result = validateBonusNumber(" 7 ", winningNumbers); + expect(result).toBe(7); + }); + + test("๋นˆ ๋ฌธ์ž์—ด์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateBonusNumber("", winningNumbers); + }).toThrow("[ERROR]"); + }); + + test("๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ์ค‘๋ณต๋˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateBonusNumber("6", winningNumbers); + }).toThrow("[ERROR]"); + }); + + test("๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚œ ๋ฒˆํ˜ธ์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateBonusNumber("46", winningNumbers); + }).toThrow("[ERROR]"); + }); + + test("์ˆซ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateBonusNumber("abc", winningNumbers); + }).toThrow("[ERROR]"); + }); +}); + +describe("incrementCount ํ…Œ์ŠคํŠธ", () => { + test("3๊ฐœ ์ผ์น˜์ด๋ฉด result[0]์˜ count๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค.", () => { + const result = structuredClone(winningInfo); + incrementCount(result, 3, false); + + expect(result[0].count).toBe(1); + }); + + test("4๊ฐœ ์ผ์น˜์ด๋ฉด result[1]์˜ count๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค.", () => { + const result = structuredClone(winningInfo); + incrementCount(result, 4, false); + + expect(result[1].count).toBe(1); + }); + + test("5๊ฐœ ์ผ์น˜์ด๊ณ  ๋ณด๋„ˆ์Šค ์ˆซ์ž๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด result[2]์˜ count๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค.", () => { + const result = structuredClone(winningInfo); + incrementCount(result, 5, false); + + expect(result[2].count).toBe(1); + expect(result[BONUS_PRIZE_INDEX].count).toBe(0); + }); + + test("5๊ฐœ ์ผ์น˜์ด๊ณ  ๋ณด๋„ˆ์Šค ์ˆซ์ž๊ฐ€ ์ผ์น˜ํ•˜๋ฉด result[3]์˜ count๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค.", () => { + const result = structuredClone(winningInfo); + incrementCount(result, 5, true); + + expect(result[2].count).toBe(0); + expect(result[BONUS_PRIZE_INDEX].count).toBe(1); + }); + + test("6๊ฐœ ์ผ์น˜์ด๋ฉด result[4]์˜ count๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค.", () => { + const result = structuredClone(winningInfo); + incrementCount(result, 6, false); + + expect(result[4].count).toBe(1); + }); + + test("2๊ฐœ ์ดํ•˜ ์ผ์น˜์ด๋ฉด count๊ฐ€ ์ฆ๊ฐ€ํ•˜์ง€ ์•Š๋Š”๋‹ค.", () => { + const result = structuredClone(winningInfo); + incrementCount(result, 2, false); + + result.forEach((item) => { + expect(item.count).toBe(0); + }); + }); +}); + +describe("calcWinningResult ํ…Œ์ŠคํŠธ", () => { + test("๋‹น์ฒจ๋œ ๋กœ๋˜๊ฐ€ ์žˆ์œผ๋ฉด ํ•ด๋‹น ๋“ฑ์ˆ˜ count๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค.", () => { + const lottoArray = [ + new Lotto([1, 2, 3, 4, 5, 6]), + new Lotto([1, 2, 3, 4, 5, 7]), + new Lotto([1, 2, 3, 4, 10, 11]), + new Lotto([1, 2, 3, 10, 11, 12]), + ]; + const winningNumbers = [1, 2, 3, 4, 5, 6]; + const bonusNumber = 7; + + const result = calcWinningResult(lottoArray, winningNumbers, bonusNumber); + + expect(result[0].count).toBe(1); + expect(result[1].count).toBe(1); + expect(result[2].count).toBe(0); + expect(result[BONUS_PRIZE_INDEX].count).toBe(1); + expect(result[4].count).toBe(1); + }); +}); + +describe("calcTotalYield ํ…Œ์ŠคํŠธ", () => { + test("๋‹น์ฒจ๊ธˆ์ด ์—†์œผ๋ฉด ์ˆ˜์ต๋ฅ ์€ 0.0%์ด๋‹ค.", () => { + const result = structuredClone(winningInfo); + const purchasePrice = 1000; + + const totalYield = calcTotalYield(result, purchasePrice); + + expect(totalYield).toBe("0.0"); + }); + + test("๋‹น์ฒจ๊ธˆ์ด ์žˆ์œผ๋ฉด ์ˆ˜์ต๋ฅ ์„ ๊ณ„์‚ฐํ•œ๋‹ค.", () => { + const result = structuredClone(winningInfo); + result[0].count = 1; + result[1].count = 1; + const purchasePrice = 10000; + + const totalYield = calcTotalYield(result, purchasePrice); + + expect(totalYield).toBe("550.0"); + }); + + test("์ˆ˜์ต๋ฅ ์€ ์†Œ์ˆ˜์  ์ฒซ์งธ ์ž๋ฆฌ๊นŒ์ง€ ๋ฐ˜ํ™˜ํ•œ๋‹ค.", () => { + const result = structuredClone(winningInfo); + result[0].count = 1; + const purchasePrice = 3000; + + const totalYield = calcTotalYield(result, purchasePrice); + + expect(totalYield).toBe("166.7"); + }); + + test("๋ชจ๋“  ๋“ฑ์ˆ˜ ๋‹น์ฒจ ์‹œ ์ˆ˜์ต๋ฅ ์„ ๊ณ„์‚ฐํ•œ๋‹ค.", () => { + const result = structuredClone(winningInfo); + result[0].count = 1; + result[1].count = 1; + result[2].count = 1; + result[BONUS_PRIZE_INDEX].count = 1; + result[4].count = 1; + const purchasePrice = 10000; + + const totalYield = calcTotalYield(result, purchasePrice); + + expect(totalYield).toBe("20315550.0"); + }); +}); diff --git a/__tests__/LottoTest.js b/__tests__/LottoTest.js index 409aaf69b..feae83e7f 100644 --- a/__tests__/LottoTest.js +++ b/__tests__/LottoTest.js @@ -1,18 +1,100 @@ import Lotto from "../src/Lotto"; describe("๋กœ๋˜ ํด๋ž˜์Šค ํ…Œ์ŠคํŠธ", () => { - test("๋กœ๋˜ ๋ฒˆํ˜ธ์˜ ๊ฐœ์ˆ˜๊ฐ€ 6๊ฐœ๊ฐ€ ๋„˜์–ด๊ฐ€๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { - expect(() => { - new Lotto([1, 2, 3, 4, 5, 6, 7]); - }).toThrow("[ERROR]"); + describe("๋กœ๋˜ ๋ฒˆํ˜ธ ๊ฒ€์ฆ ํ…Œ์ŠคํŠธ", () => { + test("๋กœ๋˜ ๋ฒˆํ˜ธ์˜ ๊ฐœ์ˆ˜๊ฐ€ 6๊ฐœ๊ฐ€ ๋„˜์–ด๊ฐ€๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + new Lotto([1, 2, 3, 4, 5, 6, 7]); + }).toThrow("[ERROR]"); + }); + + // TODO: ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ต๊ณผํ•˜๋„๋ก ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ ๊ตฌํ˜„ + test("๋กœ๋˜ ๋ฒˆํ˜ธ์— ์ค‘๋ณต๋œ ์ˆซ์ž๊ฐ€ ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + new Lotto([1, 2, 3, 4, 5, 5]); + }).toThrow("[ERROR]"); + }); + + test("๋กœ๋˜ ๋ฒˆํ˜ธ์— 0์ด ํฌํ•จ๋˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + new Lotto([0, 1, 2, 3, 4, 5]); + }).toThrow("[ERROR]"); + }); + + test("๋กœ๋˜ ๋ฒˆํ˜ธ์— ์Œ์ˆ˜๊ฐ€ ํฌํ•จ๋˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + new Lotto([-1, 1, 2, 3, 4, 5]); + }).toThrow("[ERROR]"); + }); + + test("๋กœ๋˜ ๋ฒˆํ˜ธ์— 45๋ณด๋‹ค ํฐ ์ˆซ์ž๊ฐ€ ํฌํ•จ๋˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + new Lotto([1, 2, 3, 4, 5, 46]); + }).toThrow("[ERROR]"); + }); + + test("๋กœ๋˜ ๋ฒˆํ˜ธ์— null์ด ํฌํ•จ๋˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + new Lotto([1, 2, 3, 4, 5, null]); + }).toThrow("[ERROR]"); + }); + + test("๋กœ๋˜ ๋ฒˆํ˜ธ์— undefined๊ฐ€ ํฌํ•จ๋˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + new Lotto([1, 2, 3, 4, 5, undefined]); + }).toThrow("[ERROR]"); + }); + + test("๋กœ๋˜ ๋ฒˆํ˜ธ์— ๋นˆ ๋ฌธ์ž์—ด์ด ํฌํ•จ๋˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + new Lotto([1, 2, 3, 4, 5, ""]); + }).toThrow("[ERROR]"); + }); + + test("๋กœ๋˜ ๋ฒˆํ˜ธ์— ๋ฌธ์ž์—ด์ด ํฌํ•จ๋˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + new Lotto([1, 2, 3, 4, 5, "abc"]); + }).toThrow("[ERROR]"); + }); + + test("๋กœ๋˜ ๋ฒˆํ˜ธ์— ์†Œ์ˆ˜๊ฐ€ ํฌํ•จ๋˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + new Lotto([1, 2, 3, 4, 5, 1.5]); + }).toThrow("[ERROR]"); + }); }); - // TODO: ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ต๊ณผํ•˜๋„๋ก ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ ๊ตฌํ˜„ - test("๋กœ๋˜ ๋ฒˆํ˜ธ์— ์ค‘๋ณต๋œ ์ˆซ์ž๊ฐ€ ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { - expect(() => { - new Lotto([1, 2, 3, 4, 5, 5]); - }).toThrow("[ERROR]"); + describe("numbers getter ํ…Œ์ŠคํŠธ", () => { + test("numbers getter๊ฐ€ ๋กœ๋˜ ๋ฒˆํ˜ธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.", () => { + const numbers = [1, 15, 25, 35, 40, 45]; + const lotto = new Lotto(numbers); + expect(lotto.numbers).toEqual(numbers); + }); }); - // TODO: ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„์— ๋”ฐ๋ฅธ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ + describe("getMatchCount ํ…Œ์ŠคํŠธ", () => { + test("๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ์ผ์น˜ํ•˜๋Š” ๊ฐœ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.", () => { + const lotto = new Lotto([1, 2, 3, 4, 5, 6]); + const winningNumbers = [1, 2, 3, 7, 8, 9]; + expect(lotto.getMatchCount(winningNumbers)).toBe(3); + }); + + test("์ผ์น˜ํ•˜๋Š” ๋ฒˆํ˜ธ๊ฐ€ ์—†์œผ๋ฉด 0์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.", () => { + const lotto = new Lotto([1, 2, 3, 4, 5, 6]); + const winningNumbers = [7, 8, 9, 10, 11, 12]; + expect(lotto.getMatchCount(winningNumbers)).toBe(0); + }); + }); + + describe("getHasBonus ํ…Œ์ŠคํŠธ", () => { + test("๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.", () => { + const lotto = new Lotto([1, 2, 3, 4, 5, 6]); + expect(lotto.getHasBonus(6)).toBe(true); + }); + + test("๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.", () => { + const lotto = new Lotto([1, 2, 3, 4, 5, 6]); + expect(lotto.getHasBonus(7)).toBe(false); + }); + }); }); diff --git a/__tests__/ValidateTest.js b/__tests__/ValidateTest.js new file mode 100644 index 000000000..a9cd49311 --- /dev/null +++ b/__tests__/ValidateTest.js @@ -0,0 +1,143 @@ +import { + validateEmptyInput, + validatePositiveNumber, + validateThousandUnit, + validateLottoNumberRange, + validateNumbersLength, + validateDuplicateNumbers, +} from "../src/validate.js"; +import { FIELD_NAMES } from "../src/const.js"; + +describe("validateEmptyInput ํ…Œ์ŠคํŠธ", () => { + test("๋นˆ ๋ฌธ์ž์—ด์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateEmptyInput("", FIELD_NAMES.PURCHASE_PRICE); + }).toThrow("[ERROR]"); + }); + + test("null์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateEmptyInput(null, FIELD_NAMES.PURCHASE_PRICE); + }).toThrow("[ERROR]"); + }); + + test("undefined์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateEmptyInput(undefined, FIELD_NAMES.PURCHASE_PRICE); + }).toThrow("[ERROR]"); + }); +}); + +describe("validatePositiveNumber ํ…Œ์ŠคํŠธ", () => { + test("๋ฌธ์ž์—ด์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validatePositiveNumber("abc", FIELD_NAMES.PURCHASE_PRICE); + }).toThrow("[ERROR]"); + }); + + test("์†Œ์ˆ˜์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validatePositiveNumber(1000.5, FIELD_NAMES.PURCHASE_PRICE); + }).toThrow("[ERROR]"); + }); + + test("0์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validatePositiveNumber(0, FIELD_NAMES.PURCHASE_PRICE); + }).toThrow("[ERROR]"); + }); + + test("์Œ์ˆ˜์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validatePositiveNumber(-1000, FIELD_NAMES.PURCHASE_PRICE); + }).toThrow("[ERROR]"); + }); + + test("์–‘์˜ ์ •์ˆ˜์ด๋ฉด ์ •์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.", () => { + const result = validatePositiveNumber("1000", FIELD_NAMES.PURCHASE_PRICE); + expect(result).toBe(1000); + }); +}); + +describe("validateThousandUnit ํ…Œ์ŠคํŠธ", () => { + test("1000์œผ๋กœ ๋‚˜๋ˆ„์–ด๋–จ์–ด์ง€์ง€ ์•Š์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateThousandUnit(1500); + }).toThrow(`[ERROR]`); + }); + + test("1000๋‹จ์œ„์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.", () => { + expect(() => { + validateThousandUnit(1000); + }).not.toThrow(); + }); +}); + +describe("validateLottoNumberRange ํ…Œ์ŠคํŠธ", () => { + test("1๋ณด๋‹ค ์ž‘์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateLottoNumberRange(0, FIELD_NAMES.LOTTO_NUMBERS); + }).toThrow("[ERROR]"); + }); + + test("45๋ณด๋‹ค ํฌ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateLottoNumberRange(46, FIELD_NAMES.LOTTO_NUMBERS); + }).toThrow("[ERROR]"); + }); + + test("1~45 ์‚ฌ์ด์˜ ์ˆซ์ž์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.", () => { + expect(() => { + validateLottoNumberRange(25, FIELD_NAMES.LOTTO_NUMBERS); + }).not.toThrow(); + }); +}); + +describe("validateNumbersLength ํ…Œ์ŠคํŠธ", () => { + test("6๊ฐœ๊ฐ€ ์•„๋‹ˆ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateNumbersLength([1, 2, 3, 4, 5], FIELD_NAMES.LOTTO_NUMBERS); + }).toThrow("[ERROR]"); + }); + + test("6๊ฐœ๋ณด๋‹ค ๋งŽ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateNumbersLength([1, 2, 3, 4, 5, 6, 7], FIELD_NAMES.LOTTO_NUMBERS); + }).toThrow("[ERROR]"); + }); + + test("6๊ฐœ์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.", () => { + expect(() => { + validateNumbersLength([1, 2, 3, 4, 5, 6], FIELD_NAMES.LOTTO_NUMBERS); + }).not.toThrow(); + }); +}); + +describe("validateDuplicateNumbers ํ…Œ์ŠคํŠธ", () => { + test("์ค‘๋ณต๋œ ์ˆซ์ž๊ฐ€ ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateDuplicateNumbers([1, 2, 3, 4, 5, 5], FIELD_NAMES.LOTTO_NUMBERS); + }).toThrow("[ERROR]"); + }); + + test("์ค‘๋ณต๋œ ์ˆซ์ž๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + expect(() => { + validateDuplicateNumbers([1, 1, 2, 2, 3, 3], FIELD_NAMES.LOTTO_NUMBERS); + }).toThrow("[ERROR]"); + }); + + test("์ค‘๋ณต๋œ ์ˆซ์ž๊ฐ€ ์—†์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.", () => { + expect(() => { + validateDuplicateNumbers([1, 2, 3, 4, 5, 6], FIELD_NAMES.LOTTO_NUMBERS); + }).not.toThrow(); + }); + + test("๋ชจ๋“  ์ˆซ์ž๊ฐ€ ์ค‘๋ณต๋˜์ง€ ์•Š์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.", () => { + expect(() => { + validateDuplicateNumbers( + [1, 15, 25, 35, 40, 45], + FIELD_NAMES.LOTTO_NUMBERS + ); + }).not.toThrow(); + }); +}); diff --git a/src/App.js b/src/App.js index 091aa0a5d..45960a12a 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,14 @@ +import { startLottoMachine } from "./lottoMachine.js"; +import { Console } from "@woowacourse/mission-utils"; + class App { - async run() {} + async run() { + try { + await startLottoMachine(); + } catch (error) { + Console.print(error.message); + } + } } export default App; diff --git a/src/Lotto.js b/src/Lotto.js index cb0b1527e..a36b247b4 100644 --- a/src/Lotto.js +++ b/src/Lotto.js @@ -1,5 +1,13 @@ +import { + validateDuplicateNumbers, + validateEmptyInput, + validateLottoNumberRange, + validateNumbersLength, + validatePositiveNumber, +} from "./validate.js"; + class Lotto { - #numbers; + #numbers; // ๋กœ๋˜ ๋ฒˆํ˜ธ constructor(numbers) { this.#validate(numbers); @@ -7,12 +15,27 @@ class Lotto { } #validate(numbers) { - if (numbers.length !== 6) { - throw new Error("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 6๊ฐœ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); - } + const validateField = "๋กœ๋˜ ๋ฒˆํ˜ธ"; + validateNumbersLength(numbers, validateField); + validateDuplicateNumbers(numbers, validateField); + numbers.forEach((num) => { + validateEmptyInput(num, validateField); + validatePositiveNumber(num, validateField); + validateLottoNumberRange(num, validateField); + }); + } + + get numbers() { + return this.#numbers; } - // TODO: ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„ + getMatchCount(winningNumbers) { + return this.#numbers.filter((num) => winningNumbers.includes(num)).length; + } + + getHasBonus(bonusNumber) { + return this.#numbers.includes(bonusNumber); + } } export default Lotto; diff --git a/src/const.js b/src/const.js new file mode 100644 index 000000000..ef36eb81e --- /dev/null +++ b/src/const.js @@ -0,0 +1,40 @@ +export const ERROR_HEADER = "[ERROR]"; + +export const PURCHASE_UNIT = 1000; + +export const FIELD_NAMES = { + PURCHASE_PRICE: "๊ตฌ์ž…๊ธˆ์•ก", + WINNING_NUMBERS: "๋‹น์ฒจ ๋ฒˆํ˜ธ", + BONUS_NUMBERS: "๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ", + LOTTO_NUMBERS: "๋กœ๋˜ ๋ฒˆํ˜ธ", +}; + +export const winningInfo = [ + { + match: 3, + prizeMoney: 5000, + count: 0, + }, + { + match: 4, + prizeMoney: 50000, + count: 0, + }, + { + match: 5, + prizeMoney: 1500000, + count: 0, + }, + { + match: 5, + prizeMoney: 30000000, + count: 0, + }, + { + match: 6, + prizeMoney: 2000000000, + count: 0, + }, +]; + +export const BONUS_PRIZE_INDEX = 3; diff --git a/src/lottoMachine.js b/src/lottoMachine.js new file mode 100644 index 000000000..aa173fe7b --- /dev/null +++ b/src/lottoMachine.js @@ -0,0 +1,172 @@ +import { Console, MissionUtils } from "@woowacourse/mission-utils"; +import Lotto from "./Lotto.js"; +import { + validateDuplicateNumbers, + validateEmptyInput, + validateLottoNumberRange, + validateNumbersLength, + validatePositiveNumber, + validateThousandUnit, +} from "./validate.js"; +import { + BONUS_PRIZE_INDEX, + FIELD_NAMES, + PURCHASE_UNIT, + winningInfo, +} from "./const.js"; + +export const validatePurchasePrice = (rawInput) => { + const input = rawInput.trim(); + validateEmptyInput(input, FIELD_NAMES.PURCHASE_PRICE); + const numberInput = validatePositiveNumber(input, FIELD_NAMES.PURCHASE_PRICE); + validateThousandUnit(numberInput); + + return numberInput; +}; + +const inputPurchasePrice = async () => { + const rawInput = await Console.readLineAsync( + `\n${FIELD_NAMES.PURCHASE_PRICE}์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.\n` + ); + return validatePurchasePrice(rawInput); +}; + +export const calcPurchaseCount = (price) => { + const purchaseNum = Number(price) / PURCHASE_UNIT; + return purchaseNum; +}; + +export const createLottoNumbers = (purchaseNum) => { + const lottoArray = []; + + for (let i = 1; i <= purchaseNum; i++) { + const lottoNumbers = MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6); + const sortedLottoNumbers = lottoNumbers.sort( + (first, second) => first - second + ); + lottoArray.push(new Lotto(sortedLottoNumbers)); + } + + return lottoArray; +}; + +const printLottoArray = (lottoArray) => { + lottoArray.forEach((lotto) => { + Console.print(`[${lotto.numbers.join(", ")}]`); + }); +}; + +export const validateWinningNumbers = (rawInput) => { + const splitArray = rawInput.trim().split(","); + const numberArray = splitArray.map((input) => { + validateEmptyInput(input, FIELD_NAMES.WINNING_NUMBERS); + const numberInput = validatePositiveNumber( + input, + FIELD_NAMES.WINNING_NUMBERS + ); + validateLottoNumberRange(numberInput, FIELD_NAMES.WINNING_NUMBERS); + return numberInput; + }); + validateNumbersLength(numberArray, FIELD_NAMES.WINNING_NUMBERS); + validateDuplicateNumbers(numberArray, FIELD_NAMES.WINNING_NUMBERS); + return numberArray; +}; + +const inputWinningNumbers = async () => { + const rawInput = await Console.readLineAsync( + `\n${FIELD_NAMES.WINNING_NUMBERS}๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.\n` + ); + return validateWinningNumbers(rawInput); +}; + +export const validateBonusNumber = (rawInput, winningNumbers) => { + const input = rawInput.trim(); + validateEmptyInput(input, FIELD_NAMES.BONUS_NUMBERS); + const numberInput = validatePositiveNumber(input, FIELD_NAMES.BONUS_NUMBERS); + validateLottoNumberRange(numberInput); + validateDuplicateNumbers( + [...winningNumbers, numberInput], + FIELD_NAMES.BONUS_NUMBERS + ); + + return numberInput; +}; + +const inputBonusNumber = async (winningNumbers) => { + const rawInput = await Console.readLineAsync( + `\n${FIELD_NAMES.BONUS_NUMBERS}๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.\n` + ); + return validateBonusNumber(rawInput, winningNumbers); +}; + +export const incrementCount = (result, matchCount, hasBonus) => { + if (matchCount < 3) { + return; + } + if (matchCount === 5 && hasBonus) { + result[BONUS_PRIZE_INDEX].count += 1; + return; + } + + const target = result.find((item) => item.match === matchCount); + if (!target) { + return; + } + target.count += 1; +}; + +export const calcWinningResult = (lottoArray, winningNumbers, bonusNumber) => { + const result = structuredClone(winningInfo); + + lottoArray.forEach((lotto) => { + const matchCount = lotto.getMatchCount(winningNumbers); + const hasBonus = lotto.getHasBonus(bonusNumber); + + incrementCount(result, matchCount, hasBonus); + }); + + return result; +}; + +const setWinningResultMessage = (item, index) => { + const formattedPrice = item.prizeMoney.toLocaleString(); + if (index === BONUS_PRIZE_INDEX) { + Console.print( + `${item.match}๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜ (${formattedPrice}์›) - ${item.count}๊ฐœ` + ); + } else { + Console.print( + `${item.match}๊ฐœ ์ผ์น˜ (${formattedPrice}์›) - ${item.count}๊ฐœ` + ); + } +}; + +const printWinningResult = (result) => { + result.forEach((item, index) => { + setWinningResultMessage(item, index); + }); +}; + +export const calcTotalYield = (result, purchasePrice) => { + let totalPrice = 0; + result.forEach((item) => { + totalPrice += item.count * item.prizeMoney; + }); + const totalYield = (totalPrice / purchasePrice) * 100; + return totalYield.toFixed(1); +}; + +export const startLottoMachine = async () => { + const purchasePrice = await inputPurchasePrice(); + const purchaseCount = calcPurchaseCount(purchasePrice); + Console.print(`\n${purchaseCount}๊ฐœ๋ฅผ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค.`); + const lottoArray = createLottoNumbers(purchaseCount); + printLottoArray(lottoArray); + const winningNumbers = await inputWinningNumbers(); + const bonusNumber = await inputBonusNumber(winningNumbers); + Console.print("\n๋‹น์ฒจ ํ†ต๊ณ„\n---"); + const result = calcWinningResult(lottoArray, winningNumbers, bonusNumber); + printWinningResult(result); + const totalYield = calcTotalYield(result, purchasePrice); + Console.print(`์ด ์ˆ˜์ต๋ฅ ์€ ${totalYield}%์ž…๋‹ˆ๋‹ค.`); +}; diff --git a/src/validate.js b/src/validate.js new file mode 100644 index 000000000..8ebcd219e --- /dev/null +++ b/src/validate.js @@ -0,0 +1,55 @@ +import { ERROR_HEADER, PURCHASE_UNIT } from "./const.js"; + +export const validateEmptyInput = (input, field) => { + if (!input || input === "") { + throw new Error(`${ERROR_HEADER} ${field}์˜ ๊ฐ’์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.`); + } +}; + +export const validatePositiveNumber = (input, field) => { + const numInput = Number(input); + + if (Number.isNaN(numInput)) { + throw new Error(`${ERROR_HEADER} ${field}์˜ ๊ฐ’์€ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.`); + } + if (!Number.isInteger(numInput)) { + throw new Error(`${ERROR_HEADER} ${field}์˜ ๊ฐ’์€ ์ •์ˆ˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.`); + } + if (numInput <= 0) { + throw new Error( + `${ERROR_HEADER} ${field}์˜ ๊ฐ’์€ 1์ด์ƒ์˜ ์–‘์˜ ์ •์ˆ˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.` + ); + } + return numInput; +}; + +export const validateThousandUnit = (input) => { + if (Number(input) % PURCHASE_UNIT !== 0) { + throw new Error( + `${ERROR_HEADER} ๊ตฌ์ž… ๊ธˆ์•ก์˜ ์ž…๋ ฅ๊ฐ’์€ ${PURCHASE_UNIT}๋‹จ์œ„ ์ˆซ์ž๋กœ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.` + ); + } +}; + +export const validateLottoNumberRange = (input, field) => { + const numInput = Number(input); + const isLottoNumberRange = numInput >= 1 && numInput <= 45; + if (!isLottoNumberRange) { + throw new Error( + `${ERROR_HEADER} ${field}์˜ ๊ฐ’์€ 1 ~ 45 ์ด๋‚ด์˜ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.` + ); + } +}; + +export const validateNumbersLength = (numbers, field) => { + if (numbers.length !== 6) { + throw new Error(`${ERROR_HEADER} ${field}๋Š” 6๊ฐœ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.`); + } +}; + +export const validateDuplicateNumbers = (numbers, field) => { + const uniqueNumbers = new Set(numbers); + if (uniqueNumbers.size !== numbers.length) { + throw new Error(`${ERROR_HEADER} ${field}์— ์ค‘๋ณต๋œ ์ˆซ์ž๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.`); + } +};