Skip to content

Commit

Permalink
[디디] 블랙잭 1주차 미션 제출합니다. (#13)
Browse files Browse the repository at this point in the history
* feat: PlayerNameTest 검증 테스트 구현

* feat: PlayerName 클래스 구현

* feat: PlayerTest 구현

* feat: Player 구현

* refactor : 플레이어 관련 메서드 refactor

* feat : YesOrNo 테스트 구현

* feat : YesOrNo 구현

* feat : Card와 관련된 클래스 및 Enum 구현

* feat : Deck 의 Draw 기능 관련 테스트 및 필요 클래스 구현

* feat : CardHand 테스트 및 클래스 구현

* feat : User를 상속받는 Dealer, Player 클래스 구현

* refactor : User 생성 방식 변경

* feat : Controller, Application, View 클래스 구현

* refactor : StringUtils의 Trim 메서드 구현

* feat : Players 일급 컬렉션 구현

* refactor : User 관련 클래스 refactor

* refactor : CardHand 관련 클래스 리팩터

* refactor : Strategy 관련 클래스 리팩터

* refactor : Symbol enum 리팩터

* refactor : PlayerName 클래스 리팩터

* refactor : 불필요한 Strategy 인터페이스 제거 및 리팩토링

* feat : 게임 Controller 진행 순서 구현

* feat : 게임 결과 관련 클래스 계산 구현

* feat : Input, Output View 구현

* refactor : Result enum 메서드 추가

* refactor : DeckTest 수정

* refactor : 컨벤션 리팩토링

* refactor : 안쓰는 메서드 제거 및 컨벤션 리팩터링

* refactor : README.md 수정

* Update README.md

* refactor: YesOrNo 클래스 enum 으로 변경

* refactor: 불필요한 drawWhenYes 메서드 삭제

* refactor: CardHand의 점수 계산 로직 캡슐화

* refactor: Result 판단 로직 위치 변경

* refactor: Deck 자료구조 Stack 으로 변경

* refactor: toStringCardHand 메서드 상속 받도록 변경

* feat: 게임결과를 담당하는 GameResult 클래스 구현

* refactor: player, dealer 생성 방식 수정

* refactor: 피드백 반영

* refactor: 컨벤션 수정

* refactor: 주석 코드 삭제

* refactor: 변수명 수정

* refactor: User 생성자, addCard 메서드 drawCount 파라미터 추가

* refactor: 테스트 알맞게 수정
  • Loading branch information
fucct authored Mar 18, 2020
1 parent 5e57a47 commit 1c2ce34
Show file tree
Hide file tree
Showing 33 changed files with 1,103 additions and 4 deletions.
69 changes: 65 additions & 4 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)
## 기능 요구사항

- 블랙잭 게임을 변형한 프로그램을 구현한다. 블랙잭 게임은 딜러와 플레이어 중 카드의 합이 21 또는 21에 가장 가까운 숫자를 가지는 쪽이 이기는 게임이다.
- 플레이어는 게임을 시작할 때 배팅 금액을 정해야 한다. 카드의 숫자 계산은 카드 숫자를 기본으로 하며, 예외로 Ace는 1 또는 11로 계산할 수 있으며, King, Queen, Jack은 각각 10으로 계산한다.
- 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며, 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다. 단, 카드를 추가로 뽑아 21을 초과할 경우 배팅 금액을 모두 잃게 된다.
- 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 추가로 받아야 하고, 17점 이상이면 추가로 받을 수 없다.
- 게임을 완료한 후 각 플레이어별로 승패를 출력한다.

## 프로그래밍 요구사항

- 딜러와 플레이어에서 발생하는 중복 코드를 제거해야 한다.

## 기능 구현

PlayerNames 클래스

- 입력 검증 구현
- split / Empty / Null


User 클래스
- draw
- 보유한 카드
- 카드 점수 계산

- Player 클래스
- draw(Overriding)
- 이름
- result
- Dealer 클래스
- draw(Overriding)
- 이름
- result

Players
- Player 일급 컬렉션


Card

- enum
- 숫자
- 모양

CardFactory

- 카드 덱 생성

Deck

- 팩토리로 생성된 실제 게임 사용 덱

CardHand

- 일급 컬렉션

YesOrNo

- 카드를 받을 건지 입력 받고 검

Result

- enum
- 최종승패 결과
10 changes: 10 additions & 0 deletions src/main/java/Application/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package Application;

import controller.BlackJackGame;

public class Application {

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

import model.*;
import view.InputView;
import view.OutputView;

public class BlackJackGame {
public static final int ADDITIONAL_DRAW_COUNT = 1;
public static final int INITIAL_DRAW_COUNT = 2;
public static final int HIT_BOUNDARY = 16;
public static final int BLACK_JACK_COUNT = 21;

public static void play() {
Deck deck = new Deck(CardFactory.createCardList());
PlayerNames playerNames = new PlayerNames(InputView.inputPlayerNames());
Players players = new Players(playerNames, deck, INITIAL_DRAW_COUNT);
Dealer dealer = new Dealer(deck, INITIAL_DRAW_COUNT);

OutputView.printInitialCards(players, dealer);
OutputView.printUsersCard(players, dealer);
drawCardToPlayers(players, deck);
hitOrStayForDealer(dealer, deck);
OutputView.printFinalCardHandResult(players, dealer);

GameResult gameResult = new GameResult(players, dealer);
gameResult.calculateResults();
OutputView.printResult(gameResult);
}

private static void drawCardToPlayers(final Players players, final Deck deck) {
for (Player player : players) {
drawCardEachPlayer(deck, player);
}
}

private static void drawCardEachPlayer(Deck deck, Player player) {
while (!player.isBust()) {
Answer answer = Answer.getYesOrNoByValue(InputView.inputYesOrNo(player));
if (!answer.getTrueOrFalse()) {
break;
}
player.drawCard(deck, ADDITIONAL_DRAW_COUNT);
OutputView.printPlayerCard(player);
}
}

private static void hitOrStayForDealer(Dealer dealer, Deck deck) {
if (dealer.isHitBound()) {
OutputView.printDealerDraw(dealer);
dealer.drawCard(deck, ADDITIONAL_DRAW_COUNT);
}
}
}
Empty file removed src/main/java/empty.txt
Empty file.
7 changes: 7 additions & 0 deletions src/main/java/exception/EmptyDeckException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package exception;

public class EmptyDeckException extends RuntimeException {
public EmptyDeckException(String message) {
super(message);
}
}
7 changes: 7 additions & 0 deletions src/main/java/exception/IllegalResultException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package exception;

public class IllegalResultException extends RuntimeException {
public IllegalResultException(final String s) {
super(s);
}
}
7 changes: 7 additions & 0 deletions src/main/java/exception/IllegalStringInputException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package exception;

public class IllegalStringInputException extends RuntimeException {
public IllegalStringInputException(String message) {
super(message);
}
}
7 changes: 7 additions & 0 deletions src/main/java/exception/IllegalYesOrNoInputException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package exception;

public class IllegalYesOrNoInputException extends RuntimeException {
public IllegalYesOrNoInputException(String message) {
super(message);
}
}
41 changes: 41 additions & 0 deletions src/main/java/model/Answer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package model;

import exception.IllegalYesOrNoInputException;
import utils.StringUtils;

import java.util.Arrays;

public enum Answer {
YES("y", true),
NO("n", false);

private String yesOrNo;
private boolean trueOrFalse;

Answer(String yesOrNo, boolean trueOrFalse) {
this.yesOrNo = yesOrNo;
this.trueOrFalse = trueOrFalse;
}

public static Answer getYesOrNoByValue(String input) {
validate(input);
return Arrays.stream(Answer.values())
.filter(answer -> answer.isSameYesOrNo(input))
.findAny()
.orElseThrow(() -> new IllegalYesOrNoInputException(String.format("%s는 올바르지 않은 값입니다.", input)));
}

private boolean isSameYesOrNo(final String input) {
return yesOrNo.equalsIgnoreCase(input);
}

private static void validate(String input) {
StringUtils.validateNull(input);
StringUtils.validateEmpty(input);
}

public boolean getTrueOrFalse() {
return trueOrFalse;
}
}

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

import java.util.Objects;

public class Card {
private final Symbol symbol;
private final Type type;

public Card(Symbol symbol, Type type) {
this.symbol = symbol;
this.type = type;
}

public boolean isAce() {
return symbol == Symbol.ACE;
}

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

@Override
public int hashCode() {
return Objects.hash(symbol, type);
}

@Override
public String toString() {
return symbol.toString() + type.toString();
}

public int calculateScore() {
return symbol.getScore();
}
}
20 changes: 20 additions & 0 deletions src/main/java/model/CardFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package model;

import java.util.*;

public class CardFactory {

public static List<Card> createCardList() {
List<Card> cards = new ArrayList<>();
for (Symbol symbol : Symbol.values()) {
createByType(cards, symbol);
}
return Collections.unmodifiableList(cards);
}

private static void createByType(List<Card> cards, Symbol symbol) {
for (Type type : Type.values()) {
cards.add(new Card(symbol, type));
}
}
}
51 changes: 51 additions & 0 deletions src/main/java/model/CardHand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package model;

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

public class CardHand implements Iterable<Card> {
public static final int ADDITIONAL_ACE_SCORE = 10;
public static final int BLACK_JACK_SCORE = 21;

private List<Card> cards = new ArrayList<>();

public void addCard(Card card) {
cards.add(card);
}

public int calculateScore() {
if (isAce()) {
return calculateScoreWithAce();
}
return calculateScoreWithNoAce();
}

public int calculateScoreWithAce() {
int score = calculateScoreWithNoAce();
if (score + ADDITIONAL_ACE_SCORE > BLACK_JACK_SCORE) {
return score;
}
return score + ADDITIONAL_ACE_SCORE;
}

public int calculateScoreWithNoAce() {
return cards.stream()
.mapToInt(Card::calculateScore)
.sum();
}

public boolean isAce() {
return cards.stream().anyMatch(Card::isAce);
}

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

@Override
public Iterator<Card> iterator() {
return cards.iterator();
}
}
20 changes: 20 additions & 0 deletions src/main/java/model/Dealer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package model;

import static controller.BlackJackGame.HIT_BOUNDARY;

public class Dealer extends User {
public static final int ZERO = 0;
public static final String DEALER_NAME = "딜러";

public Dealer(Deck deck, int initialDrawCount) {
super(DEALER_NAME, deck, initialDrawCount);
}

public String toStringCardHandFirst() {
return cardHand.getCards().get(ZERO).toString();
}

public boolean isHitBound() {
return getScore() <= HIT_BOUNDARY;
}
}
45 changes: 45 additions & 0 deletions src/main/java/model/Deck.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package model;

import exception.EmptyDeckException;

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

public class Deck {
private Stack<Card> cards = new Stack<>();

public Deck(List<Card> cards) {
this.cards.addAll(cards);
shuffle();
}

public Deck(CardHand cardHand) {
this.cards.addAll(cardHand.getCards());
}

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

public CardHand draw(int count) {
validateSize(count);
CardHand cardHand = new CardHand();
for (int i = 0; i < count; i++) {
Card card = cards.pop();
cardHand.addCard(card);
}
return cardHand;
}

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

private void validateSize(int count) {
if (getCurrentDeckSize() - count < 0) {
throw new EmptyDeckException(count + "장 이상 draw 할 카드가 존재하지 않습니다.");
}
}

}
Loading

0 comments on commit 1c2ce34

Please sign in to comment.