diff --git a/README.md b/README.md index 421d44d915..2c1b5ae177 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,14 @@ Java 기반으로 **객체지향 설계를 적용한 블랙잭 게임**을 구 --- -## Step2 예정 기능 +## 사이클2 추가 기능 -- 플레이어 **배팅 금액 설정** -- **Blackjack (21)** 발생 시 **1.5배 지급** -- **무승부 시 배팅 금액 반환** +- [x] 플레이어 **배팅 금액 설정** +- [x] 플레이어 블랙잭 여부 확인 +- [x] 결과에 따른 수익금 계산 +- [x] 딜러, 플레이어 모두 블랙잭 시 무승부 +- [x] **무승부 시 배팅 금액 반환** +- [x] 플레이어 **Blackjack (21)** 발생 시 수액 **1.5배 지급** --- @@ -33,27 +36,30 @@ Java 기반으로 **객체지향 설계를 적용한 블랙잭 게임**을 구 ``` blackjack -├── config -├── core ├── domain │ ├── card -│ ├── participant -│ ├── score -│ └── result +│ ├── game +│ └── participants ├── dto -└── view +├── view +├── bootstrap +└── application + ``` ### 역할 -| 패키지 | 역할 | -|---|---| -| config | 객체 생성 및 설정 | -| core | 게임 흐름 제어 | -| domain | 핵심 비즈니스 로직 | -| dto | View 전달용 데이터 | -| view | 사용자 입출력 | +| 패키지 | 역할 | +|---------------------| ------------------------------ | +| application | 프로그램 실행 흐름을 제어하고 도메인과 View를 연결 | +| bootstrap | 도메인 객체 생성에 필요한 설정 및 생성 책임 | +| domain | 블랙잭 게임의 핵심 비즈니스 로직 | +| domain.card | 카드, 덱 등 카드 관련 도메인 모델 | +| domain.game | 게임 진행, 승패 판정 등 게임 규칙 | +| domain.participants | 플레이어, 딜러, 배팅 등 참여자 관련 도메인 | +| dto | View에 전달하기 위한 출력 전용 데이터 | +| view | 사용자 입력 및 출력 처리 | --- @@ -75,17 +81,11 @@ blackjack **Suit** 카드의 모양(♠, ♥, ♦, ♣) 정의 -**Deck** -게임에서 사용할 카드 묶음 -- `CardsGenerator`를 통해 카드 생성 +**ShuffledDeck** +게임에서 사용할 랜덤 카드 묶음 +- 객체 생성 시 랜덤 카드 초기화 - 카드를 한 장씩 제공 (`draw`) -**CardsGenerator** -카드 생성 전략 인터페이스 - -**ShuffledCardsGenerator** -전체 카드를 생성한 뒤 무작위로 섞어 반환 - **Hand** 참가자가 보유한 카드 집합 - 카드 추가 @@ -116,27 +116,47 @@ blackjack - null / 공백 검증 - 문자열 정제 +**Bet** +플레이어의 배팅 금액을 표현하는 값 객체 +- 배팅 금액을 관리 +- 게임 결과에 따라 수익을 계산 + +**Profit** +수익을 표현하는 값 객체 +- 수익 금액을 관리 +- 음수전환/0원변환/배수적용 등의 계산 + **PlayerGroup** 플레이어 목록을 관리하는 일급 컬렉션 -- 플레이어 목록 관리 +- 플레이어 목록 관리 +- 플레이어 전체 딜링 + +**PlayerNames** +플레이어 이름 목록을 관리하는 일급 컬렉션 +- 플레이어 이름 목록 관리 - 이름 중복 검증 --- -### Result 도메인 +### Game 흐름 + +**BlackjackGame** +블랙잭 게임의 핵심 도메인 상태와 동작을 관리 +- 덱, 딜러, 플레이어 그룹 관리 +- 초기 카드 분배 수행 +- 참가자에게 카드 hit +- 딜러 턴 진행 +- GameReferee 을 통한 승패 판정 **Score** -블랙잭 점수 비교 +블랙잭 점수 비교 - 카드 총 합 관리 **GameResult** -게임 결과 정의 +게임 결과 정의 - 승 / 패 / 무 -**GameReferee** -게임 승패 판정 규칙을 정의하는 인터페이스 - -**BlackjackGameReferee** +**BlackjackGameReferee** - 블랙잭 규칙에 따라 점수를 비교하고 승패를 판정 --- @@ -152,13 +172,10 @@ blackjack - 점수 계산 - 게임 결과 출력 -**BlackjackGame** -블랙잭 게임의 핵심 도메인 상태와 동작을 관리 -- 덱, 딜러, 플레이어 그룹 관리 -- 초기 카드 분배 수행 -- 참가자에게 카드 hit -- 딜러 턴 진행 -- GameReferee 을 통한 승패 판정 +**BlackjackGameBootstrap** +애플리케이션의 생성에 필요한 설정 및 생성 책임 +- 플레이어(이름, 배팅금액) 초기화 +- BlackjackGame 생성 --- diff --git a/src/main/java/blackjack/BlackjackApplication.java b/src/main/java/blackjack/BlackjackApplication.java index aa7f8f2bdc..a170fbb5d3 100644 --- a/src/main/java/blackjack/BlackjackApplication.java +++ b/src/main/java/blackjack/BlackjackApplication.java @@ -1,22 +1,23 @@ package blackjack; -import blackjack.core.BlackjackGame; -import blackjack.domain.card.ShuffledCardsGenerator; -import blackjack.domain.participant.Player; -import blackjack.domain.result.BlackjackGameReferee; -import blackjack.domain.result.GameResult; +import blackjack.domain.game.BlackjackGame; +import blackjack.domain.participants.Player; +import blackjack.domain.participants.Profit; import blackjack.dto.DealerHitDto; import blackjack.dto.GameResultDtos; import blackjack.dto.InitialDealDtos; import blackjack.dto.ParticipantCardsDto; import blackjack.dto.ParticipantScoreDtos; import blackjack.view.BlackjackView; -import blackjack.view.InputView; -import blackjack.view.OutputView; import java.util.Map; import java.util.stream.Collectors; public class BlackjackApplication { + public static void main(String[] args) { + BlackjackApplication application = BlackjackConsoleBootstrap.createApplication(); + application.run(); + } + private final BlackjackView view; private final BlackjackGame game; @@ -32,7 +33,7 @@ public void run() { playDealerTurn(); printScore(); - printResult(); + printProfit(); } private void initialDeal() { @@ -45,7 +46,7 @@ private void playPlayersTurn() { } private void playTurn(Player player) { - while (player.canHit() && view.isHitAnswer(player.getName())) { + while (game.canHit(player) && view.isHitAnswer(player.getName())) { game.hit(player); view.printPlayerCards(ParticipantCardsDto.from(player)); } @@ -53,8 +54,9 @@ private void playTurn(Player player) { private void playDealerTurn() { int dealerHitCount = game.playDealerTurn(); - view.printDealerHit( - DealerHitDto.of(game.getDealer(), dealerHitCount)); + if (dealerHitCount > 0) { + view.printDealerHit(DealerHitDto.of(game.getDealer(), dealerHitCount)); + } } private void printScore() { @@ -62,27 +64,16 @@ private void printScore() { ParticipantScoreDtos.of(game.getDealer(), game.getPlayers())); } - private void printResult() { - Map playerResults = parseResultMap(); + private void printProfit() { + Map playerResults = calculatePlayerProfits(); view.printResult(GameResultDtos.of(playerResults)); } - private Map parseResultMap() { + private Map calculatePlayerProfits() { return game.getPlayers().stream() .collect(Collectors.toMap( player -> player, - game::judge + game::calculateProfit )); } - - public static void main(String[] args) { - BlackjackView view = new BlackjackView(new InputView(), new OutputView()); - BlackjackGame game = BlackjackGame.create( - new ShuffledCardsGenerator(), - new BlackjackGameReferee(), - view.readPlayers() - ); - - new BlackjackApplication(view, game).run(); - } } diff --git a/src/main/java/blackjack/BlackjackConsoleBootstrap.java b/src/main/java/blackjack/BlackjackConsoleBootstrap.java new file mode 100644 index 0000000000..2c70cd47a1 --- /dev/null +++ b/src/main/java/blackjack/BlackjackConsoleBootstrap.java @@ -0,0 +1,33 @@ +package blackjack; + +import blackjack.domain.game.BlackjackGame; +import blackjack.domain.participants.Bet; +import blackjack.domain.participants.Name; +import blackjack.domain.participants.Player; +import blackjack.domain.participants.PlayerGroup; +import blackjack.domain.participants.PlayerNames; +import blackjack.view.BlackjackView; +import blackjack.view.InputView; +import blackjack.view.OutputView; +import java.util.List; + +public class BlackjackConsoleBootstrap { + public static BlackjackApplication createApplication() { + BlackjackView view = new BlackjackView(new InputView(), new OutputView()); + List players = readPlayers(view); + + BlackjackGame game = BlackjackGame.createBasic(new PlayerGroup(players)); + return new BlackjackApplication(view, game); + } + + private static List readPlayers(BlackjackView view) { + PlayerNames playerNames = PlayerNames.from(view.readPlayers()); + return playerNames.names().stream() + .map(name -> initPlayer(name, view.readBetAmount(name.getValue()))) + .toList(); + } + + private static Player initPlayer(Name name, long betAmount) { + return Player.createEmptyHand(name, new Bet(betAmount)); + } +} diff --git a/src/main/java/blackjack/core/BlackjackGame.java b/src/main/java/blackjack/core/BlackjackGame.java deleted file mode 100644 index fdf62296ba..0000000000 --- a/src/main/java/blackjack/core/BlackjackGame.java +++ /dev/null @@ -1,69 +0,0 @@ -package blackjack.core; - -import blackjack.domain.card.CardsGenerator; -import blackjack.domain.card.Deck; -import blackjack.domain.participant.Dealer; -import blackjack.domain.participant.Participant; -import blackjack.domain.participant.Player; -import blackjack.domain.participant.PlayerGroup; -import blackjack.domain.result.GameReferee; -import blackjack.domain.result.GameResult; -import java.util.List; - -public class BlackjackGame { - private static final int INITIAL_DEAL_COUNT = 2; - - private final Deck deck; - private final Dealer dealer; - private final GameReferee referee; - private final PlayerGroup playerGroup; - - public BlackjackGame(Deck deck, Dealer dealer, GameReferee referee, PlayerGroup playerGroup) { - this.deck = deck; - this.dealer = dealer; - this.referee = referee; - this.playerGroup = playerGroup; - } - - public static BlackjackGame create( - CardsGenerator cardsGenerator, GameReferee referee, String playerNames) { - - return new BlackjackGame( - Deck.create(cardsGenerator), - Dealer.create(), - referee, - PlayerGroup.from(playerNames)); - } - - public List getPlayers() { - return playerGroup.players(); - } - - public Dealer getDealer() { - return dealer; - } - - public void initialDeal() { - for (int i = 0; i < INITIAL_DEAL_COUNT; i++) { - hit(dealer); - playerGroup.deal(deck); - } - } - - public void hit(Participant participant) { - participant.hit(deck.draw()); - } - - public int playDealerTurn() { - int hitCount = 0; - while (dealer.canHit()) { - hit(dealer); - hitCount++; - } - return hitCount; - } - - public GameResult judge(Player player) { - return referee.judge(dealer, player); - } -} diff --git a/src/main/java/blackjack/domain/card/CardsGenerator.java b/src/main/java/blackjack/domain/card/CardsGenerator.java deleted file mode 100644 index 9f39e1a009..0000000000 --- a/src/main/java/blackjack/domain/card/CardsGenerator.java +++ /dev/null @@ -1,7 +0,0 @@ -package blackjack.domain.card; - -import java.util.List; - -public interface CardsGenerator { - List create(); -} diff --git a/src/main/java/blackjack/domain/card/Deck.java b/src/main/java/blackjack/domain/card/Deck.java deleted file mode 100644 index ce9c2e2ff0..0000000000 --- a/src/main/java/blackjack/domain/card/Deck.java +++ /dev/null @@ -1,25 +0,0 @@ -package blackjack.domain.card; - -import java.util.ArrayDeque; -import java.util.Collection; -import java.util.Queue; - -public class Deck { - - private final Queue cards; - - public Deck(Collection cards) { - this.cards = new ArrayDeque<>(cards); - } - - public static Deck create(CardsGenerator cardsGenerator) { - return new Deck(cardsGenerator.create()); - } - - public Card draw() { - if (cards.isEmpty()) { - throw new IllegalStateException("덱이 비어 있습니다."); - } - return cards.poll(); - } -} diff --git a/src/main/java/blackjack/domain/card/Hand.java b/src/main/java/blackjack/domain/card/Hand.java index 1b3f39d6a9..97a8fe194a 100644 --- a/src/main/java/blackjack/domain/card/Hand.java +++ b/src/main/java/blackjack/domain/card/Hand.java @@ -1,44 +1,32 @@ package blackjack.domain.card; -import blackjack.domain.result.Score; +import blackjack.domain.game.Score; import java.util.ArrayList; import java.util.List; -public class Hand { +public record Hand(List cards) { private static final int ACE_ADJUST_AMOUNT = 10; private static final int BUST_THRESHOLD = 21; + private static final int BLACKJACK_SCORE = 21; + private static final int BLACKJACK_CARD_COUNT = 2; - private final List cards; - - public Hand() { - this(new ArrayList<>()); - } - - public Hand(List cards) { - this.cards = cards; - } - - public List getCards() { - return List.copyOf(cards); + public static Hand empty() { + return new Hand(new ArrayList<>()); } public void addCard(Card card) { this.cards.add(card); } - public boolean isBust() { - return getScore().isBiggerThan(BUST_THRESHOLD); - } - - public Score getScore() { - int sum = calculateSum(); + public Score calculateScore() { + int sum = calculateRawSum(); if (canApplyAceAmount(sum)) { return new Score(sum + ACE_ADJUST_AMOUNT); } return new Score(sum); } - private int calculateSum() { + private int calculateRawSum() { return cards.stream() .mapToInt(Card::getValue) .sum(); @@ -49,6 +37,20 @@ private boolean canApplyAceAmount(int sum) { } private boolean containsAce() { - return cards.stream().anyMatch(Card::isAce); + return cards.stream() + .anyMatch(Card::isAce); + } + + public boolean isBust() { + return calculateScore().isBiggerThan(BUST_THRESHOLD); + } + + public boolean isBlackjack() { + Score score = calculateScore(); + return score.isEqualTo(BLACKJACK_SCORE) & hasBlackjackCardCount(); + } + + private boolean hasBlackjackCardCount() { + return cards.size() == BLACKJACK_CARD_COUNT; } } diff --git a/src/main/java/blackjack/domain/card/ShuffledCardsGenerator.java b/src/main/java/blackjack/domain/card/ShuffledCardsGenerator.java deleted file mode 100644 index 654a1f9066..0000000000 --- a/src/main/java/blackjack/domain/card/ShuffledCardsGenerator.java +++ /dev/null @@ -1,26 +0,0 @@ -package blackjack.domain.card; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class ShuffledCardsGenerator implements CardsGenerator { - - @Override - public List create() { - List cards = Arrays.stream(Rank.values()) - .flatMap(ShuffledCardsGenerator::createCardsByRank) - .collect(Collectors.toCollection(ArrayList::new)); - - Collections.shuffle(cards); - return Collections.unmodifiableList(cards); - } - - private static Stream createCardsByRank(Rank rank) { - return Arrays.stream(Suit.values()) - .map(suit -> new Card(rank, suit)); - } -} diff --git a/src/main/java/blackjack/domain/card/ShuffledDeck.java b/src/main/java/blackjack/domain/card/ShuffledDeck.java new file mode 100644 index 0000000000..2f2912a4ef --- /dev/null +++ b/src/main/java/blackjack/domain/card/ShuffledDeck.java @@ -0,0 +1,40 @@ +package blackjack.domain.card; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Queue; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ShuffledDeck { + private final Queue cards; + + public ShuffledDeck(List cards) { + Collections.shuffle(cards); + this.cards = new ArrayDeque<>(cards); + } + + public static ShuffledDeck create() { + List cards = Arrays.stream(Rank.values()) + .flatMap(ShuffledDeck::createCardsByRank) + .collect(Collectors.toCollection(ArrayList::new)); + Collections.shuffle(cards); + + return new ShuffledDeck(cards); + } + + private static Stream createCardsByRank(Rank rank) { + return Arrays.stream(Suit.values()) + .map(suit -> new Card(rank, suit)); + } + + public Card draw() { + if (cards.isEmpty()) { + throw new IllegalStateException("덱이 비어 있습니다."); + } + return cards.poll(); + } +} diff --git a/src/main/java/blackjack/domain/game/BlackjackGame.java b/src/main/java/blackjack/domain/game/BlackjackGame.java new file mode 100644 index 0000000000..d879d97d61 --- /dev/null +++ b/src/main/java/blackjack/domain/game/BlackjackGame.java @@ -0,0 +1,66 @@ +package blackjack.domain.game; + +import blackjack.domain.card.ShuffledDeck; +import blackjack.domain.participants.Dealer; +import blackjack.domain.participants.Player; +import blackjack.domain.participants.PlayerGroup; +import blackjack.domain.participants.Profit; +import java.util.List; + +public class BlackjackGame { + private static final int INITIAL_DEAL_COUNT = 2; + + private final ShuffledDeck deck; + private final Dealer dealer; + private final PlayerGroup playerGroup; + + public BlackjackGame(ShuffledDeck deck, Dealer dealer, PlayerGroup playerGroup) { + this.deck = deck; + this.dealer = dealer; + this.playerGroup = playerGroup; + } + + public static BlackjackGame createBasic(PlayerGroup playerGroup) { + return new BlackjackGame( + ShuffledDeck.create(), + Dealer.createEmptyHand(), + playerGroup); + } + + public List getPlayers() { + return playerGroup.players(); + } + + public Dealer getDealer() { + return dealer; + } + + public void initialDeal() { + for (int i = 0; i < INITIAL_DEAL_COUNT; i++) { + dealer.hit(deck.draw()); + playerGroup.deal(deck); + } + } + + public boolean canHit(Player player) { + return player.canHit(); + } + + public void hit(Player player) { + player.hit(deck.draw()); + } + + public int playDealerTurn() { + int hitCount = 0; + while (dealer.canHit()) { + dealer.hit(deck.draw()); + hitCount++; + } + return hitCount; + } + + public Profit calculateProfit(Player player) { + GameResult judge = BlackjackGameReferee.judge(dealer, player); + return player.calculateProfit(judge); + } +} diff --git a/src/main/java/blackjack/domain/result/BlackjackGameReferee.java b/src/main/java/blackjack/domain/game/BlackjackGameReferee.java similarity index 63% rename from src/main/java/blackjack/domain/result/BlackjackGameReferee.java rename to src/main/java/blackjack/domain/game/BlackjackGameReferee.java index d68afaf5d2..07afe19d94 100644 --- a/src/main/java/blackjack/domain/result/BlackjackGameReferee.java +++ b/src/main/java/blackjack/domain/game/BlackjackGameReferee.java @@ -1,11 +1,10 @@ -package blackjack.domain.result; +package blackjack.domain.game; -import blackjack.domain.participant.Dealer; -import blackjack.domain.participant.Player; +import blackjack.domain.participants.Dealer; +import blackjack.domain.participants.Player; -public class BlackjackGameReferee implements GameReferee { - @Override - public GameResult judge(Dealer dealer, Player player) { +public class BlackjackGameReferee { + public static GameResult judge(Dealer dealer, Player player) { if (player.isBust()) { return GameResult.DEALER_WIN; } @@ -15,7 +14,7 @@ public GameResult judge(Dealer dealer, Player player) { return competeScoreWith(dealer, player); } - private GameResult competeScoreWith(Dealer dealer, Player player) { + private static GameResult competeScoreWith(Dealer dealer, Player player) { Score dealerScore = dealer.getScore(); Score playerScore = player.getScore(); diff --git a/src/main/java/blackjack/domain/game/GameResult.java b/src/main/java/blackjack/domain/game/GameResult.java new file mode 100644 index 0000000000..9e48d872e7 --- /dev/null +++ b/src/main/java/blackjack/domain/game/GameResult.java @@ -0,0 +1,21 @@ +package blackjack.domain.game; + +public enum GameResult { + PLAYER_BLACKJACK, PLAYER_WIN, DEALER_WIN, PUSH; + + public boolean isPlayerBlackjack() { + return this == PLAYER_BLACKJACK; + } + + public boolean isPlayerWin() { + return this == PLAYER_WIN; + } + + public boolean isDealerWin() { + return this == DEALER_WIN; + } + + public boolean isPush() { + return this == PUSH; + } +} diff --git a/src/main/java/blackjack/domain/result/Score.java b/src/main/java/blackjack/domain/game/Score.java similarity index 60% rename from src/main/java/blackjack/domain/result/Score.java rename to src/main/java/blackjack/domain/game/Score.java index c001946070..19b2de7fc2 100644 --- a/src/main/java/blackjack/domain/result/Score.java +++ b/src/main/java/blackjack/domain/game/Score.java @@ -1,4 +1,4 @@ -package blackjack.domain.result; +package blackjack.domain.game; public record Score(int value) { @@ -13,4 +13,12 @@ public boolean isBiggerThan(final int otherValue) { public boolean isLessThanOrEqual(final int otherValue) { return !isBiggerThan(otherValue); } + + public boolean isEqualTo(Score other) { + return this.value == other.value; + } + + public boolean isEqualTo(final int otherValue) { + return isEqualTo(new Score(otherValue)); + } } diff --git a/src/main/java/blackjack/domain/participant/Player.java b/src/main/java/blackjack/domain/participant/Player.java deleted file mode 100644 index f3518aaa58..0000000000 --- a/src/main/java/blackjack/domain/participant/Player.java +++ /dev/null @@ -1,19 +0,0 @@ -package blackjack.domain.participant; - -import blackjack.domain.card.Hand; - -public class Player extends Participant { - - public Player(Name name, Hand hand) { - super(name, hand); - } - - public Player(String name, Hand hand) { - super(new Name(name), hand); - } - - @Override - public boolean canHit() { - return !isBust(); - } -} diff --git a/src/main/java/blackjack/domain/participants/Bet.java b/src/main/java/blackjack/domain/participants/Bet.java new file mode 100644 index 0000000000..cbbbec607c --- /dev/null +++ b/src/main/java/blackjack/domain/participants/Bet.java @@ -0,0 +1,40 @@ +package blackjack.domain.participants; + +import blackjack.domain.game.GameResult; + +public class Bet { + private final long amount; + + public static Bet zero() { + return new Bet(0L); + } + + public Bet(long amount) { + validatePositive(amount); + this.amount = amount; + } + + private static void validatePositive(long amount) { + if (amount <= 0) { + throw new IllegalArgumentException("배팅 금액은 양수여야 합니다."); + } + } + + public Profit calculateProfit(GameResult gameResult) { + Profit baseProfit = new Profit(amount); + + if (gameResult.isPlayerBlackjack()) { + return baseProfit.applyBlackjackPayout(); + } + if (gameResult.isPush()) { + return Profit.zero(); + } + if (gameResult.isPlayerWin()) { + return baseProfit; + } + if (gameResult.isDealerWin()) { + return baseProfit.negative(); + } + throw new IllegalArgumentException("유효하지 않은 게임 결과로 수익을 계산할 수 없습니다."); + } +} diff --git a/src/main/java/blackjack/domain/participant/Dealer.java b/src/main/java/blackjack/domain/participants/Dealer.java similarity index 79% rename from src/main/java/blackjack/domain/participant/Dealer.java rename to src/main/java/blackjack/domain/participants/Dealer.java index 86ae2adf07..fea8664ed2 100644 --- a/src/main/java/blackjack/domain/participant/Dealer.java +++ b/src/main/java/blackjack/domain/participants/Dealer.java @@ -1,4 +1,4 @@ -package blackjack.domain.participant; +package blackjack.domain.participants; import blackjack.domain.card.Hand; @@ -10,8 +10,8 @@ public Dealer(Hand hand) { super(DEALER_NAME, hand); } - public static Dealer create() { - return new Dealer(new Hand()); + public static Dealer createEmptyHand() { + return new Dealer(Hand.empty()); } public static boolean isDealerName(Name name) { diff --git a/src/main/java/blackjack/domain/participant/Name.java b/src/main/java/blackjack/domain/participants/Name.java similarity index 95% rename from src/main/java/blackjack/domain/participant/Name.java rename to src/main/java/blackjack/domain/participants/Name.java index dd528c8181..b66e733f5c 100644 --- a/src/main/java/blackjack/domain/participant/Name.java +++ b/src/main/java/blackjack/domain/participants/Name.java @@ -1,4 +1,4 @@ -package blackjack.domain.participant; +package blackjack.domain.participants; import java.util.Objects; diff --git a/src/main/java/blackjack/domain/participant/Participant.java b/src/main/java/blackjack/domain/participants/Participant.java similarity index 77% rename from src/main/java/blackjack/domain/participant/Participant.java rename to src/main/java/blackjack/domain/participants/Participant.java index fd22f0662e..7ef21152e7 100644 --- a/src/main/java/blackjack/domain/participant/Participant.java +++ b/src/main/java/blackjack/domain/participants/Participant.java @@ -1,11 +1,11 @@ -package blackjack.domain.participant; +package blackjack.domain.participants; -import blackjack.domain.card.Hand; -import blackjack.domain.result.Score; import blackjack.domain.card.Card; +import blackjack.domain.card.Hand; +import blackjack.domain.game.Score; import java.util.List; -public abstract class Participant { +abstract class Participant { private final Name name; private final Hand hand; @@ -19,7 +19,7 @@ public final String getName() { } public final List getCards() { - return hand.getCards(); + return hand.cards(); } public final void hit(Card card) { @@ -27,7 +27,7 @@ public final void hit(Card card) { } public final Score getScore() { - return hand.getScore(); + return hand.calculateScore(); } public final boolean isBust() { diff --git a/src/main/java/blackjack/domain/participants/Player.java b/src/main/java/blackjack/domain/participants/Player.java new file mode 100644 index 0000000000..5bacdd0ebf --- /dev/null +++ b/src/main/java/blackjack/domain/participants/Player.java @@ -0,0 +1,31 @@ +package blackjack.domain.participants; + +import blackjack.domain.card.Hand; +import blackjack.domain.game.GameResult; + +public class Player extends Participant { + private final Bet bet; + + public Player(Name name, Hand hand, Bet bet) { + super(name, hand); + this.bet = bet; + } + + public Player(String name, Hand hand, Bet bet) { + super(new Name(name), hand); + this.bet = bet; + } + + public static Player createEmptyHand(Name name, Bet bet) { + return new Player(name, Hand.empty(), bet); + } + + @Override + public boolean canHit() { + return !isBust(); + } + + public Profit calculateProfit(GameResult gameResult) { + return bet.calculateProfit(gameResult); + } +} diff --git a/src/main/java/blackjack/domain/participants/PlayerGroup.java b/src/main/java/blackjack/domain/participants/PlayerGroup.java new file mode 100644 index 0000000000..094b35187a --- /dev/null +++ b/src/main/java/blackjack/domain/participants/PlayerGroup.java @@ -0,0 +1,10 @@ +package blackjack.domain.participants; + +import blackjack.domain.card.ShuffledDeck; +import java.util.List; + +public record PlayerGroup(List players) { + public void deal(ShuffledDeck deck) { + players.forEach(participant -> participant.hit(deck.draw())); + } +} diff --git a/src/main/java/blackjack/domain/participant/PlayerGroup.java b/src/main/java/blackjack/domain/participants/PlayerNames.java similarity index 50% rename from src/main/java/blackjack/domain/participant/PlayerGroup.java rename to src/main/java/blackjack/domain/participants/PlayerNames.java index 459ec75dd5..756a2af929 100644 --- a/src/main/java/blackjack/domain/participant/PlayerGroup.java +++ b/src/main/java/blackjack/domain/participants/PlayerNames.java @@ -1,27 +1,19 @@ -package blackjack.domain.participant; +package blackjack.domain.participants; -import blackjack.domain.card.Deck; -import blackjack.domain.card.Hand; import java.util.Arrays; import java.util.List; -public record PlayerGroup(List players) { +public record PlayerNames(List names) { private static final String DELIMITER = ","; private static final int INCLUDE_EMPTY_ELEMENT = -1; - public static PlayerGroup from(final String rawPlayerNames) { - List players = parsePlayersFrom(rawPlayerNames); - return new PlayerGroup(players); - } - - private static List parsePlayersFrom(String rawPlayerNames) { + public static PlayerNames from(String rawPlayerNames) { List playerNames = Arrays.stream( rawPlayerNames.split(DELIMITER, INCLUDE_EMPTY_ELEMENT)) .map(Name::new) .toList(); validateDuplicatedNames(playerNames); - - return parsePlayersFrom(playerNames); + return new PlayerNames(playerNames); } private static void validateDuplicatedNames(List playerNames) { @@ -34,25 +26,17 @@ private static void validateDuplicatedNames(List playerNames) { } private static boolean containsDealerName(List playerNames) { - return playerNames.stream().anyMatch(Dealer::isDealerName); + return playerNames.stream() + .anyMatch(Dealer::isDealerName); } private static boolean isDuplicated(List playerNames) { - return playerNames.stream().distinct().count() != playerNames.size(); + return countDistinct(playerNames) != playerNames.size(); } - private static List parsePlayersFrom(List playerNames) { + private static long countDistinct(List playerNames) { return playerNames.stream() - .map(playerName -> new Player(playerName, new Hand())) - .toList(); - } - - @Override - public List players() { - return List.copyOf(players); - } - - public void deal(Deck deck) { - players.forEach(participant -> participant.hit(deck.draw())); + .distinct() + .count(); } } diff --git a/src/main/java/blackjack/domain/participants/Profit.java b/src/main/java/blackjack/domain/participants/Profit.java new file mode 100644 index 0000000000..624c4d563f --- /dev/null +++ b/src/main/java/blackjack/domain/participants/Profit.java @@ -0,0 +1,18 @@ +package blackjack.domain.participants; + +public record Profit(long value) { + private static final long BLACKJACK_PAYOUT_NUMERATOR = 3; + private static final long BLACKJACK_PAYOUT_DENOMINATOR = 2; + + public static Profit zero() { + return new Profit(0L); + } + + public Profit negative() { + return new Profit(this.value * -1); + } + + public Profit applyBlackjackPayout() { + return new Profit(this.value * BLACKJACK_PAYOUT_NUMERATOR / BLACKJACK_PAYOUT_DENOMINATOR); + } +} diff --git a/src/main/java/blackjack/domain/result/GameReferee.java b/src/main/java/blackjack/domain/result/GameReferee.java deleted file mode 100644 index ba0f5245e6..0000000000 --- a/src/main/java/blackjack/domain/result/GameReferee.java +++ /dev/null @@ -1,8 +0,0 @@ -package blackjack.domain.result; - -import blackjack.domain.participant.Dealer; -import blackjack.domain.participant.Player; - -public interface GameReferee { - GameResult judge(Dealer dealer, Player player); -} diff --git a/src/main/java/blackjack/domain/result/GameResult.java b/src/main/java/blackjack/domain/result/GameResult.java deleted file mode 100644 index bd2c9c339b..0000000000 --- a/src/main/java/blackjack/domain/result/GameResult.java +++ /dev/null @@ -1,5 +0,0 @@ -package blackjack.domain.result; - -public enum GameResult { - PLAYER_WIN, DEALER_WIN, PUSH -} diff --git a/src/main/java/blackjack/dto/DealerHitDto.java b/src/main/java/blackjack/dto/DealerHitDto.java index 7881e44500..fadbb9be2b 100644 --- a/src/main/java/blackjack/dto/DealerHitDto.java +++ b/src/main/java/blackjack/dto/DealerHitDto.java @@ -1,6 +1,6 @@ package blackjack.dto; -import blackjack.domain.participant.Dealer; +import blackjack.domain.participants.Dealer; public record DealerHitDto(String dealerName, int hitCount) { public static DealerHitDto of(Dealer dealer, int dealerHitCount) { diff --git a/src/main/java/blackjack/dto/DealerProfitDto.java b/src/main/java/blackjack/dto/DealerProfitDto.java new file mode 100644 index 0000000000..fd571454bf --- /dev/null +++ b/src/main/java/blackjack/dto/DealerProfitDto.java @@ -0,0 +1,18 @@ +package blackjack.dto; + +import blackjack.domain.participants.Player; +import blackjack.domain.participants.Profit; +import java.util.Map; + +public record DealerProfitDto(long profit) { + public static DealerProfitDto from(Map playerProfits) { + long playerProfitSum = playerProfits.values().stream() + .mapToLong(Profit::value) + .sum(); + return new DealerProfitDto(calculateDealerProfit(playerProfitSum)); + } + + private static long calculateDealerProfit(long playerProfitSum) { + return playerProfitSum * -1; + } +} diff --git a/src/main/java/blackjack/dto/DealerResultDto.java b/src/main/java/blackjack/dto/DealerResultDto.java deleted file mode 100644 index 838f852501..0000000000 --- a/src/main/java/blackjack/dto/DealerResultDto.java +++ /dev/null @@ -1,30 +0,0 @@ -package blackjack.dto; - -import blackjack.domain.participant.Player; -import blackjack.domain.result.GameResult; -import java.util.Map; - -public record DealerResultDto(int win, int lose, int push) { - public static DealerResultDto from(Map results) { - int winCount = getPlayerWinCount(results); - int loseCount = getPlayerLoseCount(results); - int pushCount = getPushCount(results.size(), loseCount, winCount); - return new DealerResultDto(winCount, loseCount, pushCount); - } - - private static int getPlayerWinCount(Map results) { - return (int) results.values().stream() - .filter(result -> result == GameResult.DEALER_WIN) - .count(); - } - - private static int getPlayerLoseCount(Map results) { - return (int) results.values().stream() - .filter(result -> result == GameResult.PLAYER_WIN) - .count(); - } - - private static int getPushCount(int playerCount, int loseCount, int winCount) { - return playerCount - loseCount - winCount; - } -} diff --git a/src/main/java/blackjack/dto/GameResultDto.java b/src/main/java/blackjack/dto/GameResultDto.java deleted file mode 100644 index 6b17fec1d3..0000000000 --- a/src/main/java/blackjack/dto/GameResultDto.java +++ /dev/null @@ -1,13 +0,0 @@ -package blackjack.dto; - -import blackjack.domain.participant.Player; -import blackjack.domain.result.GameResult; - -public record GameResultDto( - String playerName, - GameResult result -) { - public static GameResultDto of(Player player, GameResult gameResult) { - return new GameResultDto(player.getName(), gameResult); - } -} diff --git a/src/main/java/blackjack/dto/GameResultDtos.java b/src/main/java/blackjack/dto/GameResultDtos.java index 4cff8d46e2..2fbb29dd32 100644 --- a/src/main/java/blackjack/dto/GameResultDtos.java +++ b/src/main/java/blackjack/dto/GameResultDtos.java @@ -1,20 +1,25 @@ package blackjack.dto; -import blackjack.domain.participant.Player; -import blackjack.domain.result.GameResult; +import blackjack.domain.participants.Player; +import blackjack.domain.participants.Profit; import java.util.List; import java.util.Map; +import java.util.Map.Entry; public record GameResultDtos( - DealerResultDto dealerResultDto, - List gameResultDtos + DealerProfitDto dealerProfitDto, + List playerProfitDtos ) { - public static GameResultDtos of(Map results) { - List gameResultDtos = results.entrySet().stream() - .map(entry -> GameResultDto.of(entry.getKey(), entry.getValue())) + public static GameResultDtos of(Map playerProfits) { + List playerProfitDtos = playerProfits.entrySet().stream() + .map(GameResultDtos::convert) .toList(); - DealerResultDto dealerResultDto = DealerResultDto.from(results); - return new GameResultDtos(dealerResultDto, gameResultDtos); + DealerProfitDto dealerProfitDto = DealerProfitDto.from(playerProfits); + return new GameResultDtos(dealerProfitDto, playerProfitDtos); + } + + private static PlayerProfitDto convert(Entry playerProfit) { + return PlayerProfitDto.of(playerProfit.getKey(), playerProfit.getValue()); } } diff --git a/src/main/java/blackjack/dto/InitialDealDtos.java b/src/main/java/blackjack/dto/InitialDealDtos.java index 59b8201005..09eec6591c 100644 --- a/src/main/java/blackjack/dto/InitialDealDtos.java +++ b/src/main/java/blackjack/dto/InitialDealDtos.java @@ -1,7 +1,7 @@ package blackjack.dto; -import blackjack.domain.participant.Dealer; -import blackjack.domain.participant.Player; +import blackjack.domain.participants.Dealer; +import blackjack.domain.participants.Player; import java.util.List; public record InitialDealDtos( diff --git a/src/main/java/blackjack/dto/ParticipantCardsDto.java b/src/main/java/blackjack/dto/ParticipantCardsDto.java index 5f8ce22126..09b47c1250 100644 --- a/src/main/java/blackjack/dto/ParticipantCardsDto.java +++ b/src/main/java/blackjack/dto/ParticipantCardsDto.java @@ -1,24 +1,36 @@ package blackjack.dto; -import blackjack.domain.participant.Participant; +import blackjack.domain.card.Card; +import blackjack.domain.participants.Dealer; +import blackjack.domain.participants.Player; import java.util.List; public record ParticipantCardsDto( String participantName, List cards ) { - public static ParticipantCardsDto from(Participant participant) { - List cards = participant.getCards().stream() + public static ParticipantCardsDto from(Player player) { + return new ParticipantCardsDto( + player.getName(), + convertCards(player.getCards())); + } + + public static ParticipantCardsDto of(Dealer dealer, long size) { + return new ParticipantCardsDto( + dealer.getName(), + convertCards(dealer.getCards(), size)); + } + + private static List convertCards(List cards) { + return cards.stream() .map(CardNameDto::from) .toList(); - return new ParticipantCardsDto(participant.getName(), cards); } - public static ParticipantCardsDto of(Participant participant, long size) { - List cards = participant.getCards().stream() - .limit(size) + private static List convertCards(List cards, long size) { + return cards.stream() .map(CardNameDto::from) + .limit(size) .toList(); - return new ParticipantCardsDto(participant.getName(), cards); } } diff --git a/src/main/java/blackjack/dto/ParticipantScoreDto.java b/src/main/java/blackjack/dto/ParticipantScoreDto.java index 480859a08d..66936343d0 100644 --- a/src/main/java/blackjack/dto/ParticipantScoreDto.java +++ b/src/main/java/blackjack/dto/ParticipantScoreDto.java @@ -1,7 +1,8 @@ package blackjack.dto; -import blackjack.domain.participant.Participant; -import blackjack.domain.result.Score; +import blackjack.domain.game.Score; +import blackjack.domain.participants.Dealer; +import blackjack.domain.participants.Player; import java.util.List; public record ParticipantScoreDto( @@ -9,10 +10,17 @@ public record ParticipantScoreDto( List cards, Score score ) { - public static ParticipantScoreDto from(Participant participant, Score score) { - List cards = participant.getCards().stream() + public static ParticipantScoreDto from(Dealer dealer, Score score) { + List cards = dealer.getCards().stream() .map(CardNameDto::from) .toList(); - return new ParticipantScoreDto(participant.getName(), cards, score); + return new ParticipantScoreDto(dealer.getName(), cards, score); + } + + public static ParticipantScoreDto from(Player player, Score score) { + List cards = player.getCards().stream() + .map(CardNameDto::from) + .toList(); + return new ParticipantScoreDto(player.getName(), cards, score); } } diff --git a/src/main/java/blackjack/dto/ParticipantScoreDtos.java b/src/main/java/blackjack/dto/ParticipantScoreDtos.java index fe5a2db74c..3e54272890 100644 --- a/src/main/java/blackjack/dto/ParticipantScoreDtos.java +++ b/src/main/java/blackjack/dto/ParticipantScoreDtos.java @@ -1,28 +1,26 @@ package blackjack.dto; -import blackjack.domain.participant.Dealer; -import blackjack.domain.participant.Participant; -import blackjack.domain.participant.Player; +import blackjack.domain.participants.Dealer; +import blackjack.domain.participants.Player; +import java.util.ArrayList; import java.util.List; -import java.util.stream.Stream; public record ParticipantScoreDtos(List scoreDtos) { public static ParticipantScoreDtos of(Dealer dealer, List players) { - List scoreDtos = participantStream(dealer, players) - .map(ParticipantScoreDtos::from) - .toList(); + List scoreDtos = new ArrayList<>(); + + scoreDtos.add(converScore(dealer)); + players.forEach(player -> scoreDtos.add(converScore(player))); + return new ParticipantScoreDtos(scoreDtos); } - private static Stream participantStream(Dealer dealer, List players) { - return Stream.concat( - Stream.of(dealer), - players.stream() - ); + private static ParticipantScoreDto converScore(Dealer dealer) { + return ParticipantScoreDto.from(dealer, dealer.getScore()); } - private static ParticipantScoreDto from(Participant participant) { - return ParticipantScoreDto.from(participant, participant.getScore()); + private static ParticipantScoreDto converScore(Player player) { + return ParticipantScoreDto.from(player, player.getScore()); } } diff --git a/src/main/java/blackjack/dto/PlayerProfitDto.java b/src/main/java/blackjack/dto/PlayerProfitDto.java new file mode 100644 index 0000000000..92a3c8c42e --- /dev/null +++ b/src/main/java/blackjack/dto/PlayerProfitDto.java @@ -0,0 +1,13 @@ +package blackjack.dto; + +import blackjack.domain.participants.Player; +import blackjack.domain.participants.Profit; + +public record PlayerProfitDto( + String playerName, + long profit +) { + public static PlayerProfitDto of(Player player, Profit profit) { + return new PlayerProfitDto(player.getName(), profit.value()); + } +} diff --git a/src/main/java/blackjack/view/BlackjackView.java b/src/main/java/blackjack/view/BlackjackView.java index c9b819d621..c1a2555038 100644 --- a/src/main/java/blackjack/view/BlackjackView.java +++ b/src/main/java/blackjack/view/BlackjackView.java @@ -20,6 +20,11 @@ public String readPlayers() { return inputView.readPlayerNames(); } + public long readBetAmount(final String playerName) { + outputView.askBetAmount(playerName); + return inputView.readLong(); + } + public boolean isHitAnswer(final String playerName) { outputView.askHit(playerName); return inputView.isHitAnswer(); diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java index 1db235686a..b2bf23d14c 100644 --- a/src/main/java/blackjack/view/InputView.java +++ b/src/main/java/blackjack/view/InputView.java @@ -16,6 +16,18 @@ public String readPlayerNames() { return playerNames; } + public long readLong() { + return parseLong(readStrippedLine()); + } + + public Long parseLong(final String rawInput) { + try { + return Long.parseLong(rawInput); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("숫자만 입력 가능합니다."); + } + } + public boolean isHitAnswer() { return isYes(readStrippedLine()); } diff --git a/src/main/java/blackjack/view/OutputView.java b/src/main/java/blackjack/view/OutputView.java index 06316f110d..382096f917 100644 --- a/src/main/java/blackjack/view/OutputView.java +++ b/src/main/java/blackjack/view/OutputView.java @@ -1,38 +1,36 @@ package blackjack.view; -import blackjack.domain.result.GameResult; -import blackjack.domain.result.Score; +import blackjack.domain.game.Score; import blackjack.dto.CardNameDto; import blackjack.dto.DealerHitDto; -import blackjack.dto.DealerResultDto; -import blackjack.dto.GameResultDto; +import blackjack.dto.DealerProfitDto; import blackjack.dto.GameResultDtos; import blackjack.dto.InitialDealDtos; import blackjack.dto.ParticipantCardsDto; import blackjack.dto.ParticipantScoreDto; import blackjack.dto.ParticipantScoreDtos; +import blackjack.dto.PlayerProfitDto; import java.util.List; -import java.util.Map; public class OutputView { private static final String YES = "y"; private static final String NO = "n"; - private static final Map GAME_RESULT_MESSAGES = Map.of( - GameResult.PLAYER_WIN, "승", - GameResult.DEALER_WIN, "패", - GameResult.PUSH, "푸시" - ); - public void askPlayerNames() { System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); } + public void askBetAmount(String playerName) { + System.out.println(playerName + "의 배팅 금액은?"); + } + public void printInitialDeal(InitialDealDtos initialDealDtos) { List playerNames = initialDealDtos.playerCardsDtos().stream() .map(ParticipantCardsDto::participantName) .toList(); String joinedPlayerNames = String.join(", ", playerNames); + + System.out.println(); System.out.printf( "%s와 %s에게 2장을 나누었습니다.%n", initialDealDtos.dealerCardsDto().participantName(), @@ -40,14 +38,15 @@ public void printInitialDeal(InitialDealDtos initialDealDtos) { ); printParticipantInitialCards(initialDealDtos); - System.out.println(); } private void printParticipantInitialCards(InitialDealDtos initialDealDtos) { + System.out.println(); printParticipantCards(initialDealDtos.dealerCardsDto()); - for (ParticipantCardsDto participantCardsDto : initialDealDtos.playerCardsDtos()) { - printParticipantCards(participantCardsDto); + for (ParticipantCardsDto playerCardsDto : initialDealDtos.playerCardsDtos()) { + printParticipantCards(playerCardsDto); } + System.out.println(); } public void printParticipantCards(ParticipantCardsDto participantCardsDto) { @@ -62,6 +61,7 @@ public void askHit(final String playerName) { } public void printDealerHit(DealerHitDto dealerHitDto) { + System.out.println(); System.out.printf("%s는 16장이하라 %d장의 카드를 더 받았습니다.%n", dealerHitDto.dealerName(), dealerHitDto.hitCount()); @@ -92,15 +92,13 @@ private List parseCardsToOutputs(List cards) { public void printResults(GameResultDtos resultDto) { System.out.println("## 최종 승패"); - DealerResultDto dealerResult = resultDto.dealerResultDto(); - System.out.printf("딜러: %d승 %d패 %d무\n", dealerResult.win(), dealerResult.lose(), - dealerResult.push()); + DealerProfitDto dealerProfitDto = resultDto.dealerProfitDto(); + System.out.println("딜러: " + dealerProfitDto.profit()); - resultDto.gameResultDtos().forEach(this::printResult); + resultDto.playerProfitDtos().forEach(this::printResult); } - public void printResult(GameResultDto gameResultDto) { - System.out.println(gameResultDto.playerName() + ": " + GAME_RESULT_MESSAGES.get( - gameResultDto.result())); + public void printResult(PlayerProfitDto playerProfitDto) { + System.out.println(playerProfitDto.playerName() + ": " + playerProfitDto.profit()); } } diff --git a/src/test/java/blackjack/domain/card/CardTest.java b/src/test/java/blackjack/domain/card/CardTest.java index 3bc4d0cbe8..1057cbb8f4 100644 --- a/src/test/java/blackjack/domain/card/CardTest.java +++ b/src/test/java/blackjack/domain/card/CardTest.java @@ -8,7 +8,6 @@ import org.junit.jupiter.params.provider.EnumSource.Mode; class CardTest { - @Test void ACE_카드와_일치한다면_TRUE_를_반환한다() { // given diff --git a/src/test/java/blackjack/domain/card/HandTest.java b/src/test/java/blackjack/domain/card/HandTest.java index 6a17364266..69dd1512b6 100644 --- a/src/test/java/blackjack/domain/card/HandTest.java +++ b/src/test/java/blackjack/domain/card/HandTest.java @@ -2,7 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import blackjack.domain.result.Score; +import blackjack.domain.game.Score; import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -12,18 +12,19 @@ class HandTest { private static final int ACE_ADJUST_AMOUNT = 10; private static final int BUST_THRESHOLD = 21; + private static final int BLACKJACK_SCORE = 21; private final Card card = new Card(Rank.ACE, Suit.CLOVER); @Test void 한장의_카드를_받아서_손패에_추가한다() { // given - Hand hand = new Hand(); - List existCards = hand.getCards(); + Hand hand = Hand.empty(); + List existCards = hand.cards(); // when hand.addCard(card); // then - List addedCards = hand.getCards(); + List addedCards = hand.cards(); assertThat(addedCards.size()).isEqualTo(existCards.size() + 1); } @@ -38,7 +39,7 @@ class HandTest { int sum = cards.stream().mapToInt(Card::getValue).sum(); // when Hand hand = new Hand(cards); - Score score = hand.getScore(); + Score score = hand.calculateScore(); // then assertThat(score.value()).isEqualTo(sum + ACE_ADJUST_AMOUNT); } @@ -55,7 +56,7 @@ class HandTest { int sum = cards.stream().mapToInt(Card::getValue).sum(); // when Hand hand = new Hand(cards); - Score score = hand.getScore(); + Score score = hand.calculateScore(); // then assertThat(score.value()).isEqualTo(sum); } @@ -68,7 +69,7 @@ class HandTest { int sum = rank.getValue(); // when Hand hand = new Hand(cards); - Score score = hand.getScore(); + Score score = hand.calculateScore(); // then assertThat(score.value()).isEqualTo(sum); } @@ -101,4 +102,58 @@ class HandTest { assertThat(sum <= BUST_THRESHOLD).isTrue(); assertThat(hand.isBust()).isFalse(); } + + @Test + void 점수와_카드수_모두_블랙잭_조건이면_블랙잭이다() { + // given + List cards = List.of( + new Card(Rank.ACE, Suit.HEART), + new Card(Rank.TEN, Suit.HEART) + ); + Hand hand = new Hand(cards); + // when & then + assertThat(hand.calculateScore().value()).isEqualTo(BLACKJACK_SCORE); + assertThat(hand.isBlackjack()).isTrue(); + } + + @Test + void 점수만_블랙잭_조건이면_블랙잭이_아니다() { + // given + List cards = List.of( + new Card(Rank.ACE, Suit.HEART), + new Card(Rank.ACE, Suit.HEART), + new Card(Rank.NINE, Suit.HEART) + ); + Hand hand = new Hand(cards); + // when & then + assertThat(hand.calculateScore().value()).isEqualTo(BLACKJACK_SCORE); + assertThat(hand.isBlackjack()).isFalse(); + } + + @Test + void 카드수만_블랙잭_조건이면_블랙잭이_아니다() { + // given + List cards = List.of( + new Card(Rank.ACE, Suit.HEART), + new Card(Rank.NINE, Suit.HEART) + ); + Hand hand = new Hand(cards); + // when & then + assertThat(hand.calculateScore().value()).isNotEqualTo(BLACKJACK_SCORE); + assertThat(hand.isBlackjack()).isFalse(); + } + + @Test + void 점수와_카드수_모두_블랙잭_조건이_아니면_블랙잭이_아니다() { + // given + List cards = List.of( + new Card(Rank.ACE, Suit.HEART), + new Card(Rank.TWO, Suit.HEART), + new Card(Rank.THREE, Suit.HEART) + ); + Hand hand = new Hand(cards); + // when & then + assertThat(hand.calculateScore().value()).isNotEqualTo(BLACKJACK_SCORE); + assertThat(hand.isBlackjack()).isFalse(); + } } diff --git a/src/test/java/blackjack/domain/card/ShuffledCardsGeneratorTest.java b/src/test/java/blackjack/domain/card/ShuffledCardsGeneratorTest.java deleted file mode 100644 index e1dfd201e8..0000000000 --- a/src/test/java/blackjack/domain/card/ShuffledCardsGeneratorTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package blackjack.domain.card; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.List; -import org.junit.jupiter.api.Test; - -class ShuffledCardsGeneratorTest { - - @Test - void 덱을_생성하면_모든_경우의_수의_중복이_없는_카드들이_생성한다() { - // given - CardsGenerator generator = new ShuffledCardsGenerator(); - // when - List cards = generator.create(); - // then - int rankSize = Rank.values().length; - int suitSize = Suit.values().length; - assertThat(cards.size()).isEqualTo(rankSize * suitSize); - } -} diff --git a/src/test/java/blackjack/domain/card/DeckTest.java b/src/test/java/blackjack/domain/card/ShuffledDeckTest.java similarity index 59% rename from src/test/java/blackjack/domain/card/DeckTest.java rename to src/test/java/blackjack/domain/card/ShuffledDeckTest.java index 1eadc49723..7751e569e7 100644 --- a/src/test/java/blackjack/domain/card/DeckTest.java +++ b/src/test/java/blackjack/domain/card/ShuffledDeckTest.java @@ -4,19 +4,18 @@ import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -class DeckTest { - - private final CardsGenerator generator = new ShuffledCardsGenerator(); - private final CardsGenerator emptyCardsGenerator = List::of; +class ShuffledDeckTest { @Test void CardsGenerator를_통해_덱_인스턴스를_생성한다() { - assertThatCode(() -> Deck.create(generator)) + assertThatCode(ShuffledDeck::create) .doesNotThrowAnyException(); } @@ -26,7 +25,7 @@ class ReturnCard { @Test void 남은_카드가_있다면_예외가_발생하지_않는다() { // given - Deck deck = Deck.create(generator); + ShuffledDeck deck = ShuffledDeck.create(); // when & then assertThatCode(deck::draw) .doesNotThrowAnyException(); @@ -35,7 +34,7 @@ class ReturnCard { @Test void null이_아닌_인스턴스를_반환한다() { // given - Deck deck = Deck.create(generator); + ShuffledDeck deck = ShuffledDeck.create(); // when Card card = deck.draw(); // then @@ -45,10 +44,25 @@ class ReturnCard { @Test void 남은_카드가_없다면_예외를_던진다() { // given - Deck deck = Deck.create(emptyCardsGenerator); + ShuffledDeck emptyDeck = new ShuffledDeck(List.of()); // when & then - assertThatThrownBy(deck::draw) + assertThatThrownBy(emptyDeck::draw) .isInstanceOf(IllegalStateException.class); } } + + @Test + void 덱을_생성하면_모든_경우의_수의_중복이_없는_카드들이_생성한다() { + // given & when + ShuffledDeck deck = ShuffledDeck.create(); + // then + int expectedCardCount = Rank.values().length * Suit.values().length; + + Set uniqueCards = new HashSet<>(); + for (int i = 0; i < expectedCardCount; i++) { + uniqueCards.add(deck.draw()); + } + + assertThat(uniqueCards).hasSize(expectedCardCount); + } } diff --git a/src/test/java/blackjack/domain/result/BlackjackGameRefereeTest.java b/src/test/java/blackjack/domain/game/BlackjackGameRefereeTest.java similarity index 54% rename from src/test/java/blackjack/domain/result/BlackjackGameRefereeTest.java rename to src/test/java/blackjack/domain/game/BlackjackGameRefereeTest.java index 405aeb661b..13fe09268a 100644 --- a/src/test/java/blackjack/domain/result/BlackjackGameRefereeTest.java +++ b/src/test/java/blackjack/domain/game/BlackjackGameRefereeTest.java @@ -1,4 +1,4 @@ -package blackjack.domain.result; +package blackjack.domain.game; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; @@ -6,34 +6,34 @@ import blackjack.domain.card.Hand; import blackjack.domain.card.Rank; import blackjack.domain.card.Suit; -import blackjack.domain.participant.Dealer; -import blackjack.domain.participant.Player; +import blackjack.domain.participants.Bet; +import blackjack.domain.participants.Dealer; +import blackjack.domain.participants.Player; import java.util.List; -import javax.security.auth.RefreshFailedException; import org.junit.jupiter.api.Test; class BlackjackGameRefereeTest { - private static final String PLAYER_NAME = "플레이어"; + private final String playerName = "플레이어"; + private final Bet bet = new Bet(1000L); + private final BlackjackGameReferee referee = new BlackjackGameReferee(); - private static final Hand LOWER_SCORE_HAND = + private final Hand lowerScoreHand = new Hand(List.of(new Card(Rank.TEN, Suit.DIAMOND))); - private static final Hand DEFAULT_SCORE_HAND = + private final Hand defaultScoreHand = new Hand(List.of(new Card(Rank.TEN, Suit.DIAMOND), new Card(Rank.FIVE, Suit.CLOVER))); - private static final Hand HIGHER_SCORE_HAND = + private final Hand higherScoreHand = new Hand(List.of(new Card(Rank.TEN, Suit.DIAMOND), new Card(Rank.TEN, Suit.CLOVER))); - private static final Hand BUST_SCORE_HAND = + private final Hand bustScoreHand = new Hand(List.of(new Card(Rank.TEN, Suit.DIAMOND), new Card(Rank.TEN, Suit.CLOVER), new Card(Rank.TWO, Suit.CLOVER))); - private static final BlackjackGameReferee REFEREE = new BlackjackGameReferee(); - @Test void 둘_다_버스트가_아니면서_플레이어가_점수가_더_높다면_플레이어가_승리한다() { // given - Dealer dealer = new Dealer(LOWER_SCORE_HAND); - Player player = new Player(PLAYER_NAME, HIGHER_SCORE_HAND); + Dealer dealer = new Dealer(lowerScoreHand); + Player player = new Player(this.playerName, higherScoreHand, bet); // when - GameResult result = REFEREE.judge(dealer, player); + GameResult result = referee.judge(dealer, player); // then assertThat(result).isEqualTo(GameResult.PLAYER_WIN); } @@ -41,10 +41,10 @@ class BlackjackGameRefereeTest { @Test void 둘_다_버스트가_아니면서_딜러가_점수가_더_높다면_딜러가_승리한다() { // given - Dealer dealer = new Dealer(HIGHER_SCORE_HAND); - Player player = new Player(PLAYER_NAME, LOWER_SCORE_HAND); + Dealer dealer = new Dealer(higherScoreHand); + Player player = new Player(this.playerName, lowerScoreHand, bet); // when - GameResult result = REFEREE.judge(dealer, player); + GameResult result = referee.judge(dealer, player); // then assertThat(result).isEqualTo(GameResult.DEALER_WIN); } @@ -52,10 +52,10 @@ class BlackjackGameRefereeTest { @Test void 둘_다_버스트가_아니면서_점수가_같다면_무승부한다() { // given - Dealer dealer = new Dealer(DEFAULT_SCORE_HAND); - Player player = new Player(PLAYER_NAME, DEFAULT_SCORE_HAND); + Dealer dealer = new Dealer(defaultScoreHand); + Player player = new Player(this.playerName, defaultScoreHand, bet); // when - GameResult result = REFEREE.judge(dealer, player); + GameResult result = referee.judge(dealer, player); // then assertThat(result).isEqualTo(GameResult.PUSH); } @@ -63,10 +63,10 @@ class BlackjackGameRefereeTest { @Test void 플레이어가_버스트라면_딜러가_승리한다() { // given - Dealer dealer = new Dealer(DEFAULT_SCORE_HAND); - Player player = new Player(PLAYER_NAME, BUST_SCORE_HAND); + Dealer dealer = new Dealer(defaultScoreHand); + Player player = new Player(this.playerName, bustScoreHand, bet); // when - GameResult result = REFEREE.judge(dealer, player); + GameResult result = referee.judge(dealer, player); // then assertThat(result).isEqualTo(GameResult.DEALER_WIN); } @@ -74,10 +74,10 @@ class BlackjackGameRefereeTest { @Test void 딜러만_버스트라면_플레이어가_승리한다() { // given - Dealer dealer = new Dealer(BUST_SCORE_HAND); - Player player = new Player(PLAYER_NAME, DEFAULT_SCORE_HAND); + Dealer dealer = new Dealer(bustScoreHand); + Player player = new Player(this.playerName, defaultScoreHand, bet); // when - GameResult result = REFEREE.judge(dealer, player); + GameResult result = referee.judge(dealer, player); // then assertThat(result).isEqualTo(GameResult.PLAYER_WIN); } @@ -85,12 +85,11 @@ class BlackjackGameRefereeTest { @Test void 둘_다_버스트라면_딜러가_승리한다() { // given - Dealer dealer = new Dealer(BUST_SCORE_HAND); - Player player = new Player(PLAYER_NAME, BUST_SCORE_HAND); + Dealer dealer = new Dealer(bustScoreHand); + Player player = new Player(this.playerName, bustScoreHand, bet); // when - GameResult result = REFEREE.judge(dealer, player); + GameResult result = referee.judge(dealer, player); // then assertThat(result).isEqualTo(GameResult.DEALER_WIN); } - } \ No newline at end of file diff --git a/src/test/java/blackjack/domain/game/BlackjackGameTest.java b/src/test/java/blackjack/domain/game/BlackjackGameTest.java new file mode 100644 index 0000000000..8d99ddf5c9 --- /dev/null +++ b/src/test/java/blackjack/domain/game/BlackjackGameTest.java @@ -0,0 +1,46 @@ +package blackjack.domain.game; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.card.Hand; +import blackjack.domain.participants.Bet; +import blackjack.domain.participants.Dealer; +import blackjack.domain.participants.Name; +import blackjack.domain.participants.Player; +import blackjack.domain.participants.PlayerGroup; +import java.util.List; +import org.junit.jupiter.api.Test; + +class BlackjackGameTest { + private static final int INITIAL_DEAL_COUNT = 2; + private static final int DEALER_HIT_THRESHOLD = 16; + + private final Bet bet = new Bet(1000L); + + private final BlackjackGame game = BlackjackGame.createBasic( + new PlayerGroup(List.of(new Player(new Name("pobi"), Hand.empty(), bet)))); + + @Test + void 초기_카드를_딜러와_플레이어들에게_2장씩_분배한다() { + // given & when + game.initialDeal(); + // then + Dealer dealer = game.getDealer(); + List players = game.getPlayers(); + + assertThat(dealer.getCards()).hasSize(INITIAL_DEAL_COUNT); + players.forEach(player -> + assertThat(player.getCards()).hasSize(INITIAL_DEAL_COUNT)); + } + + @Test + void 딜러는_카드_합계가_임계점을_초과할_때까지_hit한다() { + // given & when + int hitCount = game.playDealerTurn(); + // then + Dealer dealer = game.getDealer(); + + assertThat(dealer.getScore().value()).isGreaterThan(DEALER_HIT_THRESHOLD); + assertThat(dealer.getCards()).hasSize(hitCount); + } +} \ No newline at end of file diff --git a/src/test/java/blackjack/domain/result/ScoreTest.java b/src/test/java/blackjack/domain/game/ScoreTest.java similarity index 98% rename from src/test/java/blackjack/domain/result/ScoreTest.java rename to src/test/java/blackjack/domain/game/ScoreTest.java index 1f3604aaf3..beeba95f31 100644 --- a/src/test/java/blackjack/domain/result/ScoreTest.java +++ b/src/test/java/blackjack/domain/game/ScoreTest.java @@ -1,4 +1,4 @@ -package blackjack.domain.result; +package blackjack.domain.game; import static org.assertj.core.api.Assertions.assertThat; diff --git a/src/test/java/blackjack/domain/participant/PlayerTest.java b/src/test/java/blackjack/domain/participant/PlayerTest.java deleted file mode 100644 index b9e9017af0..0000000000 --- a/src/test/java/blackjack/domain/participant/PlayerTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package blackjack.domain.participant; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; - -import blackjack.domain.card.Hand; -import org.junit.jupiter.api.Test; - -class PlayerTest { - private final Hand emptyHand = new Hand(); - - @Test - void 본인의_이름을_반환한다() { - // given - String playerName = "Player Name"; - // when - Player player = new Player(playerName, emptyHand); - // then - assertThat(player.getName()).isEqualTo(playerName); - } - - @Test - void 이름이_null인_경우_에러를_던진다() { - // given - String playerName = null; - // when & then - assertThatThrownBy(() -> new Player(playerName, emptyHand)) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - void 이름이_blank인_경우_에러를_던진다() { - // given - String playerName = ""; - // when & then - assertThatThrownBy(() -> new Player(playerName, emptyHand)) - .isInstanceOf(IllegalArgumentException.class); - } -} diff --git a/src/test/java/blackjack/domain/participants/BetTest.java b/src/test/java/blackjack/domain/participants/BetTest.java new file mode 100644 index 0000000000..609fe6d0b0 --- /dev/null +++ b/src/test/java/blackjack/domain/participants/BetTest.java @@ -0,0 +1,67 @@ +package blackjack.domain.participants; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import blackjack.domain.game.GameResult; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class BetTest { + private static final long BET_AMOUNT = 1000; + private static final long BLACKJACK_PAYOUT_NUMERATOR = 3; + private static final long BLACKJACK_PAYOUT_DENOMINATOR = 2; + + @ParameterizedTest + @ValueSource(ints = {-1, 0}) + void 금액이_양수가_아니면_예외를_던진다(int amount) { + assertThatThrownBy(() -> new Bet(amount)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 플레이어가_블랙잭이면_배팅금의_1_5배의_수익를_반환한다() { + // given + Bet bet = new Bet(BET_AMOUNT); + GameResult gameResult = GameResult.PLAYER_BLACKJACK; + // when + Profit profit = bet.calculateProfit(gameResult); + // then + assertThat(profit.value()).isEqualTo( + BET_AMOUNT * BLACKJACK_PAYOUT_NUMERATOR / BLACKJACK_PAYOUT_DENOMINATOR); + } + + @Test + void 결과가_무승부라면_0원의_수익을_반환한다() { + // given + Bet bet = new Bet(BET_AMOUNT); + GameResult gameResult = GameResult.PUSH; + // when + Profit profit = bet.calculateProfit(gameResult); + // then + assertThat(profit.value()).isEqualTo(0L); + } + + @Test + void 딜러가_승리하면_배팅금만큼_손실한_금액을_반환한다() { + // given + Bet bet = new Bet(BET_AMOUNT); + GameResult gameResult = GameResult.DEALER_WIN; + // when + Profit profit = bet.calculateProfit(gameResult); + // then + assertThat(profit.value()).isEqualTo(BET_AMOUNT * -1); + } + + @Test + void 플레이어가_승리하면_배팅_금액과_동일한_금액의_수익을_반환한다() { + // given + Bet bet = new Bet(BET_AMOUNT); + GameResult gameResult = GameResult.PLAYER_WIN; + // when + Profit profit = bet.calculateProfit(gameResult); + // then + assertThat(profit.value()).isEqualTo(BET_AMOUNT); + } +} \ No newline at end of file diff --git a/src/test/java/blackjack/domain/participant/DealerTest.java b/src/test/java/blackjack/domain/participants/DealerTest.java similarity index 86% rename from src/test/java/blackjack/domain/participant/DealerTest.java rename to src/test/java/blackjack/domain/participants/DealerTest.java index 286b2d621d..1b34dba835 100644 --- a/src/test/java/blackjack/domain/participant/DealerTest.java +++ b/src/test/java/blackjack/domain/participants/DealerTest.java @@ -1,4 +1,4 @@ -package blackjack.domain.participant; +package blackjack.domain.participants; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; @@ -11,7 +11,7 @@ class DealerTest { @Test void 점수가_임계점을_초과하면_스탠드한다() { // given - Dealer dealer = Dealer.create(); + Dealer dealer = Dealer.createEmptyHand(); dealer.hit(new Card(Rank.SEVEN, Suit.CLOVER)); dealer.hit(new Card(Rank.TEN, Suit.CLOVER)); // when @@ -23,7 +23,7 @@ class DealerTest { @Test void 점수가_임계점_이하면_힛한다() { // given - Dealer dealer = Dealer.create(); + Dealer dealer = Dealer.createEmptyHand(); dealer.hit(new Card(Rank.FIVE, Suit.CLOVER)); dealer.hit(new Card(Rank.TEN, Suit.CLOVER)); // when diff --git a/src/test/java/blackjack/domain/participant/NameTest.java b/src/test/java/blackjack/domain/participants/NameTest.java similarity index 95% rename from src/test/java/blackjack/domain/participant/NameTest.java rename to src/test/java/blackjack/domain/participants/NameTest.java index 774fd8a1d8..b66cbd9e62 100644 --- a/src/test/java/blackjack/domain/participant/NameTest.java +++ b/src/test/java/blackjack/domain/participants/NameTest.java @@ -1,4 +1,4 @@ -package blackjack.domain.participant; +package blackjack.domain.participants; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; @@ -6,7 +6,6 @@ import org.junit.jupiter.api.Test; class NameTest { - @Test void 공백을_제거한_이름을_반환한다() { // given diff --git a/src/test/java/blackjack/domain/participants/PlayerGroupTest.java b/src/test/java/blackjack/domain/participants/PlayerGroupTest.java new file mode 100644 index 0000000000..575de3832c --- /dev/null +++ b/src/test/java/blackjack/domain/participants/PlayerGroupTest.java @@ -0,0 +1,4 @@ +package blackjack.domain.participants; + +class PlayerGroupTest { +} diff --git a/src/test/java/blackjack/domain/participant/PlayerGroupTest.java b/src/test/java/blackjack/domain/participants/PlayerNamesTest.java similarity index 57% rename from src/test/java/blackjack/domain/participant/PlayerGroupTest.java rename to src/test/java/blackjack/domain/participants/PlayerNamesTest.java index 33949d3d59..0488d4c3e4 100644 --- a/src/test/java/blackjack/domain/participant/PlayerGroupTest.java +++ b/src/test/java/blackjack/domain/participants/PlayerNamesTest.java @@ -1,32 +1,28 @@ -package blackjack.domain.participant; +package blackjack.domain.participants; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -class PlayerGroupTest { - +class PlayerNamesTest { @Test void 구분자를_기준으로_전체_플레이어_리스트를_생성한다() { // given String rawPlayerNames = "pobi,jason"; // when - PlayerGroup playerGroup = PlayerGroup.from(rawPlayerNames); + PlayerNames names = PlayerNames.from(rawPlayerNames); // then - List actualPlayerNames = playerGroup.players().stream() - .map(Participant::getName) - .toList(); - assertThat(actualPlayerNames).contains("pobi", "jason"); + assertThat(names.names().size()).isEqualTo(2); + assertThat(names.names()).contains(new Name("pobi"), new Name("jason")); } @ParameterizedTest @ValueSource(strings = {"pobi,pobi", "pobi, pobi"}) void 플레이어_이름이_중복되면_에러를_던진다(String duplicatedRawPlayerNames) { - assertThatThrownBy(() -> PlayerGroup.from(duplicatedRawPlayerNames)) - .isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> PlayerNames.from(duplicatedRawPlayerNames)) + .isInstanceOf(IllegalArgumentException.class); } -} +} \ No newline at end of file diff --git a/src/test/java/blackjack/domain/participants/PlayerTest.java b/src/test/java/blackjack/domain/participants/PlayerTest.java new file mode 100644 index 0000000000..2c80e47bde --- /dev/null +++ b/src/test/java/blackjack/domain/participants/PlayerTest.java @@ -0,0 +1,20 @@ +package blackjack.domain.participants; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.Test; + +class PlayerTest { + private final Bet bet = new Bet(1000L); + + @Test + void 본인의_이름을_반환한다() { + // given + String playerNameValue = "player"; + Name playerName = new Name(playerNameValue); + // when + Player player = Player.createEmptyHand(playerName, bet); + // then + assertThat(player.getName()).isEqualTo(playerNameValue); + } +} diff --git a/src/test/java/blackjack/domain/participants/ProfitTest.java b/src/test/java/blackjack/domain/participants/ProfitTest.java new file mode 100644 index 0000000000..99a59c444e --- /dev/null +++ b/src/test/java/blackjack/domain/participants/ProfitTest.java @@ -0,0 +1,42 @@ +package blackjack.domain.participants; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class ProfitTest { + private static final long BASE_AMOUNT = 1000L; + private static final long BLACKJACK_PAYOUT_NUMERATOR = 3; + private static final long BLACKJACK_PAYOUT_DENOMINATOR = 2; + + @Test + void 수익_0원의_객체를_생성한다() { + // given + Profit profit = Profit.zero(); + // when & then + assertThat(profit.value()).isEqualTo(0L); + } + + @Test + void 수익을_음수로_변환한다() { + // given + Profit profit = new Profit(BASE_AMOUNT); + // when + Profit negativeProfit = profit.negative(); + // then + long expected = profit.value() * -1; + assertThat(negativeProfit.value()).isEqualTo(expected); + } + + @Test + void 수익의_1_5배를_변환한다() { + // given + Profit profit = new Profit(BASE_AMOUNT); + // when + Profit blackjackProfit = profit.applyBlackjackPayout(); + // then + long expected = profit.value() * BLACKJACK_PAYOUT_NUMERATOR / BLACKJACK_PAYOUT_DENOMINATOR; + assertThat(blackjackProfit.value()).isEqualTo(expected); + } +} \ No newline at end of file