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단계 - 블랙잭 구현] 에어(김준서) 미션 제출합니다. #141

Merged
merged 70 commits into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
829ea84
feat: 테스트 기반으로 상속을 이용하여 연료 주입 기능 구현
pika96 Mar 2, 2021
ed81499
docs(README.md): 기능 목록 작성
pika96 Mar 2, 2021
5f8c9cd
refactor: 패키지명 소문자로 변경
pika96 Mar 2, 2021
8135255
feat(Card): 카드 생성 및 문양, 숫자 enum 구현
pika96 Mar 2, 2021
6c3f177
docs(README.md): 카드 구현 완료 및 체크
pika96 Mar 2, 2021
85b3436
docs(README.md): 카드덱 기능 목록 변경 및 체크
pika96 Mar 2, 2021
7c519c4
feat(CardDeck): 카드 덱 생성 및 드로우 기능 구현
pika96 Mar 2, 2021
6f09cf1
feat(ToDo.md): 해야할 일 추가
pika96 Mar 3, 2021
78c5086
docs(README.md): 플레이어 기능 목록 추가 및 완료
pika96 Mar 3, 2021
b48b6db
feat(Player): Person을 상속받은 플레이어 카드 추가 및 반환 메서드
pika96 Mar 3, 2021
181e7b3
feat(Person): Player와 Dealer를 상속할 추상클래스 Person 구현
pika96 Mar 3, 2021
8b325ec
refactor: 관련 도메인끼리 패키지 분리
pika96 Mar 3, 2021
b18ed5e
docs(README.md): 참가자 기능 목록 변경 및 체크
pika96 Mar 3, 2021
57e1f1f
docs(ToDo.md): 할일 추가
pika96 Mar 3, 2021
69f0507
feat(Dealer): 딜러 및 테스트 코드 구현
pika96 Mar 3, 2021
3abcd5d
feat(Players): 참가자 목록을 저장하는 Players 클래스 구현
pika96 Mar 3, 2021
9334c4b
docs(README.md): 참가자 최대 인원 예외 처리 체크
pika96 Mar 3, 2021
e350be4
feat(Players): 참가자 최대 인원 8명 제한 추가
pika96 Mar 3, 2021
159587d
docs(README.me): 카드 꾸러미 기능 목록 추가 및 완료
pika96 Mar 3, 2021
57dfcfe
feat(Cards): 카드 꾸러미 생성 구현
pika96 Mar 3, 2021
ce201a2
refactor: 모든 생성자 테스트 코드 isNotNull 메서드를 이용하여 테스트
pika96 Mar 3, 2021
8beda92
refactor: 이름 공백 유효성 검사위치를 Players 에서 Player로 변경
pika96 Mar 3, 2021
979201d
feat(Cards): 카드 꾸러미(일급컬렉션) 및 카드추가 메서드 구현
pika96 Mar 3, 2021
bd4a464
List<Card>를 Cards 일급 컬렉션으로 변경
pika96 Mar 3, 2021
497bec0
docs(ToDo.md): Cards 로 변경하는 일 완료
pika96 Mar 3, 2021
ed4b9b3
docs(README.md): 점수 계산 기능 체크
pika96 Mar 3, 2021
e6ae929
feat(Score): 점수 계산 구현
pika96 Mar 3, 2021
6aa276d
feat(BlackJackResult): 게임 결과 반환 구현
pika96 Mar 4, 2021
77aa60f
docs(README.md): 컨트롤러와 뷰 구현 완료 체크
pika96 Mar 4, 2021
25748fd
feat:컨트롤러와 뷰 구현 및 로직 구현
pika96 Mar 4, 2021
56066f8
refactor(BlackJackGame): 메서드 길이 리펙토링
pika96 Mar 4, 2021
b6b5350
refactor: 플레이어가 카드 뽑는 로직 Players로 변경
KJunseo Mar 4, 2021
94aa62b
refactor: 하드 코딩 상수화
KJunseo Mar 4, 2021
5cd9058
refactor: 카드 52장 캐싱 적용
KJunseo Mar 4, 2021
3742dca
refactor: 컨트롤러 내부 메서드분리
KJunseo Mar 4, 2021
b7aae0b
refactor: 컨트롤러 내부 비즈니스 로직 서비스로 이동
KJunseo Mar 4, 2021
b45e1e6
refactor: 점수 계산 로직 Cards에서 처리
KJunseo Mar 4, 2021
4a1518a
refactor: 패키지명 변경
KJunseo Mar 4, 2021
e9da342
style: getter 위치 아래로 내리기
KJunseo Mar 4, 2021
6db57ca
refector: 참가 인원 예외처리 수정
KJunseo Mar 4, 2021
4b49d62
refactor: OutputView 정리
KJunseo Mar 4, 2021
e133719
refactor: 상수 처리
KJunseo Mar 4, 2021
8ae5b3f
refector: name 원시값 포장
KJunseo Mar 4, 2021
63cd0a7
refactor: 사용하지 않는 구문 삭제
KJunseo Mar 4, 2021
9375525
refactor: 추상클래스 생성자 추가
KJunseo Mar 7, 2021
8fbed79
refactor: 공통으로 사용하는 메서드 override 못하도록 final 붙이기
KJunseo Mar 7, 2021
d1dc31f
refactor: 추상 클래스 명 변경
KJunseo Mar 7, 2021
70ac9f7
refactor: Players 생성시 Player 리스트 외부에서 주입
KJunseo Mar 7, 2021
62e4cea
refactor: BlackJackResult 객체 생성자 변경
KJunseo Mar 7, 2021
2098adb
refactor: 카드 점수를 나타내는 메서드 이름 변경
KJunseo Mar 7, 2021
ec6b89a
refactor: 52장의 카드 캐싱을 Card 클래스에서 생성
KJunseo Mar 7, 2021
e17eeaf
refactor: Cards 일급 컬렉션 불변객체로 만들지 않기
KJunseo Mar 7, 2021
8924113
refactor: 테스트에만 사용하는 코드 제거
KJunseo Mar 8, 2021
fb4abb0
refactor: ace 점수 계산 로직 Denomination에서 처리
KJunseo Mar 8, 2021
ff9fbcd
refactor: 중복된 테스트 제거
KJunseo Mar 8, 2021
352ab82
refactor: 패키지 구조 수정
KJunseo Mar 8, 2021
5bea04f
refactor: 객체 생성 테스트 변경 및 equals & hashCode 오버라이드
KJunseo Mar 8, 2021
5fd2200
refactor: Denomination에서 점수를 get하지 않고 누적된 점수를 반환하는 식으로 변경
KJunseo Mar 8, 2021
ef71b4d
refactor: players 카드 출력을 OutputView로 변경
KJunseo Mar 8, 2021
a8fbe3d
refactor: service 삭제
KJunseo Mar 8, 2021
a49fb24
refactor: 결과 비교 로직에서 스트림 사용하여 indent 줄이기
KJunseo Mar 8, 2021
10c7fab
test: Ace가 아닌 경우 & 참가자 드로우 가능 여부 테스트 추가
KJunseo Mar 8, 2021
7237aec
refactor: Name -> Nickname 변경 및 Players equals & hashCode 제거
KJunseo Mar 8, 2021
8b96d4d
refactor: dto 이용해서 view에 전달
KJunseo Mar 8, 2021
7282d15
test: 테스트 추가
KJunseo Mar 8, 2021
52de1de
feat: 카드 보유에 따른 상태 객체 생성
KJunseo Mar 9, 2021
8025f27
feat: BlackJack, Bust, Stay 객체를 Finished 로 묶기
KJunseo Mar 9, 2021
f92b07e
refactor: 상태 패턴을 사용하여 게임 진행해보기
KJunseo Mar 9, 2021
c15d25d
refactor: OutputView 정리
KJunseo Mar 9, 2021
a78fefb
refactor: 승 패 비교 로직 수정
KJunseo Mar 9, 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
65 changes: 63 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,66 @@
# java-blackjack
블랙잭 게임 미션 저장소

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

### 도메인

[참가자]
- 참가자 생성
- [x] 쉼표 기준으로 분리
- [x] 이름 한 글자 이상
- [x] 참가 최대 인원 8명

[카드]
- [x] 카드 문양, 번호 ENUM
- [x] 카드 문양, 번호

[카드 덱]
- [x] 서로 다른 52장의 카드를 생성
- [x] 카드를 드로우 할 수 있다.

[카드 꾸러미]
- [x] 여러 장의 카드를 가질 수 있는 일급 컬렉션

[플레이어, 딜러 - 추상 클래스]
Person
- [x] Player와 Dealer의 추상 클래스
- [x] 카드를 추가할 수 있다.
- [x] 보유한 카드를 가져올 수 있다.
- [x] 보유한 카드로 점수를 계산할 수 있다.
Player, Dealer

[게임 결과]
- [x] 딜러와 플레이어간의 승 패 결과

### 게임 진행

[카드 분배]
- [x] 딜러와 플레이어에게 카드 2장씩 분배
- [x] 딜러 카드는 1장만 공개
- [x] 분배한 카드는 덱에서 제거

[카드 추가]
- 플레이어에게 카드를 더 받을 지 물어보기
- [x] 입력은 y나 n 둘 중 하나
- 카드 합에 따라 달라진다.
- y : [x] 새로운 카드를 뽑기, 현재 보유 카드 출력
- n : [x] 현재 보유 카드 출력
- [x] 딜러의 카드 합이 16이하면 17이상이 될 때까지 카드 추가

[게임 결과]
- 딜러와 플레이어가 가진 카드와 합 출력
- [x] Ace 카드는 1이나 11으로 계산
- [x] 21을 넘지 않는 선에서 21에 가깝게 계산
- [x] K,Q,J는 10으로 계산
- 최종 승패 출력
- 각 플레이어와 딜러 점수 비교
- 플레이어 승
- [x] 플레이어 카드 합이 21이 넘지 않으면서 딜러보다 높은 경우
- [x] 딜러 카드 합이 21을 넘고 플레이어 카드합이 21을 넘지 않은 경우
- 딜러 승
- [x] 딜러 카드 합이 21이 넘지 않으면서 플레이어보다 21에 가까운 경우
- [x] 플레이어 카드 합이 21을 넘는 경우
- 무승부
- [x] 딜러와 플레이어 카드 합이 둘다 21을 넘는 경우
- [x] 딜러와 플레이어 카드 합이 같은 경우
6 changes: 6 additions & 0 deletions src/ToDo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# ToDo
생성자 -> 정적 팩토리 메서드
Score 계산 depth와 stream 변경 리펙토링!!

# Done
List<Card> cards -> Cards 일급컬렉션
10 changes: 10 additions & 0 deletions src/main/java/blackjack/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package blackjack;

import blackjack.controller.BlackJackGame;

public class Application {
public static void main(String[] args) {
BlackJackGame blackJackGame = new BlackJackGame();
blackJackGame.start();
}
}
110 changes: 110 additions & 0 deletions src/main/java/blackjack/controller/BlackJackGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package blackjack.controller;

import blackjack.domain.card.Card;
import blackjack.domain.card.CardDeck;
import blackjack.domain.card.Cards;
import blackjack.domain.participant.Dealer;
import blackjack.domain.participant.Nickname;
import blackjack.domain.participant.Player;
import blackjack.domain.participant.Players;
import blackjack.domain.result.BlackJackResult;
import blackjack.view.InputView;
import blackjack.view.OutputView;

import java.util.List;
import java.util.stream.Collectors;

public class BlackJackGame {
public void start() {
try {
Players players = registerPlayers();
Dealer dealer = new Dealer();
CardDeck cardDeck = cardDeckSetting();
distributeCards(players, dealer, cardDeck);
playersTurn(players, cardDeck);
dealerTurn(dealer, cardDeck);
showResult(players, dealer);
} catch (IllegalStateException e) {
OutputView.printError(e.getMessage());
}
}

private Players registerPlayers() {
try {
OutputView.enterPlayersName();
List<String> allPlayersName = InputView.inputName();
return new Players(generatePlayers(allPlayersName));
} catch (IllegalArgumentException e) {
OutputView.printError(e.getMessage());
return registerPlayers();
}
}

private List<Player> generatePlayers(List<String> allPlayersName) {
return allPlayersName.stream()
.map(Nickname::new)
.map(Player::new)
.collect(Collectors.toList());
}

private CardDeck cardDeckSetting() {
Cards deck = new Cards(Card.values());
CardDeck cardDeck = new CardDeck(deck);
cardDeck.shuffleCard();
return cardDeck;
}

private void distributeCards(Players players, Dealer dealer, CardDeck cardDeck) {
dealer.firstDraw(cardDeck.drawCard(), cardDeck.drawCard());
players.eachPlayerFirstDraw(cardDeck);
OutputView.distributeFirstTwoCard(players.toPlayersDto(), dealer.toParticipantDto());
}

private void playersTurn(Players players, CardDeck cardDeck) {
for (Player player : players.getPlayers()) {
eachPlayerTurn(cardDeck, player);
}
}

private void eachPlayerTurn(CardDeck cardDeck, Player player) {
while (player.canDraw() && askDrawCard(player)) {
player.draw(cardDeck.drawCard());
OutputView.showCards(player.toParticipantDto());
}
}

private boolean askDrawCard(Player player) {
try {
OutputView.askOneMoreCard(player.toParticipantDto());
boolean wantDraw = InputView.inputAnswer();
playerWantStopDraw(player, wantDraw);
return wantDraw;
} catch (IllegalArgumentException e) {
OutputView.printError(e.getMessage());
return askDrawCard(player);
}
}

private void playerWantStopDraw(Player player, boolean wantDraw) {
if (!wantDraw) {
OutputView.showCards(player.toParticipantDto());
player.stay();
}
}

private void dealerTurn(Dealer dealer, CardDeck cardDeck) {
while (dealer.canDraw()) {
dealer.draw(cardDeck.drawCard());
OutputView.dealerReceiveOneCard();
}
if (!dealer.isBust() && dealer.isHit()) {
dealer.stay();
}
}

private void showResult(Players players, Dealer dealer) {
OutputView.showAllCards(players.toPlayersDto(), dealer.toParticipantDto());
BlackJackResult blackJackResult = new BlackJackResult(players.verifyResultByCompareScore(dealer));
OutputView.showFinalResult(blackJackResult.toDealerResultDto(), blackJackResult.toPlayersResultDto());
}
}
59 changes: 59 additions & 0 deletions src/main/java/blackjack/domain/card/Card.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package blackjack.domain.card;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class Card {
private static final List<Card> CACHE;

static {
CACHE = Arrays.stream(Denomination.values())
.flatMap(denomination -> Arrays.stream(Shape.values())
.map(shape -> new Card(shape, denomination)))
.collect(Collectors.toList());
}

private final Shape shape;
private final Denomination denomination;

public Card(Shape shape, Denomination denomination) {
this.shape = shape;
this.denomination = denomination;
}

public boolean isAce() {
return this.denomination.isAce();
}

public int addScore(int score) {
return denomination.addScore(score);
}

public static List<Card> values() {
return Collections.unmodifiableList(CACHE);
}

public Denomination getDenomination() {
return this.denomination;
}

public Shape getShape() {
return this.shape;
}

@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 && denomination == card.denomination;
}

@Override
public int hashCode() {
return Objects.hash(shape, denomination);
}
}
17 changes: 17 additions & 0 deletions src/main/java/blackjack/domain/card/CardDeck.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package blackjack.domain.card;

public class CardDeck {
private final Cards deck;

public CardDeck(Cards deck) {
this.deck = deck;
}

public Card drawCard() {
return deck.draw();
}

public void shuffleCard() {
deck.shuffle();
}
}
97 changes: 97 additions & 0 deletions src/main/java/blackjack/domain/card/Cards.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package blackjack.domain.card;

import java.util.*;

import static blackjack.domain.state.BlackJack.BLACKJACK_NUMBER;

public class Cards {
private static final String NO_REMAIN_CARD_ERROR_MESSAGE = "남은 카드가 없습니다.";
private static final String DUPLICATE_CARD_ERROR_MESSAGE = "중복된 카드는 존재할 수 없습니다.";
private static final int DEALER_DRAW_CONDITION = 16;
public static final int TOP_CARD = 0;

private final List<Card> cards;

public Cards(Card... cards) {
this(Arrays.asList(cards));
}

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

public void addCard(Card card) {
if (cards.contains(card)) {
throw new IllegalStateException(DUPLICATE_CARD_ERROR_MESSAGE);
}
cards.add(card);
}

public Card draw() {
if (cards.size() == 0) {
throw new IndexOutOfBoundsException(NO_REMAIN_CARD_ERROR_MESSAGE);
}
return cards.remove(TOP_CARD);
}

public void shuffle() {
Collections.shuffle(cards);
}

public boolean isBlackJack() {
return calculateScore() == BLACKJACK_NUMBER;
}

public boolean isBust() {
return calculateScore() > BLACKJACK_NUMBER;
}

public boolean isStay() {
return cards.size() > 2 && isBlackJack();
}

public boolean isWin(Cards cards) {
return calculateScore() > cards.calculateScore();
}

public boolean isDraw(Cards cards) {
return calculateScore() == cards.calculateScore();
}

public boolean lessThanSixteen() {
return calculateScore() <= DEALER_DRAW_CONDITION;
}

public int calculateScore() {
int score = calculateTotalScore();
long aceCount = cards.stream()
.filter(Card::isAce)
.count();
return Denomination.plusRemainAceScore(score, aceCount);
}

public int calculateTotalScore() {
int score = 0;
for (Card card : cards) {
score = card.addScore(score);
}
return score;
}

public List<Card> getCards() {
return Collections.unmodifiableList(cards);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Cards cards1 = (Cards) o;
return Objects.equals(cards, cards1.cards);
}

@Override
public int hashCode() {
return Objects.hash(cards);
}
}
Loading