Skip to content

Commit

Permalink
[라빈] 블랙잭 2단계 미션 제출합니다. (#65)
Browse files Browse the repository at this point in the history
* refactor: Cards 의 중복 제거 / 블랙잭 로직 뎁스 1로 수정

* [refactor] 상속 의도를 명확하게 나타내기 위해 player와 user 네이밍 상호 변경

* [feat] 배팅 금액을 관리하는 객체 구현

* feat: 수익을 계산하는 클래스 구현 및 테스트 코드 추가

* [feat] WinningResult 수익을 담는 필드 추가

* feat: 컨트롤러와 뷰 수정

* refactor: 사용하지 않는 생성자, 메서드 제거 / PlayerFactory -> UserFactory 로 클래스 명 변경

* refactor: 테스트 코드 리팩토링

* refactor: 플레이어와 딜러의 카드 추가 지급 기준을 선택하는 메서드를 유저에 추상 메서드로 구현 / 중복 코드, 클래스 제거

* refactor:  플레이어의 이름과 배팅 금액을 받는 DTO 를 분리해서 설계 / 사용하지 않는 상수 제거

* refactor:  딜러의 카드 합에 따라 받은 카드의 개수 출력

* refactor: playerNameDTO, playerBettingMoneyDTO -> playerDTO 로 통합

* refactor: 현재 플레이어가 소유한 카드가 블랙잭인지 검사하는 로직을 BlackJackRule 로 이동

* refactor: CardCalculator 의 메서드명을 기능에 맞게 수정, 상수 이름 수정

* refactor: 딜러의 추가 지급 카드 수를 세는 불필요한 변수 제거

* refactor:  수익 계산을 인터페이스로 도출

* refactor:  게임 로직 버그 수정

* refactor:  ProfitStrategy 의 구현체들에 각 조건을 메서드로 만들어서 ProfitFactory 에서 if 절 제거

* refactor:  딜러의 수익 계산 결과를 WinningResult 가 담당하도록 변경

* refactor:  딜러의 추가 지급 카드 수 계산의 책임을 딜러로 이동

Co-authored-by: Eun Seok <jeseok95@gmail.com>
  • Loading branch information
giantim and joseph415 authored Mar 23, 2020
1 parent d16df7f commit e69e0c9
Show file tree
Hide file tree
Showing 48 changed files with 949 additions and 683 deletions.
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,25 @@
- 카드(숫자 원시값 포장)
- 카드의 종류를 담당하는 enum

- 딜러(Dealer): implements Player
- 딜러(Dealer)
- 16을 기준으로 카드를 받을지 결정하는 메서드
- 유저(User): implements Player
- 플레이어(Player)
- 유저에는 이름 프로퍼티가 필요하다 -> 생성자에서 이름을 받도록 변경해야 한다
- 승 / 패
- enum Y / N 을 기준으로 카드를 받을지 결정하는 메서드
- 현재 받은 카드의 총 합이 21을 초과하는지 검사하는 메서드
- 승 / 패 계산하는 메서드
- 플레이어(Player): interface
- 유저(User): abstract class
- 돈 관리 객체를 갖는다
- 카드묶음을 갖는다
- 카드를 받는 기능
- Users: 게임에 참여한 사람의 이름으로 생성한 유저 일급 컬렉션

- 예 / 아니오 값을 갖는 enum
- 입력한 값에 대해 유효성을 검사하는 메서드

- 수익률 계산 하는 클래스
- 수익 계산하는 기능
- 카드계산 클래스
- 플레이어의 카드 묶음을 받아서 총 합을 계산
- 21(블랙잭)인지 판단하는 메서드
Expand All @@ -41,9 +44,11 @@ DTO
- 카드를 담고 있는 DTO
- 카드묶음의 합도 포함
- 플레이어들의 상태 정보를 담는 DTO
- 돈 입력값을 전달하는 객체

view
InputViewer
- 돈을 입력받는 기능
- 유저 이름을 입력 받을 때 중복 안되게 해야한다
- 유저를 한 명 이상 입력해야 한다
- 이름은 쉼표로 구분해야 한다
2 changes: 1 addition & 1 deletion gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ if $cygwin ; then
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
# Add a player-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
Expand Down
77 changes: 32 additions & 45 deletions src/main/java/controller/BlackjackController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@

import domain.Answer;
import domain.BlackJackRule;
import domain.PlayerFactory;
import domain.UserFactory;
import domain.WinningResult;
import domain.card.CardDeck;
import domain.card.Cards;
import domain.player.Dealer;
import domain.player.Player;
import domain.player.Players;
import domain.player.User;
import dto.RequestAnswerDTO;
import dto.RequestPlayerNamesDTO;
import dto.ResponsePlayerDTO;
import dto.ResponseWinningResultDTO;
import domain.player.Users;
import domain.player.Player;
import dto.*;
import view.InputView;
import view.OutputView;

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

public class BlackjackController {
private static BlackJackRule blackJackRule = new BlackJackRule();
Expand All @@ -28,62 +27,50 @@ private BlackjackController() {
public static void run() {
CardDeck cardDeck = new CardDeck();

RequestPlayerNamesDTO requestPlayerNamesDTO = inputRequestPlayerNamesDTO();
Players players = PlayerFactory.create(cardDeck, requestPlayerNamesDTO.getPlayerName());
List<ResponsePlayerDTO> responsePlayerDTOS = ResponsePlayerDTO.createResponsePlayerDTOs(players);
Map<String, Integer> bettingMoneyToName = getBettingMoneyToName();
Users users = UserFactory.create(cardDeck, bettingMoneyToName);
List<ResponsePlayerDTO> responsePlayerDTOS = ResponsePlayerDTO.createResponsePlayerDTOs(users);
OutputView.printInitialResult(responsePlayerDTOS);

runUserBlackJack(cardDeck, players.getUsers());
runDealerBlackJack(cardDeck, players.getDealer());

OutputView.printFinalResult(ResponsePlayerDTO.createResponsePlayerDTOs(players));
runPlayerBlackJack(cardDeck, users.getPlayer());
runDealerBlackJack(cardDeck, users.getDealer());
OutputView.printFinalResult(ResponsePlayerDTO.createResponsePlayerDTOs(users));

WinningResult winningResult = new WinningResult(players);
WinningResult winningResult = WinningResult.create(users);
ResponseWinningResultDTO responseWinningResultDTO = ResponseWinningResultDTO.create(
winningResult.getWinningResult());
OutputView.printWinningResult(responseWinningResultDTO);
OutputView.printFinalProfit(responseWinningResultDTO);
}

private static void runUserBlackJack(CardDeck cardDeck, List<User> users) {
for (User user : users) {
runBlackJackPerUser(cardDeck, user);
}
private static Map<String, Integer> getBettingMoneyToName() {
List<RequestPlayerDTO> requestPlayerDTOS = InputView.inputPlayer();

return requestPlayerDTOS.stream()
.collect(Collectors.toMap(RequestPlayerDTO::getName, RequestPlayerDTO::getBettingMoney,
(x, y) -> y, LinkedHashMap::new));
}

private static void runBlackJackPerUser(CardDeck cardDeck, User user) {
if (user.isBlackJack()) {
return;
private static void runPlayerBlackJack(CardDeck cardDeck, List<Player> players) {
for (Player player : players) {
runBlackJackPerPlayer(cardDeck, player);
}
}

Answer answer = getAnswer(user);
while (blackJackRule.isHit(user, answer)) {
blackJackRule.hit(user, cardDeck.drawCard());
OutputView.printUserCard(ResponsePlayerDTO.create(user));
if (blackJackRule.isUserCardSumOverBlackJack(user)) {
break;
}
answer = getAnswer(user);
private static void runBlackJackPerPlayer(CardDeck cardDeck, Player player) {
while (blackJackRule.isHit(player) && getAnswer(player).isYes()) {
blackJackRule.hit(player, cardDeck.drawCard());
OutputView.printUserCard(ResponsePlayerDTO.create(player));
}
}

private static void runDealerBlackJack(CardDeck cardDeck, Dealer dealer) {
Cards dealerCards = dealer.getCard();
if (dealerCards.isCardsSumUnderSixteen()) {
while (blackJackRule.isHit(dealer)) {
blackJackRule.hit(dealer, cardDeck.drawCard());
OutputView.printDealerAdditionalCard();
}
}

private static RequestPlayerNamesDTO inputRequestPlayerNamesDTO() {
try {
return InputView.inputPlayerName();
} catch (IllegalArgumentException e) {
OutputView.printErrorMessage(e.getMessage());
return inputRequestPlayerNamesDTO();
}
OutputView.printDealerAdditionalCard(dealer.getHitCardsCount());
}

private static Answer getAnswer(Player user) {
private static Answer getAnswer(User user) {
try {
RequestAnswerDTO requestAnswerDTO = InputView.inputAnswer(ResponsePlayerDTO.create(user));
return Answer.of(requestAnswerDTO.getAnswer());
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/domain/BettingMoney.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package domain;

public class BettingMoney {
private static final int MIN_BETTING_MONEY = 1;

private int bettingMoney;

public BettingMoney(int bettingMoney) {
if (bettingMoney < MIN_BETTING_MONEY) {
throw new IllegalArgumentException("배팅 금액은 1원 이상 가능합니다.");
}

this.bettingMoney = bettingMoney;
}

public int getBettingMoney() {
return bettingMoney;
}
}
24 changes: 5 additions & 19 deletions src/main/java/domain/BlackJackRule.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,18 @@
package domain;

import domain.card.Card;
import domain.player.Player;
import domain.player.User;

public class BlackJackRule {
private static final int BLACK_JACK = 21;

public boolean isUserCardSumOverBlackJack(User user) {
public boolean isHit(User user) {
if (user == null) {
throw new NullPointerException("유저를 입력하지 않았습니다.");
}

return user.sumCardNumber() >= BLACK_JACK;
}

public boolean isHit(User user, Answer answer) {
if (user == null || answer == null) {
throw new NullPointerException("유저 또는 카드 선택 여부를 입력하지 않았습니다.");
throw new NullPointerException("플레이어를 입력하지 않았습니다.");
}

if (isUserCardSumOverBlackJack(user)) {
return false;
}
return answer.isYes();
return !user.isBlackJack() && user.isHit();
}

public void hit(Player player, Card card) {
player.hitCard(card);
public void hit(User user, Card card) {
user.hitCard(card);
}
}
37 changes: 27 additions & 10 deletions src/main/java/domain/CardCalculator.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
import java.util.List;

public class CardCalculator {
private static final int BLACK_JACK = 21;
private static final int MAX_CARDS_SUM = 21;
private static final int SUM_CONTAIN_ACE = 10;
private static final int DEALER_STANDARD_ADDITIONAL_CARD = 16;

private CardCalculator() {
}
Expand All @@ -18,32 +19,48 @@ public static int calculateAceStrategy(Cards cards) {
}

int playerCardsSum = calculateCards(cards.getCards());
if (cards.containAce() && playerCardsSum + SUM_CONTAIN_ACE <= BLACK_JACK) {
if (cards.containAce() && isAceStrategy(playerCardsSum)) {
playerCardsSum += SUM_CONTAIN_ACE;
}
return playerCardsSum;
}

private static boolean isAceStrategy(int playerCardsSum) {
return playerCardsSum + SUM_CONTAIN_ACE <= MAX_CARDS_SUM;
}

private static int calculateCards(List<Card> cards) {
return (int) cards.stream()
.mapToLong(Card::getCardNumber)
.sum();
}

public static boolean determineWinner(Cards userCards, Cards dealerCards) {
if (userCards == null || dealerCards == null) {
throw new NullPointerException("유저 또는 딜러의 카드를 입력하지 않았습니다.");
public static boolean determineWinner(Cards playerCards, Cards dealerCards) {
if (playerCards == null || dealerCards == null) {
throw new NullPointerException("플레이어 또는 딜러의 카드를 입력하지 않았습니다.");
}

int userCardSum = calculateAceStrategy(userCards);
int dealerCardSum = calculateAceStrategy(dealerCards);
int playerCardsSum = calculateAceStrategy(playerCards);
int dealerCardsSum = calculateAceStrategy(dealerCards);

if (userCardSum <= BLACK_JACK && dealerCardSum > BLACK_JACK) {
if (playerCardsSum <= MAX_CARDS_SUM && dealerCardsSum > MAX_CARDS_SUM) {
return true;
}
if (userCardSum > BLACK_JACK) {
if (playerCardsSum > MAX_CARDS_SUM) {
return false;
}
return userCardSum >= dealerCardSum;
return playerCardsSum >= dealerCardsSum;
}

public static boolean isMaxCardsSum(Cards cards) {
return calculateAceStrategy(cards) == MAX_CARDS_SUM;
}

public static boolean isUnderMaxCardsSum(Cards playerCards) {
return calculateAceStrategy(playerCards) < MAX_CARDS_SUM;
}

public static boolean isUnderDealerStandard(Cards dealerCards) {
return calculateAceStrategy(dealerCards) <= DEALER_STANDARD_ADDITIONAL_CARD;
}
}
17 changes: 0 additions & 17 deletions src/main/java/domain/DecisionWinner.java

This file was deleted.

29 changes: 0 additions & 29 deletions src/main/java/domain/PlayerFactory.java

This file was deleted.

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

import domain.card.CardDeck;
import domain.player.Dealer;
import domain.player.Player;
import domain.player.User;
import domain.player.Users;

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

public class UserFactory {
private UserFactory() {
}

public static Users create(CardDeck cardDeck, Map<String, Integer> playerInformation) {
if (cardDeck == null || playerInformation == null) {
throw new IllegalArgumentException("플레이어를 생성할 수 없습니다.");
}

List<User> users = new ArrayList<>();
users.add(new Dealer(cardDeck.initialDraw()));
users.addAll(playerInformation.entrySet().stream()
.map(entry -> new Player(entry.getKey(), cardDeck.initialDraw(), entry.getValue()))
.collect(Collectors.toList()));
return new Users(users);
}
}
Loading

0 comments on commit e69e0c9

Please sign in to comment.