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단계 - 블랙잭] 연로그(권시연) 미션 제출합니다. #220

Merged
merged 54 commits into from
Mar 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
50fdc1c
docs : 기능 구현 목록 작성
kimchan123 Mar 8, 2022
1433c94
feat : 카드 생성 기능
kimchan123 Mar 8, 2022
aab3cc1
feat : 카드 분배 기능
kimchan123 Mar 8, 2022
32abc93
feat : 카드 목록 합계 계산 기능
kimchan123 Mar 8, 2022
043a150
feat : 카드 목록 추가 기능
kimchan123 Mar 8, 2022
8c793fa
feat : 플레이어 생성 기능
kimchan123 Mar 8, 2022
865c9ec
feat : InputView 생성
kimchan123 Mar 8, 2022
06eaaa4
refactor : isBust 메서드 이동, Denomination getter 추가
kimchan123 Mar 8, 2022
0d2c62a
feat : 딜러 생성
kimchan123 Mar 8, 2022
34d85fa
feat : 참가자 interface 생성
kimchan123 Mar 8, 2022
0b0200a
feat : 게임 승패 구현
kimchan123 Mar 9, 2022
dd0bdee
feat : 딜러 게임 승패 횟수 구현
kimchan123 Mar 9, 2022
d5a2aa6
feat : OutputView 작성
kimchan123 Mar 9, 2022
54b5549
fix : 포맷 오류 수정
kimchan123 Mar 9, 2022
88fe468
fix : 카드 분배 로직 수정
kimchan123 Mar 9, 2022
a4d8dfc
feat : Name으로 원시값 포장
kimchan123 Mar 9, 2022
5725744
style : 불필요한 import 제거
kimchan123 Mar 9, 2022
197bc05
refactor : 하드 코딩 제거 및 toString 메서드 추가
kimchan123 Mar 9, 2022
66912db
feat : 게임을 진행하는 BlackJackGame 생성
kimchan123 Mar 9, 2022
aad861c
refactor : Participant를 인터페이스에서 추상클래스로 변경
kimchan123 Mar 10, 2022
2769851
feat : 플레이어와 딜러를 가져오는 기능 구현
kimchan123 Mar 10, 2022
37d9882
feat : GameResult 생성
kimchan123 Mar 10, 2022
3a07b4c
refactor : getName 메서드에서 이름을 바로 반환
kimchan123 Mar 10, 2022
8087eb6
refactor : 자바 컨벤션 통일
kimchan123 Mar 10, 2022
1d798b8
feat : Controller 구현
kimchan123 Mar 10, 2022
06c4eb6
feat : 연료 주입 구현
kimchan123 Mar 10, 2022
d065ece
refactor: Command Enum 도입
Mar 10, 2022
181e279
refactor: 불필요한 getValue 호출 제거
Mar 10, 2022
742b5ca
refactor: 카드 첫 분배 로직 변경
Mar 10, 2022
8d59b77
refactor: 무승부 경우 생성
Mar 10, 2022
3bf4950
refactor: 카드 발급 여부 입력 시 trim 처리
Mar 10, 2022
ee935fa
docs: 리드미 갱신 - 용어 정리 및 기능 목록 체크
Mar 10, 2022
75518e7
test: 테스트 코드 리팩토링
Mar 10, 2022
c9375bf
docs: 블랙잭 용어 보충 설명
Mar 14, 2022
848c1d6
style: controller 이름 변경
Mar 14, 2022
61162cf
refactor: while문 조건 단순화
Mar 14, 2022
8e314c7
style: 클래스명 변경 Denomination -> Number
Mar 14, 2022
e4103fd
refactor: 불필요한 조건 제거
Mar 14, 2022
15e593e
test: 테스트 유틸 분리
Mar 14, 2022
2b18bef
refactor: List<Participant>를 Participants로 대체
Mar 14, 2022
1818dd3
refactor: instanceof 대신 getClass로 비교
Mar 14, 2022
c29202c
refactor: 플레이 결과를 반환하는 메소드 이동
Mar 14, 2022
42d265f
style: 변수명 변경
Mar 14, 2022
2df5fe6
style: BlackJack -> Blackjack 이름 변경
Mar 14, 2022
77a5daf
style: blackjack 관련 클래스들 패키지 이동
Mar 14, 2022
13d6d80
refactor: 에러 종류 변경
Mar 15, 2022
a8759aa
style: Command 클래스 적합한 위치로 이동
Mar 15, 2022
2e27244
refactor: Participants에서 Player와 Dealer 구분
Mar 15, 2022
50fc6e6
refactor: 참가자의 인원 제한
Mar 15, 2022
a24d9ed
refactor: Player와 Dealer 구분
Mar 15, 2022
66dc29c
refactor: 불필요한 메소드 삭제
Mar 15, 2022
760f543
refactor: 카드의 현재 상태(Status) 도입
Mar 15, 2022
64be231
refactor: 값 비교를 도메인 내에서 진행하도록 변경
Mar 15, 2022
b95466e
docs: 구현 기능 목록 설명 추가
Mar 15, 2022
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
46 changes: 44 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,49 @@
# java-blackjack

블랙잭 미션 저장소
## 블랙잭 용어 정리
- 버스트: 숫자 카드의 합이 21 초과
- 블랙잭: 숫자 카드 합이 21 + 카드 수가 2장
- 푸시: 딜러와 플레이어 카드 동점 (=비김)
- 스테이: 카드를 그만 받겠다
- 힛: 카드를 한장 더 받겠다

## 구현 기능 목록

- [x] 참여할 사람의 이름 입력
- 쉼표 기준으로 분리
- (e) 빈 값, 공백 불가
- (e) 중복 불가
- (e) 참가 인원은 최대 8명
- [x] 카드 분배
- 딜러, 참가자에게 카드 2장씩 분배
- 딜러의 카드 1장 출력
- 참가자의 카드 2장 출력
- 참가자의 카드 합이 21이면 게임 종료
- [x] 참가자 카드 추가 분배
- 카드 합이 21 이상인 참가자는 턴 종료
- 참가자 별로 한장의 카드 추가 여부를 입력 받기 (y/n)
- 참가자가 카드 추가 여부 입력
- y를 선택한 경우 카드 추가 분배
- n를 선택한 경우 해당 참가자의 턴 종료
- 참가자의 카드 목록 출력
- [x] 딜러 카드 추가 분배
- 16 이하면 1장 추가 분배
- 17 이상이면 턴 종료
- [x] 결과 출력
- 카드 목록 및 총합 출력
(ex: 딜러 카드: 3다이아몬드, 9클로버, 8다이아몬드 - 결과: 20)
- 최종 승패 계산
- 최종 승패 출력
- 딜러 %d승 %d무 %d패 출력
- 참가자 승/무/패 결과 출력

## 기능 요구 사항

- 카드의 숫자 계산은 카드 숫자를 기본으로 하며, 예외로 Ace는 1 또는 11로 계산할 수 있으며, King, Queen, Jack은 각각 10으로 계산한다.
- 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며, 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다.
- 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 추가로 받아야 하고, 17점 이상이면 추가로 받을 수 없다.
- 게임을 완료한 후 각 플레이어별로 승패를 출력한다.

## 우아한테크코스 코드리뷰

- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)
- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)
8 changes: 8 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import blackjack.game.ConsoleGame;

public class Application {
public static void main(String[] args) {
ConsoleGame consoleGame = new ConsoleGame();
consoleGame.run();
}
}
51 changes: 51 additions & 0 deletions src/main/java/blackjack/domain/card/Card.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package blackjack.domain.card;

import java.util.Objects;

public class Card {

private final Number number;
private final Suit suit;

public Card(final Number number, final Suit suit) {
this.number = number;
this.suit = suit;
}

public Number getDenomination() {
return number;
}

public Suit getSuit() {
return suit;
}

public int toInt() {
return this.number.getValue();
}

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

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

@Override
public int hashCode() {
return Objects.hash(number, suit);
}

@Override
public String toString() {
return "Card{" +
"denomination=" + number +
", suit=" + suit +
'}';
}
}
45 changes: 45 additions & 0 deletions src/main/java/blackjack/domain/card/CardDistributor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package blackjack.domain.card;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;

public class CardDistributor {

private static final String DECK_IS_EMPTY = "카드가 모두 소요됐습니다.";
private static final List<Card> CACHE = new ArrayList<>();

private final Stack<Card> deck = new Stack<>();

static {
for (Suit suit : Suit.values()) {
for (Number number : Number.values()) {
CACHE.add(new Card(number, suit));
}
}
}

public CardDistributor() {
Collections.shuffle(CACHE);
deck.addAll(CACHE);
}

public Card distribute() {
if (isEmpty()) {
throw new IllegalStateException(DECK_IS_EMPTY);
}
return deck.pop();
}

private boolean isEmpty() {
return deck.isEmpty();
}

@Override
public String toString() {
return "CardDistributor{" +
"deck=" + deck +
'}';
}
}
66 changes: 66 additions & 0 deletions src/main/java/blackjack/domain/card/Cards.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package blackjack.domain.card;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Cards {

private static final int ACE_ADDITIONAL_VALUE = 10;
protected static final int BLACKJACK_VALUE = 21;
protected static final int BLACKJACK_COUNT = 2;
Copy link

Choose a reason for hiding this comment

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

접근제어자가 왜 protected일까요??

이곳에서 카드 상태와 점수를 도출하고 BlackJack에 대한 기준도 관리하고 있는데요.
연로그는 Cards의 역할과 책임을 어떻게 정했을까요??
카드의 상태와 게임의 규칙과 관련된 것을 Status에서 관리한다면 status에 있는게 맞지 않을까요??
아니면 결과를 도출하기 위해 필요한 값들이니 GameResult에 있는게 맞을 수도 있지 않을까요??
ace가 있는 경우 10을 더할지 말지를 정하는 것은 어디의 역할일까요??

Copy link
Author

Choose a reason for hiding this comment

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

Status에서 사용하기 때문에 protected로 열었습니다!
Enum에는 상단에 pirvate static final의 선언이 불가능한데 21를 넣는건 매직넘버로 느껴져서 Cards로 빼서 사용했습니다
지금 다시 생각해보니 Enum에서 사용하기 위해 불필요한 상수를 넣다니 좋지 않은 생각인 것 같네요😂

카드의 상태에 관련해서는 Status로 옮기는게 맞을 것 같네요!
enum은 상수로 쓰자에 포인트를 두다보니 자꾸 로직을 밖에서 쓰는 경향이 있는 것 같습니다ㅠ.ㅠ

원래는 ACE가 있는 경우 10을 더할지 말지랑 별개로 '카드들의 합계'를 구하는거니 Cards에 있어도 괜찮다고 생각했습니다
하지만 로운의 말을 듣고보니까 게임의 규칙과 연관이 있는거니 GameResult에 있는게 맞을 것 같아요
다음 단계 때 반영하겠습니다 :D

Copy link

Choose a reason for hiding this comment

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

enum 상단에 private static final의 선언이 불가능하다고 했는데요.

public enum Yn {
    Y, N;

    private static final number = 1;

이렇게 사용하면 되지 않나요??

Copy link
Author

Choose a reason for hiding this comment

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

public enum Command {
    private static final String YES = "y";
    private static final String NO = "n";
    
    HIT(YES),
    STAY(NO);
}
public enum Command {
    HIT(YES),
    STAY(NO);

    private static final String YES = "y";
    private static final String NO = "n";
}

이런 식의 사용이 불가능하다는 의미였습니다!


private final List<Card> value;

public Cards(List<Card> cards) {
this.value = new ArrayList<>(cards);
}

public void add(Card card) {
this.value.add(card);
}

public Status getStatus() {
return Status.findStatus(this);
}

public int getCount() {
return value.size();
}

public int sum() {
int sum = value.stream()
.mapToInt(Card::toInt)
.sum();

if (canAddAddtionalValue(sum)) {
sum += ACE_ADDITIONAL_VALUE;
}

return sum;
}

private boolean canAddAddtionalValue(int sum) {
return hasAce() && !exceedBust(sum);
}

private boolean hasAce() {
return value.stream()
.anyMatch(Card::isAce);
}

private boolean exceedBust(int sum) {
return sum + ACE_ADDITIONAL_VALUE > BLACKJACK_VALUE;
}

public List<Card> getValue() {
return Collections.unmodifiableList(value);
}

@Override
public String toString() {
return "Cards{" +
"value=" + value +
'}';
}
}
38 changes: 38 additions & 0 deletions src/main/java/blackjack/domain/card/Number.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package blackjack.domain.card;

public enum Number {

ACE(1, "A"),
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(10, "J"),
QUEEN(10, "Q"),
KING(10, "K");

private final int value;
private final String name;

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

public boolean isAce() {
return this == ACE;
}

public int getValue() {
return value;
}

public String getName() {
return name;
}
}
24 changes: 24 additions & 0 deletions src/main/java/blackjack/domain/card/Status.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package blackjack.domain.card;

import java.util.Arrays;
import java.util.function.Predicate;

public enum Status {
BLACKJACK((cards) -> cards.getCount() == Cards.BLACKJACK_COUNT && cards.sum() == Cards.BLACKJACK_VALUE),
BUST((cards) -> cards.sum() > Cards.BLACKJACK_VALUE),
NONE((cards) -> cards.sum() < Cards.BLACKJACK_VALUE
|| (cards.sum() == Cards.BLACKJACK_VALUE && cards.getCount() != Cards.BLACKJACK_COUNT));

private final Predicate<Cards> condition;

Status(Predicate<Cards> condition) {
this.condition = condition;
}

public static Status findStatus(Cards cards) {
return Arrays.stream(Status.values())
.filter(status -> status.condition.test(cards))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("유효하지 않은 cards 입니다."));
}
}
19 changes: 19 additions & 0 deletions src/main/java/blackjack/domain/card/Suit.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package blackjack.domain.card;

public enum Suit {

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

private final String name;

Suit(String name) {
this.name = name;
}

public String getName() {
return name;
}
}
62 changes: 62 additions & 0 deletions src/main/java/blackjack/domain/game/BlackjackGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package blackjack.domain.game;

import blackjack.domain.card.Card;
import blackjack.domain.card.CardDistributor;
import blackjack.domain.card.Cards;
import blackjack.domain.participant.Dealer;
import blackjack.domain.participant.Name;
import blackjack.domain.participant.Participant;
import blackjack.domain.participant.Participants;
import blackjack.domain.participant.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class BlackjackGame {

public static final String DEALER_NAME = "딜러";
private static final int INIT_CARD_COUNT = 2;

private final Participants participants;
private final CardDistributor cardDistributor = new CardDistributor();

public BlackjackGame(List<Name> names) {
Dealer dealer = new Dealer(new Name(DEALER_NAME), drawInitialCards());
List<Player> players = initializePlayers(new ArrayList<>(names));
this.participants = new Participants(players, dealer);
}

private List<Player> initializePlayers(List<Name> names) {
return names.stream()
.map(name -> new Player(name, drawInitialCards()))
.collect(Collectors.toUnmodifiableList());
}

private Cards drawInitialCards() {
List<Card> cards = new ArrayList<>();
for (int i = 0; i < INIT_CARD_COUNT; i++) {
cards.add(cardDistributor.distribute());
}
return new Cards(cards);
}

public void drawCard(Participant participant) {
participants.drawCard(participant, cardDistributor.distribute());
}

public GameResult createGameResult() {
return new GameResult(participants.getPlayers(), participants.getDealer());
}

public Participants getParticipants() {
return participants;
}

@Override
public String toString() {
return "BlackjackGame{" +
"participants=" + participants +
", cardDistributor=" + cardDistributor +
'}';
}
}
Loading