Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0365c48
docs: ver.1 구현 기능 작성 완료
sunJ0120 Jan 10, 2026
e08cb05
feat: lotto 도메인 작성 완료
sunJ0120 Jan 10, 2026
880c1a9
feat: rank 도메인 구현 완료
sunJ0120 Jan 10, 2026
905686d
feat: 등수 통계화를 위한 도메인 마련
sunJ0120 Jan 10, 2026
6253a49
feat: 우승 로또 도메인 분리
sunJ0120 Jan 10, 2026
35a568f
feat: 생성된 로또들을 저장하는 repotitory 구현
sunJ0120 Jan 10, 2026
288263f
feat: 로또를 생성하는 로직과, 로또 계산하는 로직 작성
sunJ0120 Jan 10, 2026
b96ceff
feat: 컨트롤러에서 조립 완료
sunJ0120 Jan 10, 2026
8457f65
feat: application에 연결 완료, test 동작 확인 완료
sunJ0120 Jan 10, 2026
fe00302
docs: 행운 게임 구현 목표 기능 정리 완료
sunJ0120 Jan 10, 2026
19e1817
docs: 행운 게임 기능 리드미에 정리 완료
sunJ0120 Jan 10, 2026
2d521c1
feat: 게임 진행을 위한 user 도메인 구현 완료
sunJ0120 Jan 10, 2026
4721fff
feat: 유저가 사용할 weapon domain 구현 완료
sunJ0120 Jan 10, 2026
6ed011f
feat: 유저의 승률 정보를 담은 도메인 구현
sunJ0120 Jan 10, 2026
333e7fb
feat: 유저의 승률 정보를 담은 도메인 구현 완료
sunJ0120 Jan 10, 2026
120d56c
feat: outputView에 전달하기 위한 dto 구현 완료
sunJ0120 Jan 10, 2026
bd92329
feat: 유저 정보들을 저장하기 위한 userRepository 구현 완료
sunJ0120 Jan 10, 2026
ded39c5
feat: 유저 프로필 조회 service를 만들기 위한 클래스 구현 완료
sunJ0120 Jan 10, 2026
e0b5fec
feat: 게임 진행을 위한 새로운 inputView 구현 완료
sunJ0120 Jan 10, 2026
2c12ff1
test: 도메인 객체 test 완료
sunJ0120 Jan 10, 2026
cd3530e
Merge pull request #1 from sunJ0120/feat/lucky-game
sunJ0120 Jan 10, 2026
7ab9ca5
refactor: controller에서 각 기능별로 while 문을 method에 묶어서 정리
sunJ0120 Jan 10, 2026
78d6bd8
refactor: 로또 최소, 최대 번호 매직 넘버 제거
sunJ0120 Jan 10, 2026
c9ed579
feat: 4,5등의 경우 보너스 번호가 맞지 않았을 경우도 혜택을 주기 위한 추가 필드 구성
sunJ0120 Jan 10, 2026
8a9723f
fix: 주석 정리
sunJ0120 Jan 10, 2026
d591e48
feat: 새로 만들어진 등급의 경우, 행운게임에서만 사용하기 위해 lotto 로직에서 제외하도록 구성
sunJ0120 Jan 10, 2026
bb20650
docs: 최종 구현 기능 정리 완료
sunJ0120 Jan 10, 2026
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
78 changes: 77 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,77 @@
# java-planetlotto-precourse
# 미션 : 행성 로또 - 구현 기능 정리

## 구현한 기능 목록
- [x] 사용자에게서 금액을 입력받으면, lottoMaker는 lotto를 만들어준다.
- [x] 당첨 번호와 보너스 번호를 입력하면, 내가 구매한 로또가 몇 등인지를 알 수 있다.
- [x] 총 통계를 결과로 내보낸다.

## 예외 처리
### Lotto
- [x] 번호는 숫자여야 한다.
- [x] 번호의 범위는 1~30이다.
- [x] 번호는 중복되어서는 안된다.

## ✅ 추가 기능
### 새로운 등급 마련
- 4, 5등에 해당하는데 아쉽게 보너스 번호 한끝 차이로 당첨금을 못 받을 경우를 위한 새로운 등급 마련
- 기존의 로또 당첨금 체계에 추가할 경우, 로또 기본 기능 사항을 어기는 것이 되므로, 새로운 기능인 **게임**기능에 연결하도록 구성
- 4, 5등의 점수인데 보너스 번호 하나 어긋나서 상금 수령이 어려운 경우, 상금은 0이지만, 4,5등과 같은 행운 스코어를 게임에서 적립하도록 한다.
- 시간 관계상 행운 스코어를 적립하는 로직은 만들지 못하였지만, 이를 통해 사용자들이 더 재미있게 로또 게임을 즐길 수 있을 것으로 보여집니다:)

```java
BONUS_FOURTH("보너스 4등", 3, false, 0, 500_000),
BONUS_FIFTH("보너스 5등", 2, false, 0, 5_000);

private final String displayName;
private final int countNum;
private final boolean isBonus;
private final int price;
private final int luckyScore;
```

-----

# 🍀 도전 과제 - 행운 점수 게임
> 저는 항상 무언가를 만들고, 새로 배울때 하고 싶은 것을 무작정 시도해보고는 합니다.
>
> 오픈 미션을 통해 Rust라는 언어를 배우고, 이 언어의 쓰임새를 확장해보고 싶어서 Rudis를 만들었듯이
>
> 이번에도 lotto 프로젝트를 확장하여 **lotto로 쌓은 나만의 행운 점수로 하는 게임**을 만들어보고자 합니다.

최근에 저와 제 친구들은, 카카오톡의 **검 키우기**에 빠져 있습니다.
이 게임의 매력은, 검으로 벌어들인 돈으로 여러 랜덤 무기들을 뽑고, 결투를 통해 승률을 올리는 것입니다.
단순한 게임이지만 참 재밌어서 아직까지도 하는 중이고, 제 친구들도 이미 중독되어 버렸습니다.

> 😋 만약 로또를 사는것에서 끝나지 않고, 로또뽑기를 통해 얻은 행운점수로 게임을 할 수 있다면 어떨까요?

## 구현 목표 기능
> 🌟 원래 view를 생성해서 제가 만든 기능으로 콘솔 전환할 수 있는 기능을 만들고 싶었는데, 이렇게 하니 기본 **기능 테스트가 NoSuchArgumentException으>로 깨져서**, 뷰 없이 기능들만 만들어 뒀습니다.

### 1. 내 프로필 확인 (완성, GameOutputView는 미완)
- [x] 유저가 행운점수 기능에 입장하면, 1번을 클릭하여 내 프로필을 볼 수 있습니다.
- [x] 각 유저들은 자신만의 행운 점수를 가지고 있습니다. (행운 점수는 로또 당첨을 통해 크게 얻을 수도 있고, 승률을 올리면 얻을 수 있습니다.)
- [x] 승률은 ENUM으로 관리하여 각 승률을 통해 얻을 수 있는 행운 점수를 내부에 포함하도록 합니다.

### 2. 랭킹 확인 (미완)
- [ ] 플레이 유저들의 승률 순위를 볼 수 있습니다
- [ ] 사용자들은 자신의 순위를 보고, 더 분발해서 게임을 즐길 수 있습니다.

### 3. 내 무기 강화하기 (미완)
- [ ] 💥 아마 시간관계상 여기까지 못 만들 것 같습니다...
- [ ] 유저들은 각자 자신의 무기를 가집니다.
- [ ] 무기의 레벨이 오를수록 공격력이 올라 승리 확률이 커집니다.
- [ ] 무기는 레벨이 오를수록 파괴 확률도 올라갑니다.

## 신경쓴 부분
### enum 도메인 test 로직 작성
- 현재 승,무,패를 관리하는 WinRate에는 승리, 패배 로직이 있습니다.
- 이 로직의 경우, 각각 유저의 럭키 스코어를 수정해야 함과 동시에 WinRecord의 승,무,패 count도 수정해야 합니다.
- 정합성이 유지되어야 하는 로직이므로, test code를 작성하여 검증하였습니다.

### DTO 마련
- 유저의 프로필을 조회하는 기능을 만들기 위해, 유저의 여러 정보와 무기의 정보, 승패의 정보를 가져와야 했습니다.
- 해당 정보들을 outputView에서 더 쉽게 화면에 출력할 수 있도록 하기 위해 dto를 마련했습니다.

### Service 분기
- 원래 view 연결이 가능했다면, 입력받는 번호별로 기능을 분기해서 잡을 계획이었으나, view 연결이 불가능한 상황이라 임시 view를 만들어서 각각 서비스별로 분기하도록 해두었습니다.
- 시간 관계상 목표했던 두 번째 기능인 랭킹 확인 기능은 만들지 못하였으나, 첫 번째 기능인 프로필 확인 기능을 GameProfileService로 따로 분기하여 구성했습니다.
4 changes: 4 additions & 0 deletions src/main/java/planetlotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package planetlotto;

import planetlotto.controller.MainController;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
MainController controller = new MainController();
controller.run();
}
}
25 changes: 25 additions & 0 deletions src/main/java/planetlotto/controller/GameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package planetlotto.controller;

import planetlotto.repository.UserRepository;
import planetlotto.service.GameProfileService;
import planetlotto.view.GameInputView;

public class GameController {
private final GameInputView inputView;
private final GameProfileService gameProfileService;

public GameController() {
this.inputView = new GameInputView();
UserRepository userRepository = new UserRepository();
this.gameProfileService = new GameProfileService(userRepository);
}

public void run(){
// 원래 시나리오 대로라면, 1을 누르면 연결된다.
String input = inputView.chooseMenu();
if(input.equals("1")){
String nickName = inputView.inputNick();
gameProfileService.getUserProfile(nickName);
}
}
}
80 changes: 80 additions & 0 deletions src/main/java/planetlotto/controller/LottoController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package planetlotto.controller;

import planetlotto.domain.Lotto;
import planetlotto.domain.Stats;
import planetlotto.domain.WinningLotto;
import planetlotto.repository.LottoRepository;
import planetlotto.service.LottoService;
import planetlotto.view.InputView;
import planetlotto.view.OutputView;

import java.util.List;
import java.util.Map;

public class LottoController {
private final InputView inputView;
private final OutputView outputView;
private final LottoService lottoService;

public LottoController() {
this.inputView = new InputView();
this.outputView = new OutputView();

LottoRepository lottoRepository = new LottoRepository();
this.lottoService = new LottoService(lottoRepository);
}

public void run() {
int amount = getAmount();
generateLottos(amount);

WinningLotto winningLotto = createWinningLotto();

Stats stats = new Stats();
Map<Integer, Integer> rankStat = lottoService.calculateRank(winningLotto, stats);
outputView.printResult(rankStat);
}

private int getAmount() {
int amount;
while (true) {
try {
amount = inputView.askAmount();
break;
} catch (IllegalArgumentException e) {
System.out.println("[ERROR] " + e.getMessage());
}
}
return amount;
}

private WinningLotto createWinningLotto() {
WinningLotto winningLotto;

while (true) {
try {
List<Integer> nums = inputView.askWinningLotto();
int bonus = inputView.askBonusNumber();
winningLotto = new WinningLotto(new Lotto(nums), bonus);
break;
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}

return winningLotto;
}

private void generateLottos(int amount) {
while (true) {
try {
List<List<Integer>> lottos = lottoService.generateLotto(amount);
outputView.printPurchasedLottos(lottos);

break;
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}
}
24 changes: 24 additions & 0 deletions src/main/java/planetlotto/controller/MainController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package planetlotto.controller;
/*
원래 로또가 끝나고 바로 연결하는 로직을 만들고 싶었으나, 이렇게 하면 기능 테스트에서 에러가 나서,, 주석 처리 해두었습니다.
*/
public class MainController {
private LottoController lottoController;
private GameController gameController;

public MainController() {
lottoController = new LottoController();
gameController = new GameController();
}

public void run() {
lottoController.run();

// String input = gampInputView.inputEnterButton();
// if ("종료".equals(input)) {
// return;
// }
// 여기서 새로 붙인 기능으로 기능변환
// gameController.run();
}
}
58 changes: 58 additions & 0 deletions src/main/java/planetlotto/domain/Lotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package planetlotto.domain;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Lotto {
private static final int MINIMUM_NUMBER_OF_LOTTO = 1;
private static final int MAXIMUM_NUMBER_OF_LOTTO = 30;

private final List<Integer> lotto;

public Lotto(List<Integer> nums) {
validate(nums);
this.lotto = nums;
}

public List<Integer> getLotto() {
return lotto;
}

private void validate(List<Integer> nums) {
validateRange(nums);
validateDuplicate(nums);
}

private void validateRange(List<Integer> nums) {
for (Integer num : nums) {
if (num < MINIMUM_NUMBER_OF_LOTTO || num > MAXIMUM_NUMBER_OF_LOTTO) {
throw new IllegalArgumentException("[ERROR] 로또 번호는 1과 30 사이어야 합니다.");
}
}
}

private void validateDuplicate(List<Integer> nums) {
Set<Integer> numSet = new HashSet<>();
for (Integer num : nums) {
numSet.add(num);
}

if (numSet.size() != nums.size()) {
throw new IllegalArgumentException("[ERROR] 로또 번호는 중복이 없어야 합니다.");
}
}

public int countNum(List<Integer> compareNumber) {
int count = 0;
for (Integer compare : compareNumber) {
if (this.lotto.contains(compare))
count++;
}
return count;
}

public boolean contains(int number) {
return this.lotto.contains(number);
}
}
89 changes: 89 additions & 0 deletions src/main/java/planetlotto/domain/Rank.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package planetlotto.domain;

public enum Rank {
NONE("낙첨", 0, false, 0, 0),
FIRST("1등", 5, false, 100_000_000, 100_000_000),
SECOND("2등", 4, true, 10_000_000, 10_000_000),
THIRD("3등", 4, false, 1_500_000, 1_500_000),
FOURTH("4등", 3, true, 500_000, 500_000),
FIFTH("5첨", 2, true, 5_000, 5_000),
BONUS_FOURTH("보너스 4등", 3, false, 0, 500_000),
BONUS_FIFTH("보너스 5등", 2, false, 0, 5_000);

private final String displayName;
private final int countNum;
private final boolean isBonus;
private final int price;
private final int luckyScore;

Rank(String displayName, int countNum, boolean isBonus, int price, int luckyScore) {
this.displayName = displayName;
this.countNum = countNum;
this.isBonus = isBonus;
this.price = price;
this.luckyScore = luckyScore;
}

public String getDisplayName() {
return displayName;
}

public int getCountNum() {
return countNum;
}

public boolean isBonus() {
return isBonus;
}

public int getPrice() {
return price;
}

public int getLuckyScore() {
return luckyScore;
}

public static boolean isBonusRank(Rank rank) {
if (rank == Rank.BONUS_FOURTH || rank == Rank.BONUS_FIFTH) {
return true;
}
return false;
}

public static Rank getBonusRank(int countNum, boolean isBonus) {
if (countNum == 3 && !isBonus) { // 아쉽게 보너스 번호 하나 안 맞은 당신! 럭키 스코어는 그대로
return BONUS_FOURTH;
}

if (countNum == 2 && !isBonus) { // 아쉽게 보너스 번호 하나 안 맞은 당신! 럭키 스코어는 그대로
return BONUS_FIFTH;
}

return NONE;
}

public static Rank calculateRank(int countNum, boolean isBonus) {
if (countNum == 5 && !isBonus) {
return FIRST;
}

if (countNum == 4 && isBonus) {
return SECOND;
}

if (countNum == 4 && !isBonus) {
return THIRD;
}

if (countNum == 3 && isBonus) {
return FOURTH;
}

if (countNum == 2 && isBonus) {
return FIFTH;
}

return getBonusRank(countNum, isBonus);
}
}
25 changes: 25 additions & 0 deletions src/main/java/planetlotto/domain/Stats.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package planetlotto.domain;

import java.util.HashMap;
import java.util.Map;

public class Stats {
private Map<Integer, Integer> rank; // 순위 인덱스와 카운트

public Stats() {
this.rank = new HashMap<>();
}

public Map<Integer, Integer> getRank() {
return rank;
}

public void addCount(Rank rank) {
int rankInd = rank.ordinal();

int cnt = this.rank.getOrDefault(rankInd, 0);
cnt++;

this.rank.put(rankInd, cnt);
}
}
Loading