diff --git a/README.md b/README.md index 556099c4de3..10d729dcc75 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,79 @@ ## 우아한테크코스 코드리뷰 - [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) + +```mermaid +graph TD +의존성그래프 +BlackJackGame --> Players +BlackJackGame --> Deck +BlackJackGame --> Dealer + +Players --> Dealer +Dealer --> Player + +Participant --> CardPocket +Dealer --> Participant +Player --> Participant + +Players --> Player +Player --> Name +CardPocket --> Card + +Deck --> Card +Card --> Shape +Card --> Symbol +``` + +# 기능구현 목록 + +## UI + +1. 입력 + +- 참여할 사람을 입력한다 +- List으로 변환 및 반환한다 +- + +## 도메인 + +1. Deck + - [x] 카드를 생성한다 + - [x] 총 52장 + - [x] Shape마다 13장을 출력 + - [x] 카드를 나눠준다 + +2. BlackJack + - 딜러와 플레이어에게 카드를 2장씩 나눠준다. + -- 카드를 나눠준다 +3. Participant + - [x] 21 이상인지 확인하고 + - [x] 21 이상이라면, + - [x] 블랙잭인지 확인한다 = 받을 수 없음 + - [x] BURST인지 확인한다 = 받을 수 없음4 + - [x] 현재 점수를 숫자 형태로 반환한다 + - [x] 현재 카드를 반환한다 +4. Player + - [x] 21 미만이면, 받을 수 있다는 boolean +5. Dealer + - [x] 16 이하이면, 받을 수 있따는 boolean +6. CardPocket + - [x] 카드의 Symbol 점수를 계산한다.(ScoreCalculator 역할) + - 카드반환 한다 + - [x] 카드 계산 총합 반환 +7. Shape + - [x] enum 형태로 하트, 스페이스, 클로버, 다이아를 저장한다 +8. Symbol + - [x] a=1 2-9, j,q,k =10 + - [x] value는 int 형으로 저장한다 +9. Card + - [x] Shape와 Symbol을 저장하는 자료구조 + +10. Players + - [x] 플레이어 이름 중복 + - [x] 플레이어 수 1명이상, 5명 이하 + - [x] 플레이어에게 카드 나눠주기 + - [x] 플레이어의 draw여부 알기 +11. Name + - [x] isBlank 체크 + - [x] 100자 이하 체크 diff --git a/src/main/java/blackjack/Application.java b/src/main/java/blackjack/Application.java new file mode 100644 index 00000000000..863b6b26f8c --- /dev/null +++ b/src/main/java/blackjack/Application.java @@ -0,0 +1,14 @@ +package blackjack; + +import blackjack.controller.BlackJackController; +import blackjack.domain.card.ShuffledDeckFactory; +import blackjack.view.InputView; +import blackjack.view.OutputView; + +public class Application { + + public static void main(final String[] args) { + final BlackJackController blackJackController = new BlackJackController(new InputView(), new OutputView()); + blackJackController.play(new ShuffledDeckFactory()); + } +} diff --git a/src/main/java/blackjack/controller/BlackJackController.java b/src/main/java/blackjack/controller/BlackJackController.java new file mode 100644 index 00000000000..8022fb03f38 --- /dev/null +++ b/src/main/java/blackjack/controller/BlackJackController.java @@ -0,0 +1,63 @@ +package blackjack.controller; + +import static blackjack.util.Repeater.repeatUntilNoException; + +import blackjack.domain.card.DeckFactory; +import blackjack.service.BlackJackGame; +import blackjack.view.DrawCommand; +import blackjack.view.InputView; +import blackjack.view.OutputView; +import java.util.List; + +public class BlackJackController { + + private final InputView inputView; + private final OutputView outputView; + + public BlackJackController(final InputView inputView, final OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void play(final DeckFactory deckFactory) { + final BlackJackGame blackJackGame = repeatUntilNoException( + () -> BlackJackGame.of( + inputPlayerNames(), + deckFactory), + outputView::printError); + + blackJackGame.distributeInitialCard(); + outputView.printInitialCards(blackJackGame.getDealerFirstCard(), blackJackGame.getPlayerCards()); + + for (final String playerName : blackJackGame.getPlayerNames()) { + DrawCommand playerChoice = DrawCommand.DRAW; + while (blackJackGame.isPlayerDrawable(playerName) && playerChoice != DrawCommand.STAY) { + playerChoice = inputPlayerChoice(playerName); + if (playerChoice == DrawCommand.DRAW) { + blackJackGame.drawPlayerCard(playerName); + } + outputView.printCardStatusOfPlayer(playerName, blackJackGame.getPlayerCardsResponse(playerName)); + } + } + + while (blackJackGame.isDealerDrawable()) { + blackJackGame.drawDealerCard(); + outputView.printDealerCardDrawMessage(); + } + + outputView.printFinalStatusOfDealer(blackJackGame.getDealerScore(), blackJackGame.getDealerCardsResponse()); + outputView.printFinalStatusOfPlayers(blackJackGame.getPlayerCards(), blackJackGame.getPlayerScores()); + outputView.printFinalDealerResult(blackJackGame.getDealerResult()); + outputView.printFinalPlayersResult(blackJackGame.generatePlayersResult()); + } + + private DrawCommand inputPlayerChoice(final String playerName) { + return repeatUntilNoException( + () -> inputView.inputCommand(playerName), outputView::printError); + } + + private List inputPlayerNames() { + return inputView.inputPlayerNames(); + } + +} diff --git a/src/main/java/blackjack/domain/BlackJackRule.java b/src/main/java/blackjack/domain/BlackJackRule.java new file mode 100644 index 00000000000..d85a0b014dc --- /dev/null +++ b/src/main/java/blackjack/domain/BlackJackRule.java @@ -0,0 +1,9 @@ +package blackjack.domain; + +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; + +public interface BlackJackRule { + + ResultType calculateDealerResult(Dealer dealer, Player player); +} diff --git a/src/main/java/blackjack/domain/BlackJackRuleImpl.java b/src/main/java/blackjack/domain/BlackJackRuleImpl.java new file mode 100644 index 00000000000..c9ff65b9b9f --- /dev/null +++ b/src/main/java/blackjack/domain/BlackJackRuleImpl.java @@ -0,0 +1,39 @@ +package blackjack.domain; + +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; + +public class BlackJackRuleImpl implements BlackJackRule { + + private static final int BUST_POINT = 21; + + @Override + public ResultType calculateDealerResult(final Dealer dealer, final Player player) { + final int dealerScore = dealer.currentScore(); + final int playerScore = player.currentScore(); + if (isTie(dealerScore, playerScore)) { + return ResultType.TIE; + } + if (isDealerWin(dealerScore, playerScore)) { + return ResultType.WIN; + } + return ResultType.LOSE; + } + + private boolean isTie(final int dealerScore, final int playerScore) { + if (playerScore > BUST_POINT && dealerScore > BUST_POINT) { + return true; + } + return playerScore == dealerScore; + } + + private boolean isDealerWin(final int dealerScore, final int playerScore) { + if (playerScore > BUST_POINT) { + return true; + } + if (dealerScore > BUST_POINT) { + return false; + } + return dealerScore > playerScore; + } +} diff --git a/src/main/java/blackjack/domain/ResultType.java b/src/main/java/blackjack/domain/ResultType.java new file mode 100644 index 00000000000..ef71e53ad46 --- /dev/null +++ b/src/main/java/blackjack/domain/ResultType.java @@ -0,0 +1,27 @@ +package blackjack.domain; + +public enum ResultType { + WIN("승"), + TIE("무"), + LOSE("패"); + + private final String name; + + ResultType(final String name) { + this.name = name; + } + + public ResultType getOppositeResult() { + if (this == WIN) { + return LOSE; + } + if (this == TIE) { + return TIE; + } + return WIN; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/blackjack/domain/ResultType.java~ b/src/main/java/blackjack/domain/ResultType.java~ new file mode 100644 index 00000000000..d2a14809646 --- /dev/null +++ b/src/main/java/blackjack/domain/ResultType.java~ @@ -0,0 +1,17 @@ +package blackjack.domain; + +public enum ResultType { + WIN("승"), + TIE("무"), + LOSE("패"); + + public ResultType getOppositeResult() { + if (this == WIN) { + return LOSE; + } + if (this == TIE) { + return TIE; + } + return WIN; + } +} 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..5134c1f77bd --- /dev/null +++ b/src/main/java/blackjack/domain/card/Card.java @@ -0,0 +1,28 @@ +package blackjack.domain.card; + +public class Card { + + private final Shape shape; + private final Symbol symbol; + + public Card(final Shape shape, final Symbol symbol) { + this.shape = shape; + this.symbol = symbol; + } + + boolean isAce() { + return symbol.isAce(); + } + + int getScore() { + return symbol.getScore(); + } + + public Symbol getSymbol() { + return symbol; + } + + public Shape getShape() { + return shape; + } +} diff --git a/src/main/java/blackjack/domain/card/CardPocket.java b/src/main/java/blackjack/domain/card/CardPocket.java new file mode 100644 index 00000000000..c9906657ae0 --- /dev/null +++ b/src/main/java/blackjack/domain/card/CardPocket.java @@ -0,0 +1,62 @@ +package blackjack.domain.card; + +import java.util.ArrayList; +import java.util.List; + +public class CardPocket { + + private static final int BUST_SCORE = 21; + private static final int VALUE_ACE = 10; + private final List cards; + + private CardPocket(final List cards) { + validateCardPocket(cards); + this.cards = new ArrayList<>(cards); + } + + public static CardPocket empty() { + return new CardPocket(new ArrayList<>()); + } + + private void validateCardPocket(final List cards) { + if (cards == null) { + throw new IllegalArgumentException("카드에 null 값이 들어갈 수 없습니다"); + } + } + + public void addCard(final Card card) { + cards.add(card); + } + + public int calculateScore() { + final int countOfAce = countAce(); + int scoreOfCards = calculateMinimumScore(); + for (int i = 0; i < countOfAce; i++) { + scoreOfCards = calculateAceScore(scoreOfCards); + } + return scoreOfCards; + } + + private int countAce() { + return (int) cards.stream() + .filter(Card::isAce) + .count(); + } + + private int calculateMinimumScore() { + return cards.stream() + .mapToInt(Card::getScore) + .sum(); + } + + private int calculateAceScore(final int score) { + if (score + VALUE_ACE > BUST_SCORE) { + return score; + } + return score + VALUE_ACE; + } + + public List getCards() { + return List.copyOf(cards); + } +} diff --git a/src/main/java/blackjack/domain/card/Deck.java b/src/main/java/blackjack/domain/card/Deck.java new file mode 100644 index 00000000000..981601cf776 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Deck.java @@ -0,0 +1,32 @@ +package blackjack.domain.card; + +import java.util.Queue; + +public class Deck { + + private static final int NUMBER_OF_CARDS_IN_DECK = 52; + + private final Queue cards; + + Deck(final Queue cards) { + validateCards(cards); + this.cards = cards; + } + + private void validateCards(final Queue cards) { + if (cards == null) { + throw new IllegalArgumentException("카드에 null 이 들어왔습니다"); + } + if (cards.size() != NUMBER_OF_CARDS_IN_DECK) { + throw new IllegalArgumentException( + "카드 숫자는 " + NUMBER_OF_CARDS_IN_DECK + "장이어야 합니다 현재 :" + cards.size() + "장"); + } + } + + public Card popCard() { + if (cards.isEmpty()) { + throw new IllegalArgumentException("덱에 카드가 없습니다"); + } + return cards.remove(); + } +} diff --git a/src/main/java/blackjack/domain/card/DeckFactory.java b/src/main/java/blackjack/domain/card/DeckFactory.java new file mode 100644 index 00000000000..3ca5497cae1 --- /dev/null +++ b/src/main/java/blackjack/domain/card/DeckFactory.java @@ -0,0 +1,6 @@ +package blackjack.domain.card; + +public interface DeckFactory { + + Deck generate(); +} diff --git a/src/main/java/blackjack/domain/card/Shape.java b/src/main/java/blackjack/domain/card/Shape.java new file mode 100644 index 00000000000..446b373e9e4 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Shape.java @@ -0,0 +1,17 @@ +package blackjack.domain.card; + +public enum Shape { + HEART("하트"), + DIAMOND("다이아몬드"), + SPADE("스페이드"), + CLOVER("클로버"); + private final String name; + + Shape(final String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/blackjack/domain/card/ShuffledDeckFactory.java b/src/main/java/blackjack/domain/card/ShuffledDeckFactory.java new file mode 100644 index 00000000000..a00516b0c96 --- /dev/null +++ b/src/main/java/blackjack/domain/card/ShuffledDeckFactory.java @@ -0,0 +1,25 @@ +package blackjack.domain.card; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class ShuffledDeckFactory implements DeckFactory { + + @Override + public Deck generate() { + final List cards = generateCards(); + Collections.shuffle(cards); + + return new Deck(new ArrayDeque<>(cards)); + } + + private List generateCards() { + return Arrays.stream(Shape.values()) + .flatMap(shape -> Arrays.stream(Symbol.values()) + .map(symbol -> new Card(shape, symbol))) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/blackjack/domain/card/Symbol.java b/src/main/java/blackjack/domain/card/Symbol.java new file mode 100644 index 00000000000..15f48a57a3b --- /dev/null +++ b/src/main/java/blackjack/domain/card/Symbol.java @@ -0,0 +1,37 @@ +package blackjack.domain.card; + +public enum Symbol { + 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 score; + private final String name; + + Symbol(final int score, final String name) { + this.score = score; + this.name = name; + } + + public int getScore() { + return score; + } + + public boolean isAce() { + return this == ACE; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/blackjack/domain/participant/Dealer.java b/src/main/java/blackjack/domain/participant/Dealer.java new file mode 100644 index 00000000000..57428dbaa83 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Dealer.java @@ -0,0 +1,18 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Card; + +public class Dealer extends Participant { + + private static final int CARD_DRAW_POINT = 16; + + @Override + public boolean isDrawable() { + final int currentScore = currentScore(); + return currentScore <= CARD_DRAW_POINT; + } + + public Card getFirstCard() { + return getCards().get(0); + } +} diff --git a/src/main/java/blackjack/domain/participant/Name.java b/src/main/java/blackjack/domain/participant/Name.java new file mode 100644 index 00000000000..7d3b09b4c5e --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Name.java @@ -0,0 +1,34 @@ +package blackjack.domain.participant; + +public class Name { + + private static final int MAX_NAME_LENGTH = 100; + + private final String name; + + Name(final String name) { + validateName(name); + this.name = name; + } + + private void validateName(final String name) { + validateEmptyName(name); + validateLength(name); + } + + private void validateEmptyName(final String name) { + if (name == null || name.isBlank()) { + throw new IllegalArgumentException("이름을 입력하지 않았습니다"); + } + } + + private void validateLength(final String name) { + if (name.length() > MAX_NAME_LENGTH) { + throw new IllegalArgumentException("이름이 " + MAX_NAME_LENGTH + "글자를 초과했습니다"); + } + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/blackjack/domain/participant/Participant.java b/src/main/java/blackjack/domain/participant/Participant.java new file mode 100644 index 00000000000..84ea3f89b71 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Participant.java @@ -0,0 +1,33 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Card; +import blackjack.domain.card.CardPocket; +import java.util.List; + +public abstract class Participant { + + private final CardPocket cardPocket; + + Participant() { + cardPocket = CardPocket.empty(); + } + + public void drawInitialCard(final Card first, final Card second) { + cardPocket.addCard(first); + cardPocket.addCard(second); + } + + public void drawCard(final Card card) { + cardPocket.addCard(card); + } + + public int currentScore() { + return cardPocket.calculateScore(); + } + + public List getCards() { + return cardPocket.getCards(); + } + + public abstract boolean isDrawable(); +} diff --git a/src/main/java/blackjack/domain/participant/Participants.java b/src/main/java/blackjack/domain/participant/Participants.java new file mode 100644 index 00000000000..af89d6d6985 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Participants.java @@ -0,0 +1,57 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Deck; +import java.util.List; + +public class Participants { + + private final Players players; + private final Dealer dealer; + + public Participants(final Players players, final Dealer dealer) { + this.players = players; + this.dealer = dealer; + } + + public void distributeInitialCards(final Deck deck) { + players.distributeInitialCards(deck); + dealer.drawInitialCard(deck.popCard(), deck.popCard()); + } + + public Player findPlayerByName(final String playerName) { + return players.findPlayerByName(playerName); + } + + public boolean isPlayerDrawable(final String playerName) { + return players.isDrawable(playerName); + } + + public void drawPlayerCard(final String playerName, final Card card) { + players.draw(playerName, card); + } + + public boolean isDealerDrawable() { + return dealer.isDrawable(); + } + + public void drawDealerCard(final Card card) { + dealer.drawCard(card); + } + + public Dealer getDealer() { + return dealer; + } + + public List getPlayerCards(final String playerName) { + return players.findPlayerByName(playerName).getCards(); + } + + public Players getPlayers() { + return players; + } + + public List getPlayerNames() { + return players.getPlayerNames(); + } +} diff --git a/src/main/java/blackjack/domain/participant/Player.java b/src/main/java/blackjack/domain/participant/Player.java new file mode 100644 index 00000000000..0553ad3ff99 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Player.java @@ -0,0 +1,27 @@ +package blackjack.domain.participant; + +public class Player extends Participant { + + private final int BUST_POINT = 21; + + private final Name name; + + Player(final String name) { + this.name = new Name(name); + } + + @Override + public boolean isDrawable() { + final int currentScore = currentScore(); + return currentScore < BUST_POINT; + } + + public String getName() { + return name.getName(); + } + + boolean hasName(final String playerName) { + return name.getName() + .equals(playerName); + } +} diff --git a/src/main/java/blackjack/domain/participant/Players.java b/src/main/java/blackjack/domain/participant/Players.java new file mode 100644 index 00000000000..141b61eb3f0 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Players.java @@ -0,0 +1,98 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Deck; +import blackjack.domain.participant.exception.PlayerNotFoundException; +import java.util.List; +import java.util.stream.Collectors; + +public class Players { + + private static final int MAX_PLAYER_COUNT = 5; + private static final int MIN_PLAYER_COUNT = 1; + private static final String OVER_RANGE_MESSAGE = + "사용자 수는 " + MIN_PLAYER_COUNT + " 이상 " + MAX_PLAYER_COUNT + " 이하여야 합니다. 현재 : %s 명입니다"; + + private final List players; + + private Players(final List players) { + this.players = players; + } + + public static Players from(final List playerNames) { + validatePlayerNames(playerNames); + final List players = createPlayers(playerNames); + return new Players(players); + } + + private static List createPlayers(final List playerNames) { + return playerNames.stream() + .map(Player::new) + .collect(Collectors.toList()); + } + + private static void validatePlayerNames(final List playerNames) { + validateNull(playerNames); + validatePlayerCount(playerNames); + validateDuplicate(playerNames); + } + + private static void validateNull(final List playerNames) { + if (playerNames == null) { + throw new IllegalArgumentException("사용자 이름이 입력되지 않았습니다"); + } + } + + private static void validatePlayerCount(final List playerNames) { + if (MIN_PLAYER_COUNT > playerNames.size() || playerNames.size() > MAX_PLAYER_COUNT) { + throw new IllegalArgumentException(String.format(OVER_RANGE_MESSAGE, playerNames.size())); + } + } + + private static void validateDuplicate(final List playerNames) { + if (playerNames.stream().distinct().count() != playerNames.size()) { + throw new IllegalArgumentException("사용자의 이름이 중복됩니다."); + } + } + + void distributeInitialCards(final Deck deck) { + for (final Player player : players) { + final Card firstCard = deck.popCard(); + final Card secondCard = deck.popCard(); + player.drawInitialCard(firstCard, secondCard); + } + } + + List getPlayerNames() { + return players.stream() + .map(Player::getName) + .collect(Collectors.toList()); + } + + boolean isDrawable(final String playerName) { + return players.stream() + .filter(player -> player.hasName(playerName)) + .findFirst() + .map(Player::isDrawable) + .orElseThrow(PlayerNotFoundException::new); + } + + void draw(final String playerName, final Card card) { + final Player targetPlayer = players.stream() + .filter(player -> player.hasName(playerName)) + .findFirst() + .orElseThrow(PlayerNotFoundException::new); + targetPlayer.drawCard(card); + } + + Player findPlayerByName(final String name) { + return players.stream() + .filter(player -> player.hasName(name)) + .findFirst() + .orElseThrow(PlayerNotFoundException::new); + } + + public List getPlayers() { + return players; + } +} diff --git a/src/main/java/blackjack/domain/participant/exception/PlayerNotFoundException.java b/src/main/java/blackjack/domain/participant/exception/PlayerNotFoundException.java new file mode 100644 index 00000000000..376df990e4d --- /dev/null +++ b/src/main/java/blackjack/domain/participant/exception/PlayerNotFoundException.java @@ -0,0 +1,8 @@ +package blackjack.domain.participant.exception; + +public class PlayerNotFoundException extends RuntimeException { + + public PlayerNotFoundException() { + super("없는 사용자 입니다"); + } +} diff --git a/src/main/java/blackjack/response/CardResponse.java b/src/main/java/blackjack/response/CardResponse.java new file mode 100644 index 00000000000..fae1cda56c7 --- /dev/null +++ b/src/main/java/blackjack/response/CardResponse.java @@ -0,0 +1,26 @@ +package blackjack.response; + +import blackjack.domain.card.Card; + +public class CardResponse { + + private final String symbol; + private final String shape; + + private CardResponse(final String symbol, final String shape) { + this.symbol = symbol; + this.shape = shape; + } + + public static CardResponse from(final Card card) { + return new CardResponse(card.getSymbol().getName(), card.getShape().getName()); + } + + public String getSymbol() { + return symbol; + } + + public String getShape() { + return shape; + } +} diff --git a/src/main/java/blackjack/response/ResultTypeResponse.java b/src/main/java/blackjack/response/ResultTypeResponse.java new file mode 100644 index 00000000000..978139de6a0 --- /dev/null +++ b/src/main/java/blackjack/response/ResultTypeResponse.java @@ -0,0 +1,42 @@ +package blackjack.response; + +import blackjack.domain.ResultType; +import java.util.Objects; + +public class ResultTypeResponse { + + private final String result; + + private ResultTypeResponse(final String result) { + this.result = result; + } + + public static ResultTypeResponse from(final ResultType resultType) { + return new ResultTypeResponse(resultType.getName()); + } + + public static ResultTypeResponse from(final String resultType) { + return new ResultTypeResponse(resultType); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ResultTypeResponse that = (ResultTypeResponse) o; + return Objects.equals(result, that.result); + } + + @Override + public int hashCode() { + return Objects.hash(result); + } + + public String getResult() { + return result; + } +} diff --git a/src/main/java/blackjack/service/BlackJackGame.java b/src/main/java/blackjack/service/BlackJackGame.java new file mode 100644 index 00000000000..609d1c01e5b --- /dev/null +++ b/src/main/java/blackjack/service/BlackJackGame.java @@ -0,0 +1,148 @@ +package blackjack.service; + +import blackjack.domain.BlackJackRule; +import blackjack.domain.BlackJackRuleImpl; +import blackjack.domain.ResultType; +import blackjack.domain.card.Card; +import blackjack.domain.card.Deck; +import blackjack.domain.card.DeckFactory; +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Participants; +import blackjack.domain.participant.Player; +import blackjack.domain.participant.Players; +import blackjack.response.CardResponse; +import blackjack.response.ResultTypeResponse; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class BlackJackGame { + + private final Participants participants; + private final Deck deck; + + private BlackJackGame(final Players players, final DeckFactory deckFactory) { + participants = new Participants(players, new Dealer()); + deck = deckFactory.generate(); + } + + public static BlackJackGame of(final List playerNames, final DeckFactory deckFactory) { + return new BlackJackGame(Players.from(playerNames), deckFactory); + } + + public void distributeInitialCard() { + participants.distributeInitialCards(deck); + } + + public boolean isPlayerDrawable(final String playerName) { + return participants.isPlayerDrawable(playerName); + } + + public void drawPlayerCard(final String playerName) { + participants.drawPlayerCard(playerName, deck.popCard()); + } + + public void drawDealerCard() { + participants.drawDealerCard(deck.popCard()); + } + + public boolean isDealerDrawable() { + return participants.isDealerDrawable(); + } + + public List getPlayerNames() { + return participants.getPlayerNames(); + } + + public CardResponse getDealerFirstCard() { + final Card dealerFirstCard = participants.getDealer().getFirstCard(); + return CardResponse.from(dealerFirstCard); + } + + public Map> getPlayerCards() { + final Map> playerCards = new HashMap<>(); + for (final String playerName : getPlayerNames()) { + final List cardResponses = participants.getPlayerCards(playerName).stream() + .map(CardResponse::from) + .collect(Collectors.toList()); + playerCards.put(playerName, cardResponses); + } + return playerCards; + } + + public Map getPlayerScores() { + final Map playerScores = new HashMap<>(); + for (final String playerName : getPlayerNames()) { + final Player player = participants.findPlayerByName(playerName); + playerScores.put(playerName, player.currentScore()); + } + return playerScores; + } + + public List getPlayerCardsResponse(final String playerName) { + final Player player = participants.findPlayerByName(playerName); + return player.getCards().stream() + .map(CardResponse::from) + .collect(Collectors.toList()); + } + + public List getDealerCardsResponse() { + final Dealer dealer = participants.getDealer(); + return dealer.getCards().stream() + .map(CardResponse::from) + .collect(Collectors.toList()); + } + + public int getDealerScore() { + return participants.getDealer().currentScore(); + } + + public Map getDealerResult() { + final BlackJackRule blackJackRule = new BlackJackRuleImpl(); + final ParticipantResults participantResults = new ParticipantResults(); + final Dealer dealer = participants.getDealer(); + participants.getPlayers().getPlayers().forEach(player -> { + final ResultType resultType = blackJackRule.calculateDealerResult(dealer, player); + participantResults.addPlayerResult(player.getName(), resultType); + }); + final Map playersToResult = participantResults.getPlayerNameToResultType(); + return playersToResult.entrySet() + .stream() + .collect(Collectors.groupingBy( + result -> ResultTypeResponse.from(result.getValue()), + Collectors.counting())); + } + + public Map generatePlayersResult() { + final BlackJackRule blackJackRule = new BlackJackRuleImpl(); + final ParticipantResults participantResults = new ParticipantResults(); + final Dealer dealer = participants.getDealer(); + participants.getPlayers().getPlayers().forEach(player -> { + final ResultType resultType = blackJackRule.calculateDealerResult(dealer, player).getOppositeResult(); + participantResults.addPlayerResult(player.getName(), resultType); + }); + final Map playersToResult = participantResults.getPlayerNameToResultType(); + return playersToResult.entrySet() + .stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + result -> ResultTypeResponse.from(result.getValue()), + (x, y) -> y, + LinkedHashMap::new)); + } + + private static class ParticipantResults { + + private final Map playerNameToResultType = new HashMap<>(); + + void addPlayerResult(final String playerName, final ResultType resultType) { + playerNameToResultType.put(playerName, resultType); + } + + Map getPlayerNameToResultType() { + return playerNameToResultType; + } + } +} diff --git a/src/main/java/blackjack/util/Repeater.java b/src/main/java/blackjack/util/Repeater.java new file mode 100644 index 00000000000..34d121f5c67 --- /dev/null +++ b/src/main/java/blackjack/util/Repeater.java @@ -0,0 +1,29 @@ +package blackjack.util; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class Repeater { + + private Repeater() { + } + + public static T repeatUntilNoException(final Supplier supplier, + final Consumer exceptionHandler) { + T result = null; + while (result == null) { + result = createOutputOrNull(supplier, exceptionHandler); + } + return result; + } + + private static T createOutputOrNull(final Supplier inputSupplier, + final Consumer exceptionHandler) { + try { + return inputSupplier.get(); + } catch (final IllegalArgumentException e) { + exceptionHandler.accept(e); + return null; + } + } +} diff --git a/src/main/java/blackjack/view/DrawCommand.java b/src/main/java/blackjack/view/DrawCommand.java new file mode 100644 index 00000000000..6d81fcd6ce2 --- /dev/null +++ b/src/main/java/blackjack/view/DrawCommand.java @@ -0,0 +1,20 @@ +package blackjack.view; + +import java.util.Arrays; + +public enum DrawCommand { + DRAW("y"), + STAY("n"); + public final String command; + + DrawCommand(final String command) { + this.command = command; + } + + public static DrawCommand from(final String command) { + return Arrays.stream(values()) + .filter(it -> it.command.equals(command)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("y, n 만 입력 가능합니다")); + } +} diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java new file mode 100644 index 00000000000..80e5b3a15dc --- /dev/null +++ b/src/main/java/blackjack/view/InputView.java @@ -0,0 +1,29 @@ +package blackjack.view; + +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import java.util.stream.Collectors; + +public class InputView { + + private static final Scanner scanner = new Scanner(System.in); + private static final String INPUT_PLAYER_NAMES_MESSAGE = "게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"; + private static final String INPUT_COMMAND_MESSAGE = "{0}은/는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)"; + private static final String INPUT_DELIMITER = ","; + + public List inputPlayerNames() { + System.out.println(INPUT_PLAYER_NAMES_MESSAGE); + final String input = scanner.nextLine(); + return Arrays.stream(input.split(INPUT_DELIMITER, -1)) + .map(String::strip) + .collect(Collectors.toList()); + } + + public DrawCommand inputCommand(final String playerName) { + System.out.println(MessageFormat.format(INPUT_COMMAND_MESSAGE, playerName)); + final String input = scanner.nextLine(); + return DrawCommand.from(input); + } +} diff --git a/src/main/java/blackjack/view/OutputView.java b/src/main/java/blackjack/view/OutputView.java new file mode 100644 index 00000000000..584c9ff4a3b --- /dev/null +++ b/src/main/java/blackjack/view/OutputView.java @@ -0,0 +1,139 @@ +package blackjack.view; + +import blackjack.response.CardResponse; +import blackjack.response.ResultTypeResponse; +import java.text.MessageFormat; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class OutputView { + + private static final String OUTPUT_DISTRIBUTE_MESSAGE = "딜러와 {0}에게 2장을 나누었습니다."; + + private static final String OUTPUT_DEALER_STATUS_MESSAGE = "딜러는 16이하라 한장의 카드를 더 받았습니다."; + private static final String DELIMITER_BETWEEN_CARDS = ", "; + private static final String DELIMITER = ": "; + private static final String DEALER = "딜러"; + private static final String CARD = "카드"; + private static final String RESULT = " - 결과: "; + + public void printInitialCards(final CardResponse dealerCard, + final Map> playerNameToCards) { + printInitialMessage(playerNameToCards); + printInitialDealerCard(dealerCard); + playerNameToCards.forEach(this::printInitialPlayerCard); + System.out.println(); + } + + private void printInitialMessage(final Map> playerNameToCards) { + final String playerNames = String.join(DELIMITER_BETWEEN_CARDS, playerNameToCards.keySet()); + System.out.println(); + System.out.println(MessageFormat.format(OUTPUT_DISTRIBUTE_MESSAGE, playerNames)); + } + + private void printInitialDealerCard(final CardResponse cardResponse) { + System.out.println(DEALER + DELIMITER + convertCard(cardResponse)); + } + + private void printInitialPlayerCard(final String name, final List cards) { + final String card = cards.stream() + .map(this::convertCard) + .collect(Collectors.joining(DELIMITER_BETWEEN_CARDS)); + + System.out.println(name + CARD + DELIMITER + card); + } + + private String convertCard(final CardResponse cardResponse) { + final String convertedSymbol = cardResponse.getSymbol(); + final String convertedShape = cardResponse.getShape(); + + return convertedSymbol + convertedShape; + } + + public void printCardStatusOfPlayer(final String planerName, final List playerCardsResponse) { + final String cards = playerCardsResponse + .stream() + .map(this::convertCard) + .collect(Collectors.joining(DELIMITER_BETWEEN_CARDS)); + System.out.println(planerName + DELIMITER + cards); + } + + public void printDealerCardDrawMessage() { + System.out.println(); + System.out.println(OUTPUT_DEALER_STATUS_MESSAGE); + } + + public void printFinalStatusOfDealer(final int score, final List dealerCards) { + final String cards = dealerCards + .stream() + .map(this::convertCard) + .collect(Collectors.joining(DELIMITER_BETWEEN_CARDS)); + System.out.println(); + System.out.println(DEALER + " " + CARD + DELIMITER + cards + RESULT + score); + } + + public void printFinalStatusOfPlayers(final Map> playersCardsResponse, + final Map playersScore) { + playersScore.keySet().forEach(name -> { + printFinalPlayerCard(name, playersCardsResponse.get(name)); + printFinalPlayerScore(name, playersScore.get(name)); + }); + } + + + private void printFinalPlayerCard(final String name, final List cards) { + final String card = cards.stream() + .map(this::convertCard) + .collect(Collectors.joining(DELIMITER_BETWEEN_CARDS)); + + System.out.print(name + CARD + DELIMITER + card); + } + + private void printFinalPlayerScore(final String name, final int score) { + System.out.println(RESULT + score); + } + + public void printFinalResult(final Map dealerResult) { + System.out.println(); + System.out.println("## 최종 승패"); + System.out.print(DEALER + DELIMITER); + printDealerResult(dealerResult); + } + + private void printDealerResult(final Map dealerResult) { + printDealer(dealerResult, ResultTypeResponse.from("승")); + printDealer(dealerResult, ResultTypeResponse.from("무")); + printDealer(dealerResult, ResultTypeResponse.from("패")); + System.out.println(); + } + + private void printPlayerResult(final String name, final ResultTypeResponse resultTypeResponse) { + System.out.println(name + DELIMITER + resultTypeResponse.getResult()); + } + + private void printDealer( + final Map dealerResult, + final ResultTypeResponse resultTypeResponse) { + + if (dealerResult.containsKey(resultTypeResponse)) { + System.out.print( + dealerResult.get(resultTypeResponse) + resultTypeResponse.getResult()); + } + } + + public void printError(final Exception exception) { + System.out.println(exception.getMessage()); + } + + public void printFinalDealerResult(final Map dealerResult) { + System.out.println(); + System.out.println("## 최종 승패"); + System.out.print(DEALER + DELIMITER); + printDealerResult(dealerResult); + } + + public void printFinalPlayersResult(final Map playersResult) { + playersResult.forEach(this::printPlayerResult); + } +} diff --git a/src/test/java/blackjack/domain/card/CardPocketTest.java b/src/test/java/blackjack/domain/card/CardPocketTest.java new file mode 100644 index 00000000000..1fb04c46ba4 --- /dev/null +++ b/src/test/java/blackjack/domain/card/CardPocketTest.java @@ -0,0 +1,70 @@ +package blackjack.domain.card; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@SuppressWarnings({"NonAsciiCharacters", "SpellCheckingInspection"}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class CardPocketTest { + + private static final List cards = List.of(new Card(Shape.CLOVER, Symbol.ACE), + new Card(Shape.HEART, Symbol.EIGHT)); + + private CardPocket cardPocket; + + @BeforeEach + void setCardPocket() { + cardPocket = CardPocket.empty(); + cards.forEach(cardPocket::addCard); + } + + @Test + void 카드_포켓_안의_카드의_점수_계산() { + assertThat(cardPocket.calculateScore()) + .isEqualTo(19); + + } + + @Test + void 카드_포켓에_카드_추가한_후_카드의_점수_계산() { + cardPocket.addCard(new Card(Shape.DIAMOND, Symbol.ACE)); + + assertThat(cardPocket.calculateScore()) + .isEqualTo(20); + + } + + @Test + void 카드_포켓에_카드_두번_추가한_후_카드의_점수_계산() { + cardPocket.addCard(new Card(Shape.DIAMOND, Symbol.TEN)); + cardPocket.addCard(new Card(Shape.DIAMOND, Symbol.EIGHT)); + + assertThat(cardPocket.calculateScore()) + .isEqualTo(27); + } + + @Test + void 카드_포켓에서_카드_가져오는_기능_추가() { + assertThat(cardPocket.getCards()) + .isEqualTo(cards); + } + + @Test + void 카드_포켓에서_Ace_의_점수_계산이_11_과_1로_잘_계산된다() { + final CardPocket cardPocket = CardPocket.empty(); + cardPocket.addCard(new Card(Shape.DIAMOND, Symbol.ACE)); + cardPocket.addCard(new Card(Shape.DIAMOND, Symbol.TEN)); + + assertThat(cardPocket.calculateScore()) + .isEqualTo(21); + + cardPocket.addCard(new Card(Shape.DIAMOND, Symbol.ACE)); + assertThat(cardPocket.calculateScore()) + .isEqualTo(12); + } +} 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..b853886a287 --- /dev/null +++ b/src/test/java/blackjack/domain/card/CardTest.java @@ -0,0 +1,40 @@ +package blackjack.domain.card; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@SuppressWarnings({"NonAsciiCharacters", "SpellCheckingInspection"}) +@DisplayNameGeneration(ReplaceUnderscores.class) +class CardTest { + + private final Shape shape = Shape.CLOVER; + private final Symbol symbol = Symbol.ACE; + private final Card card = new Card(shape, symbol); + + @Test + void 카드의_Symbol을_반환한다() { + assertThat(card.getSymbol()) + .isEqualTo(symbol); + } + + @Test + void 카드의_Shape를_반환한다() { + assertThat(card.getShape()) + .isEqualTo(shape); + } + + @Test + void 카드의_점수를_반환한다() { + assertThat(card.getScore()) + .isEqualTo(symbol.getScore()); + } + + @Test + void 카드가_Ace인지_확인한다() { + assertThat(card.isAce()) + .isEqualTo(symbol.isAce()); + } +} diff --git a/src/test/java/blackjack/domain/card/DeckTest.java b/src/test/java/blackjack/domain/card/DeckTest.java new file mode 100644 index 00000000000..9b2660d6815 --- /dev/null +++ b/src/test/java/blackjack/domain/card/DeckTest.java @@ -0,0 +1,38 @@ +package blackjack.domain.card; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.ArrayDeque; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@SuppressWarnings({"NonAsciiCharacters"}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class DeckTest { + + @Test + void 생성시_null_이면_예외() { + assertThatThrownBy(() -> new Deck(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("카드에 null 이 들어왔습니다"); + } + + @Test + void 생성시_52장이_아니면_예외() { + assertThatThrownBy(() -> new Deck(new ArrayDeque<>())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("카드 숫자는 52장이어야 합니다 현재 :0장"); + } + + @Test + void 제거_시도를_52번보다_많이_하면_예외() { + final Deck deck = new ShuffledDeckFactory().generate(); + for (int i = 0; i < 52; i++) { + deck.popCard(); + } + assertThatThrownBy(deck::popCard) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("덱에 카드가 없습니다"); + } +} diff --git a/src/test/java/blackjack/domain/participant/DealerTest.java b/src/test/java/blackjack/domain/participant/DealerTest.java new file mode 100644 index 00000000000..ff647069913 --- /dev/null +++ b/src/test/java/blackjack/domain/participant/DealerTest.java @@ -0,0 +1,58 @@ +package blackjack.domain.participant; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Shape; +import blackjack.domain.card.Symbol; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@SuppressWarnings({"NonAsciiCharacters"}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class DealerTest { + + private static final List overDrawPointCards = List.of( + new Card(Shape.CLOVER, Symbol.ACE), + new Card(Shape.HEART, Symbol.KING)); + private static final List underDrawPointCards = List.of( + new Card(Shape.CLOVER, Symbol.TWO), + new Card(Shape.HEART, Symbol.EIGHT)); + + @Test + void 딜러의_카드가_16_이하의_점수라면_드로우_합니다() { + final Dealer dealer = new Dealer(); + underDrawPointCards.forEach(dealer::drawCard); + + assertThat(dealer.isDrawable()) + .isTrue(); + } + + @Test + void 딜러의_카드가_17_이상의_점수라면_스테이_합니다() { + final Dealer dealer = new Dealer(); + overDrawPointCards.forEach(dealer::drawCard); + + assertThat(dealer.isDrawable()) + .isFalse(); + } + + @Nested + @DisplayName("딜러를 통해 결과를 계산하면") + class CalculateResultTest { + + private final Dealer dealer = new Dealer(); + private Player player; + + @BeforeEach + void setUp() { + dealer.drawCard(new Card(Shape.DIAMOND, Symbol.FIVE)); + player = new Player("pobi"); + } + } +} diff --git a/src/test/java/blackjack/domain/participant/NameTest.java b/src/test/java/blackjack/domain/participant/NameTest.java new file mode 100644 index 00000000000..d1242f67791 --- /dev/null +++ b/src/test/java/blackjack/domain/participant/NameTest.java @@ -0,0 +1,31 @@ +package blackjack.domain.participant; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.stream.IntStream; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@SuppressWarnings({"NonAsciiCharacters", "SpellCheckingInspection"}) +@DisplayNameGeneration(ReplaceUnderscores.class) +class NameTest { + + @Test + void 생성시_null이면_예외() { + assertThatThrownBy(() -> new Name(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이름을 입력하지 않았습니다"); + } + + @Test + void 길이가_100글자_초과시_에러() { + final StringBuilder stringBuilder = new StringBuilder(); + IntStream.range(0, 100) + .forEach(stringBuilder::append); + + assertThatThrownBy(() -> new Name(stringBuilder.toString())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이름이 100글자를 초과했습니다"); + } +} diff --git a/src/test/java/blackjack/domain/participant/ParticipantTest.java b/src/test/java/blackjack/domain/participant/ParticipantTest.java new file mode 100644 index 00000000000..f81f6f15445 --- /dev/null +++ b/src/test/java/blackjack/domain/participant/ParticipantTest.java @@ -0,0 +1,55 @@ +package blackjack.domain.participant; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Shape; +import blackjack.domain.card.Symbol; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@SuppressWarnings({"NonAsciiCharacters", "SpellCheckingInspection"}) +@DisplayNameGeneration(ReplaceUnderscores.class) +class ParticipantTest { + + private final Card[] cards = new Card[]{ + new Card(Shape.CLOVER, Symbol.ACE), + new Card(Shape.CLOVER, Symbol.ACE) + }; + private Participant participant; + + @BeforeEach + void setUp() { + participant = new Participant() { + @Override + public boolean isDrawable() { + return true; + } + }; + participant.drawInitialCard(cards[0], cards[1]); + } + + @Test + void 참가자는_점수를_계산할_수_있다() { + + final int score = participant.currentScore(); + + assertThat(score).isEqualTo(12); + } + + @Test + void 참가자는_추가로_드로우를_해서_점수를_계산할_수_있다() { + participant.drawCard(new Card(Shape.CLOVER, Symbol.ACE)); + + final int score = participant.currentScore(); + + assertThat(score).isEqualTo(13); + } + + @Test + void 참가자는_자기가_가지고_있는_카드를_확인할_수_있다() { + assertThat(participant.getCards()).containsExactly(cards); + } +} diff --git a/src/test/java/blackjack/domain/participant/PlayerTest.java b/src/test/java/blackjack/domain/participant/PlayerTest.java new file mode 100644 index 00000000000..35d2caaa3a3 --- /dev/null +++ b/src/test/java/blackjack/domain/participant/PlayerTest.java @@ -0,0 +1,46 @@ +package blackjack.domain.participant; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Shape; +import blackjack.domain.card.Symbol; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@SuppressWarnings({"NonAsciiCharacters", "SpellCheckingInspection"}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class PlayerTest { + + private static final List overDrawPointCards = List.of( + new Card(Shape.CLOVER, Symbol.ACE), + new Card(Shape.HEART, Symbol.KING)); + private static final List underDrawPointCards = List.of( + new Card(Shape.CLOVER, Symbol.TWO), + new Card(Shape.HEART, Symbol.EIGHT)); + + private Player player; + + @BeforeEach + void setup() { + player = new Player("pobi"); + } + + @Test + void 플레이어_카드의_점수가_21미만이면_드로우_할수있다() { + underDrawPointCards.forEach(player::drawCard); + assertThat(player.isDrawable()) + .isTrue(); + } + + @Test + void 플레이어_카드_점수가_21이상이면_드로우_할수없다() { + overDrawPointCards.forEach(player::drawCard); + assertThat(player.isDrawable()) + .isFalse(); + } + +} diff --git a/src/test/java/blackjack/domain/participant/PlayersTest.java b/src/test/java/blackjack/domain/participant/PlayersTest.java new file mode 100644 index 00000000000..94caf402445 --- /dev/null +++ b/src/test/java/blackjack/domain/participant/PlayersTest.java @@ -0,0 +1,57 @@ +package blackjack.domain.participant; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@SuppressWarnings({"NonAsciiCharacters", "SpellCheckingInspection"}) +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class PlayersTest { + + @Nested + @DisplayName("Players를 생성할 때") + class PlayerInitiatorTest { + + @Test + void 플레이어의_목록이_null_이면_예외() { + assertThatThrownBy(() -> Players.from(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("사용자 이름이 입력되지 않았습니다"); + } + + @Test + void 플레이어의_수가_0명이면_예외() { + final List playerNames = new ArrayList<>(); + + assertThatThrownBy(() -> Players.from(playerNames)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("사용자 수는 1 이상 5 이하여야 합니다. 현재 : 0 명입니다"); + } + + @Test + void 플레이어의_수가_5명초과면_예외() { + final List playerNames = List.of("pobi", "crong", "honux", "wannte", "디디", "누누"); + + assertThatThrownBy(() -> Players.from(playerNames)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("사용자 수는 1 이상 5 이하여야 합니다. 현재 : 6 명입니다"); + } + + @Test + void 플레이어의_이름이_중복되면_예외() { + final List playerNames = List.of("pobi", "pobi", "honux", "wannte", "디디"); + + assertThatThrownBy(() -> Players.from(playerNames)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("사용자의 이름이 중복됩니다."); + } + } + + +}