-
Notifications
You must be signed in to change notification settings - Fork 626
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[로또] 김민경 미션 제출합니다. #641
base: main
Are you sure you want to change the base?
[로또] 김민경 미션 제출합니다. #641
Changes from all commits
2965877
6da2a40
9719a00
c323cf5
e427e11
d228026
eaf6b7a
29f7c65
0e53e3f
707d78d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# 미션 - 로또 | ||
|
||
## 🎯 목표 | ||
|
||
- [ ] 클래스(객체)를 분리하는 연습 | ||
- [ ] 도메인 로직에 대한 단위 테스트를 작성하는 연습 | ||
|
||
## 🕹 기능 요구 사항(구현할 기능 목록) | ||
|
||
### ⌨️ 사용자 입력 받기 | ||
|
||
- [x] 로또 구입 금액을 입력 받기 | ||
- [x] 당첨 번호를 입력 받기 | ||
- [x] 보너스 번호를 입력 받기 | ||
|
||
#### 예외 : "[ERROR]"로 시작 | ||
|
||
- [x] 숫자 외의 것을 입력할 시 예외 처리 | ||
- [x] 1,000원으로 나누어 떨어지지 않을 시 예외 처리 | ||
- ~~[ ] 쉼표(,)로 구분하지 않을 시 예외 처리~~ | ||
- [x] 로또 번호 입력받을 때 1~45의 숫자 범위를 벗어날 시 예외 처리 | ||
|
||
### 🖨 출력 | ||
|
||
- [x] 발행한 로또 수량 출력 | ||
- [x] 발행한 로또 번호 출력 | ||
- [x] 로또 번호는 오름차순으로 정렬 | ||
|
||
#### 🎮 게임 종료 | ||
|
||
- [x] 당첨 내역을 출력 : 당첨 기준에 따른 당첨된 로또 개수 출력 | ||
- [x] 총 수익률 출력 | ||
- [x] 소수점 둘째 자리에서 반올림 | ||
|
||
### 🎰 게임 판단 | ||
|
||
- [x] 사용자가 구매한 로또 번호와 당첨 번호 비교 | ||
|
||
## 🖥 프로그래밍 요구 사항 | ||
|
||
- [x] `Node.js` `18.17.1` 버전에서 실행 가능 | ||
- [x] `App.js`의 `play` 메서드에서 프로그램 실행 | ||
- [ ] `ApplicationTest` 의 모든 테스트 성공 | ||
- [x] indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현 | ||
- [ ] Jest를 이용하여 내가 정리한 기능 목록이 정상 동작함을 테스트 코드로 확인 | ||
|
||
### 추가 요구 사항 | ||
|
||
- [ ] 함수(또는 메서드)의 길이가 15라인을 넘어가지 않도록 구현 | ||
- [ ] 함수(또는 메서드)가 한 가지 일만 하도록 구현 | ||
- else를 지양 | ||
(힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 됨. | ||
때로는 if/else, switch문을 사용하는 것이 더 깔끔해 보일 수 있음.) | ||
- [ ] 도메인 로직에 단위 테스트를 구현 | ||
- [ ] 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분 | ||
|
||
### 라이브러리 | ||
|
||
- [x] `@woowacourse/mission-utils`에서 제공하는 `Random` 및 `Console` API를 사용하여 구현 | ||
|
||
### Lotto 클래스 | ||
|
||
- [x] 제공된 `Lotto` 클래스를 활용해 구현 | ||
- [x] `numbers`의 `#` prefix를 변경할 수 없음 | ||
- [x] `Lotto`에 필드를 추가할 수 없음 | ||
|
||
## 📓 과제 진행 요구 사항 | ||
|
||
- [x] 저장소를 Fork & Clone | ||
- [x] 구현할 기능 목록 작성 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,169 @@ | ||
import { Random, Console } from "@woowacourse/mission-utils"; | ||
import { GAME, ERROR } from "./message"; | ||
import Lotto from "./Lotto"; | ||
|
||
class App { | ||
async play() {} | ||
#amount; | ||
#lottos; | ||
|
||
constructor() { | ||
this.#amount = 0; | ||
this.#lottos = []; | ||
} | ||
|
||
validateAmount = (amount) => { | ||
if (isNaN(amount) || amount < 0) { | ||
throw new Error(ERROR.NOT_NUMBER); | ||
} | ||
if (amount % 1000 !== 0) { | ||
throw new Error(ERROR.INVALID_AMOUNT); | ||
} | ||
}; | ||
|
||
validateWinningNumbers = (numbers) => { | ||
if (numbers.length !== 6) { | ||
throw new Error(ERROR.INVALID_ARRAY_LENGTH); | ||
} | ||
|
||
for (const number of numbers) { | ||
if (number < 1 || number > 45) { | ||
throw new Error(ERROR.OUT_OF_RANGE); | ||
} | ||
} | ||
|
||
const uniqueNumbers = new Set(numbers); | ||
if (uniqueNumbers.size !== numbers.length) { | ||
throw new Error(ERROR.DUPLICATE_NUMBER); | ||
} | ||
}; | ||
|
||
validateBonusNumber = (number, winningNumbers) => { | ||
if (isNaN(number) || number < 0) { | ||
throw new Error(ERROR.NOT_NUMBER); | ||
} | ||
if (number < 1 || number > 45) { | ||
throw new Error(ERROR.OUT_OF_RANGE); | ||
} | ||
if (winningNumbers.includes(number)) { | ||
throw new Error(ERROR.DUPLICATE_NUMBER); | ||
} | ||
}; | ||
|
||
inputPurchaseAmount = async () => { | ||
while (true) { | ||
try { | ||
this.#amount = Number(await Console.readLineAsync(GAME.INPUT.AMOUNT)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 구매 금액에 대한 유효성 검사 후에 검사를 통과한 구매 금액은 this.#amount 의 값으로 넣어주는게 더 나을 것 같아요 |
||
this.validateAmount(this.#amount); | ||
const count = this.#amount / 1000; | ||
this.generateLottosList(count); | ||
break; | ||
} catch (error) { | ||
throw new Error(ERROR.INPUT); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. validateAmount에서 오류가 나면 throw를 하기 때문에 throw문이 필요없어요. 그런데 오류 메세지를 출력하는 코드가 보이지 않네요. |
||
} | ||
} | ||
Comment on lines
+53
to
+63
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혹시 throw error아니면 |
||
}; | ||
|
||
generateLottosList = (count) => { | ||
Console.print(`${count}개를 구입했습니다.`); | ||
|
||
for (let i = 0; i < count; i += 1) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for 문 보다 |
||
const numbers = Random.pickUniqueNumbersInRange(1, 45, 6); | ||
this.#lottos.push(numbers); | ||
const lotto = new Lotto(numbers); | ||
lotto.printLottos(); | ||
} | ||
}; | ||
|
||
inputWinningNumbers = async () => { | ||
while (true) { | ||
try { | ||
const numbers = await Console.readLineAsync(GAME.INPUT.WINNING_NUMBER); | ||
const numbersArray = numbers | ||
.split(",") | ||
.map((num) => Number(num.trim())); | ||
this.validateWinningNumbers(numbersArray); | ||
return numbersArray; | ||
} catch (error) { | ||
throw new Error(ERROR.INPUT); | ||
} | ||
} | ||
}; | ||
|
||
inputBonusNumber = async (winningNumbers) => { | ||
while (true) { | ||
try { | ||
const number = Number( | ||
await Console.readLineAsync(GAME.INPUT.BONUS_NUMBER) | ||
); | ||
this.validateBonusNumber(number, winningNumbers); | ||
return number; | ||
} catch (error) { | ||
throw new Error(ERROR.INPUT); | ||
} | ||
} | ||
}; | ||
|
||
matchedNumbers = async () => { | ||
const winningNumbers = await this.inputWinningNumbers(); | ||
const bonusNumber = await this.inputBonusNumber(winningNumbers); | ||
|
||
let matched = [0, 0, 0, 0, 0]; | ||
|
||
this.#lottos.forEach((lotto) => { | ||
const matchedCount = lotto.filter((number) => | ||
winningNumbers.includes(number) | ||
).length; | ||
|
||
if (matchedCount === 6) matched[4] += 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. else문을 지양하라는 요구사항이 있어서 switch문을 사용해 보시는 거는 어떨까요? |
||
else if (matchedCount === 5 && lotto.includes(bonusNumber)) { | ||
matched[3] += 1; | ||
} else if (matchedCount === 5) matched[2] += 1; | ||
else if (matchedCount === 4) matched[1] += 1; | ||
else if (matchedCount === 3) matched[0] += 1; | ||
}); | ||
|
||
return matched; | ||
}; | ||
|
||
winningStatistics = async () => { | ||
const matchedCounts = await this.matchedNumbers(); | ||
this.printWinningStatistics(matchedCounts); | ||
this.calculateProfitRate(matchedCounts); | ||
}; | ||
|
||
printWinningStatistics = (matchedCounts) => { | ||
Console.print(GAME.WINNING_STATISTICS); | ||
|
||
const prizeMoney = [ | ||
"5,000", | ||
"50,000", | ||
"1,500,000", | ||
"30,000,000", | ||
"2,000,000,000", | ||
]; | ||
for (let i = 0; i < 5; i += 1) { | ||
Console.print( | ||
`${i + 3}개 일치 (${prizeMoney[i]}원) - ${matchedCounts[i]}개` | ||
); | ||
} | ||
}; | ||
|
||
calculateProfitRate = (matchedCounts) => { | ||
const prizeMoney = [5000, 50000, 1500000, 30000000, 2000000000]; | ||
let totalCost = 0; | ||
|
||
for (let i = 0; i < 5; i += 1) { | ||
totalCost += prizeMoney[i] * matchedCounts[i]; | ||
} | ||
const profitRate = ((totalCost / this.#amount) * 100).toFixed(1); | ||
|
||
Console.print(`총 수익률은 ${profitRate}%입니다.`); | ||
}; | ||
|
||
async play() { | ||
await this.inputPurchaseAmount(); | ||
await this.winningStatistics(); | ||
} | ||
} | ||
|
||
export default App; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
export const GAME = Object.freeze({ | ||
INPUT: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 상수로 쓰인 변수의 속성은 대문자로 사용하지 않으셔도 돼요 |
||
AMOUNT: "구입금액을 입력해 주세요.\n", | ||
WINNING_NUMBER: "당첨 번호를 입력해 주세요\n", | ||
BONUS_NUMBER: "보너스 번호를 입력해 주세요.\n", | ||
}, | ||
WINNING_STATISTICS: "당첨 통계\n---\n", | ||
}); | ||
|
||
export const ERROR = Object.freeze({ | ||
INPUT: "[ERROR] 입력을 실패하였습니다.", | ||
NOT_NUMBER: "[ERROR] 0 이상의 자연수로 입력해야 합니다.", | ||
INVALID_AMOUNT: "[ERROR] 구입 금액은 1000원 단위로 입력해야 합니다.", | ||
INVALID_ARRAY_LENGTH: "[ERROR] 로또 번호는 6개여야 합니다.", | ||
OUT_OF_RANGE: "[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다.", | ||
DUPLICATE_NUMBER: "[ERROR] 중복된 번호가 포함되어 있습니다", | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
필드 선언시에
으로 값을 설정하는게 더 좋을 것 같아요