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단계 - 블랙잭] 기론(김규철) 미션 제출합니다. #226

Merged
merged 77 commits into from
Mar 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
5aa725d
test: 테스트 작성
Juhyung990122 Mar 8, 2022
67807ba
feat: 추상클래스 Car 생성
Juhyung990122 Mar 8, 2022
ee1a9fe
feat: 종류별 자동차 도메인 구현
Juhyung990122 Mar 8, 2022
b28bbe9
feat: 연료 계산 및 주입 기능 구현
Juhyung990122 Mar 8, 2022
7161dc5
docs: 기능 요구 사항 작성
Juhyung990122 Mar 8, 2022
2ffc15c
feat: 참가자 이름 입력 기능 구현
Juhyung990122 Mar 9, 2022
6592e9d
feat: 유저 도메인 생성
Juhyung990122 Mar 9, 2022
6f32dec
feat: 카드, 카드팩토리 도메인 생성 및 캐싱
Juhyung990122 Mar 9, 2022
ec380dd
feat: Player,Dealer 도메인 생성 및 Dto변환 구현
Juhyung990122 Mar 9, 2022
65ea3ec
feat: 카드 넘버에 이름 추가
Juhyung990122 Mar 9, 2022
807fbf7
feat: CardDto 변환로직 구현 및 UserDto 리턴타입 수정
Juhyung990122 Mar 9, 2022
898beda
feat: 딜러와 참가자 카드 출력 기능 구현
Juhyung990122 Mar 9, 2022
bd394f5
feat: player 생성 및 카드 분배 기능 구현
Juhyung990122 Mar 9, 2022
f7f5050
feat: 딜러 카드 공개 로직 추가
Juhyung990122 Mar 9, 2022
66f520b
feat: 한장의 카드 받는 기능 구현
Juhyung990122 Mar 9, 2022
ea9fa87
refactor: 뷰의 책임을 컨트롤러로 이전
Juhyung990122 Mar 9, 2022
ac35b89
test: 카드분배기능 테스트 작성
Juhyung990122 Mar 10, 2022
05827d6
feat: 카드 추가 기능 구현
Juhyung990122 Mar 10, 2022
8750ed4
feat: 카드 추가 질의 기능 구현
Juhyung990122 Mar 10, 2022
0542747
feat: 플레이어 소지 카드 출력 구현
Juhyung990122 Mar 10, 2022
002889e
chore: 안내메세지 추가
Juhyung990122 Mar 10, 2022
bf87a2e
feat: 카드 리스트를 일급컬렉션으로 변환
Juhyung990122 Mar 10, 2022
b39dc59
feat: 카드 점수 계산 기능 구현
Juhyung990122 Mar 10, 2022
e4e796a
refactor: 접근제어자 수정
Juhyung990122 Mar 10, 2022
a64fdc1
test: Cards 테스트 추가
Juhyung990122 Mar 10, 2022
3557611
feat: 딜러 카드 추가 조건 구현
Juhyung990122 Mar 10, 2022
f58dd8f
feat: 최종 결과 출력 기능 구현
Juhyung990122 Mar 10, 2022
07a3b75
refactor: 미사용 코드 삭제
Juhyung990122 Mar 10, 2022
8c7ee51
feat: 최종 승패 출력 기능 구현
Juhyung990122 Mar 10, 2022
e76aa89
feat: 결과 DTO 생성
Juhyung990122 Mar 10, 2022
8159fe2
feat: 승패 판단 로직 수정
Juhyung990122 Mar 10, 2022
afaf8e5
test: 승패 판정 테스트 작성
Juhyung990122 Mar 10, 2022
314393a
feat: 입력 검증 로직 구현
Juhyung990122 Mar 10, 2022
b3c44d5
refactor: Controller 구조개선 및 Dealer 도메인 수정
Juhyung990122 Mar 10, 2022
ef0935d
feat: 카드 추가 로직 입력 형식 검증
Juhyung990122 Mar 10, 2022
0ac0ff6
refactor: 승패 판단 로직 수정
Juhyung990122 Mar 10, 2022
8cae334
refactor: 패키지 분리 및 코드포멧팅
Juhyung990122 Mar 10, 2022
2393038
refactor: 변수에 final 적용 및 매서드에 람다식 적용
Gyuchool Mar 12, 2022
dbc2aa8
refactor: public 상수 private으로 변경 및 매직넘버 제거
Gyuchool Mar 12, 2022
6e80286
refactor: 처음 카드들 초기화 하는 매서드 네이밍 수정
Gyuchool Mar 12, 2022
da8abdd
refactor: cards 도메인에 맞는 자료구조 변경 및 부생성자 생성
Gyuchool Mar 12, 2022
3ed5fe1
refactor: players일급 컬렉션 추가
Gyuchool Mar 12, 2022
c6a1387
refactor: controller가 하는 일 줄이도록 수정
Gyuchool Mar 12, 2022
b42b547
refactor: 방어적 복사 적용
Gyuchool Mar 12, 2022
3f646fc
test: 유저 입력한 만큼 참가자 생성되는지 테스트
Gyuchool Mar 12, 2022
f7af5bb
refactor: convention에 맞게 수정
Gyuchool Mar 12, 2022
5cb9d5a
refactor: ace찾는 로직 get함수 제거
Gyuchool Mar 13, 2022
d100e02
refactor: indent 2 제거
Gyuchool Mar 13, 2022
56ad261
refactor: CARD_CACHE 자료구조 Queue로 변경
Gyuchool Mar 14, 2022
d8586d9
refactor: 덱에서 캐싱 제거
Gyuchool Mar 14, 2022
ba13f67
refactor: 정적 팩토리 메서드 네이밍 수정
Gyuchool Mar 14, 2022
d03126b
refactor: ace찾는 매서드 및 네이밍 수정
Gyuchool Mar 14, 2022
6ea48e5
fix: 게임 규칙에 맞게 21이 넘으면 카드를 더 받을지 안 물어보도록 수정
Gyuchool Mar 14, 2022
2d334d3
refactor: dealer도 cardFactory를 통해서 카드 받도록 수정
Gyuchool Mar 14, 2022
2e80acc
refactor: y와 n 매직 넘버 제거
Gyuchool Mar 14, 2022
83b7f5b
refactor: dealer카드를 더 받는지 확인하는 매서드 네이밍 수정
Gyuchool Mar 14, 2022
5d622a5
refactor: get함수 제거
Gyuchool Mar 14, 2022
87a951e
test: 통계 테스트 추가
Gyuchool Mar 14, 2022
4022d7d
refactor: 디렉토리 이름 변경 및 카드에 캐싱 적용
Gyuchool Mar 15, 2022
79540cd
refactor: drawOneCard를 통한 initCards매서드 수정
Gyuchool Mar 15, 2022
1268c7b
refactor: 매서드 분리 및 매서드 메서드 네이밍 수정
Gyuchool Mar 15, 2022
08781bb
refactor: bustLine검증 책임 cards로 이전
Gyuchool Mar 15, 2022
3b9a5e5
refactor: 우승자 결정 로직 PlayerResult로 옮김
Gyuchool Mar 15, 2022
51f79ec
fix: 블랙잭 규칙에 맞게 수정
Gyuchool Mar 15, 2022
178d91b
refactor: hit일때 boolean값을 반환하도록 수정
Gyuchool Mar 15, 2022
2eb9509
refactor: 네이밍 수정
Gyuchool Mar 15, 2022
e8c5cc1
refactor: dealer Cards를 받아 생성하도록 수정
Gyuchool Mar 15, 2022
dc38f44
test: UserTest 추가
Gyuchool Mar 15, 2022
2d4c447
refactor: 딜러 승패무 횟수 얻는 로직 수정
Gyuchool Mar 15, 2022
63786b2
refactor: 승패 로직 수정
Gyuchool Mar 15, 2022
860426a
refactor: Card를 VO로 만든다.
Gyuchool Mar 15, 2022
10ec7f0
refactor: 무승부일때 로직 수정 및 코드 포멧팅
Gyuchool Mar 15, 2022
bee8566
refactor: dealer lose할 경우의 수 추가
Gyuchool Mar 15, 2022
ebe0b74
Revert "refactor: dealer lose할 경우의 수 추가"
Gyuchool Mar 15, 2022
3e8852b
refactor: result Test 추가 및 dealer lose 경우의 수 추가
Gyuchool Mar 15, 2022
91dbac5
refactor: 접근 지정자 private으로 수정
Gyuchool Mar 15, 2022
2a85843
refactor: add 반환 타입 boolean으로 변경
Gyuchool Mar 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,29 @@
## 우아한테크코스 코드리뷰

- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)

## 기능 요구 사항
### 입력
- 참가자의 이름 입력받는 기능
- [예외] 입력이 null일떄
- [예외] 입력이 비어있을때
- 참가자에게 카드를 더 받겠는지 묻는 기능
- [예외] y,n이 아닌경우

### 출력
- 참가자 이름들 출력 기능
- 딜러 및 참가자들이 가지고 있는 카드 출력
- 딜러의 승패 및 참가자들 승패 여부 출력

### 계산
- 딜러 카드 숫자 합 계산 : 16이하이면 카드를 더 받는다는 메시지 출력
- 참가자들이 가지고 있는 카드들의 합 계산 기능

### 카드 배분
- 딜러와 참가자들 2장씩 카드 나눠 갖도록 기능 구현

### 참가자
- 카드를 더 받을지 선택하는 기능
- 카드의 합이 21을 넘어갈 경우 딜러에게 패배한다.
-

12 changes: 12 additions & 0 deletions src/main/java/blackJack/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package blackJack;

import blackJack.controller.BlackjackController;

public class Application {
public static void main(String[] args) {
BlackjackController blackjackController = new BlackjackController();
blackjackController.run();

}

}
59 changes: 59 additions & 0 deletions src/main/java/blackJack/controller/BlackjackController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package blackJack.controller;
jnsorn marked this conversation as resolved.
Show resolved Hide resolved

import blackJack.domain.Card.CardFactory;
import blackJack.domain.User.Dealer;
import blackJack.domain.User.Player;
import blackJack.domain.User.Players;
import blackJack.dto.DealerResultDto;
import blackJack.dto.PlayerResultsDto;
import blackJack.dto.UserDto;
import blackJack.view.InputView;
import blackJack.view.OutputView;

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

public class BlackjackController {

public void run() {
CardFactory cardFactory = new CardFactory();
List<String> inputPlayerNames = InputView.inputPlayerNames();
Dealer dealer = new Dealer(cardFactory.initCards());
Players players = Players.create(inputPlayerNames, cardFactory);
OutputView.printDrawMessage(inputPlayerNames);
OutputView.printTotalUserCards(convertToUserDtos(dealer, players));

OutputView.printTotalResult(playGame(dealer, players, cardFactory));

Map<String, String> statistics = players.getStatistics(dealer);
OutputView.printFinalResult(PlayerResultsDto.from(statistics), DealerResultDto.from(statistics));
}

private List<UserDto> playGame(Dealer dealer, Players players, CardFactory cardFactory) {

players.getPlayers()
.forEach(player -> askOneMoreCard(player, cardFactory));
while (dealer.hit(cardFactory)) {
OutputView.printAddDealerCard();
}
return convertToUserDtos(dealer, players);

}

private void askOneMoreCard(Player player, CardFactory cardFactory) {
while (!player.isBust() && InputView.askOneMoreCard(player.getName())) {
player.hit(cardFactory);
OutputView.printPlayerCard(UserDto.from(player));
}
}

private List<UserDto> convertToUserDtos(Dealer dealer, Players players) {
List<UserDto> userDtos = new ArrayList<>();
userDtos.add(UserDto.from(dealer));
players.getPlayers().stream()
.map(UserDto::from)
.forEach(userDtos::add);
return userDtos;
}
}
71 changes: 71 additions & 0 deletions src/main/java/blackJack/domain/Card/Card.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package blackJack.domain.Card;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import static blackJack.utils.ExeptionMessage.EMPTY_CARD;

public class Card {
private static final int TOTAL_CARDS_SIZE = 52;
private static final List<Card> CACHE = new ArrayList<>(TOTAL_CARDS_SIZE);

private final Shape shape;
private final Number number;

static {
Arrays.stream(Shape.values())
.forEach(shape -> Arrays.stream(Number.values())
.map(number -> new Card(shape, number))
.forEach(CACHE::add));
}
Comment on lines +17 to +22
Copy link

Choose a reason for hiding this comment

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

추가로 가독성을 위해 메서드로 분리해봐요 :) #226 (comment)

이 부분에 대한 코멘트였습니다 :)


public Card(Shape shape, Number number) {
this.shape = shape;
this.number = number;
}

public static Card valueOf(Shape shape, Number number) {
return CACHE.stream().filter(card -> card.isSameShape(shape) && card.isSameNumber(number))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(EMPTY_CARD));
}
Comment on lines +29 to +33
Copy link

Choose a reason for hiding this comment

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

Map을 이용하면 꺼내기 더 쉽지 않을까요?


public Shape getShape() {
return shape;
}

public Number getNumber() {
return number;
}

public boolean isAce() {
return number.isAce();
}

private boolean isSameShape(Shape shape) {
return this.shape == shape;
}

private boolean isSameNumber(Number number) {
return this.number == number;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Card card = (Card) o;
return shape == card.shape && number == card.number;
}

@Override
public int hashCode() {
return Objects.hash(shape, number);
}
Gyuchool marked this conversation as resolved.
Show resolved Hide resolved
}
43 changes: 43 additions & 0 deletions src/main/java/blackJack/domain/Card/CardFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package blackJack.domain.Card;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class CardFactory {
Copy link

Choose a reason for hiding this comment

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

CardFactory보다는 Deck의 일급컬렉션처럼 보이는데, 기론은 어떻게 생각하시나요?

Copy link
Author

Choose a reason for hiding this comment

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

그러네요.. 객체 생성보다는 카드들을 관리하고 분배하니 일급컬렉션에 가까워보입니다.
사실상 deck의 역할을 하고 있으니 아예 네이밍을 deck으로 수정하겠습니다!


public static final int INIT_CARD_SIZE = 2;

private final Queue<Card> deck;

public CardFactory() {
List<Card> cards = createCards();
Collections.shuffle(cards);
deck = new LinkedList<>(cards);
}

private List<Card> createCards() {
List<Card> cards = new ArrayList<>();
Arrays.stream(Shape.values())
.forEach(shape -> Arrays.stream(Number.values())
.map(number -> Card.valueOf(shape, number))
.forEach(cards::add));
return cards;
}

public Cards initCards() {
List<Card> cards = IntStream.range(0, INIT_CARD_SIZE)
.mapToObj(i -> drawOneCard())
.collect(Collectors.toList());

return new Cards(cards);
}
Comment on lines +28 to +34
Copy link

Choose a reason for hiding this comment

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

기론은 for문IntStream.range()를 어떤 기준으로 사용하시나요?
(참고) for-loop 를 Stream.forEach() 로 바꾸지 말아야 할 3가지 이유

Copy link
Author

@Gyuchool Gyuchool Mar 16, 2022

Choose a reason for hiding this comment

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

저는 collection을 구할때 stream을 유용하게 사용해서 웬만한 반복문은 통일성 있게 stream으로 바꾸려고 했었습니다! 또는 코드 길이가 길어지면 코드의 길이를 줄이기 또는 indent를 줄이기 위해 사용하기도 했습니다!

forEach는 System.out.println와 같이 단순한 출력문일때만 사용하는게 좋다고 알고 있었는데 몇몇 군데 그냥 무심코 사용했던 곳이 보이네요😭
그리고 IntStream.range()Stream.forEach()와 별개인줄 알았는데 같이 성능상 문제가 있었군요!


public Card drawOneCard() {
return deck.poll();
}

public int size() {
return deck.size();
}
}
72 changes: 72 additions & 0 deletions src/main/java/blackJack/domain/Card/Cards.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package blackJack.domain.Card;

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

import static blackJack.domain.Card.CardFactory.INIT_CARD_SIZE;

public class Cards {
private static final int BUST_LINE = 21;
private static final int EXTRA_SCORE = 10;

private final Set<Card> cards;

public Cards(List<Card> deck) {
this(new HashSet<>(deck));
}

private Cards(Set<Card> deck) {
this.cards = new HashSet<>(deck);
}

public Set<Card> getCards() {
return new HashSet<>(cards);
}

public boolean add(Card card) {
return cards.add(card);
}
Comment on lines +27 to +29
Copy link

Choose a reason for hiding this comment

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

메서드가 한 가지 일만 하고 있나요?

Copy link
Author

Choose a reason for hiding this comment

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

코드를 줄이려고 욕심부리다 보니 자꾸 퇴보하는거 같네요...😂
수정했습니다!


public int getScore() {
int score = cards.stream().mapToInt(card -> card.getNumber().getValue()).sum();
Gyuchool marked this conversation as resolved.
Show resolved Hide resolved

return addAceScore(score);
}

private int addAceScore(int score) {
long countAce = cards.stream().filter(Card::isAce).count();

for (int i = 0; i < countAce; i++) {
score = calculateAceScore(score);
}
return score;
}

private int calculateAceScore(int score) {
if (score + EXTRA_SCORE <= BUST_LINE) {
score += EXTRA_SCORE;
}
return score;
}

public int size() {
return cards.size();
}

public boolean isBlackJack() {
return cards.size() == INIT_CARD_SIZE && getScore() == BUST_LINE;
}

public boolean isBust() {
return getScore() > BUST_LINE;
}

public boolean isGreaterThan(Cards cards) {
return this.getScore() > cards.getScore();
}

public boolean isSameScore(Cards cards) {
return !isBust() && this.getScore() == cards.getScore();
}
}
37 changes: 37 additions & 0 deletions src/main/java/blackJack/domain/Card/Number.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package blackJack.domain.Card;

public enum Number {
ACE("A", 1),
TWO("2", 2),
THREE("3", 3),
FOUR("4", 4),
FIVE("5", 5),
SIX("6", 6),
SEVEN("7", 7),
EIGHT("8", 8),
NINE("9", 9),
TEN("10", 10),
JACK("J", 10),
QUEEN("Q", 10),
KING("K", 10);

private final String denomination;
private final int value;

Number(String denomination, int value) {
this.denomination = denomination;
this.value = value;
}

public String getDenomination() {
return denomination;
}

public int getValue() {
return value;
}

public boolean isAce() {
return this == ACE;
}
}
19 changes: 19 additions & 0 deletions src/main/java/blackJack/domain/Card/Shape.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package blackJack.domain.Card;

public enum Shape {
HEART("하트"),
DIAMOND("다이아몬드"),
SPADE("스페이드"),
CLOVER("클로버");

private final String shapeName;

Shape(String shapeName) {
this.shapeName = shapeName;
}

public String getShapeName() {
return shapeName;
}

}
39 changes: 39 additions & 0 deletions src/main/java/blackJack/domain/PlayerResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package blackJack.domain;

import blackJack.domain.User.Dealer;
import blackJack.domain.User.Player;

public enum PlayerResult {
WIN("승"),
DRAW("무"),
LOSE("패");

private final String value;

PlayerResult(String value) {
this.value = value;
}

public String getValue() {
return value;
}

public static PlayerResult decision(Dealer dealer, Player player) {
if (isAllBlackJack(dealer, player) || dealer.isSameScore(player)) {
return PlayerResult.DRAW;
}
if (isLose(dealer, player)) {
return PlayerResult.WIN;
}
Comment on lines +25 to +27
Copy link

Choose a reason for hiding this comment

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

isLose가 true면 Win을 반환하네요. 이 네이밍은 누구 기준일까요?

Copy link
Author

Choose a reason for hiding this comment

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

명확한 이름이 필요하겠군요!

return PlayerResult.LOSE;
}

private static boolean isLose(Dealer dealer, Player player) {
return player.isBlackJack() || dealer.isBust() || (player.isGreaterScoreThan(dealer) && !player.isBust());
}

private static boolean isAllBlackJack(Dealer dealer, Player player) {
return dealer.isBlackJack() && player.isBlackJack();
}

}