diff --git a/src/main/java/blackjack/Application.java b/src/main/java/blackjack/Application.java index f8d94ab2ca9..72011cb941d 100644 --- a/src/main/java/blackjack/Application.java +++ b/src/main/java/blackjack/Application.java @@ -1,10 +1,12 @@ package blackjack; +import blackjack.config.AppConfig; import blackjack.controller.BlackjackController; public class Application { public static void main(String[] args) { - BlackjackController blackjackController = new BlackjackController(); + AppConfig config = new AppConfig(); + BlackjackController blackjackController = config.blackjackController(); blackjackController.run(); } } diff --git a/src/main/java/blackjack/config/AppConfig.java b/src/main/java/blackjack/config/AppConfig.java new file mode 100644 index 00000000000..5bc162e909d --- /dev/null +++ b/src/main/java/blackjack/config/AppConfig.java @@ -0,0 +1,11 @@ +package blackjack.config; + +import blackjack.controller.BlackjackController; +import blackjack.view.InputView; +import blackjack.view.OutputView; + +public class AppConfig { + public BlackjackController blackjackController(){ + return new BlackjackController(new InputView(), new OutputView()); + } +} diff --git a/src/main/java/blackjack/controller/BlackjackController.java b/src/main/java/blackjack/controller/BlackjackController.java index 9800f496143..b23b1da7aa1 100644 --- a/src/main/java/blackjack/controller/BlackjackController.java +++ b/src/main/java/blackjack/controller/BlackjackController.java @@ -1,85 +1,60 @@ package blackjack.controller; -import blackjack.domain.MatchResult; -import blackjack.domain.Player; +import blackjack.domain.BlackjackGame; +import blackjack.domain.vo.GameResult; import blackjack.dto.DealResultDto; import blackjack.dto.GameResultDto; -import blackjack.dto.PlayerHandDto; -import blackjack.domain.BlackjackGame; import blackjack.util.Parser; +import blackjack.view.InputView; +import blackjack.view.OutputView; +import java.math.BigDecimal; import java.util.Collections; import java.util.List; -import java.util.Map; import static blackjack.util.Parser.splitDelimiter; -import static blackjack.view.InputView.readPlayNames; -import static blackjack.view.InputView.readYesOrNo; -import static blackjack.view.OutputView.printCurrentPlayerHand; -import static blackjack.view.OutputView.printDealResult; -import static blackjack.view.OutputView.printDealerDrawMessage; -import static blackjack.view.OutputView.printEmptyLine; -import static blackjack.view.OutputView.printFinalResult; -import static blackjack.view.OutputView.printGameResult; public class BlackjackController { - public void run() { - List names = inputNames(); - BlackjackGame blackjackGame = BlackjackGame.create(names, Collections::shuffle); - - DealResultDto dealResultDto = deal(blackjackGame); - printDealResult(dealResultDto); + private final InputView inputView; + private final OutputView outputView; - playPlayerTurn(blackjackGame); - playDealerTurn(blackjackGame); - - GameResultDto gameResultDto = blackjackGame.generateGameResult(); - printGameResult(gameResultDto); - - Map playerFinalResult = blackjackGame.getPlayerFinalResult(); - Map dealerFinalResult = blackjackGame.getDealerFinalResult(playerFinalResult); - printFinalResult(playerFinalResult, dealerFinalResult); + public BlackjackController(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; } - private List inputNames() { - String input = readPlayNames(); - Parser.notEmpty(input); - return splitDelimiter(input); - } + public void run() { + List names = inputNames(); + outputView.printEmptyLine(); + BlackjackGame game = BlackjackGame.create(names, Collections::shuffle); - private DealResultDto deal(BlackjackGame blackjackGame) { - blackjackGame.deal(); - return DealResultDto.from(blackjackGame.getPlayers(), blackjackGame.getDealer()); - } + game.betPlayers(name -> { + String amount = inputView.readBetAmount(name); + outputView.printEmptyLine(); + return amount; + }); - private void playPlayerTurn(BlackjackGame blackjackGame) { - for (int i = 0; i < blackjackGame.playerCount(); i++) { - playTurn(blackjackGame, i); - } - } + game.deal(); + outputView.printDealResult(DealResultDto.from(game)); - private void playTurn(BlackjackGame blackjackGame, int index) { - while (blackjackGame.canPlayerHit(index) && wantsToHit(blackjackGame, index)) { - hitAndPrintHand(blackjackGame, index); - } - printEmptyLine(); - } + game.playPlayerTurns( + name -> Parser.parseDrawInput(inputView.readYesOrNo(name)).isHit(), + outputView::printPlayerHand + ); + outputView.printEmptyLine(); - private boolean wantsToHit(BlackjackGame blackjackGame, int index) { - String answer = readYesOrNo(blackjackGame.playerNameByIndex(index)); - DrawCommand drawCommand = Parser.parseDrawInput(answer); - return drawCommand.isHit(); - } + game.playDealerTurn(); + outputView.printDealerDrawMessage(); + + outputView.printGameResult(GameResultDto.from(game)); - private void hitAndPrintHand(BlackjackGame blackjackGame, int index){ - PlayerHandDto playerHandDto = PlayerHandDto.from(blackjackGame.playerDraw(index)); - printCurrentPlayerHand(playerHandDto); + List gameResults = game.calculatePlayerProfits(); + BigDecimal dealerProfit = game.calculateDealerProfit(gameResults); + outputView.printFinalResult(gameResults, dealerProfit); } - private void playDealerTurn(BlackjackGame blackjackGame){ - while (blackjackGame.canDealerHit()) { - printDealerDrawMessage(); - blackjackGame.dealerDraw(); - } + private List inputNames() { + String input = inputView.readPlayNames(); + return splitDelimiter(input); } } \ No newline at end of file diff --git a/src/main/java/blackjack/domain/Bet.java b/src/main/java/blackjack/domain/Bet.java new file mode 100644 index 00000000000..7cb72bbd318 --- /dev/null +++ b/src/main/java/blackjack/domain/Bet.java @@ -0,0 +1,43 @@ +package blackjack.domain; + +import java.math.BigDecimal; +import java.util.Objects; + +public class Bet { + private final BigDecimal amount; + + private Bet(BigDecimal amount) { + validate(amount); + this.amount = amount; + } + + private void validate(BigDecimal amount) { + Objects.requireNonNull(amount, "배팅 금액은 null일 수 없습니다."); + if (amount.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("배팅 금액은 음수가 나올 수 없습니다."); + } + } + + public static Bet init(){ + return new Bet(BigDecimal.ZERO); + } + + public static Bet of(String amount){ + return new Bet(new BigDecimal(amount)); + } + + public BigDecimal getAmount() { + return amount; + } + + @Override + public final boolean equals(Object o) { + if (!(o instanceof Bet bet)) return false; + return Objects.equals(amount, bet.amount); + } + + @Override + public int hashCode() { + return Objects.hashCode(amount); + } +} diff --git a/src/main/java/blackjack/domain/BetDecision.java b/src/main/java/blackjack/domain/BetDecision.java new file mode 100644 index 00000000000..5a4bfdbcbe1 --- /dev/null +++ b/src/main/java/blackjack/domain/BetDecision.java @@ -0,0 +1,6 @@ +package blackjack.domain; + +@FunctionalInterface +public interface BetDecision { + String decideBet(String name); +} diff --git a/src/main/java/blackjack/domain/BlackjackGame.java b/src/main/java/blackjack/domain/BlackjackGame.java index 059e557f042..3db0b783362 100644 --- a/src/main/java/blackjack/domain/BlackjackGame.java +++ b/src/main/java/blackjack/domain/BlackjackGame.java @@ -1,14 +1,16 @@ package blackjack.domain; -import blackjack.dto.GameResultDto; +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; +import blackjack.domain.vo.GameResult; +import blackjack.domain.vo.Payoff; -import java.util.LinkedHashMap; +import java.math.BigDecimal; +import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; - public class BlackjackGame { private final Dealer dealer; private final Players players; @@ -40,52 +42,68 @@ private static Players generatePlayers(List names) { .collect(Collectors.collectingAndThen(Collectors.toList(), Players::of)); } - public void deal() { - players.receiveCards(deck); - dealer.receiveCards(deck.drawSecondTimes()); - } - - public int playerCount() { - return players.count(); - } - - public boolean canPlayerHit(int index) { - return players.playerAt(index).canHit(); + public void betPlayers(BetDecision decision) { + players.forEach(player -> { + String amount = decision.decideBet(player.getName()); + player.placeBet(amount); + }); } - public String playerNameByIndex(int index) { - return players.playerAt(index).name(); + public void deal() { + players.forEach( + player -> { + player.hit(deck.deal()); + player.hit(deck.deal()); + }); + dealer.hit(deck.deal()); + dealer.hit(deck.deal()); } - public Player playerDraw(int index) { - TrumpCard drawn = deck.draw(); - players.playerAt(index).receiveCard(drawn); - return players.playerAt(index); + public void playPlayerTurns(HitDecision decision, TurnDisplay display) { + players.forEach(player -> { + playerTurn(player, decision, display); + }); } - public boolean canDealerHit() { - return dealer.shouldHit(); + private void playerTurn(Player player, HitDecision decision, TurnDisplay display) { + boolean playing = true; + while (player.canHit() && playing) { + playing = processHit(player, decision, display); + } } - public void dealerDraw() { - TrumpCard drawn = deck.draw(); - dealer.receive(drawn); + private boolean processHit(Player player, HitDecision decision, TurnDisplay display) { + if (!decision.wantsHit(player.getName())) { + display.show(player.getName(), player.getCardNames()); + return false; + } + player.hit(deck.deal()); + display.show(player.getName(), player.getCardNames()); + return true; } - public GameResultDto generateGameResult() { - return GameResultDto.from(players, dealer); + public void playDealerTurn() { + while (dealer.canHit()) { + dealer.hit(deck.deal()); + } } - public Map getPlayerFinalResult() { - Map playerResult = new LinkedHashMap<>(); - for (Player player : players.getPlayers()) { - playerResult.put(player, MatchResult.playerResult(player, dealer)); - } - return playerResult; + public List calculatePlayerProfits(){ + List results = new ArrayList<>(); + players.forEach(player -> { + BigDecimal profit = Payoff.playerResult(player, dealer) + .calculateProfit(player.getBet().getAmount()); + results.add(GameResult.from(player.getName(), profit)); + } + ); + return results; } - public Map getDealerFinalResult(Map playerResult) { - return MatchResult.dealerResult(playerResult); + public BigDecimal calculateDealerProfit(List gameResults) { + return gameResults.stream() + .map(GameResult::profit) + .reduce(BigDecimal.ZERO, BigDecimal::add) + .negate(); } public Players getPlayers() { diff --git a/src/main/java/blackjack/domain/Dealer.java b/src/main/java/blackjack/domain/Dealer.java deleted file mode 100644 index 5a7bd327fdf..00000000000 --- a/src/main/java/blackjack/domain/Dealer.java +++ /dev/null @@ -1,53 +0,0 @@ -package blackjack.domain; - -import java.util.List; -import java.util.Objects; - -public class Dealer { - private static final int DEALER_HIT_THRESHOLD = 16; - - private final Hand hand; - - private Dealer(Hand hand) { - validate(hand); - this.hand = hand; - } - - private void validate(Hand hand) { - Objects.requireNonNull(hand, "딜러 덱(카드)는 null일 수 없습니다."); - } - - public static Dealer of() { - return new Dealer(Hand.init()); - } - - public void receiveCards(List cards) { - for (TrumpCard card : cards) { - hand.receive(card); - } - } - - public void receive(TrumpCard card) { - hand.receive(card); - } - - public int score() { - return hand.calculateScore(); - } - - public List getCards() { - return hand.getCards(); - } - - public boolean shouldHit() { - return score() <= DEALER_HIT_THRESHOLD; - } - - public TrumpCard getOpenCard() { - return hand.getCards().getFirst(); - } - - public boolean isBust() { - return score() > 21; - } -} diff --git a/src/main/java/blackjack/domain/Deck.java b/src/main/java/blackjack/domain/Deck.java index 316df52ca8c..2edc2734b13 100644 --- a/src/main/java/blackjack/domain/Deck.java +++ b/src/main/java/blackjack/domain/Deck.java @@ -1,10 +1,8 @@ package blackjack.domain; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; public class Deck { private static final int CARDS_COUNT = 52; @@ -21,10 +19,7 @@ public static Deck of(List cards) { } public static Deck create(ShuffleStrategy strategy) { - List cards = Arrays.stream(Suit.values()) - .flatMap(suit -> Arrays.stream(Rank.values()) - .map(rank -> TrumpCard.of(suit, rank))) - .collect(Collectors.toList()); + List cards = new ArrayList<>(TrumpCard.ALL_CARD); strategy.shuffle(cards); return new Deck(cards); } @@ -51,18 +46,14 @@ private void validateDuplicates(List cards) { } } - public List drawSecondTimes() { - List cards = new ArrayList<>(); - - cards.add(draw()); - cards.add(draw()); - return cards; - } - - public TrumpCard draw() { + public TrumpCard deal() { if (cards.isEmpty()) { - throw new IllegalArgumentException("덱에 카드가 없습니다."); + throw new IllegalStateException("덱에 카드가 없습니다."); } return cards.removeFirst(); } + + public int countCards() { + return cards.size(); + } } diff --git a/src/main/java/blackjack/domain/Hand.java b/src/main/java/blackjack/domain/Hand.java index 2b531026aa8..55406498180 100644 --- a/src/main/java/blackjack/domain/Hand.java +++ b/src/main/java/blackjack/domain/Hand.java @@ -6,6 +6,7 @@ public class Hand { private static final int ACE_DIFFERENCE = 10; private static final int BLACKJACK_THRESHOLD = 21; + private static final int BLACKJACK_CARD_COUNT = 2; private final List cards; @@ -17,12 +18,15 @@ public static Hand init() { return new Hand(); } - public void receive(TrumpCard card) { + public void add(TrumpCard card) { cards.add(card); } - public int countCards() { - return cards.size(); + public boolean isBlackjack(){ + return countCards() == BLACKJACK_CARD_COUNT && calculateScore() == BLACKJACK_THRESHOLD; + } + public boolean isBust() { + return calculateScore() > BLACKJACK_THRESHOLD; } public int calculateScore() { @@ -32,7 +36,7 @@ public int calculateScore() { private int sumCardScores() { return cards.stream() - .mapToInt(TrumpCard::score) + .mapToInt(TrumpCard::getScore) .sum(); } @@ -51,6 +55,20 @@ private int countAces() { .count(); } + public List cardNames() { + return cards.stream() + .map(TrumpCard::name) + .toList(); + } + + public int countCards() { + return cards.size(); + } + + public TrumpCard getFirstCard(){ + return cards.getFirst(); + } + public List getCards() { return List.copyOf(cards); } diff --git a/src/main/java/blackjack/domain/HitDecision.java b/src/main/java/blackjack/domain/HitDecision.java new file mode 100644 index 00000000000..a439a00bf91 --- /dev/null +++ b/src/main/java/blackjack/domain/HitDecision.java @@ -0,0 +1,6 @@ +package blackjack.domain; + +@FunctionalInterface +public interface HitDecision { + boolean wantsHit(String name); +} diff --git a/src/main/java/blackjack/domain/Player.java b/src/main/java/blackjack/domain/Player.java deleted file mode 100644 index ea91099dc7a..00000000000 --- a/src/main/java/blackjack/domain/Player.java +++ /dev/null @@ -1,58 +0,0 @@ -package blackjack.domain; - -import java.util.List; -import java.util.Objects; - -public class Player { - private final Name name; - private final Hand hand; - - private Player(Name name, Hand hand) { - validate(name, hand); - this.name = name; - this.hand = hand; - } - - private void validate(Name name, Hand hand) { - Objects.requireNonNull(name, "플레이어 이름은 null일 수 없습니다."); - Objects.requireNonNull(hand, "플레이어 덱(카드)은 null일 수 없습니다."); - } - - public static Player of(Name name) { - return new Player(name, Hand.init()); - } - - public void receiveCards(List cards) { - for (TrumpCard card : cards) { - hand.receive(card); - } - } - - public void receiveCard(TrumpCard card) { - hand.receive(card); - } - - public int countCards() { - return hand.countCards(); - } - - public int score() { - return hand.calculateScore(); - } - - public String name() { - return name.getName(); - } - - public boolean canHit() { - return score() <= 21; - } - - public boolean isBust() { - return score() > 21; - } - - public List getCards() { - return hand.getCards(); - } -} diff --git a/src/main/java/blackjack/domain/Players.java b/src/main/java/blackjack/domain/Players.java index af486c65118..30c7eedf37f 100644 --- a/src/main/java/blackjack/domain/Players.java +++ b/src/main/java/blackjack/domain/Players.java @@ -1,7 +1,10 @@ package blackjack.domain; +import blackjack.domain.participant.Player; + import java.util.List; import java.util.Objects; +import java.util.function.Consumer; public class Players { private final List players; @@ -22,29 +25,15 @@ public static Players of(List players) { return new Players(players); } - public void receiveCards(Deck deck) { - for (Player player : players) { - player.receiveCards(deck.drawSecondTimes()); - } - } + public void forEach(Consumer action){ + players.forEach(action); + } public int count() { - return this.players.size(); - } - - public boolean canHit(int playerIndex) { - return this.players.get(playerIndex).canHit(); + return players.size(); } public List getPlayers() { - return players; - } - - public void hitPlayer(int index, TrumpCard card) { - players.get(index).receiveCard(card); - } - - public Player playerAt(int index) { - return players.get(index); + return List.copyOf(players); } } diff --git a/src/main/java/blackjack/domain/TrumpCard.java b/src/main/java/blackjack/domain/TrumpCard.java index cdd99bcb350..e71d6aea3ff 100644 --- a/src/main/java/blackjack/domain/TrumpCard.java +++ b/src/main/java/blackjack/domain/TrumpCard.java @@ -1,8 +1,15 @@ package blackjack.domain; +import java.util.Arrays; +import java.util.List; import java.util.Objects; public class TrumpCard { + public static final List ALL_CARD = Arrays.stream(Suit.values()) + .flatMap(suit -> Arrays.stream(Rank.values()) + .map(rank -> TrumpCard.of(suit, rank))) + .toList(); + private final Suit suit; private final Rank rank; @@ -25,16 +32,12 @@ public boolean isAce() { return rank == Rank.ACE; } - public String koreanName() { - return suit.getKoreanName(); - } - - public int score() { + public int getScore() { return rank.getScore(); } - public String symbol() { - return rank.getSymbol(); + public String name() { + return rank.getSymbol() + suit.getKoreanName(); } @Override diff --git a/src/main/java/blackjack/domain/TurnDisplay.java b/src/main/java/blackjack/domain/TurnDisplay.java new file mode 100644 index 00000000000..321239a1cc6 --- /dev/null +++ b/src/main/java/blackjack/domain/TurnDisplay.java @@ -0,0 +1,8 @@ +package blackjack.domain; + +import java.util.List; + +@FunctionalInterface +public interface TurnDisplay { + void show(String name, List cardNames); +} diff --git a/src/main/java/blackjack/domain/participant/Dealer.java b/src/main/java/blackjack/domain/participant/Dealer.java new file mode 100644 index 00000000000..2c290a3bb3d --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Dealer.java @@ -0,0 +1,26 @@ +package blackjack.domain.participant; + +import blackjack.domain.Hand; +import blackjack.domain.Name; +import blackjack.domain.TrumpCard; + +public class Dealer extends Participant { + private static final int DEALER_HIT_THRESHOLD = 16; + + private Dealer(Name name, Hand hand) { + super(name, hand); + } + + public static Dealer of() { + return new Dealer(Name.of("딜러"), Hand.init()); + } + + @Override + public boolean canHit() { + return score() <= DEALER_HIT_THRESHOLD; + } + + public TrumpCard openFirstCard(){ + return hand.getFirstCard(); + } +} diff --git a/src/main/java/blackjack/domain/participant/Participant.java b/src/main/java/blackjack/domain/participant/Participant.java new file mode 100644 index 00000000000..748da17837b --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Participant.java @@ -0,0 +1,62 @@ +package blackjack.domain.participant; + +import blackjack.domain.Hand; +import blackjack.domain.Name; +import blackjack.domain.TrumpCard; + +import java.util.List; +import java.util.Objects; + +abstract class Participant { + protected final Name name; + protected final Hand hand; + + protected Participant(Name name, Hand hand) { + validate(name, hand); + this.name = name; + this.hand = hand; + } + + private void validate(Name name, Hand hand) { + Objects.requireNonNull(name, "참가자 이름은 null일 수 없습니다."); + Objects.requireNonNull(hand, "참자가 덱(카드)은 null일 수 없습니다."); + } + + public void hit(TrumpCard card) { + hand.add(card); + } + + public boolean hasHigherScoreThan(Participant other){ + return this.score() > other.score(); + } + + public int score() { + return hand.calculateScore(); + } + + public boolean isBlackjack() { + return hand.isBlackjack(); + } + + public boolean isBust() { + return hand.isBust(); + } + + public int countCards() { + return hand.countCards(); + } + + public List getCardNames() { + return hand.cardNames(); + } + + public String getName() { + return name.getName(); + } + + public abstract boolean canHit(); + + public List getCards() { + return hand.getCards(); + } +} diff --git a/src/main/java/blackjack/domain/participant/Player.java b/src/main/java/blackjack/domain/participant/Player.java new file mode 100644 index 00000000000..feb1b4fc301 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Player.java @@ -0,0 +1,32 @@ +package blackjack.domain.participant; + +import blackjack.domain.Bet; +import blackjack.domain.Hand; +import blackjack.domain.Name; + +public class Player extends Participant{ + private Bet bet; + + private Player(Name name, Hand hand, Bet bet) { + super(name, hand); + this.bet = bet; + } + + public static Player of(Name name) { + return new Player(name, Hand.init(), Bet.init()); + } + + public void placeBet(String amount) { + this.bet = Bet.of(amount); + } + + public Bet getBet() { + return bet; + } + + @Override + public boolean canHit() { + return !isBust(); + } +} + diff --git a/src/main/java/blackjack/domain/vo/GameResult.java b/src/main/java/blackjack/domain/vo/GameResult.java new file mode 100644 index 00000000000..2b43b9b6c71 --- /dev/null +++ b/src/main/java/blackjack/domain/vo/GameResult.java @@ -0,0 +1,12 @@ +package blackjack.domain.vo; + +import java.math.BigDecimal; + +public record GameResult( + String name, + BigDecimal profit +) { + public static GameResult from(String name, BigDecimal profit) { + return new GameResult(name, profit); + } +} diff --git a/src/main/java/blackjack/domain/MatchResult.java b/src/main/java/blackjack/domain/vo/MatchResult.java similarity index 84% rename from src/main/java/blackjack/domain/MatchResult.java rename to src/main/java/blackjack/domain/vo/MatchResult.java index 563da76bc34..7e3cfb34eab 100644 --- a/src/main/java/blackjack/domain/MatchResult.java +++ b/src/main/java/blackjack/domain/vo/MatchResult.java @@ -1,4 +1,7 @@ -package blackjack.domain; +package blackjack.domain.vo; + +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; import java.util.Map; @@ -33,7 +36,7 @@ public static MatchResult playerResult(Player player, Dealer dealer) { return DRAW; } - public static Map dealerResult(Map playerResults) { + public static Map dealerResult(Map playerResults) { return Map.of( "승", playerResults.values().stream().filter(r -> r == LOSE).count(), "패", playerResults.values().stream().filter(r -> r == WIN).count(), diff --git a/src/main/java/blackjack/domain/vo/Payoff.java b/src/main/java/blackjack/domain/vo/Payoff.java new file mode 100644 index 00000000000..72cf3d85d82 --- /dev/null +++ b/src/main/java/blackjack/domain/vo/Payoff.java @@ -0,0 +1,55 @@ +package blackjack.domain.vo; + +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; + +import java.math.BigDecimal; + +public enum Payoff { + BLACKJACK(1.5), + WIN(1.0), + DRAW(0.0), + LOSE(-1.0); + + private final double earningRate; + + Payoff(double earningRate) { + this.earningRate = earningRate; + } + + public static Payoff playerResult(Player player, Dealer dealer) { + if (player.isBust()) { + return LOSE; + } + + if (player.isBlackjack() && dealer.isBlackjack()) { + return DRAW; + } + + if (player.isBlackjack()) { + return BLACKJACK; + } + + if (dealer.isBlackjack()) { + return LOSE; + } + + if (dealer.isBust()) { + return WIN; + } + + if (player.hasHigherScoreThan(dealer)) { + return WIN; + } + + if (dealer.hasHigherScoreThan(player)) { + return LOSE; + } + + return DRAW; + } + + public BigDecimal calculateProfit(BigDecimal betAmount) { + return betAmount.multiply(BigDecimal.valueOf(earningRate)); + } +} diff --git a/src/main/java/blackjack/dto/CardDto.java b/src/main/java/blackjack/dto/CardDto.java index 2f638744520..01636beece8 100644 --- a/src/main/java/blackjack/dto/CardDto.java +++ b/src/main/java/blackjack/dto/CardDto.java @@ -6,6 +6,6 @@ public record CardDto( String display ) { public static CardDto from(TrumpCard card) { - return new CardDto(card.symbol() + card.koreanName()); + return new CardDto(card.name()); } } diff --git a/src/main/java/blackjack/dto/DealResultDto.java b/src/main/java/blackjack/dto/DealResultDto.java index f3d37eed668..b90917d53a4 100644 --- a/src/main/java/blackjack/dto/DealResultDto.java +++ b/src/main/java/blackjack/dto/DealResultDto.java @@ -1,19 +1,18 @@ package blackjack.dto; -import blackjack.domain.Dealer; -import blackjack.domain.Players; - +import blackjack.domain.BlackjackGame; import java.util.List; public record DealResultDto( List playerHands, CardDto dealerOpenCard ) { - public static DealResultDto from(Players players, Dealer dealer) { - List playerHands = players.getPlayers().stream() + public static DealResultDto from(BlackjackGame game) { + List playerHands = game.getPlayers().getPlayers().stream() .map(PlayerHandDto::from) .toList(); - CardDto dealerCard = CardDto.from(dealer.getOpenCard()); + CardDto dealerCard = CardDto.from(game.getDealer().openFirstCard()); return new DealResultDto(playerHands, dealerCard); } } + diff --git a/src/main/java/blackjack/dto/DealerScoreDto.java b/src/main/java/blackjack/dto/DealerScoreDto.java index 7c25f36dec9..c6c85b5cf8c 100644 --- a/src/main/java/blackjack/dto/DealerScoreDto.java +++ b/src/main/java/blackjack/dto/DealerScoreDto.java @@ -1,6 +1,6 @@ package blackjack.dto; -import blackjack.domain.Dealer; +import blackjack.domain.participant.Dealer; import java.util.List; diff --git a/src/main/java/blackjack/dto/GameResultDto.java b/src/main/java/blackjack/dto/GameResultDto.java index c31fd692a1c..04e8824e796 100644 --- a/src/main/java/blackjack/dto/GameResultDto.java +++ b/src/main/java/blackjack/dto/GameResultDto.java @@ -1,7 +1,6 @@ package blackjack.dto; -import blackjack.domain.Dealer; -import blackjack.domain.Players; +import blackjack.domain.BlackjackGame; import java.util.List; @@ -9,12 +8,12 @@ public record GameResultDto( List playerResults, DealerScoreDto dealerResult ) { - public static GameResultDto from(Players players, Dealer dealer) { - List playerResults = players.getPlayers().stream() + public static GameResultDto from(BlackjackGame game) { + List playerResults = game.getPlayers().getPlayers().stream() .map(PlayerScoreDto::from) .toList(); - DealerScoreDto dealerResult = DealerScoreDto.from(dealer); + DealerScoreDto dealerResult = DealerScoreDto.from(game.getDealer()); return new GameResultDto(playerResults, dealerResult); } } \ No newline at end of file diff --git a/src/main/java/blackjack/dto/PlayerHandDto.java b/src/main/java/blackjack/dto/PlayerHandDto.java index 20686addd17..54cc7c6ea16 100644 --- a/src/main/java/blackjack/dto/PlayerHandDto.java +++ b/src/main/java/blackjack/dto/PlayerHandDto.java @@ -1,6 +1,6 @@ package blackjack.dto; -import blackjack.domain.Player; +import blackjack.domain.participant.Player; import java.util.List; @@ -12,6 +12,6 @@ public static PlayerHandDto from(Player player) { List cardDtos = player.getCards().stream() .map(CardDto::from) .toList(); - return new PlayerHandDto(player.name(), cardDtos); + return new PlayerHandDto(player.getName(), cardDtos); } } diff --git a/src/main/java/blackjack/dto/PlayerScoreDto.java b/src/main/java/blackjack/dto/PlayerScoreDto.java index 410db8d084b..fa37d0172c5 100644 --- a/src/main/java/blackjack/dto/PlayerScoreDto.java +++ b/src/main/java/blackjack/dto/PlayerScoreDto.java @@ -1,6 +1,6 @@ package blackjack.dto; -import blackjack.domain.Player; +import blackjack.domain.participant.Player; import java.util.List; @@ -13,6 +13,6 @@ public static PlayerScoreDto from(Player player) { List cardDtos = player.getCards().stream() .map(CardDto::from) .toList(); - return new PlayerScoreDto(player.name(), cardDtos, player.score()); + return new PlayerScoreDto(player.getName(), cardDtos, player.score()); } } diff --git a/src/main/java/blackjack/util/Parser.java b/src/main/java/blackjack/util/Parser.java index 1b19adf2b26..f7db8b491aa 100644 --- a/src/main/java/blackjack/util/Parser.java +++ b/src/main/java/blackjack/util/Parser.java @@ -5,11 +5,14 @@ import java.util.ArrayList; import java.util.List; +import static blackjack.util.StringUtils.notEmpty; + public class Parser { private Parser(){ } public static List splitDelimiter(String playerNames) { + notEmpty(playerNames); String[] values = playerNames.split(","); List splitResult = new ArrayList<>(); @@ -24,10 +27,4 @@ public static DrawCommand parseDrawInput(String input){ notEmpty(input); return DrawCommand.of(input); } - - public static void notEmpty(String value) { - if (value == null || value.isBlank()) { - throw new IllegalArgumentException("입력이 비어있습니다."); - } - } } diff --git a/src/main/java/blackjack/util/StringUtils.java b/src/main/java/blackjack/util/StringUtils.java new file mode 100644 index 00000000000..5c6e19e3319 --- /dev/null +++ b/src/main/java/blackjack/util/StringUtils.java @@ -0,0 +1,12 @@ +package blackjack.util; + +public class StringUtils { + private StringUtils(){ + } + + public static void notEmpty(String value) { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException("입력이 비어있습니다."); + } + } +} diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java index 3088b93e2dd..fee18684a6a 100644 --- a/src/main/java/blackjack/view/InputView.java +++ b/src/main/java/blackjack/view/InputView.java @@ -5,16 +5,18 @@ public class InputView { private static final Scanner SCANNER = new Scanner(System.in); - private InputView() { - } - - public static String readPlayNames() { + public String readPlayNames() { System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); return SCANNER.nextLine(); } - public static String readYesOrNo(String playerName) { + public String readYesOrNo(String playerName) { System.out.println(playerName + "는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)"); return SCANNER.nextLine(); } + + public String readBetAmount(String playerName) { + System.out.println(playerName + "의 배팅 금액은?"); + return SCANNER.nextLine(); + } } diff --git a/src/main/java/blackjack/view/OutputView.java b/src/main/java/blackjack/view/OutputView.java index 5201baca2d5..b853d9805e4 100644 --- a/src/main/java/blackjack/view/OutputView.java +++ b/src/main/java/blackjack/view/OutputView.java @@ -1,7 +1,6 @@ package blackjack.view; -import blackjack.domain.MatchResult; -import blackjack.domain.Player; +import blackjack.domain.vo.GameResult; import blackjack.dto.CardDto; import blackjack.dto.DealResultDto; import blackjack.dto.DealerScoreDto; @@ -9,15 +8,15 @@ import blackjack.dto.PlayerHandDto; import blackjack.dto.PlayerScoreDto; +import java.math.BigDecimal; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; public class OutputView { - private OutputView() { - } - public static void printDealResult(DealResultDto dealResultDto) { + public void printDealResult(DealResultDto dealResultDto) { + String names = joinPlayerNames(dealResultDto); + System.out.println("딜러와 " + names + "에게 2장을 나누었습니다."); System.out.println("딜러카드: " + dealResultDto.dealerOpenCard().display()); for (PlayerHandDto playerHand : dealResultDto.playerHands()) { printCurrentPlayerHand(playerHand); @@ -25,12 +24,23 @@ public static void printDealResult(DealResultDto dealResultDto) { System.out.println(); } - public static void printCurrentPlayerHand(PlayerHandDto playerHand) { + private String joinPlayerNames(DealResultDto dealResultDto) { + return dealResultDto.playerHands().stream() + .map(PlayerHandDto::name) + .collect(Collectors.joining(", ")); + } + + public void printCurrentPlayerHand(PlayerHandDto playerHand) { String cards = formatCards(playerHand.cards()); System.out.println(playerHand.name() + "카드: " + cards); } - public static void printGameResult(GameResultDto gameResultDto) { + public void printPlayerHand(String name, List cards) { + String cardDisplay = String.join(", ", cards); + System.out.println(name + "카드: " + cardDisplay); + } + + public void printGameResult(GameResultDto gameResultDto) { DealerScoreDto dealer = gameResultDto.dealerResult(); System.out.println("딜러카드: " + formatCards(dealer.cards()) + " - 결과: " + dealer.score()); @@ -42,34 +52,29 @@ public static void printGameResult(GameResultDto gameResultDto) { System.out.println(); } - private static String formatCards(List cards) { + private String formatCards(List cards) { return cards.stream() .map(CardDto::display) .collect(Collectors.joining(", ")); } - public static void printDealerDrawMessage() { + public void printDealerDrawMessage() { System.out.println("딜러는 16이하라 한장의 카드를 더 받았습니다."); System.out.println(); } - public static void printFinalResult(Map playerFinalResult, Map dealerFinalResult) { + public void printFinalResult(List gameResults, BigDecimal dealerProfit) { System.out.println("## 최종 승패"); - printDealerFinalResult(dealerFinalResult.get("승"), dealerFinalResult.get("패")); - for (Player player : playerFinalResult.keySet()) { - printPlayerFinalResult(player.name(), playerFinalResult.get(player).getDisplay()); - } - } - - private static void printDealerFinalResult(long dealerWinCount, long dealerLoseCount) { - System.out.println("딜러: " + dealerWinCount + "승 " + dealerLoseCount + "패"); + printDealerFinalResult(dealerProfit); + gameResults.forEach(result -> + System.out.println(result.name() + ": " + result.profit())); } - private static void printPlayerFinalResult(String playerName, String result) { - System.out.println(playerName + ": " + result); + private void printDealerFinalResult(BigDecimal dealerProfit) { + System.out.println("딜러: " + dealerProfit); } - public static void printEmptyLine(){ + public void printEmptyLine(){ System.out.println(); } } diff --git a/src/test/java/blackjack/domain/BetTest.java b/src/test/java/blackjack/domain/BetTest.java new file mode 100644 index 00000000000..38be9dbbc24 --- /dev/null +++ b/src/test/java/blackjack/domain/BetTest.java @@ -0,0 +1,43 @@ +package blackjack.domain; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; + +import static org.assertj.core.api.Assertions.assertThat; + +class BetTest { + + @Test + void 배팅_금액은_음수가_들어오면_예외처리한다(){ + Assertions.assertThatThrownBy(() -> Bet.of("-5000")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("배팅 금액은 음수가 나올 수 없습니다."); + } + + @Test + void 배팅_금액이_양수로_들어오면_정상입력된다(){ + //given + String amount = "5000"; + + //when + Bet bet = Bet.of(amount); + //then + assertThat(bet).isNotNull(); + } + + @Test + void 배팅_금액은_초반에_0으로_초기화된다(){ + Bet bet = Bet.init(); + assertThat(bet.getAmount()).isEqualByComparingTo(BigDecimal.ZERO); + } + + @Test + void 배팅_금액이_추가되면_그_금액만큼_늘어난다(){ + String amount = "5000"; + Bet bet = Bet.of(amount); + + assertThat(bet.getAmount()).isEqualByComparingTo(new BigDecimal("5000")); + } +} \ No newline at end of file diff --git a/src/test/java/blackjack/domain/BlackjackGameTest.java b/src/test/java/blackjack/domain/BlackjackGameTest.java new file mode 100644 index 00000000000..ea0cf81994a --- /dev/null +++ b/src/test/java/blackjack/domain/BlackjackGameTest.java @@ -0,0 +1,79 @@ +package blackjack.domain; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class BlackjackGameTest { + private BlackjackGame createGame(String... names){ + return BlackjackGame.create(List.of(names), cards -> {}); + } + + @Test + void 모든_플레이어에게_배팅_금액을_입력받는다(){ + BlackjackGame game = createGame("pobi", "jason"); + + List askedPlayers = new ArrayList<>(); + game.betPlayers(name -> { + askedPlayers.add(name); + return "10000"; + }); + + assertThat(askedPlayers).containsExactly("pobi", "jason"); + } + + @Test + void 모든_플레이어는_2장_딜러는_2장을_가진다(){ + BlackjackGame game = createGame("pobi", "jason"); + + game.deal(); + + assertThat(game.getDealer().countCards()).isEqualTo(2); + List cardCounts = new ArrayList<>(); + game.getPlayers().forEach(player -> { + cardCounts.add(player.countCards()); + } + ); + assertThat(cardCounts).containsOnly(2); + } + + @Test + void 플레이어가_hit을_선택하면_카드를_받는다(){ + BlackjackGame game = createGame("dalsoo"); + game.deal(); + List cardCounts = new ArrayList<>(); + + game.playPlayerTurns( + name -> true, + (name, cards) -> cardCounts.add(cards.size()) + ); + assertThat(cardCounts).isSorted(); + } + + @Test + void 플레이어가_stay를_선택하면_카드를_받지_않는다(){ + BlackjackGame game = createGame("dalsoo"); + game.deal(); + List cardCounts = new ArrayList<>(); + + game.playPlayerTurns( + name -> false, + (name, cards) -> cardCounts.add(cards.size()) + ); + + assertThat(cardCounts).containsExactly(2); + } + + @Test + void 딜러는_16이하면_카드를_더_받는다(){ + BlackjackGame game = createGame("dalsoo"); + game.deal(); + game.playPlayerTurns(name -> false, (name, cards) -> {}); + game.playDealerTurn(); + + assertThat(game.getDealer().canHit()).isFalse(); + } +} \ No newline at end of file diff --git a/src/test/java/blackjack/domain/DealerTest.java b/src/test/java/blackjack/domain/DealerTest.java index d65f6bd39e1..317e289065c 100644 --- a/src/test/java/blackjack/domain/DealerTest.java +++ b/src/test/java/blackjack/domain/DealerTest.java @@ -2,82 +2,59 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.util.ArrayList; -import java.util.List; -import org.junit.jupiter.api.BeforeEach; +import blackjack.domain.participant.Dealer; import org.junit.jupiter.api.Test; class DealerTest { - private Dealer dealer; + private Dealer dealerWith(TrumpCard... cards) { + Dealer dealer = Dealer.of(); + for (TrumpCard card : cards) { + dealer.hit(card); + } + return dealer; + } - @BeforeEach - void setUp() { - dealer = Dealer.of(); + private TrumpCard card(Suit suit, Rank rank) { + return TrumpCard.of(suit, rank); } @Test void 딜러를_생성한다() { + Dealer dealer = Dealer.of(); assertThat(dealer).isNotNull(); } @Test void 딜러가_카드_1장을_받는다() { - TrumpCard spadeAce = TrumpCard.of(Suit.SPADE, Rank.ACE); - dealer.receive(spadeAce); - assertThat(dealer.getCards()).hasSize(1); - } - - @Test - void 딜러가_여러_카드를_받는다() { - List cards = new ArrayList<>(); - cards.add(TrumpCard.of(Suit.SPADE, Rank.ACE)); - cards.add(TrumpCard.of(Suit.HEART, Rank.KING)); - - dealer.receiveCards(cards); - assertThat(dealer.getCards()).hasSize(2); + Dealer dealer = Dealer.of(); + dealer.hit(TrumpCard.of(Suit.SPADE, Rank.ACE)); + assertThat(dealer.countCards()).isEqualTo(1); } @Test void 딜러의_현재_점수를_계산한다() { - TrumpCard spadeKing = TrumpCard.of(Suit.SPADE, Rank.KING); - TrumpCard heartFive = TrumpCard.of(Suit.HEART, Rank.FIVE); - - dealer.receive(spadeKing); - dealer.receive(heartFive); - + Dealer dealer = dealerWith( + card(Suit.SPADE, Rank.KING), + card(Suit.HEART, Rank.FIVE) + ); assertThat(dealer.score()).isEqualTo(15); } @Test void 딜러는_16이하면_카드를_더_받아야한다() { - TrumpCard spadeKing = TrumpCard.of(Suit.SPADE, Rank.KING); - TrumpCard heartFive = TrumpCard.of(Suit.HEART, Rank.FIVE); - - dealer.receive(spadeKing); - dealer.receive(heartFive); - - assertThat(dealer.shouldHit()).isTrue(); + Dealer dealer = dealerWith( + card(Suit.SPADE, Rank.KING), + card(Suit.HEART, Rank.FIVE) + ); + assertThat(dealer.canHit()).isTrue(); } @Test void 딜러는_17이상이면_카드를_받지_않는다() { - TrumpCard spadeKing = TrumpCard.of(Suit.SPADE, Rank.KING); - TrumpCard heartSeven = TrumpCard.of(Suit.HEART, Rank.SEVEN); - - dealer.receive(spadeKing); - dealer.receive(heartSeven); - - assertThat(dealer.shouldHit()).isFalse(); - } - - @Test - void 딜러의_첫_번째_카드를_조회한다() { - TrumpCard spadeKing = TrumpCard.of(Suit.SPADE, Rank.KING); - TrumpCard heartFive = TrumpCard.of(Suit.HEART, Rank.FIVE); - - dealer.receive(spadeKing); - dealer.receive(heartFive); - - assertThat(dealer.getOpenCard()).isEqualTo(spadeKing); + Dealer dealer = dealerWith( + card(Suit.SPADE, Rank.KING), + card(Suit.HEART, Rank.SEVEN) + ); + assertThat(dealer.canHit()).isFalse(); } } diff --git a/src/test/java/blackjack/domain/DeckTest.java b/src/test/java/blackjack/domain/DeckTest.java index 1d3f028d7d1..11cb20d51b7 100644 --- a/src/test/java/blackjack/domain/DeckTest.java +++ b/src/test/java/blackjack/domain/DeckTest.java @@ -9,15 +9,26 @@ import org.junit.jupiter.api.Test; class DeckTest { + private Deck emptyDeck(){ + Deck deck = Deck.of(new ArrayList<>(TrumpCard.ALL_CARD)); + for (int i = 0; i < 52; i++) { + deck.deal(); + } + return deck; + } @Test - void 전체_카드_수가_52장이_아니면_예외_발생한다() { + void 전체_카드_수가_52장보다_적으면_예외_발생한다() { List cards = new ArrayList<>(); - for (Suit suit : Suit.values()) { - for (Rank rank : Rank.values()) { - cards.add(TrumpCard.of(suit, rank)); - } - } + cards.add(TrumpCard.of(Suit.SPADE, Rank.ACE)); + assertThatThrownBy(() -> Deck.of(cards)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("전체 카드 수는 52장이어야 합니다."); + } + + @Test + void 전체_카드_수가_52장보다_많으면_예외_발생한다() { + List cards = new ArrayList<>(TrumpCard.ALL_CARD); cards.add(TrumpCard.of(Suit.SPADE, Rank.ACE)); assertThatThrownBy(() -> Deck.of(cards)) @@ -27,12 +38,7 @@ class DeckTest { @Test void 중복된_카드가_존재하면_예외_발생한다() { - List cards = new ArrayList<>(); - for (Suit suit : Suit.values()) { - for (Rank rank : Rank.values()) { - cards.add(TrumpCard.of(suit, rank)); - } - } + List cards = new ArrayList<>(TrumpCard.ALL_CARD); cards.removeFirst(); cards.add(TrumpCard.of(Suit.CLOVER, Rank.KING)); @@ -43,54 +49,26 @@ class DeckTest { @Test void 카드를_지급할_때_카드가_없으면_예외_발생한다() { - List cards = new ArrayList<>(); - for (Suit suit : Suit.values()) { - for (Rank rank : Rank.values()) { - cards.add(TrumpCard.of(suit, rank)); - } - } - Deck deck = Deck.of(cards); - for (int i = 0; i < 52; i++) { - deck.draw(); - } + Deck deck = emptyDeck(); - assertThatThrownBy(deck::draw) + assertThatThrownBy(deck::deal) .isInstanceOf(IllegalArgumentException.class) .hasMessage("덱에 카드가 없습니다."); } @Test void 카드를_지급하면_전체_카드_수가_줄어든다() { - List cards = new ArrayList<>(); - for (Suit suit : Suit.values()) { - for (Rank rank : Rank.values()) { - cards.add(TrumpCard.of(suit, rank)); - } - } - Deck deck = Deck.of(cards); - TrumpCard draw = deck.draw(); + Deck deck = Deck.of(new ArrayList<>(TrumpCard.ALL_CARD)); + + deck.deal(); - assertThat(draw).isNotNull(); + assertThat(deck.countCards()).isNotNull(); } @Test void 게임을_시작하면_카드를_셔플한다() { Deck deck = Deck.create(Collections::reverse); - TrumpCard first = deck.draw(); + TrumpCard first = deck.deal(); assertThat(first).isEqualTo(TrumpCard.of(Suit.CLOVER, Rank.KING)); } - - @Test - void 카드_2장을_한번에_지급한다() { - List cards = new ArrayList<>(); - for (Suit suit : Suit.values()) { - for (Rank rank : Rank.values()) { - cards.add(TrumpCard.of(suit, rank)); - } - } - Deck deck = Deck.of(cards); - List drawnCards = deck.drawSecondTimes(); - - assertThat(drawnCards).hasSize(2); - } } \ No newline at end of file diff --git a/src/test/java/blackjack/domain/HandTest.java b/src/test/java/blackjack/domain/HandTest.java index 95b3241db62..ccb6d8ba7c8 100644 --- a/src/test/java/blackjack/domain/HandTest.java +++ b/src/test/java/blackjack/domain/HandTest.java @@ -5,6 +5,17 @@ import org.junit.jupiter.api.Test; class HandTest { + private Hand handWith(TrumpCard... cards) { + Hand hand = Hand.init(); + for (TrumpCard card : cards) { + hand.add(card); + } + return hand; + } + + private TrumpCard card(Suit suit, Rank rank) { + return TrumpCard.of(suit, rank); + } @Test void 첫_카드는_비어있다() { @@ -16,34 +27,57 @@ class HandTest { void 카드를_받으면_카드가_늘어난다() { Hand hand = Hand.init(); TrumpCard newCard = TrumpCard.of(Suit.SPADE, Rank.ACE); - hand.receive(newCard); + + hand.add(newCard); + assertThat(hand.countCards()).isEqualTo(1); } @Test void 현재까지_확보한_카드_총_점수를_계산한다() { - Hand hand = Hand.init(); - TrumpCard spadeKing = TrumpCard.of(Suit.SPADE, Rank.KING); - TrumpCard heartFive = TrumpCard.of(Suit.HEART, Rank.FIVE); - - hand.receive(spadeKing); - hand.receive(heartFive); + Hand hand = handWith( + card(Suit.SPADE, Rank.KING), + card(Suit.HEART, Rank.FIVE) + ); int score = hand.calculateScore(); assertThat(score).isEqualTo(15); } + @Test + void 카드_총합이_21을_이하면_에이스는_11로_계산된다(){ + Hand hand = handWith( + card(Suit.HEART, Rank.ACE), + card(Suit.SPADE, Rank.NINE) + ); + + int score = hand.calculateScore(); + + assertThat(score).isEqualTo(20); + } @Test - void 카드_총합이_21을_넘으면_에이스_값이_1로_변환된다() { - Hand hand = Hand.init(); - TrumpCard heartAce = TrumpCard.of(Suit.HEART, Rank.ACE); - TrumpCard diamondNine = TrumpCard.of(Suit.DIAMOND, Rank.NINE); + void 카드_총합이_21을_넘으면_에이스_값이_1로_변환되어_계산된다() { + Hand hand = handWith( + card(Suit.HEART, Rank.ACE), + card(Suit.SPADE, Rank.KING), + card(Suit.DIAMOND, Rank.NINE) + ); - hand.receive(heartAce); - hand.receive(heartAce); - hand.receive(diamondNine); + int score = hand.calculateScore(); + + assertThat(score).isEqualTo(20); + } + + @Test + void 카드_총합이_21을_넘으면_에이스_값이_여러_개라도_각각의_에이스가_1로_변환되어_계산된다() { + Hand hand = handWith( + card(Suit.HEART, Rank.ACE), + card(Suit.SPADE, Rank.ACE), + card(Suit.DIAMOND, Rank.NINE), + card(Suit.DIAMOND, Rank.KING) + ); int score = hand.calculateScore(); @@ -51,15 +85,23 @@ class HandTest { } @Test - void 현재_보유한_카드_목록을_반환한다() { - Hand hand = Hand.init(); - TrumpCard spadeKing = TrumpCard.of(Suit.SPADE, Rank.KING); - TrumpCard heartFive = TrumpCard.of(Suit.HEART, Rank.FIVE); + void 카드_총합이_21_이하면_버스트가_아니다(){ + Hand hand = handWith( + card(Suit.SPADE, Rank.KING), + card(Suit.HEART, Rank.FIVE) + ); - hand.receive(spadeKing); - hand.receive(heartFive); + assertThat(hand.isBust()).isFalse(); + } - assertThat(hand.getCards()).hasSize(2); - assertThat(hand.getCards()).containsExactly(spadeKing, heartFive); + @Test + void 카드_총합이_21을_초과하면_버스트이다(){ + Hand hand = handWith( + card(Suit.SPADE, Rank.KING), + card(Suit.HEART, Rank.KING), + card(Suit.HEART, Rank.THREE) + ); + + assertThat(hand.isBust()).isTrue(); } } \ No newline at end of file diff --git a/src/test/java/blackjack/domain/MatchResultTest.java b/src/test/java/blackjack/domain/MatchResultTest.java index 0f1e7491481..d0eac1de1f1 100644 --- a/src/test/java/blackjack/domain/MatchResultTest.java +++ b/src/test/java/blackjack/domain/MatchResultTest.java @@ -1,22 +1,24 @@ package blackjack.domain; +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; +import blackjack.domain.vo.MatchResult; import org.junit.jupiter.api.Test; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.junit.jupiter.api.Assertions.*; class MatchResultTest { @Test void 플레이어가_버스트라면_진다(){ Player player = Player.of(Name.of("pobi")); - player.receiveCard(TrumpCard.of(Suit.SPADE, Rank.TEN)); - player.receiveCard(TrumpCard.of(Suit.HEART, Rank.TEN)); - player.receiveCard(TrumpCard.of(Suit.DIAMOND, Rank.FIVE)); + player.hit(TrumpCard.of(Suit.SPADE, Rank.TEN)); + player.hit(TrumpCard.of(Suit.HEART, Rank.TEN)); + player.hit(TrumpCard.of(Suit.DIAMOND, Rank.FIVE)); Dealer dealer = Dealer.of(); - dealer.receive(TrumpCard.of(Suit.CLOVER, Rank.TEN)); - dealer.receive(TrumpCard.of(Suit.SPADE, Rank.SEVEN)); + dealer.hit(TrumpCard.of(Suit.CLOVER, Rank.TEN)); + dealer.hit(TrumpCard.of(Suit.SPADE, Rank.SEVEN)); assertThat(MatchResult.playerResult(player, dealer)).isEqualTo(MatchResult.LOSE); } @@ -24,13 +26,13 @@ class MatchResultTest { @Test void 플레이어가_버스트가_아니고_딜러가_버스트면_이긴다(){ Player player = Player.of(Name.of("pobi")); - player.receiveCard(TrumpCard.of(Suit.SPADE, Rank.TEN)); - player.receiveCard(TrumpCard.of(Suit.HEART, Rank.SEVEN)); + player.hit(TrumpCard.of(Suit.SPADE, Rank.TEN)); + player.hit(TrumpCard.of(Suit.HEART, Rank.SEVEN)); Dealer dealer = Dealer.of(); - dealer.receive(TrumpCard.of(Suit.CLOVER, Rank.TEN)); - dealer.receive(TrumpCard.of(Suit.DIAMOND, Rank.TEN)); - dealer.receive(TrumpCard.of(Suit.SPADE, Rank.FIVE)); + dealer.hit(TrumpCard.of(Suit.CLOVER, Rank.TEN)); + dealer.hit(TrumpCard.of(Suit.DIAMOND, Rank.TEN)); + dealer.hit(TrumpCard.of(Suit.SPADE, Rank.FIVE)); assertThat(MatchResult.playerResult(player, dealer)).isEqualTo(MatchResult.WIN); } @@ -38,12 +40,12 @@ class MatchResultTest { @Test void 둘_다_버스트가_아니면_더_큰_쪽이_이긴다(){ Player player = Player.of(Name.of("pobi")); - player.receiveCard(TrumpCard.of(Suit.SPADE, Rank.TEN)); - player.receiveCard(TrumpCard.of(Suit.HEART, Rank.TEN)); + player.hit(TrumpCard.of(Suit.SPADE, Rank.TEN)); + player.hit(TrumpCard.of(Suit.HEART, Rank.TEN)); Dealer dealer = Dealer.of(); - dealer.receive(TrumpCard.of(Suit.CLOVER, Rank.TEN)); - dealer.receive(TrumpCard.of(Suit.SPADE, Rank.SEVEN)); + dealer.hit(TrumpCard.of(Suit.CLOVER, Rank.TEN)); + dealer.hit(TrumpCard.of(Suit.SPADE, Rank.SEVEN)); assertThat(MatchResult.playerResult(player, dealer)).isEqualTo(MatchResult.WIN); } @@ -51,12 +53,12 @@ class MatchResultTest { @Test void 점수가_같으면_무승부() { Player player = Player.of(Name.of("pobi")); - player.receiveCard(TrumpCard.of(Suit.SPADE, Rank.TEN)); - player.receiveCard(TrumpCard.of(Suit.HEART, Rank.SEVEN)); + player.hit(TrumpCard.of(Suit.SPADE, Rank.TEN)); + player.hit(TrumpCard.of(Suit.HEART, Rank.SEVEN)); Dealer dealer = Dealer.of(); - dealer.receive(TrumpCard.of(Suit.CLOVER, Rank.TEN)); - dealer.receive(TrumpCard.of(Suit.DIAMOND, Rank.SEVEN)); + dealer.hit(TrumpCard.of(Suit.CLOVER, Rank.TEN)); + dealer.hit(TrumpCard.of(Suit.DIAMOND, Rank.SEVEN)); assertThat(MatchResult.playerResult(player, dealer)).isEqualTo(MatchResult.DRAW); } diff --git a/src/test/java/blackjack/domain/NameTest.java b/src/test/java/blackjack/domain/NameTest.java index 3f9725ca2aa..49b2baf59bb 100644 --- a/src/test/java/blackjack/domain/NameTest.java +++ b/src/test/java/blackjack/domain/NameTest.java @@ -16,6 +16,13 @@ class NameTest { .hasMessage("이름의 길이는 10이 넘을 수 없습니다."); } + @Test + void 이름의_길이가_10개이면_정상_생성된다(){ + String name = "일이삼사오육칠팔구십"; + Name playerName = Name.of(name); + assertThat(playerName).isNotNull(); + } + @Test void 이름이_비어있으면_예외_처리한다() { String name = ""; diff --git a/src/test/java/blackjack/domain/PlayerTest.java b/src/test/java/blackjack/domain/PlayerTest.java index 81018b07252..4bda75e1d3c 100644 --- a/src/test/java/blackjack/domain/PlayerTest.java +++ b/src/test/java/blackjack/domain/PlayerTest.java @@ -2,104 +2,43 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.util.ArrayList; -import java.util.List; -import org.junit.jupiter.api.BeforeEach; +import blackjack.domain.participant.Player; import org.junit.jupiter.api.Test; public class PlayerTest { - private Player player; - - @BeforeEach - void setUp() { - Name newPlayerName = Name.of("한다"); - player = Player.of(newPlayerName); - } - - @Test - void 플레이어를_생성한다() { - assertThat(player).isNotNull(); - } - - @Test - void 플레이어의_이름을_반환한다() { - assertThat(player.name()).isEqualTo("한다"); - } - - - @Test - void 플레이어는_초기에_비어있는_핸드를_가진다() { - assertThat(player.countCards()).isEqualTo(0); - } - - @Test - void 플레이어가_카드1장_받는다() { - TrumpCard spaceAce = TrumpCard.of(Suit.SPADE, Rank.ACE); - player.receiveCard(spaceAce); - assertThat(player.countCards()).isEqualTo(1); - } - - @Test - void 플레이어가_여러_카드를_받는다() { - TrumpCard spaceAce = TrumpCard.of(Suit.SPADE, Rank.ACE); - TrumpCard heartKing = TrumpCard.of(Suit.HEART, Rank.KING); - - player.receiveCard(spaceAce); - player.receiveCard(heartKing); - assertThat(player.countCards()).isEqualTo(2); + private Player playerWith(TrumpCard... cards) { + Player player = Player.of(Name.of("한다")); + for (TrumpCard card : cards) { + player.hit(card); + } + return player; } - @Test - void 플레이어의_현재_점수를_계산한다() { - TrumpCard spaceAce = TrumpCard.of(Suit.SPADE, Rank.ACE); - TrumpCard heartKing = TrumpCard.of(Suit.HEART, Rank.KING); - - player.receiveCard(spaceAce); - player.receiveCard(heartKing); - - assertThat(player.score()).isEqualTo(21); + private TrumpCard card(Suit suit, Rank rank) { + return TrumpCard.of(suit, rank); } @Test - void 플레이어가_여러_카드를_한번에_받는다() { - List cards = new ArrayList<>(); - cards.add(TrumpCard.of(Suit.SPADE, Rank.ACE)); - cards.add(TrumpCard.of(Suit.HEART, Rank.KING)); - - player.receiveCards(cards); - assertThat(player.countCards()).isEqualTo(2); + void 플레이어는_이름을_가진다(){ + Player player = Player.of(Name.of("한다")); + assertThat(player.getName()).isEqualTo("한다"); } @Test - void 플레이어가_21이하면_카드를_더_받을_수_있다() { - TrumpCard spadeKing = TrumpCard.of(Suit.SPADE, Rank.KING); - player.receiveCard(spadeKing); - + void 버스트가_아니면_카드를_더_받을_수_있다() { + Player player = playerWith( + card(Suit.SPADE, Rank.KING) + ); assertThat(player.canHit()).isTrue(); } @Test - void 플레이어가_21을_초과하면_카드를_받을_수_없다() { - TrumpCard spadeKing = TrumpCard.of(Suit.SPADE, Rank.KING); - TrumpCard heartQueen = TrumpCard.of(Suit.HEART, Rank.QUEEN); - TrumpCard diamondFive = TrumpCard.of(Suit.DIAMOND, Rank.FIVE); - - player.receiveCard(spadeKing); - player.receiveCard(heartQueen); - player.receiveCard(diamondFive); - + void 버스트이면_카드를_받을_수_없다() { + Player player = playerWith( + card(Suit.SPADE, Rank.KING), + card(Suit.HEART, Rank.QUEEN), + card(Suit.DIAMOND, Rank.FIVE) + ); assertThat(player.canHit()).isFalse(); } - - @Test - void 플레이어의_보유_카드_목록을_반환한다() { - TrumpCard spadeAce = TrumpCard.of(Suit.SPADE, Rank.ACE); - TrumpCard heartKing = TrumpCard.of(Suit.HEART, Rank.KING); - - player.receiveCard(spadeAce); - player.receiveCard(heartKing); - - assertThat(player.getCards()).hasSize(2); - assertThat(player.getCards()).containsExactly(spadeAce, heartKing); - } } diff --git a/src/test/java/blackjack/domain/PlayersTest.java b/src/test/java/blackjack/domain/PlayersTest.java index 6ad9d6b80a2..6eff8bdaf04 100644 --- a/src/test/java/blackjack/domain/PlayersTest.java +++ b/src/test/java/blackjack/domain/PlayersTest.java @@ -4,60 +4,47 @@ import java.util.ArrayList; import java.util.List; -import org.junit.jupiter.api.BeforeEach; + +import blackjack.domain.participant.Player; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; public class PlayersTest { - private List playerList; - private Deck deck; - - @BeforeEach - void setUp() { - playerList = new ArrayList<>(); - playerList.add(Player.of(Name.of("handa"))); - playerList.add(Player.of(Name.of("dalsu"))); - - List cards = new ArrayList<>(); - for (Suit suit : Suit.values()) { - for (Rank rank : Rank.values()) { - cards.add(TrumpCard.of(suit, rank)); - } - } - deck = Deck.of(cards); + private Player player(String name){ + return Player.of(Name.of(name)); } @Test - void 게임에_참가한_플레이어의_명수를_반환한다() { - Players players = Players.of(playerList); - assertThat(players.count()).isEqualTo(2); + void 플레이어_목록이_비어있으면_예외_발생한다(){ + Assertions.assertThatThrownBy(() -> Players.of(List.of())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("플레이어는 1명 이상이어야 합니다."); } @Test - void 게임이_시작되면_모든_플레이어가_카드를_받는다() { - Players players = Players.of(playerList); - players.receiveCards(deck); - - List result = players.getPlayers(); - assertThat(result.get(0).countCards()).isEqualTo(2); - assertThat(result.get(1).countCards()).isEqualTo(2); + void 플레이어_목록이_null이면_예외_발생한다(){ + Assertions.assertThatThrownBy(() -> Players.of(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("플레이어 목록은 null일 수 없습니다."); } - @Test - void 특정_플레이어가_카드를_추가로_받는다() { - Players players = Players.of(playerList); - players.receiveCards(deck); - - TrumpCard newCard = deck.draw(); - players.hitPlayer(0, newCard); - - List result = players.getPlayers(); - assertThat(result.get(0).countCards()).isEqualTo(3); - assertThat(result.get(1).countCards()).isEqualTo(2); + void 게임에_참가한_플레이어의_명수를_반환한다() { + Players players = Players.of(List.of( + player("handa"), + player("dalsu") + )); + assertThat(players.count()).isEqualTo(2); } @Test - void 특정_플레이어가_카드를_더_받을_수_있는지_확인한다() { - Players players = Players.of(playerList); - assertThat(players.canHit(0)).isTrue(); + void 모든_플레이어에_대해_작업을_수행한다() { + Players players = Players.of(List.of( + player("handa"), + player("dalsu") + )); + List names = new ArrayList<>(); + players.forEach(p -> names.add(p.getName())); + + assertThat(names).containsExactly("handa", "dalsu"); } } diff --git a/src/test/java/blackjack/domain/TrumpCardTest.java b/src/test/java/blackjack/domain/TrumpCardTest.java index a6463875f51..c103fefdfc1 100644 --- a/src/test/java/blackjack/domain/TrumpCardTest.java +++ b/src/test/java/blackjack/domain/TrumpCardTest.java @@ -46,12 +46,6 @@ class TrumpCardTest { @Test void 카드의_한글_이름을_반환한다() { TrumpCard trumpCard = TrumpCard.of(Suit.of("하트"), Rank.of("A")); - assertThat(trumpCard.koreanName()).isEqualTo("하트"); - } - - @Test - void 카드의_랭크_이름을_반환한다() { - TrumpCard trumpCard = TrumpCard.of(Suit.of("하트"), Rank.of("A")); - assertThat(trumpCard.symbol()).isEqualTo("A"); + assertThat(trumpCard.name()).isEqualTo("A하트"); } } \ No newline at end of file