Skip to content
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

[로또] 이승준 미션 제출합니다. #621

Open
wants to merge 53 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
fc4d9d9
docs : 기능 요구사항 작성
1eeSJ Nov 4, 2023
6e8fac7
test : (입력) 구입금액 입력 테스트 작성
1eeSJ Nov 5, 2023
8005486
feat : (입력) 구입금액 입력 기능
1eeSJ Nov 5, 2023
012e050
refactor : (입력) 구입금액 입력 문구 constants 파일에 분리
1eeSJ Nov 5, 2023
2796d78
test : (입력) 구입금액이 1000으로 나누어 떨어지지 않을 경우의 테스트 작성
1eeSJ Nov 5, 2023
e3ff45c
feat : (입력) 구입금액이 1000으로 나누어 떨어지지 않을 경우 예외 처리 기능
1eeSJ Nov 5, 2023
67e06d0
refactor : (입력) 구입금액이 1000으로 나누어 떨어지지 않을 경우 예외 처리 파일 분리
1eeSJ Nov 5, 2023
2c79348
docs : (입력) 구입금액 입력 기능 완료
1eeSJ Nov 5, 2023
91c83c0
docs : 구입 금액이 0 이하일 경우 예외 처리 요구사항 추가.
1eeSJ Nov 5, 2023
4650eb2
test : (입력) 구입 금액이 0 이하일 경우 예외 처리 테스트 작성
1eeSJ Nov 5, 2023
afe2cb3
style : 변수명 변경 inputMoney -> inputPrice, perchase -> price
1eeSJ Nov 5, 2023
c5e12b5
feat : (입력) 구입 금액이 0 이하일 경우 예외 처리 기능
1eeSJ Nov 5, 2023
9fa1be8
refactor : (입력) 구입 금액이 0 이하일 경우 예외 처리 기능 파일 분리
1eeSJ Nov 5, 2023
b11a6df
docs : 구입 금액이 0 이하일 경우 예외 처리 완료
1eeSJ Nov 5, 2023
212de5d
rename : 파일명 변경 Lotto.js -> lotto.js , 파일 위치 변경 model폴더 안으로 변경
1eeSJ Nov 5, 2023
9fa5544
rename : InputTest.js 파일 위치 변경 /__tests__ -> /__tests__/veiw
1eeSJ Nov 5, 2023
a71ae57
rename : LottoTest.js 파일 위치 변경 /__tests__ -> /__tests__/model
1eeSJ Nov 5, 2023
95fcf53
fix : LottoTest.js, InputTest.js 파일 위치 변경에 따른 import 경로 수정
1eeSJ Nov 5, 2023
6759b09
fix : lotto.js 파일명 변경에 따른 LottoTest.js import 부분 변경
1eeSJ Nov 5, 2023
c0bd065
refactor : 로또번호 갯수 검사 부분 lotto-validation 파일을 만들어서 분리
1eeSJ Nov 7, 2023
b22de46
feat : 로또 번호 중복 예외처리 기능
1eeSJ Nov 7, 2023
fa9301e
test : 로또 번호가 1-45범위를 초과하면 예외 처리 테스트 작성
1eeSJ Nov 8, 2023
b7fc29b
feat : 로또 번호가 1-45 범위를 초과할 경우 예외 처리 기능
1eeSJ Nov 8, 2023
a953c67
refactor : lotto의 유효성 검사 함수들을 객체로 묶어서 export 하도록 수정
1eeSJ Nov 8, 2023
9fcc56a
refactor : input값의 유효성 검사 함수들을 객체로 묶어서 export 하도록 수정
1eeSJ Nov 8, 2023
6d2bad0
rename : validation폴더명과 파일명을 validator로 변경
1eeSJ Nov 8, 2023
e54e23d
fix : InputValidator객체로 변경하면서 함수 이름 변경에 따른 오류 수정
1eeSJ Nov 8, 2023
4ea4ef3
style : 객체명, 파일명 오타 수정
1eeSJ Nov 8, 2023
337c99d
style : 요구사항에 따라 Lotto.js 파일명 다시 변경
1eeSJ Nov 8, 2023
d5fb05e
test : 요구사항에 따라 입력값 테스트는 삭제
1eeSJ Nov 8, 2023
d2edae7
style : 테스트 코드 주석 입력값 테스트에서 입력값 예외처리 테스트로 텍스트 변경
1eeSJ Nov 8, 2023
1fa00b2
docs : 로또 번호가 자연수가 아닐 경우 예외 처리 요구사항 추가
1eeSJ Nov 8, 2023
2c71616
test : 로또 번호가 자연수가 아닐 경우의 테스트 작성
1eeSJ Nov 8, 2023
c9623c5
feat : 로또 번호가 자연수가 아닐 경우 예외 처리 기능
1eeSJ Nov 8, 2023
cca7f88
docs : 로또 번호가 자연수가 아닐 경우 예외 처리 기능 완료
1eeSJ Nov 8, 2023
a1e2721
rename : model파일명 domain 으로 변경
1eeSJ Nov 8, 2023
a69cd68
feat : LottoController 생성
1eeSJ Nov 8, 2023
71fd758
feat : 로또번호 랜덤 생성 기능
1eeSJ Nov 8, 2023
24a699d
feat : Lotto클래스 로또 번호를 반환하는 getNumbers함수 추가
1eeSJ Nov 8, 2023
3afbc0d
feat : MyLotto객체 생성 기능
1eeSJ Nov 8, 2023
3c359c1
refactor : 출력 기능 리팩토링
1eeSJ Nov 8, 2023
d75fe0b
feat : 로또 당첨 번호 입력 기능
1eeSJ Nov 8, 2023
7d11344
feat : 보너스 번호 유효성 검사 기능과 보너스 번호 입력 기능 추가
1eeSJ Nov 8, 2023
ac618d1
fix : 로또 당첨 번호 입력 시 배열 형태가 아니여서 발생하는 오류 수정
1eeSJ Nov 8, 2023
6bfd6c1
fix : 보너스 번호 입력 시 유효성 검사 실패 오류 수정
1eeSJ Nov 8, 2023
adf5eac
docs : 요구사항 완료상태 갱신
1eeSJ Nov 8, 2023
441e954
refactor : Lotto클래스를 상속받은 WinningLotto클래스를 만들어서 input부분 리팩토링
1eeSJ Nov 8, 2023
e3b2ab2
style : 세미콜론 누락 수정
1eeSJ Nov 8, 2023
759fdd3
feat : 출력 내용 상수 설정
1eeSJ Nov 8, 2023
ecf5309
feat : 로또의 결과 객체 생성
1eeSJ Nov 8, 2023
102c382
feat : 결과 출력 기능 구현
1eeSJ Nov 8, 2023
6c8ba11
style : 출력 상수 개행 부분 수정
1eeSJ Nov 8, 2023
122cd08
fix : 요구사항에 예외처리 될 경우 잘못 입력한 부분 부터 재시작 수정
1eeSJ Nov 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions __tests__/LottoTest.js → __tests__/domain/LottoTest.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Lotto from "../src/Lotto.js";
import Lotto from "../../src/domain/Lotto";

describe("로또 클래스 테스트", () => {
test("로또 번호의 개수가 6개가 넘어가면 예외가 발생한다.", () => {
Expand All @@ -7,12 +7,20 @@ describe("로또 클래스 테스트", () => {
}).toThrow("[ERROR]");
});

// TODO: 이 테스트가 통과할 수 있게 구현 코드 작성
test("로또 번호에 중복된 숫자가 있으면 예외가 발생한다.", () => {
expect(() => {
new Lotto([1, 2, 3, 4, 5, 5]);
}).toThrow("[ERROR]");
});

// 아래에 추가 테스트 작성 가능
test("로또 번호 1~45 범위를 초과하면 예외가 발생한다.", () => {
expect(() => {
new Lotto([1, 2, 3, 4, 5, 46]);
}).toThrow("[ERROR]");
});
test("로또 번호가 자연수가 아니면 예외가 발생한다.", () => {
expect(() => {
new Lotto([1, 2, 3, 4, 5, 6.5]);
}).toThrow("[ERROR]");
});
});
34 changes: 34 additions & 0 deletions __tests__/view/InputTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { MissionUtils } from "@woowacourse/mission-utils";
import InputView from "../../src/view/input-view";

const mockInput = (input) => {
MissionUtils.Console.readLineAsync = jest.fn();
MissionUtils.Console.readLineAsync.mockResolvedValue(input);
};

describe("입력값 예외처리 테스트", () => {
test("구입 금액이 1000으로 나누어 떨어지지 않을 경우 예외 처리", async () => {
//given
const input = 14500;
mockInput(input);

//when
const inputValue = new InputView();

//then
await expect(inputValue.readPurchaseAmount()).rejects.toThrow("[ERROR]");
});

test.each([
0, -1000
])("구입 금액이 0 이하 일 경우 예외 처리", async (inputs) => {
//given
mockInput(inputs);

//when
const inputValue = new InputView();

//then
await expect(inputValue.readPurchaseAmount()).rejects.toThrow("[ERROR]");
});
});
160 changes: 160 additions & 0 deletions docs/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
## 기능 요구 사항

로또 게임 기능을 구현해야 한다. 로또 게임은 아래와 같은 규칙으로 진행된다.

```
- 로또 번호의 숫자 범위는 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원이다.
- 당첨 번호와 보너스 번호를 입력받는다.
- 사용자가 구매한 로또 번호와 당첨 번호를 비교하여 당첨 내역 및 수익률을 출력하고 로또 게임을 종료한다.
- 사용자가 잘못된 값을 입력할 경우 `throw`문을 사용해 예외를 발생시킨다. 그런 다음, "[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 사이의 숫자여야 합니다.
```

#
### 구현 완료 여부

#### 입력 기능

<table>
<tr>
<th style = 'width : 100px'>완료 상태</th>
<th>테스트</th>
</tr>
<tr>
<td><input type="checkBox" checked></td>
<td>로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다.</td>
</tr>
<tr>
<td><input type="checkBox" checked></td>
<td>로또 1장의 가격은 1,000원이다. 1,000원으로 나누어 떨어지지 않는 경우 예외 처리한다.</td>
</tr>
<tr>
<td><input type="checkBox" checked></td>
<td>로또 구입 금액이 0 이하일 경우 예외 처리한다.</td>
</tr>
<tr>
<td><input type="checkBox" checked></td>
<td>로또 번호가 자연수가 아닐 경우 예외 처리한다.</td>
</tr>
<tr>
<td><input type="checkBox" checked></td>
<td>당첨 번호를 입력 받는다. 번호는 쉼표(,)를 기준으로 구분한다.</td>
</tr>
<tr>
<td><input type="checkBox" checked></td>
<td>보너스 번호를 입력받는다.</td>
</tr>
<tr>
<td><input type="checkBox" checked></td>
<td>로또 번호는 1-45사이 숫자이며 아닐 경우 예외 처리한다.</td>
</tr>
<tr>
<td><input type="checkBox" checked></td>
<td>로또 번호가 중복되는 숫자일 경우 예외 처리한다.</td>
</tr>
</table>

<br>


#### 출력 기능

<table>
<tr>
<th style = 'width : 100px'>완료 상태</th>
<th>테스트</th>
</tr>
<tr>
<td><input type="checkBox" checked></td>
<td>발행한 로또 수량 및 번호를 출력한다.</td>
</tr>
<tr>
<td><input type="checkBox" checked></td>
<td>로또 번호는 오름차순으로 정렬하여 보여준다.</td>
</tr>
<tr>
<td><input type="checkBox" disabled></td>
<td>당첨 내역을 출력한다.</td>
</tr>
<tr>
<td><input type="checkBox" disabled></td>
<td>수익률은 소수점 둘째 자리에서 반올림한다. (ex. 100.0%, 51.5%, 1,000,000.0%)</td>
</tr>
<tr>
<td><input type="checkBox" disabled></td>
<td>예외 상황 시 에러 문구를 출력해야 한다. 단, 에러 문구는 "[ERROR]"로 시작해야 한다</td>
</tr>
</table>
12 changes: 11 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import LottoController from "./controller/lotto-controller.js";

class App {
async play() {}
#lottoController;

constructor() {
this.#lottoController = new LottoController();
}

async play() {
await this.#lottoController.play();
}
}

export default App;
18 changes: 0 additions & 18 deletions src/Lotto.js

This file was deleted.

26 changes: 26 additions & 0 deletions src/constants/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export const INPUT_MESSAGE = {
inputPurchaseAmount : "구입 금액을 입력해 주세요.\n",
inputWinningNumbers : "\n당첨 번호를 입력해 주세요.\n",
inputBonusNumber : "\n보너스 번호를 입력해 주세요.\n",
};

export const OUTPUT_MESSAGE = {
printPurchaseNumber : (count) => `\n${count}개 구매했습니다.`,
printWinningStatistics : "\n당첨 통계\n---",
printFifth : (count) => `3개 일치 (5,000원) - ${count}개`,
printFourth : (count) => `4개 일치 (50,000원) - ${count}개`,
printThird : (count) => `5개 일치 (1,500,000원) - ${count}개`,
printSecond : (count) => `5개 일치, 보너스 볼 일치 (30,000,000원) - ${count}개`,
printFirst : (count) => `6개 일치 (2,000,000,000원) - ${count}개`,
printRateReturn : (rate) => `총 수익률은 ${rate}%입니다.`,
};

export const ERROR_MESSAGE = {
purchaseError : "[ERROR] 구입 금액은 1000으로 나누어 떨어지는 수 입니다.",
purchaseRangeError : "[ERROR] 구입 금액 범위는 양수인 정수입니다.",
lottoLengthError : "[ERROR] 로또 번호는 6개여야 합니다.",
lottoDuplicatedError : "[ERROR] 로또 번호는 중복될 수 없습니다.",
lottoRangeError : "[ERROR] 로또 번호의 범위는 1~45 폐구간 입니다.",
lottoTypeError : "[ERROR] 로또 번호는 자연수입니다.",
bonusLengthError : "[ERROR] 보너스 번호는 1개여야 합니다.",
};
26 changes: 26 additions & 0 deletions src/controller/lotto-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import MyLotto from "../domain/MyLotto.js";
import InputView from "../view/input-view.js";
import OutputView from "../view/output-view.js";

class LottoController {
#inputView;
#outputView;

constructor() {
this.#inputView = new InputView();
this.#outputView = new OutputView();
}

async play() {
let perchaseNumber = await this.#inputView.readPurchaseAmount();
let myLotto = new MyLotto(perchaseNumber);

this.#outputView.printMyLotto(perchaseNumber, myLotto);

let winningLotto = await this.#inputView.readWinningLotto();

this.#outputView.printResult(myLotto, winningLotto);
}
}

export default LottoController;
23 changes: 23 additions & 0 deletions src/domain/Lotto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import LottoValidator from "../validator/lotto-validator.js";

class Lotto {
#numbers;

constructor(numbers) {
this.#validate(numbers);
this.#numbers = numbers;
}

#validate(numbers) {
LottoValidator.lottoLengthValidation(numbers);
LottoValidator.lottoDuplicatedValidation(numbers);
LottoValidator.lottoRangeValidation(numbers);
LottoValidator.lottoTypeValidation(numbers);
}

getNumbers() {
return this.#numbers;
}
}

export default Lotto;
52 changes: 52 additions & 0 deletions src/domain/LottoResult.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
class LottoResult{
#myLotto;
#winningLotto;
#matching;
#matchReward;

constructor(myLotto, winningLotto) {
this.#myLotto = myLotto;
this.#winningLotto = winningLotto;
this.#matching = [0, 0, 0, 0, 0];
this.#matchReward = [5000, 50000, 1500000, 30000000, 2000000000];
this.#compareLotto();
}

#updateMatch(match, isHave) {
switch(match){
case 3 : this.#matching[0]++; break;
case 4 : this.#matching[1]++; break;
case 6 : this.#matching[4]++; break;
}
if (match == 5 && !isHave){
this.#matching[2]++;
}
if (match == 5 && isHave){
this.#matching[3]++;
}
}

#compareLotto() {
for (let lotto of this.#myLotto.getMyLottos()){
let match = lotto.getNumbers().filter(element => this.#winningLotto.getNumbers().includes(element));
this.#updateMatch(match.length, lotto.getNumbers().includes(this.#winningLotto.getBonusNumber()));
}
}

calculateRateResult() {
let purchaseAmount = this.#myLotto.getLottoSize() * 1000;
let reward = this.#matching.reduce((sum, value, index) => {
return sum + (value * this.#matchReward[index]);
}, 0);

return (reward/purchaseAmount).toFixed(1);
}

getMatching() {
return this.#matching;
}


}

export default LottoResult;
Loading