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

[1단계 - 블랙잭 구현] 현구막(최현구) 미션 제출합니다. #147

Merged
merged 67 commits into from
Mar 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
7253882
feat : 차량에 따른 연료주입 추상클래스로 구현
Hyeon9mak Mar 2, 2021
f080ffc
feat : 차량에 따른 연료주입 인터페이스로 구현
Hyeon9mak Mar 2, 2021
8e9d573
docs: 기능 구현 목록 작성
Hyeon9mak Mar 2, 2021
048c307
docs : 기능 목록 추가
Hyeon9mak Mar 2, 2021
598aa92
docs : 테스트 구상 목록 추가
Hyeon9mak Mar 2, 2021
ad90477
chore: 연료주입 미션 패키지 통합
Hyeon9mak Mar 3, 2021
6d6d18a
feat : 숫자와 문양을 가지는 블랙잭 카드 구현
Hyeon9mak Mar 3, 2021
7761340
refactor : 카드의 필드명 중복 제거
Hyeon9mak Mar 3, 2021
6c9311a
feat : 무늬별 13장씩 총 52장의 카드를 가지는 카드덱 구현
Hyeon9mak Mar 3, 2021
be25491
refactor : 카드 덱 캐싱 제너레이터 클래스 추가
Hyeon9mak Mar 3, 2021
1cc6048
feat : 손패를 가지는 일급 컬렉션 Hand 추가
Hyeon9mak Mar 3, 2021
336fa85
feat : 카드를 주거나 받는 딜러 추가
Hyeon9mak Mar 3, 2021
14f8a2b
feat : 카드는 받는 플레이어 구현
Hyeon9mak Mar 3, 2021
ec5ea9b
feat : 손패를 반환하는 메서드 추가
Hyeon9mak Mar 3, 2021
54b595c
feat : 플레이어가 이름을 가질 수 있게 구현
Hyeon9mak Mar 3, 2021
6d5dd92
feat : 플레이어 일급 컬렉션 구현
Hyeon9mak Mar 3, 2021
3ff5f52
feat : 플레이어의 이름을 받는 입력, 출력, 초기셋팅 추가
Hyeon9mak Mar 4, 2021
410f23d
docs : 리팩토링 목록 추가
Hyeon9mak Mar 4, 2021
9d11355
refactor : 포장된 원시값을 반환하는 메서드 생성
Hyeon9mak Mar 4, 2021
730f1c8
refactor : 딜러와 플레이어를 매니저가 상태로 가지지 않도록 삭제
Hyeon9mak Mar 4, 2021
401d5cb
feat : 플레이어와 딜러의 카드 히트 여부 입출력 구현
Hyeon9mak Mar 4, 2021
109882a
feat : 딜러가 초기 카드 2장을 받는 로직 추가
Hyeon9mak Mar 4, 2021
5bc9fd7
feat : 모든 히트가 완료된 후 최종 손패와 점수를 보여주는 출력 추가
Hyeon9mak Mar 4, 2021
749ea1c
docs : 질문 리스트 추가
Hyeon9mak Mar 4, 2021
8dc738c
feat : 승리 패배 무승부 3가지를 요소로 가지는 enum 생성
Hyeon9mak Mar 4, 2021
dfa2844
feat : 플레이어와 딜러의 승패 그리고 무승부 결과를 반환하는 로직 구현
Hyeon9mak Mar 4, 2021
f6f3334
feat : 플레이어가 버스트되었을 시 히트할 수 없는 로직 구현
Hyeon9mak Mar 4, 2021
83cd6f6
refactor : 입출력 개행 수정
Hyeon9mak Mar 4, 2021
f9e301c
feat : 최대 참여 플레이어 예외처리 추가
Hyeon9mak Mar 4, 2021
7729e99
refactor : 딜러와 플레이어의 중복되는 부분을 추상 메서드로 분리
Hyeon9mak Mar 4, 2021
832ba0d
refactor : 컨트롤러 메서드 분리
Hyeon9mak Mar 4, 2021
822610f
docs : 예외처리 목록 체크 및 테스트 목록 추가
Hyeon9mak Mar 4, 2021
c9c614f
test : 게임 초기화 및 결과반환 테스트 추가
Hyeon9mak Mar 4, 2021
5bc2a30
style : 코드 포맷 맞춤
Hyeon9mak Mar 4, 2021
ceab5ad
test : 플레이어와 딜러 승패 판단 결과 테스트 추가
Hyeon9mak Mar 4, 2021
4390fd6
docs: add refactoring list in README.md
Hyeon9mak Mar 6, 2021
ae33e6e
refactor(Card): move Card caching logic to Card class
Hyeon9mak Mar 7, 2021
d5b9258
refactor(Number): rename field member, add compare ACE method
Hyeon9mak Mar 7, 2021
23c0fe7
refactor(Hand): fix more suitable stream API
Hyeon9mak Mar 7, 2021
110209b
refactor(CardDeck): generate deck from Card class
Hyeon9mak Mar 7, 2021
00b7a36
refactor(BlackjackManager): move resposibility from Dealer to CardDeck
Hyeon9mak Mar 7, 2021
f11621e
refactor(Dto): add Dto create method in Manager
Hyeon9mak Mar 7, 2021
66744d0
refactor: fix method name for score, card list
Hyeon9mak Mar 7, 2021
45913ec
refactor: move judge method from GameResult to Dealer
Hyeon9mak Mar 7, 2021
d466c3a
refactor: move the burst check logic from Player to Hand
Hyeon9mak Mar 7, 2021
6855220
refactor: add final keyword, add this keyword, fix class name
Hyeon9mak Mar 7, 2021
db5bd0c
refactor: add create Dto method in BlackjackManager
Hyeon9mak Mar 7, 2021
3845fb1
refactor: add burst check method in Player
Hyeon9mak Mar 7, 2021
15da9a6
feat: add find player by name method
Hyeon9mak Mar 7, 2021
fd465b6
docs: update refactor list in README.md
Hyeon9mak Mar 7, 2021
cd17b80
docs: add refactoring list in README.md
Hyeon9mak Mar 8, 2021
07ea032
refactor: wrap player and dealer score to Score class
Hyeon9mak Mar 9, 2021
3d8c0ef
feat: add init draw object, add Hit state
Hyeon9mak Mar 9, 2021
8110b0c
feat: add Bust state
Hyeon9mak Mar 9, 2021
a7bb0b9
feat: add Stay state
Hyeon9mak Mar 9, 2021
b2bd489
feat: add Blackjack state
Hyeon9mak Mar 9, 2021
55b789d
refactor: abstract Blackjack, Bust, Stay state to Finished
Hyeon9mak Mar 9, 2021
8cd8b02
feat: add return cards method in Finished and Hit
Hyeon9mak Mar 9, 2021
ddc902f
refactor: abstract Hit, Running, Finished, AfterInitDraw
Hyeon9mak Mar 9, 2021
b0af6a5
refactor: state interface, abstract, class refactoring
Hyeon9mak Mar 10, 2021
1a7ec15
feat: add compare score logic
Hyeon9mak Mar 10, 2021
742b87d
refactor: move hand class into state package
Hyeon9mak Mar 10, 2021
813ddad
feat: add create custom deck method for test code
Hyeon9mak Mar 10, 2021
55b0fac
refactor: refactoring for view classes
Hyeon9mak Mar 10, 2021
08fb3b1
refactor: move resposibility in Participant objects
Hyeon9mak Mar 10, 2021
dc56f69
refactor: move resposibility from Controller to Manager class
Hyeon9mak Mar 10, 2021
c5c958a
style: add enter lines, add blank
Hyeon9mak Mar 10, 2021
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
76 changes: 75 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,78 @@
블랙잭 게임 미션 저장소

## 우아한테크코스 코드리뷰
* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)
* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)

<br>

## 기능 구현 목록
- 참가자 추상 클래스 구현
- 플레이어 구현
- 손에 카드를 가지고 있다
- 카드를 받는다, 안받는다
- [x] y, n 이 아닌 다른 값 입력
- [x] 공백 또는 NULL
- 이름을 가지고 있는다
- [x] 공백 또는 NULL
- 딜러 구현
- 손에 카드를 가지고 있다
- 점수 총합이 16 이하일시 카드를 받는다
- 카드를 플레이어들에게 나눠준다
- 카드덱 구현 Deque
- 카드 구현 Card 클래스
- 카드 숫자 Enum
- 카드 무늬 Enum
-
- 블랙잭 구현
- 플레이어들의 이름을 받는다
- [x] 플레이어의 이름 개수가 7개 초과
- 점수를 계산하여 승자를 판단한다
- 점수가 21을 초과할 경우 패배시킨다

<br>

## 테스트 구상 목록
- 카드덱 뭉치
- [x] 사이즈가 52인지
- [x] 중복 카드가 입력 되었는지
- [x] 무늬별 13장씩 있는지
- [x] 무늬가 총 4개로 분류되는지
- [x] 덱에서 카드가 잘 나오는지
- 카드
- [x] 카드가 잘 생성 되는지
- 승패 테스트
- [x] 플레이어 승패 잘 판단되는지
- [x] 딜러 승패 잘 반환 되는지

<br>

## 질문 리스트
- 카드 셔플을 딜러가 아닌 카드가 해도 되는지
- 제네레이터 클래스를 만든것에 대해.
- Number Enum 클래스가 번호별 출력양식을 가진 것에 대하여.
- 만일 OutputView 쪽에서 Enum별 출력 양식을 가지면 그건 도메인 역할을 침범하는게 아닌가?

<br>

## 리팩토링 구상 목록
- `BlackjackManager`가 꼭 유틸 클래스일 필요가 있는가?
- `BlackjackManager` 가 꼭 유틸 클래스이지 말라는 법은 없지만, 꼭 유틸 클래스일 필요도 없다.
- 게다가, 대부분의 경우 유틸 클래스를 지양하라는 피드백을 받았었다.
- 대안 1. `BlackjackManager` 클래스를 제거한다.
- 미립이 `BlackjackManager` 존재의 필요성에 대해 의문을 표시해주셨다.
- "현재 컨트롤러가 `BlackjackManager`의 역할을 모두 수행하고 있는 것이 아닌가?"
- 대안 2. `BlackjackManager`가 덱을 갖도록 한다.
- 딜러가 카드를 꼭 나눠줄 필요가 없다. 객체지향은 현실세계 모방일뿐.
- 카드가 직접 딜러와 플레이어에게 나눠주는 행동을 하자.

- Dto 변경
- `BlackjackManager` 를 서비스레이어처럼 생각하고 Dto를 만드는 역할 추가

- 블랙잭 승패 비교를 GameResult Enum 클래스가 아닌 딜러가 하도록

- `InputView` 이름 파싱 조금 더 여유롭게 (정규표현식 사용)

- Controller 쪽으로 Player, Players, Dealer가 빠져나가지 않도록
- 핵심은 `BlackjackManager` 혹은 `Players` 쪽에 현재 플레이어를 기억할 방법을 찾는 것.

- 도메인(`BlackjackManager`)이 Dto 로직을 모르도록
11 changes: 11 additions & 0 deletions src/main/java/blackjack/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package blackjack;

import blackjack.controller.BlackjackController;

public class Application {

public static void main(String[] args) {
BlackjackController blackjackController = new BlackjackController();
blackjackController.play();
}
}
84 changes: 84 additions & 0 deletions src/main/java/blackjack/controller/BlackjackController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package blackjack.controller;

import blackjack.domain.BlackjackManager;
import blackjack.domain.DtoAssembler;
import blackjack.domain.participant.Dealer;
import blackjack.domain.participant.Players;
import blackjack.view.InputView;
import blackjack.view.OutputView;
import blackjack.view.dto.ParticipantDto;
import blackjack.view.dto.ResultDto;
import java.util.List;

public class BlackjackController {

public void play() {
Dealer dealer = new Dealer();
Players players = new Players(InputView.getPlayerNames());
BlackjackManager blackjackManager = new BlackjackManager(dealer, players);

initDrawCardsDealerAndAllPlayers(blackjackManager);
hitOrStayAllPlayers(blackjackManager);
hitDealerUntilOverLimitScore(blackjackManager);
printCardsWithScoreOfDealerAndAllPlayers(blackjackManager);
printBlackjackResult(blackjackManager);
}

private void initDrawCardsDealerAndAllPlayers(BlackjackManager blackjackManager) {
blackjackManager.initDrawCards();
ParticipantDto dealerInitStatus = DtoAssembler
.createDealerInitStatusDto(blackjackManager.getDealer());
List<ParticipantDto> playerStatuses = DtoAssembler
.createPlayerStatusDtos(blackjackManager.getPlayers());
OutputView.printInitStatuses(dealerInitStatus, playerStatuses);
}

private void hitOrStayAllPlayers(BlackjackManager blackjackManager) {
while (!blackjackManager.isFinishedAllPlayers()) {
hitOrStayCurrentPlayer(blackjackManager);
blackjackManager.passTurnToNextPlayer();
}
blackjackManager.passTurnToNextPlayer();
}

private void hitOrStayCurrentPlayer(BlackjackManager blackjackManager) {
if (!blackjackManager.isFinishedCurrentPlayer()) {
blackjackManager.hitOrStayCurrentPlayer(
InputView.getHitOrStay(blackjackManager.getCurrentPlayerName()));
OutputView.printPlayerStatus(
DtoAssembler.createPlayerStatusDto(blackjackManager.getCurrentPlayer()));
}
}

private void hitDealerUntilOverLimitScore(BlackjackManager blackjackManager) {
OutputView.printNewLine();
while (!blackjackManager.isFinishedDealer()) {
hitOrStayDealer(blackjackManager);
}
OutputView.printNewLine();
}

private void hitOrStayDealer(BlackjackManager blackjackManager) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 로직이 blackjackManager 밖에 나와 있을 필요가 있나요 ?
딜러가 모든 패를 받고 출력을 해도 될 것 같아요
현재

[3 ,7]
[3 ,7, 4] HIT
출력
[3, 7, 4, J] BUST
출력

변경

[3 ,7]
[3 ,7, 4] HIT
[3, 7, 4, J] BUST

출력
[3 ,7]
[3 ,7, 4] 
[3, 7, 4, J]

ex ) 

for (int i = 1; i < cards.size(); i++) {
   for (int j = 0; int j = i; j++) {
       System.out.print(cards.get(j));
   }
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아... 출력 예시만 따라서 구현을 했는데, 더 유연하게 생각해볼 수 있었네요. 😮😮😮

if (blackjackManager.isDealerScoreOverThenLimit()) {
blackjackManager.stayDealer();
return;
}
blackjackManager.hitDealer();
OutputView.printDealerHit();
}

private void printCardsWithScoreOfDealerAndAllPlayers(BlackjackManager blackjackManager) {
OutputView
.printStatusWithScore(DtoAssembler.createDealerStatusDto(blackjackManager.getDealer()));
DtoAssembler.createPlayerStatusDtos(blackjackManager.getPlayers())
.forEach(OutputView::printStatusWithScore);
}

private void printBlackjackResult(BlackjackManager blackjackManager) {
Dealer dealer = blackjackManager.getDealer();
Players players = blackjackManager.getPlayers();
ResultDto dealerResult = DtoAssembler.createDealerResultDto(dealer, players);
List<ResultDto> playerResultDtos = DtoAssembler.createPlayerResultDtos(dealer, players);
OutputView.printBlackjackResult(dealerResult, playerResultDtos);
}
}
80 changes: 80 additions & 0 deletions src/main/java/blackjack/domain/BlackjackManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package blackjack.domain;

import blackjack.domain.carddeck.CardDeck;
import blackjack.domain.participant.Dealer;
import blackjack.domain.participant.Player;
import blackjack.domain.participant.Players;

public class BlackjackManager {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 객체의 필요성을 잘 모르겠어요.

카드를 나눠주는 행위는 컨트롤러에서 수행 가능할 것 같고

get 메소드들은 playerResult 에서 가능할 것 같은데 그럼 playerResult 를 객체로 만드는것은 어떤가요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CardDeck을 딜러가 아닌 BlackjackManager가 갖게 하고, BlackjackManager가 도메인 정보들을 DTO로 변환시키는 역할도 갖도록 해봤어요!


private final CardDeck cardDeck;
private final Dealer dealer;
private final Players players;

public BlackjackManager(final Dealer dealer, final Players players) {
this(CardDeck.newShuffledDeck(), dealer, players);
}

public BlackjackManager(final CardDeck cardDeck, final Dealer dealer, final Players players) {
this.cardDeck = cardDeck;
this.dealer = dealer;
this.players = players;
}

public void initDrawCards() {
this.dealer.initDraw(this.cardDeck);
this.players.initDraw(this.cardDeck);
}

public void hitOrStayCurrentPlayer(boolean isPlayerHit) {
if (isPlayerHit) {
this.players.drawFirstOrderPlayer(this.cardDeck.draw());
return;
}
this.players.stayFirstOrderPlayer();
}

public void passTurnToNextPlayer() {
this.players.passTurnToNextPlayer();
}

public boolean isFinishedCurrentPlayer() {
return this.players.isFinishedCurrentPlayer();
}

public boolean isFinishedAllPlayers() {
return this.players.isAllPlayerFinished();
}

public void hitDealer() {
this.dealer.draw(this.cardDeck.draw());
}

public void stayDealer() {
this.dealer.stay();
}

public boolean isDealerScoreOverThenLimit() {
return this.dealer.isOverThenLimitScore();
}

public boolean isFinishedDealer() {
return this.dealer.isFinished();
}

public Players getPlayers() {
return this.players;
}

public Player getCurrentPlayer() {
return this.players.getFirstOrderPlayer();
}

public String getCurrentPlayerName() {
return this.players.getFirstOrderPlayerName();
}

public Dealer getDealer() {
return this.dealer;
}
}
85 changes: 85 additions & 0 deletions src/main/java/blackjack/domain/DtoAssembler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package blackjack.domain;

import blackjack.domain.carddeck.Card;
import blackjack.domain.participant.Dealer;
import blackjack.domain.participant.Player;
import blackjack.domain.participant.Players;
import blackjack.view.dto.CardDto;
import blackjack.view.dto.ParticipantDto;
import blackjack.view.dto.ResultDto;
import java.util.List;
import java.util.stream.Collectors;

public class DtoAssembler {

private static final String DELIMITER = ", ";

public static ParticipantDto createDealerInitStatusDto(final Dealer dealer) {
return new ParticipantDto(
createCardDtos(dealer.getInitCard()),
dealer.getScoreToInt()
);
}

public static ParticipantDto createDealerStatusDto(Dealer dealer) {
return new ParticipantDto(
createCardDtos(dealer.getCards()),
dealer.getScoreToInt()
);
}

public static List<ParticipantDto> createPlayerStatusDtos(final Players players) {
return players.toList()
.stream()
.map(DtoAssembler::createPlayerStatusDto)
.collect(Collectors.toList())
;
}

public static ParticipantDto createPlayerStatusDto(final Player player) {
return new ParticipantDto(
player.getName(),
createCardDtos(player.getCards()),
player.getScoreToInt()
);
}

private static List<CardDto> createCardDtos(final List<Card> cards) {
return cards.stream()
.map(card -> new CardDto(card.getNumberName() + card.getPatternName()))
.collect(Collectors.toList())
;
}

public static ResultDto createDealerResultDto(final Dealer dealer, Players players) {
List<Result> results = getResults(dealer, players);
return new ResultDto(
getResultString(results, Result.WIN) + DELIMITER
+ getResultString(results, Result.LOSE) + DELIMITER
+ getResultString(results, Result.DRAW)
);
}

private static List<Result> getResults(final Dealer dealer, final Players players) {
return players.toList()
.stream()
.map(player -> player.judgeByDealerState(dealer))
.map(Result::reverse)
.collect(Collectors.toList());
}

private static String getResultString(final List<Result> results, final Result result) {
return results.stream()
.filter(compareResult -> compareResult.equals(result))
.count() + result.getResult();
}

public static List<ResultDto> createPlayerResultDtos(final Dealer dealer,
final Players players) {
return players.toList()
.stream()
.map(player -> new ResultDto(player.getName(),
player.judgeByDealerState(dealer).getResult()))
.collect(Collectors.toList());
}
}
28 changes: 28 additions & 0 deletions src/main/java/blackjack/domain/Result.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package blackjack.domain;

public enum Result {

WIN("승"),
LOSE("패"),
DRAW("무");

private final String result;

Result(final String result) {
this.result = result;
}

public Result reverse() {
if (this == WIN) {
return LOSE;
}
if (this == LOSE) {
return WIN;
}
return DRAW;
}

public String getResult() {
return this.result;
}
}
Loading