diff --git a/README.md b/README.md index c99e2c8de16..983ed1cf35c 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ ### 기능 요구사항 체크리스트 - [x] 게임에 참여할 사람의 이름을 쉼표(,) 기준으로 입력받아 분리한다. +- [x] 사용자로부터 베팅 금액을 입력받는다. - [x] 0부터 51 사이의 난수를 생성해 무늬와 숫자가 매핑된 중복 없는 카드를 발급한다. - [x] 게임 시작 시 딜러와 플레이어에게 각각 2장씩 카드를 분배한다. - [x] 딜러의 초기 카드는 1장만 보여주고, 플레이어는 보유한 모든 카드를 보여준다. @@ -12,23 +13,37 @@ - [x] 카드의 합을 계산할 때 K, Q, J는 10으로 계산하고, A는 11로 계산하고 21을 초과하면 1로 계산한다. - [x] 딜러는 처음에 받은 2장의 합계가 16 이하이면 무조건 1장의 카드를 추가로 받고, 17 이상이면 받지 않는다. - [x] 딜러와 플레이어의 턴이 모두 끝나면 각자의 최종 카드 목록과 점수를 출력한다. -- [x] 최종 점수를 비교해 딜러와 각 플레이어의 승/패/무 결과를 계산하고 출력한다. +- [x] 최종 점수와 딜러와 각 플레이어의 버스트/블랙잭/승패 결과를 통해 최종 수익을 계산해 출력한다. ### 주요 로직 요약 - **카드 생성** - - `Card`에서 `0~51` 범위의 난수를 뽑아 카드 번호를 만든다. + - `DeckFactory`에서 `0~51` 범위의 카드를 생성해 'Deck' 에서 shuffle 로 카드를 무작위로 섞는다. - **카드 이름/점수 매핑** - 카드 번호를 `shape(무늬)`와 `number(랭크)`로 분리해 문자열 카드명으로 변환한다. - 점수는 `A=11`, `2~10=숫자값`, `J/Q/K=10`으로 계산한다. - **중복 없는 카드 보장** - - `DuplicationSet`에 이미 나온 번호를 저장해, 중복 번호가 나오면 다시 뽑는다. + - `Deck`에서 카드를 뽑을 때마다 List의 첫 번째 요소를 제거하며 반환 - **버스트와 A 점수 보정** - 플레이어 점수가 21을 초과하면, 보유한 A 개수만큼 `11 -> 1` 보정(10점 차감)을 적용한다. - **게임 진행** - 시작 시 딜러/플레이어 모두 2장 배분 - 플레이어는 입력(`y/n`)으로 추가 카드 선택 - 딜러는 합계가 16 이하일 때 자동으로 추가 카드 드로우 -- **승패 계산** - - 버스트는 `-1` 처리 후 딜러와 각 플레이어 점수를 비교해 `승/패/무`를 기록한다. - \ No newline at end of file +- **수익 계산** + - 플레이어의 카드가 21을 초과할 경우 베팅 금액을 모두 잃게 된다 + - 처음 두 장의 카드 합이 21일 경우 블랙잭이 되면 베팅 금액의 1.5 배를 딜러에게 받는다. + - 딜러와 플레이어 모두 블랙잭인 경우 베팅 금액을 돌려받는다. + - 딜러가 21을 초과하면 남아 있던 플레이어들은 패에 상관 없이 베팅 금액을 받는다. + +### 최종 수익 계산 + +| 상황 | 결과 | 수익 배율 | 비고 | +|:-----------------------------|:----|:-----:|:------------------| +| **플레이어 버스트(Bust)** | 패배 | -1.0 | 베팅 금액 상실 | +| **플레이어 블랙잭 & 딜러 블랙잭 아님** | 승리 | +1.5 | 베팅 금액의 1.5배 수익 발생 | +| **플레이어 & 딜러 모두 블랙잭** | 무승부 | 0 | 베팅 금액 돌려받음 | +| **플레이어 21 이하 & 딜러 버스트** | 승리 | +1.0 | 베팅 금액만큼 수익 발생 | +| **둘 다 21 이하 (플레이어 점수 > 딜러)** | 승리 | +1.0 | 베팅 금액만큼 수익 발생 | +| **둘 다 21 이하 (플레이어 점수 < 딜러)** | 패배 | -1.0 | 베팅 금액 상실 | +| **둘 다 21 이하 (플레이어 점수 = 딜러)** | 무승부 | 0 | 베팅 금액 돌려받음 | diff --git a/src/main/java/controller/BlackJackGame.java b/src/main/java/controller/BlackJackGame.java index dad29ad70c3..de3ae5d7152 100644 --- a/src/main/java/controller/BlackJackGame.java +++ b/src/main/java/controller/BlackJackGame.java @@ -1,24 +1,20 @@ package controller; -import domain.Card; -import domain.Dealer; -import domain.Deck; -import domain.GameResult; -import domain.Outcome; -import domain.Participant; -import domain.Player; -import domain.Players; -import dto.AllOutcomeDto; +import domain.card.Deck; +import domain.card.DeckFactory; +import domain.participant.Dealer; +import domain.participant.Money; +import domain.participant.Player; +import domain.participant.Players; +import domain.result.Outcome; import dto.FinalScoreDto; import dto.InitialDto; +import dto.MoneyDTO; import dto.ParticipantDto; import dto.ParticipantScoreDto; import dto.PlayerOutcomeDto; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; import java.util.stream.IntStream; import util.NameParser; import view.InputView; @@ -34,46 +30,61 @@ public BlackJackGame(InputView inputView, ResultView resultView) { } public void run() { - Deck deck = createDeck(); + Deck deck = DeckFactory.create(); Dealer dealer = new Dealer(); - Players players = NameParser.makeNameList(inputView.getParticipant()); + Players players = setupPlayers(); + playGame(deck, dealer, players); + } + + private void playGame(Deck deck, Dealer dealer, Players players) { initialDraw(deck, dealer, players); printInitialStatus(dealer, players); - players.getPlayers().forEach(player -> drawPlayerTurn(deck, player)); + if (dealer.isFirstBlackJack()) { + printResults(dealer, players); + return; + } + players.players().forEach(player -> drawPlayerTurn(deck, player)); drawDealerTurn(deck, dealer); + printResults(dealer, players); + } + + private void printResults(Dealer dealer, Players players) { printFinalScore(dealer, players); - printFinalWinner(dealer, players); + printFinalProfit(dealer, players); } - private Deck createDeck() { - List cards = IntStream.range(0, 52) - .mapToObj(Card::new) - .collect(Collectors.toList()); - return new Deck(cards); + private Players setupPlayers() { + List playerNames = NameParser.makeNameList(inputView.getParticipant()); + List players = new ArrayList<>(); + for (String name : playerNames) { + String money = inputView.getMoney(name); + players.add(new Player(name, new Money(money))); + } + return new Players(players); } private void initialDraw(Deck deck, Dealer dealer, Players players) { IntStream.range(0, 2).forEach(i -> { dealer.drawCard(deck.draw()); - players.getPlayers().forEach(player -> player.drawCard(deck.draw())); + players.players().forEach(player -> player.drawCard(deck.draw())); }); } private void printInitialStatus(Dealer dealer, Players players) { - ParticipantDto dealerDto = new ParticipantDto(dealer.getName(), getCardNames(dealer)); - List participantDtos = players.getPlayers().stream() - .map(player -> new ParticipantDto(player.getName(), getCardNames(player))) - .collect(Collectors.toList()); - String joinedNames = players.getPlayers().stream() - .map(Player::getName) - .collect(Collectors.joining(", ")); - resultView.printGameStart(new InitialDto(joinedNames, dealerDto, participantDtos)); + ParticipantDto dealerDto = ParticipantDto.from(dealer); + List participants = + players.players() + .stream() + .map(ParticipantDto::from) + .toList(); + String joinedNames = players.getJoinedNames(); + resultView.printGameStart(new InitialDto(joinedNames, dealerDto, participants)); } private void drawPlayerTurn(Deck deck, Player player) { while (player.canDraw() && inputView.getMoreCards(player.getName()).equals("y")) { player.drawCard(deck.draw()); - resultView.printParticipantMoreCard(new ParticipantDto(player.getName(), getCardNames(player))); + resultView.printParticipantMoreCard(ParticipantDto.from(player)); } } @@ -84,39 +95,25 @@ private void drawDealerTurn(Deck deck, Dealer dealer) { } } - private void printFinalWinner(Dealer dealer, Players players) { - Map playerResults = new LinkedHashMap<>(); - List outcomeDtos = new ArrayList<>(); - for (Player player : players.getPlayers()) { - Outcome outcome = Outcome.decideWinner(player.getScore(), dealer.getScore()); - playerResults.put(player, outcome); - outcomeDtos.add(new PlayerOutcomeDto(player.getName(), outcome.getName())); - } - GameResult gameResult = new GameResult(playerResults); - resultView.printWinner(new AllOutcomeDto(gameResult.getDealerResult(), outcomeDtos)); - } - private void printFinalScore(Dealer dealer, Players players) { - ParticipantScoreDto dealerScoreDto = createScoreDto(dealer); - List playerScoreDtos = players.getPlayers().stream() - .map(this::createScoreDto) - .collect(Collectors.toList()); + ParticipantScoreDto dealerScoreDto = ParticipantScoreDto.from(dealer); + List playerScoreDtos = + players.players() + .stream() + .map(ParticipantScoreDto::from) + .toList(); resultView.printResult(new FinalScoreDto(dealerScoreDto, playerScoreDtos)); } - private ParticipantScoreDto createScoreDto(Participant participant) { - return new ParticipantScoreDto( - participant.getName(), - getCardNames(participant), - participant.getScore().getGameScore() - ); - } - - private List getCardNames(Participant participant) { - List cardNames = new ArrayList<>(); - for (Card card : participant.getHand().getCards()) { - cardNames.add(card.getCardName()); + private void printFinalProfit(Dealer dealer, Players players) { + int dealerProfit = 0; + List outcomeDtos = new ArrayList<>(); + for (Player player : players.players()) { + Outcome outcome = Outcome.decideWinner(player, dealer); + Money playerProfit = player.getMoney().multiply(outcome.getRate()); + dealerProfit -= playerProfit.getAmount(); + outcomeDtos.add(PlayerOutcomeDto.of(player, playerProfit)); } - return cardNames; + resultView.printScore(new MoneyDTO(String.valueOf(dealerProfit), outcomeDtos)); } -} \ No newline at end of file +} diff --git a/src/main/java/domain/Outcome.java b/src/main/java/domain/Outcome.java deleted file mode 100644 index 499846d5c28..00000000000 --- a/src/main/java/domain/Outcome.java +++ /dev/null @@ -1,44 +0,0 @@ -package domain; - -public enum Outcome { - WIN("승"), - LOSE("패"), - DRAW("무"); - - private final String name; - - Outcome(String name) { - this.name = name; - } - - public static Outcome decideWinner(Score playerScore, Score dealerScore) { - if (playerScore.isBust()) { - return LOSE; - } - if (dealerScore.isBust()) { - return WIN; - } - - if (playerScore.getGameScore() > dealerScore.getGameScore()) { - return WIN; - } - if (playerScore.getGameScore() < dealerScore.getGameScore()) { - return LOSE; - } - return DRAW; - } - - public String getName() { - return name; - } - - public Outcome opposite() { - if (this == WIN) { - return LOSE; - } - if (this == LOSE) { - return WIN; - } - return DRAW; - } -} \ No newline at end of file diff --git a/src/main/java/domain/Player.java b/src/main/java/domain/Player.java deleted file mode 100644 index 47d62a53102..00000000000 --- a/src/main/java/domain/Player.java +++ /dev/null @@ -1,12 +0,0 @@ -package domain; - -public class Player extends Participant { - public Player(String name) { - super(name); - } - - @Override - public boolean canDraw() { - return !getScore().isBust(); - } -} \ No newline at end of file diff --git a/src/main/java/domain/Players.java b/src/main/java/domain/Players.java deleted file mode 100644 index 3b2d3204ca0..00000000000 --- a/src/main/java/domain/Players.java +++ /dev/null @@ -1,24 +0,0 @@ -package domain; - -import java.util.ArrayList; -import java.util.List; - -public class Players { - private final List players; - - public Players(List players) { - this.players = players; - } - - public int getSize() { - return players.size(); - } - - public Player getPlayer(int index) { - return players.get(index); - } - - public List getPlayers() { - return new ArrayList<>(this.players); - } -} diff --git a/src/main/java/domain/Card.java b/src/main/java/domain/card/Card.java similarity index 72% rename from src/main/java/domain/Card.java rename to src/main/java/domain/card/Card.java index 9dc0842b87e..f277187a607 100644 --- a/src/main/java/domain/Card.java +++ b/src/main/java/domain/card/Card.java @@ -1,12 +1,6 @@ -package domain; - -public class Card { - private final int card; - - public Card(int card) { - this.card = card; - } +package domain.card; +public record Card(int card) { public String getCardName() { int shape = card / 13; int number = card % 13; @@ -15,23 +9,16 @@ public String getCardName() { public int getScore() { int cardNumber = card % 13; - if (1 <= cardNumber && cardNumber <= 9) { return cardNumber + 1; } - if (cardNumber >= 10) { return 10; } - return 11; } public boolean isAce() { return card % 13 == 0; } - - public int getCard() { - return card; - } -} \ No newline at end of file +} diff --git a/src/main/java/domain/CardSuitMap.java b/src/main/java/domain/card/CardSuitMap.java similarity index 94% rename from src/main/java/domain/CardSuitMap.java rename to src/main/java/domain/card/CardSuitMap.java index adb39f6e3f4..00ed1db6d6e 100644 --- a/src/main/java/domain/CardSuitMap.java +++ b/src/main/java/domain/card/CardSuitMap.java @@ -1,4 +1,4 @@ -package domain; +package domain.card; import java.util.Map; @@ -26,6 +26,10 @@ public final class CardSuitMap { Map.entry(12, "K") ); + private CardSuitMap() { + + } + public static String getSuit(int shape) { return SUIT_MAP.get(shape); } @@ -34,3 +38,4 @@ public static String getRank(int number) { return RANK_MAP.get(number); } } + diff --git a/src/main/java/domain/Deck.java b/src/main/java/domain/card/Deck.java similarity index 84% rename from src/main/java/domain/Deck.java rename to src/main/java/domain/card/Deck.java index ee6a77ecccc..abdf138320f 100644 --- a/src/main/java/domain/Deck.java +++ b/src/main/java/domain/card/Deck.java @@ -1,11 +1,9 @@ -package domain; +package domain.card; import java.util.Collections; import java.util.List; -public class Deck { - private final List cards; - +public record Deck(List cards) { public Deck(List cards) { this.cards = cards; shuffle(); diff --git a/src/main/java/domain/card/DeckFactory.java b/src/main/java/domain/card/DeckFactory.java new file mode 100644 index 00000000000..a97eb49aa6a --- /dev/null +++ b/src/main/java/domain/card/DeckFactory.java @@ -0,0 +1,19 @@ +package domain.card; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.IntStream; + +public class DeckFactory { + + private DeckFactory() { + } + + public static Deck create() { + List cards = IntStream.range(0, 52) + .mapToObj(Card::new) + .toList(); + + return new Deck(new ArrayList<>(cards)); + } +} diff --git a/src/main/java/domain/Dealer.java b/src/main/java/domain/participant/Dealer.java similarity index 91% rename from src/main/java/domain/Dealer.java rename to src/main/java/domain/participant/Dealer.java index d580dcd29ec..1396ccfe78e 100644 --- a/src/main/java/domain/Dealer.java +++ b/src/main/java/domain/participant/Dealer.java @@ -1,4 +1,4 @@ -package domain; +package domain.participant; public class Dealer extends Participant { private static final int DEALER_DRAW_BOUND = 16; @@ -12,4 +12,4 @@ public Dealer() { public boolean canDraw() { return getScore().getGameScore() <= DEALER_DRAW_BOUND; } -} \ No newline at end of file +} diff --git a/src/main/java/domain/Hand.java b/src/main/java/domain/participant/Hand.java similarity index 62% rename from src/main/java/domain/Hand.java rename to src/main/java/domain/participant/Hand.java index 15843f3caa6..edc05b4141c 100644 --- a/src/main/java/domain/Hand.java +++ b/src/main/java/domain/participant/Hand.java @@ -1,14 +1,14 @@ -package domain; +package domain.participant; +import domain.card.Card; +import domain.result.Score; import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class Hand { - private final List cards; - +public record Hand(List cards) { public Hand() { - this.cards = new ArrayList<>(); + this(new ArrayList<>()); } public Hand(List cards) { @@ -20,20 +20,16 @@ public void add(Card card) { } public Score calculateScore() { - int totalScore = 0; - int aceCount = 0; - for (Card card : cards) { - totalScore += card.getScore(); - if (card.isAce()) { - aceCount++; - } - } - + int totalScore = cards.stream() + .mapToInt(Card::getScore) + .sum(); + int aceCount = (int) cards.stream() + .filter(Card::isAce) + .count(); while (canLowerAceScore(totalScore, aceCount)) { totalScore -= 10; aceCount--; } - return new Score(totalScore); } @@ -45,7 +41,8 @@ private boolean canLowerAceScore(int score, int aceCount) { return score > 21 && aceCount > 0; } - public List getCards() { + @Override + public List cards() { return Collections.unmodifiableList(cards); } } diff --git a/src/main/java/domain/participant/Money.java b/src/main/java/domain/participant/Money.java new file mode 100644 index 00000000000..8a7068f44bb --- /dev/null +++ b/src/main/java/domain/participant/Money.java @@ -0,0 +1,36 @@ +package domain.participant; + +import java.util.regex.Pattern; + +public class Money { + private static final Pattern COUNT_PATTERN = Pattern.compile("\\d+"); + private final int amount; + + public Money(String input) { + validateNumeric(input); + this.amount = Integer.parseInt(input); + } + + private Money(int amount) { + this.amount = amount; + } + + public Money multiply(double rate) { + return new Money((int) (amount * rate)); + } + + public int getAmount() { + return amount; + } + + @Override + public String toString() { + return String.valueOf(amount); + } + + private void validateNumeric(String input) { + if (!COUNT_PATTERN.matcher(input).matches()) { + throw new IllegalArgumentException("숫자가 아닙니다."); + } + } +} diff --git a/src/main/java/domain/Name.java b/src/main/java/domain/participant/Name.java similarity index 66% rename from src/main/java/domain/Name.java rename to src/main/java/domain/participant/Name.java index b30b7d24043..3da219af171 100644 --- a/src/main/java/domain/Name.java +++ b/src/main/java/domain/participant/Name.java @@ -1,25 +1,16 @@ -package domain; +package domain.participant; -public final class Name { - private final String name; - - public Name(String name) { +public record Name(String name) { + public Name { validate(name); - this.name = name; } private void validate(String name) { if (name == null) { throw new IllegalArgumentException("이름은 비어 있거나 공백만 있을 수 없습니다."); } - if (name.isBlank()) { throw new IllegalArgumentException("이름은 비어 있거나 공백만 있을 수 없습니다."); } } - - public String getName() { - return name; - } } - diff --git a/src/main/java/domain/Participant.java b/src/main/java/domain/participant/Participant.java similarity index 51% rename from src/main/java/domain/Participant.java rename to src/main/java/domain/participant/Participant.java index b95bf7fb835..fc10566e0f9 100644 --- a/src/main/java/domain/Participant.java +++ b/src/main/java/domain/participant/Participant.java @@ -1,4 +1,8 @@ -package domain; +package domain.participant; + +import domain.card.Card; +import domain.result.Score; +import java.util.List; public abstract class Participant { private final Hand hand; @@ -9,6 +13,10 @@ public Participant(String name) { this.hand = new Hand(); } + public boolean isFirstBlackJack() { + return hand.size() == 2 && hand.calculateScore().isBlackjack(); + } + public void drawCard(Card card) { hand.add(card); } @@ -18,12 +26,18 @@ public Score getScore() { } public String getName() { - return name.getName(); + return name.name(); } public Hand getHand() { - return new Hand(hand.getCards()); + return new Hand(hand.cards()); + } + + public List getCardNames() { + return hand.cards().stream() + .map(Card::getCardName) + .toList(); } public abstract boolean canDraw(); -} \ No newline at end of file +} diff --git a/src/main/java/domain/participant/Player.java b/src/main/java/domain/participant/Player.java new file mode 100644 index 00000000000..adbddc6413e --- /dev/null +++ b/src/main/java/domain/participant/Player.java @@ -0,0 +1,19 @@ +package domain.participant; + +public class Player extends Participant { + private final Money money; + + public Player(String name, Money money) { + super(name); + this.money = money; + } + + public Money getMoney() { + return money; + } + + @Override + public boolean canDraw() { + return !getScore().isBust() && !getScore().isBlackjack(); + } +} diff --git a/src/main/java/domain/participant/Players.java b/src/main/java/domain/participant/Players.java new file mode 100644 index 00000000000..fc2b4b2b698 --- /dev/null +++ b/src/main/java/domain/participant/Players.java @@ -0,0 +1,18 @@ +package domain.participant; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public record Players(List players) { + public String getJoinedNames() { + return players.stream() + .map(Player::getName) + .collect(Collectors.joining(", ")); + } + + @Override + public List players() { + return new ArrayList<>(this.players); + } +} diff --git a/src/main/java/domain/GameResult.java b/src/main/java/domain/result/GameResult.java similarity index 92% rename from src/main/java/domain/GameResult.java rename to src/main/java/domain/result/GameResult.java index 86b8ceed259..dd8f62fd6dc 100644 --- a/src/main/java/domain/GameResult.java +++ b/src/main/java/domain/result/GameResult.java @@ -1,5 +1,6 @@ -package domain; +package domain.result; +import domain.participant.Player; import java.util.EnumMap; import java.util.Map; @@ -21,4 +22,4 @@ public Map getDealerResult() { } return dealerResult; } -} \ No newline at end of file +} diff --git a/src/main/java/domain/result/Outcome.java b/src/main/java/domain/result/Outcome.java new file mode 100644 index 00000000000..9ab21491577 --- /dev/null +++ b/src/main/java/domain/result/Outcome.java @@ -0,0 +1,67 @@ +package domain.result; + +import domain.participant.Participant; + +public enum Outcome { + BLACKJACK_WIN("블랙잭 승", 1.5), + WIN("승", 1.0), + DRAW("무", 1.0), + LOSE("패", -1.0); + + private final String name; + private final double rate; + + Outcome(String name, double rate) { + this.name = name; + this.rate = rate; + } + + public static Outcome decideWinner(Participant player, Participant dealer) { + if (player.isFirstBlackJack() && dealer.isFirstBlackJack()) { + return DRAW; + } + if (player.isFirstBlackJack()) { + return BLACKJACK_WIN; + } + if (dealer.isFirstBlackJack()) { + return LOSE; + } + Score playerScore = player.getScore(); + Score dealerScore = dealer.getScore(); + return decideByScore(playerScore, dealerScore); + } + + private static Outcome decideByScore(Score playerScore, Score dealerScore) { + if (playerScore.isBust()) { + return LOSE; + } + if (dealerScore.isBust()) { + return WIN; + } + if (playerScore.getGameScore() > dealerScore.getGameScore()) { + return WIN; + } + if (playerScore.getGameScore() < dealerScore.getGameScore()) { + return LOSE; + } + return DRAW; + } + + public String getName() { + return name; + } + + public double getRate() { + return rate; + } + + public Outcome opposite() { + if (this == WIN) { + return LOSE; + } + if (this == LOSE) { + return WIN; + } + return DRAW; + } +} diff --git a/src/main/java/domain/Score.java b/src/main/java/domain/result/Score.java similarity index 66% rename from src/main/java/domain/Score.java rename to src/main/java/domain/result/Score.java index 0e629d278be..d4310d0ff41 100644 --- a/src/main/java/domain/Score.java +++ b/src/main/java/domain/result/Score.java @@ -1,14 +1,8 @@ -package domain; +package domain.result; -public final class Score { +public record Score(int score) { private static final int BLACKJACK_SCORE = 21; - private final int score; - - public Score(int score) { - this.score = score; - } - public int getGameScore() { return score; } @@ -20,4 +14,4 @@ public boolean isBust() { public boolean isBlackjack() { return score == BLACKJACK_SCORE; } -} \ No newline at end of file +} diff --git a/src/main/java/dto/AllOutcomeDto.java b/src/main/java/dto/AllOutcomeDto.java index 3fd4c2e7cc9..c7ae943d071 100644 --- a/src/main/java/dto/AllOutcomeDto.java +++ b/src/main/java/dto/AllOutcomeDto.java @@ -1,6 +1,6 @@ package dto; -import domain.Outcome; +import domain.result.Outcome; import java.util.List; import java.util.Map; diff --git a/src/main/java/dto/MoneyDTO.java b/src/main/java/dto/MoneyDTO.java new file mode 100644 index 00000000000..4605d60e81a --- /dev/null +++ b/src/main/java/dto/MoneyDTO.java @@ -0,0 +1,9 @@ +package dto; + +import java.util.List; + +public record MoneyDTO( + String dealerMoney, + List playerOutcomes +) { +} diff --git a/src/main/java/dto/ParticipantDto.java b/src/main/java/dto/ParticipantDto.java index 655343f2a12..a60072813db 100644 --- a/src/main/java/dto/ParticipantDto.java +++ b/src/main/java/dto/ParticipantDto.java @@ -1,6 +1,13 @@ package dto; +import domain.participant.Participant; import java.util.List; public record ParticipantDto(String name, List cardNames) { + public static ParticipantDto from(Participant participant) { + return new ParticipantDto( + participant.getName(), + List.copyOf(participant.getCardNames()) + ); + } } diff --git a/src/main/java/dto/ParticipantScoreDto.java b/src/main/java/dto/ParticipantScoreDto.java index e2f707326d8..6e088dbbfed 100644 --- a/src/main/java/dto/ParticipantScoreDto.java +++ b/src/main/java/dto/ParticipantScoreDto.java @@ -1,6 +1,14 @@ package dto; +import domain.participant.Participant; import java.util.List; public record ParticipantScoreDto(String name, List cardNames, int score) { -} \ No newline at end of file + public static ParticipantScoreDto from(Participant participant) { + return new ParticipantScoreDto( + participant.getName(), + List.copyOf(participant.getCardNames()), + participant.getScore().getGameScore() + ); + } +} diff --git a/src/main/java/dto/PlayerOutcomeDto.java b/src/main/java/dto/PlayerOutcomeDto.java index 135a7cc4afa..df344b1b411 100644 --- a/src/main/java/dto/PlayerOutcomeDto.java +++ b/src/main/java/dto/PlayerOutcomeDto.java @@ -1,4 +1,13 @@ package dto; -public record PlayerOutcomeDto(String name, String outcome) { +import domain.participant.Money; +import domain.participant.Player; + +public record PlayerOutcomeDto(String name, String profit) { + public static PlayerOutcomeDto of(Player player, Money profit) { + return new PlayerOutcomeDto( + player.getName(), + String.valueOf(profit.getAmount()) + ); + } } diff --git a/src/main/java/message/IOMessage.java b/src/main/java/message/IOMessage.java index e09d449c142..064e7aef776 100644 --- a/src/main/java/message/IOMessage.java +++ b/src/main/java/message/IOMessage.java @@ -4,7 +4,9 @@ public enum IOMessage { ASK_GAME_PARTICIPANT("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"), ASK_MORE_CARD("는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)"), DEALER_ONE_CARD("딜러는 16이하라 한장의 카드를 더 받았습니다."), - WINNING_STATISTICS("## 최종 승패"); + WINNING_STATISTICS("## 최종 승패"), + FINAL_MONEY_STATISTICS("## 최종 수익"), + ASK_MONEY("의 배팅 금액은?"); private final String message; diff --git a/src/main/java/util/NameParser.java b/src/main/java/util/NameParser.java index 31707b5ce1a..0701b2b142a 100644 --- a/src/main/java/util/NameParser.java +++ b/src/main/java/util/NameParser.java @@ -1,18 +1,13 @@ package util; -import domain.Player; -import domain.Players; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class NameParser { - public static Players makeNameList(String input) { - List playerList = Arrays.stream(input.split(",")) + public static List makeNameList(String input) { + return Arrays.stream(input.split(",")) .map(String::trim) - .map(Player::new) .collect(Collectors.toList()); - - return new Players(playerList); } } diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 5901c706b6d..5864e5e845b 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -15,4 +15,9 @@ public String getMoreCards(String name) { System.out.println(name + IOMessage.ASK_MORE_CARD.message()); return scanner.nextLine(); } + + public String getMoney(String name) { + System.out.println(name + IOMessage.ASK_MONEY.message()); + return scanner.nextLine(); + } } diff --git a/src/main/java/view/ResultView.java b/src/main/java/view/ResultView.java index 76cb344e56a..e3c6bf9dd9c 100644 --- a/src/main/java/view/ResultView.java +++ b/src/main/java/view/ResultView.java @@ -1,14 +1,12 @@ package view; -import domain.Outcome; -import dto.AllOutcomeDto; import dto.FinalScoreDto; import dto.InitialDto; +import dto.MoneyDTO; import dto.ParticipantDto; import dto.ParticipantScoreDto; import dto.PlayerOutcomeDto; import java.util.List; -import java.util.Map; import message.IOMessage; public class ResultView { @@ -44,24 +42,21 @@ public void printResult(FinalScoreDto finalScoreDto) { } } - public void printWinner(AllOutcomeDto allOutcomeDto) { + public void printScore(MoneyDTO moneyDTO) { System.out.println(); - System.out.println(IOMessage.WINNING_STATISTICS.message()); - printDealerOutcome(allOutcomeDto.dealerResult()); - printPlayerOutcomes(allOutcomeDto.playerOutcomes()); + System.out.println(IOMessage.FINAL_MONEY_STATISTICS.message()); + printDealerOutcome(moneyDTO.dealerMoney()); + printPlayerOutcomes(moneyDTO.playerOutcomes()); } - private void printDealerOutcome(Map dealerOutcomes) { - System.out.print("딜러: "); - for (Outcome outcome : Outcome.values()) { - System.out.print(dealerOutcomes.get(outcome) + outcome.getName() + " "); - } + private void printDealerOutcome(String dealerOutcomes) { + System.out.print("딜러: " + dealerOutcomes); System.out.println(); } private void printPlayerOutcomes(List playerOutcomes) { for (PlayerOutcomeDto outcomeDto : playerOutcomes) { - System.out.println(outcomeDto.name() + ": " + outcomeDto.outcome()); + System.out.println(outcomeDto.name() + ": " + outcomeDto.profit()); } } } diff --git a/src/test/java/domain/HandTest.java b/src/test/java/domain/HandTest.java index 76601d93716..3cfb7962043 100644 --- a/src/test/java/domain/HandTest.java +++ b/src/test/java/domain/HandTest.java @@ -3,6 +3,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import domain.card.Card; +import domain.participant.Hand; +import domain.result.Score; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -42,4 +45,4 @@ void calculateScoreExact21() { assertEquals(21, hand.calculateScore().getGameScore()); } -} \ No newline at end of file +} diff --git a/src/test/java/domain/OutcomeTest.java b/src/test/java/domain/OutcomeTest.java index b1e7bbf09a9..4e8e172b396 100644 --- a/src/test/java/domain/OutcomeTest.java +++ b/src/test/java/domain/OutcomeTest.java @@ -1,43 +1,100 @@ package domain; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; +import domain.card.Card; +import domain.participant.Dealer; +import domain.participant.Money; +import domain.participant.Player; +import domain.result.Outcome; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; class OutcomeTest { + @Test - @DisplayName("점수가 같으면 무승부다") - void drawResult() { - Score dealerScore = new Score(20); - Score playerScore = new Score(20); - Outcome outcome = Outcome.decideWinner(playerScore, dealerScore); - assertEquals(Outcome.DRAW, outcome); + @DisplayName("1. 플레이어만 처음 2장으로 블랙잭일 경우") + void playerOnlyBlackJack() { + Player player = new Player("pobi", new Money("10000")); + Dealer dealer = new Dealer(); + + player.drawCard(new Card(0)); + player.drawCard(new Card(10)); + + dealer.drawCard(new Card(10)); + dealer.drawCard(new Card(8)); + + Outcome outcome = Outcome.decideWinner(player, dealer); + Money profit = player.getMoney().multiply(outcome.getRate()); + assertThat(outcome).isEqualTo(Outcome.BLACKJACK_WIN); + assertThat(profit.getAmount()).isEqualTo(15000); } @Test - @DisplayName("딜러 점수가 더 크면 플레이어는 패배한다") - void loseResult() { - Score dealerScore = new Score(20); - Score playerScore = new Score(18); - Outcome outcome = Outcome.decideWinner(playerScore, dealerScore); - assertEquals(Outcome.LOSE, outcome); + @DisplayName("2. 플레이어와 딜러 모두 처음 2장으로 블랙잭인 경우") + void bothBlackJack() { + Player player = new Player("pobi", new Money("10000")); + Dealer dealer = new Dealer(); + + player.drawCard(new Card(0)); + player.drawCard(new Card(10)); + + dealer.drawCard(new Card(0)); + dealer.drawCard(new Card(10)); + + Outcome outcome = Outcome.decideWinner(player, dealer); + Money profit = player.getMoney().multiply(outcome.getRate()); + assertThat(outcome).isEqualTo(Outcome.DRAW); + assertThat(profit.getAmount()).isEqualTo(10000); + } + + @Test + @DisplayName("3. 딜러만 처음 2장으로 블랙잭인 경우") + void dealerOnlyBlackJack() { + Player player = new Player("pobi", new Money("10000")); + Dealer dealer = new Dealer(); + + player.drawCard(new Card(10)); + player.drawCard(new Card(8)); + + dealer.drawCard(new Card(0)); + dealer.drawCard(new Card(10)); + + Outcome outcome = Outcome.decideWinner(player, dealer); + Money profit = player.getMoney().multiply(outcome.getRate()); + assertThat(outcome).isEqualTo(Outcome.LOSE); + assertThat(profit.getAmount()).isEqualTo(-10000); } @Test - @DisplayName("딜러가 버스트면 플레이어는 승리한다") - void winWhenDealerBust() { - Score dealerScore = new Score(22); - Score playerScore = new Score(18); - Outcome outcome = Outcome.decideWinner(playerScore, dealerScore); - assertEquals(Outcome.WIN, outcome); + @DisplayName("4. 플레이어가 카드를 추가로 뽑아 21을 초과할 경우") + void playerBust() { + Player player = new Player("pobi", new Money("10000")); + Dealer dealer = new Dealer(); + player.drawCard(new Card(10)); + player.drawCard(new Card(5)); + player.drawCard(new Card(10)); + dealer.drawCard(new Card(10)); + dealer.drawCard(new Card(8)); + Outcome outcome = Outcome.decideWinner(player, dealer); + Money profit = player.getMoney().multiply(outcome.getRate()); + assertThat(outcome).isEqualTo(Outcome.LOSE); + assertThat(profit.getAmount()).isEqualTo(-10000); } @Test - @DisplayName("플레이어와 딜러가 모두 버스트인 경우 플레이어는 패배한다") - void bothBust() { - Score dealerScore = new Score(22); - Score playerScore = new Score(23); - assertEquals(Outcome.LOSE, Outcome.decideWinner(playerScore, dealerScore)); + @DisplayName("5. 딜러가 21을 초과하고 플레이어는 21 이하일 경우") + void dealerBust_playerAlive() { + Player player = new Player("pobi", new Money("10000")); + Dealer dealer = new Dealer(); + player.drawCard(new Card(10)); + player.drawCard(new Card(5)); + dealer.drawCard(new Card(10)); + dealer.drawCard(new Card(5)); + dealer.drawCard(new Card(10)); + Outcome outcome = Outcome.decideWinner(player, dealer); + Money profit = player.getMoney().multiply(outcome.getRate()); + assertThat(outcome).isEqualTo(Outcome.WIN); + assertThat(profit.getAmount()).isEqualTo(10000); } -} \ No newline at end of file +} diff --git a/src/test/java/domain/PlayerTest.java b/src/test/java/domain/PlayerTest.java index ebde50d05fb..f80c5afe27d 100644 --- a/src/test/java/domain/PlayerTest.java +++ b/src/test/java/domain/PlayerTest.java @@ -2,6 +2,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import domain.card.Card; +import domain.participant.Dealer; +import domain.participant.Money; +import domain.participant.Player; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -9,9 +13,9 @@ class PlayerTest { @Test @DisplayName("플레이어가 카드를 받으면 핸드의 카드 개수가 증가한다") void drawCardByPlayer() { - Player player = new Player("pobi"); + Player player = new Player("pobi", new Money("0")); player.drawCard(new Card(1)); - assertEquals(1, player.getHand().getCards().size()); + assertEquals(1, player.getHand().cards().size()); } @Test @@ -19,6 +23,6 @@ void drawCardByPlayer() { void drawCardByDealer() { Dealer dealer = new Dealer(); dealer.drawCard(new Card(1)); - assertEquals(1, dealer.getHand().getCards().size()); + assertEquals(1, dealer.getHand().cards().size()); } } \ No newline at end of file diff --git a/src/test/java/domain/ScoreTest.java b/src/test/java/domain/ScoreTest.java index e158a97cab1..1d9b1634386 100644 --- a/src/test/java/domain/ScoreTest.java +++ b/src/test/java/domain/ScoreTest.java @@ -2,6 +2,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import domain.card.Card; +import domain.result.Score; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/util/NameParserTest.java b/src/test/java/util/NameParserTest.java index 589217713f2..b6d050f3c41 100644 --- a/src/test/java/util/NameParserTest.java +++ b/src/test/java/util/NameParserTest.java @@ -2,7 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import domain.Players; +import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -10,16 +10,16 @@ class NameParserTest { @Test @DisplayName("이름 문자열을 쉼표로 분리해 플레이어 수를 만든다") void makePlayersSize() { - Players players = NameParser.makeNameList("pobi,jason"); + List names = NameParser.makeNameList("pobi,jason"); - assertEquals(2, players.getSize()); + assertEquals(2, names.size()); } @Test @DisplayName("쉼표 뒤 공백을 제거해 이름을 저장한다") void trimPlayerName() { - Players players = NameParser.makeNameList("pobi, jason"); + List names = NameParser.makeNameList("pobi, jason"); - assertEquals("jason", players.getPlayer(1).getName()); + assertEquals("jason", names.get(1)); } }