diff --git a/README.md b/README.md index 556099c4de3..0c54f930077 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,84 @@ -# java-blackjack +# 블랙잭 -블랙잭 미션 저장소 -## 우아한테크코스 코드리뷰 +## 기능 요구 사항 -- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) +### 플레이어 이름 + +### 플레이어 + +- [X] 플레이어는 이름을 갖는다 +- [X] 플레이어는 카드들을 갖는다 +- [X] 플레이어는 카드를 저장한다 +- [X] 죽었는지 안 죽었는지 여부를 반환한다 (21을 초과하는지) + + +### 카드 + +- [X] 문자와 모양을 상태로 갖는다 + + +### Hand(카드들) + +- [X] 여러개의 카드를 갖는다 +- [X] 카드의 총합 계산한다 +- [X] 카드 추가한다 + + +### 카드 게임 + +- [X] 각 플레이어에게 카드를 2장씩 지급한다 +- [X] 플레이어마다 추가 지급한다 + + +### 덱 + +- [X] 모든 카드를 1장씩 갖고 있다. +- [X] 랜덤으로 카드를 뽑는다. + + +### 게임 승패 결정 + +- [X] 딜러와 모든 플레이어의 승패 여부를 결정한다. + - [X] 딜러와 플레이어 둘다 21을 초과할 경우, 플레이어가 패배한다. + - [X] 카드 합계가 딜러는 21 이하, 플레이어는 21 초과인 경우, 플레이어가 패배한다. + - [X] 카드 합계가 딜러는 21 초과, 플레이어는 21 이하인 경우, 플레이어가 승리한다. + - [X] 카드 합계가 딜러와 플레이어 모두 21 이하인 경우, 숫자가 큰 사람이 승리한다. + - [X] 카드 합계가 딜러와 플레이어 모두 21 이하이고 동일한 경우, 무승부다. + + +### 인풋 뷰 + +- [X] 참가자의 이름을 입력받는다. + - [X] 이름은 쉼표 기준으로 분리한다. +- [X] 카드 추가 여부를 입력받는다. + - [X] y 또는 n이 아닌 경우, 예외를 발생한다. + - [X] 사용자 카드 합이 21을 초과하면, 카드 추가 여부를 묻지 않는다. + - [X] 플레이어가 n을 입력할 때까지 카드 추가 여부를 묻는다. + + +### 아웃풋 뷰 + +- [X] 딜러와 플레이어 전원에게 초기에 분배한 카드 출력한다. + - [X] 딜러의 카드는 1장만 공개한다. +- [X] 플레이어가 보유한 모든 카드를 출력한다. +- [X] 딜러가 추가 카드를 발급 받았는지 여부 출력한다. +- [X] 보유한 모든 카드의 합을 출력한다. +- [X] 최종 승패를 출력한다. + + +## 베팅 + +### 카지노 + +- [x] 은행과 유사한 역할로 플레이어의 계좌를 관리한다. +- [x] 계좌는 사용자 이름으로 맵핑한다. +- [x] 승패 결과에 따라 송금을 진행한다. +- [x] 플레이어의 잔고를 조회한다. + + +### 잔고 + +- [x] 사용자의 돈을 저장한다. +- [x] 입금할 수 있다. +- [x] 출금할 수 있다. diff --git a/src/main/java/blackjack/BlackjackApplication.java b/src/main/java/blackjack/BlackjackApplication.java new file mode 100644 index 00000000000..0d903c9a9aa --- /dev/null +++ b/src/main/java/blackjack/BlackjackApplication.java @@ -0,0 +1,16 @@ +package blackjack; + +import blackjack.view.InputView; +import blackjack.view.MessageResolver; +import blackjack.view.OutputView; + +public class BlackjackApplication { + + public static void main(String[] args) { + final InputView inputView = new InputView(); + final OutputView outputView = new OutputView(new MessageResolver()); + + final BlackjackController blackjackController = new BlackjackController(inputView, outputView); + blackjackController.run(); + } +} diff --git a/src/main/java/blackjack/BlackjackController.java b/src/main/java/blackjack/BlackjackController.java new file mode 100644 index 00000000000..3e653e05af0 --- /dev/null +++ b/src/main/java/blackjack/BlackjackController.java @@ -0,0 +1,91 @@ +package blackjack; + +import blackjack.domain.betting.BetDetails; +import blackjack.domain.betting.ProfitDetails; +import blackjack.domain.cardgame.CardDeck; +import blackjack.domain.cardgame.CardGameResult; +import blackjack.domain.player.Dealer; +import blackjack.domain.player.Name; +import blackjack.domain.player.Participant; +import blackjack.domain.player.Player; +import blackjack.view.InputView; +import blackjack.view.OutputView; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.stream.Collectors; + +public class BlackjackController { + private final InputView inputView; + private final OutputView outputView; + + BlackjackController(final InputView inputView, final OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + void run() { + final List names = inputView.askPlayerNames(); + final Dealer dealer = new Dealer(); + final List players = names.stream().map(Player::new).toList(); + final CardDeck deck = CardDeck.createShuffledDeck(); + + final BetDetails playersBetDetails = getPlayersBettingMoney(names); + initializeHand(deck, dealer, players); + outputView.printInitialHandOfEachPlayer(dealer, players); + + for (final Player player : players) { + givePlayerMoreCardsIfWanted(deck, player); + } + giveDealerMoreCardsIfNeeded(deck, dealer); + + printHandStatusOfEachPlayer(dealer, players); + final CardGameResult result = CardGameResult.of(dealer, players); + ProfitDetails profits = playersBetDetails.calculateProfit(result); + printProfit(profits); + } + + private BetDetails getPlayersBettingMoney(final List playerNames) { + return playerNames.stream() + .collect( + Collectors.collectingAndThen( + Collectors.toMap( + name -> name, + inputView::askBettingMoney, + (x, y) -> x, + LinkedHashMap::new + ), BetDetails::new)); + } + + private void initializeHand(final CardDeck deck, final Dealer dealer, final List players) { + deck.giveInitialCards(dealer); + for (final Participant player : players) { + deck.giveInitialCards(player); + } + } + + private void givePlayerMoreCardsIfWanted(final CardDeck deck, final Player player) { + while (player.isAlive() && inputView.askForMoreCard(player.name())) { + deck.giveCard(player); + outputView.printPlayerCard(player); + } + } + + private void giveDealerMoreCardsIfNeeded(final CardDeck deck, final Dealer dealer) { + while (dealer.isMoreCardNeeded()) { + deck.giveCard(dealer); + outputView.printDealerHitMessage(dealer); + } + } + + private void printHandStatusOfEachPlayer(final Dealer dealer, final List players) { + outputView.printParticipantCardWithScore(dealer); + for (final Player player : players) { + outputView.printParticipantCardWithScore(player); + } + } + + private void printProfit(final ProfitDetails profits) { + outputView.printPlayerProfit(profits); + } +} diff --git a/src/main/java/blackjack/domain/betting/BetDetails.java b/src/main/java/blackjack/domain/betting/BetDetails.java new file mode 100644 index 00000000000..5188adc5165 --- /dev/null +++ b/src/main/java/blackjack/domain/betting/BetDetails.java @@ -0,0 +1,41 @@ +package blackjack.domain.betting; + +import blackjack.domain.cardgame.CardGameResult; +import blackjack.domain.cardgame.WinningStatus; +import blackjack.domain.player.Name; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class BetDetails { + private final Map playersBettingMoney; + + public BetDetails(final Map playersBettingMoney) { + this.playersBettingMoney = playersBettingMoney; + } + + public ProfitDetails calculateProfit(final CardGameResult result) { + final Map profitDetails = new LinkedHashMap<>(); + + for (final var nameAndStatus : result.totalResult().entrySet()) { + final Name name = extractName(nameAndStatus); + final WinningStatus status = extractStatus(nameAndStatus); + final Profit profit = Profit.of(findBettingMoney(name), status); + profitDetails.put(name, profit); + } + + return new ProfitDetails(profitDetails); + } + + private Name extractName(final Map.Entry nameAndStatus) { + return nameAndStatus.getKey(); + } + + private WinningStatus extractStatus(final Map.Entry nameAndStatus) { + return nameAndStatus.getValue(); + } + + private Money findBettingMoney(final Name name) { + return playersBettingMoney.get(name); + } +} diff --git a/src/main/java/blackjack/domain/betting/Money.java b/src/main/java/blackjack/domain/betting/Money.java new file mode 100644 index 00000000000..e5e6f8613df --- /dev/null +++ b/src/main/java/blackjack/domain/betting/Money.java @@ -0,0 +1,22 @@ +package blackjack.domain.betting; + +public record Money(int value) { + + public Money { + validateMinimum(value); + } + + private void validateMinimum(final int value) { + if (value < 0) { + throw new IllegalArgumentException("돈의 액수는 0 이상이어야 합니다."); + } + } + + int minusValue() { + return -value; + } + + int multipleValue(final double rate) { + return (int) (value * rate); + } +} diff --git a/src/main/java/blackjack/domain/betting/Profit.java b/src/main/java/blackjack/domain/betting/Profit.java new file mode 100644 index 00000000000..db24294e0e3 --- /dev/null +++ b/src/main/java/blackjack/domain/betting/Profit.java @@ -0,0 +1,29 @@ +package blackjack.domain.betting; + +import blackjack.domain.cardgame.WinningStatus; + +import static blackjack.domain.cardgame.WinningStatus.BLACKJACK; +import static blackjack.domain.cardgame.WinningStatus.LOSE; +import static blackjack.domain.cardgame.WinningStatus.PUSH; +import static blackjack.domain.cardgame.WinningStatus.WIN; + +public record Profit(int value) { + private static final double BLACKJACK_PROFIT_RATE = 1.5; + + public static Profit of(final Money money, final WinningStatus status) { + if (WIN.equals(status)) { + return new Profit(money.value()); + } + if (LOSE.equals(status)) { + return new Profit(money.minusValue()); + } + if (PUSH.equals(status)) { + return new Profit(0); + } + if (BLACKJACK.equals(status)) { + return new Profit(money.multipleValue(BLACKJACK_PROFIT_RATE)); + } + + throw new IllegalStateException(); + } +} diff --git a/src/main/java/blackjack/domain/betting/ProfitDetails.java b/src/main/java/blackjack/domain/betting/ProfitDetails.java new file mode 100644 index 00000000000..77d8d2e1d46 --- /dev/null +++ b/src/main/java/blackjack/domain/betting/ProfitDetails.java @@ -0,0 +1,26 @@ +package blackjack.domain.betting; + +import blackjack.domain.player.Name; + +import java.util.Collections; +import java.util.Map; + +public class ProfitDetails { + private final Map profits; + + public ProfitDetails(final Map profits) { + this.profits = profits; + } + + public Profit getDealerProfit() { + final int playersProfitSum = profits.values() + .stream() + .mapToInt(Profit::value) + .sum(); + return new Profit(-playersProfitSum); + } + + public Map details() { + return Collections.unmodifiableMap(profits); + } +} diff --git a/src/main/java/blackjack/domain/card/Card.java b/src/main/java/blackjack/domain/card/Card.java new file mode 100644 index 00000000000..ddcc69bfb2b --- /dev/null +++ b/src/main/java/blackjack/domain/card/Card.java @@ -0,0 +1,27 @@ +package blackjack.domain.card; + +public class Card { + private final CardNumber cardNumber; + private final CardShape cardShape; + + public Card(final CardNumber cardNumber, final CardShape cardShape) { + this.cardNumber = cardNumber; + this.cardShape = cardShape; + } + + public boolean isAceCard() { + return cardNumber == CardNumber.ACE; + } + + public int getScore() { + return cardNumber.getValue(); + } + + public CardNumber getNumber() { + return cardNumber; + } + + public CardShape getShape() { + return cardShape; + } +} diff --git a/src/main/java/blackjack/domain/card/CardNumber.java b/src/main/java/blackjack/domain/card/CardNumber.java new file mode 100644 index 00000000000..c99818bc6f8 --- /dev/null +++ b/src/main/java/blackjack/domain/card/CardNumber.java @@ -0,0 +1,27 @@ +package blackjack.domain.card; + +public enum CardNumber { + ACE(1), + TWO(2), + THREE(3), + FOUR(4), + FIVE(5), + SIX(6), + SEVEN(7), + EIGHT(8), + NINE(9), + TEN(10), + JACK(10), + QUEEN(10), + KING(10); + + private final int value; + + CardNumber(final int value) { + this.value = value; + } + + int getValue() { + return value; + } +} diff --git a/src/main/java/blackjack/domain/card/CardShape.java b/src/main/java/blackjack/domain/card/CardShape.java new file mode 100644 index 00000000000..a3f9a873009 --- /dev/null +++ b/src/main/java/blackjack/domain/card/CardShape.java @@ -0,0 +1,8 @@ +package blackjack.domain.card; + +public enum CardShape { + SPADE, + HEART, + DIAMOND, + CLOVER +} diff --git a/src/main/java/blackjack/domain/cardgame/CardDeck.java b/src/main/java/blackjack/domain/cardgame/CardDeck.java new file mode 100644 index 00000000000..b7ca308ca28 --- /dev/null +++ b/src/main/java/blackjack/domain/cardgame/CardDeck.java @@ -0,0 +1,56 @@ +package blackjack.domain.cardgame; + +import blackjack.domain.card.Card; +import blackjack.domain.card.CardNumber; +import blackjack.domain.card.CardShape; +import blackjack.domain.player.Participant; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class CardDeck { + private static final int INITIAL_CARD_NUMBER = 2; + + private final Deque deck; + + private CardDeck(Deque deck) { + this.deck = deck; + } + + public static CardDeck createShuffledDeck() { + final List allKindOfCards = Arrays.stream(CardShape.values()) + .flatMap(CardDeck::createEachNumber) + .collect(Collectors.toList()); + + Collections.shuffle(allKindOfCards); + + return new CardDeck(new LinkedList<>(allKindOfCards)); + } + + private static Stream createEachNumber(final CardShape cardShape) { + return Arrays.stream(CardNumber.values()) + .map(cardNumber -> new Card(cardNumber, cardShape)); + } + + public void giveInitialCards(final Participant participant) { + for (int i = 0; i < INITIAL_CARD_NUMBER; i++) { + giveCard(participant); + } + } + + public void giveCard(final Participant participant) { + participant.addCard(this.draw()); + } + + private Card draw() { + if (deck.size() == 0) { + throw new IllegalStateException("카드가 존재하지 않습니다."); + } + return deck.pop(); + } +} diff --git a/src/main/java/blackjack/domain/cardgame/CardGameResult.java b/src/main/java/blackjack/domain/cardgame/CardGameResult.java new file mode 100644 index 00000000000..add5353f963 --- /dev/null +++ b/src/main/java/blackjack/domain/cardgame/CardGameResult.java @@ -0,0 +1,47 @@ +package blackjack.domain.cardgame; + +import blackjack.domain.player.Dealer; +import blackjack.domain.player.Name; +import blackjack.domain.player.Player; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static blackjack.domain.cardgame.WinningStatus.LOSE; +import static blackjack.domain.cardgame.WinningStatus.WIN; + +public record CardGameResult(Map totalResult) { + + public static CardGameResult of(final Dealer dealer, final List players) { + return players.stream() + .collect( + Collectors.collectingAndThen( + Collectors.toMap( + Player::name, + player -> WinningStatus.doesPlayerWin(dealer, player), + (x, y) -> x, + LinkedHashMap::new + ), CardGameResult::new)); + } + + public Map totalResult() { + return Collections.unmodifiableMap(totalResult); + } + + public int getDealerWinCount() { + return (int) totalResult.values() + .stream() + .filter(playerWinningStatus -> playerWinningStatus.equals(LOSE)) + .count(); + } + + public int getDealerLoseCount() { + return (int) totalResult.values() + .stream() + .filter(status -> status.equals(WIN)) + .count(); + } +} diff --git a/src/main/java/blackjack/domain/cardgame/WinningStatus.java b/src/main/java/blackjack/domain/cardgame/WinningStatus.java new file mode 100644 index 00000000000..3ccbc5bb267 --- /dev/null +++ b/src/main/java/blackjack/domain/cardgame/WinningStatus.java @@ -0,0 +1,30 @@ +package blackjack.domain.cardgame; + +import blackjack.domain.player.Dealer; +import blackjack.domain.player.Player; + +public enum WinningStatus { + WIN, + PUSH, + LOSE, + BLACKJACK; + + public static WinningStatus doesPlayerWin(final Dealer dealer, final Player player) { + if (player.isBlackjack()) { + return WinningStatus.BLACKJACK; + } + if (!player.isAlive()) { + return WinningStatus.LOSE; + } + if (!dealer.isAlive()) { + return WinningStatus.WIN; + } + if (dealer.score() == player.score()) { + return WinningStatus.PUSH; + } + if (dealer.score() < player.score()) { + return WinningStatus.WIN; + } + return WinningStatus.LOSE; + } +} diff --git a/src/main/java/blackjack/domain/player/Dealer.java b/src/main/java/blackjack/domain/player/Dealer.java new file mode 100644 index 00000000000..e2534085aa4 --- /dev/null +++ b/src/main/java/blackjack/domain/player/Dealer.java @@ -0,0 +1,27 @@ +package blackjack.domain.player; + +import blackjack.domain.card.Card; + +public class Dealer extends Participant { + private static final int HIT_CONDITION = 16; + + private Dealer(final Hand hand, final Name name) { + super(hand, name); + } + + public Dealer() { + this(new Hand(), new Name("딜러")); + } + + public boolean isMoreCardNeeded() { + return this.score() <= HIT_CONDITION; + } + + public Card getFirstCard() { + try { + return hand.getAllCards().get(0); + } catch (IndexOutOfBoundsException e) { + throw new RuntimeException("[ERROR] 딜러가 카드를 갖고 있지 않습니다."); + } + } +} diff --git a/src/main/java/blackjack/domain/player/Hand.java b/src/main/java/blackjack/domain/player/Hand.java new file mode 100644 index 00000000000..1d476e864e4 --- /dev/null +++ b/src/main/java/blackjack/domain/player/Hand.java @@ -0,0 +1,49 @@ +package blackjack.domain.player; + +import blackjack.domain.card.Card; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static blackjack.domain.player.Player.BUST_CONDITION; + +public class Hand { + private static final int BONUS_SCORE = 10; + private static final int NON_SCORE = 0; + private final List cards = new ArrayList<>(); + + Hand() { + } + + int getScore() { + final int minimumScore = cards.stream() + .mapToInt(Card::getScore) + .sum(); + final int bonusScore = this.getBonusScore(); + + if (minimumScore + bonusScore <= BUST_CONDITION) { + return minimumScore + bonusScore; + } + return minimumScore; + } + + private int getBonusScore() { + if (this.cards.stream().anyMatch(Card::isAceCard)) { + return BONUS_SCORE; + } + return NON_SCORE; + } + + void add(final Card card) { + cards.add(card); + } + + List getAllCards() { + return Collections.unmodifiableList(cards); + } + + public boolean hasExactlyTwoCards() { + return cards.size() == 2; + } +} diff --git a/src/main/java/blackjack/domain/player/Name.java b/src/main/java/blackjack/domain/player/Name.java new file mode 100644 index 00000000000..0490b19dd2d --- /dev/null +++ b/src/main/java/blackjack/domain/player/Name.java @@ -0,0 +1,33 @@ +package blackjack.domain.player; + +import java.util.Objects; + +public record Name(String value) { + private static final int MINIMUM_LENGTH = 1; + private static final int MAXIMUM_LENGTH = 10; + + public Name { + validateLength(value); + } + + private void validateLength(final String value) { + if (MINIMUM_LENGTH <= value.length() && value.length() <= MAXIMUM_LENGTH) { + return; + } + + final String errorMessage = String.format("[ERROR] 이름의 길이는 %s 이상, %s 이하여야 합니다.", + MINIMUM_LENGTH, MAXIMUM_LENGTH); + throw new IllegalArgumentException(errorMessage); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final Name name = (Name) o; + + return Objects.equals(value, name.value); + } + +} diff --git a/src/main/java/blackjack/domain/player/Participant.java b/src/main/java/blackjack/domain/player/Participant.java new file mode 100644 index 00000000000..91968f99ca9 --- /dev/null +++ b/src/main/java/blackjack/domain/player/Participant.java @@ -0,0 +1,37 @@ +package blackjack.domain.player; + +import blackjack.domain.card.Card; + +import java.util.List; + +public abstract class Participant { + static final int BUST_CONDITION = 21; + + protected final Hand hand; + private final Name name; + + protected Participant(final Hand hand, final Name name) { + this.hand = hand; + this.name = name; + } + + public final void addCard(final Card card) { + hand.add(card); + } + + public final boolean isAlive() { + return hand.getScore() <= BUST_CONDITION; + } + + public final int score() { + return hand.getScore(); + } + + public final List getCards() { + return hand.getAllCards(); + } + + public final Name name() { + return name; + } +} diff --git a/src/main/java/blackjack/domain/player/Player.java b/src/main/java/blackjack/domain/player/Player.java new file mode 100644 index 00000000000..7bb6595b4f7 --- /dev/null +++ b/src/main/java/blackjack/domain/player/Player.java @@ -0,0 +1,17 @@ +package blackjack.domain.player; + +public class Player extends Participant { + private static final int BLACKJACK_CONDITION = 21; + + private Player(final Hand hand, final Name name) { + super(hand, name); + } + + public Player(final Name name) { + this(new Hand(), name); + } + + public boolean isBlackjack() { + return hand.hasExactlyTwoCards() && hand.getScore() == BLACKJACK_CONDITION; + } +} diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java new file mode 100644 index 00000000000..6cc01589a36 --- /dev/null +++ b/src/main/java/blackjack/view/InputView.java @@ -0,0 +1,56 @@ +package blackjack.view; + +import blackjack.domain.betting.Money; +import blackjack.domain.player.Name; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import java.util.stream.Collectors; + +import static blackjack.view.PlayerChoice.HIT; +import static blackjack.view.PlayerChoice.STAND; + +public class InputView { + private static final Scanner scanner = new Scanner(System.in); + private static final String LINE_SEPARATOR = System.lineSeparator(); + + public List askPlayerNames() { + printAskingPlayerNamesInputMessage(); + String rawInput = scanner.nextLine(); + return Arrays.stream(rawInput.split(",", -1)) + .map(Name::new) + .collect(Collectors.toList()); + } + + public boolean askForMoreCard(Name name) { + printAskingForAnotherCardMessage(name); + String rawInput = scanner.nextLine(); + return PlayerChoice.isDrawable(rawInput); + } + + private void printAskingPlayerNamesInputMessage() { + String askingMessage = "게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"; + System.out.println(askingMessage); + } + + private void printAskingForAnotherCardMessage(Name name) { + String askingMessage = String.format("%s는 한장의 카드를 더 받겠습니까?", name.value()); + String exampleMessage = String.format("(예는 %s, 아니오는 %s)", HIT.getMessage(), STAND.getMessage()); + System.out.println(String.join("", LINE_SEPARATOR, askingMessage, exampleMessage)); + } + + public Money askBettingMoney(Name name) { + printAskingBettingMoneyMessage(name); + try { + return new Money(Integer.parseInt(scanner.nextLine())); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("돈의 액수는 숫자여야 합니다."); + } + } + + private void printAskingBettingMoneyMessage(Name name) { + String askingMessage = String.format("%s의 베팅 금액은?", name.value()); + System.out.println(String.join("", LINE_SEPARATOR, askingMessage)); + } +} diff --git a/src/main/java/blackjack/view/MessageResolver.java b/src/main/java/blackjack/view/MessageResolver.java new file mode 100644 index 00000000000..c8868bec011 --- /dev/null +++ b/src/main/java/blackjack/view/MessageResolver.java @@ -0,0 +1,134 @@ +package blackjack.view; + +import blackjack.domain.betting.Profit; +import blackjack.domain.betting.ProfitDetails; +import blackjack.domain.card.Card; +import blackjack.domain.card.CardNumber; +import blackjack.domain.card.CardShape; +import blackjack.domain.player.Dealer; +import blackjack.domain.player.Name; +import blackjack.domain.player.Participant; +import blackjack.domain.player.Player; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static blackjack.domain.card.CardNumber.*; +import static blackjack.domain.card.CardShape.CLOVER; +import static blackjack.domain.card.CardShape.DIAMOND; +import static blackjack.domain.card.CardShape.HEART; +import static blackjack.domain.card.CardShape.SPADE; + +public class MessageResolver { + private static final Map CARD_SHAPE_NAME_MAP = Map.of( + SPADE, "스페이드", + HEART, "하트", + DIAMOND, "다이아몬드", + CLOVER, "클로버" + ); + + private static final Map CARD_NUMBER_NAME_MAP = Map.ofEntries( + Map.entry(ACE, "A"), Map.entry(TWO, "2"), + Map.entry(THREE, "3"), Map.entry(FOUR, "4"), + Map.entry(FIVE, "5"), Map.entry(SIX, "6"), + Map.entry(SEVEN, "7"), Map.entry(EIGHT, "8"), + Map.entry(NINE, "9"), Map.entry(TEN, "10"), + Map.entry(JACK, "J"), Map.entry(QUEEN, "Q"), + Map.entry(KING, "K") + ); + + private static final String LINE_SEPARATOR = System.lineSeparator(); + + public String resolveInitialHand(Dealer dealer, List players) { + String initialDistributionMessage = resolveInitialDistributionMessage(dealer, players); + String dealerCardMessage = resolveCard(dealer); + String playersCardMessage = players.stream() + .map(this::resolveCard) + .collect(Collectors.joining(LINE_SEPARATOR)); + return String.join(LINE_SEPARATOR, initialDistributionMessage, dealerCardMessage, playersCardMessage); + } + + private String resolveInitialDistributionMessage(Dealer dealer, List players) { + String dealerNameMessage = resolveName(dealer); + String playerNamesMessage = resolvePlayerNames(players); + String message = String.format("%s와 %s에게 2장을 나누었습니다.", dealerNameMessage, playerNamesMessage); + return String.join("", LINE_SEPARATOR, message); + } + + private String resolvePlayerNames(List players) { + return players.stream() + .map(this::resolveName) + .collect(Collectors.joining(", ")); + } + + private String resolveCard(Dealer dealer) { + Card card = dealer.getFirstCard(); + return String.format("%s: %s", resolveName(dealer.name()), resolveCardDetail(card)); + } + + public String resolveCard(Player player) { + String cardDetailMessage = player.getCards().stream() + .map(this::resolveCardDetail) + .collect(Collectors.joining(", ")); + return String.format("%s: %s", resolveName(player.name()), cardDetailMessage); + } + + public String resolveDealerHitMessage(Dealer dealer) { + String dealerHitMessage = String.format("%s는 16이하라 한장의 카드를 더 받았습니다.", resolveName(dealer)); + return String.join("", LINE_SEPARATOR, dealerHitMessage, LINE_SEPARATOR); + } + + public String resolveParticipantCardWithScore(Participant participant) { + String cardDetails = resolveCardDetails(participant.getCards(), participant.name()); + return String.format("%s - 결과: %d", cardDetails, participant.score()); + } + + private String resolveCardDetails(List cards, Name name) { + String cardsInfo = cards.stream() + .map(this::resolveCardDetail) + .collect(Collectors.joining(", ")); + return String.format("%s카드: %s", resolveName(name), cardsInfo); + } + + private String resolveCardDetail(Card card) { + return CARD_NUMBER_NAME_MAP.get(card.getNumber()) + CARD_SHAPE_NAME_MAP.get(card.getShape()); + } + + public String resolvePlayersProfitDetail(ProfitDetails profits) { + String prefixMessage = LINE_SEPARATOR + "## 최종 수익"; + String dealerProfitDetailsMessage = resolveDealerProfitDetail(profits.getDealerProfit()); + String playersProfitDetailsMessage = profits.details().entrySet().stream() + .map(nameAndProfit -> resolveProfitDetail(extractName(nameAndProfit), extractProfit(nameAndProfit))) + .collect(Collectors.joining(LINE_SEPARATOR)); + return String.join(LINE_SEPARATOR, prefixMessage, dealerProfitDetailsMessage, playersProfitDetailsMessage); + } + + private String resolveDealerProfitDetail(Profit profit) { + return String.format("딜러: %d", resolveProfit(profit)); + } + + private String resolveProfitDetail(Name name, Profit profit) { + return String.format("%s: %d", resolveName(name), resolveProfit(profit)); + } + + private Name extractName(final Map.Entry nameAndProfit) { + return nameAndProfit.getKey(); + } + + private Profit extractProfit(final Map.Entry nameAndProfit) { + return nameAndProfit.getValue(); + } + + private String resolveName(Name name) { + return name.value(); + } + + private String resolveName(Participant player) { + return player.name().value(); + } + + private int resolveProfit(Profit profit) { + return profit.value(); + } +} diff --git a/src/main/java/blackjack/view/OutputView.java b/src/main/java/blackjack/view/OutputView.java new file mode 100644 index 00000000000..de49b1bdc95 --- /dev/null +++ b/src/main/java/blackjack/view/OutputView.java @@ -0,0 +1,36 @@ +package blackjack.view; + +import blackjack.domain.betting.ProfitDetails; +import blackjack.domain.player.Dealer; +import blackjack.domain.player.Participant; +import blackjack.domain.player.Player; + +import java.util.List; + +public class OutputView { + private final MessageResolver messageResolver; + + public OutputView(MessageResolver messageResolver) { + this.messageResolver = messageResolver; + } + + public void printInitialHandOfEachPlayer(Dealer dealer, List players) { + System.out.println(messageResolver.resolveInitialHand(dealer, players)); + } + + public void printPlayerCard(Player player) { + System.out.println(messageResolver.resolveCard(player)); + } + + public void printDealerHitMessage(Dealer dealer) { + System.out.println(messageResolver.resolveDealerHitMessage(dealer)); + } + + public void printParticipantCardWithScore(Participant participant) { + System.out.println(messageResolver.resolveParticipantCardWithScore(participant)); + } + + public void printPlayerProfit(ProfitDetails profits) { + System.out.println(messageResolver.resolvePlayersProfitDetail(profits)); + } +} diff --git a/src/main/java/blackjack/view/PlayerChoice.java b/src/main/java/blackjack/view/PlayerChoice.java new file mode 100644 index 00000000000..dcca0596ec3 --- /dev/null +++ b/src/main/java/blackjack/view/PlayerChoice.java @@ -0,0 +1,28 @@ +package blackjack.view; + +public enum PlayerChoice { + HIT("y"), + STAND("n"); + + private final String message; + + PlayerChoice(final String message) { + this.message = message; + } + + static boolean isDrawable(final String choice) { + if (HIT.message.equals(choice)) { + return true; + } + if (STAND.message.equals(choice)) { + return false; + } + + final String errorMessage = String.format("%s 또는 %s 만 입력할 수 있습니다.", HIT.message, STAND.message); + throw new IllegalArgumentException(errorMessage); + } + + String getMessage() { + return message; + } +} diff --git a/src/test/java/blackjack/domain/betting/MoneyTest.java b/src/test/java/blackjack/domain/betting/MoneyTest.java new file mode 100644 index 00000000000..03bb4edc15f --- /dev/null +++ b/src/test/java/blackjack/domain/betting/MoneyTest.java @@ -0,0 +1,46 @@ +package blackjack.domain.betting; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class MoneyTest { + @ParameterizedTest + @ValueSource(ints = {0, 1, 100000000}) + void 돈이_정상적으로_생성된다(final int value) { + assertThatCode(() -> new Money(value)) + .doesNotThrowAnyException(); + } + + @Test + void 음수의_돈은_만들어지지_않는다() { + assertThatThrownBy(() -> new Money(-1)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("돈의 액수는 0 이상이어야 합니다."); + } + + @Test + void 돈의_액수를_음수로_반환한다() { + Money money = new Money(1); + + assertThat(money.minusValue()).isEqualTo(-1); + } + + @Test + void 돈의_액수에_곱한_값을_반환한다() { + Money money = new Money(2); + + assertThat(money.multipleValue(1.5)).isEqualTo(3); + } + + @Test + void 돈의_액수가_홀수면_곱하고_1의_자리를_반환한다() { + Money money = new Money(1); + + assertThat(money.multipleValue(1.5)).isEqualTo(1); + } +} diff --git a/src/test/java/blackjack/domain/betting/ProfitDetailsTest.java b/src/test/java/blackjack/domain/betting/ProfitDetailsTest.java new file mode 100644 index 00000000000..b5720c76f8b --- /dev/null +++ b/src/test/java/blackjack/domain/betting/ProfitDetailsTest.java @@ -0,0 +1,36 @@ +package blackjack.domain.betting; + +import blackjack.domain.player.Name; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class ProfitDetailsTest { + + @Test + void 딜러의_수익이_음수다() { + Map profits = Map.of( + new Name("PlayerA"), new Profit(1000), + new Name("PlayerB"), new Profit(2000), + new Name("PlayerC"), new Profit(3000) + ); + ProfitDetails profitDetails = new ProfitDetails(profits); + + assertThat(profitDetails.getDealerProfit()).isEqualTo(new Profit(-6000)); + } + + @Test + void 딜러의_수익이_양수다() { + Map profits = Map.of( + new Name("PlayerA"), new Profit(1000), + new Name("PlayerB"), new Profit(-2000), + new Name("PlayerC"), new Profit(-3000) + ); + ProfitDetails profitDetails = new ProfitDetails(profits); + + assertThat(profitDetails.getDealerProfit()).isEqualTo(new Profit(4000)); + } + +} diff --git a/src/test/java/blackjack/domain/betting/ProfitTest.java b/src/test/java/blackjack/domain/betting/ProfitTest.java new file mode 100644 index 00000000000..dba5b2efe2c --- /dev/null +++ b/src/test/java/blackjack/domain/betting/ProfitTest.java @@ -0,0 +1,44 @@ +package blackjack.domain.betting; + +import blackjack.domain.cardgame.WinningStatus; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ProfitTest { + @Test + void 블랙잭이_아니고_이긴_경우의_수익률은_베팅_금액과_동일하다() { + Money money = new Money(1000); + WinningStatus status = WinningStatus.WIN; + Profit profit = Profit.of(money, status); + + assertThat(profit).isEqualTo(new Profit(1000)); + } + + @Test + void 진_경우에_수익률은_베팅_금액의_마이너스_값이다() { + Money money = new Money(1000); + WinningStatus status = WinningStatus.LOSE; + Profit profit = Profit.of(money, status); + + assertThat(profit).isEqualTo(new Profit(-1000)); + } + + @Test + void 비긴_경우에_수익률은_0이다() { + Money money = new Money(1000); + WinningStatus status = WinningStatus.PUSH; + Profit profit = Profit.of(money, status); + + assertThat(profit).isEqualTo(new Profit(0)); + } + + @Test + void 블랙잭인_경우에_수익률은_베팅_금액에_150퍼센트이다() { + Money money = new Money(1000); + WinningStatus status = WinningStatus.BLACKJACK; + Profit profit = Profit.of(money, status); + + assertThat(profit).isEqualTo(new Profit(1500)); + } +} diff --git a/src/test/java/blackjack/domain/card/CardTest.java b/src/test/java/blackjack/domain/card/CardTest.java new file mode 100644 index 00000000000..4ee68e37d4b --- /dev/null +++ b/src/test/java/blackjack/domain/card/CardTest.java @@ -0,0 +1,24 @@ +package blackjack.domain.card; + +import org.junit.jupiter.api.Test; + +import static blackjack.domain.card.CardNumber.ACE; +import static blackjack.domain.card.CardNumber.TWO; +import static blackjack.domain.card.CardShape.HEART; +import static org.assertj.core.api.Assertions.assertThat; + +public class CardTest { + @Test + void 에이스_카드인지_확인한다() { + Card aceCard = new Card(ACE, HEART); + + assertThat(aceCard.isAceCard()).isTrue(); + } + + @Test + void 에이스_카드가_아닌지_확인한다() { + Card notAceCard = new Card(TWO, HEART); + + assertThat(notAceCard.isAceCard()).isFalse(); + } +} diff --git a/src/test/java/blackjack/domain/cardgame/CardDeckTest.java b/src/test/java/blackjack/domain/cardgame/CardDeckTest.java new file mode 100644 index 00000000000..2b736166086 --- /dev/null +++ b/src/test/java/blackjack/domain/cardgame/CardDeckTest.java @@ -0,0 +1,44 @@ +package blackjack.domain.cardgame; + +import blackjack.domain.player.Player; +import org.junit.jupiter.api.Test; + +import static blackjack.fixture.PlayerFixture.player; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +public class CardDeckTest { + @Test + void 덱에_정확히_52장의_카드가_존재한다() { + CardDeck deck = CardDeck.createShuffledDeck(); + Player player = player(); + + for (int ignored = 0; ignored < 52; ignored++) { + deck.giveCard(player); + } + + assertThatThrownBy(() -> deck.giveCard(player)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("카드가 존재하지 않습니다."); + } + + @Test + void 카드_한_장을_플레이어에게_지급한다() { + CardDeck deck = CardDeck.createShuffledDeck(); + Player player = player(); + + deck.giveCard(player); + + assertThat(player.getCards().size()).isEqualTo(1); + } + + @Test + void 플레이어에게_최초_2개의_카드를_지급한다() { + CardDeck deck = CardDeck.createShuffledDeck(); + Player player = player(); + + deck.giveInitialCards(player); + + assertThat(player.getCards().size()).isEqualTo(2); + } +} diff --git a/src/test/java/blackjack/domain/cardgame/CardGameResultTest.java b/src/test/java/blackjack/domain/cardgame/CardGameResultTest.java new file mode 100644 index 00000000000..ceb48ea023e --- /dev/null +++ b/src/test/java/blackjack/domain/cardgame/CardGameResultTest.java @@ -0,0 +1,142 @@ +package blackjack.domain.cardgame; + +import blackjack.domain.card.Card; +import blackjack.domain.player.Dealer; +import blackjack.domain.player.Name; +import blackjack.domain.player.Player; +import org.junit.jupiter.api.Test; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static blackjack.domain.card.CardNumber.ACE; +import static blackjack.domain.card.CardNumber.KING; +import static blackjack.domain.card.CardNumber.QUEEN; +import static blackjack.domain.card.CardNumber.TWO; +import static blackjack.domain.card.CardShape.CLOVER; +import static blackjack.domain.card.CardShape.DIAMOND; +import static blackjack.domain.card.CardShape.HEART; +import static blackjack.domain.card.CardShape.SPADE; +import static blackjack.domain.cardgame.WinningStatus.LOSE; +import static blackjack.domain.cardgame.WinningStatus.WIN; +import static blackjack.fixture.PlayerFixture.dealer; +import static blackjack.fixture.PlayerFixture.player; +import static org.assertj.core.api.Assertions.assertThat; + +class CardGameResultTest { + private static final Name nameA = new Name("nameA"); + private static final Name nameB = new Name("nameB"); + private static final Name nameC = new Name("nameC"); + private static final Name nameD = new Name("nameD"); + + @Test + void 딜러가_승패_횟수를_계산할_수_있다() { + Map result = new LinkedHashMap<>(); + result.put(nameA, WIN); + result.put(nameB, WIN); + result.put(nameC, LOSE); + + CardGameResult cardGameResult = new CardGameResult(result); + + int dealerWinCount = cardGameResult.getDealerWinCount(); + int dealerLoseCount = cardGameResult.getDealerLoseCount(); + + assertThat(dealerWinCount).isEqualTo(1); + assertThat(dealerLoseCount).isEqualTo(2); + } + + @Test + void 딜러와_플레이어_둘다_21을_초과할_경우에_플레이어가_패배한다() { + Player player = player( + nameA, + new Card(KING, CLOVER), + new Card(KING, HEART), + new Card(KING, SPADE)); + Dealer dealer = dealer( + new Card(QUEEN, CLOVER), + new Card(QUEEN, HEART), + new Card(QUEEN, SPADE)); + + var result = CardGameResult.of(dealer, List.of(player)) + .totalResult(); + + assertThat(result.get(player.name())).isEqualTo(WinningStatus.LOSE); + } + + @Test + void 딜러와_여러_플레이어의_숫자가_21_이하인_경우_숫자가_큰_사람이_이긴다() { + Player player = player(nameA, new Card(KING, SPADE)); + Dealer dealer = dealer(new Card(TWO, SPADE)); + + var result = CardGameResult.of(dealer, List.of(player)) + .totalResult(); + + assertThat(result.get(player.name())).isEqualTo(WinningStatus.WIN); + } + + @Test + void 카드_합계가_딜러는_21_이하_플레이어는_21_초과인_경우_플레이어가_패배한다() { + Player player = player( + nameA, + new Card(KING, CLOVER), + new Card(KING, HEART), + new Card(KING, DIAMOND)); + Dealer dealer = dealer(new Card(TWO, HEART)); + + var result = CardGameResult.of(dealer, List.of(player)) + .totalResult(); + + assertThat(result.get(player.name())).isEqualTo(WinningStatus.LOSE); + } + + @Test + void 카드_합계가_딜러는_21_초과_플레이어는_21_이하인_경우_플레이어가_승리한다() { + Player player = player(nameA, new Card(TWO, HEART)); + Dealer dealer = dealer( + new Card(KING, CLOVER), + new Card(KING, HEART), + new Card(KING, SPADE)); + + var result = CardGameResult.of(dealer, List.of(player)) + .totalResult(); + + assertThat(result.get(player.name())).isEqualTo(WinningStatus.WIN); + } + + @Test + void 카드_합계가_딜러와_플레이어_모두_21_이하인_경우_숫자가_큰_사람이_승리한다() { + Player player = player(nameA, new Card(ACE, HEART)); + Dealer dealer = dealer(new Card(KING, SPADE)); + + var result = CardGameResult.of(dealer, List.of(player)) + .totalResult(); + + assertThat(result.get(player.name())).isEqualTo(WinningStatus.WIN); + } + + @Test + void 카드_합계가_딜러와_플레이어_모두_21_이하이고_동일한_경우_무승부다() { + Player player = player(nameA, new Card(ACE, HEART)); + Dealer dealer = dealer(new Card(ACE, HEART)); + + var result = CardGameResult.of(dealer, List.of(player)) + .totalResult(); + + assertThat(result.get(nameA)).isEqualTo(WinningStatus.PUSH); + } + + @Test + void 게임_결과에서_플레이어가_순서를_유지하고_있다() { + Player playerA = player(nameA, new Card(TWO, HEART)); + Player playerB = player(nameB, new Card(TWO, SPADE)); + Player playerC = player(nameC, new Card(TWO, CLOVER)); + Player playerD = player(nameD, new Card(TWO, DIAMOND)); + Dealer dealer = dealer(new Card(ACE, HEART)); + + var result = CardGameResult.of(dealer, List.of(playerA, playerB, playerC, playerD)) + .totalResult(); + + assertThat(result.keySet()).containsExactly(nameA, nameB, nameC, nameD); + } +} diff --git a/src/test/java/blackjack/domain/player/DealerTest.java b/src/test/java/blackjack/domain/player/DealerTest.java new file mode 100644 index 00000000000..1b5865971f7 --- /dev/null +++ b/src/test/java/blackjack/domain/player/DealerTest.java @@ -0,0 +1,49 @@ +package blackjack.domain.player; + +import blackjack.domain.card.Card; +import org.junit.jupiter.api.Test; + +import static blackjack.domain.card.CardNumber.ACE; +import static blackjack.domain.card.CardNumber.KING; +import static blackjack.domain.card.CardNumber.SEVEN; +import static blackjack.domain.card.CardNumber.SIX; +import static blackjack.domain.card.CardShape.SPADE; +import static blackjack.fixture.PlayerFixture.dealer; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class DealerTest { + @Test + void 합계가_16이하라면_카드가_더_필요하다() { + Dealer dealer = dealer( + new Card(KING, SPADE), + new Card(SIX, SPADE)); + + assertThat(dealer.isMoreCardNeeded()).isTrue(); + } + + @Test + void 합계가_16보다_크다면_카드가_더_필요하다() { + Dealer dealer = dealer( + new Card(KING, SPADE), + new Card(SEVEN, SPADE)); + + assertThat(dealer.isMoreCardNeeded()).isFalse(); + } + + @Test + void 딜러에게_카드가_있는_경우에_첫_카드를_요청하면_정상적으로_돌려준다() { + Dealer dealer = dealer(new Card(ACE, SPADE)); + + assertThat(dealer.getFirstCard()).isNotNull(); + } + + @Test + void 딜러에게_카드가_없는_경우에_첫_카드를_요청하면_예외가_발생한다() { + Dealer dealer = dealer(); + + assertThatThrownBy(dealer::getFirstCard) + .isInstanceOf(RuntimeException.class) + .hasMessage("[ERROR] 딜러가 카드를 갖고 있지 않습니다."); + } +} diff --git a/src/test/java/blackjack/domain/player/HandTest.java b/src/test/java/blackjack/domain/player/HandTest.java new file mode 100644 index 00000000000..ee47036d679 --- /dev/null +++ b/src/test/java/blackjack/domain/player/HandTest.java @@ -0,0 +1,49 @@ +package blackjack.domain.player; + +import blackjack.domain.card.Card; +import org.junit.jupiter.api.Test; + +import static blackjack.domain.card.CardNumber.ACE; +import static blackjack.domain.card.CardNumber.JACK; +import static blackjack.domain.card.CardNumber.KING; +import static blackjack.domain.card.CardNumber.QUEEN; +import static blackjack.domain.card.CardShape.SPADE; +import static org.assertj.core.api.Assertions.assertThat; + +public class HandTest { + @Test + void 가진_패의_숫자의_합계를_구할_수_있다() { + Hand hand = new Hand(); + hand.add(new Card(JACK, SPADE)); + hand.add(new Card(QUEEN, SPADE)); + hand.add(new Card(KING, SPADE)); + + int sum = hand.getScore(); + + assertThat(sum).isEqualTo(30); + } + + @Test + void 에이스_카드가_4개인_경우에_합계를_구할_수_있다() { + Hand hand = new Hand(); + hand.add(new Card(ACE, SPADE)); + hand.add(new Card(ACE, SPADE)); + hand.add(new Card(ACE, SPADE)); + hand.add(new Card(ACE, SPADE)); + + int sum = hand.getScore(); + + assertThat(sum).isEqualTo(14); + } + + @Test + void 에이스_카드를_포함해서_합계_21인_경우에_정확하게_계산할_수_있다() { + Hand hand = new Hand(); + hand.add(new Card(ACE, SPADE)); + hand.add(new Card(KING, SPADE)); + + int sum = hand.getScore(); + + assertThat(sum).isEqualTo(21); + } +} diff --git a/src/test/java/blackjack/domain/player/NameTest.java b/src/test/java/blackjack/domain/player/NameTest.java new file mode 100644 index 00000000000..f19063006a6 --- /dev/null +++ b/src/test/java/blackjack/domain/player/NameTest.java @@ -0,0 +1,15 @@ +package blackjack.domain.player; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class NameTest { + @ParameterizedTest + @ValueSource(strings = {"", "01234567890"}) + void 이름의_길이는_1이상_10이하여야_한다(String value) { + assertThatThrownBy(() -> new Name(value)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/blackjack/domain/player/PlayerTest.java b/src/test/java/blackjack/domain/player/PlayerTest.java new file mode 100644 index 00000000000..cfa54ae0953 --- /dev/null +++ b/src/test/java/blackjack/domain/player/PlayerTest.java @@ -0,0 +1,25 @@ +package blackjack.domain.player; + +import blackjack.domain.card.Card; +import org.junit.jupiter.api.Test; + +import static blackjack.domain.card.CardNumber.KING; +import static blackjack.domain.card.CardShape.CLOVER; +import static blackjack.domain.card.CardShape.HEART; +import static blackjack.domain.card.CardShape.SPADE; +import static blackjack.fixture.PlayerFixture.player; +import static org.assertj.core.api.Assertions.assertThat; + +public class PlayerTest { + @Test + void 플레이어는_죽었는지_여부를_반환한다() { + Player player = player( + new Card(KING, CLOVER), + new Card(KING, SPADE), + new Card(KING, HEART)); + + boolean isAlive = player.isAlive(); + + assertThat(isAlive).isFalse(); + } +} diff --git a/src/test/java/blackjack/fixture/PlayerFixture.java b/src/test/java/blackjack/fixture/PlayerFixture.java new file mode 100644 index 00000000000..ee7b7abd9b1 --- /dev/null +++ b/src/test/java/blackjack/fixture/PlayerFixture.java @@ -0,0 +1,32 @@ +package blackjack.fixture; + +import blackjack.domain.card.Card; +import blackjack.domain.player.Dealer; +import blackjack.domain.player.Name; +import blackjack.domain.player.Player; + +public class PlayerFixture { + public static Player player(Card... cards) { + Player player = new Player(new Name("player")); + for (Card card : cards) { + player.addCard(card); + } + return player; + } + + public static Player player(Name name, Card... cards) { + Player player = new Player(name); + for (Card card : cards) { + player.addCard(card); + } + return player; + } + + public static Dealer dealer(Card... cards) { + Dealer dealer = new Dealer(); + for (Card card : cards) { + dealer.addCard(card); + } + return dealer; + } +}