diff --git a/README.md b/README.md index b958c3d6869..56f1b141c07 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,11 @@ 블랙잭 게임을 변형한 프로그램을 구현한다. 블랙잭 게임은 딜러와 플레이어 중 카드의 합이 21 또는 21에 가장 가까운 숫자를 가지는 쪽이 이기는 게임이다. +- 플레이어는 게임을 시작할 때 배팅 금액을 정해야 한다. - 카드의 숫자 계산은 카드 숫자를 기본으로 하며, 예외로 Ace는 1 또는 11로 계산할 수 있으며, King, Queen, Jack은 각각 10으로 계산한다. -- 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며, 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다. -- 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 추가로 받아야 하고, 17점 이상이면 추가로 받을 수 없다. +- 게임을 시작하면 플레이어는 두 장의 카드를 지급 받으며, 두 장의 카드 숫자를 합쳐 21을 초과하지 않으면서 21에 가깝게 만들면 이긴다. 21을 넘지 않을 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다. 단, 카드를 추가로 뽑아 21을 초과할 경우 배팅 금액을 모두 잃게 된다. +- 처음 두 장의 카드 합이 21일 경우 블랙잭이 되면 베팅 금액의 1.5 배를 딜러에게 받는다. 딜러와 플레이어가 모두 동시에 블랙잭인 경우 플레이어는 베팅한 금액을 돌려받는다. +- 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 추가로 받아야 하고, 17점 이상이면 추가로 받을 수 없다. 딜러가 21을 초과하면 그 시점까지 남아 있던 플레이어들은 가지고 있는 패에 상관 없이 승리해 베팅 금액을 받는다. - 게임을 완료한 후 각 플레이어별로 승패를 출력한다. ### 실행 결과 @@ -18,6 +20,12 @@ 게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리) pobi,jason +pobi의 배팅 금액은? +10000 + +jason의 배팅 금액은? +20000 + 딜러와 pobi, jason에게 2장을 나누었습니다. 딜러카드: 3다이아몬드 pobi카드: 2하트, 8스페이드 @@ -38,10 +46,10 @@ jason카드: 7클로버, K스페이드 pobi카드: 2하트, 8스페이드, A클로버 - 결과: 21 jason카드: 7클로버, K스페이드 - 결과: 17 -## 최종 승패 -딜러: 1승 1패 -pobi: 승 -jason: 패 +## 최종 수익 +딜러: 10000 +pobi: 10000 +jason: -20000 ``` ## 프로그래밍 요구 사항 @@ -64,8 +72,6 @@ jason: 패 - 모든 원시 값과 문자열을 포장한다. - 줄여 쓰지 않는다(축약 금지). - 일급 컬렉션을 쓴다. - -### 추가된 요구 사항 - 모든 엔티티를 작게 유지한다. - 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다. - 딜러와 플레이어에서 발생하는 중복 코드를 제거해야 한다. @@ -77,18 +83,21 @@ jason: 패 - **1. 플레이어 이름 입력** - **1-1.** 플레이어 이름 유효성 검사 - **1-2.** 플레이어 인원수 유효성 검사 -- **2. 모든 참가자에게 카드 분배 안내 메시지 출력** -- **3.** 모든 참가자에 대해 이름과 up 카드 출력 -- **4. 참가자 별로 추가 카드를 받을지 선택하는 입력** - - **4-1.** 대답 유효성 검사 - - **4-2.** 이름과 up 카드 출력 -- **5. 딜러는 카드 합계에 따라 다음 행동을 수행** - - **5-1.** 17 미만일 경우, 카드를 더 받는 행동을 반복 - - **5-2.** 17 이상일 경우, 카드를 받지 않음 -- **6. 모든 참가자에 대해 모든 카드와 점수 결과 출력** -- **7. 최종 승패 출력** +- **2. 모든 참가자에게 배팅 금액 입력 받기** + - **2-1.** 배팅 금액 유효성 검사 +- **3. 모든 참가자에게 카드 분배 안내 메시지 출력** +- **4.** 모든 참가자에 대해 이름과 up 카드 출력 +- **5. 참가자 별로 추가 카드를 받을지 선택하는 입력** + - **5-1.** 대답 유효성 검사 + - **5-2.** 이름과 up 카드 출력 +- **6. 딜러는 카드 합계에 따라 다음 행동을 수행** + - **6-1.** 17 미만일 경우, 카드를 더 받는 행동을 반복 + - **6-2.** 17 이상일 경우, 카드를 받지 않음 +- **7. 모든 참가자에 대해 모든 카드와 점수 결과 출력** +- **8. 최종 수익 출력** ### 정상 입력 - 플레이어 이름은 쉼표(`,`)로 구분해 입력한다. - 플레이어 이름은 4~10자로 입력한다. +- 배팅 금액은 양의 정수로 입력한다. - 추가 카드 선택은 y 혹은 n으로 입력한다. \ No newline at end of file diff --git a/src/main/java/blackjack/Application.java b/src/main/java/blackjack/Application.java index 57549711f32..aae6267e33a 100644 --- a/src/main/java/blackjack/Application.java +++ b/src/main/java/blackjack/Application.java @@ -1,7 +1,7 @@ package blackjack; import blackjack.controller.BlackjackController; -import blackjack.domain.RandomShuffleStrategy; +import blackjack.domain.strategy.RandomShuffleStrategy; import blackjack.strategy.ShuffleStrategy; public class Application { diff --git a/src/main/java/blackjack/controller/BlackjackController.java b/src/main/java/blackjack/controller/BlackjackController.java index 445e82bfcdd..1b501c869e9 100644 --- a/src/main/java/blackjack/controller/BlackjackController.java +++ b/src/main/java/blackjack/controller/BlackjackController.java @@ -1,21 +1,26 @@ package blackjack.controller; -import blackjack.domain.Answer; -import blackjack.domain.Dealer; -import blackjack.domain.Hand; -import blackjack.domain.Participants; -import blackjack.domain.Player; -import blackjack.domain.Players; -import blackjack.domain.Status; -import blackjack.domain.Trump; -import blackjack.dto.FinalResultDto; +import blackjack.domain.judgement.Answer; +import blackjack.domain.judgement.BettingMoney; +import blackjack.domain.judgement.BettingMoneyInfo; +import blackjack.domain.participant.Dealer; +import blackjack.domain.card.Hand; +import blackjack.domain.participant.Name; +import blackjack.domain.participant.Participants; +import blackjack.domain.participant.Player; +import blackjack.domain.participant.Players; +import blackjack.domain.judgement.Status; +import blackjack.domain.card.Trump; +import blackjack.dto.FinalProfitDto; import blackjack.strategy.ShuffleStrategy; import blackjack.utils.Parser; import blackjack.utils.RetryExecutor; import blackjack.view.InputView; import blackjack.view.OutputView; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class BlackjackController { @@ -30,13 +35,15 @@ public void run() { Players players = RetryExecutor.retry(this::readPlayers); Participants participants = new Participants(players, dealer); + BettingMoneyInfo bettingMoneyInfo = readBettingMoney(players); + dealer.pitch(players.all()); OutputView.printStartMessage(players.all(), dealer); - + players.all().forEach(this::handleBlackjack); players.all().forEach(player -> handlePlayerAction(player, dealer)); handleDealerAction(dealer); - printResult(participants, players, dealer); + printResult(participants, bettingMoneyInfo); } private Dealer readyGame() { @@ -45,10 +52,25 @@ private Dealer readyGame() { return new Dealer(emptyHand, Status.HIT, trump); } - private void printResult(Participants participants, Players players, Dealer dealer) { + private BettingMoneyInfo readBettingMoney(Players players) { + Map bettingMoneyByPlayer = new HashMap<>(); + players.all().forEach(player -> { + BettingMoney bettingMoney = RetryExecutor.retry(this::readBettingMoneyByPlayer, player); + bettingMoneyByPlayer.put(player.getName(), bettingMoney); + }); + + return new BettingMoneyInfo(bettingMoneyByPlayer); + } + + private BettingMoney readBettingMoneyByPlayer(Player player) { + String rawBettingMoney = InputView.readBettingMoney(player.getName().toString()); + return new BettingMoney(rawBettingMoney); + } + + private void printResult(Participants participants, BettingMoneyInfo bettingMoneyInfo) { OutputView.printFinalStatus(participants); - FinalResultDto finalResultDto = FinalResultDto.of(players.all(), dealer); - OutputView.printFinalResult(finalResultDto); + FinalProfitDto finalProfitDto = FinalProfitDto.of(participants, bettingMoneyInfo); + OutputView.printProfit(finalProfitDto); } private void handleDealerAction(Dealer dealer) { @@ -61,10 +83,14 @@ private void handleDealerAction(Dealer dealer) { dealer.handleBurst(); } + private void handleBlackjack(Player player) { + player.handleBlackjack(); + } + private void handlePlayerAction(Player player, Dealer dealer) { while (player.isHit()) { Answer answer = - RetryExecutor.retry(this::readAnswer, player.getNickname()); + RetryExecutor.retry(this::readAnswer, player.getName().toString()); handleAnswer(player, dealer, answer); OutputView.printCardStatus(player); } diff --git a/src/main/java/blackjack/domain/Player.java b/src/main/java/blackjack/domain/Player.java deleted file mode 100644 index 18b9c428f01..00000000000 --- a/src/main/java/blackjack/domain/Player.java +++ /dev/null @@ -1,42 +0,0 @@ -package blackjack.domain; - -import java.util.List; - -public class Player extends Participant { - private static final int NICKNAME_MINIMUM_LENGTH = 4; - private static final int NICKNAME_MAXIMUM_LENGTH = 10; - - private final String nickname; - - - public Player(final Hand hand, final Status status, final String nickname) { - super(hand, status); - validateNicknameLength(nickname); - this.nickname = nickname; - } - - private void validateNicknameLength(final String nickname) { - if (nickname.length() > NICKNAME_MAXIMUM_LENGTH || nickname.length() < NICKNAME_MINIMUM_LENGTH) { - throw new IllegalArgumentException("닉네임은 4~10자 이어야 합니다."); - } - } - - public GameResult calculateGameResult(Dealer dealer) { - if (isBurst()) { - return GameResult.LOSE; - } - if (dealer.isBurst() || dealer.getScore() < getScore()) { - return GameResult.WIN; - } - if (dealer.getScore() == getScore()) { - return GameResult.DRAW; - } - return GameResult.LOSE; - } - - @Override - public String getNickname() { - return nickname; - } - -} diff --git a/src/main/java/blackjack/domain/Card.java b/src/main/java/blackjack/domain/card/Card.java similarity index 94% rename from src/main/java/blackjack/domain/Card.java rename to src/main/java/blackjack/domain/card/Card.java index 7ec3e6189aa..b748ac8056d 100644 --- a/src/main/java/blackjack/domain/Card.java +++ b/src/main/java/blackjack/domain/card/Card.java @@ -1,4 +1,4 @@ -package blackjack.domain; +package blackjack.domain.card; public class Card { diff --git a/src/main/java/blackjack/domain/Denomination.java b/src/main/java/blackjack/domain/card/Denomination.java similarity index 97% rename from src/main/java/blackjack/domain/Denomination.java rename to src/main/java/blackjack/domain/card/Denomination.java index 141f68acea4..a4d2a472106 100644 --- a/src/main/java/blackjack/domain/Denomination.java +++ b/src/main/java/blackjack/domain/card/Denomination.java @@ -1,4 +1,4 @@ -package blackjack.domain; +package blackjack.domain.card; import java.util.List; import java.util.Objects; diff --git a/src/main/java/blackjack/domain/Hand.java b/src/main/java/blackjack/domain/card/Hand.java similarity index 89% rename from src/main/java/blackjack/domain/Hand.java rename to src/main/java/blackjack/domain/card/Hand.java index b31ce11553d..d6215f025c1 100644 --- a/src/main/java/blackjack/domain/Hand.java +++ b/src/main/java/blackjack/domain/card/Hand.java @@ -1,6 +1,5 @@ -package blackjack.domain; +package blackjack.domain.card; -import java.util.ArrayList; import java.util.List; public class Hand { @@ -40,6 +39,10 @@ public List getCardNames(int startInclusive) { .toList(); } + public boolean isBlackjack() { + return cards.size() == 2 && calculateScore() == BURST_THRESHOLD; + } + private int handleAce(int totalScore) { long aceCount = countAce(); int updatedScore = totalScore; diff --git a/src/main/java/blackjack/domain/Suit.java b/src/main/java/blackjack/domain/card/Suit.java similarity index 92% rename from src/main/java/blackjack/domain/Suit.java rename to src/main/java/blackjack/domain/card/Suit.java index f039eb30f03..68595ceb8ba 100644 --- a/src/main/java/blackjack/domain/Suit.java +++ b/src/main/java/blackjack/domain/card/Suit.java @@ -1,4 +1,4 @@ -package blackjack.domain; +package blackjack.domain.card; import java.util.List; diff --git a/src/main/java/blackjack/domain/Trump.java b/src/main/java/blackjack/domain/card/Trump.java similarity index 96% rename from src/main/java/blackjack/domain/Trump.java rename to src/main/java/blackjack/domain/card/Trump.java index 727d49c7d97..3964145e00d 100644 --- a/src/main/java/blackjack/domain/Trump.java +++ b/src/main/java/blackjack/domain/card/Trump.java @@ -1,4 +1,4 @@ -package blackjack.domain; +package blackjack.domain.card; import blackjack.strategy.ShuffleStrategy; import java.util.ArrayList; diff --git a/src/main/java/blackjack/domain/Answer.java b/src/main/java/blackjack/domain/judgement/Answer.java similarity index 93% rename from src/main/java/blackjack/domain/Answer.java rename to src/main/java/blackjack/domain/judgement/Answer.java index 1f35cd44505..bc6a3ee6171 100644 --- a/src/main/java/blackjack/domain/Answer.java +++ b/src/main/java/blackjack/domain/judgement/Answer.java @@ -1,4 +1,4 @@ -package blackjack.domain; +package blackjack.domain.judgement; import java.util.List; import java.util.Objects; diff --git a/src/main/java/blackjack/domain/judgement/BettingMoney.java b/src/main/java/blackjack/domain/judgement/BettingMoney.java new file mode 100644 index 00000000000..c2e1964c93c --- /dev/null +++ b/src/main/java/blackjack/domain/judgement/BettingMoney.java @@ -0,0 +1,35 @@ +package blackjack.domain.judgement; + +public class BettingMoney { + + private final Integer money; + + public BettingMoney(String rawBettingMoney) { + int money = parseInt(rawBettingMoney); + validatePositive(money); + this.money = money; + } + + @Override + public String toString() { + return String.valueOf(money); + } + + private int parseInt(String input) { + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("배팅 금액은 정수를 입력해 주세요."); + } + } + + private void validatePositive(Integer money) { + if (money < 0) { + throw new IllegalArgumentException("배팅 금액은 음수일 수 없습니다."); + } + } + + public double multiply(double dividendRate) { + return money * dividendRate; + } +} diff --git a/src/main/java/blackjack/domain/judgement/BettingMoneyInfo.java b/src/main/java/blackjack/domain/judgement/BettingMoneyInfo.java new file mode 100644 index 00000000000..d57c87d99c7 --- /dev/null +++ b/src/main/java/blackjack/domain/judgement/BettingMoneyInfo.java @@ -0,0 +1,19 @@ +package blackjack.domain.judgement; + +import blackjack.domain.participant.Name; +import java.util.Map; + +public class BettingMoneyInfo { + + private final Map bettingMoneyInfo; + + public BettingMoneyInfo(Map bettingMoneyInfo) { + this.bettingMoneyInfo = bettingMoneyInfo; + } + + public BettingMoney findMoneyByName(Name nickname) { + return bettingMoneyInfo.get(nickname); + } + + +} diff --git a/src/main/java/blackjack/domain/DealerResult.java b/src/main/java/blackjack/domain/judgement/DealerResult.java similarity index 77% rename from src/main/java/blackjack/domain/DealerResult.java rename to src/main/java/blackjack/domain/judgement/DealerResult.java index a122fa7ae5a..f0490c4c2ff 100644 --- a/src/main/java/blackjack/domain/DealerResult.java +++ b/src/main/java/blackjack/domain/judgement/DealerResult.java @@ -1,4 +1,4 @@ -package blackjack.domain; +package blackjack.domain.judgement; public record DealerResult( long dealerWinCount, diff --git a/src/main/java/blackjack/domain/GameResult.java b/src/main/java/blackjack/domain/judgement/GameResult.java similarity index 50% rename from src/main/java/blackjack/domain/GameResult.java rename to src/main/java/blackjack/domain/judgement/GameResult.java index ba1a2034a10..819f858a4e6 100644 --- a/src/main/java/blackjack/domain/GameResult.java +++ b/src/main/java/blackjack/domain/judgement/GameResult.java @@ -1,18 +1,21 @@ -package blackjack.domain; +package blackjack.domain.judgement; import java.util.List; import java.util.Objects; public enum GameResult { - WIN("승"), - DRAW("무"), - LOSE("패"); + BLACKJACK("블랙잭", 1.5), + WIN("승", 1.0), + DRAW("무", 0), + LOSE("패", -1.0); private final String name; + private final double dividendRate; - GameResult(String name) { + GameResult(String name, double dividendRate) { this.name = name; + this.dividendRate = dividendRate; } public String getName() { @@ -26,6 +29,11 @@ private static List all() { public static GameResult pick(String name) { return all().stream() .filter(gameResult -> Objects.equals(name, gameResult.name)) - .findFirst().orElse(DRAW); + .findFirst() + .orElse(DRAW); + } + + public double calculateProfit(BettingMoney bettingMoney) { + return bettingMoney.multiply(dividendRate); } } diff --git a/src/main/java/blackjack/domain/Status.java b/src/main/java/blackjack/domain/judgement/Status.java similarity index 83% rename from src/main/java/blackjack/domain/Status.java rename to src/main/java/blackjack/domain/judgement/Status.java index c88d69aa0a1..0408620df86 100644 --- a/src/main/java/blackjack/domain/Status.java +++ b/src/main/java/blackjack/domain/judgement/Status.java @@ -1,4 +1,4 @@ -package blackjack.domain; +package blackjack.domain.judgement; public enum Status { diff --git a/src/main/java/blackjack/domain/Dealer.java b/src/main/java/blackjack/domain/participant/Dealer.java similarity index 63% rename from src/main/java/blackjack/domain/Dealer.java rename to src/main/java/blackjack/domain/participant/Dealer.java index c665f629da6..32de9c42b17 100644 --- a/src/main/java/blackjack/domain/Dealer.java +++ b/src/main/java/blackjack/domain/participant/Dealer.java @@ -1,18 +1,23 @@ -package blackjack.domain; +package blackjack.domain.participant; +import blackjack.domain.card.Card; +import blackjack.domain.card.Hand; +import blackjack.domain.judgement.Status; +import blackjack.domain.card.Trump; import java.util.List; import java.util.stream.IntStream; public class Dealer extends Participant { public static final int DEALER_HIT_THRESHOLD = 16; - public static final String DEALER_NICKNAME = "딜러"; private final Trump trump; + private final Name nickname; public Dealer(final Hand hand, final Status status, final Trump trump) { super(hand, status); this.trump = trump; + nickname = new DealerName(); } public void giveCard(final Player player) { @@ -28,14 +33,14 @@ public void giveCard() { public void pitch(final List players) { final int distributeCount = 2; IntStream.range(0, distributeCount) - .forEach(round -> { - players.forEach(this::giveCard); - giveCard(); - }); + .forEach(round -> { + players.forEach(this::giveCard); + giveCard(); + }); } public void decideHit() { - int totalScore = hand.calculateScore(); + int totalScore = getScore(); if (totalScore > DEALER_HIT_THRESHOLD) { stay(); } @@ -43,11 +48,11 @@ public void decideHit() { public List getOpenCardNames() { final int holeIndex = 1; - return hand.getCardNames(holeIndex); + return getCardNames(holeIndex); } @Override - public String getNickname() { - return DEALER_NICKNAME; + public Name getName() { + return nickname; } } diff --git a/src/main/java/blackjack/domain/participant/DealerName.java b/src/main/java/blackjack/domain/participant/DealerName.java new file mode 100644 index 00000000000..e617c73182e --- /dev/null +++ b/src/main/java/blackjack/domain/participant/DealerName.java @@ -0,0 +1,11 @@ +package blackjack.domain.participant; + +public class DealerName implements Name { + + public static final String DEALER_NICKNAME = "딜러"; + + @Override + public String toString() { + return DEALER_NICKNAME; + } +} diff --git a/src/main/java/blackjack/domain/participant/Name.java b/src/main/java/blackjack/domain/participant/Name.java new file mode 100644 index 00000000000..49e583774fb --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Name.java @@ -0,0 +1,5 @@ +package blackjack.domain.participant; + +public interface Name { + String toString(); +} diff --git a/src/main/java/blackjack/domain/participant/Nickname.java b/src/main/java/blackjack/domain/participant/Nickname.java new file mode 100644 index 00000000000..9f2d936f578 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Nickname.java @@ -0,0 +1,24 @@ +package blackjack.domain.participant; + +public class Nickname implements Name{ + private static final int NICKNAME_MINIMUM_LENGTH = 4; + private static final int NICKNAME_MAXIMUM_LENGTH = 10; + + private final String nickname; + + public Nickname(String nickname) { + validateNicknameLength(nickname); + this.nickname = nickname; + } + + private void validateNicknameLength(final String nickname) { + if (nickname.length() > NICKNAME_MAXIMUM_LENGTH || nickname.length() < NICKNAME_MINIMUM_LENGTH) { + throw new IllegalArgumentException("닉네임은 4~10자 이어야 합니다."); + } + } + + @Override + public String toString() { + return nickname; + } +} diff --git a/src/main/java/blackjack/domain/Participant.java b/src/main/java/blackjack/domain/participant/Participant.java similarity index 62% rename from src/main/java/blackjack/domain/Participant.java rename to src/main/java/blackjack/domain/participant/Participant.java index 5fdb8574f94..c8e1f6ca46c 100644 --- a/src/main/java/blackjack/domain/Participant.java +++ b/src/main/java/blackjack/domain/participant/Participant.java @@ -1,10 +1,13 @@ -package blackjack.domain; +package blackjack.domain.participant; +import blackjack.domain.card.Card; +import blackjack.domain.card.Hand; +import blackjack.domain.judgement.Status; import java.util.List; public abstract class Participant { - protected final Hand hand; + private final Hand hand; private Status status; public Participant(final Hand hand, final Status status) { @@ -32,6 +35,10 @@ public boolean isBurst() { return status == Status.BURST; } + public boolean isBlackjack() { + return hand.isBlackjack(); + } + public void stay() { if (status == Status.HIT) { status = Status.STAY; @@ -44,9 +51,19 @@ public void handleBurst() { } } + public void handleBlackjack() { + if (isBlackjack()) { + status = Status.STAY; + } + } + public List getCardNames() { return hand.getCardNames(0); } - public abstract String getNickname(); + protected List getCardNames(int startInclusive) { + return hand.getCardNames(startInclusive); + } + + public abstract Name getName(); } diff --git a/src/main/java/blackjack/domain/Participants.java b/src/main/java/blackjack/domain/participant/Participants.java similarity index 50% rename from src/main/java/blackjack/domain/Participants.java rename to src/main/java/blackjack/domain/participant/Participants.java index 2c5c0287700..71b14e48f49 100644 --- a/src/main/java/blackjack/domain/Participants.java +++ b/src/main/java/blackjack/domain/participant/Participants.java @@ -1,18 +1,12 @@ -package blackjack.domain; +package blackjack.domain.participant; import java.util.ArrayList; import java.util.List; -public class Participants { - - private final Players players; - private final Dealer dealer; - - public Participants(Players players, Dealer dealer) { - this.players = players; - this.dealer = dealer; - } - +public record Participants( + Players players, + Dealer dealer +) { public List all() { final List participants = new ArrayList<>(List.of(dealer)); participants.addAll(players.all()); 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..05d23d5e4a9 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Player.java @@ -0,0 +1,46 @@ +package blackjack.domain.participant; + +import blackjack.domain.judgement.GameResult; +import blackjack.domain.card.Hand; +import blackjack.domain.judgement.Status; + +public class Player extends Participant { + + private final Nickname nickname; + + public Player(final Hand hand, final Status status, final String nickname) { + super(hand, status); + this.nickname = new Nickname(nickname); + } + + public GameResult calculateGameResult(Dealer dealer) { + if (isBlackjack() && dealer.isBlackjack()) { + return GameResult.DRAW; + } + if (isBlackjack()) { + return GameResult.BLACKJACK; + } + if (dealer.isBlackjack()) { + return GameResult.LOSE; + } + return calculateScoreResult(dealer); + } + + private GameResult calculateScoreResult(Dealer dealer) { + if (isBurst()) { + return GameResult.LOSE; + } + if (dealer.isBurst() || dealer.getScore() < getScore()) { + return GameResult.WIN; + } + if (dealer.getScore() == getScore()) { + return GameResult.DRAW; + } + return GameResult.LOSE; + } + + @Override + public Name getName() { + return nickname; + } +} diff --git a/src/main/java/blackjack/domain/Players.java b/src/main/java/blackjack/domain/participant/Players.java similarity index 95% rename from src/main/java/blackjack/domain/Players.java rename to src/main/java/blackjack/domain/participant/Players.java index 1e4176e1a6c..a7b73333517 100644 --- a/src/main/java/blackjack/domain/Players.java +++ b/src/main/java/blackjack/domain/participant/Players.java @@ -1,4 +1,4 @@ -package blackjack.domain; +package blackjack.domain.participant; import java.util.Collections; import java.util.List; diff --git a/src/main/java/blackjack/domain/RandomShuffleStrategy.java b/src/main/java/blackjack/domain/strategy/RandomShuffleStrategy.java similarity index 78% rename from src/main/java/blackjack/domain/RandomShuffleStrategy.java rename to src/main/java/blackjack/domain/strategy/RandomShuffleStrategy.java index 69b3957518c..373938901bb 100644 --- a/src/main/java/blackjack/domain/RandomShuffleStrategy.java +++ b/src/main/java/blackjack/domain/strategy/RandomShuffleStrategy.java @@ -1,5 +1,6 @@ -package blackjack.domain; +package blackjack.domain.strategy; +import blackjack.domain.card.Card; import blackjack.strategy.ShuffleStrategy; import java.util.Collections; import java.util.List; diff --git a/src/main/java/blackjack/dto/FinalProfitDto.java b/src/main/java/blackjack/dto/FinalProfitDto.java new file mode 100644 index 00000000000..5d1cd526ea0 --- /dev/null +++ b/src/main/java/blackjack/dto/FinalProfitDto.java @@ -0,0 +1,45 @@ +package blackjack.dto; + +import blackjack.domain.judgement.BettingMoneyInfo; +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Name; +import blackjack.domain.participant.Participants; + +import blackjack.domain.participant.Player; +import blackjack.domain.participant.Players; +import java.util.Map; +import java.util.stream.Collectors; + +public record FinalProfitDto( + Map bettingMoneyInfo, + double profitByDealer +) { + + public static FinalProfitDto of(Participants participants, BettingMoneyInfo bettingMoneyInfo) { + Map profitByPlayer = calculatePlayerProfit(participants, bettingMoneyInfo); + + double profitByDealer = calculateDealerProfit(profitByPlayer); + + return new FinalProfitDto(profitByPlayer, profitByDealer); + } + + private static Map calculatePlayerProfit(Participants participants, BettingMoneyInfo bettingMoneyInfo) { + Players players = participants.players(); + Dealer dealer = participants.dealer(); + return players.all().stream() + .collect(Collectors.toUnmodifiableMap( + Player::getName, + player -> player.calculateGameResult(dealer) + .calculateProfit(bettingMoneyInfo.findMoneyByName(player.getName())) + ) + ); + } + + private static double calculateDealerProfit(Map profitByPlayer) { + return profitByPlayer.values().stream() + .mapToDouble(money -> -money) + .sum(); + } + + +} diff --git a/src/main/java/blackjack/dto/FinalResultDto.java b/src/main/java/blackjack/dto/FinalResultDto.java index cb49b12f920..6854afea96b 100644 --- a/src/main/java/blackjack/dto/FinalResultDto.java +++ b/src/main/java/blackjack/dto/FinalResultDto.java @@ -1,13 +1,12 @@ package blackjack.dto; -import blackjack.domain.Dealer; -import blackjack.domain.DealerResult; -import blackjack.domain.GameResult; -import blackjack.domain.Player; +import blackjack.domain.participant.Dealer; +import blackjack.domain.judgement.DealerResult; +import blackjack.domain.judgement.GameResult; +import blackjack.domain.participant.Players; import java.util.Collection; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; public record FinalResultDto( @@ -15,10 +14,10 @@ public record FinalResultDto( DealerResult dealerResult ) { - public static FinalResultDto of(List players, Dealer dealer) { + public static FinalResultDto of(Players players, Dealer dealer) { Map playerGameResultMap = new LinkedHashMap<>(); - players.forEach(player -> - playerGameResultMap.put(player.getNickname(), player.calculateGameResult(dealer))); + players.all().forEach(player -> + playerGameResultMap.put(player.getName().toString(), player.calculateGameResult(dealer))); long dealerWinCount = countByGameResult(playerGameResultMap.values(), GameResult.LOSE); long dealerDrawCount = countByGameResult(playerGameResultMap.values(), GameResult.DRAW); long dealerLoseCount = countByGameResult(playerGameResultMap.values(), GameResult.WIN); diff --git a/src/main/java/blackjack/strategy/ShuffleStrategy.java b/src/main/java/blackjack/strategy/ShuffleStrategy.java index d8fca4a5535..be0becbca73 100644 --- a/src/main/java/blackjack/strategy/ShuffleStrategy.java +++ b/src/main/java/blackjack/strategy/ShuffleStrategy.java @@ -1,6 +1,6 @@ package blackjack.strategy; -import blackjack.domain.Card; +import blackjack.domain.card.Card; import java.util.List; @FunctionalInterface diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java index 4c09550d0ac..d91a7ba852c 100644 --- a/src/main/java/blackjack/view/InputView.java +++ b/src/main/java/blackjack/view/InputView.java @@ -15,4 +15,9 @@ public static String readAnswer(String nickname) { return Console.readLine(); } + public static String readBettingMoney(String nickname) { + System.out.println(nickname + "의 배팅 금액은?"); + return Console.readLine(); + } + } diff --git a/src/main/java/blackjack/view/OutputView.java b/src/main/java/blackjack/view/OutputView.java index 7b466ed7db0..0648e3c7960 100644 --- a/src/main/java/blackjack/view/OutputView.java +++ b/src/main/java/blackjack/view/OutputView.java @@ -1,16 +1,16 @@ package blackjack.view; -import static blackjack.domain.Dealer.DEALER_HIT_THRESHOLD; - -import blackjack.domain.Dealer; -import blackjack.domain.DealerResult; -import blackjack.domain.Participant; -import blackjack.domain.Participants; -import blackjack.domain.Player; +import static blackjack.domain.participant.Dealer.DEALER_HIT_THRESHOLD; + +import blackjack.domain.participant.Dealer; +import blackjack.domain.judgement.DealerResult; +import blackjack.domain.participant.Name; +import blackjack.domain.participant.Participant; +import blackjack.domain.participant.Participants; +import blackjack.domain.participant.Player; +import blackjack.dto.FinalProfitDto; import blackjack.dto.FinalResultDto; -import java.io.FilterOutputStream; import java.util.List; -import java.util.Map; public class OutputView { @@ -23,7 +23,8 @@ public static void printErrorMessage(final String message) { public static void printStartMessage(final List players, final Dealer dealer) { List playerNicknames = players.stream() - .map(Player::getNickname) + .map(Player::getName) + .map(Name::toString) .toList(); System.out.printf("딜러와 %s에게 %s장을 나누었습니다.\n", @@ -62,9 +63,17 @@ public static void printFinalResult(FinalResultDto dto) { System.out.printf("%s: %s\n", key, value.getName())); } + public static void printProfit(FinalProfitDto dto) { + System.out.println("## 최종 수익"); + System.out.printf("딜러: %.1f\n", dto.profitByDealer()); + dto.bettingMoneyInfo() + .forEach((key, value) -> + System.out.printf("%s: %s\n", key, value)); + } + private static String participantHandFormat(final Participant participant) { return String.format("%s카드: %s", - participant.getNickname(), + participant.getName(), String.join(", ", participant.getCardNames()) ); } diff --git a/src/test/java/domain/BettingMoneyTest.java b/src/test/java/domain/BettingMoneyTest.java new file mode 100644 index 00000000000..43d508a16af --- /dev/null +++ b/src/test/java/domain/BettingMoneyTest.java @@ -0,0 +1,31 @@ +package domain; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import blackjack.domain.judgement.BettingMoney; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class BettingMoneyTest { + + @Test + @DisplayName("베팅 금액 정상 테스트") + void 베팅_금액_정상_테스트() { + assertDoesNotThrow(() -> new BettingMoney("10000")); + } + + @Test + @DisplayName("배팅 금액 오류 테스트: 배팅 금액이 음수인 경우") + void 배팅_금액이_음수인_경우() { + assertThatThrownBy(() -> new BettingMoney("-100")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("배팅 금액 오류 테스트: 배팅 금액이 정수가 아닌 경우") + void 배팅_금액이_정수가_아닌_경우() { + assertThatThrownBy(() -> new BettingMoney("10.0")) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/domain/DealerTest.java b/src/test/java/domain/DealerTest.java index 56cddb5b69e..0b1d92c572c 100644 --- a/src/test/java/domain/DealerTest.java +++ b/src/test/java/domain/DealerTest.java @@ -2,14 +2,14 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import blackjack.domain.Card; -import blackjack.domain.Dealer; -import blackjack.domain.Denomination; -import blackjack.domain.Hand; -import blackjack.domain.Player; -import blackjack.domain.Status; -import blackjack.domain.Suit; -import blackjack.domain.Trump; +import blackjack.domain.card.Card; +import blackjack.domain.participant.Dealer; +import blackjack.domain.card.Denomination; +import blackjack.domain.card.Hand; +import blackjack.domain.participant.Player; +import blackjack.domain.judgement.Status; +import blackjack.domain.card.Suit; +import blackjack.domain.card.Trump; import blackjack.strategy.ShuffleStrategy; import java.util.ArrayList; import java.util.List; diff --git a/src/test/java/domain/DenominationTest.java b/src/test/java/domain/DenominationTest.java index ab35fe4e400..756b466fa05 100644 --- a/src/test/java/domain/DenominationTest.java +++ b/src/test/java/domain/DenominationTest.java @@ -2,7 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import blackjack.domain.Denomination; +import blackjack.domain.card.Denomination; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; diff --git a/src/test/java/domain/GameResultTest.java b/src/test/java/domain/GameResultTest.java new file mode 100644 index 00000000000..45f1fae1c51 --- /dev/null +++ b/src/test/java/domain/GameResultTest.java @@ -0,0 +1,47 @@ +package domain; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import blackjack.domain.judgement.BettingMoney; +import blackjack.domain.judgement.GameResult; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class GameResultTest { + + @Test + @DisplayName("승리 시, 배팅 금액 1배 반환") + void 승리_처리_테스트() { + GameResult gameResult = GameResult.WIN; + BettingMoney bettingMoney = new BettingMoney("10000"); + double expected = 10000; + + double actual = gameResult.calculateProfit(bettingMoney); + + assertThat(actual).isEqualTo(expected); + } + + @Test + @DisplayName("패비 시, 배팅 금액 1배 반납") + void 패배_처리_테스트() { + GameResult gameResult = GameResult.LOSE; + BettingMoney bettingMoney = new BettingMoney("10000"); + double expected = -10000; + + double actual = gameResult.calculateProfit(bettingMoney); + + assertThat(actual).isEqualTo(expected); + } + + @Test + @DisplayName("블랙잭 시, 배팅 금액 1.5배 반환") + void 블랙잭_처리_테스트() { + GameResult gameResult = GameResult.BLACKJACK; + BettingMoney bettingMoney = new BettingMoney("10000"); + double expected = 15000; + + double actual = gameResult.calculateProfit(bettingMoney); + + assertThat(actual).isEqualTo(expected); + } +} diff --git a/src/test/java/domain/HandTest.java b/src/test/java/domain/HandTest.java index 9696b15a6bf..9d2a8b7667b 100644 --- a/src/test/java/domain/HandTest.java +++ b/src/test/java/domain/HandTest.java @@ -2,10 +2,10 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import blackjack.domain.Card; -import blackjack.domain.Denomination; -import blackjack.domain.Hand; -import blackjack.domain.Suit; +import blackjack.domain.card.Card; +import blackjack.domain.card.Denomination; +import blackjack.domain.card.Hand; +import blackjack.domain.card.Suit; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/domain/NicknameTest.java b/src/test/java/domain/NicknameTest.java new file mode 100644 index 00000000000..dea51cb7d77 --- /dev/null +++ b/src/test/java/domain/NicknameTest.java @@ -0,0 +1,31 @@ +package domain; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import blackjack.domain.participant.Nickname; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class NicknameTest { + + @Test + @DisplayName("닉네임 처리 테스트: 닉네임이 4~10자인 경우") + void 정상_테스트_1() { + assertDoesNotThrow(() -> new Nickname("pobi")); + } + + @Test + @DisplayName("닉네임 처리 테스트: 닉네임이 4자 미만인 경우") + void 예외_테스트_1() { + assertThatThrownBy(() -> new Nickname("pob")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("닉네임 처리 테스트: 닉네임이 10자 초과인 경우") + void 예외_테스트_2() { + assertThatThrownBy(() -> new Nickname("jasonjasonj")) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/domain/NoShuffleStrategy.java b/src/test/java/domain/NoShuffleStrategy.java index f40864630e2..ed5e2251638 100644 --- a/src/test/java/domain/NoShuffleStrategy.java +++ b/src/test/java/domain/NoShuffleStrategy.java @@ -1,6 +1,6 @@ package domain; -import blackjack.domain.Card; +import blackjack.domain.card.Card; import blackjack.strategy.ShuffleStrategy; import java.util.List; diff --git a/src/test/java/domain/PlayerTest.java b/src/test/java/domain/PlayerTest.java index 9878226c5cd..9342f4f3eef 100644 --- a/src/test/java/domain/PlayerTest.java +++ b/src/test/java/domain/PlayerTest.java @@ -4,17 +4,16 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import blackjack.domain.Card; -import blackjack.domain.Dealer; -import blackjack.domain.Denomination; -import blackjack.domain.GameResult; -import blackjack.domain.Hand; -import blackjack.domain.Player; -import blackjack.domain.Status; -import blackjack.domain.Suit; -import blackjack.domain.Trump; +import blackjack.domain.card.Card; +import blackjack.domain.participant.Dealer; +import blackjack.domain.card.Denomination; +import blackjack.domain.judgement.GameResult; +import blackjack.domain.card.Hand; +import blackjack.domain.participant.Player; +import blackjack.domain.judgement.Status; +import blackjack.domain.card.Suit; +import blackjack.domain.card.Trump; import blackjack.strategy.ShuffleStrategy; -import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -31,26 +30,6 @@ void setUp() { trump = new Trump(strategy); } - @Test - @DisplayName("닉네임 처리 테스트: 닉네임이 4~10자인 경우") - void 정상_테스트_1() { - assertDoesNotThrow(() -> new Player(new Hand(new ArrayList<>()), Status.HIT, "pobi")); - } - - @Test - @DisplayName("닉네임 처리 테스트: 닉네임이 4자 미만인 경우") - void 예외_테스트_1() { - assertThatThrownBy(() -> new Player(new Hand(new ArrayList<>()), Status.HIT, "pob")) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - @DisplayName("닉네임 처리 테스트: 닉네임이 10자 초과인 경우") - void 예외_테스트_2() { - assertThatThrownBy(() -> new Player(new Hand(new ArrayList<>()), Status.HIT, "jasonjasonj")) - .isInstanceOf(IllegalArgumentException.class); - } - @Nested @DisplayName("승패 판정 테스트") class 승패_판정_테스트 { @@ -166,5 +145,65 @@ class 승패_판정_테스트 { assertThat(actual).isEqualTo(expected); } + + @Test + @DisplayName("딜러 스테이, 플레이어 스테이: 둘 다 블랙잭인 경우") + void 딜러_스테이_플레이어_스테이_둘_다_블랙잭인_경우() { + Hand dealerHand = new Hand(List.of( + new Card(Suit.DIAMOND, Denomination.TEN), + new Card(Suit.SPADE, Denomination.ACE))); + Dealer dealer = new Dealer(dealerHand, Status.STAY, trump); + + Hand playerHand = new Hand(List.of( + new Card(Suit.SPADE, Denomination.TEN), + new Card(Suit.DIAMOND, Denomination.ACE))); + Player player = new Player(playerHand, Status.STAY, "pobi"); + + GameResult expected = GameResult.DRAW; + + GameResult actual = player.calculateGameResult(dealer); + + assertThat(actual).isEqualTo(expected); + } + + @Test + @DisplayName("딜러 스테이, 플레이어 스테이: 플레이어만 블랙잭인 경우") + void 딜러_스테이_플레이어_스테이_플레이어만_블랙잭인_경우() { + Hand dealerHand = new Hand(List.of( + new Card(Suit.DIAMOND, Denomination.TEN), + new Card(Suit.SPADE, Denomination.NINE))); + Dealer dealer = new Dealer(dealerHand, Status.STAY, trump); + + Hand playerHand = new Hand(List.of( + new Card(Suit.SPADE, Denomination.TEN), + new Card(Suit.DIAMOND, Denomination.ACE))); + Player player = new Player(playerHand, Status.STAY, "pobi"); + + GameResult expected = GameResult.BLACKJACK; + + GameResult actual = player.calculateGameResult(dealer); + + assertThat(actual).isEqualTo(expected); + } + + @Test + @DisplayName("딜러 스테이, 플레이어 스테이: 딜러만 블랙잭인 경우") + void 딜러_스테이_플레이어_스테이_딜러만_블랙잭인_경우() { + Hand dealerHand = new Hand(List.of( + new Card(Suit.DIAMOND, Denomination.TEN), + new Card(Suit.SPADE, Denomination.ACE))); + Dealer dealer = new Dealer(dealerHand, Status.STAY, trump); + + Hand playerHand = new Hand(List.of( + new Card(Suit.SPADE, Denomination.TEN), + new Card(Suit.DIAMOND, Denomination.NINE))); + Player player = new Player(playerHand, Status.STAY, "pobi"); + + GameResult expected = GameResult.LOSE; + + GameResult actual = player.calculateGameResult(dealer); + + assertThat(actual).isEqualTo(expected); + } } } diff --git a/src/test/java/domain/PlayersTest.java b/src/test/java/domain/PlayersTest.java index 77630f2fd0e..4b799462f4b 100644 --- a/src/test/java/domain/PlayersTest.java +++ b/src/test/java/domain/PlayersTest.java @@ -3,10 +3,10 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; -import blackjack.domain.Hand; -import blackjack.domain.Player; -import blackjack.domain.Players; -import blackjack.domain.Status; +import blackjack.domain.card.Hand; +import blackjack.domain.participant.Player; +import blackjack.domain.participant.Players; +import blackjack.domain.judgement.Status; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/domain/TrumpTest.java b/src/test/java/domain/TrumpTest.java index 53fd50a3ddb..ede2ebebe4d 100644 --- a/src/test/java/domain/TrumpTest.java +++ b/src/test/java/domain/TrumpTest.java @@ -2,15 +2,14 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import blackjack.domain.Card; -import blackjack.domain.Denomination; -import blackjack.domain.Suit; -import blackjack.domain.Trump; +import blackjack.domain.card.Card; +import blackjack.domain.card.Denomination; +import blackjack.domain.card.Suit; +import blackjack.domain.card.Trump; import blackjack.strategy.ShuffleStrategy; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; -import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test;