diff --git a/README.md b/README.md index 5a33d0f5e30..09f7c164631 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,11 @@ - [x] 카드덱에서 카드를 한 장 뽑아서 건네줄 수 있다. - [x] 카드덱에서 보유한 카드 개수보다 많이 뽑으면 예외가 발생한다. - [x] 참여자의 핸드에 초기 카드를 분배할 수 있다. + - [x] 플레이어는 10억까지 베팅할 수 있다. + - [x] 플레이어는 핸드를 완성시키는 단계에서 버스트 하면 금액을 모두 잃는다. + - [x] 처음 두 장의 카드 합이 21이 되는 블랙잭일 경우 베팅 금액의 1.5배를 추가로 얻는다. (소숫점 제거) + - [x] 플레이어가 블랙잭이고 딜러도 블랙잭이면 플레이어는 베팅 금액을 그대로 돌려받는다. + - [x] 플레이어가 딜러에게 승리하면 베팅 금액만큼을 추가로 얻는다. - [x] 출력 - [x] 각 참여자의 카드 정보를 출력할 수 있다. - [x] 각 참여자의 카드 합을 출력할 수 있다. diff --git a/src/main/java/blackjack/Application.java b/src/main/java/blackjack/Application.java index 859056e6da5..47214ff1a6f 100644 --- a/src/main/java/blackjack/Application.java +++ b/src/main/java/blackjack/Application.java @@ -7,8 +7,7 @@ public class Application { public static void main(String[] args) { - - InputView inputView = new InputView(); + InputView inputView = new InputView(new InputMapper()); OutputView outputView = new OutputView(new MessageResolver()); BlackJackGame blackJackGame = new BlackJackGame(inputView, outputView); diff --git a/src/main/java/blackjack/BlackJackGame.java b/src/main/java/blackjack/BlackJackGame.java index 4a8b5ece20f..c3f3788f57a 100644 --- a/src/main/java/blackjack/BlackJackGame.java +++ b/src/main/java/blackjack/BlackJackGame.java @@ -1,17 +1,13 @@ package blackjack; -import static blackjack.domain.DrawDecision.YES; - -import blackjack.domain.DrawDecision; -import blackjack.domain.card.Card; +import blackjack.domain.bet.PlayerBets; import blackjack.domain.card.CardDeck; +import blackjack.domain.card.Hand; import blackjack.domain.player.Dealer; -import blackjack.domain.player.Hand; -import blackjack.domain.player.Participant; import blackjack.domain.player.Player; import blackjack.domain.player.PlayerName; import blackjack.domain.player.Players; -import blackjack.domain.rule.Judge; +import blackjack.domain.result.PlayerProfits; import blackjack.view.InputView; import blackjack.view.OutputView; import java.util.List; @@ -29,73 +25,33 @@ public BlackJackGame(InputView inputView, OutputView outputView) { public void run() { CardDeck cardDeck = CardDeck.createShuffledDeck(); Players players = initPlayers(cardDeck); + PlayerBets playerBets = inputView.readBetInformation(players); Dealer dealer = new Dealer(Hand.createHandFrom(cardDeck)); - printPlayersInformation(players, dealer); + outputView.printParticipantInitialHand(dealer, players); completePlayersHand(players, cardDeck); - completeDealerHand(dealer, cardDeck); - outputView.printDealerPopCount(Dealer.HIT_THRESHOLD, dealer.countPop()); + dealer.completeHand(cardDeck); + outputView.printCompletedHandsStatus(dealer, players); - printParticipantScore(dealer); - printPlayersScore(players); - printDealerGameResult(dealer, players); - printPlayersGameResult(players, dealer); + PlayerProfits playerProfits = playerBets.calculateProfitResult(dealer); + outputView.printProfitResults(playerProfits); } private Players initPlayers(CardDeck cardDeck) { - InputMapper inputMapper = new InputMapper(); - List playerNames = inputMapper.mapToPlayers(inputView.readNames()); - + List playerNames = inputView.readNames(); return new Players(playerNames.stream() .map(playerName -> new Player(playerName, Hand.createHandFrom(cardDeck))) .toList()); } - private void printPlayersInformation(Players players, Dealer dealer) { - outputView.printHandOutEvent(players, 2); - outputView.printDealerInitialHand(dealer); - players.getPlayers().forEach(outputView::printPlayerHand); - } - private void completePlayersHand(Players players, CardDeck cardDeck) { players.getPlayers().forEach(player -> completePlayerHand(player, cardDeck)); } private void completePlayerHand(Player participant, CardDeck cardDeck) { - while (participant.canHit() && readHitDecision(participant) == YES) { + while (participant.canHit() && inputView.readDrawDecision(participant.getName()).isYes()) { participant.appendCard(cardDeck.popCard()); outputView.printPlayerHand(participant); } } - - private DrawDecision readHitDecision(Player participant) { - InputMapper inputMapper = new InputMapper(); - return inputMapper.mapToDrawDecision(inputView.readDrawPlan(participant.getName())); - } - - private void completeDealerHand(Dealer dealer, CardDeck cardDeck) { - while (dealer.canHit()) { - Card card = cardDeck.popCard(); - dealer.appendCard(card); - } - } - - private void printPlayersScore(Players players) { - players.getPlayers().forEach(this::printParticipantScore); - } - - private void printParticipantScore(Participant participant) { - outputView.printParticipantScore(participant, participant.calculateHandScore()); - } - - private void printDealerGameResult(Dealer dealer, Players players) { - Judge judge = new Judge(); - outputView.printDealerGameResult(judge.calculateDealerGameResult(dealer, players)); - } - - private void printPlayersGameResult(Players players, Dealer dealer) { - Judge judge = new Judge(); - players.getPlayers() - .forEach(player -> outputView.printPlayerGameResult(player, judge.isPlayerWin(dealer, player))); - } } diff --git a/src/main/java/blackjack/InputMapper.java b/src/main/java/blackjack/InputMapper.java index e6e8feb734b..f108f887aae 100644 --- a/src/main/java/blackjack/InputMapper.java +++ b/src/main/java/blackjack/InputMapper.java @@ -9,7 +9,7 @@ public class InputMapper { private static final String DELIMITER = ","; - public List mapToPlayers(String target) { + public List mapToPlayerNames(String target) { String[] split = target.split(DELIMITER); return Arrays.stream(split) .map(PlayerName::new) diff --git a/src/main/java/blackjack/domain/DealerGameResult.java b/src/main/java/blackjack/domain/DealerGameResult.java deleted file mode 100644 index 6e0eae9731e..00000000000 --- a/src/main/java/blackjack/domain/DealerGameResult.java +++ /dev/null @@ -1,20 +0,0 @@ -package blackjack.domain; - -public class DealerGameResult { - - private final int winCount; - private final int loseCount; - - public DealerGameResult(int winCount, int loseCount) { - this.winCount = winCount; - this.loseCount = loseCount; - } - - public int getWinCount() { - return winCount; - } - - public int getLoseCount() { - return loseCount; - } -} diff --git a/src/main/java/blackjack/domain/DrawDecision.java b/src/main/java/blackjack/domain/DrawDecision.java index 3d9850e0ee9..ed5b60b65df 100644 --- a/src/main/java/blackjack/domain/DrawDecision.java +++ b/src/main/java/blackjack/domain/DrawDecision.java @@ -19,4 +19,8 @@ public static DrawDecision from(String code) { .findFirst() .orElseThrow(() -> new IllegalArgumentException("[ERROR] " + YES.code + "또는 " + NO.code + "로 입력해주세요")); } + + public boolean isYes() { + return this == YES; + } } diff --git a/src/main/java/blackjack/domain/Score.java b/src/main/java/blackjack/domain/Score.java new file mode 100644 index 00000000000..d3d63e4e495 --- /dev/null +++ b/src/main/java/blackjack/domain/Score.java @@ -0,0 +1,47 @@ +package blackjack.domain; + +import java.util.Objects; + +public class Score { + + private static final int MAX_SCORE = 21; + + private final int value; + + public Score(int value) { + this.value = value; + } + + public boolean isAbove(Score target) { + return this.value > target.value; + } + + public boolean isMaxScore() { + return value == MAX_SCORE; + } + + public boolean isBustScore() { + return value > MAX_SCORE; + } + + public int getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Score score = (Score) o; + return value == score.value; + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/main/java/blackjack/domain/bet/BetAmount.java b/src/main/java/blackjack/domain/bet/BetAmount.java new file mode 100644 index 00000000000..df77af49f50 --- /dev/null +++ b/src/main/java/blackjack/domain/bet/BetAmount.java @@ -0,0 +1,24 @@ +package blackjack.domain.bet; + +public class BetAmount { + + private static final int MAX = 1_000_000_000; + private static final int MIN = 1_000; + + private final int amount; + + public BetAmount(int amount) { + validateRange(amount); + this.amount = amount; + } + + public Profit calculateProfit(double leverage) { + return new Profit((int) (amount * leverage)); + } + + private void validateRange(int amount) { + if (amount < MIN || MAX < amount) { + throw new IllegalArgumentException("[ERROR] 베팅 금액은 " + MIN + "부터 " + MAX + "이하까지 가능합니다."); + } + } +} diff --git a/src/main/java/blackjack/domain/bet/PlayerBets.java b/src/main/java/blackjack/domain/bet/PlayerBets.java new file mode 100644 index 00000000000..194f7f3f6d5 --- /dev/null +++ b/src/main/java/blackjack/domain/bet/PlayerBets.java @@ -0,0 +1,30 @@ +package blackjack.domain.bet; + +import blackjack.domain.player.Dealer; +import blackjack.domain.player.Player; +import blackjack.domain.result.GameResult; +import blackjack.domain.result.PlayerProfits; +import java.util.Map; +import java.util.stream.Collectors; + +public class PlayerBets { + + private final Map playerBets; + + public PlayerBets(Map playerBets) { + this.playerBets = playerBets; + } + + public PlayerProfits calculateProfitResult(Dealer dealer) { + return new PlayerProfits(playerBets.keySet().stream() + .collect(Collectors.toMap( + player -> player, + player -> calculatePlayerProfit(player, dealer)))); + } + + private Profit calculatePlayerProfit(Player player, Dealer dealer) { + GameResult gameResult = GameResult.judge(dealer, player); + BetAmount betAmount = playerBets.get(player); + return betAmount.calculateProfit(gameResult.getProfitLeverage()); + } +} diff --git a/src/main/java/blackjack/domain/bet/Profit.java b/src/main/java/blackjack/domain/bet/Profit.java new file mode 100644 index 00000000000..9632aacee6a --- /dev/null +++ b/src/main/java/blackjack/domain/bet/Profit.java @@ -0,0 +1,41 @@ +package blackjack.domain.bet; + +import java.util.Objects; + +public class Profit { + + private final int value; + + public Profit(int value) { + this.value = value; + } + + public Profit add(Profit other) { + return new Profit(this.value + other.value); + } + + public Profit inverse() { + return new Profit(-1 * value); + } + + public int getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Profit profit = (Profit) o; + return value == profit.value; + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/main/java/blackjack/domain/card/Card.java b/src/main/java/blackjack/domain/card/Card.java index 6b4f0b8cd90..9d6b960bcbf 100644 --- a/src/main/java/blackjack/domain/card/Card.java +++ b/src/main/java/blackjack/domain/card/Card.java @@ -16,6 +16,14 @@ public boolean isAce() { return cardNumber.isAce(); } + public CardNumber getCardNumber() { + return cardNumber; + } + + public CardShape getCardShape() { + return cardShape; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -32,12 +40,4 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(cardShape, cardNumber); } - - public CardNumber getCardNumber() { - return cardNumber; - } - - public CardShape getCardShape() { - return cardShape; - } } diff --git a/src/main/java/blackjack/domain/card/CardDeck.java b/src/main/java/blackjack/domain/card/CardDeck.java index 8ad67fa1b08..93c764de1d2 100644 --- a/src/main/java/blackjack/domain/card/CardDeck.java +++ b/src/main/java/blackjack/domain/card/CardDeck.java @@ -2,18 +2,24 @@ import java.util.Arrays; import java.util.Collections; +import java.util.LinkedList; import java.util.List; +import java.util.Queue; import java.util.stream.Collectors; import java.util.stream.IntStream; public class CardDeck { - private final List cards; + private Queue cards; - CardDeck(List cards) { + CardDeck(Queue cards) { this.cards = cards; } + CardDeck(List cards) { + this((Queue) new LinkedList<>(cards)); + } + public static CardDeck createShuffledDeck() { List cards = Arrays.stream(CardShape.values()) .flatMap(shape -> Arrays.stream(CardNumber.values()) @@ -27,7 +33,7 @@ public Card popCard() { if (cards.isEmpty()) { throw new IllegalArgumentException("[ERROR] 남아있는 카드가 부족하여 카드를 뽑을 수 없습니다"); } - return cards.remove(cards.size() - 1); + return cards.poll(); } public List popCards(int count) { diff --git a/src/main/java/blackjack/domain/player/Hand.java b/src/main/java/blackjack/domain/card/Hand.java similarity index 60% rename from src/main/java/blackjack/domain/player/Hand.java rename to src/main/java/blackjack/domain/card/Hand.java index 6e99b759d2a..74b18904e55 100644 --- a/src/main/java/blackjack/domain/player/Hand.java +++ b/src/main/java/blackjack/domain/card/Hand.java @@ -1,15 +1,12 @@ -package blackjack.domain.player; +package blackjack.domain.card; -import blackjack.domain.card.Card; -import blackjack.domain.card.CardDeck; -import blackjack.domain.card.CardNumber; -import blackjack.domain.rule.Score; +import blackjack.domain.Score; import java.util.List; public class Hand { - private static final int INITIAL_HAND_SIZE = 2; - private static final int BLACK_JACK = 21; + public static final int INITIAL_HAND_SIZE = 2; + private static final int BUST_THRESHOLD = 21; private static final int ACE_WEIGHT = 10; private final List cards; @@ -22,6 +19,14 @@ public static Hand createHandFrom(CardDeck cardDeck) { return new Hand(cardDeck.popCards(INITIAL_HAND_SIZE)); } + public Score calculateScore() { + int sum = calculateCardSummation(); + if (hasAce() && isWeightAppliedScoreBust(sum)) { + sum += ACE_WEIGHT; + } + return new Score(sum); + } + public int calculateCardSummation() { return cards.stream() .map(Card::getCardNumber) @@ -33,27 +38,24 @@ public void appendCard(Card card) { cards.add(card); } - public int countAce() { - return (int) cards.stream() - .filter(Card::isAce) - .count(); + public boolean isBlackJack() { + return calculateScore().isMaxScore() && cards.size() == INITIAL_HAND_SIZE; } - public int countPop() { + public int countDraw() { return cards.size() - INITIAL_HAND_SIZE; } - public Score calculateScore() { - int aceCount = countAce(); - int sum = calculateCardSummation(); - while (aceCount > 0 && (sum + ACE_WEIGHT) <= BLACK_JACK) { - sum += ACE_WEIGHT; - aceCount--; - } - return new Score(sum); - } - public List getCards() { return List.copyOf(cards); } + + private boolean hasAce() { + return cards.stream() + .anyMatch(Card::isAce); + } + + private boolean isWeightAppliedScoreBust(int sum) { + return (sum + ACE_WEIGHT) <= BUST_THRESHOLD; + } } diff --git a/src/main/java/blackjack/domain/player/Dealer.java b/src/main/java/blackjack/domain/player/Dealer.java index 56a536ead0e..b8292a8dc4a 100644 --- a/src/main/java/blackjack/domain/player/Dealer.java +++ b/src/main/java/blackjack/domain/player/Dealer.java @@ -1,5 +1,8 @@ package blackjack.domain.player; +import blackjack.domain.card.CardDeck; +import blackjack.domain.card.Hand; + public class Dealer extends Participant { private static final String DEALER_NAME = "딜러"; @@ -13,4 +16,10 @@ public Dealer(Hand hand) { public boolean canHit() { return hand.calculateCardSummation() <= HIT_THRESHOLD; } + + public void completeHand(CardDeck cardDeck) { + while (canHit()) { + appendCard(cardDeck.popCard()); + } + } } diff --git a/src/main/java/blackjack/domain/player/Participant.java b/src/main/java/blackjack/domain/player/Participant.java index 6a3d875fc1a..9ffb9d06f04 100644 --- a/src/main/java/blackjack/domain/player/Participant.java +++ b/src/main/java/blackjack/domain/player/Participant.java @@ -1,7 +1,8 @@ package blackjack.domain.player; +import blackjack.domain.Score; import blackjack.domain.card.Card; -import blackjack.domain.rule.Score; +import blackjack.domain.card.Hand; import java.util.Objects; public abstract class Participant { @@ -14,6 +15,8 @@ protected Participant(PlayerName name, Hand hand) { this.hand = hand; } + public abstract boolean canHit(); + public void appendCard(Card card) { hand.appendCard(card); } @@ -22,11 +25,41 @@ public Score calculateHandScore() { return hand.calculateScore(); } - public int countPop() { - return hand.countPop(); + public int countDraw() { + return hand.countDraw(); } - public abstract boolean canHit(); + public boolean isBusted() { + return hand.calculateScore().isBustScore(); + } + + public boolean isNotBusted() { + return !hand.calculateScore().isBustScore(); + } + + public boolean hasBlackJackHand() { + return hand.isBlackJack(); + } + + public boolean hasNoBlackJackHand() { + return !hand.isBlackJack(); + } + + public boolean hasScoreAbove(Participant other) { + return this.calculateHandScore().isAbove(other.calculateHandScore()); + } + + public boolean hasSameScore(Participant other) { + return this.calculateHandScore().equals(other.calculateHandScore()); + } + + public String getName() { + return name.getValue(); + } + + public Hand getHand() { + return hand; + } @Override public boolean equals(Object o) { @@ -44,12 +77,4 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(name); } - - public String getName() { - return name.getValue(); - } - - public Hand getHand() { - return hand; - } } diff --git a/src/main/java/blackjack/domain/player/Player.java b/src/main/java/blackjack/domain/player/Player.java index be9461db70e..9711b317b28 100644 --- a/src/main/java/blackjack/domain/player/Player.java +++ b/src/main/java/blackjack/domain/player/Player.java @@ -1,5 +1,7 @@ package blackjack.domain.player; +import blackjack.domain.card.Hand; + public class Player extends Participant { public static final int HIT_THRESHOLD = 21; diff --git a/src/main/java/blackjack/domain/player/PlayerName.java b/src/main/java/blackjack/domain/player/PlayerName.java index 675d9d6d1bd..fa868c3da22 100644 --- a/src/main/java/blackjack/domain/player/PlayerName.java +++ b/src/main/java/blackjack/domain/player/PlayerName.java @@ -17,6 +17,10 @@ private void validateNotEmpty(String value) { } } + public String getValue() { + return value; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -33,8 +37,4 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(value); } - - public String getValue() { - return value; - } } diff --git a/src/main/java/blackjack/domain/player/Players.java b/src/main/java/blackjack/domain/player/Players.java index e0c17514b52..b76573739c3 100644 --- a/src/main/java/blackjack/domain/player/Players.java +++ b/src/main/java/blackjack/domain/player/Players.java @@ -1,6 +1,5 @@ package blackjack.domain.player; -import blackjack.domain.rule.Score; import java.util.List; public class Players { @@ -14,17 +13,6 @@ public Players(List players) { this.players = players; } - public int countPlayer() { - return players.size(); - } - - public int countPlayerWithScoreAbove(Score target) { - return (int) players.stream() - .map(Participant::calculateHandScore) - .filter(playerScore -> playerScore.isAbove(target)) - .count(); - } - private void validate(List participants) { validateEachPlayerNameUnique(participants); validateEntryNotEmpty(participants); diff --git a/src/main/java/blackjack/domain/result/GameResult.java b/src/main/java/blackjack/domain/result/GameResult.java new file mode 100644 index 00000000000..4e415221656 --- /dev/null +++ b/src/main/java/blackjack/domain/result/GameResult.java @@ -0,0 +1,35 @@ +package blackjack.domain.result; + +import blackjack.domain.player.Dealer; +import blackjack.domain.player.Player; +import java.util.Arrays; +import java.util.NoSuchElementException; +import java.util.function.BiPredicate; + +public enum GameResult { + + BLACKJACK_WIN(1.5, (dealer, player) -> dealer.hasNoBlackJackHand() && player.hasBlackJackHand()), + PLAYER_LOSE(-1.0, (dealer, player) -> player.isBusted() || (dealer.isNotBusted() && dealer.hasScoreAbove(player))), + PLAYER_WIN(1.0, (dealer, player) -> (player.isNotBusted() && (dealer.isBusted() || player.hasScoreAbove(dealer)))), + PUSH(0.0, (dealer, player) -> player.isNotBusted() && dealer.isNotBusted() && player.hasSameScore(dealer)); + + + private final double profitLeverage; + private final BiPredicate biPredicate; + + GameResult(double profitLeverage, BiPredicate biPredicate) { + this.profitLeverage = profitLeverage; + this.biPredicate = biPredicate; + } + + public static GameResult judge(Dealer dealer, Player player) { + return Arrays.stream(values()) + .filter(gameResult -> gameResult.biPredicate.test(dealer, player)) + .findAny() + .orElseThrow(() -> new NoSuchElementException("[INTERNAL ERROR] 게임 결과를 판정할 수 없습니다")); + } + + public double getProfitLeverage() { + return profitLeverage; + } +} diff --git a/src/main/java/blackjack/domain/result/PlayerProfits.java b/src/main/java/blackjack/domain/result/PlayerProfits.java new file mode 100644 index 00000000000..7120ac26ec9 --- /dev/null +++ b/src/main/java/blackjack/domain/result/PlayerProfits.java @@ -0,0 +1,31 @@ +package blackjack.domain.result; + +import blackjack.domain.bet.Profit; +import blackjack.domain.player.Player; +import java.util.Map; + +public class PlayerProfits { + + private final Map playerProfits; + + public PlayerProfits(Map playerProfits) { + this.playerProfits = playerProfits; + } + + public Profit findProfitOfPlayer(Player player) { + return playerProfits.get(player); + } + + public Profit calculateTotalProfit() { + return playerProfits.values().stream() + .reduce(new Profit(0), Profit::add); + } + + public Profit calculateDealerProfit() { + return calculateTotalProfit().inverse(); + } + + public Map getPlayerProfits() { + return playerProfits; + } +} diff --git a/src/main/java/blackjack/domain/rule/Judge.java b/src/main/java/blackjack/domain/rule/Judge.java deleted file mode 100644 index 4bcf103ab14..00000000000 --- a/src/main/java/blackjack/domain/rule/Judge.java +++ /dev/null @@ -1,31 +0,0 @@ -package blackjack.domain.rule; - -import blackjack.domain.DealerGameResult; -import blackjack.domain.player.Dealer; -import blackjack.domain.player.Player; -import blackjack.domain.player.Players; - -public class Judge { - - private static final Score BLACK_JACK = new Score(21); - - public DealerGameResult calculateDealerGameResult(Dealer dealer, Players players) { - int playerWinCount = (int) players.getPlayers().stream() - .filter(player -> isPlayerWin(dealer, player)) - .count(); - int dealerWinCount = players.countPlayer() - playerWinCount; - return new DealerGameResult(dealerWinCount, playerWinCount); - } - - public boolean isPlayerWin(Dealer dealer, Player player) { - Score dealerScore = dealer.calculateHandScore(); - Score playerScore = player.calculateHandScore(); - if (playerScore.isAbove(BLACK_JACK)) { - return false; - } - if (dealerScore.isAbove(BLACK_JACK)) { - return true; - } - return playerScore.isAbove(dealerScore); - } -} diff --git a/src/main/java/blackjack/domain/rule/Score.java b/src/main/java/blackjack/domain/rule/Score.java deleted file mode 100644 index 53ec0d43f21..00000000000 --- a/src/main/java/blackjack/domain/rule/Score.java +++ /dev/null @@ -1,18 +0,0 @@ -package blackjack.domain.rule; - -public class Score { - - private final int value; - - public Score(int value) { - this.value = value; - } - - public boolean isAbove(Score target) { - return this.value > target.value; - } - - public int getValue() { - return value; - } -} diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java index 9025acdc90e..47fb0468626 100644 --- a/src/main/java/blackjack/view/InputView.java +++ b/src/main/java/blackjack/view/InputView.java @@ -1,20 +1,45 @@ package blackjack.view; +import blackjack.InputMapper; +import blackjack.domain.DrawDecision; +import blackjack.domain.bet.BetAmount; +import blackjack.domain.bet.PlayerBets; +import blackjack.domain.player.Player; +import blackjack.domain.player.PlayerName; +import blackjack.domain.player.Players; +import java.util.List; import java.util.Scanner; +import java.util.stream.Collectors; public class InputView { private static final String LINE_SEPARATOR = System.lineSeparator(); + + private final InputMapper inputMapper; private final Scanner scanner = new Scanner(System.in); - public String readNames() { + public InputView(InputMapper inputMapper) { + this.inputMapper = inputMapper; + } + + public List readNames() { System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); - return scanner.nextLine(); + return inputMapper.mapToPlayerNames(scanner.nextLine()); } - public String readDrawPlan(String name) { + public DrawDecision readDrawDecision(String name) { String message = String.format("%s는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)", name); System.out.println(String.join("", LINE_SEPARATOR, message)); - return scanner.nextLine(); + return inputMapper.mapToDrawDecision(scanner.nextLine()); + } + + public PlayerBets readBetInformation(Players players) { + return new PlayerBets(players.getPlayers().stream() + .collect(Collectors.toMap(player -> player, this::readBetAmount))); + } + + public BetAmount readBetAmount(Player player) { + System.out.println(String.format("%s의 배팅 금액은?", player.getName())); + return new BetAmount(Integer.parseInt(scanner.nextLine())); } } diff --git a/src/main/java/blackjack/view/MessageResolver.java b/src/main/java/blackjack/view/MessageResolver.java index ed237db8a2c..896964d0e94 100644 --- a/src/main/java/blackjack/view/MessageResolver.java +++ b/src/main/java/blackjack/view/MessageResolver.java @@ -1,18 +1,20 @@ package blackjack.view; +import static blackjack.domain.card.Hand.INITIAL_HAND_SIZE; import static blackjack.view.CardDescription.NUMBER_NAME; import static blackjack.view.CardDescription.SHAPE_NAME; -import blackjack.domain.DealerGameResult; +import blackjack.domain.bet.Profit; import blackjack.domain.card.Card; import blackjack.domain.card.CardNumber; import blackjack.domain.card.CardShape; +import blackjack.domain.card.Hand; import blackjack.domain.player.Dealer; -import blackjack.domain.player.Hand; import blackjack.domain.player.Participant; import blackjack.domain.player.Player; import blackjack.domain.player.Players; -import blackjack.domain.rule.Score; +import blackjack.domain.result.PlayerProfits; +import java.util.Map; import java.util.stream.Collectors; public class MessageResolver { @@ -20,9 +22,43 @@ public class MessageResolver { private static final String LINE_SEPARATOR = System.lineSeparator(); private static final String SEPARATOR = ", "; - public String resolveHandOutEventMessage(Players players, int handOutCount) { + public String resolvePlayerHandMessage(Participant participant) { + return String.format("%s 카드: %s", participant.getName(), resolveHandMessage(participant.getHand())); + } + + public String resolveInitialHandsMessage(Dealer dealer, Players players) { + return new StringBuilder() + .append(resolveHandOutEventMessage(players)) + .append(LINE_SEPARATOR) + .append(resolveDealerHandMessage(dealer)) + .append(LINE_SEPARATOR) + .append(resolvePlayersHandMessage(players)) + .toString(); + } + + public String resolveCompletedHandsMessage(Dealer dealer, Players players) { + return new StringBuilder() + .append(resolveDealerPopCountMessage(dealer)) + .append(resolvePlayerScore(dealer)) + .append(LINE_SEPARATOR) + .append(resolvePlayersScore(players)) + .toString(); + } + + public String resolveProfitResultMessage(PlayerProfits playerProfits) { + return new StringBuilder() + .append(LINE_SEPARATOR) + .append("##최종 수익") + .append(LINE_SEPARATOR) + .append(resolveParticipantProfitMessage(playerProfits.calculateDealerProfit())) + .append(LINE_SEPARATOR) + .append(resolvePlayerProfitMessage(playerProfits)) + .toString(); + } + + private String resolveHandOutEventMessage(Players players) { String namesMessage = resolveNamesMessage(players); - String message = String.format("딜러와 %s에게 %d장을 나누었습니다.", namesMessage, handOutCount); + String message = String.format("딜러와 %s에게 %d장을 나누었습니다.", namesMessage, INITIAL_HAND_SIZE); return String.join("", LINE_SEPARATOR, message); } @@ -32,8 +68,15 @@ private String resolveNamesMessage(Players players) { .collect(Collectors.joining(SEPARATOR)); } - public String resolvePlayerHandMessage(Participant participant) { - return String.format("%s 카드: %s", participant.getName(), resolveHandMessage(participant.getHand())); + private String resolveDealerHandMessage(Dealer dealer) { + Card card = dealer.getHand().getCards().get(0); + return String.format("%s: %s", dealer.getName(), resolveCardMessage(card)); + } + + private String resolvePlayersHandMessage(Players players) { + return players.getPlayers().stream() + .map(this::resolvePlayerHandMessage) + .collect(Collectors.joining(LINE_SEPARATOR)); } private String resolveHandMessage(Hand hand) { @@ -48,38 +91,39 @@ private String resolveCardMessage(Card card) { return String.format("%s%s", NUMBER_NAME.get(cardNumber), SHAPE_NAME.get(cardShape)); } - public String resolveDealerHandMessage(Dealer dealer) { - Card card = dealer.getHand().getCards().get(0); - return String.format("%s: %s", dealer.getName(), resolveCardMessage(card)); - } - - public String resolveDealerPopCountMessage(int dealerDrawThreshold, int popCount) { - if (popCount > 0) { - String message = String.format("딜러는 %d이하라 %d장의 카드를 더 받았습니다.", dealerDrawThreshold, popCount); + private String resolveDealerPopCountMessage(Dealer dealer) { + if (dealer.countDraw() > 0) { + String message = String.format("딜러는 %d이하라 %d장의 카드를 더 받았습니다.", Dealer.HIT_THRESHOLD, dealer.countDraw()); return String.join("", LINE_SEPARATOR, message, LINE_SEPARATOR); } return ""; } - public String resolveParticipantScoreMessage(Participant participant, Score score) { + private String resolvePlayersScore(Players players) { + return players.getPlayers().stream() + .map(this::resolvePlayerScore) + .collect(Collectors.joining(LINE_SEPARATOR)); + } + + private String resolvePlayerScore(Participant participant) { String handMessage = resolvePlayerHandMessage(participant); - return String.format("%s - 결과: %d", handMessage, score.getValue()); + return String.format("%s - 결과: %d", handMessage, participant.calculateHandScore().getValue()); } - public String resolvePlayerGameResult(Player participant, boolean win) { - return String.format("%s: %s", participant.getName(), resolveGameResultMessage(win)); + private String resolveParticipantProfitMessage(Profit dealerProfit) { + return String.format("딜러: %d", dealerProfit.getValue()); } - private String resolveGameResultMessage(boolean win) { - if (win) { - return "승"; - } - return "패"; + private String resolvePlayerProfitMessage(PlayerProfits playerProfits) { + Map playerProfitMap = playerProfits.getPlayerProfits(); + return playerProfitMap.entrySet().stream() + .map(this::resolveSingleProfitMessage) + .collect(Collectors.joining(LINE_SEPARATOR)); } - public String resolveDealerGameResult(DealerGameResult dealerGameResult) { - String prefix = String.join("", LINE_SEPARATOR, "## 최종 승패"); - String message = String.format("딜러: %d승 %d패", dealerGameResult.getWinCount(), dealerGameResult.getLoseCount()); - return String.join("", prefix, LINE_SEPARATOR, message); + private String resolveSingleProfitMessage(Map.Entry playerProfit) { + String playerName = playerProfit.getKey().getName(); + int profit = playerProfit.getValue().getValue(); + return String.format("%s: %d", playerName, profit); } } diff --git a/src/main/java/blackjack/view/OutputView.java b/src/main/java/blackjack/view/OutputView.java index 0048798f34c..56635caa9e9 100644 --- a/src/main/java/blackjack/view/OutputView.java +++ b/src/main/java/blackjack/view/OutputView.java @@ -1,11 +1,9 @@ package blackjack.view; -import blackjack.domain.DealerGameResult; import blackjack.domain.player.Dealer; -import blackjack.domain.player.Participant; import blackjack.domain.player.Player; import blackjack.domain.player.Players; -import blackjack.domain.rule.Score; +import blackjack.domain.result.PlayerProfits; public class OutputView { @@ -15,31 +13,19 @@ public OutputView(MessageResolver messageResolver) { this.messageResolver = messageResolver; } - public void printHandOutEvent(Players players, int handedCount) { - System.out.println(messageResolver.resolveHandOutEventMessage(players, handedCount)); + public void printParticipantInitialHand(Dealer dealer, Players players) { + System.out.println(messageResolver.resolveInitialHandsMessage(dealer, players)); } - public void printPlayerHand(Player participant) { - System.out.println(messageResolver.resolvePlayerHandMessage(participant)); + public void printPlayerHand(Player player) { + System.out.println(messageResolver.resolvePlayerHandMessage(player)); } - public void printDealerPopCount(int dealerPopThreshold, int count) { - System.out.println(messageResolver.resolveDealerPopCountMessage(dealerPopThreshold, count)); + public void printCompletedHandsStatus(Dealer dealer, Players players) { + System.out.println(messageResolver.resolveCompletedHandsMessage(dealer, players)); } - public void printParticipantScore(Participant participant, Score score) { - System.out.println(messageResolver.resolveParticipantScoreMessage(participant, score)); - } - - public void printPlayerGameResult(Player participant, boolean win) { - System.out.println(messageResolver.resolvePlayerGameResult(participant, win)); - } - - public void printDealerGameResult(DealerGameResult dealerGameResult) { - System.out.println(messageResolver.resolveDealerGameResult(dealerGameResult)); - } - - public void printDealerInitialHand(Dealer dealer) { - System.out.println(messageResolver.resolveDealerHandMessage(dealer)); + public void printProfitResults(PlayerProfits playerProfits) { + System.out.println(messageResolver.resolveProfitResultMessage(playerProfits)); } } diff --git a/src/test/java/blackjack/domain/DrawDecisionTest.java b/src/test/java/blackjack/domain/DrawDecisionTest.java index c5d6dd45761..4e81c7ff554 100644 --- a/src/test/java/blackjack/domain/DrawDecisionTest.java +++ b/src/test/java/blackjack/domain/DrawDecisionTest.java @@ -1,9 +1,14 @@ package blackjack.domain; +import static blackjack.domain.DrawDecision.NO; +import static blackjack.domain.DrawDecision.YES; +import static blackjack.domain.DrawDecision.from; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; @@ -14,8 +19,8 @@ class DrawDecisionTest { @DisplayName("존재하지 않는 코드명이면 예외가 발생한다") @ParameterizedTest @ValueSource(strings = {"1", "libi", "jerry"}) - void testEnumFromInvalidCode(String code) { - assertThatThrownBy(() -> DrawDecision.from(code)) + void enumFromInvalidCodeTest(String code) { + assertThatThrownBy(() -> from(code)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("[ERROR] y또는 n로 입력해주세요"); } @@ -23,7 +28,16 @@ void testEnumFromInvalidCode(String code) { @DisplayName("존재하는 코드명이면 적절한 상수를 반환받는다") @ParameterizedTest @CsvSource(value = {"y, YES", "n, NO",}) - void testEnumFromValidCode(String code, DrawDecision drawDecision) { - assertThat(DrawDecision.from(code)).isEqualTo(drawDecision); + void enumFromValidCodeTest(String code, DrawDecision drawDecision) { + assertThat(from(code)).isEqualTo(drawDecision); + } + + @DisplayName("YES인지 확인할 수 있다") + @Test + void testIsYes() { + assertAll( + () -> assertThat(YES.isYes()).isTrue(), + () -> assertThat(NO.isYes()).isFalse() + ); } } diff --git a/src/test/java/blackjack/domain/rule/ScoreTest.java b/src/test/java/blackjack/domain/ScoreTest.java similarity index 56% rename from src/test/java/blackjack/domain/rule/ScoreTest.java rename to src/test/java/blackjack/domain/ScoreTest.java index 25adcc506f3..6fd9f5c69d4 100644 --- a/src/test/java/blackjack/domain/rule/ScoreTest.java +++ b/src/test/java/blackjack/domain/ScoreTest.java @@ -1,8 +1,9 @@ -package blackjack.domain.rule; +package blackjack.domain; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -12,7 +13,7 @@ class ScoreTest { @DisplayName("더 낮은 점수와 비교할 수 있다") @ParameterizedTest @ValueSource(ints = {1, 2, 3, 4, 5}) - void testScoreCompareWithBigger(int value) { + void scoreCompareWithBiggerTest(int value) { Score score = new Score(0); Score target = new Score(value); assertThat(score.isAbove(target)).isFalse(); @@ -21,9 +22,24 @@ void testScoreCompareWithBigger(int value) { @DisplayName("점수가 파라미터를 넘지 않는지 확인할 수 있다") @ParameterizedTest @ValueSource(ints = {1, 2, 3, 4, 5}) - void testScoreCompareWithLower(int value) { + void scoreCompareWithLowerTest(int value) { Score score = new Score(6); Score target = new Score(value); assertThat(score.isAbove(target)).isTrue(); } + + @DisplayName("최고 점수(21)인지 확인할 수 있다") + @Test + void isMaxScoreTest() { + Score score = new Score(21); + assertThat(score.isMaxScore()).isTrue(); + } + + @DisplayName("버스트 된 점수인지 확인할 수 있다") + @ParameterizedTest + @ValueSource(ints = {22, 23, 24, 25}) + void isBustTest(int scoreValue) { + Score score = new Score(scoreValue); + assertThat(score.isBustScore()).isTrue(); + } } diff --git a/src/test/java/blackjack/domain/bet/BetAmountTest.java b/src/test/java/blackjack/domain/bet/BetAmountTest.java new file mode 100644 index 00000000000..03d9db0684f --- /dev/null +++ b/src/test/java/blackjack/domain/bet/BetAmountTest.java @@ -0,0 +1,39 @@ +package blackjack.domain.bet; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +@DisplayName("돈 테스트") +class BetAmountTest { + + @DisplayName("생성 시 범위를 지키지 못하면 생성 검증에 실패한다") + @ParameterizedTest + @ValueSource(ints = {-1, -2, 998, 888, 1_000_000_001}) + void createMoneyWithInvalidRangeTest(int amount) { + assertThatThrownBy(() -> new BetAmount(amount)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 베팅 금액은 1000부터 1000000000이하까지 가능합니다."); + } + + + @DisplayName("생성 검증을 통과하면 생성에 성공한다") + @ParameterizedTest + @ValueSource(ints = {1000, 2000, 30000}) + void createMoneyWithValidDataTest(int amount) { + assertThatCode(() -> new BetAmount(amount)) + .doesNotThrowAnyException(); + } + + @DisplayName("돈에 특정 이율을 적용한 Profit을 계산할 수 있다") + @Test + void calculateProfitTest() { + BetAmount betAmount = new BetAmount(1000); + assertThat(betAmount.calculateProfit(1.5).getValue()).isEqualTo(1500); + } +} diff --git a/src/test/java/blackjack/domain/bet/PlayerBetsTest.java b/src/test/java/blackjack/domain/bet/PlayerBetsTest.java new file mode 100644 index 00000000000..d1a762a6e84 --- /dev/null +++ b/src/test/java/blackjack/domain/bet/PlayerBetsTest.java @@ -0,0 +1,38 @@ +package blackjack.domain.bet; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import blackjack.domain.card.TestHandCreator; +import blackjack.domain.player.Dealer; +import blackjack.domain.player.Player; +import blackjack.domain.player.TestPlayerCreator; +import blackjack.domain.result.PlayerProfits; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("베팅 관리자 도메인 테스트") +class PlayerBetsTest { + + @DisplayName("플레이어들의 통합 수익을 계산할 수 있다") + @Test + void calculatePlayerProfitTest() { + Player player1 = TestPlayerCreator.of("리비", 1, 10); + Player player2 = TestPlayerCreator.of("썬", 3, 4); + Dealer dealer = new Dealer(TestHandCreator.of(3, 4, 5)); + + Map playerMoneyMap = new HashMap<>(); + playerMoneyMap.put(player1, new BetAmount(1000)); + playerMoneyMap.put(player2, new BetAmount(1000)); + PlayerBets playerBets = new PlayerBets(playerMoneyMap); + + PlayerProfits playerProfits = playerBets.calculateProfitResult(dealer); + + assertAll( + () -> assertThat(playerProfits.findProfitOfPlayer(player1).getValue()).isEqualTo(1500), + () -> assertThat(playerProfits.findProfitOfPlayer(player2).getValue()).isEqualTo(-1000) + ); + } +} diff --git a/src/test/java/blackjack/domain/bet/ProfitTest.java b/src/test/java/blackjack/domain/bet/ProfitTest.java new file mode 100644 index 00000000000..1e178de88c0 --- /dev/null +++ b/src/test/java/blackjack/domain/bet/ProfitTest.java @@ -0,0 +1,25 @@ +package blackjack.domain.bet; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("수익 금액 테스트") +class ProfitTest { + + @DisplayName("수익에 다른 수익을 더할 수 있다") + @Test + void addTest() { + Profit profit1 = new Profit(1000); + Profit profit2 = new Profit(-1000); + assertThat(profit1.add(profit2).getValue()).isEqualTo(0); + } + + @DisplayName("수익을 반전시킨 결과를 볼 수 있다") + @Test + void inverseTest() { + Profit profit = new Profit(1000); + assertThat(profit.inverse().getValue()).isEqualTo(-1000); + } +} diff --git a/src/test/java/blackjack/domain/card/CardDeckTest.java b/src/test/java/blackjack/domain/card/CardDeckTest.java index 1c6c858e7f7..4f967b51a59 100644 --- a/src/test/java/blackjack/domain/card/CardDeckTest.java +++ b/src/test/java/blackjack/domain/card/CardDeckTest.java @@ -13,7 +13,7 @@ class CardDeckTest { @DisplayName("덱에서 카드를 뽑을 수 있다") @Test - void testPopCardFromDeck() { + void popCardFromDeckTest() { List cards = new ArrayList<>(); Card card1 = new Card(CardShape.HEART, CardNumber.TWO); Card card2 = new Card(CardShape.CLUB, CardNumber.THREE); @@ -25,12 +25,12 @@ void testPopCardFromDeck() { CardDeck cardDeck = new CardDeck(cards); Card popped = cardDeck.popCard(); - assertThat(popped).isEqualTo(card3); + assertThat(popped).isEqualTo(card1); } @DisplayName("덱에서 횟수만큼 카드를 뽑을 수 있다") @Test - void testPopCardsFromDeck() { + void popCardsFromDeckTest() { List cards = new ArrayList<>(); Card card1 = new Card(CardShape.HEART, CardNumber.TWO); Card card2 = new Card(CardShape.CLUB, CardNumber.THREE); @@ -48,7 +48,7 @@ void testPopCardsFromDeck() { @DisplayName("덱에서 카드를 하나 뽑는 경우 카드가 부족하면 예외가 발생한다") @Test - void testInvalidPopInsufficientDeckCount() { + void invalidPopInsufficientDeckCountTest() { List cards = new ArrayList<>(); CardDeck cardDeck = new CardDeck(cards); @@ -59,7 +59,7 @@ void testInvalidPopInsufficientDeckCount() { @DisplayName("덱에서 카드를 여러개 뽑는 경우 카드가 부족하면 예외가 발생한다") @Test - void testInvalidPopCardsInsufficientDeckCount() { + void invalidPopCardsInsufficientDeckCountTest() { List cards = new ArrayList<>(); Card card1 = new Card(CardShape.HEART, CardNumber.TWO); Card card2 = new Card(CardShape.CLUB, CardNumber.THREE); diff --git a/src/test/java/blackjack/domain/card/CardNumberTest.java b/src/test/java/blackjack/domain/card/CardNumberTest.java index 0a9493f1bdb..d191d3eabb9 100644 --- a/src/test/java/blackjack/domain/card/CardNumberTest.java +++ b/src/test/java/blackjack/domain/card/CardNumberTest.java @@ -11,7 +11,7 @@ class CardNumberTest { @DisplayName("Ace에 해당하는 지 확인할 수 있다") @Test - void testIsCardNumberAce() { + void isCardNumberAceTest() { assertAll( () -> assertThat(CardNumber.ACE.isAce()).isTrue(), () -> assertThat(CardNumber.TWO.isAce()).isFalse() diff --git a/src/test/java/blackjack/domain/player/HandTest.java b/src/test/java/blackjack/domain/card/HandTest.java similarity index 71% rename from src/test/java/blackjack/domain/player/HandTest.java rename to src/test/java/blackjack/domain/card/HandTest.java index 6ec98853bbf..2a1cbd5d4ec 100644 --- a/src/test/java/blackjack/domain/player/HandTest.java +++ b/src/test/java/blackjack/domain/card/HandTest.java @@ -1,10 +1,7 @@ -package blackjack.domain.player; +package blackjack.domain.card; import static org.assertj.core.api.Assertions.assertThat; -import blackjack.domain.card.Card; -import blackjack.domain.card.CardDeck; -import blackjack.domain.card.TestCardCreator; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -13,21 +10,21 @@ class HandTest { @DisplayName("초기 핸드의 사이즈는 2이다") @Test - void testCreateInitialHandSize() { + void createInitialHandSizeTest() { Hand hand = Hand.createHandFrom(CardDeck.createShuffledDeck()); assertThat(hand.getCards().size()).isEqualTo(2); } @DisplayName("카드의 합을 계산할 수 있다.") @Test - void testHandSummation() { + void handSummationTest() { Hand hand = TestHandCreator.of(2, 2, 2); assertThat(hand.calculateCardSummation()).isEqualTo(6); } @DisplayName("특정 카드를 핸드에 추가할 수 있다") @Test - void testAppendCard() { + void appendCardTest() { Card card = TestCardCreator.from(2); Hand hand = TestHandCreator.of(); hand.appendCard(card); @@ -36,45 +33,52 @@ void testAppendCard() { @DisplayName("몇개의 카드를 더 뽑은 상태인지 확인할 수 있다") @Test - void testCountPop() { + void countPopTest() { CardDeck cardDeck = CardDeck.createShuffledDeck(); Hand hand = Hand.createHandFrom(cardDeck); hand.appendCard(cardDeck.popCard()); - assertThat(hand.countPop()).isEqualTo(1); - } - - @DisplayName("핸드에 에이스 카드가 몇개 있는지 확인할 수 있다") - @Test - void testCountAceInHand() { - Hand hand = TestHandCreator.of(1, 1, 1, 3, 4, 5); - assertThat(hand.countAce()).isEqualTo(3); + assertThat(hand.countDraw()).isEqualTo(1); } @DisplayName("적절한 점수를 계산할 수 있다 - Ace 카드 없음") @Test - void testCalculateScoreWithNoAce() { + void calculateScoreWithNoAceTest() { Hand hand = TestHandCreator.of(2, 3, 4, 5); assertThat(hand.calculateScore().getValue()).isEqualTo(14); } @DisplayName("적절한 점수를 계산할 수 있다 - Ace 카드가 11로 이용됨") @Test - void testCalculateScoreWithBigAce() { + void calculateScoreWithBigAceTest() { Hand hand = TestHandCreator.of(1, 10); assertThat(hand.calculateScore().getValue()).isEqualTo(21); } @DisplayName("적절한 점수를 계산할 수 있다 - Ace 카드가 1로 이용됨") @Test - void testCalculateScoreWithLowAce() { + void calculateScoreWithLowAceTest() { Hand hand = TestHandCreator.of(1, 10, 2); assertThat(hand.calculateScore().getValue()).isEqualTo(13); } @DisplayName("적절한 점수를 계산할 수 있다 - Ace 카드 두개 이상") @Test - void testCalculateScoreWithMultipleAce() { + void calculateScoreWithMultipleAceTest() { Hand hand = TestHandCreator.of(1, 1, 1); assertThat(hand.calculateScore().getValue()).isEqualTo(13); } + + @DisplayName("손패가 블랙잭인지 알 수 있다") + @Test + void isBlackJackTest() { + Hand hand = TestHandCreator.of(1, 10); + assertThat(hand.isBlackJack()).isTrue(); + } + + @DisplayName("손패의 점수가 21이더라도 드로우 한 이력이 있으면 블랙잭이 아니다") + @Test + void isNotBlackJackTest() { + Hand hand = TestHandCreator.of(2, 9, 10); + assertThat(hand.isBlackJack()).isFalse(); + } } diff --git a/src/test/java/blackjack/domain/card/TestCardCreator.java b/src/test/java/blackjack/domain/card/TestCardCreator.java index 50541eafffe..677f45952fb 100644 --- a/src/test/java/blackjack/domain/card/TestCardCreator.java +++ b/src/test/java/blackjack/domain/card/TestCardCreator.java @@ -6,6 +6,9 @@ public class TestCardCreator { + private TestCardCreator() { + } + public static Card from(int number) { CardNumber cardNumber = Arrays.stream(CardNumber.values()) .filter(cn -> cn.getValue() == number) diff --git a/src/test/java/blackjack/domain/card/TestCardDeckCreator.java b/src/test/java/blackjack/domain/card/TestCardDeckCreator.java new file mode 100644 index 00000000000..bf66e1fd35c --- /dev/null +++ b/src/test/java/blackjack/domain/card/TestCardDeckCreator.java @@ -0,0 +1,16 @@ +package blackjack.domain.card; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class TestCardDeckCreator { + + private TestCardDeckCreator() { + } + + public static CardDeck createFrom(int... cards) { + return new CardDeck(Arrays.stream(cards) + .mapToObj(TestCardCreator::from) + .collect(Collectors.toList())); + } +} diff --git a/src/test/java/blackjack/domain/player/TestHandCreator.java b/src/test/java/blackjack/domain/card/TestHandCreator.java similarity index 78% rename from src/test/java/blackjack/domain/player/TestHandCreator.java rename to src/test/java/blackjack/domain/card/TestHandCreator.java index 8a4aeb229c2..ff070e6e694 100644 --- a/src/test/java/blackjack/domain/player/TestHandCreator.java +++ b/src/test/java/blackjack/domain/card/TestHandCreator.java @@ -1,11 +1,13 @@ -package blackjack.domain.player; +package blackjack.domain.card; -import blackjack.domain.card.TestCardCreator; import java.util.Arrays; import java.util.stream.Collectors; public class TestHandCreator { + private TestHandCreator() { + } + public static Hand of(int... numbers) { return new Hand(Arrays.stream(numbers) .mapToObj(TestCardCreator::from) diff --git a/src/test/java/blackjack/domain/player/DealerTest.java b/src/test/java/blackjack/domain/player/DealerTest.java index c4003d07c05..47a0ef3ec3b 100644 --- a/src/test/java/blackjack/domain/player/DealerTest.java +++ b/src/test/java/blackjack/domain/player/DealerTest.java @@ -2,7 +2,11 @@ import static org.assertj.core.api.Assertions.assertThat; +import blackjack.domain.card.CardDeck; +import blackjack.domain.card.TestCardDeckCreator; +import blackjack.domain.card.TestHandCreator; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -12,7 +16,7 @@ class DealerTest { @DisplayName("딜러는 16점 이하이면 카드를 받을 수 있다") @ParameterizedTest @CsvSource(value = {"6, 10", "5, 10", "4, 10"}) - void testCannotHit(int card1, int card2) { + void cannotHitTest(int card1, int card2) { Dealer dealer = new Dealer(TestHandCreator.of(card1, card2)); assertThat(dealer.canHit()).isTrue(); } @@ -20,8 +24,17 @@ void testCannotHit(int card1, int card2) { @DisplayName("딜러는 17점 이상이면 카드를 받을 수 없다") @ParameterizedTest @CsvSource(value = {"7, 10", "8, 10", "9, 10"}) - void testCanHit(int card1, int card2) { + void canHitTest(int card1, int card2) { Dealer dealer = new Dealer(TestHandCreator.of(card1, card2)); assertThat(dealer.canHit()).isFalse(); } + + @DisplayName("히트 규칙을 기반으로 덱을 완성할 수 있다") + @Test + void completeDealerHandTest() { + Dealer dealer = new Dealer(TestHandCreator.of(4, 3)); + CardDeck deck = TestCardDeckCreator.createFrom(3, 4, 5, 10, 9); + dealer.completeHand(deck); + assertThat(dealer.calculateHandScore().getValue()).isEqualTo(19); + } } diff --git a/src/test/java/blackjack/domain/player/ParticipantNameTest.java b/src/test/java/blackjack/domain/player/ParticipantNameTest.java index e2f523e93f6..2048c0fda50 100644 --- a/src/test/java/blackjack/domain/player/ParticipantNameTest.java +++ b/src/test/java/blackjack/domain/player/ParticipantNameTest.java @@ -10,7 +10,7 @@ class ParticipantNameTest { @DisplayName("이름은 빈 문자열일 수 없다") @Test - void testCreatePlayerNameWithEmpty() { + void createPlayerNameWithEmptyTest() { assertThatThrownBy(() -> new PlayerName("")) .isInstanceOf(IllegalArgumentException.class) .hasMessage("[ERROR] 이름이 빈 문자열입니다."); diff --git a/src/test/java/blackjack/domain/player/PlayerTest.java b/src/test/java/blackjack/domain/player/PlayerTest.java index ef15046bcd1..9581761405c 100644 --- a/src/test/java/blackjack/domain/player/PlayerTest.java +++ b/src/test/java/blackjack/domain/player/PlayerTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import blackjack.domain.card.TestHandCreator; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -12,7 +13,7 @@ class PlayerTest { @DisplayName("플레이어는 21이 넘으면 히트할 수 없다") @ParameterizedTest @CsvSource(value = {"2, 10, 10", "3, 10, 10", "4, 10, 10"}) - void testCannotHit(int card1, int card2, int card3) { + void cannotHitTest(int card1, int card2, int card3) { Player player = new Player(new PlayerName("썬"), TestHandCreator.of(card1, card2, card3)); assertThat(player.canHit()).isFalse(); } @@ -20,7 +21,7 @@ void testCannotHit(int card1, int card2, int card3) { @DisplayName("플레이어는 21이 넘으면 히트할 수 없다") @ParameterizedTest @CsvSource(value = {"1, 10, 10", "2, 8, 10", "2, 7, 10"}) - void testCanHit(int card1, int card2, int card3) { + void canHitTest(int card1, int card2, int card3) { Player player = new Player(new PlayerName("썬"), TestHandCreator.of(card1, card2, card3)); assertThat(player.canHit()).isTrue(); } diff --git a/src/test/java/blackjack/domain/player/PlayersTest.java b/src/test/java/blackjack/domain/player/PlayersTest.java index c4a6b9f6f0b..25969937948 100644 --- a/src/test/java/blackjack/domain/player/PlayersTest.java +++ b/src/test/java/blackjack/domain/player/PlayersTest.java @@ -12,7 +12,7 @@ class PlayersTest { @DisplayName("참가자들 중 이름이 중복되는 경우는 생성 검증에 실패한다") @Test - void testCreatePlayersWithDuplicateNames() { + void createPlayersWithDuplicateNamesTest() { Player player1 = TestPlayerCreator.of("리비", 1, 2); Player player2 = TestPlayerCreator.of("리비", 3, 4); @@ -23,7 +23,7 @@ void testCreatePlayersWithDuplicateNames() { @DisplayName("플레이어가 없으면 생성에 실패한다") @Test - void testCreatePlayersWithEmptyEntry() { + void createPlayersWithEmptyEntryTest() { assertThatThrownBy(() -> new Players(List.of())) .isInstanceOf(IllegalArgumentException.class) .hasMessage("[ERROR] 플레이어가 없습니다"); @@ -31,7 +31,7 @@ void testCreatePlayersWithEmptyEntry() { @DisplayName("딜러를 제외한 게임 참여자가 3명을 넘는 경우는 생성 검증에 실패한다") @Test - void testCreatePlayersWithInvalidEntryCount() { + void createPlayersWithInvalidEntryCountTest() { Player player1 = TestPlayerCreator.of("리비", 1, 2); Player player2 = TestPlayerCreator.of("제리", 3, 4); Player player3 = TestPlayerCreator.of("잉크", 1, 2); @@ -44,7 +44,7 @@ void testCreatePlayersWithInvalidEntryCount() { @DisplayName("생성 검증을 모두 통과하면 생성에 성공한다") @Test - void testCreateWithValidPlayers() { + void createWithValidPlayersTest() { Player player1 = TestPlayerCreator.of("리비", 1, 2); Player player2 = TestPlayerCreator.of("제리", 3, 4); diff --git a/src/test/java/blackjack/domain/player/TestPlayerCreator.java b/src/test/java/blackjack/domain/player/TestPlayerCreator.java index 003bf10f2a3..130762332e0 100644 --- a/src/test/java/blackjack/domain/player/TestPlayerCreator.java +++ b/src/test/java/blackjack/domain/player/TestPlayerCreator.java @@ -1,7 +1,12 @@ package blackjack.domain.player; +import blackjack.domain.card.TestHandCreator; + public class TestPlayerCreator { + private TestPlayerCreator() { + } + public static Player of(String name, int... hand) { return new Player(new PlayerName(name), TestHandCreator.of(hand)); } diff --git a/src/test/java/blackjack/domain/result/GameResultTest.java b/src/test/java/blackjack/domain/result/GameResultTest.java new file mode 100644 index 00000000000..d27f44fa228 --- /dev/null +++ b/src/test/java/blackjack/domain/result/GameResultTest.java @@ -0,0 +1,58 @@ +package blackjack.domain.result; + +import static blackjack.domain.result.GameResult.BLACKJACK_WIN; +import static blackjack.domain.result.GameResult.PLAYER_LOSE; +import static blackjack.domain.result.GameResult.PLAYER_WIN; +import static blackjack.domain.result.GameResult.PUSH; +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.card.TestHandCreator; +import blackjack.domain.player.Dealer; +import blackjack.domain.player.Player; +import blackjack.domain.player.TestPlayerCreator; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("게임 결과 테스트") +class GameResultTest { + + @DisplayName("플레이어 블랙잭이고 딜러가 블랙잭이 아니면 플레이어가 블랙잭 승리를 한다.") + @Test + void playerBlackJackWinTest() { + Player player = TestPlayerCreator.of("리비", 1, 10); + Dealer dealer = new Dealer(TestHandCreator.of(3, 4)); + assertThat(GameResult.judge(dealer, player)).isEqualTo(BLACKJACK_WIN); + } + + @DisplayName("딜러와 플레이어 모두 21점이더라도 블랙잭 핸드가 있다면 블랙잭쪽이 이긴다.") + @Test + void playerBlackJackWinMaxScoreTest() { + Player player = TestPlayerCreator.of("리비", 1, 10); + Dealer dealer = new Dealer(TestHandCreator.of(9, 2, 10)); + assertThat(GameResult.judge(dealer, player)).isEqualTo(BLACKJACK_WIN); + } + + @DisplayName("딜러와 플레이어 모두 블랙잭인 경우 결과는 PUSH이다.") + @Test + void playerAndDealerBlackJackTest() { + Player player = TestPlayerCreator.of("리비", 1, 10); + Dealer dealer = new Dealer(TestHandCreator.of(1, 10)); + assertThat(GameResult.judge(dealer, player)).isEqualTo(PUSH); + } + + @DisplayName("딜러가 버스트 되고 플레이어가 살아있다면 플레이어가 승리한다") + @Test + void playerWinTest() { + Player player = TestPlayerCreator.of("리비", 10, 10); + Dealer dealer = new Dealer(TestHandCreator.of(2, 10)); + assertThat(GameResult.judge(dealer, player)).isEqualTo(PLAYER_WIN); + } + + @DisplayName("딜러 플레이어 모두 버스트 되지 않은 경우 딜러의 점수보다 플레이어의 점수가 낮다면 플레이어가 패한다") + @Test + void playerLoseTest() { + Player player = TestPlayerCreator.of("리비", 2, 10); + Dealer dealer = new Dealer(TestHandCreator.of(10, 10)); + assertThat(GameResult.judge(dealer, player)).isEqualTo(PLAYER_LOSE); + } +} diff --git a/src/test/java/blackjack/domain/result/PlayerProfitsTest.java b/src/test/java/blackjack/domain/result/PlayerProfitsTest.java new file mode 100644 index 00000000000..900c72c3c5c --- /dev/null +++ b/src/test/java/blackjack/domain/result/PlayerProfitsTest.java @@ -0,0 +1,57 @@ +package blackjack.domain.result; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.bet.Profit; +import blackjack.domain.player.Player; +import blackjack.domain.player.TestPlayerCreator; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("플레이어의 수익 도메인 테스트") +class PlayerProfitsTest { + + @DisplayName("특정 플레이어의 수익을 조회할 수 있다") + @Test + void findProfitOfPlayerTest() { + Player player1 = TestPlayerCreator.of("썬", 1, 2, 3, 4); + Player player2 = TestPlayerCreator.of("리비", 3, 4); + + Map playerProfitMap = new HashMap<>(); + playerProfitMap.put(player1, new Profit(30)); + playerProfitMap.put(player2, new Profit(40)); + PlayerProfits playerProfits = new PlayerProfits(playerProfitMap); + + assertThat(playerProfits.findProfitOfPlayer(player1)).isEqualTo(new Profit(30)); + } + + @DisplayName("전체 플레이어들의 수익을 총합계산할 수 있다") + @Test + void calculateTotalProfitTest() { + Player player1 = TestPlayerCreator.of("썬", 1, 2, 3, 4); + Player player2 = TestPlayerCreator.of("리비", 3, 4); + + Map playerProfitMap = new HashMap<>(); + playerProfitMap.put(player1, new Profit(-30000)); + playerProfitMap.put(player2, new Profit(40000)); + PlayerProfits playerProfits = new PlayerProfits(playerProfitMap); + + assertThat(playerProfits.calculateTotalProfit().getValue()).isEqualTo(10000); + } + + @DisplayName("딜러의 수익을 계산할 수 있다") + @Test + void calculateDealerProfitTest() { + Player player1 = TestPlayerCreator.of("썬", 1, 2, 3, 4); + Player player2 = TestPlayerCreator.of("리비", 3, 4); + + Map playerProfitMap = new HashMap<>(); + playerProfitMap.put(player1, new Profit(-30000)); + playerProfitMap.put(player2, new Profit(40000)); + PlayerProfits playerProfits = new PlayerProfits(playerProfitMap); + + assertThat(playerProfits.calculateDealerProfit().getValue()).isEqualTo(-10000); + } +} diff --git a/src/test/java/blackjack/domain/rule/JudgeTest.java b/src/test/java/blackjack/domain/rule/JudgeTest.java deleted file mode 100644 index fdf79a5e2db..00000000000 --- a/src/test/java/blackjack/domain/rule/JudgeTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package blackjack.domain.rule; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - -import blackjack.domain.DealerGameResult; -import blackjack.domain.player.Dealer; -import blackjack.domain.player.Player; -import blackjack.domain.player.Players; -import blackjack.domain.player.TestHandCreator; -import blackjack.domain.player.TestPlayerCreator; -import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class JudgeTest { - - private Judge judge; - - @BeforeEach - void setUp() { - judge = new Judge(); - } - - @DisplayName("딜러와 플레이어 중 누가 이겼는지 알 수 있다") - @Test - void testSelectWinner() { - Player player = TestPlayerCreator.of("리비", 2, 3); - Dealer dealer = new Dealer(TestHandCreator.of(3, 4)); - assertThat(judge.isPlayerWin(dealer, player)).isFalse(); - } - - @DisplayName("딜러의 전적을 계산할 수 있다") - @Test - void testCalculateDealerResult() { - Player player = TestPlayerCreator.of("리비", 2, 3); - Dealer dealer = new Dealer(TestHandCreator.of(3, 4)); - Players players = new Players(List.of(player)); - DealerGameResult dealerGameResult = judge.calculateDealerGameResult(dealer, players); - assertAll( - () -> assertThat(dealerGameResult.getWinCount()).isEqualTo(1), - () -> assertThat(dealerGameResult.getLoseCount()).isEqualTo(0) - ); - } -}