diff --git a/README.md b/README.md index 15bb106b5..57454abde 100644 --- a/README.md +++ b/README.md @@ -1 +1,158 @@ -# javascript-lotto-precourse +# ๋กœ๋˜ + +## ๐Ÿ” ๊ธฐ๋Šฅ ์š”๊ตฌ ์‚ฌํ•ญ + +๊ฐ„๋‹จํ•œ ๋กœ๋˜ ๋ฐœ๋งค๊ธฐ๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค. + +- ๋กœ๋˜ ๋ฒˆํ˜ธ์˜ ์ˆซ์ž ๋ฒ”์œ„๋Š” 1~45๊นŒ์ง€์ด๋‹ค. +- 1๊ฐœ์˜ ๋กœ๋˜๋ฅผ ๋ฐœํ–‰ํ•  ๋•Œ ์ค‘๋ณต๋˜์ง€ ์•Š๋Š” 6๊ฐœ์˜ ์ˆซ์ž๋ฅผ ๋ฝ‘๋Š”๋‹ค. +- ๋‹น์ฒจ ๋ฒˆํ˜ธ ์ถ”์ฒจ ์‹œ ์ค‘๋ณต๋˜์ง€ ์•Š๋Š” ์ˆซ์ž 6๊ฐœ์™€ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ 1๊ฐœ๋ฅผ ๋ฝ‘๋Š”๋‹ค. +- ๋‹น์ฒจ์€ 1๋“ฑ๋ถ€ํ„ฐ 5๋“ฑ๊นŒ์ง€ ์žˆ๋‹ค. ๋‹น์ฒจ ๊ธฐ์ค€๊ณผ ๊ธˆ์•ก์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค. + - 1๋“ฑ: 6๊ฐœ ๋ฒˆํ˜ธ ์ผ์น˜ / 2,000,000,000์› + - 2๋“ฑ: 5๊ฐœ ๋ฒˆํ˜ธ + ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ ์ผ์น˜ / 30,000,000์› + - 3๋“ฑ: 5๊ฐœ ๋ฒˆํ˜ธ ์ผ์น˜ / 1,500,000์› + - 4๋“ฑ: 4๊ฐœ ๋ฒˆํ˜ธ ์ผ์น˜ / 50,000์› + - 5๋“ฑ: 3๊ฐœ ๋ฒˆํ˜ธ ์ผ์น˜ / 5,000์› +- ๋กœ๋˜ ๊ตฌ์ž… ๊ธˆ์•ก์„ ์ž…๋ ฅํ•˜๋ฉด ๊ตฌ์ž… ๊ธˆ์•ก์— ํ•ด๋‹นํ•˜๋Š” ๋งŒํผ ๋กœ๋˜๋ฅผ ๋ฐœํ–‰ํ•ด์•ผ ํ•œ๋‹ค. +- ๋กœ๋˜ 1์žฅ์˜ ๊ฐ€๊ฒฉ์€ 1,000์›์ด๋‹ค. +- ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅ๋ฐ›๋Š”๋‹ค. +- ์‚ฌ์šฉ์ž๊ฐ€ ๊ตฌ๋งคํ•œ ๋กœ๋˜ ๋ฒˆํ˜ธ์™€ ๋‹น์ฒจ ๋ฒˆํ˜ธ๋ฅผ ๋น„๊ตํ•˜์—ฌ ๋‹น์ฒจ ๋‚ด์—ญ ๋ฐ ์ˆ˜์ต๋ฅ ์„ ์ถœ๋ ฅํ•˜๊ณ  ๋กœ๋˜ ๊ฒŒ์ž„์„ ์ข…๋ฃŒํ•œ๋‹ค. +- ์‚ฌ์šฉ์ž๊ฐ€ ์ž˜๋ชป๋œ ๊ฐ’์„ ์ž…๋ ฅํ•  ๊ฒฝ์šฐ "[ERROR]"๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ Error๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๊ณ  ํ•ด๋‹น ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•œ ๋‹ค์Œ ํ•ด๋‹น ์ง€์ ๋ถ€ํ„ฐ ๋‹ค์‹œ ์ž…๋ ฅ์„ ๋ฐ›๋Š”๋‹ค. + +### ์ž…์ถœ๋ ฅ ์š”๊ตฌ ์‚ฌํ•ญ + +์ž…๋ ฅ + +- ๋กœ๋˜ ๊ตฌ์ž… ๊ธˆ์•ก์„ ์ž…๋ ฅ ๋ฐ›๋Š”๋‹ค. ๊ตฌ์ž… ๊ธˆ์•ก์€ 1,000์› ๋‹จ์œ„๋กœ ์ž…๋ ฅ ๋ฐ›์œผ๋ฉฐ 1,000์›์œผ๋กœ ๋‚˜๋ˆ„์–ด ๋–จ์–ด์ง€์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌํ•œ๋‹ค. + +``` +14000 +``` + +- ๋‹น์ฒจ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅ ๋ฐ›๋Š”๋‹ค. ๋ฒˆํ˜ธ๋Š” ์‰ผํ‘œ(,)๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ตฌ๋ถ„ํ•œ๋‹ค. + +``` +1,2,3,4,5,6 +``` + +- ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅ ๋ฐ›๋Š”๋‹ค. + +``` +7 +``` + + +์ถœ๋ ฅ + +- ๋ฐœํ–‰ํ•œ ๋กœ๋˜ ์ˆ˜๋Ÿ‰ ๋ฐ ๋ฒˆํ˜ธ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค. ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜์—ฌ ๋ณด์—ฌ์ค€๋‹ค. + +``` +8๊ฐœ๋ฅผ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค. +[8, 21, 23, 41, 42, 43] +[3, 5, 11, 16, 32, 38] +[7, 11, 16, 35, 36, 44] +[1, 8, 11, 31, 41, 42] +[13, 14, 16, 38, 42, 45] +[7, 11, 30, 40, 42, 43] +[2, 13, 22, 32, 38, 45] +[1, 3, 5, 14, 22, 45] +``` + +- ๋‹น์ฒจ ๋‚ด์—ญ์„ ์ถœ๋ ฅํ•œ๋‹ค. + +``` +3๊ฐœ ์ผ์น˜ (5,000์›) - 1๊ฐœ +4๊ฐœ ์ผ์น˜ (50,000์›) - 0๊ฐœ +5๊ฐœ ์ผ์น˜ (1,500,000์›) - 0๊ฐœ +5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜ (30,000,000์›) - 0๊ฐœ +6๊ฐœ ์ผ์น˜ (2,000,000,000์›) - 0๊ฐœ +``` + +- ์ˆ˜์ต๋ฅ ์€ ์†Œ์ˆ˜์  ๋‘˜์งธ ์ž๋ฆฌ์—์„œ ๋ฐ˜์˜ฌ๋ฆผํ•œ๋‹ค. (ex. 100.0%, 51.5%, 1,000,000.0%) + +``` +์ด ์ˆ˜์ต๋ฅ ์€ 62.5%์ž…๋‹ˆ๋‹ค. +``` + +- ์˜ˆ์™ธ ์ƒํ™ฉ ์‹œ ์—๋Ÿฌ ๋ฌธ๊ตฌ๋ฅผ ์ถœ๋ ฅํ•ด์•ผ ํ•œ๋‹ค. ๋‹จ, ์—๋Ÿฌ ๋ฌธ๊ตฌ๋Š” "[ERROR]"๋กœ ์‹œ์ž‘ํ•ด์•ผ ํ•œ๋‹ค. + +``` +[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 1๋ถ€ํ„ฐ 45 ์‚ฌ์ด์˜ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. +``` + +์‹คํ–‰ ๊ฒฐ๊ณผ ์˜ˆ์‹œ +``` +๊ตฌ์ž…๊ธˆ์•ก์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”. +8000 + +8๊ฐœ๋ฅผ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค. +[8, 21, 23, 41, 42, 43] +[3, 5, 11, 16, 32, 38] +[7, 11, 16, 35, 36, 44] +[1, 8, 11, 31, 41, 42] +[13, 14, 16, 38, 42, 45] +[7, 11, 30, 40, 42, 43] +[2, 13, 22, 32, 38, 45] +[1, 3, 5, 14, 22, 45] + +๋‹น์ฒจ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”. +1,2,3,4,5,6 + +๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”. +7 + +๋‹น์ฒจ ํ†ต๊ณ„ +--- +3๊ฐœ ์ผ์น˜ (5,000์›) - 1๊ฐœ +4๊ฐœ ์ผ์น˜ (50,000์›) - 0๊ฐœ +5๊ฐœ ์ผ์น˜ (1,500,000์›) - 0๊ฐœ +5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜ (30,000,000์›) - 0๊ฐœ +6๊ฐœ ์ผ์น˜ (2,000,000,000์›) - 0๊ฐœ +์ด ์ˆ˜์ต๋ฅ ์€ 62.5%์ž…๋‹ˆ๋‹ค. +``` + +## ๐Ÿ”ฅ ๊ตฌํ˜„ํ•  ๊ธฐ๋Šฅ ๋ชฉ๋ก + +1. ๋กœ๋˜ ๊ตฌ์ž… ๊ธˆ์•ก์„ ์ž…๋ ฅ ๋ฐ›๋Š”๋‹ค +- 1000์œผ๋กœ ๋‚˜๋ˆ„์–ด ๋–จ์–ด์ง€์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ : [ERROR] ๊ธˆ์•ก์€ 1000์œผ๋กœ ๋‚˜๋ˆ„์–ด ๋–จ์–ด์ง€๋Š” ์ˆ˜๋งŒ ์ž…๋ ฅ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. + +2. ์ž…๋ ฅํ•œ ๊ธˆ์•ก๋งŒํผ(์ž…๋ ฅ ๊ธˆ/1000)์˜ ๋กœ๋˜๋ฅผ ๋ฐฐ์—ด์— ๋‹ด์•„์„œ ๋ฐœํ–‰ +- ํ•˜๋‚˜์˜ ๋กœ๋˜(๋ฐฐ์—ด)๋Š” 6๊ฐœ์˜ ์ค‘๋ณต๋˜์ง€ ์•Š๋Š” ๋žœ๋ค ์ˆซ์ž๋ฅผ ๊ฐ€์ง +- ์ด๋•Œ ๊ฐ ๋ฐฐ์—ด์˜ ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌ + +3. ๋‹น์ฒจ ๋ฒˆํ˜ธ 6๊ฐœ ์ž…๋ ฅ๋ฐ›๊ธฐ +- ๊ฐ ๋ฒˆํ˜ธ๋Š” ,๋ฅผ ํ†ตํ•ด์„œ ๊ตฌ๋ถ„ +- ,๋กœ ๊ตฌ๋ถ„ํ•œ ๋ฌธ์ž์—ด์— ์ˆซ์ž๊ฐ€ ์•„๋‹Œ ๊ฐ’์ด ์žˆ์œผ๋ฉด ์˜ˆ์™ธ์ฒ˜๋ฆฌ : +[ERROR] ๋‹น์ฒจ ๋ฒˆํ˜ธ๋Š” 1~45 ๋ฒ”์œ„ ๋‚ด์˜ ์ค‘๋ณต๋˜์ง€ ์•Š๋Š” 6๊ฐœ์˜ ์ˆซ์ž๊ฐ’์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. +- ๊ฐ ๋ฒˆํ˜ธ๋Š” 1~45์˜ ๋ฒ”์œ„ ๋‚ด์— ์žˆ์–ด์•ผ ํ•œ๋‹ค. + ๋งŒ์•ฝ ๋ฒ”์œ„ ์™ธ์˜ ์ˆซ์ž๋ฅผ ํฌํ•จํ•œ๋‹ค๋ฉด ์˜ˆ์™ธ ์ฒ˜๋ฆฌ : [ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 1~45์˜ ์ˆซ์ž์˜ ๋ฒ”์œ„์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. +- ๊ฐ ๋ฒˆํ˜ธ๋Š” ์ค‘๋ณต์—†์ด 6๊ฐœ์—ฌ์•ผ๋งŒ ํ•œ๋‹ค. + ๋งŒ์•ฝ, ๊ฐœ์ˆ˜๊ฐ€ 6๊ฐœ๊ฐ€ ์•ˆ๋˜๋ฉด ์˜ˆ์™ธ์ฒ˜๋ฆฌ: [ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 6๊ฐœ์˜ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +4. ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ ์ž…๋ ฅ๋ฐ›๊ธฐ +- ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ ์—ญ์‹œ 1~45 ๋ฒ”์œ„ ๋‚ด์˜ ์ˆซ์ž์—ฌ์•ผ ํ•จ + ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๊ฐ€ 1~45 ์™ธ์˜ ์ˆซ์ž์ผ ๊ฒฝ์šฐ ์˜ˆ์™ธ์ฒ˜๋ฆฌ : [ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” 1~45 ๋ฒ”์œ„ ์ค‘ ํ•˜๋‚˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค +- ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ์ค‘๋ณต๋˜๋ฉด ์•ˆ๋จ + ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ์™€ ๋‹น์ฒจ๋ฒˆํ˜ธ๊ฐ€ ์ค‘๋ณต๋  ๊ฒฝ์šฐ ์˜ˆ์™ธ์ฒ˜๋ฆฌ : [ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ์ค‘๋ณต๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. + +5. ์•„๋ž˜์˜ ๋‹น์ฒจ ๊ธฐ์ค€์— ๋งž๋Š” ๋‹น์ฒจ ํ†ต๊ณ„ ์ถœ๋ ฅํ•˜๊ธฐ +``` +1๋“ฑ: 6๊ฐœ ๋ฒˆํ˜ธ ์ผ์น˜ / 2,000,000,000์› +2๋“ฑ: 5๊ฐœ ๋ฒˆํ˜ธ + ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ ์ผ์น˜ / 30,000,000์› +3๋“ฑ: 5๊ฐœ ๋ฒˆํ˜ธ ์ผ์น˜ / 1,500,000์› +4๋“ฑ: 4๊ฐœ ๋ฒˆํ˜ธ ์ผ์น˜ / 50,000์› +5๋“ฑ: 3๊ฐœ ๋ฒˆํ˜ธ ์ผ์น˜ / 5,000์› +``` + +- ๊ฐ ๋กœ๋˜ ๋ฐฐ์—ด๊ณผ ๋กœ๋˜ ๋‹น์ฒจ ๋ฐฐ์—ด์„ ํ™•์ธํ•ด์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅํ•œ๋‹ค. +``` +3๊ฐœ ์ผ์น˜ (5,000์›) - 1๊ฐœ +4๊ฐœ ์ผ์น˜ (50,000์›) - 0๊ฐœ +5๊ฐœ ์ผ์น˜ (1,500,000์›) - 0๊ฐœ +5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜ (30,000,000์›) - 0๊ฐœ +6๊ฐœ ์ผ์น˜ (2,000,000,000์›) - 0๊ฐœ +์ด ์ˆ˜์ต๋ฅ ์€ 62.5%์ž…๋‹ˆ๋‹ค. +``` + +- ์ˆ˜์ต๋ฅ ์€ (์ด ์ˆ˜์ต/์ด ํˆฌ์ž ๊ธˆ์•ก) * 100๋กœ ๊ณ„์‚ฐํ•œ๋‹ค. +- ์ˆ˜์ต๋ฅ ์€ ์†Œ์ˆ˜์  ๋‘˜์งธ ์ž๋ฆฌ์—์„œ ๋ฐ˜์˜ฌ๋ฆผํ•œ๋‹ค. diff --git a/__tests__/ApplicationTest.js b/__tests__/ApplicationTest.js index 872380c9c..18af06b9c 100644 --- a/__tests__/ApplicationTest.js +++ b/__tests__/ApplicationTest.js @@ -1,5 +1,5 @@ -import App from "../src/App.js"; -import { MissionUtils } from "@woowacourse/mission-utils"; +import App from '../src/App.js'; +import { MissionUtils } from '@woowacourse/mission-utils'; const mockQuestions = (inputs) => { MissionUtils.Console.readLineAsync = jest.fn(); @@ -19,7 +19,7 @@ const mockRandoms = (numbers) => { }; const getLogSpy = () => { - const logSpy = jest.spyOn(MissionUtils.Console, "print"); + const logSpy = jest.spyOn(MissionUtils.Console, 'print'); logSpy.mockClear(); return logSpy; }; @@ -29,25 +29,29 @@ const runException = async (input) => { const logSpy = getLogSpy(); const RANDOM_NUMBERS_TO_END = [1, 2, 3, 4, 5, 6]; - const INPUT_NUMBERS_TO_END = ["1000", "1,2,3,4,5,6", "7"]; + const INPUT_NUMBERS_TO_END = ['1000', '1,2,3,4,5,6', '7']; mockRandoms([RANDOM_NUMBERS_TO_END]); mockQuestions([input, ...INPUT_NUMBERS_TO_END]); // when const app = new App(); - await app.run(); + try { + await app.run(); + } catch (error) { + console.log('Captured Error:', error.message); + } // then - expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("[ERROR]")); + expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('[ERROR]')); }; -describe("๋กœ๋˜ ํ…Œ์ŠคํŠธ", () => { +describe('๋กœ๋˜ ํ…Œ์ŠคํŠธ', () => { beforeEach(() => { jest.restoreAllMocks(); }); - test("๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ", async () => { + test('๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ', async () => { // given const logSpy = getLogSpy(); @@ -61,7 +65,7 @@ describe("๋กœ๋˜ ํ…Œ์ŠคํŠธ", () => { [2, 13, 22, 32, 38, 45], [1, 3, 5, 14, 22, 45], ]); - mockQuestions(["8000", "1,2,3,4,5,6", "7"]); + mockQuestions(['8000', '1,2,3,4,5,6', '7']); // when const app = new App(); @@ -69,21 +73,21 @@ describe("๋กœ๋˜ ํ…Œ์ŠคํŠธ", () => { // then const logs = [ - "8๊ฐœ๋ฅผ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค.", - "[8, 21, 23, 41, 42, 43]", - "[3, 5, 11, 16, 32, 38]", - "[7, 11, 16, 35, 36, 44]", - "[1, 8, 11, 31, 41, 42]", - "[13, 14, 16, 38, 42, 45]", - "[7, 11, 30, 40, 42, 43]", - "[2, 13, 22, 32, 38, 45]", - "[1, 3, 5, 14, 22, 45]", - "3๊ฐœ ์ผ์น˜ (5,000์›) - 1๊ฐœ", - "4๊ฐœ ์ผ์น˜ (50,000์›) - 0๊ฐœ", - "5๊ฐœ ์ผ์น˜ (1,500,000์›) - 0๊ฐœ", - "5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜ (30,000,000์›) - 0๊ฐœ", - "6๊ฐœ ์ผ์น˜ (2,000,000,000์›) - 0๊ฐœ", - "์ด ์ˆ˜์ต๋ฅ ์€ 62.5%์ž…๋‹ˆ๋‹ค.", + '8๊ฐœ๋ฅผ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค.', + '[8, 21, 23, 41, 42, 43]', + '[3, 5, 11, 16, 32, 38]', + '[7, 11, 16, 35, 36, 44]', + '[1, 8, 11, 31, 41, 42]', + '[13, 14, 16, 38, 42, 45]', + '[7, 11, 30, 40, 42, 43]', + '[2, 13, 22, 32, 38, 45]', + '[1, 3, 5, 14, 22, 45]', + '3๊ฐœ ์ผ์น˜ (5,000์›) - 1๊ฐœ', + '4๊ฐœ ์ผ์น˜ (50,000์›) - 0๊ฐœ', + '5๊ฐœ ์ผ์น˜ (1,500,000์›) - 0๊ฐœ', + '5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜ (30,000,000์›) - 0๊ฐœ', + '6๊ฐœ ์ผ์น˜ (2,000,000,000์›) - 0๊ฐœ', + '์ด ์ˆ˜์ต๋ฅ ์€ 62.5%์ž…๋‹ˆ๋‹ค.', ]; logs.forEach((log) => { @@ -91,7 +95,7 @@ describe("๋กœ๋˜ ํ…Œ์ŠคํŠธ", () => { }); }); - test("์˜ˆ์™ธ ํ…Œ์ŠคํŠธ", async () => { - await runException("1000j"); + test('์˜ˆ์™ธ ํ…Œ์ŠคํŠธ', async () => { + await runException('1000j'); }); }); diff --git a/__tests__/LottoTest.js b/__tests__/LottoTest.js index 409aaf69b..7ed17b8c9 100644 --- a/__tests__/LottoTest.js +++ b/__tests__/LottoTest.js @@ -1,18 +1,37 @@ -import Lotto from "../src/Lotto"; +import Lotto from '../src/Lotto'; -describe("๋กœ๋˜ ํด๋ž˜์Šค ํ…Œ์ŠคํŠธ", () => { - test("๋กœ๋˜ ๋ฒˆํ˜ธ์˜ ๊ฐœ์ˆ˜๊ฐ€ 6๊ฐœ๊ฐ€ ๋„˜์–ด๊ฐ€๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { +describe('๋กœ๋˜ ํด๋ž˜์Šค ํ…Œ์ŠคํŠธ', () => { + test('๋กœ๋˜ ๋ฒˆํ˜ธ์˜ ๊ฐœ์ˆ˜๊ฐ€ 6๊ฐœ๊ฐ€ ๋„˜์–ด๊ฐ€๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.', () => { expect(() => { new Lotto([1, 2, 3, 4, 5, 6, 7]); - }).toThrow("[ERROR]"); + }).toThrow('[ERROR]'); }); - // TODO: ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ต๊ณผํ•˜๋„๋ก ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ ๊ตฌํ˜„ - test("๋กœ๋˜ ๋ฒˆํ˜ธ์— ์ค‘๋ณต๋œ ์ˆซ์ž๊ฐ€ ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.", () => { + test('๋กœ๋˜ ๋ฒˆํ˜ธ์˜ ๊ฐœ์ˆ˜๊ฐ€ 6๊ฐœ๋ณด๋‹ค ์ ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.', () => { + expect(() => { + new Lotto([1, 2, 3, 4, 5]); + }).toThrow('[ERROR]'); + }); + + test('๋กœ๋˜ ๋ฒˆํ˜ธ์— ์ค‘๋ณต๋œ ์ˆซ์ž๊ฐ€ ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.', () => { expect(() => { new Lotto([1, 2, 3, 4, 5, 5]); - }).toThrow("[ERROR]"); + }).toThrow('[ERROR]'); + }); + + test('๋กœ๋˜ ๋ฒˆํ˜ธ๊ฐ€ 1~45 ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.', () => { + expect(() => { + new Lotto([0, 2, 3, 4, 5, 6]); + }).toThrow('[ERROR]'); + + expect(() => { + new Lotto([1, 2, 3, 4, 5, 46]); + }).toThrow('[ERROR]'); }); - // TODO: ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„์— ๋”ฐ๋ฅธ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ + test('์œ ํšจํ•œ ๋กœ๋˜ ๋ฒˆํ˜ธ ๋ฐฐ์—ด์ด ์ฃผ์–ด์กŒ์„ ๋•Œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.', () => { + expect(() => { + new Lotto([1, 2, 3, 4, 5, 6]); + }).not.toThrow(); + }); }); diff --git a/src/App.js b/src/App.js index 091aa0a5d..0070eb34f 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,71 @@ +import { Console } from '@woowacourse/mission-utils'; +import { CONSTANTS } from './constants.js'; +import Lotto from './Lotto.js'; +import { + validateAmount, + validateWinningNumbers, + validateBonusNumber, +} from './Validation.js'; +import { calculateStatistics } from './Statistics.js'; + class App { - async run() {} + async run() { + try { + Console.print(CONSTANTS.MESSAGE_PURCHASE_AMOUNT); + const totalAmount = await this.getPurchaseAmount(); + validateAmount(totalAmount); + + const numberOfTickets = totalAmount / CONSTANTS.LOTTO_PRICE; + Console.print( + `\n${numberOfTickets}${CONSTANTS.MESSAGE_PURCHASED_TICKETS}` + ); + + const lottos = this.generateLottos(numberOfTickets); + lottos.forEach((lottoNumbers) => { + Console.print(`[${lottoNumbers.join(', ')}]`); + }); + + Console.print(CONSTANTS.MESSAGE_WINNING_NUMBERS); + const winningNumbers = await this.getWinningNumbers(); + + Console.print(CONSTANTS.MESSAGE_BONUS_NUMBER); + const bonusNumber = await this.getBonusNumber(winningNumbers); + + calculateStatistics(lottos, winningNumbers, bonusNumber, totalAmount); + } catch (error) { + Console.print(error.message); + throw error; + } + } + + async getPurchaseAmount() { + const input = await Console.readLineAsync(''); + return Number(input); + } + + generateLottos(count) { + const lottos = []; + for (let i = 0; i < count; i++) { + const lotto = new Lotto(); + lottos.push(lotto.getNumbers()); + } + return lottos; + } + + async getWinningNumbers() { + const input = await Console.readLineAsync(''); + const winningNumbers = input.split(',').map(Number); + validateWinningNumbers(winningNumbers); + return winningNumbers; + } + + async getBonusNumber(winningNumbers) { + const input = await Console.readLineAsync(''); + const bonusNumber = Number(input); + validateBonusNumber(bonusNumber, winningNumbers); + return bonusNumber; + } } +const app = new App(); export default App; diff --git a/src/Lotto.js b/src/Lotto.js index cb0b1527e..e62f8903a 100644 --- a/src/Lotto.js +++ b/src/Lotto.js @@ -1,18 +1,35 @@ +import { Random } from '@woowacourse/mission-utils'; +import { CONSTANTS } from './constants.js'; + class Lotto { #numbers; constructor(numbers) { - this.#validate(numbers); - this.#numbers = numbers; + this.#numbers = + numbers || + Random.pickUniqueNumbersInRange(1, 45, 6).sort((a, b) => a - b); + this.#validate(this.#numbers); } #validate(numbers) { if (numbers.length !== 6) { - throw new Error("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 6๊ฐœ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + throw new Error(CONSTANTS.ERROR_INSUFFICIENT_LENGTH); + } + + const uniqueNumbers = new Set(numbers); + if (uniqueNumbers.size !== numbers.length) { + throw new Error(CONSTANTS.ERROR_DUPLICATE_NUMBERS); + } + + const isInRange = numbers.every((num) => num >= 1 && num <= 45); + if (!isInRange) { + throw new Error(CONSTANTS.ERROR_INVALID_RANGE); } } - // TODO: ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„ + getNumbers() { + return this.#numbers; + } } export default Lotto; diff --git a/src/Statistics.js b/src/Statistics.js new file mode 100644 index 000000000..43e11d604 --- /dev/null +++ b/src/Statistics.js @@ -0,0 +1,58 @@ +import { Console } from '@woowacourse/mission-utils'; +import { CONSTANTS } from './constants.js'; + +export function calculateStatistics( + lottos, + winningNumbers, + bonusNumber, + totalAmount +) { + const rankCounts = { + THREE_MATCH: 0, + FOUR_MATCH: 0, + FIVE_MATCH: 0, + FIVE_MATCH_BONUS: 0, + SIX_MATCH: 0, + }; + + lottos.forEach((lottoNumbers) => { + const matchCount = lottoNumbers.filter((num) => + winningNumbers.includes(num) + ).length; + const hasBonus = lottoNumbers.includes(bonusNumber); + + if (matchCount === 6) rankCounts.SIX_MATCH++; + else if (matchCount === 5 && hasBonus) rankCounts.FIVE_MATCH_BONUS++; + else if (matchCount === 5) rankCounts.FIVE_MATCH++; + else if (matchCount === 4) rankCounts.FOUR_MATCH++; + else if (matchCount === 3) rankCounts.THREE_MATCH++; + }); + + const totalPrize = + rankCounts.SIX_MATCH * CONSTANTS.WINNING_RANKS.SIX_MATCH.prize + + rankCounts.FIVE_MATCH_BONUS * + CONSTANTS.WINNING_RANKS.FIVE_MATCH_BONUS.prize + + rankCounts.FIVE_MATCH * CONSTANTS.WINNING_RANKS.FIVE_MATCH.prize + + rankCounts.FOUR_MATCH * CONSTANTS.WINNING_RANKS.FOUR_MATCH.prize + + rankCounts.THREE_MATCH * CONSTANTS.WINNING_RANKS.THREE_MATCH.prize; + + const profitRate = ((totalPrize / totalAmount) * 100).toFixed(1); + + Console.print(CONSTANTS.MESSAGE_WINNING_STATISTICS); + Console.print( + `${CONSTANTS.WINNING_RANKS.THREE_MATCH.message}${rankCounts.THREE_MATCH}๊ฐœ` + ); + Console.print( + `${CONSTANTS.WINNING_RANKS.FOUR_MATCH.message}${rankCounts.FOUR_MATCH}๊ฐœ` + ); + Console.print( + `${CONSTANTS.WINNING_RANKS.FIVE_MATCH.message}${rankCounts.FIVE_MATCH}๊ฐœ` + ); + Console.print( + `${CONSTANTS.WINNING_RANKS.FIVE_MATCH_BONUS.message}${rankCounts.FIVE_MATCH_BONUS}๊ฐœ` + ); + Console.print( + `${CONSTANTS.WINNING_RANKS.SIX_MATCH.message}${rankCounts.SIX_MATCH}๊ฐœ` + ); + Console.print(`${CONSTANTS.MESSAGE_PROFIT_RATE} ${profitRate}%์ž…๋‹ˆ๋‹ค.`); +} diff --git a/src/Validation.js b/src/Validation.js new file mode 100644 index 000000000..8e4292ada --- /dev/null +++ b/src/Validation.js @@ -0,0 +1,32 @@ +import { CONSTANTS } from './constants.js'; + +export function validateAmount(amount) { + if (isNaN(amount) || amount % CONSTANTS.LOTTO_PRICE !== 0) { + throw new Error(CONSTANTS.ERROR_INVALID_AMOUNT); + } +} + +export function validateWinningNumbers(numbers) { + if (numbers.length !== 6) { + throw new Error(CONSTANTS.ERROR_INSUFFICIENT_NUMBERS); + } + + const isWithinRange = numbers.every((num) => num >= 1 && num <= 45); + if (!isWithinRange) { + throw new Error(CONSTANTS.ERROR_INVALID_RANGE); + } + + const isUnique = new Set(numbers).size === 6; + if (!isUnique) { + throw new Error(CONSTANTS.ERROR_INVALID_WINNING_NUMBERS); + } +} + +export function validateBonusNumber(bonusNumber, winningNumbers) { + if (bonusNumber < 1 || bonusNumber > 45) { + throw new Error(CONSTANTS.ERROR_INVALID_RANGE); + } + if (winningNumbers.includes(bonusNumber)) { + throw new Error(CONSTANTS.ERROR_DUPLICATE_BONUS_NUMBER); + } +} diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 000000000..7ddda97f9 --- /dev/null +++ b/src/constants.js @@ -0,0 +1,37 @@ +export const CONSTANTS = { + LOTTO_PRICE: 1000, + + MESSAGE_PURCHASE_AMOUNT: '๊ตฌ์ž…๊ธˆ์•ก์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.', + MESSAGE_PURCHASED_TICKETS: '๊ฐœ๋ฅผ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค.', + MESSAGE_WINNING_NUMBERS: '\n๋‹น์ฒจ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.', + MESSAGE_BONUS_NUMBER: '\n๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.', + MESSAGE_WINNING_STATISTICS: '\n๋‹น์ฒจ ํ†ต๊ณ„\n---', + MESSAGE_PROFIT_RATE: '์ด ์ˆ˜์ต๋ฅ ์€', + + WINNING_RANKS: { + THREE_MATCH: { prize: 5000, message: '3๊ฐœ ์ผ์น˜ (5,000์›) - ' }, + FOUR_MATCH: { prize: 50000, message: '4๊ฐœ ์ผ์น˜ (50,000์›) - ' }, + FIVE_MATCH: { prize: 1500000, message: '5๊ฐœ ์ผ์น˜ (1,500,000์›) - ' }, + FIVE_MATCH_BONUS: { + prize: 30000000, + message: '5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜ (30,000,000์›) - ', + }, + SIX_MATCH: { + prize: 2000000000, + message: '6๊ฐœ ์ผ์น˜ (2,000,000,000์›) - ', + }, + }, + + ERROR_INVALID_AMOUNT: + '[ERROR] ๊ธˆ์•ก์€ 1000์œผ๋กœ ๋‚˜๋ˆ„์–ด ๋–จ์–ด์ง€๋Š” ์ˆซ์ž๋งŒ ์ž…๋ ฅ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.', + ERROR_INVALID_WINNING_NUMBERS: + '[ERROR] ์ˆซ์ž๊ฐ€ ์ค‘๋ณต๋ฉ๋‹ˆ๋‹ค. ๋‹น์ฒจ ๋ฒˆํ˜ธ๋Š” 1~45 ๋ฒ”์œ„ ๋‚ด์˜ ์ค‘๋ณต๋˜์ง€ ์•Š๋Š” 6๊ฐœ์˜ ์ˆซ์ž๊ฐ’์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.', + ERROR_INVALID_RANGE: + '[ERROR] ์ž…๋ ฅ ๊ฐ€๋Šฅํ•œ ์ˆซ์ž ๋ฒ”์œ„๋ฅผ ๋„˜์Šต๋‹ˆ๋‹ค. ๋ฒˆํ˜ธ๋Š” 1~45 ๋ฒ”์œ„ ์ค‘ ํ•˜๋‚˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.', + ERROR_INSUFFICIENT_NUMBERS: + '[ERROR] ์ž…๋ ฅ๋œ ์ˆซ์ž๊ฐ’์ด ๋ชจ์ž๋ฆ…๋‹ˆ๋‹ค. 6๊ฐœ์˜ ์ˆซ์ž๊ฐ€ ์ž…๋ ฅ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.', + ERROR_DUPLICATE_BONUS_NUMBER: + '[ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ์ค‘๋ณต๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.', + ERROR_INSUFFICIENT_LENGTH: '[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 6๊ฐœ์˜ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.', + ERROR_DUPLICATE_NUMBERS: '[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ์— ์ค‘๋ณต๋œ ์ˆซ์ž๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.', +};