diff --git a/README.md b/README.md index 556099c4de..b6c80bc2af 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,68 @@ -# java-blackjack +## 기능 요구 사항 -블랙잭 미션 저장소 +### 플레이어 이름 입력 -## 우아한테크코스 코드리뷰 +1. 플레이어의 이름은 ","을 기준으로 구분한다. +2. 플레이어의 이름은 길이가 1 이상이어야 한다. +3. 플레이어의 이름은 알파벳 대소문자와 숫자로만 구성되어야 한다. +4. 플레이어의 이름은 중복되면 안된다. -- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) +### 플레이어 및 딜러 점수 계산 + +1. 플레이어가 소유한 카드의 점수의 합이 플레이어의 점수다. +2. 카드의 점수는 카드의 숫자로 계산한다. +3. Ace 카드는 1 혹은 11 중 카드의 소유자가 더 유리한 것으로 계산한다. +4. King, Queen, Jack은 각각 10으로 계산한다. + +### 게임 진행 규칙 + +1. 모든 플레이어와 딜러는 카드를 2장씩 가지고 시작한다. +2. 플레이어는 서로 돌아가면서 자기 턴을 가진다. +3. 플레이어는 자신의 턴에 자신이 카드를 더 받을지 말지 선택할 수 있다. 단, 자신의 점수가 21 이상인 경우 카드를 더 받을 수 없다. +4. 플레이어는 한 번 카드를 받지 않기로 결정한 경우, 앞으로의 턴에서 카드를 더 받을 수 없다. +5. 모든 플레이어가 카드를 더 받을 수 없는 경우, 딜러의 턴으로 넘어간다. +6. 딜러의 턴에서 딜러는 딜러의 점수가 16점 이하인 경우 카드를 더 받는다. 17점 이상인 경우 카드를 더 받지 않는다. +7. 딜러의 턴이 끝나면, 최종 점수 계산 및 게임의 승패를 가린다. +8. 최종 점수가 21점에 가장 가까우면서 21점을 넘기지 않는 사람이 승리한다. 동점인 플레이어(딜러 포함)이 나온 경우, 무승부로 판단한다. + +### 게임 진행 상황 출력 + +1. 게임이 시작 되자마자, 딜러와 플레이어가 받은 카드를 출력한다. +2. 단, 딜러는 카드를 한장만 출력한다. +3. 이 후 플레이어의 차례마다 플레이어가 소유한 카드를 출력한다. + +### 게임 결과 출력 + +1. 게임을 완료한 후 각 플레이어(딜러 포함)가 보유한 카드 및 점수를 출력한다. +2. 게임을 완료한 후 각 플레이어별로 승패를 출력한다. + - 딜러는 다른 모든 플레이어에 대한 승패가 출력된다. + - 딜러가 아닌 플레이어는 딜러에 대한 승패가 출력된다. + +### 게임 진행 가이드 출력 + +- 게임의 원활한 진행을 위해 가이드 문구를 출력한다. + +## 기능 목록 + +- [x] 플레이어 이름 입력 기능 +- [x] 카드 점수 계산 기능 +- [x] Ace 카드 점수 보정 기능 +- [x] 플레이어 및 딜러 점수 계산 기능 +- [x] 플레이어 및 딜러간 승부 계산 기능 +- [x] 플레이어 및 딜러 손패 출력 기능 +- [x] 게임 결과 출력 기능 +- [x] 게임 승부 결과 출력 기능 +- [x] 덱 관리 기능 +- [x] 보유한 카드 점수 합계 계산 기능 +- [x] 카드 받을지 여부 입력 기능 +- [x] 전체 게임 진행 기능 +- [x] 딜러 Ace 카드 점수 보정 기능 + +## 1단계 피드백 반영 예정 목록 + +- [x] 드로우가 가능한지 확인하는 책임 컨트롤러에서 다른 곳으로 이동 +- [x] 드로우 여부 결정하는 정책과 드로우 방식을 결정하는 정책 분리 +- [x] 의미 있는 상수화 +- [x] 테스트 코드에서 사용하는 테스트 객체 생성 역할을 분리 +- [x] view만을 위한 enum 추가 +- [x] Card Enum화 diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 0000000000..5433dfb075 --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,9 @@ +import controller.BlackjackController; +import domain.card.Deck; + +public class Main { + public static void main(String[] args) { + BlackjackController blackjackController = new BlackjackController(); + blackjackController.startBlackjackGame(Deck.fullDeck()); + } +} diff --git a/src/main/java/controller/BlackjackController.java b/src/main/java/controller/BlackjackController.java new file mode 100644 index 0000000000..2f11057fa9 --- /dev/null +++ b/src/main/java/controller/BlackjackController.java @@ -0,0 +1,147 @@ +package controller; + +import domain.blackjack.Dealer; +import domain.blackjack.DrawResult; +import domain.blackjack.GameResult; +import domain.blackjack.HoldingCards; +import domain.blackjack.Player; +import domain.blackjack.Players; +import domain.card.Card; +import domain.card.Deck; +import domain.card.RandomCardSelectStrategy; +import dto.DealerDTO; +import dto.DealerGameResultDTO; +import dto.PlayerDTO; +import dto.PlayerGameResultDTO; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; +import view.NameInputView; +import view.OutputView; +import view.YesOrNoInputView; +import view.gamer.DealerOutputView; +import view.gamer.PlayerOutputView; +import view.gameresult.GameResultOutputView; + +public class BlackjackController { + + public void startBlackjackGame(Deck deck) { + OutputView.printStartGame(); + final Dealer dealer = generateDealer(); + final Players players = generatePlayers(); + + initialDraw(deck, dealer, players); + printDealerAndPlayers(dealer, players); + + playersTryDraw(deck, players); + dealerTryDraw(deck, dealer); + + printDealerWithPoint(dealer); + printPlayersWithPoint(players); + + printDealerGameResult(dealer, players); + printPlayersGameResult(dealer, players); + } + + + private Dealer generateDealer() { + return Dealer.of(HoldingCards.of()); + } + + private Players generatePlayers() { + return new Players(NameInputView.getNames()); + } + + private void initialDraw(Deck deck, Dealer dealer, Players players) { + final int initialDrawCount = 2; + IntStream.range(0, initialDrawCount).forEach(index -> { + players.forEach(player -> playerDraw(deck, player)); + dealerDraw(deck, dealer); + }); + } + + private DrawResult dealerDraw(Deck deck, Dealer dealer) { + return dealer.draw(deck, RandomCardSelectStrategy.INSTANCE); + } + + private DrawResult playerDraw(Deck deck, Player player) { + return player.draw(deck, RandomCardSelectStrategy.INSTANCE); + } + + private void printDealerAndPlayers(Dealer dealer, Players players) { + OutputView.printInitGameDoneMessage(players.getPlayerNames()); + printDealer(dealer); + players.forEach(BlackjackController::printPlayer); + } + + private static void printDealer(Dealer dealer) { + List rawHoldingCards = dealer.getRawHoldingCardsWithoutFirstCard(); + DealerDTO dealerDTO = new DealerDTO(rawHoldingCards, dealer.getRawSummationCardPoint()); + DealerOutputView.printWithoutSummationCardPoint(dealerDTO); + } + + private static void printPlayer(Player player) { + PlayerDTO playerDTO = new PlayerDTO(player.getRawName(), player.getRawHoldingCards(), + player.getRawSummationCardPoint()); + PlayerOutputView.printWithoutSummationCardPoint(playerDTO); + } + + private void playersTryDraw(Deck deck, Players players) { + players.forEach(player -> playerTryDraw(deck, player)); + } + + private void playerTryDraw(Deck deck, Player player) { + boolean hasNextDrawChance = true; + while (hasNextDrawChance) { + hasNextDrawChance = playerTryDrawOnce(deck, player); + } + } + + private boolean playerTryDrawOnce(Deck deck, Player player) { + boolean needToDraw = YesOrNoInputView.getYNAsBoolean(player.getRawName()); + DrawResult drawResult = null; + if (needToDraw) { + drawResult = playerDraw(deck, player); + } + printPlayer(player); + if (drawResult == null) { + return false; + } + return drawResult.hasNextChance(); + } + + private void dealerTryDraw(Deck deck, Dealer dealer) { + DrawResult drawResult = dealerDraw(deck, dealer); + if (drawResult.isSuccess()) { + OutputView.printDealerDrawDone(); + } + } + + private void printDealerWithPoint(Dealer dealer) { + DealerDTO dealerDTO = new DealerDTO(dealer.getRawHoldingCards(), + dealer.getRawSummationCardPoint()); + DealerOutputView.print(dealerDTO); + } + + private void printPlayersWithPoint(Players players) { + players.forEach(player -> { + PlayerDTO playerDTO = new PlayerDTO(player.getRawName(), player.getRawHoldingCards(), + player.getRawSummationCardPoint()); + PlayerOutputView.print(playerDTO); + }); + } + + private void printDealerGameResult(Dealer dealer, Players players) { + Map dealerGameResultCounts = dealer.calculateGameResultWithPlayers(players); + DealerGameResultDTO dealerGameResultDTO = new DealerGameResultDTO(dealerGameResultCounts); + GameResultOutputView.print(dealerGameResultDTO); + } + + private void printPlayersGameResult(Dealer dealer, Players players) { + List playerGameResultDTOs = players.calculateGameResultsWithAsMap(dealer) + .entrySet().stream() + .map(entry -> new PlayerGameResultDTO(entry.getKey(), entry.getValue())) + .toList(); + GameResultOutputView.print(playerGameResultDTOs); + } +} diff --git a/src/main/java/domain/blackjack/BlackJackGameMachine.java b/src/main/java/domain/blackjack/BlackJackGameMachine.java new file mode 100644 index 0000000000..592478b67a --- /dev/null +++ b/src/main/java/domain/blackjack/BlackJackGameMachine.java @@ -0,0 +1,63 @@ +package domain.blackjack; + +import static domain.card.CardName.TEN; + +import domain.card.Card; +import domain.card.CardDrawCondition; +import domain.card.CardSelectStrategy; +import domain.card.Deck; +import java.util.List; + +class BlackJackGameMachine { + private final HoldingCards holdingCards; + + BlackJackGameMachine(HoldingCards holdingCards) { + this.holdingCards = holdingCards; + } + + DrawResult draw(Deck deck, CardSelectStrategy cardSelectStrategy, CardDrawCondition cardDrawCondition) { + if (isBust() || !cardDrawCondition.canDraw()) { + return DrawResult.fail("카드를 더이상 뽑을 수 없습니다.", false); + } + try { + Card draw = deck.draw(cardSelectStrategy); + holdingCards.add(draw); + return DrawResult.success(!isBust()); + } catch (IllegalArgumentException | IllegalStateException e) { + return DrawResult.fail(e, !isBust()); + } + } + + SummationCardPoint calculateSummationCardPoint() { + SummationCardPoint summationCardPoint = holdingCards.calculateTotalPoint(); + if (hasAceInHoldingCards()) { + int rawPoint = fixPoint(summationCardPoint.summationCardPoint()); + return new SummationCardPoint(rawPoint); + } + return summationCardPoint; + } + + private int fixPoint(int rawPoint) { + SummationCardPoint fixPoint = new SummationCardPoint(rawPoint + TEN.getCardNumber()); + if (!fixPoint.isDeadPoint()) { + return fixPoint.summationCardPoint(); + } + return rawPoint; + } + + List getRawHoldingCards() { + return List.copyOf(holdingCards.getHoldingCards()); + } + + int calculateSummationCardPointAsInt() { + return calculateSummationCardPoint().summationCardPoint(); + } + + boolean isBust() { + return calculateSummationCardPoint().isDeadPoint(); + } + + boolean hasAceInHoldingCards() { + return holdingCards.hasAce(); + } +} diff --git a/src/main/java/domain/blackjack/CardPoint.java b/src/main/java/domain/blackjack/CardPoint.java new file mode 100644 index 0000000000..ba3f1640ca --- /dev/null +++ b/src/main/java/domain/blackjack/CardPoint.java @@ -0,0 +1,5 @@ +package domain.blackjack; + +record CardPoint(int point) { + +} diff --git a/src/main/java/domain/blackjack/CardPointCalculator.java b/src/main/java/domain/blackjack/CardPointCalculator.java new file mode 100644 index 0000000000..318129bd76 --- /dev/null +++ b/src/main/java/domain/blackjack/CardPointCalculator.java @@ -0,0 +1,17 @@ +package domain.blackjack; + +import static domain.card.CardName.TEN; + +import domain.card.Card; +import domain.card.CardName; + +class CardPointCalculator { + static CardPoint calculate(Card card) { + CardName cardName = card.cardName(); + int cardNumber = cardName.getCardNumber(); + if (cardNumber > TEN.getCardNumber()) { + return new CardPoint(TEN.getCardNumber()); + } + return new CardPoint(cardNumber); + } +} diff --git a/src/main/java/domain/blackjack/Dealer.java b/src/main/java/domain/blackjack/Dealer.java new file mode 100644 index 0000000000..99801d4c5e --- /dev/null +++ b/src/main/java/domain/blackjack/Dealer.java @@ -0,0 +1,39 @@ +package domain.blackjack; + +import domain.card.Card; +import domain.card.CardSelectStrategy; +import domain.card.Deck; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class Dealer extends Gamer { + + public static Dealer of(HoldingCards holdingCards) { + return new Dealer(new BlackJackGameMachine(holdingCards)); + } + + Dealer(BlackJackGameMachine blackJackGameMachine) { + super(blackJackGameMachine); + } + + @Override + public DrawResult draw(Deck deck, CardSelectStrategy cardSelectStrategy) { + return blackJackGameMachine.draw(deck, cardSelectStrategy, new DealerCardDrawCondition(blackJackGameMachine)); + } + + public List getRawHoldingCardsWithoutFirstCard() { + List rawHoldingCards = new ArrayList<>(blackJackGameMachine.getRawHoldingCards()); + rawHoldingCards.remove(0); + return List.copyOf(rawHoldingCards); + } + + public Map calculateGameResultWithPlayers(Players players) { + List gameResults = players.calculateGameResultsWith(this).stream() + .map(GameResult::changeBase) + .toList(); + return gameResults.stream() + .collect(Collectors.groupingBy(gameResult -> gameResult, Collectors.summingInt(value -> 1))); + } +} diff --git a/src/main/java/domain/blackjack/DealerCardDrawCondition.java b/src/main/java/domain/blackjack/DealerCardDrawCondition.java new file mode 100644 index 0000000000..f4690a6448 --- /dev/null +++ b/src/main/java/domain/blackjack/DealerCardDrawCondition.java @@ -0,0 +1,20 @@ +package domain.blackjack; + +import domain.card.CardDrawCondition; + +public final class DealerCardDrawCondition implements CardDrawCondition { + private final BlackJackGameMachine blackJackGameMachine; + + public DealerCardDrawCondition(BlackJackGameMachine blackJackGameMachine) { + this.blackJackGameMachine = blackJackGameMachine; + } + + @Override + public boolean canDraw() { + final int rawDealerDrawThresholdPoint = 16; + SummationCardPoint dealerDrawThresholdPoint = new SummationCardPoint(rawDealerDrawThresholdPoint); + + SummationCardPoint summationCardPoint = blackJackGameMachine.calculateSummationCardPoint(); + return !summationCardPoint.isBiggerThan(dealerDrawThresholdPoint); + } +} diff --git a/src/main/java/domain/blackjack/DrawResult.java b/src/main/java/domain/blackjack/DrawResult.java new file mode 100644 index 0000000000..e5a6a65eb5 --- /dev/null +++ b/src/main/java/domain/blackjack/DrawResult.java @@ -0,0 +1,35 @@ +package domain.blackjack; + +public class DrawResult { + private final String failCause; + private final boolean hasNextChance; + + public static DrawResult success(boolean hasNextChance) { + return new DrawResult(null, hasNextChance); + } + + public static DrawResult fail(Exception drawFailCause, boolean hasNextChance) { + return new DrawResult(drawFailCause.getMessage(), hasNextChance); + } + + public static DrawResult fail(String failCause, boolean hasNextChance) { + return new DrawResult(failCause, hasNextChance); + } + + private DrawResult(String failCause, boolean hasNextChance) { + this.failCause = failCause; + this.hasNextChance = hasNextChance; + } + + public boolean hasNextChance() { + return hasNextChance; + } + + public boolean isSuccess() { + return failCause == null; + } + + String getFailCause() { + return failCause; + } +} diff --git a/src/main/java/domain/blackjack/GameResult.java b/src/main/java/domain/blackjack/GameResult.java new file mode 100644 index 0000000000..4dd8119565 --- /dev/null +++ b/src/main/java/domain/blackjack/GameResult.java @@ -0,0 +1,15 @@ +package domain.blackjack; + +public enum GameResult { + WIN, LOSE, TIE; + + public GameResult changeBase() { + if (this == WIN) { + return LOSE; + } + if (this == LOSE) { + return WIN; + } + return TIE; + } +} diff --git a/src/main/java/domain/blackjack/GameResultCalculator.java b/src/main/java/domain/blackjack/GameResultCalculator.java new file mode 100644 index 0000000000..26eecc90d6 --- /dev/null +++ b/src/main/java/domain/blackjack/GameResultCalculator.java @@ -0,0 +1,38 @@ +package domain.blackjack; + +public class GameResultCalculator { + /** + * baseGamer의 otherGamer 에 대한 승부 결과 반환 + * + * @param baseBlackJackGameMachine 기준 게이머 + * @param otherBlackJackGameMachine 상대 게이머 + * @return baseGamer의 otherGamer 에 대한 승부 결과 + */ + public static GameResult calculate(BlackJackGameMachine baseBlackJackGameMachine, + BlackJackGameMachine otherBlackJackGameMachine) { + if (baseBlackJackGameMachine.isBust() && otherBlackJackGameMachine.isBust()) { + return GameResult.TIE; + } + if (baseBlackJackGameMachine.isBust()) { + return GameResult.LOSE; + } + if (otherBlackJackGameMachine.isBust()) { + return GameResult.WIN; + } + return getGameResultWhenNobodyDead(baseBlackJackGameMachine, otherBlackJackGameMachine); + } + + private static GameResult getGameResultWhenNobodyDead(BlackJackGameMachine baseBlackJackGameMachine, + BlackJackGameMachine otherBlackJackGameMachine) { + SummationCardPoint baseGamerSummationCardPoint = baseBlackJackGameMachine.calculateSummationCardPoint(); + SummationCardPoint otherGamerSummationCardPoint = otherBlackJackGameMachine.calculateSummationCardPoint(); + + if (baseGamerSummationCardPoint.isBiggerThan(otherGamerSummationCardPoint)) { + return GameResult.WIN; + } + if (baseGamerSummationCardPoint.equals(otherGamerSummationCardPoint)) { + return GameResult.TIE; + } + return GameResult.LOSE; + } +} diff --git a/src/main/java/domain/blackjack/Gamer.java b/src/main/java/domain/blackjack/Gamer.java new file mode 100644 index 0000000000..2bb01fe0dd --- /dev/null +++ b/src/main/java/domain/blackjack/Gamer.java @@ -0,0 +1,28 @@ +package domain.blackjack; + +import domain.card.Card; +import domain.card.CardSelectStrategy; +import domain.card.Deck; +import java.util.List; + +abstract class Gamer { + protected final BlackJackGameMachine blackJackGameMachine; + + public Gamer(BlackJackGameMachine blackJackGameMachine) { + this.blackJackGameMachine = blackJackGameMachine; + } + + public abstract DrawResult draw(Deck deck, CardSelectStrategy cardSelectStrategy); + + public final List getRawHoldingCards() { + return blackJackGameMachine.getRawHoldingCards(); + } + + public final int getRawSummationCardPoint() { + return blackJackGameMachine.calculateSummationCardPointAsInt(); + } + + public final GameResult calculateGameResult(Gamer other) { + return GameResultCalculator.calculate(blackJackGameMachine, other.blackJackGameMachine); + } +} diff --git a/src/main/java/domain/blackjack/HoldingCards.java b/src/main/java/domain/blackjack/HoldingCards.java new file mode 100644 index 0000000000..3bbbe1f8b0 --- /dev/null +++ b/src/main/java/domain/blackjack/HoldingCards.java @@ -0,0 +1,40 @@ +package domain.blackjack; + +import domain.card.Card; +import domain.card.CardName; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class HoldingCards { + private final List holdingCards; + + private HoldingCards(List holdingCards) { + this.holdingCards = holdingCards; + } + + public static HoldingCards of(Card... cards) { + return new HoldingCards(new ArrayList<>(List.of(cards))); + } + + SummationCardPoint calculateTotalPoint() { + List cardPoints = holdingCards.stream() + .map(CardPointCalculator::calculate) + .toList(); + + return SummationCardPoint.of(cardPoints); + } + + void add(Card card) { + holdingCards.add(card); + } + + List getHoldingCards() { + return Collections.unmodifiableList(holdingCards); + } + + boolean hasAce() { + return holdingCards.stream() + .anyMatch(card -> card.cardName() == CardName.ACE); + } +} diff --git a/src/main/java/domain/blackjack/Player.java b/src/main/java/domain/blackjack/Player.java new file mode 100644 index 0000000000..ba45411b59 --- /dev/null +++ b/src/main/java/domain/blackjack/Player.java @@ -0,0 +1,26 @@ +package domain.blackjack; + +import domain.card.CardSelectStrategy; +import domain.card.Deck; + +public class Player extends Gamer { + private final String name; + + public static Player from(String name, HoldingCards holdingCards) { + return new Player(name, new BlackJackGameMachine(holdingCards)); + } + + private Player(String name, BlackJackGameMachine blackJackGameMachine) { + super(blackJackGameMachine); + this.name = name; + } + + @Override + public DrawResult draw(Deck deck, CardSelectStrategy cardSelectStrategy) { + return blackJackGameMachine.draw(deck, cardSelectStrategy, new PlayerCardDrawCondition(blackJackGameMachine)); + } + + public String getRawName() { + return name; + } +} diff --git a/src/main/java/domain/blackjack/PlayerCardDrawCondition.java b/src/main/java/domain/blackjack/PlayerCardDrawCondition.java new file mode 100644 index 0000000000..d7d7b1b196 --- /dev/null +++ b/src/main/java/domain/blackjack/PlayerCardDrawCondition.java @@ -0,0 +1,16 @@ +package domain.blackjack; + +import domain.card.CardDrawCondition; + +public final class PlayerCardDrawCondition implements CardDrawCondition { + private final BlackJackGameMachine player; + + public PlayerCardDrawCondition(BlackJackGameMachine player) { + this.player = player; + } + + @Override + public boolean canDraw() { + return !player.isBust(); + } +} diff --git a/src/main/java/domain/blackjack/Players.java b/src/main/java/domain/blackjack/Players.java new file mode 100644 index 0000000000..afd4414d16 --- /dev/null +++ b/src/main/java/domain/blackjack/Players.java @@ -0,0 +1,35 @@ +package domain.blackjack; + +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +public class Players { + private final List players; + + public Players(List playerNames) { + this.players = playerNames.stream() + .map(playerName -> Player.from(playerName, HoldingCards.of())) + .toList(); + } + + public Map calculateGameResultsWithAsMap(Dealer dealer) { + return players.stream() + .collect(Collectors.toMap(Player::getRawName, player -> player.calculateGameResult(dealer))); + } + + List calculateGameResultsWith(Dealer dealer) { + return players.stream() + .map(player -> player.calculateGameResult(dealer)) + .toList(); + } + + public void forEach(Consumer consumer) { + players.forEach(consumer); + } + + public List getPlayerNames() { + return players.stream().map(Player::getRawName).toList(); + } +} diff --git a/src/main/java/domain/blackjack/SummationCardPoint.java b/src/main/java/domain/blackjack/SummationCardPoint.java new file mode 100644 index 0000000000..d8f5ff4194 --- /dev/null +++ b/src/main/java/domain/blackjack/SummationCardPoint.java @@ -0,0 +1,23 @@ +package domain.blackjack; + +import java.util.List; + +public record SummationCardPoint(int summationCardPoint) { + private static final int DEAD_POINT_THRESHOLD = 21; + + static SummationCardPoint of(List cardPoints) { + int summationCardPoint = cardPoints.stream() + .mapToInt(CardPoint::point) + .sum(); + return new SummationCardPoint(summationCardPoint); + } + + boolean isBiggerThan(SummationCardPoint other) { + int otherPoint = other.summationCardPoint(); + return summationCardPoint > otherPoint; + } + + boolean isDeadPoint() { + return this.isBiggerThan(new SummationCardPoint(DEAD_POINT_THRESHOLD)); + } +} diff --git a/src/main/java/domain/card/Card.java b/src/main/java/domain/card/Card.java new file mode 100644 index 0000000000..4f28046ceb --- /dev/null +++ b/src/main/java/domain/card/Card.java @@ -0,0 +1,98 @@ +package domain.card; + +import static domain.card.CardName.ACE; +import static domain.card.CardName.EIGHT; +import static domain.card.CardName.FIVE; +import static domain.card.CardName.FOUR; +import static domain.card.CardName.JACK; +import static domain.card.CardName.KING; +import static domain.card.CardName.NINE; +import static domain.card.CardName.QUEEN; +import static domain.card.CardName.SEVEN; +import static domain.card.CardName.SIX; +import static domain.card.CardName.TEN; +import static domain.card.CardName.THREE; +import static domain.card.CardName.TWO; +import static domain.card.CardType.CLOVER; +import static domain.card.CardType.DIAMOND; +import static domain.card.CardType.HEART; +import static domain.card.CardType.SPADE; + +public enum Card { + + ACE_HEART(ACE, HEART), + TWO_HEART(TWO, HEART), + THREE_HEART(THREE, HEART), + FOUR_HEART(FOUR, HEART), + FIVE_HEART(FIVE, HEART), + SIX_HEART(SIX, HEART), + SEVEN_HEART(SEVEN, HEART), + EIGHT_HEART(EIGHT, HEART), + NINE_HEART(NINE, HEART), + TEN_HEART(TEN, HEART), + JACK_HEART(JACK, HEART), + QUEEN_HEART(QUEEN, HEART), + KING_HEART(KING, HEART), + ACE_SPADE(ACE, SPADE), + TWO_SPADE(TWO, SPADE), + THREE_SPADE(THREE, SPADE), + FOUR_SPADE(FOUR, SPADE), + FIVE_SPADE(FIVE, SPADE), + SIX_SPADE(SIX, SPADE), + SEVEN_SPADE(SEVEN, SPADE), + EIGHT_SPADE(EIGHT, SPADE), + NINE_SPADE(NINE, SPADE), + TEN_SPADE(TEN, SPADE), + JACK_SPADE(JACK, SPADE), + QUEEN_SPADE(QUEEN, SPADE), + KING_SPADE(KING, SPADE), + ACE_CLOVER(ACE, CLOVER), + TWO_CLOVER(TWO, CLOVER), + THREE_CLOVER(THREE, CLOVER), + FOUR_CLOVER(FOUR, CLOVER), + FIVE_CLOVER(FIVE, CLOVER), + SIX_CLOVER(SIX, CLOVER), + SEVEN_CLOVER(SEVEN, CLOVER), + EIGHT_CLOVER(EIGHT, CLOVER), + NINE_CLOVER(NINE, CLOVER), + TEN_CLOVER(TEN, CLOVER), + JACK_CLOVER(JACK, CLOVER), + QUEEN_CLOVER(QUEEN, CLOVER), + KING_CLOVER(KING, CLOVER), + ACE_DIAMOND(ACE, DIAMOND), + TWO_DIAMOND(TWO, DIAMOND), + THREE_DIAMOND(THREE, DIAMOND), + FOUR_DIAMOND(FOUR, DIAMOND), + FIVE_DIAMOND(FIVE, DIAMOND), + SIX_DIAMOND(SIX, DIAMOND), + SEVEN_DIAMOND(SEVEN, DIAMOND), + EIGHT_DIAMOND(EIGHT, DIAMOND), + NINE_DIAMOND(NINE, DIAMOND), + TEN_DIAMOND(TEN, DIAMOND), + JACK_DIAMOND(JACK, DIAMOND), + QUEEN_DIAMOND(QUEEN, DIAMOND), + KING_DIAMOND(KING, DIAMOND); + + private final CardName cardName; + private final CardType cardType; + + Card(CardName cardName, CardType cardType) { + this.cardName = cardName; + this.cardType = cardType; + } + + public CardName cardName() { + return cardName; + } + + public CardType cardType() { + return cardType; + } + + @Override + public String toString() { + return "Card[" + + "name=" + cardName + ", " + + "cardType=" + cardType + ']'; + } +} diff --git a/src/main/java/domain/card/CardDrawCondition.java b/src/main/java/domain/card/CardDrawCondition.java new file mode 100644 index 0000000000..eb85014bf6 --- /dev/null +++ b/src/main/java/domain/card/CardDrawCondition.java @@ -0,0 +1,5 @@ +package domain.card; + +public interface CardDrawCondition { + boolean canDraw(); +} diff --git a/src/main/java/domain/card/CardName.java b/src/main/java/domain/card/CardName.java new file mode 100644 index 0000000000..8336f679ee --- /dev/null +++ b/src/main/java/domain/card/CardName.java @@ -0,0 +1,27 @@ +package domain.card; + +public enum CardName { + ACE(1), + TWO(2), + THREE(3), + FOUR(4), + FIVE(5), + SIX(6), + SEVEN(7), + EIGHT(8), + NINE(9), + TEN(10), + JACK(11), + QUEEN(12), + KING(13); + + private final int cardNumber; + + CardName(int cardNumber) { + this.cardNumber = cardNumber; + } + + public int getCardNumber() { + return cardNumber; + } +} diff --git a/src/main/java/domain/card/CardSelectStrategy.java b/src/main/java/domain/card/CardSelectStrategy.java new file mode 100644 index 0000000000..c0b8c7d026 --- /dev/null +++ b/src/main/java/domain/card/CardSelectStrategy.java @@ -0,0 +1,7 @@ +package domain.card; + +import java.util.List; + +public interface CardSelectStrategy { + Card select(List cards); +} diff --git a/src/main/java/domain/card/CardType.java b/src/main/java/domain/card/CardType.java new file mode 100644 index 0000000000..1a06a857b9 --- /dev/null +++ b/src/main/java/domain/card/CardType.java @@ -0,0 +1,5 @@ +package domain.card; + +public enum CardType { + HEART, SPADE, CLOVER, DIAMOND; +} diff --git a/src/main/java/domain/card/Deck.java b/src/main/java/domain/card/Deck.java new file mode 100644 index 0000000000..bd819d156c --- /dev/null +++ b/src/main/java/domain/card/Deck.java @@ -0,0 +1,44 @@ +package domain.card; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class Deck { + private final List cards; + + private Deck(List cards) { + validateDuplicateCard(cards); + this.cards = new ArrayList<>(cards); + } + + public static Deck of(Card... cards) { + return new Deck(List.of(cards)); + } + + public static Deck fullDeck() { + List cards = Arrays.stream(Card.values()) + .toList(); + return new Deck(cards); + } + + private void validateDuplicateCard(List cards) { + Set cardSet = new HashSet<>(cards); + if (cardSet.size() != cards.size()) { + throw new IllegalArgumentException("중복되는 카드가 있습니다."); + } + } + + public Card draw(CardSelectStrategy cardSelectStrategy) { + if (cards.isEmpty()) { + throw new IllegalArgumentException("덱이 비어있습니다."); + } + + Card card = cardSelectStrategy.select(cards); + cards.remove(card); + + return card; + } +} diff --git a/src/main/java/domain/card/RandomCardSelectStrategy.java b/src/main/java/domain/card/RandomCardSelectStrategy.java new file mode 100644 index 0000000000..08e39c442b --- /dev/null +++ b/src/main/java/domain/card/RandomCardSelectStrategy.java @@ -0,0 +1,16 @@ +package domain.card; + +import java.util.List; +import java.util.Random; + +public final class RandomCardSelectStrategy implements CardSelectStrategy { + + public static final RandomCardSelectStrategy INSTANCE = new RandomCardSelectStrategy(); + private final Random random = new Random(); + + @Override + public Card select(List cards) { + int idx = random.nextInt(cards.size()); + return cards.get(idx); + } +} diff --git a/src/main/java/dto/DealerDTO.java b/src/main/java/dto/DealerDTO.java new file mode 100644 index 0000000000..e45ffd5d75 --- /dev/null +++ b/src/main/java/dto/DealerDTO.java @@ -0,0 +1,22 @@ +package dto; + +import domain.card.Card; +import java.util.List; + +public class DealerDTO { + private final List holdingCards; + private final int summationCardPoint; + + public DealerDTO(List holdingCards, int summationCardPoint) { + this.holdingCards = holdingCards; + this.summationCardPoint = summationCardPoint; + } + + public List getHoldingCards() { + return holdingCards; + } + + public int getSummationCardPoint() { + return summationCardPoint; + } +} diff --git a/src/main/java/dto/DealerGameResultDTO.java b/src/main/java/dto/DealerGameResultDTO.java new file mode 100644 index 0000000000..10d15bbd5b --- /dev/null +++ b/src/main/java/dto/DealerGameResultDTO.java @@ -0,0 +1,16 @@ +package dto; + +import domain.blackjack.GameResult; +import java.util.Map; + +public class DealerGameResultDTO { + private final Map dealerGameResultCounts; + + public DealerGameResultDTO(Map dealerGameResultCounts) { + this.dealerGameResultCounts = dealerGameResultCounts; + } + + public Map getDealerGameResultCounts() { + return dealerGameResultCounts; + } +} diff --git a/src/main/java/dto/PlayerDTO.java b/src/main/java/dto/PlayerDTO.java new file mode 100644 index 0000000000..d30b4a43cc --- /dev/null +++ b/src/main/java/dto/PlayerDTO.java @@ -0,0 +1,28 @@ +package dto; + +import domain.card.Card; +import java.util.List; + +public class PlayerDTO { + private final String name; + private final List holdingCards; + private final int summationCardPoint; + + public PlayerDTO(String name, List holdingCards, int summationCardPoint) { + this.name = name; + this.holdingCards = holdingCards; + this.summationCardPoint = summationCardPoint; + } + + public String getName() { + return name; + } + + public List getHoldingCards() { + return holdingCards; + } + + public int getSummationCardPoint() { + return summationCardPoint; + } +} diff --git a/src/main/java/dto/PlayerGameResultDTO.java b/src/main/java/dto/PlayerGameResultDTO.java new file mode 100644 index 0000000000..5b253d059e --- /dev/null +++ b/src/main/java/dto/PlayerGameResultDTO.java @@ -0,0 +1,21 @@ +package dto; + +import domain.blackjack.GameResult; + +public class PlayerGameResultDTO { + private final String gamerName; + private final GameResult gameResult; + + public PlayerGameResultDTO(String gamerName, GameResult gameResult) { + this.gamerName = gamerName; + this.gameResult = gameResult; + } + + public String getGamerName() { + return gamerName; + } + + public GameResult getGameResult() { + return gameResult; + } +} diff --git a/src/main/java/view/Console.java b/src/main/java/view/Console.java new file mode 100644 index 0000000000..e263b50b03 --- /dev/null +++ b/src/main/java/view/Console.java @@ -0,0 +1,22 @@ +package view; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +public class Console { + public static String getInputFromConsole() { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); + String input; + try { + input = bufferedReader.readLine(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return input; + } + + public static void print(String output) { + System.out.print(output); + } +} diff --git a/src/main/java/view/NameInputView.java b/src/main/java/view/NameInputView.java new file mode 100644 index 0000000000..8a8e6001d4 --- /dev/null +++ b/src/main/java/view/NameInputView.java @@ -0,0 +1,28 @@ +package view; + +import java.util.List; + +public class NameInputView { + private static final String SEPARATOR = ","; + + public static List getNames() { + String input = Console.getInputFromConsole(); + validateSeparator(input); + List splitInput = List.of(input.split(SEPARATOR)); + validateBlank(splitInput); + return splitInput; + } + + private static void validateSeparator(String input) { + if (input.startsWith(SEPARATOR) || input.endsWith(SEPARATOR) || input.contains(SEPARATOR + SEPARATOR)) { + throw new IllegalArgumentException("입력 형식이 올바르지 않습니다."); + } + } + + private static void validateBlank(List split) { + boolean isInputContainsBlank = split.stream().anyMatch(String::isBlank); + if (isInputContainsBlank) { + throw new IllegalArgumentException("입력 형식이 올바르지 않습니다."); + } + } +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 0000000000..82705be7d7 --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,23 @@ +package view; + +import java.util.List; + +public class OutputView { + + public static void printStartGame() { + print("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); + } + + private static void print(String output) { + System.out.println(output); + } + + public static void printInitGameDoneMessage(List playerRawNames) { + String namesOutput = String.join(", ", playerRawNames); + print("딜러와 %s에게 2장을 나누었습니다.".formatted(namesOutput)); + } + + public static void printDealerDrawDone() { + print("딜러는 16이하라 한장의 카드를 더 받았습니다.\n"); + } +} diff --git a/src/main/java/view/YesOrNoInputView.java b/src/main/java/view/YesOrNoInputView.java new file mode 100644 index 0000000000..ea8275bee7 --- /dev/null +++ b/src/main/java/view/YesOrNoInputView.java @@ -0,0 +1,16 @@ +package view; + +public class YesOrNoInputView { + public static Boolean getYNAsBoolean(String playerName) { + String inputGuideOutput = "%s은(는) 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)%n".formatted(playerName); + Console.print(inputGuideOutput); + String input = Console.getInputFromConsole(); + if (input.equals("y")) { + return true; + } + if (input.equals("n")) { + return false; + } + throw new IllegalArgumentException("입력 형식이 올바르지 않습니다."); + } +} diff --git a/src/main/java/view/gamer/CardOutputGenerator.java b/src/main/java/view/gamer/CardOutputGenerator.java new file mode 100644 index 0000000000..8d49c0e6de --- /dev/null +++ b/src/main/java/view/gamer/CardOutputGenerator.java @@ -0,0 +1,15 @@ +package view.gamer; + +import domain.card.Card; +import domain.card.CardName; +import domain.card.CardType; + +class CardOutputGenerator { + static String generate(Card card) { + CardName cardName = card.cardName(); + CardType cardType = card.cardType(); + ViewCardName viewCardName = ViewCardName.of(cardName); + ViewCardType viewCardType = ViewCardType.of(cardType); + return viewCardName.getOutput() + viewCardType.getOutput(); + } +} diff --git a/src/main/java/view/gamer/DealerOutputView.java b/src/main/java/view/gamer/DealerOutputView.java new file mode 100644 index 0000000000..6c4cff09ca --- /dev/null +++ b/src/main/java/view/gamer/DealerOutputView.java @@ -0,0 +1,29 @@ +package view.gamer; + +import domain.card.Card; +import dto.DealerDTO; +import java.util.List; +import java.util.stream.Collectors; + +public class DealerOutputView { + public static void print(DealerDTO dealerDTO) { + String outputWithoutSummationCardPoint = generateOutputWithoutSummationCardPoint(dealerDTO); + String summationCardPointOutput = "결과: %d".formatted(dealerDTO.getSummationCardPoint()); + System.out.printf("%s - %s%n", outputWithoutSummationCardPoint, summationCardPointOutput); + } + + private static String generateOutputWithoutSummationCardPoint(DealerDTO dealerDTO) { + String name = "딜러"; + List cards = dealerDTO.getHoldingCards(); + String nameOutput = name + "카드"; + String cardsOutput = cards.stream() + .map(CardOutputGenerator::generate) + .collect(Collectors.joining(", ")); + return "%s: %s".formatted(nameOutput, cardsOutput); + } + + public static void printWithoutSummationCardPoint(DealerDTO dealerDTO) { + String outputWithoutSummationCardPoint = generateOutputWithoutSummationCardPoint(dealerDTO); + System.out.println(outputWithoutSummationCardPoint); + } +} diff --git a/src/main/java/view/gamer/PlayerOutputView.java b/src/main/java/view/gamer/PlayerOutputView.java new file mode 100644 index 0000000000..074c335b40 --- /dev/null +++ b/src/main/java/view/gamer/PlayerOutputView.java @@ -0,0 +1,30 @@ +package view.gamer; + +import domain.card.Card; +import dto.PlayerDTO; +import java.util.List; +import java.util.stream.Collectors; + +public class PlayerOutputView { + + public static void print(PlayerDTO playerDTO) { + String outputWithoutSummationCardPoint = generateOutputWithoutSummationCardPoint(playerDTO); + String summationCardPointOutput = "결과: %d".formatted(playerDTO.getSummationCardPoint()); + System.out.printf("%s - %s%n", outputWithoutSummationCardPoint, summationCardPointOutput); + } + + private static String generateOutputWithoutSummationCardPoint(PlayerDTO playerDTO) { + String name = playerDTO.getName(); + List cards = playerDTO.getHoldingCards(); + String nameOutput = name + "카드"; + String cardsOutput = cards.stream() + .map(CardOutputGenerator::generate) + .collect(Collectors.joining(", ")); + return "%s: %s".formatted(nameOutput, cardsOutput); + } + + public static void printWithoutSummationCardPoint(PlayerDTO playerDTO) { + String outputWithoutSummationCardPoint = generateOutputWithoutSummationCardPoint(playerDTO); + System.out.println(outputWithoutSummationCardPoint); + } +} diff --git a/src/main/java/view/gamer/ViewCardName.java b/src/main/java/view/gamer/ViewCardName.java new file mode 100644 index 0000000000..0302711499 --- /dev/null +++ b/src/main/java/view/gamer/ViewCardName.java @@ -0,0 +1,33 @@ +package view.gamer; + +import domain.card.CardName; + +public enum ViewCardName { + ACE("A"), + TWO("2"), + THREE("3"), + FOUR("4"), + FIVE("5"), + SIX("6"), + SEVEN("7"), + EIGHT("8"), + NINE("9"), + TEN("10"), + JACK("J"), + QUEEN("Q"), + KING("K"); + + private final String output; + + static ViewCardName of(CardName cardName) { + return valueOf(cardName.name()); + } + + ViewCardName(String output) { + this.output = output; + } + + String getOutput() { + return output; + } +} diff --git a/src/main/java/view/gamer/ViewCardType.java b/src/main/java/view/gamer/ViewCardType.java new file mode 100644 index 0000000000..50a45308dc --- /dev/null +++ b/src/main/java/view/gamer/ViewCardType.java @@ -0,0 +1,24 @@ +package view.gamer; + +import domain.card.CardType; + +enum ViewCardType { + HEART("하트"), + SPADE("스페이드"), + CLOVER("클로버"), + DIAMOND("다이아몬드"); + + private final String output; + + ViewCardType(String output) { + this.output = output; + } + + static ViewCardType of(CardType cardType) { + return valueOf(cardType.name()); + } + + public String getOutput() { + return output; + } +} diff --git a/src/main/java/view/gameresult/DealerGameResultOutputGenerator.java b/src/main/java/view/gameresult/DealerGameResultOutputGenerator.java new file mode 100644 index 0000000000..163c1cae41 --- /dev/null +++ b/src/main/java/view/gameresult/DealerGameResultOutputGenerator.java @@ -0,0 +1,21 @@ +package view.gameresult; + +import domain.blackjack.GameResult; +import dto.DealerGameResultDTO; +import java.util.Map; +import java.util.stream.Collectors; + +class DealerGameResultOutputGenerator { + static String generate(DealerGameResultDTO dealerGameResultDTO) { + Map dealerGameResultCounts = dealerGameResultDTO.getDealerGameResultCounts(); + return dealerGameResultCounts.entrySet() + .stream() + .map(mapEntry -> { + Integer resultCount = mapEntry.getValue(); + ViewGameResult viewGameResult = ViewGameResult.of(mapEntry.getKey()); + String viewGameResultOutput = viewGameResult.getOutput(); + return "%d%s".formatted(resultCount, viewGameResultOutput); + }) + .collect(Collectors.joining(" ")); + } +} diff --git a/src/main/java/view/gameresult/GameResultOutputView.java b/src/main/java/view/gameresult/GameResultOutputView.java new file mode 100644 index 0000000000..1e507c3402 --- /dev/null +++ b/src/main/java/view/gameresult/GameResultOutputView.java @@ -0,0 +1,25 @@ +package view.gameresult; + +import dto.DealerGameResultDTO; +import dto.PlayerGameResultDTO; +import java.util.List; + +public class GameResultOutputView { + + public static void print(List playerGameResultDTOs) { + for (PlayerGameResultDTO playerGameResultDTO : playerGameResultDTOs) { + print(playerGameResultDTO); + } + } + + public static void print(PlayerGameResultDTO playerGameResultDTO) { + String gameResultOutput = PlayerGameResultOutputGenerator.generate(playerGameResultDTO); + System.out.println(gameResultOutput); + } + + public static void print(DealerGameResultDTO dealerGameResultDTO) { + String gameResultOutput = DealerGameResultOutputGenerator.generate(dealerGameResultDTO); + System.out.printf("딜러: %s%n", gameResultOutput); + } + +} diff --git a/src/main/java/view/gameresult/PlayerGameResultOutputGenerator.java b/src/main/java/view/gameresult/PlayerGameResultOutputGenerator.java new file mode 100644 index 0000000000..b057e24ff7 --- /dev/null +++ b/src/main/java/view/gameresult/PlayerGameResultOutputGenerator.java @@ -0,0 +1,13 @@ +package view.gameresult; + +import domain.blackjack.GameResult; +import dto.PlayerGameResultDTO; + +class PlayerGameResultOutputGenerator { + static String generate(PlayerGameResultDTO playerGameResultDTO) { + String gamerName = playerGameResultDTO.getGamerName(); + GameResult gameResult = playerGameResultDTO.getGameResult(); + ViewGameResult viewGameResult = ViewGameResult.of(gameResult); + return "%s: %s".formatted(gamerName, viewGameResult.getOutput()); + } +} diff --git a/src/main/java/view/gameresult/ViewGameResult.java b/src/main/java/view/gameresult/ViewGameResult.java new file mode 100644 index 0000000000..b670f0ea85 --- /dev/null +++ b/src/main/java/view/gameresult/ViewGameResult.java @@ -0,0 +1,22 @@ +package view.gameresult; + +import domain.blackjack.GameResult; + +public enum ViewGameResult { + WIN("승"), + LOSE("패"), + TIE("무"); + private final String output; + + static ViewGameResult of(GameResult gameResult) { + return valueOf(gameResult.name()); + } + + ViewGameResult(String output) { + this.output = output; + } + + public String getOutput() { + return output; + } +} diff --git a/src/test/java/domain/blackjack/BlackJackGameMachineTest.java b/src/test/java/domain/blackjack/BlackJackGameMachineTest.java new file mode 100644 index 0000000000..697e401875 --- /dev/null +++ b/src/test/java/domain/blackjack/BlackJackGameMachineTest.java @@ -0,0 +1,81 @@ +package domain.blackjack; + +import static domain.blackjack.TestHoldingCards.DEAD_CARDS; +import static domain.blackjack.TestHoldingCards.ONLY_SEVEN_HEART; +import static domain.blackjack.TestHoldingCards.WIN_CARDS_WITHOUT_ACE; +import static domain.blackjack.TestHoldingCards.WIN_CARDS_WITH_ACE; +import static domain.card.Card.ACE_HEART; +import static domain.card.Card.EIGHT_HEART; +import static domain.card.Card.JACK_HEART; +import static domain.card.Card.QUEEN_HEART; +import static domain.card.Card.TWO_HEART; +import static domain.card.FirstCardSelectStrategy.FIRST_CARD_SELECT_STRATEGY; + +import domain.card.Card; +import domain.card.Deck; +import java.util.List; +import java.util.stream.Stream; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class BlackJackGameMachineTest { + public static Stream isDeadParameters() { + return Stream.of( + Arguments.of(TWO_HEART, true), Arguments.of(ACE_HEART, false) + ); + } + + public static Stream getSummationCardPointParameters() { + return Stream.of( + Arguments.of(new BlackJackGameMachine(ONLY_SEVEN_HEART), new SummationCardPoint(7)), + Arguments.of(new BlackJackGameMachine(WIN_CARDS_WITH_ACE), new SummationCardPoint(21)), + Arguments.of(new BlackJackGameMachine(WIN_CARDS_WITHOUT_ACE), new SummationCardPoint(21)), + Arguments.of(new BlackJackGameMachine(DEAD_CARDS), new SummationCardPoint(22)) + ); + } + + @Test + @DisplayName("카드를 뽑았을 때 소유중인 카드에 제대로 반영되는지 검증") + void draw() { + BlackJackGameMachine blackJackGameMachine = new BlackJackGameMachine(HoldingCards.of()); + Deck deck = Deck.of(JACK_HEART, EIGHT_HEART); + blackJackGameMachine.draw(deck, FIRST_CARD_SELECT_STRATEGY, new PlayerCardDrawCondition(blackJackGameMachine)); + + List actual = blackJackGameMachine.getRawHoldingCards(); + + Assertions.assertThat(actual) + .containsExactly(JACK_HEART); + } + + @Test + @DisplayName("드로우 조건에 따라 드로우 가능 여부가 결정되는지 검증") + void validateDrawLimit() { + BlackJackGameMachine blackJackGameMachine = new BlackJackGameMachine(HoldingCards.of()); + Deck deck = Deck.of(TWO_HEART); + DrawResult drawResult = blackJackGameMachine.draw(deck, FIRST_CARD_SELECT_STRATEGY, () -> true); + Assertions.assertThat(drawResult.isSuccess()).isTrue(); + } + + @ParameterizedTest + @MethodSource("isDeadParameters") + @DisplayName("점수가 21이 넘으면 죽었다고 판단하는지 검증") + void isBust(Card additionalCard, boolean expected) { + BlackJackGameMachine blackJackGameMachine = new BlackJackGameMachine( + HoldingCards.of(JACK_HEART, QUEEN_HEART, additionalCard)); + + Assertions.assertThat(blackJackGameMachine.isBust()).isEqualTo(expected); + } + + @ParameterizedTest + @MethodSource("getSummationCardPointParameters") + @DisplayName("점수가 잘 계산되는지 검증") + void getSummationCardPoint(BlackJackGameMachine blackJackGameMachine, SummationCardPoint expected) { + SummationCardPoint summationCardPoint = blackJackGameMachine.calculateSummationCardPoint(); + Assertions.assertThat(summationCardPoint) + .isEqualTo(expected); + } +} diff --git a/src/test/java/domain/blackjack/CardPointCalculatorTest.java b/src/test/java/domain/blackjack/CardPointCalculatorTest.java new file mode 100644 index 0000000000..6c47bfc48e --- /dev/null +++ b/src/test/java/domain/blackjack/CardPointCalculatorTest.java @@ -0,0 +1,38 @@ +package domain.blackjack; + +import static domain.card.CardName.TEN; + +import domain.card.Card; +import domain.card.CardName; +import java.util.Arrays; +import java.util.stream.Stream; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class CardPointCalculatorTest { + static Stream calculateParameter() { + return Stream.of(Arrays.stream(Card.values()) + .map(card -> Arguments.of(card, makeCardPoint(card.cardName()))) + .toArray(Arguments[]::new)); + } + + private static CardPoint makeCardPoint(CardName cardName) { + CardPoint cardPoint = new CardPoint(cardName.getCardNumber()); + if (cardName.getCardNumber() > TEN.getCardNumber()) { + return new CardPoint(TEN.getCardNumber()); + } + return cardPoint; + } + + @ParameterizedTest + @MethodSource("calculateParameter") + @DisplayName("카드 점수가 제대로 변환되는지 검증") + void calculate(Card card, CardPoint expected) { + CardPoint cardPoint = CardPointCalculator.calculate(card); + Assertions.assertThat(cardPoint) + .isEqualTo(expected); + } +} diff --git a/src/test/java/domain/blackjack/DealerCardDrawConditionTest.java b/src/test/java/domain/blackjack/DealerCardDrawConditionTest.java new file mode 100644 index 0000000000..ef049d812d --- /dev/null +++ b/src/test/java/domain/blackjack/DealerCardDrawConditionTest.java @@ -0,0 +1,35 @@ +package domain.blackjack; + +import static domain.card.Card.ACE_HEART; +import static domain.card.Card.FIVE_HEART; +import static domain.card.Card.JACK_HEART; +import static domain.card.Card.SEVEN_HEART; +import static domain.card.Card.SIX_HEART; + +import java.util.stream.Stream; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class DealerCardDrawConditionTest { + + public static Stream canDrawParameters() { + return Stream.of( + Arguments.of(HoldingCards.of(ACE_HEART, SIX_HEART), false), + Arguments.of(HoldingCards.of(ACE_HEART, FIVE_HEART), true), + Arguments.of(HoldingCards.of(JACK_HEART, SEVEN_HEART), false), + Arguments.of(HoldingCards.of(JACK_HEART, SIX_HEART), true) + ); + } + + @ParameterizedTest + @MethodSource("canDrawParameters") + @DisplayName("딜러의 드로우 여부가 제대로 판단되는지 검증") + void canDraw(HoldingCards holdingCards, boolean expected) { + BlackJackGameMachine dealerMachine = new BlackJackGameMachine(holdingCards); + Assertions.assertThat(new DealerCardDrawCondition(dealerMachine).canDraw()) + .isEqualTo(expected); + } +} diff --git a/src/test/java/domain/blackjack/DealerTest.java b/src/test/java/domain/blackjack/DealerTest.java new file mode 100644 index 0000000000..2e7542621b --- /dev/null +++ b/src/test/java/domain/blackjack/DealerTest.java @@ -0,0 +1,68 @@ +package domain.blackjack; + +import static domain.card.Card.ACE_HEART; +import static domain.card.Card.FIVE_HEART; +import static domain.card.Card.JACK_HEART; +import static domain.card.Card.QUEEN_HEART; +import static domain.card.Card.SEVEN_HEART; +import static domain.card.Card.TWO_HEART; +import static domain.card.FirstCardSelectStrategy.FIRST_CARD_SELECT_STRATEGY; + +import domain.card.Card; +import domain.card.Deck; +import java.util.List; +import java.util.stream.Stream; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class DealerTest { + + @Test + @DisplayName("딜러는 총합이 16이 넘으면 카드를 뽑을 수 없는지 검증") + void validateDealerDrawLimit() { + Deck deck = Deck.of(TWO_HEART); + BlackJackGameMachine blackJackGameMachine = new BlackJackGameMachine(HoldingCards.of(JACK_HEART, SEVEN_HEART)); + Dealer dealer = new Dealer(blackJackGameMachine); + + DrawResult drawResult = dealer.draw(deck, FIRST_CARD_SELECT_STRATEGY); + Assertions.assertThat(drawResult.getFailCause()) + .isEqualTo("카드를 더이상 뽑을 수 없습니다."); + } + + static Stream validateDealerHasNextDrawChanceParameters() { + return Stream.of( + Arguments.of(TWO_HEART, false), Arguments.of(ACE_HEART, true) + ); + } + + @ParameterizedTest + @MethodSource("validateDealerHasNextDrawChanceParameters") + @DisplayName("딜러의 다음 드로우 기회 유무를 제대로 판단하는지 검증") + void validatePlayerHasNextDrawChance(Card cardInHand, boolean expected) { + Deck deck = Deck.of(ACE_HEART); + BlackJackGameMachine blackJackGameMachine = new BlackJackGameMachine( + HoldingCards.of(FIVE_HEART, QUEEN_HEART, cardInHand)); + Dealer dealer = new Dealer(blackJackGameMachine); + + DrawResult drawResult = dealer.draw(deck, FIRST_CARD_SELECT_STRATEGY); + Assertions.assertThat(drawResult.hasNextChance()) + .isEqualTo(expected); + } + + @Test + @DisplayName("카드 하나가 숨겨진 상태로 조회되는지 검증") + void getRawHoldingCardsWithoutFirstCard() { + BlackJackGameMachine blackJackGameMachine = new BlackJackGameMachine( + HoldingCards.of(FIVE_HEART, QUEEN_HEART)); + Dealer dealer = new Dealer(blackJackGameMachine); + List rawHoldingCardsWithoutFirstCard = dealer.getRawHoldingCardsWithoutFirstCard(); + Assertions.assertThat(rawHoldingCardsWithoutFirstCard) + .containsExactly(QUEEN_HEART); + Assertions.assertThat(dealer.getRawHoldingCards()) + .containsExactly(FIVE_HEART, QUEEN_HEART); + } +} diff --git a/src/test/java/domain/blackjack/GameResultCalculatorTest.java b/src/test/java/domain/blackjack/GameResultCalculatorTest.java new file mode 100644 index 0000000000..a787e0bb6e --- /dev/null +++ b/src/test/java/domain/blackjack/GameResultCalculatorTest.java @@ -0,0 +1,51 @@ +package domain.blackjack; + +import static domain.blackjack.GameResult.LOSE; +import static domain.blackjack.GameResult.TIE; +import static domain.blackjack.GameResult.WIN; +import static domain.blackjack.TestHoldingCards.DEAD_CARDS; +import static domain.blackjack.TestHoldingCards.ONLY_SEVEN_HEART; +import static domain.blackjack.TestHoldingCards.ONLY_SIX_HEART; +import static domain.blackjack.TestHoldingCards.TWO_SIX_CARDS; +import static domain.blackjack.TestHoldingCards.WIN_CARDS_WITHOUT_ACE; +import static domain.blackjack.TestHoldingCards.WIN_CARDS_WITH_ACE; + +import java.util.stream.Stream; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class GameResultCalculatorTest { + + public static Stream getGameResultParameters() { + return Stream.of( + Arguments.of(new BlackJackGameMachine(ONLY_SEVEN_HEART), + new BlackJackGameMachine(ONLY_SIX_HEART), WIN), + Arguments.of(new BlackJackGameMachine(ONLY_SIX_HEART), + new BlackJackGameMachine(ONLY_SEVEN_HEART), LOSE), + Arguments.of(new BlackJackGameMachine(ONLY_SEVEN_HEART), + new BlackJackGameMachine(ONLY_SEVEN_HEART), TIE), + Arguments.of(new BlackJackGameMachine(DEAD_CARDS), + new BlackJackGameMachine(ONLY_SEVEN_HEART), LOSE), + Arguments.of(new BlackJackGameMachine(ONLY_SEVEN_HEART), + new BlackJackGameMachine(DEAD_CARDS), WIN), + Arguments.of(new BlackJackGameMachine(DEAD_CARDS), new BlackJackGameMachine(DEAD_CARDS), + TIE), + Arguments.of(new BlackJackGameMachine(WIN_CARDS_WITH_ACE), + new BlackJackGameMachine(TWO_SIX_CARDS), WIN), + Arguments.of(new BlackJackGameMachine(WIN_CARDS_WITH_ACE), + new BlackJackGameMachine(WIN_CARDS_WITHOUT_ACE), TIE) + ); + } + + @ParameterizedTest + @MethodSource("getGameResultParameters") + @DisplayName("승부가 잘 결정되는지 검증") + void calculate(BlackJackGameMachine blackJackGameMachine1, BlackJackGameMachine blackJackGameMachine2, + GameResult expected) { + Assertions.assertThat(GameResultCalculator.calculate(blackJackGameMachine1, blackJackGameMachine2)) + .isEqualTo(expected); + } +} diff --git a/src/test/java/domain/blackjack/GamerTest.java b/src/test/java/domain/blackjack/GamerTest.java new file mode 100644 index 0000000000..ebd6646b96 --- /dev/null +++ b/src/test/java/domain/blackjack/GamerTest.java @@ -0,0 +1,72 @@ +package domain.blackjack; + +import static domain.blackjack.TestHoldingCards.DEAD_CARDS; +import static domain.blackjack.TestHoldingCards.ONLY_SEVEN_HEART; +import static domain.blackjack.TestHoldingCards.WIN_CARDS_WITHOUT_ACE; +import static domain.blackjack.TestHoldingCards.WIN_CARDS_WITH_ACE; +import static domain.card.Card.ACE_HEART; +import static domain.card.Card.EIGHT_HEART; +import static domain.card.Card.JACK_HEART; +import static domain.card.Card.QUEEN_HEART; +import static domain.card.Card.TWO_HEART; +import static domain.card.FirstCardSelectStrategy.FIRST_CARD_SELECT_STRATEGY; + +import domain.card.Card; +import domain.card.Deck; +import java.util.stream.Stream; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class GamerTest { + public static Stream isDeadParameters() { + return Stream.of( + Arguments.of(TWO_HEART, true), Arguments.of(ACE_HEART, false) + ); + } + + public static Stream getSummationCardPointParameters() { + return Stream.of( + Arguments.of(new BlackJackGameMachine(ONLY_SEVEN_HEART), new SummationCardPoint(7)), + Arguments.of(new BlackJackGameMachine(WIN_CARDS_WITH_ACE), new SummationCardPoint(21)), + Arguments.of(new BlackJackGameMachine(WIN_CARDS_WITHOUT_ACE), new SummationCardPoint(21)), + Arguments.of(new BlackJackGameMachine(DEAD_CARDS), new SummationCardPoint(22)) + ); + } + + @Test + @DisplayName("게임 참가자가 카드를 뽑았을 때 점수가 올바르게 계산되는지 검증") + void draw() { + BlackJackGameMachine blackJackGameMachine = new BlackJackGameMachine(HoldingCards.of()); + Deck deck = Deck.of(JACK_HEART, EIGHT_HEART); + blackJackGameMachine.draw(deck, FIRST_CARD_SELECT_STRATEGY, new PlayerCardDrawCondition(blackJackGameMachine)); + + SummationCardPoint actual = blackJackGameMachine.calculateSummationCardPoint(); + SummationCardPoint expected = new SummationCardPoint(10); + + Assertions.assertThat(actual) + .isEqualTo(expected); + } + + @ParameterizedTest + @MethodSource("isDeadParameters") + @DisplayName("게임 참가자의 점수가 21이 넘으면 죽었다고 판단하는지 검증") + void isBust(Card additionalCard, boolean expected) { + BlackJackGameMachine blackJackGameMachine = new BlackJackGameMachine( + HoldingCards.of(JACK_HEART, QUEEN_HEART, additionalCard)); + + Assertions.assertThat(blackJackGameMachine.isBust()).isEqualTo(expected); + } + + @ParameterizedTest + @MethodSource("getSummationCardPointParameters") + @DisplayName("게임 참가자의 점수가 잘 계산되는지 검증") + void getSummationCardPoint(BlackJackGameMachine blackJackGameMachine, SummationCardPoint expected) { + SummationCardPoint summationCardPoint = blackJackGameMachine.calculateSummationCardPoint(); + Assertions.assertThat(summationCardPoint) + .isEqualTo(expected); + } +} diff --git a/src/test/java/domain/blackjack/HoldingCardsTest.java b/src/test/java/domain/blackjack/HoldingCardsTest.java new file mode 100644 index 0000000000..70fd704d5c --- /dev/null +++ b/src/test/java/domain/blackjack/HoldingCardsTest.java @@ -0,0 +1,31 @@ +package domain.blackjack; + +import static domain.card.Card.ACE_HEART; +import static domain.card.Card.JACK_HEART; +import static domain.card.Card.KING_HEART; +import static domain.card.Card.QUEEN_HEART; +import static domain.card.Card.SIX_HEART; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class HoldingCardsTest { + @Test + @DisplayName("포함된 카드의 포인트 합계가 올바른지 검증") + void calculateTotalPoint() { + HoldingCards holdingCards = HoldingCards.of(ACE_HEART, SIX_HEART, JACK_HEART, QUEEN_HEART, KING_HEART); + + SummationCardPoint actual = holdingCards.calculateTotalPoint(); + SummationCardPoint expected = new SummationCardPoint(37); + + Assertions.assertThat(actual).isEqualTo(expected); + } + + @Test + @DisplayName("Ace가 포함되었는지 여부 검증") + void hasAce() { + HoldingCards holdingCards = HoldingCards.of(ACE_HEART); + Assertions.assertThat(holdingCards.hasAce()).isTrue(); + } +} diff --git a/src/test/java/domain/blackjack/PlayerCardDrawConditionTest.java b/src/test/java/domain/blackjack/PlayerCardDrawConditionTest.java new file mode 100644 index 0000000000..64eed5fb86 --- /dev/null +++ b/src/test/java/domain/blackjack/PlayerCardDrawConditionTest.java @@ -0,0 +1,33 @@ +package domain.blackjack; + +import static domain.card.Card.ACE_HEART; +import static domain.card.Card.JACK_HEART; +import static domain.card.Card.QUEEN_HEART; +import static domain.card.Card.TWO_HEART; + +import java.util.stream.Stream; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class PlayerCardDrawConditionTest { + + public static Stream canDrawParameters() { + return Stream.of( + Arguments.of(HoldingCards.of(ACE_HEART, JACK_HEART), true), + Arguments.of(HoldingCards.of(TWO_HEART, JACK_HEART, QUEEN_HEART), false), + Arguments.of(HoldingCards.of(JACK_HEART, QUEEN_HEART), true) + ); + } + + @ParameterizedTest + @MethodSource("canDrawParameters") + @DisplayName("플레이어의 드로우 여부가 제대로 판단되는지 검증") + void canDraw(HoldingCards holdingCards, boolean expected) { + BlackJackGameMachine player = new BlackJackGameMachine(holdingCards); + Assertions.assertThat(new PlayerCardDrawCondition(player).canDraw()) + .isEqualTo(expected); + } +} diff --git a/src/test/java/domain/blackjack/PlayerTest.java b/src/test/java/domain/blackjack/PlayerTest.java new file mode 100644 index 0000000000..1b194bb790 --- /dev/null +++ b/src/test/java/domain/blackjack/PlayerTest.java @@ -0,0 +1,51 @@ +package domain.blackjack; + +import static domain.card.Card.ACE_HEART; +import static domain.card.Card.EIGHT_HEART; +import static domain.card.Card.JACK_HEART; +import static domain.card.Card.JACK_SPADE; +import static domain.card.Card.QUEEN_HEART; +import static domain.card.Card.TWO_HEART; +import static domain.card.FirstCardSelectStrategy.FIRST_CARD_SELECT_STRATEGY; + +import domain.card.Card; +import domain.card.Deck; +import java.util.stream.Stream; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class PlayerTest { + + static Stream validatePlayerHasNextDrawChanceParameters() { + return Stream.of( + Arguments.of(TWO_HEART, false), Arguments.of(ACE_HEART, true) + ); + } + + @Test + @DisplayName("플레이어는 총합이 21이 넘으면 카드를 뽑을 수 없는지 검증") + void validateDrawLimit() { + BlackJackGameMachine blackJackGameMachine = new BlackJackGameMachine( + HoldingCards.of(JACK_HEART, EIGHT_HEART, JACK_SPADE)); + Deck deck = Deck.of(TWO_HEART); + DrawResult drawResult = blackJackGameMachine.draw(deck, FIRST_CARD_SELECT_STRATEGY, new PlayerCardDrawCondition( + blackJackGameMachine)); + Assertions.assertThat(drawResult.getFailCause()) + .isEqualTo("카드를 더이상 뽑을 수 없습니다."); + } + + @ParameterizedTest + @MethodSource("validatePlayerHasNextDrawChanceParameters") + @DisplayName("플레이어의 다음 드로우 기회 유무를 제대로 판단하는지 검증") + void validatePlayerHasNextDrawChance(Card cardInDeck, boolean expected) { + BlackJackGameMachine blackJackGameMachine = new BlackJackGameMachine(HoldingCards.of(JACK_HEART, QUEEN_HEART)); + DrawResult drawResult = blackJackGameMachine.draw(Deck.of(cardInDeck), FIRST_CARD_SELECT_STRATEGY, + new PlayerCardDrawCondition(blackJackGameMachine)); + Assertions.assertThat(drawResult.hasNextChance()) + .isEqualTo(expected); + } +} diff --git a/src/test/java/domain/blackjack/SummationCardPointTest.java b/src/test/java/domain/blackjack/SummationCardPointTest.java new file mode 100644 index 0000000000..842c69e76f --- /dev/null +++ b/src/test/java/domain/blackjack/SummationCardPointTest.java @@ -0,0 +1,21 @@ +package domain.blackjack; + +import java.util.List; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class SummationCardPointTest { + @Test + @DisplayName("카드 포인트 합이 잘 생성되는지 검증") + void getSummationCardPoint() { + List cardPoints = List.of( + new CardPoint(1), new CardPoint(2), + new CardPoint(3), new CardPoint(4) + ); + SummationCardPoint summationCardPoint = SummationCardPoint.of(cardPoints); + + Assertions.assertThat(summationCardPoint) + .isEqualTo(new SummationCardPoint(10)); + } +} diff --git a/src/test/java/domain/blackjack/TestHoldingCards.java b/src/test/java/domain/blackjack/TestHoldingCards.java new file mode 100644 index 0000000000..415bcc90d3 --- /dev/null +++ b/src/test/java/domain/blackjack/TestHoldingCards.java @@ -0,0 +1,19 @@ +package domain.blackjack; + +import static domain.card.Card.ACE_HEART; +import static domain.card.Card.JACK_HEART; +import static domain.card.Card.NINE_HEART; +import static domain.card.Card.QUEEN_HEART; +import static domain.card.Card.SEVEN_HEART; +import static domain.card.Card.SIX_DIAMOND; +import static domain.card.Card.SIX_HEART; +import static domain.card.Card.TWO_HEART; + +public class TestHoldingCards { + static final HoldingCards ONLY_SIX_HEART = HoldingCards.of(SIX_HEART); + static final HoldingCards ONLY_SEVEN_HEART = HoldingCards.of(SEVEN_HEART); + static final HoldingCards DEAD_CARDS = HoldingCards.of(TWO_HEART, JACK_HEART, QUEEN_HEART); + static final HoldingCards WIN_CARDS_WITH_ACE = HoldingCards.of(ACE_HEART, QUEEN_HEART); + static final HoldingCards WIN_CARDS_WITHOUT_ACE = HoldingCards.of(TWO_HEART, JACK_HEART, NINE_HEART); + static final HoldingCards TWO_SIX_CARDS = HoldingCards.of(SIX_HEART, SIX_DIAMOND); +} diff --git a/src/test/java/domain/card/DeckTest.java b/src/test/java/domain/card/DeckTest.java new file mode 100644 index 0000000000..808a07871e --- /dev/null +++ b/src/test/java/domain/card/DeckTest.java @@ -0,0 +1,40 @@ +package domain.card; + +import static domain.card.Card.ACE_HEART; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class DeckTest { + + private static final FirstCardSelectStrategy FIRST_CARD_SELECT_STRATEGY = new FirstCardSelectStrategy(); + + @Test + @DisplayName("원하는 방식대로 카드가 뽑히는지 검증") + void validateDraw() { + Deck deck = Deck.of(ACE_HEART); + Card card = deck.draw(FIRST_CARD_SELECT_STRATEGY); + Assertions.assertThat(card) + .isEqualTo(ACE_HEART); + } + + @Test + @DisplayName("중복된 카드가 포함된 덱이 생성되지 않는지 검증") + void validateDuplicateCard() { + Assertions.assertThatThrownBy(() -> Deck.of(ACE_HEART, ACE_HEART)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("중복되는 카드가 있습니다."); + } + + @Test + @DisplayName("한 번 뽑힌 카드가 또 뽑히지 않는지 검증") + void validateDrawedCardIsRemoved() { + Deck deck = Deck.of(ACE_HEART); + deck.draw(cards -> cards.get(0)); + + Assertions.assertThatThrownBy(() -> deck.draw(FIRST_CARD_SELECT_STRATEGY)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("덱이 비어있습니다."); + } +} diff --git a/src/test/java/domain/card/FirstCardSelectStrategy.java b/src/test/java/domain/card/FirstCardSelectStrategy.java new file mode 100644 index 0000000000..dd090bf8a5 --- /dev/null +++ b/src/test/java/domain/card/FirstCardSelectStrategy.java @@ -0,0 +1,13 @@ +package domain.card; + +import java.util.List; + +public class FirstCardSelectStrategy implements CardSelectStrategy { + + public static final FirstCardSelectStrategy FIRST_CARD_SELECT_STRATEGY = new FirstCardSelectStrategy(); + + @Override + public Card select(List cards) { + return cards.get(0); + } +}