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
33 changes: 32 additions & 1 deletion __tests__/LottoTest.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import App from '../src/App.js';
import Lotto from "../src/Lotto.js";

describe("로또 클래스 테스트", () => {
Expand All @@ -14,5 +15,35 @@ describe("로또 클래스 테스트", () => {
}).toThrow("[ERROR]");
});

// 아래에 추가 테스트 작성 가능
test("로또 번호에 숫자가 아닌 값이 있으면 예외가 발생한다.", () => {
expect(() => {
new Lotto([1, 2, 3, 4, 5, 'a']);
}).toThrow("[ERROR]");
});

test("로또 번호에 1~45 이외의 숫자가 있으면 예외가 발생한다..", () => {
expect(() => {
new Lotto([1, 2, 3, 4, 5, 50]);
}).toThrow("[ERROR]");
})

test("당첨 통계 테스트1", async () => {
const lotto = new Lotto([1, 2, 3, 4, 5, 6]);

expect(
lotto.stats(
[
[7, 8, 9, 10, 11, 12], // 0
[6, 7, 8, 9, 10 , 11, 12], // 1
[5, 6, 7, 8, 9, 10], // 2
[4, 5, 6, 7, 8, 9], // 3
[3, 4, 5, 6, 7, 8], // 4
[2, 3, 4, 5, 6, 7], // 5 + bonus
[2, 3, 4, 5, 6, 8], // 5
[1, 2, 3, 4, 5, 6], // 6
],
7
)
).toEqual([1, 1, 1, 1, 1]);
})
});
36 changes: 36 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# 기능 목록 정리

## 입력
### 로또 구입 금액
- [x] 로또 구입 금액을 입력받는다.
- [x] (예외) 숫자가 아닌 경우
- [x] (예외) 1,000 단위로 나누어 떨어지지 않는 경우
- [x] (예외) 0원인 경우
- [x] 잘못된 값을 입력한 경우, 에러 메시지를 출력하고 해당 부분부터 입력을 다시 받는다.

### 당첨 번호
- [x] 쉼표(,)를 기준으로 당첨 번호를 입력받는다.
- [x] (예외) 숫자가 아닌 경우
- [x] (예외) 각 번호의 숫자 범위가 1~45에 포함되지 않는 경우
- [x] (예외) 번호가 중복되는 경우
- [x] (예외) 번호가 6개가 아닌 경우

### 보너스 번호
- [x] 보너스 번호를 입력받는다
- [x] (예외) 숫자가 아닌 경우
- [x] (예외) 숫자 범위가 1~45에 포함되지 않는 경우
- [x] (예외) 당첨 번호와 중복되는 경우


## 출력
- [x] 발행한 로또 수량 및 번호를 오름차순으로 출력한다.
- [x] 당첨 내역을 출력한다.
- [ ] 수익률은 소수점 둘째 자리에서 반올림한다.

## 진행
- [x] 구입 금액 입력
- [x] 로또 구입 금액에 해당하는 만큼 로또 발행
- [x] 당첨 번호 입력
- [x] 보너스 번호 입력
- [x] 사용자가 구매한 로또 번호와 당첨 번호를 비교
- [x] 당첨 내역 및 수익률 출력
87 changes: 86 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,90 @@
import { MissionUtils } from "@woowacourse/mission-utils";
import Lotto from './Lotto.js';

class App {
async play() {}

printResult = (result) => {
MissionUtils.Console.print('당첨 통계');
MissionUtils.Console.print('---');

MissionUtils.Console.print(`3개 일치 (5,000원) - ${result[0]}개`);
MissionUtils.Console.print(`4개 일치 (50,000원) - ${result[1]}개`);
MissionUtils.Console.print(`5개 일치 (1,500,000원) - ${result[2]}개`);
MissionUtils.Console.print(`5개 일치, 보너스 볼 일치 (30,000,000원) - ${result[3]}개`);
MissionUtils.Console.print(`6개 일치 (2,000,000,000원) - ${result[4]}개`);
}

calculateLottoCount = async () => {
while(true) {
try {
const money = await MissionUtils.Console.readLineAsync('구입금액을 입력해 주세요.\n');

if (isNaN(money)) throw new Error("[ERROR] 구입금액은 숫자여야 합니다.");
if (parseInt(money / 1000) !== money / 1000) throw new Error("[ERROR] 구입금액은 1000원 단위여야 합니다.");
if (money == 0) throw new Error("[ERROR] 구입금액은 0원 이상이어야 합니다.");

const count = money / 1000;
MissionUtils.Console.print(`${count}개를 구매했습니다.`);
return count;
} catch(error) {
MissionUtils.Console.print(error.message);
}
}
}

inputBonus = async (win) => {
while(true) {
try {
const bonus = await MissionUtils.Console.readLineAsync('보너스 번호를 입력해 주세요.\n');
if (isNaN(parseInt(bonus))) throw new Error("[ERROR] 보너스 번호는 숫자여야 합니다.");
if (1 > bonus || bonus > 45) throw new Error("[ERROR] 보너스 번호는 1~45 사이여야 합니다.");
if (win.includes(bonus)) throw new Error("[ERROR] 보너스 번호는 당첨 번호와 중복될 수 없습니다.");
return bonus;
} catch(error) {
MissionUtils.Console.print(error.message);
}
}
}

printLottoNumbers = (lottoList) => {
lottoList.forEach((lottoNumbers) => {
MissionUtils.Console.print(`[${lottoNumbers.join(', ')}]`);
});
}

calculateTotalPrize = (stats) => {
let total = 0;
total += stats[0] * 5000;
total += stats[1] * 50000;
total += stats[2] * 1500000;
total += stats[3] * 30000000;
total += stats[4] * 2000000000;
return total;
}
earningsRate = (stats, count) => {
const totalPrize = this.calculateTotalPrize(stats);
return (totalPrize / (count * 1000) * 100).toFixed(1);
}

createLotto = (count) => {
const lottoList = [];
while(count--) {
lottoList.push(MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6).sort((a, b) => a - b));
}
return lottoList;
}

async play() {
let count = await this.calculateLottoCount();
const lottoList = this.createLotto(count);
this.printLottoNumbers(lottoList);
const win = await MissionUtils.Console.readLineAsync('당첨 번호를 입력해 주세요.\n');
const lotto = new Lotto(win.split(',').map(v=>Number(v)));
const bonus = await this.inputBonus(win);
const result = lotto.stats(lottoList, bonus);
this.printResult(result);
MissionUtils.Console.print(`총 수익률은 ${this.earningsRate(result, count)}%입니다.`);
}
}

export default App;
22 changes: 21 additions & 1 deletion src/Lotto.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,32 @@ class Lotto {
}

#validate(numbers) {
[...numbers].map((v, idx) => {
if (isNaN(parseInt(v))) throw new Error("[ERROR] 로또 번호는 숫자여야 합니다.");
if (1 > v || v > 45) throw new Error("[ERROR] 로또 번호는 1~45 사이여야 합니다.");
if (numbers.indexOf(v) !== idx) throw new Error("[ERROR] 로또 번호는 중복될 수 없습니다");
})
if (numbers.length !== 6) {
throw new Error("[ERROR] 로또 번호는 6개여야 합니다.");
}
}

// TODO: 추가 기능 구현
stats(lottoList, bonus) {
const result = [0, 0, 0, 0, 0];
lottoList.forEach((lotto) => {
switch(new Set([...lotto, ...this.#numbers]).size) {
case(9): result[0]++; break;
case(8): result[1]++; break;
case(7): {
if (lotto.includes(bonus)) result[3]++;
else result[2]++;
break;
}
case(6): result[4]++; break;
}
})
return result;
}
}

export default Lotto;