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

[로또] 김정수 미션 제출합니다. #631

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
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
15 changes: 15 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"extends": "airbnb-base",
"parserOptions": {
"ecmaVersion": 13
},
"rules": {
"import/extensions": [
"error",
"always",
{
"js": "always"
}
]
}
}
74 changes: 74 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
## 흐름파악

![Alt text](image.png)

## 기능구현 목록

### 유저스토리

1. 유저는 구입 금액을 입력할 수 있다.

- 1000원 단위로 받는다 (여기서 소수점도 걸림)
- 숫자만 입력해야된다
- 양의 정수만 입력해야된다

2. 유저는 당첨 번호를 입력할 수 있다.

- 각 1~45 범위의 숫자를 입력해야된다.
- 쉼표(,)기준으로 구분한다.
- 6개를 입력해야 한다.
- 중복되지 않아야 한다.

3. 유저는 보너스 번호를 입력할 수 있다.

- 1~45 범위의 숫자 한개만 입력할 수 있다.

4. 유저는 구입한 금액만큼 발행한 로또의 수량과 번호를 볼 수있다.

- 구입한 금액 만큼 로또를 생성해야 한다.
- 오름차순이어야 한다.
- 갯수를 보여줘야된다.
- 배열 형태로 보여준다.

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원

- 수익률은 소수점 둘째자리에서 반올림 한다.

6. 유저는 예외 상황시 에러문구를 힌트로 받을 수 있다.

- 에러 문구는 "[ERROR]"로 시작해야 한다

## 요구사항

indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.
예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다.

함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.
Jest를 이용하여 본인이 정리한 기능 목록이 정상 동작함을 테스트 코드로 확인한다.

함수(또는 메서드)의 길이가 15라인을 넘어가지 않도록 구현한다.

함수(또는 메서드)가 한 가지 일만 잘 하도록 구현한다.

도메인 로직에 단위 테스트를 구현해야 한다. 단, UI(Console.readLineAsync, Console.print) 로직에 대한 단위 테스트는 제외한다.

핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다.

라이브러리 사용해서 랜덤 및 출력 구현

## 제약사항

Lotto 클래스
제공된 Lotto 클래스를 활용해 구현해야 한다.
numbers의 # prefix를 변경할 수 없다.
Lotto에 필드를 추가할 수 없다.
Binary file added docs/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/Aligner/Aligner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const Aligner = class {
static alignAscendingArr(arr) {
arr.sort((a, b) => a - b);
}
};

export default Aligner;
47 changes: 46 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,50 @@
import { MissionUtils } from '@woowacourse/mission-utils';
import Converter from './Converter/Converter.js';
import Lotto from './Lotto/Lotto.js';
import LottoIntergrator from './Lotto/LottoIntergrator.js';
import Receiver from './Receiver/Receiver.js';

import Validator from './Validator/Validator.js';
import Printer from './Printer/Printer.js';

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

#userSetLottoNums;

#bonusNum;

#lottos = new LottoIntergrator();

async play() {
this.#userMoney = await Receiver.receiveMoney();

Validator.checkPurchaseAmount(this.#userMoney);

this.#userMoney /= 1000;

this.generateLottos();

Printer.printPublishInfo(this.#userMoney, this.#lottos.allLottoInfo());

this.#userSetLottoNums = Converter.stringToArray(await Receiver.receiveLottomNums());

Validator.checkLottoNums(this.#userSetLottoNums);

this.#bonusNum = await Receiver.receiveBonusNum();

Validator.checkBonusNum(this.#bonusNum);

console.log(this.#lottos.saveBoard(this.#userSetLottoNums, this.#bonusNum));
}

generateLottos() {
for (let i = 0; i < this.#userMoney; i += 1) {
const lotto = MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6);

this.#lottos.pushLotto(new Lotto(lotto).getLotto());
}
}
}

export default App;
27 changes: 27 additions & 0 deletions src/Constant/Constant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const ERROR_MEESAGE = Object.freeze({
NOT_VALID_MONEY: '1000원 단위로 입력해 주세요',
NOT_MINUS_NUMBER: '양의 정수를 입력해주세요',
ISNAN_ERROR_MSG: '숫자만 입력해주세요',
RANGE_ERROR_MSG: '1에서 45사이의 수만 입력해주세요',
NOT_VALID_SIZE: '6개의 로또를 입력해주세요',
NOT_DUPLICATE: '중복되지 않은 수를 입력해주세요',
});

const WINNING_AMOUNT_UNITS = Object.freeze({
FIRST_PLACE: 2000000000,
SECOND_PLACE: 30000000,
THIRD_PLACE: 1500000,
FOURTH_PLACE: 50000,
FIFTH_PLACE: 5000,
});

const MONEY_UNIT = 1000;

const RANGE_START_NUM = 1;
const RANGE_END_NUM = 45;
const LOTTIO_SIZE = 6;

// prettier-ignore
export {
ERROR_MEESAGE, MONEY_UNIT, RANGE_START_NUM, RANGE_END_NUM, LOTTIO_SIZE, WINNING_AMOUNT_UNITS,
};
11 changes: 11 additions & 0 deletions src/Converter/Converter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const Converter = class {
static stringToArray(string) {
return string.split(',');
}

static arrToSetStructure(arr) {
return new Set(arr);
}
};

export default Converter;
7 changes: 7 additions & 0 deletions src/CustomError/CustonError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const CustomError = class {
constructor(errorMsg) {
throw new Error(`[ERROR] ${errorMsg}`);
}
};

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

This file was deleted.

22 changes: 22 additions & 0 deletions src/Lotto/Lotto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Aligner from '../Aligner/Aligner.js';
import Validator from '../Validator/Validator.js';

class Lotto {
#numbers;

constructor(numbers) {
Validator.checkLottoNums(numbers);
this.#numbers = numbers;
this.#align(this.#numbers);
}

#align() {
Aligner.alignAscendingArr(this.#numbers);
}

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

export default Lotto;
22 changes: 22 additions & 0 deletions src/Lotto/LottoAnalyzer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const LottoAnalyzer = class {
static getAnalysis(winningNumbers, bonusNumber, lottoNums) {
const matchCount = lottoNums.filter((num) => winningNumbers.includes(num)).length;
const isBonusMatched = lottoNums.includes(bonusNumber);
return LottoAnalyzer.getRank(matchCount, isBonusMatched);
}

static getRank(matchCount, isBonusMatched) {
if (matchCount < 3) return 0;
if (matchCount === 3) return 5;
if (matchCount === 4) return 4;
if (matchCount === 5) return isBonusMatched ? 2 : 3;
if (matchCount === 6) return 1;
return 0;
}

static toFixedNumber(number) {
return +number.toFixed(1);
}
};

export default LottoAnalyzer;
52 changes: 52 additions & 0 deletions src/Lotto/LottoIntergrator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { WINNING_AMOUNT_UNITS } from '../Constant/Constant.js';

import LottoAnalyzer from './LottoAnalyzer.js';

const LottoIntergrator = class {
#allLottos = [];

#roi;

#board = {
1: 0,
2: 0,
3: 0,
4: 0,
5: 0,
0: 0,
};

pushLotto(lotto) {
this.#allLottos.push(lotto);
}

allLottoInfo() {
return this.#allLottos;
}

saveBoard(winNum, bonusNum) {
this.#allLottos.forEach((lottos) => {
this.#board[LottoAnalyzer.getAnalysis(winNum, bonusNum, lottos)] += 1;
});

return this.#board;
}

static getstatistics(totalWinning, purchaseAmount) {
return LottoAnalyzer.toFixedNumber((totalWinning / purchaseAmount) * 0.1);
}

static totalWinningAmount(winningArray) {
let totalAmount = 0;
winningArray.forEach((winning) => {
if (winning === 1) totalAmount += WINNING_AMOUNT_UNITS.FIRST_PLACE;
if (winning === 2) totalAmount += WINNING_AMOUNT_UNITS.SECOND_PLACEs;
if (winning === 3) totalAmount += WINNING_AMOUNT_UNITS.THIRD_PLACE;
if (winning === 4) totalAmount += WINNING_AMOUNT_UNITS.FOURTH_PLACE;
if (winning === 5) totalAmount += WINNING_AMOUNT_UNITS.FIFTH_PLACE;
});
return totalAmount;
}
};

export default LottoIntergrator;
11 changes: 11 additions & 0 deletions src/Measurer/SizeMeasurer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const SizeMeasurer = class {
static getArrSize(value) {
return value.length;
}

static getSetStructureSize(value) {
return value.size;
}
};

export default SizeMeasurer;
16 changes: 16 additions & 0 deletions src/Printer/Printer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { MissionUtils } from '@woowacourse/mission-utils';

export default class Printer {
static printPublishInfo(quantity, lottos) {
Printer.printPublishQuantity(quantity);
Printer.printPublishLottos(lottos);
}

static printPublishLottos(lottos) {
lottos.forEach((lotto) => MissionUtils.Console.print(`[${lotto.join(',')}]`));
}

static printPublishQuantity(quantity) {
MissionUtils.Console.print(`${quantity}개를 구매했습니다.`);
}
}
35 changes: 35 additions & 0 deletions src/Receiver/Receiver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { MissionUtils } from '@woowacourse/mission-utils';
import {
LOTTIO_SIZE,
MONEY_UNIT,
RANGE_END_NUM,
RANGE_START_NUM,
} from '../Constant/Constant.js';

const Receiver = class {
static async receiveMoney() {
const answer = await MissionUtils.Console.readLineAsync(
`구입금액을 ${MONEY_UNIT}원 단위로 입력해 주세요.`,
);

return answer;
}

static async receiveLottomNums() {
const answer = await MissionUtils.Console.readLineAsync(
`${RANGE_START_NUM}~${RANGE_END_NUM} 사이의 당첨 번호 ${LOTTIO_SIZE}개를 입력해 주세요(입력 시','로 구분해주세요).`,
);

return answer;
}

static async receiveBonusNum() {
const answer = await MissionUtils.Console.readLineAsync(
`${RANGE_START_NUM}~${RANGE_END_NUM} 사이의 보너스 숫자를 입력해주세요`,
);

return answer;
}
};

export default Receiver;
7 changes: 7 additions & 0 deletions src/Sanitizer/Sanitizer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const Sanitizer = class {
static sanitizeEmpty(value) {
value.trim();
}
};

export default Sanitizer;
Loading