Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
# javascript-lotto-precourse
# 로또
## 기능 요구 사항
- [X] 로또 구입 금액 입력 받기
- [X] 입력 구입 금액 유효성 검사하기
- [X] 1 ~ 45 숫자중 6개 중복없이 선택하기
- [X] 당첨 번호, 보너스 번호 입력받기
- [X] 발행한 로또 수량 및 번호 출력하기
- [X] 사용자가 구입한 로또번호, 당첨번호 비교하기
- [X] 당첨 내역 및 수익률 계산하기
- [X] 당첨 내역 및 수익률 출력하기
- [X] 잘못된 값 입력에 Error발생시키기

## 프로그래밍 요구 사항
- [X] 함수의 길이가 15라인을 넘어가지 않도록 구현한다.
- [X] else를 지양한다.
- [ ] 구현한 기능에 대한 단위 테스트를 작성한다.
- [X] Lotto 클래스에 numbers 이외의 필드를 추가할 수 없다.
- [X] numbers의 접근 제어자인 #은 변경할수 없다.
14 changes: 12 additions & 2 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import LottoManager from "./LottoManager.js";

class App {
async run() {}
async run() {
const lottoManager = new LottoManager();
await lottoManager.getPurchaseAmout();
lottoManager.generateLottos();
await lottoManager.getWinNumbers();
await lottoManager.getBonusNumber();
lottoManager.getGameResult();
lottoManager.printGameResult();
}
}

export default App;
export default App;
12 changes: 11 additions & 1 deletion src/Lotto.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
import { Console } from "@woowacourse/mission-utils";

class Lotto {
#numbers;

constructor(numbers) {
this.#validate(numbers);
this.#numbers = numbers;
Console.print(`[${this.#numbers.join(", ")}]`);
// 구매한 로또 번호 출력은 누구 책임인가? 이게 잘못됐을지도? 그러면 매니저에서 받아서 프린트
}

#validate(numbers) {
if (numbers.length !== 6) {
throw new Error("[ERROR] 로또 번호는 6개여야 합니다.");
}
const numberSet = new Set(numbers);
if (numberSet.size !== numbers.length) {
throw new Error("[ERROR] 로또 번호는 중복될 수 없습니다.");
}
}

// TODO: 추가 기능 구현
getNumbers() {
return this.#numbers;
}
}

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

class LottoManager {
static winningPriceMap = {
"1등": 2000000000,
"2등": 30000000,
"3등": 1500000,
"4등": 50000,
"5등": 5000
}
static result = {
"1등": 0,
"2등": 0,
"3등": 0,
"4등": 0,
"5등": 0,
"winningPrice": 0,
}

constructor() {
this.lottos = [];
this.spentMoney = 0;
this.winNumbers = null;
this.bonusNumber = null;
this.result = { ...LottoManager.result };
this.winningRate = 0
}

// 깊이가 좀 깊은가?
// 구입 금액 입력 받기
async getPurchaseAmout() {
while (true) {
const input = await Console.readLineAsync("구입금액을 입력해 주세요.\n")
try {
this.validateSpentMoney(input);
this.spentMoney = Number(input);
break;
} catch (e) {
Console.print(e.message);
continue;
}
}
}

// 구입 금액 유효성 검사
validateSpentMoney(price) {
const parsedPrice = Number(price);
if (!Number.isInteger(parsedPrice) || parsedPrice % 1000 !== 0 || parsedPrice <= 0) throw new Error("[ERROR] 구입 금액은 1000원 단위의 숫자로 입력해 주세요.");
}

// 로또 생성
generateLottos() {
const printNums = this.spentMoney / 1000;
Console.print(`${printNums}개를 구매했습니다.`);

for (let i = 0; i < printNums; i++) {
const combination = MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6).sort((a, b) => a - b);
this.lottos.push(new Lotto(combination));
}
}
// 당첨 번호 입력 받기
async getWinNumbers() {
while (true) {
const input = await Console.readLineAsync("당첨 번호를 입력해 주세요.\n")
try {
this.validateWinNumbers(input);
this.winNumbers = input.split(",").map(Number);
break;
} catch (e) {
Console.print(e.message);
continue;
}
}
}

// 당첨 번호 유효성 검사
validateWinNumbers(numbers) {
const numTokens = numbers.split(",").map(Number);
const isLengthValid = numTokens.length === 6;
if (!isLengthValid) throw new Error("[ERROR] 당첨 번호는 6개여야 합니다.");

const isAllinRange = numTokens.every(num => Number.isInteger(num) && num >= 1 && num <= 45);
if (!isAllinRange) throw new Error("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다.");

const isNotDuplicated = new Set(numTokens).size === numTokens.length;
if (!isNotDuplicated) throw new Error("[ERROR] 당첨 번호는 중복될 수 없습니다.");
}

// 보너스 번호 입력 받기
async getBonusNumber() {
while (true) {
const input = await Console.readLineAsync("보너스 번호를 입력해 주세요.\n")
try {
this.validateBonusNumber(input);
this.bonusNumber = Number(input);
break;
} catch (e) {
Console.print(e.message);
continue;
}
}
}

// 보너스 번호 유효성 검사
validateBonusNumber(number) {
const num = Number(number);

if (!Number.isInteger(num)) throw new Error("[ERROR] 보너스 번호는 정수여야 합니다.");
if (num < 1 || num > 45) throw new Error("[ERROR] 보너스 번호는 1~45 사이여야 합니다.");
if (this.winNumbers.includes(num)) throw new Error("[ERROR] 보너스 번호는 기존 당첨 번호와 중복될 수 없습니다.");
}

// 게임 결과 계산
getGameResult() {
// 최종적으로 몇게임이 몇등인지 알아야함
for(let i = 0; i < this.lottos.length; i++) {
const curLotto = this.lottos[i];
this.getLottoResult(curLotto); // 한 로또의 결과 계산
}
this.getPriceResult();
}

// 한 로또의 결과 계산
getLottoResult(lotto) {
const lottoNumbers = lotto.getNumbers();
let matchCount = 0;
let isBonusMatched = false;

for (let number of lottoNumbers) {
if (this.winNumbers.includes(number)) matchCount++;
if (number === this.bonusNumber) isBonusMatched = true;
}
this.updateResult(matchCount, isBonusMatched);
}

// 결과 업데이트
updateResult(matchCount, isBonusMatched) {
const rankMap = {
6: "1등",
5: isBonusMatched ? "2등" : "3등",
4: "4등",
3: "5등"
};

const rank = rankMap[matchCount];
if (!rank) return; // 낙첨

this.result[rank]++;
}


// 상금 결과 계산
getPriceResult() {
const entries = Object.entries(this.result); // [ ["1등", 0], ["2등", 2], ...]
const totalPrice = entries.reduce((acc, [key, value]) => {
if (key === "winningPrice") return acc;
acc += LottoManager.winningPriceMap[key] * value;
return acc;
}, 0)

this.result.winningPrice = Number(totalPrice);
this.winningRate = (totalPrice / this.spentMoney) * 100;
}

// 게임 결과 출력
printGameResult() {
Console.print("당첨 통계");
Console.print("---");
Console.print(`3개 일치 (5,000원) - ${this.result["5등"]}개`);
Console.print(`4개 일치 (50,000원) - ${this.result["4등"]}개`);
Console.print(`5개 일치 (1,500,000원) - ${this.result["3등"]}개`);
Console.print(`5개 일치, 보너스 볼 일치 (30,000,000원) - ${this.result["2등"]}개`);
Console.print(`6개 일치 (2,000,000,000원) - ${this.result["1등"]}개`);
Console.print(`총 수익률은 ${Number(this.winningRate.toFixed(2))}%입니다.`);
}
}
export default LottoManager;