Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
208c893
docs: 구현 기능 목록 추가
kim-caron Nov 3, 2024
c8972a5
docs: 구현 기능 목록 예외 처리 추가
kim-caron Nov 3, 2024
3b9a630
docs: 구현 기능 목록 예외 처리 수정
kim-caron Nov 3, 2024
6cf6511
feat: Lotto 클래스 검증 로직 추가
kim-caron Nov 4, 2024
4b152a5
feat: Lotto 클래스 당첨 번호 일치 개수를 반환하는 메서드 구현
kim-caron Nov 4, 2024
2a56258
feat: Lotto 클래스 보너스 번호 포함 여부를 판단하는 메서드 구현
kim-caron Nov 4, 2024
d311b1b
feat: Lotto 클래스 Getter 메서드 구현
kim-caron Nov 4, 2024
f97fd53
chore: MVC 패턴 적용으로 인한 폴더 구조 수정
kim-caron Nov 4, 2024
60d7a67
feat: 입력 값 처리를 위한 InputView 생성
kim-caron Nov 4, 2024
fd3e615
feat: 출력 메시지를 처리하는 OutputView 생성
kim-caron Nov 4, 2024
d98bd9e
feat: InputView 내 사용자 입력 로직 구현
kim-caron Nov 4, 2024
ac323a9
fix: 로또 구입 매수 반환을 위한 코드 추가
kim-caron Nov 4, 2024
7affd2b
feat: 구입 금액 입력에 대한 에외 처리 로직 추가
kim-caron Nov 4, 2024
71ebb83
feat: InputView 내 당첨 번호 사용자 입력 로직 구현
kim-caron Nov 4, 2024
99770c4
feat: 당첨 번호 입력에 대한 예외 처리 로직 추가
kim-caron Nov 4, 2024
dd2dd1e
chore: 코드 컨벤션을 맞추기 위한 ; 추가
kim-caron Nov 4, 2024
028542c
feat: InputView 내 보너스 번호 입력 로직 구현
kim-caron Nov 4, 2024
55da94c
feat: 보너스 번호 입력에 대한 예외 처리 로직 추가
kim-caron Nov 4, 2024
34e83b6
chore: 코드 컨벤션을 맞추기 위한 ; 추가
kim-caron Nov 4, 2024
cca5a04
feat: 구매한 로또 개수와 목록을 출력하는 로직 구현
kim-caron Nov 4, 2024
06e7d34
feat: 당첨 통계를 출력하는 로직 구현
kim-caron Nov 4, 2024
b75547a
feat: 에러 메시지를 출력하는 로직 구현
kim-caron Nov 4, 2024
a1adb22
feat: Lotto 게임의 비즈니스 로직 처리를 위한 Lottogame 파일 추가
kim-caron Nov 4, 2024
a9f388a
feat: LottoGame 생성자 메서드 추가
kim-caron Nov 4, 2024
56e3efb
feat: 티켓별 당첨 여부를 판단하여 카운트하는 메서드 구현
kim-caron Nov 4, 2024
41fddbb
feat: 당첨 금액을 기록해놓은 prizeMoney 객체를 data.js로 공통화
kim-caron Nov 4, 2024
38fab7a
chore: data.js를 service 폴더로 이동
kim-caron Nov 4, 2024
3600dad
feat: 수익률을 계산하는 메서드 구현
kim-caron Nov 4, 2024
c53ba96
feat: LottoController.js 생성
kim-caron Nov 4, 2024
6b89524
feat: LottoController 로직 구현
kim-caron Nov 4, 2024
3ac9321
fix: 메서드 이름 오타 수정
kim-caron Nov 4, 2024
1d2a01a
feat: 랜덤 번호 생성 로직 구현(오름차순 자동 정렬)
kim-caron Nov 4, 2024
7348b9c
feat: 로또 클래스 생성 시 랜덤 번호를 전달받도록 연결
kim-caron Nov 4, 2024
7cb7954
fix: 불필요한 예외 처리 삭제
kim-caron Nov 4, 2024
6de5497
feat: App.js에 컨트롤러 연결
kim-caron Nov 4, 2024
19d9f21
fix: LottoGame.js 경로 수정
kim-caron Nov 4, 2024
442c19e
fix: input 처리 방식 변경
kim-caron Nov 4, 2024
1c07c58
feat: 당첨 결과 별 금액을 출력에 반영
kim-caron Nov 4, 2024
f4419e2
feat: 당첨 결과 별 금액을 출력에 반영
kim-caron Nov 4, 2024
e06925b
feat: 에외 처리 다시 추가
kim-caron Nov 4, 2024
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
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,63 @@
# javascript-lotto-precourse

## 구현 기능 목록

### 입력

- [ ] 사용자로부터 로또 구입 금액을 입력 받는다.
- [ ] 1,000원으로 나누어 떨어지지 않으면 예외 처리
- [ ] 숫자가 아닌 경우 예외 처리
- [ ] 사용자로부터 당첨 번호를 입력 받는다.
- [ ] 1 ~ 45 범위에 존재하지 않으면 예외 처리
- [ ] 중복되는 숫자가 존재하면 예외 처리
- [ ] 숫자가 아닌 경우 예외 처리
- [ ] 공백을 포함한 경우 예외 처리
- [ ] 6개의 숫자를 입력하지 않은 경우 예외 처리
- [ ] 당첨 번호는 쉼표(,)를 기준으로 구분한다.
- [ ] 쉼표가 아닌 구분자가 존재하면 예외 처리
- [ ] 사용자로부터 보너스 번호를 입력 받는다.
- [ ] 1 ~ 45 범위에 존재하지 않으면 예외 처리
- [ ] 중복되는 숫자가 존재하면 예외 처리
- [ ] 숫자가 아닌 경우 예외 처리

### 출력

- [ ] 로또 번호를 오름차순으로 정렬한다.

- [ ] 발행한 로또 수량 및 번호를 출력한다.
```
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%입니다.
```

- [ ] 예외 상황 시 에러 문구를 출력한다. 반드시 `[ERROR]`로 시작한다.
```
[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다.
```

### 로또 추첨

- [ ] 구입 금액에 해당하는 만큼 로또를 발행한다.
- [ ] 로또 발행 시 중복되지 않는 6개의 숫자를 뽑는다.
- [ ] 추첨 시 중복되지 않는 숫자 6개와 보너스 번호 1개를 뽑는다.
7 changes: 6 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import LottoController from "./controller/LottoController.js";

class App {
async run() {}
async run() {
const controller = new LottoController();
await controller.start();
}
}

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

This file was deleted.

25 changes: 25 additions & 0 deletions src/controller/LottoController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import InputView from "../view/InputView.js";
import OutputView from "../view/OutputView.js";
import LottoGame from "../service/LottoGame.js";

class LottoController {
async start() {
try {
const purchaseAmount = await InputView.getPurchaseAmount();
const lottoGame = new LottoGame(purchaseAmount);
OutputView.printLottoTickets(lottoGame.lottoTickets);

const winningNumbers = await InputView.getWinningNumbers();
const bonusNumber = await InputView.getBonusNumber();

const ranks = lottoGame.calculateRanks(winningNumbers, bonusNumber);
const earningRate = this.calculateEarningRate(ranks, purchaseAmount);

OutputView.printResults(ranks, earningRate);
} catch (error) {
OutputView.printError(error.message);
}
}
}

export default LottoController;
39 changes: 39 additions & 0 deletions src/model/Lotto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
class Lotto {
#numbers;

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

#validate(numbers) {
if (numbers.length !== 6) {
throw new Error("[ERROR] 로또 번호는 6개여야 합니다.");
}

if (new Set(numbers).size !== numbers.length) {
throw new Error("[ERROR] 로또 번호에 중복된 숫자가 있습니다.");
}

if (numbers.some(num => num < 1 || num > 45)) {
throw new Error("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다.");
}
}

// 당첨 번호 일치 개수를 반환
getMatchCount(winningNumbers) {
return this.#numbers.filter((num) => winningNumbers.includes(num)).length;
}

// 보너스 번호 일치 여부 확인
hasBonusNumber(bonusNumber) {
return this.#numbers.includes(bonusNumber);
}

// private으로 선언된 numbers를 외부에서 확인하기 위한 Getter
get Numbers() {
return this.#numbers;
}
}

export default Lotto;
47 changes: 47 additions & 0 deletions src/service/LottoGame.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Lotto from "../model/Lotto.js";
import prizeMoney from "./data.js";
import RandomNumberGenerator from "../util/RandomNumberGenerator.js";

class LottoGame {
constructor(purchaseAmount) {
this.lottoTickets = this.generateLottoTickets(purchaseAmount);
}

generateLottoTickets(amount) {
const ticketCount = Math.floor(amount / 1000);
return Array.from({ length: ticketCount }, () => new Lotto(RandomNumberGenerator.generateLottoNumbers()));
}

calculateRanks(winningNumbers, bonusNumber) {
const ranks = {
"3개 일치": 0,
"4개 일치": 0,
"5개 일치": 0,
"5개 일치 + 보너스 볼 일치": 0,
"6개 일치": 0,
};

this.lottoTickets.forEach((ticket) => {
const matchCount = ticket.getMatchCount(winningNumbers);
const hasBonus = ticket.hasBonusNumber(bonusNumber);

if (matchCount === 3) ranks["3개 일치"]++;
if (matchCount === 4) ranks["4개 일치"]++;
if (matchCount === 5) ranks["5개 일치"]++;
if (matchCount === 5 && hasBonus) ranks["5개 일치 + 보너스 볼 일치"]++;
if (matchCount === 6) ranks["6개 일치"]++;
});

return ranks;
}

calculateEarningRate(ranks, purchaseAmount) {
const totalPrize = Object.entries(ranks).reduce(
(sum, [rank, count]) => sum + prizeMoney[rank] * count,
0
);
return ((totalPrize / purchaseAmount) * 100).toFixed(1);
}
}

export default LottoGame;
9 changes: 9 additions & 0 deletions src/service/data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const prizeMoney = {
"3개 일치": 5000,
"4개 일치": 50000,
"5개 일치": 1500000,
"5개 일치 + 보너스 볼 일치": 30000000,
"6개 일치": 2000000000,
};

export default prizeMoney;
9 changes: 9 additions & 0 deletions src/util/RandomNumberGenerator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Random } from "@woowacourse/mission-utils";

const RandomNumberGenerator = {
generateLottoNumbers() {
return Random.pickUniqueNumbersInRange(1, 45, 6).sort((a, b) => a - b);
},
};

export default RandomNumberGenerator;
38 changes: 38 additions & 0 deletions src/view/InputView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Console } from "@woowacourse/mission-utils"

const InputView = {
async getPurchaseAmount() {
const input = await Console.readLineAsync("구입 금액을 입력해주세요.\n");
const amount = parseInt(input);

if (isNaN(amount) || amount % 1000 !== 0) {
throw new Error("[ERROR] 구입 금액은 1,000원 단위여야 합니다.");
}

return amount;
},

async getWinningNumbers() {
const input = await Console.readLineAsync("당첨 번호를 입력해 주세요.\n");
const numbers = input.split(",").map(Number);

if (numbers.length !== 6 || numbers.some((num) => isNaN(num) || num < 1 || num >45)) {
throw new Error("[ERROR] 당첨 번호는 1부터 45 사이의 숫자 6개여야 합니다.");
}

return numbers;
},

async getBonusNumber() {
const input = await Console.readLineAsync("보너스 번호를 입력해 주세요.\n");
const bonus = parseInt(input);

if (isNaN(bonus) || bonus < 1 || bonus > 45) {
throw new Error("[ERROR] 보너스 번호는 1부터 45 사이의 숫자여야 합니다.");
}

return bonus;
}
}

export default InputView;
23 changes: 23 additions & 0 deletions src/view/OutputView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Console } from "@woowacourse/mission-utils"
import prizeMoney from "../service/data.js";

const OutputView = {
printLottoTickets(tickets) {
Console.print(`\n${tickets.length}개를 구매했습니다.`);
tickets.forEach((ticket) => Console.print(`[${ticket.numbers.join(", ")}]`));
},

printResults(ranks, earningRate) {
Console.print("\n당첨 통계\n---")
Object.entries(ranks).forEach(([rank, count]) => {
Console.print(`${rank} (${prizeMoney[rank].toLocaleString("ko-KR")}원) - ${count}개`)
});
Console.print(`총 수익률은 ${earningRate}% 입니다.`);
},

printError(message) {
Console.print(message);
}
}

export default OutputView;