From 888b9e4e5a4b88039b94d5cf773c9fc2a7a37ec7 Mon Sep 17 00:00:00 2001 From: leegwichan <44027393+leegwichan@users.noreply.github.com> Date: Wed, 13 Mar 2024 01:16:36 +0900 Subject: [PATCH 01/41] =?UTF-8?q?[1=EB=8B=A8=EA=B3=84=20-=20=EB=B8=94?= =?UTF-8?q?=EB=9E=99=EC=9E=AD=20=EA=B2=8C=EC=9E=84=20=EC=8B=A4=ED=96=89]?= =?UTF-8?q?=20=EC=BB=A4=EC=B0=AC(=EC=9D=B4=EC=B6=A9=EC=95=88)=20=EB=AF=B8?= =?UTF-8?q?=EC=85=98=20=EC=A0=9C=EC=B6=9C=ED=95=A9=EB=8B=88=EB=8B=A4.=20(#?= =?UTF-8?q?622)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs : 기능 명세 및 클래스 설계 작성 * feat (Card) : 카드에 따라 점수를 반환하는 기능 구현 * feat (Deck) : 카드를 한 장 뽑는 기능 구현 * feat (Dealer) : 딜러의 점수 계산 및 카드를 뽑을 수 있는지 확인하는 메서드 구현 * style : 가독성을 위해 클래스 상단에 개행 추가, 클래스 아래 불필요한 개행 제거 * feat (Dealer) : 딜러 턴 진행 구현 * feat (Player) : 점수 계산 기능 구현 * feat (Player) : 딜러의 점수 계산 및 카드를 뽑을 수 있는지 확인하는 메서드 구현 Co-authored-by : zangsu * feat (Player) : 카드를 한 장 추가하는 기능 구현 Co-authored-by: zangsu * refactor (Dealer) : 요구 사항을 반영을 위해 턴 진행 기능을 카드를 한 장 추가하는 기능으로 변경 Co-authored-by: zangsu * refactor : 도메인 구분을 위한 participant 패키지 분리 Co-authored-by: zangsu * refactor (Participant) : 참가자의 공통 책임을 묶기 위해, 추상 클래스 사용 Co-authored-by: zangsu * docs : 이름에 대한 요구사항 추가 Co-authored-by: zangsu * feat (Name) : 요구사항에 따른 이름 검증 기능 구현 Co-authored-by: zangsu * feat (Player) : 플레이어가 이름을 갖도록 구현 Co-authored-by: zangsu * refactor : 참가자 관련 코드 개선 - Player 가 Name 을 가지도록 생성자 변경 - 추상 클래스의 생성자 접근제어자 변경 - Dealer 기본 생성자 추가 Co-authored-by: zangsu * feat (Participant) : 게임 시작 후 카드를 두 장 뽑는 기능 구현 Co-authored-by: zangsu * feat (Dealer) : 딜러가 플레이어와 승패를 확인하는 기능 구현 Co-authored-by: zangsu * feat (Player) : 플레이어가 딜러와 승패를 확인하는 기능 구현 Co-authored-by: zangsu * refactor (Deck) : 사용되지 않는 생성자 제거 Co-authored-by: zangsu * feat (Players) : 제한된 수의 플레이어만 가지는 Players 생성 Co-authored-by: zangsu * feat (Players) : 플레이어가 중복되지 않도록 검증 기능 구현 Co-authored-by: zangsu * feat (Deck) : 카드가 섞인 덱이 생성되도록 구현 Co-authored-by: zangsu * feat (Players) : 모든 플레이어들이 시작 카드를 뽑는 기능 구현 Co-authored-by: zangsu * feat (BlackJackGame) : 전반적인 제어 기능 구현 Co-authored-by: zangsu * feat (InputView) : 한장의 카드를 더 받을지 요청하는 기능 구현 Co-authored-by: zangsu * feat (OutputView) : 시작 카드를 출력하는 기능 구현 Co-authored-by: zangsu * feat (OutputView) : 카드 출력 기능 구현 * refactor (Participant) : 카드 출력 개수를 정하는 책임을 View로 넘기기 위해 시작 카드를 건네는 기능 제거 Co-authored-by: zangsu * feat (Dealer) : 모든 플레이어를 상대로 승패를 계산하는 기능 구현 Co-authored-by: zangsu * feat (OutputView) : 최종 결과 출력 기능 구현 Co-authored-by: zangsu * refactor (Participant) : 카트 점수 계산 기능의 인덴트 개선 Co-authored-by: zangsu * style : 테스트의 DisplayName 변경 Co-authored-by: zangsu * refactor (Card) : Value, Shape 클래스 분리 및 패키지 변경 Co-authored-by: zangsu * refactor (BlackJackGame) : 가독성을 위해 메서드 분리 Co-authored-by: zangsu * refactor (OutputView) : 메서드 순서 조정 * refactor (CardTest) : 다양한 상황의 카드들을 픽스쳐로 추출 Co-authored-by: zangsu * style : 테스트 이름에서 경계값 제거 Co-authored-by: zangsu * refactor : 이름에 관한 픽스쳐 NameTest에 통합 Co-authored-by: zangsu * style : 사용하지 않는 import 문 제거 Co-authored-by: zangsu * style : 의미 없는 공백 제거 * feat : 생성 단계에서 null을 입력받았을 경우, NullPointerException을 던지도록 변경 * refactor (Participant) : 외부에서 사용하지 않는 변수의 접근 제어자 변경 * docs : README에서 구현에 관련된 내용들 삭제 Co-authored-by: zangsu * style (OutputView) : 메서드 이름을 추상적으로 변경 Co-authored-by: zangsu * refactor (PlayerTurn) : 가독성을 위해 `BiConsumer` -> `PlayerTurn` 으로 변경 Co-authored-by: zangsu * refactor (Value) : 추후 유지보수의 용이성과 enum 인스턴스 자체가 값에 대한 책임을 가지기 위해 `maxValue` 필드 추가 Co-authored-by: zangsu * refactor (Deck) : 패키지 변경 Co-authored-by: zangsu * refactor : 테스트를 간결하게 작성하기 위해, Test용 생성자를 사용 Co-authored-by: zangsu * style : import 문 정리 및 TODO 제거 Co-authored-by: zangsu * feat (Hand) : 손패 계산의 책임을 분리하기 위해 Hand 구현 Co-authored-by: zangsu * feat (Hand) : 버스트 상태를 판단하는 기능 구현 Co-authored-by: zangsu * feat (Hand) : 블랙잭 판단 기능 구현 Co-authored-by: zangsu * feat (Hand) : 카드를 추가하는 기능 구현 Co-authored-by: zangsu * feat (Hand) : 빈 손패인지 확인하는 기능 구현 Co-authored-by: zangsu * refactor (Participant) : 카드와 관련된 책임들을 Hand 로 위임 Co-authored-by: zangsu * feat (Dealer) : 승패 조건에 블랙잭 추가 Co-authored-by: zangsu * refactor : 가독성을 위해 테스트 픽스처 분리 Co-authored-by: zangsu * refactor : 도메인 규칙과 직결되는 경계값을 픽스처에서 제거 Co-authored-by: zangsu * refactor : 시작 카드가 몇 개 보여지는지의 책임을 뷰에서 도메인으로 이동 Co-authored-by: zangsu * refactor : DTO 사용을 최소화하기 위해, 원시값을 전달 Co-authored-by: zangsu * refactor (Hand) : 가독성 개선을 위해 점수 계산 기능 변경 Co-authored-by: zangsu --------- Co-authored-by: zangsu --- README.md | 34 +++ src/main/java/.gitkeep | 0 .../java/blackjack/BlackJackApplication.java | 8 + src/main/java/blackjack/BlackJackGame.java | 60 ++++++ src/main/java/blackjack/domain/card/Card.java | 51 +++++ src/main/java/blackjack/domain/card/Deck.java | 40 ++++ src/main/java/blackjack/domain/card/Hand.java | 67 ++++++ .../java/blackjack/domain/card/Shape.java | 5 + .../java/blackjack/domain/card/Value.java | 37 ++++ .../blackjack/domain/participant/Dealer.java | 55 +++++ .../blackjack/domain/participant/Name.java | 40 ++++ .../domain/participant/Participant.java | 62 ++++++ .../blackjack/domain/participant/Player.java | 56 +++++ .../domain/participant/PlayerTurn.java | 8 + .../blackjack/domain/participant/Players.java | 59 ++++++ src/main/java/blackjack/view/InputView.java | 35 ++++ src/main/java/blackjack/view/OutputView.java | 123 +++++++++++ src/test/java/.gitkeep | 0 .../java/blackjack/domain/card/CardTest.java | 54 +++++ .../java/blackjack/domain/card/DeckTest.java | 35 ++++ .../java/blackjack/domain/card/HandTest.java | 128 +++++++++++ .../domain/fixture/CardsFixture.java | 44 ++++ .../domain/participant/DealerTest.java | 198 ++++++++++++++++++ .../domain/participant/NameTest.java | 32 +++ .../domain/participant/PlayerTest.java | 125 +++++++++++ .../domain/participant/PlayersTest.java | 57 +++++ 26 files changed, 1413 insertions(+) delete mode 100644 src/main/java/.gitkeep create mode 100644 src/main/java/blackjack/BlackJackApplication.java create mode 100644 src/main/java/blackjack/BlackJackGame.java create mode 100644 src/main/java/blackjack/domain/card/Card.java create mode 100644 src/main/java/blackjack/domain/card/Deck.java create mode 100644 src/main/java/blackjack/domain/card/Hand.java create mode 100644 src/main/java/blackjack/domain/card/Shape.java create mode 100644 src/main/java/blackjack/domain/card/Value.java create mode 100644 src/main/java/blackjack/domain/participant/Dealer.java create mode 100644 src/main/java/blackjack/domain/participant/Name.java create mode 100644 src/main/java/blackjack/domain/participant/Participant.java create mode 100644 src/main/java/blackjack/domain/participant/Player.java create mode 100644 src/main/java/blackjack/domain/participant/PlayerTurn.java create mode 100644 src/main/java/blackjack/domain/participant/Players.java create mode 100644 src/main/java/blackjack/view/InputView.java create mode 100644 src/main/java/blackjack/view/OutputView.java delete mode 100644 src/test/java/.gitkeep create mode 100644 src/test/java/blackjack/domain/card/CardTest.java create mode 100644 src/test/java/blackjack/domain/card/DeckTest.java create mode 100644 src/test/java/blackjack/domain/card/HandTest.java create mode 100644 src/test/java/blackjack/domain/fixture/CardsFixture.java create mode 100644 src/test/java/blackjack/domain/participant/DealerTest.java create mode 100644 src/test/java/blackjack/domain/participant/NameTest.java create mode 100644 src/test/java/blackjack/domain/participant/PlayerTest.java create mode 100644 src/test/java/blackjack/domain/participant/PlayersTest.java diff --git a/README.md b/README.md index 556099c4de3..7d9853dca80 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,37 @@ ## 우아한테크코스 코드리뷰 - [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) + +## 기능 요구 사항 + +- 블랙잭 게임은 딜러와 플레이어 중 카드의 합이 21 또는 21에 가장 가까운 숫자를 가지는 쪽이 이기는 게임이다. +- 각 카드는 점수를 가진다. + - 숫자 카드는 카드 숫자로 계산한다. + - King, Queen, Jack은 각각 10으로 계산한다. + - Ace는 1 또는 11로 계산한다. +- 플레이어는 이름을 가진다. + - 이름은 공백으로만 구성될 수 없다. + - 이름은 앞뒤 공백을 가질 수 없다. + - 중복된 이름은 가질 수 없다. + +### 승리 조건 + +- 카드 숫자를 합쳐 가능한 21에 가깝게 만들면 이긴다. + - 처음 받은 2장 합쳐 21이 나오는 경우 블랙잭이 되며, 모든 카드조합 중에 가장 강력하다. + - 카드 숫자의 합이 21을 초과하게 되는 순간 '버스트'라고 하며 딜러의 결과에 관계없이 플레이어가 패배한다. + - 딜러와 플레이어의 점수와 블랙잭 여부가 동일한 경우, 딜러가 승리한다. + +### 프로그램 진행 순서 + +1. 참가자 이름 입력한다. +2. 카드를 2장씩 나눠준 후에, 현재 카드 상태 출력한다. +3. 각 참가자들의 턴을 진행한다. + 1. 카드가 21을 넘은 경우, 턴이 종료된다. + 2. 카드를 더 받을 지 여부를 입력한다. + 3. 만약 더 받는다면, 카드를 한 장 추가하고 1으로 돌아간다. + 4. 더 받지 않을 경우, 턴을 종료한다. +4. 딜러의 턴을 진행한다. + 1. 카드가 16을 넘은 경우, 턴이 종료된다. + 2. 카드를 한 장 더 추가하고, 1로 돌아간다. +5. 딜러 및 모든 참가자의 보유 카드들과 점수를 출력한다. +6. 딜러 및 모든 참가자의 승패를 출력한다. diff --git a/src/main/java/.gitkeep b/src/main/java/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/main/java/blackjack/BlackJackApplication.java b/src/main/java/blackjack/BlackJackApplication.java new file mode 100644 index 00000000000..c508abef182 --- /dev/null +++ b/src/main/java/blackjack/BlackJackApplication.java @@ -0,0 +1,8 @@ +package blackjack; + +public class BlackJackApplication { + public static void main(String[] args) { + BlackJackGame blackJackGame = new BlackJackGame(); + blackJackGame.run(); + } +} diff --git a/src/main/java/blackjack/BlackJackGame.java b/src/main/java/blackjack/BlackJackGame.java new file mode 100644 index 00000000000..9612c621328 --- /dev/null +++ b/src/main/java/blackjack/BlackJackGame.java @@ -0,0 +1,60 @@ +package blackjack; + +import blackjack.domain.card.Deck; +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; +import blackjack.domain.participant.Players; +import blackjack.view.InputView; +import blackjack.view.OutputView; +import java.util.List; + +public class BlackJackGame { + + private final InputView inputView = new InputView(); + private final OutputView outputView = new OutputView(); + + public void run() { + Deck deck = Deck.createShuffledDeck(); + Dealer dealer = new Dealer(); + Players players = createPlayers(); + drawStartCards(dealer, players, deck); + play(players, dealer, deck); + printResult(dealer, players); + } + + private Players createPlayers() { + List names = inputView.inputPlayerNames(); + return Players.from(names); + } + + private void drawStartCards(Dealer dealer, Players players, Deck deck) { + dealer.drawStartCards(deck); + players.drawStartCards(deck); + outputView.printStartStatus(dealer, players); + } + + private void play(Players players, Dealer dealer, Deck deck) { + players.play(this::playTurn, deck); + while (dealer.isDrawable()) { + outputView.printDealerDraw(); + dealer.add(deck.draw()); + } + } + + private void playTurn(Player player, Deck deck) { + while (player.isDrawable() && inputView.isPlayerWantDraw(player.getName())) { + player.add(deck.draw()); + outputView.printPlayerCards(player); + } + } + + private void printResult(Dealer dealer, Players players) { + outputView.printEndingStatus(dealer, players); + int winCount = dealer.calculateWinCount(players); + int loseCount = dealer.calculateLoseCount(players); + outputView.printDealerMatchResult(winCount, loseCount); + for (Player player : players.getPlayers()) { + outputView.printPlayerMatchResult(player.getName(), player.isWin(dealer)); + } + } +} diff --git a/src/main/java/blackjack/domain/card/Card.java b/src/main/java/blackjack/domain/card/Card.java new file mode 100644 index 00000000000..416457c0642 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Card.java @@ -0,0 +1,51 @@ +package blackjack.domain.card; + +import java.util.Objects; + +public class Card { + + private final Value value; + private final Shape shape; + + public Card(Value value, Shape shape) { + this.value = Objects.requireNonNull(value); + this.shape = Objects.requireNonNull(shape); + } + + public int getMinScore() { + return value.getMinScore(); + } + + public int getMaxScore() { + return value.getMaxScore(); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + Card card = (Card) object; + return value == card.value && shape == card.shape; + } + + @Override + public int hashCode() { + return Objects.hash(value, shape); + } + + public Value getValue() { + return value; + } + + public Shape getShape() { + return shape; + } + + public boolean isAce() { + return value.isAce(); + } +} diff --git a/src/main/java/blackjack/domain/card/Deck.java b/src/main/java/blackjack/domain/card/Deck.java new file mode 100644 index 00000000000..3c759f38d33 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Deck.java @@ -0,0 +1,40 @@ +package blackjack.domain.card; + +import static java.util.stream.Collectors.toList; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class Deck { + + private final Queue cards; + + private Deck(List cards) { + this.cards = new LinkedList<>(cards); + } + + public static Deck createShuffledDeck() { + List cards = Arrays.stream(Shape.values()) + .map(Deck::makeCards) + .flatMap(List::stream) + .collect(toList()); + Collections.shuffle(cards); + return new Deck(cards); + } + + private static List makeCards(Shape shape) { + return Arrays.stream(Value.values()) + .map(value -> new Card(value, shape)) + .toList(); + } + + 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 new file mode 100644 index 00000000000..6b07ecb2b09 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Hand.java @@ -0,0 +1,67 @@ +package blackjack.domain.card; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Hand { + + private static final int BLACKJACK_SCORE = 21; + private static final int BLACKJACK_SIZE = 2; + + private final List cards; + + public Hand(List cards) { + this.cards = new ArrayList<>(cards); + } + + public void add(Card card) { + cards.add(card); + } + + public int calculateScore() { + int totalMinScore = getMinScore(); + int biggerScore = getBiggerScore(); + + if (biggerScore > BLACKJACK_SCORE) { + return totalMinScore; + } + return biggerScore; + } + + private int getMinScore() { + return cards.stream() + .mapToInt(Card::getMinScore) + .sum(); + } + + private int getBiggerScore() { + int score = getMinScore(); + int differenceScore = cards.stream() + .filter(Card::isAce) + .mapToInt(this::calculateDifferenceScore) + .findAny() + .orElse(0); + return score + differenceScore; + } + + private int calculateDifferenceScore(Card card) { + return card.getMaxScore() - card.getMinScore(); + } + + public boolean isBusted() { + return calculateScore() > BLACKJACK_SCORE; + } + + public boolean isBlackjack() { + return cards.size() == BLACKJACK_SIZE && calculateScore() == BLACKJACK_SCORE; + } + + public List getCards() { + return Collections.unmodifiableList(cards); + } + + public boolean isEmpty() { + return cards.isEmpty(); + } +} diff --git a/src/main/java/blackjack/domain/card/Shape.java b/src/main/java/blackjack/domain/card/Shape.java new file mode 100644 index 00000000000..292d024bab4 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Shape.java @@ -0,0 +1,5 @@ +package blackjack.domain.card; + +public enum Shape { + SPADE, DIAMOND, HEART, CLOVER +} diff --git a/src/main/java/blackjack/domain/card/Value.java b/src/main/java/blackjack/domain/card/Value.java new file mode 100644 index 00000000000..7cd8f2898db --- /dev/null +++ b/src/main/java/blackjack/domain/card/Value.java @@ -0,0 +1,37 @@ +package blackjack.domain.card; + +public enum Value { + ACE(1, 11), + TWO(2, 2), + THREE(3, 3), + FOUR(4, 4), + FIVE(5, 5), + SIX(6, 6), + SEVEN(7, 7), + EIGHT(8, 8), + NINE(9, 9), + TEN(10, 10), + JACK(10, 10), + QUEEN(10, 10), + KING(10, 10); + + private final int minScore; + private final int maxScore; + + Value(int minScore, int maxScore) { + this.minScore = minScore; + this.maxScore = maxScore; + } + + public int getMinScore() { + return minScore; + } + + public int getMaxScore() { + return maxScore; + } + + public boolean isAce() { + return this == ACE; + } +} diff --git a/src/main/java/blackjack/domain/participant/Dealer.java b/src/main/java/blackjack/domain/participant/Dealer.java new file mode 100644 index 00000000000..95d651262f2 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Dealer.java @@ -0,0 +1,55 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Card; +import java.util.Collections; +import java.util.List; + +public class Dealer extends Participant { + + private static final int DRAWABLE_MAX_SCORE = 16; + private static final int START_CARD_SIZE = 1; + + public Dealer() { + super(Collections.emptyList()); + } + + Dealer(List cards) { + super(cards); + } + + public boolean isWin(Player player) { + if (player.isBusted() || this.isBlackjack()) { + return true; + } + if (this.isBusted() || player.isBlackjack()) { + return false; + } + return this.calculateScore() >= player.calculateScore(); + } + + private boolean isLose(Player player) { + return !isWin(player); + } + + public int calculateWinCount(Players players) { + return (int) players.getPlayers().stream() + .filter(this::isWin) + .count(); + } + + public int calculateLoseCount(Players players) { + return (int) players.getPlayers().stream() + .filter(this::isLose) + .count(); + } + + @Override + protected int getMaxDrawableScore() { + return DRAWABLE_MAX_SCORE; + } + + @Override + protected int getStartCardSize() { + return START_CARD_SIZE; + } +} 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..3baa06e97a9 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Name.java @@ -0,0 +1,40 @@ +package blackjack.domain.participant; + +import java.util.Objects; + +public class Name { + + private final String name; + + public Name(String name) { + validate(name); + this.name = name.strip(); + } + + private void validate(String name) { + if (name == null || name.isBlank()) { + throw new IllegalArgumentException("이름은 적어도 한 글자 이상을 포함해야 합니다."); + } + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + Name name1 = (Name) object; + return Objects.equals(name, name1.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } +} diff --git a/src/main/java/blackjack/domain/participant/Participant.java b/src/main/java/blackjack/domain/participant/Participant.java new file mode 100644 index 00000000000..53e21f9886e --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Participant.java @@ -0,0 +1,62 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Deck; +import blackjack.domain.card.Hand; +import java.util.List; + +public abstract class Participant { + + protected static final int BLACKJACK_SCORE = 21; + private static final int START_CARDS_SIZE = 2; + + private final Hand hand; + + protected Participant(List cards) { + this.hand = new Hand(cards); + } + + public final int calculateScore() { + return hand.calculateScore(); + } + + public final boolean isDrawable() { + return calculateScore() <= getMaxDrawableScore(); + } + + public boolean isBlackjack() { + return hand.isBlackjack(); + } + + public final boolean isBusted() { + return hand.isBusted(); + } + + public final void drawStartCards(Deck deck) { + if (!hand.isEmpty()) { + throw new IllegalStateException("이미 시작 카드를 뽑았습니다."); + } + for (int i = 0; i < START_CARDS_SIZE; i++) { + add(deck.draw()); + } + } + + public final void add(Card card) { + if (!isDrawable()) { + throw new IllegalStateException("더 이상 카드를 추가할 수 없습니다."); + } + hand.add(card); + } + + public final List getStartCards() { + return getCards().subList(0, getStartCardSize()); + } + + public final List getCards() { + return hand.getCards(); + } + + protected abstract int getMaxDrawableScore(); + + protected abstract int getStartCardSize(); +} 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..a96d63e9e8a --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Player.java @@ -0,0 +1,56 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Card; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class Player extends Participant { + + private static final int START_CARD_SIZE = 2; + private final Name name; + + Player(List cards, Name name) { + super(cards); + this.name = Objects.requireNonNull(name); + } + + public static Player from(String name) { + return new Player(Collections.emptyList(), new Name(name)); + } + + public boolean isWin(Dealer dealer) { + return !dealer.isWin(this); + } + + @Override + protected int getMaxDrawableScore() { + return BLACKJACK_SCORE; + } + + @Override + protected int getStartCardSize() { + return START_CARD_SIZE; + } + + public String getName() { + return name.getName(); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + Player player = (Player) object; + return Objects.equals(name, player.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } +} diff --git a/src/main/java/blackjack/domain/participant/PlayerTurn.java b/src/main/java/blackjack/domain/participant/PlayerTurn.java new file mode 100644 index 00000000000..53ae62cc63c --- /dev/null +++ b/src/main/java/blackjack/domain/participant/PlayerTurn.java @@ -0,0 +1,8 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Deck; + +@FunctionalInterface +public interface PlayerTurn { + void play(Player player, Deck deck); +} diff --git a/src/main/java/blackjack/domain/participant/Players.java b/src/main/java/blackjack/domain/participant/Players.java new file mode 100644 index 00000000000..ca19558f47d --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Players.java @@ -0,0 +1,59 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Deck; +import java.util.List; + +public class Players { + + private static final int MAX_PLAYERS_SIZE = 4; + + private final List players; + + private Players(List players) { + validateSize(players); + validateDistinct(players); + this.players = players; + } + + public static Players from(List names) { + List players = names.stream() + .map(Player::from) + .toList(); + return new Players(players); + } + + private void validateSize(List players) { + if (players.isEmpty()) { + throw new IllegalArgumentException("최소 한 명의 플레이어가 있어야 합니다."); + } + if (players.size() > MAX_PLAYERS_SIZE) { + throw new IllegalArgumentException("최대 4명의 플레이어만 참여 가능합니다."); + } + } + + private void validateDistinct(List players) { + if (isDuplicated(players)) { + throw new IllegalArgumentException("중복된 이름을 사용할 수 없습니다."); + } + } + + private boolean isDuplicated(List players) { + return players.size() != players.stream().distinct().count(); + } + + public void drawStartCards(Deck deck) { + for (Player player : players) { + player.drawStartCards(deck); + } + } + + public List getPlayers() { + return players; + } + + public void play(PlayerTurn playTurn, Deck deck) { + for (Player player : players) { + playTurn.play(player, deck); + } + } +} diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java new file mode 100644 index 00000000000..eb40910c95f --- /dev/null +++ b/src/main/java/blackjack/view/InputView.java @@ -0,0 +1,35 @@ +package blackjack.view; + +import java.util.List; +import java.util.Scanner; + +public class InputView { + + private static final Scanner SCANNER = new Scanner(System.in); + private static final String NAME_DELIMITER = ","; + private static final String WANT_DRAW_INPUT = "y"; + private static final String WANT_NOT_DRAW_INPUT = "n"; + + public List inputPlayerNames() { + System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); + String names = SCANNER.nextLine(); + return List.of(names.split(NAME_DELIMITER, -1)); + } + + public boolean isPlayerWantDraw(String name) { + System.out.printf("%s는 한장의 카드를 더 받겠습니까?(예는 %s, 아니오는 %s)%n", name, WANT_DRAW_INPUT, WANT_NOT_DRAW_INPUT); + String input = SCANNER.nextLine(); + return isWantDraw(input); + } + + private boolean isWantDraw(String input) { + if (WANT_DRAW_INPUT.equalsIgnoreCase(input)) { + return true; + } + if (WANT_NOT_DRAW_INPUT.equalsIgnoreCase(input)) { + return false; + } + throw new IllegalArgumentException("잘못된 입력입니다. 입력은 (%s/%s) 만 가능합니다." + .formatted(WANT_DRAW_INPUT, WANT_NOT_DRAW_INPUT)); + } +} diff --git a/src/main/java/blackjack/view/OutputView.java b/src/main/java/blackjack/view/OutputView.java new file mode 100644 index 00000000000..c8174e591a9 --- /dev/null +++ b/src/main/java/blackjack/view/OutputView.java @@ -0,0 +1,123 @@ +package blackjack.view; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Shape; +import blackjack.domain.card.Value; +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; +import blackjack.domain.participant.Players; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class OutputView { + + private static final Map SHAPE_NAME = Map.of( + Shape.HEART, "하트", + Shape.SPADE, "스페이드", + Shape.DIAMOND, "다이아몬드", + Shape.CLOVER, "클로버" + ); + + private static final Map VALUE_NAME = Map.ofEntries( + Map.entry(Value.ACE, "A"), Map.entry(Value.TWO, "2"), + Map.entry(Value.THREE, "3"), Map.entry(Value.FOUR, "4"), + Map.entry(Value.FIVE, "5"), Map.entry(Value.SIX, "6"), + Map.entry(Value.SEVEN, "7"), Map.entry(Value.EIGHT, "8"), + Map.entry(Value.NINE, "9"), Map.entry(Value.TEN, "10"), + Map.entry(Value.JACK, "J"), Map.entry(Value.QUEEN, "Q"), + Map.entry(Value.KING, "K") + ); + + public void printStartStatus(Dealer dealer, Players players) { + System.out.println(); + System.out.println("딜러와 " + toPrintedFormat(players) + "에게 2장을 나누었습니다."); + printDealerCards(dealer.getStartCards()); + System.out.println(); + printPlayersCards(players); + System.out.println(); + } + + public void printEndingStatus(Dealer dealer, Players players) { + System.out.println(); + System.out.println(); + printDealerEndingStatus(dealer); + printPlayersEndingStatus(players); + System.out.println(); + } + + private void printDealerCards(List cards) { + System.out.print("딜러 카드: "); + printCards(cards); + } + + private void printPlayersCards(Players players) { + for (Player player : players.getPlayers()) { + printPlayerCards(player.getName(), player.getStartCards()); + System.out.println(); + } + } + + private void printDealerEndingStatus(Dealer dealer) { + printDealerCards(dealer.getCards()); + printScore(dealer.calculateScore()); + System.out.println(); + } + + private void printPlayerCards(String name, List cards) { + System.out.print(name + "카드: "); + printCards(cards); + } + + private void printPlayersEndingStatus(Players players) { + for (Player player : players.getPlayers()) { + printPlayerCards(player.getName(), player.getCards()); + printScore(player.calculateScore()); + System.out.println(); + } + } + + public void printPlayerCards(Player player) { + printPlayerCards(player.getName(), player.getCards()); + System.out.println(); + } + + private void printCards(List cards) { + String printingFormat = cards.stream() + .map(this::toPrintedFormat) + .collect(Collectors.joining(", ")); + System.out.print(printingFormat); + } + + private void printScore(int score) { + System.out.print(" - 결과 : " + score); + } + + private String toPrintedFormat(Players players) { + return players.getPlayers().stream() + .map(Player::getName) + .collect(Collectors.joining(", ")); + } + + private String toPrintedFormat(Card card) { + return VALUE_NAME.get(card.getValue()) + SHAPE_NAME.get(card.getShape()); + } + + public void printDealerDraw() { + System.out.println(); + System.out.print("딜러는 16이하라 한장의 카드를 더 받았습니다."); + } + + public void printDealerMatchResult(int winCount, int loseCount) { + System.out.println("## 최종 승패"); + System.out.printf("딜러 : %d승 %d패%n", winCount, loseCount); + } + + public void printPlayerMatchResult(String name, boolean isWin) { + if (isWin) { + System.out.println(name + ": 승"); + return; + } + System.out.println(name + ": 패"); + } +} diff --git a/src/test/java/.gitkeep b/src/test/java/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/test/java/blackjack/domain/card/CardTest.java b/src/test/java/blackjack/domain/card/CardTest.java new file mode 100644 index 00000000000..3790c5041e7 --- /dev/null +++ b/src/test/java/blackjack/domain/card/CardTest.java @@ -0,0 +1,54 @@ +package blackjack.domain.card; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.EnumSource; + +public class CardTest { + + @DisplayName("카드 값에 따라 점수를 확인할 수 있다") + @Nested + class CardScoreTest { + + @DisplayName("숫자 카드는 카드의 숫자 값을 점수로 가진다") + @ParameterizedTest + @CsvSource({"TWO, 2", "TEN, 10"}) + void scoreTest_whenNumberCard(Value value, int expected) { + Card card = new Card(value, Shape.CLOVER); + + assertAll( + () -> assertThat(card.getMinScore()).isEqualTo(expected), + () -> assertThat(card.getMaxScore()).isEqualTo(expected) + ); + } + + @DisplayName("Jack, Queen, King 카드는 모두 10점을 가진다.") + @ParameterizedTest + @EnumSource(names = {"JACK", "QUEEN", "KING"}) + void scoreTest_whenJQK_Card(Value value) { + Card card = new Card(value, Shape.HEART); + + assertAll( + () -> assertThat(card.getMinScore()).isEqualTo(10), + () -> assertThat(card.getMaxScore()).isEqualTo(10) + ); + } + + @DisplayName("Ace 카드는 1, 또는 11점을 가진다.") + @Test + void scoreTest_whenAceCard() { + Card card = new Card(Value.ACE, Shape.HEART); + + assertAll( + () -> assertThat(card.getMinScore()).isEqualTo(1), + () -> assertThat(card.getMaxScore()).isEqualTo(11) + ); + } + } +} diff --git a/src/test/java/blackjack/domain/card/DeckTest.java b/src/test/java/blackjack/domain/card/DeckTest.java new file mode 100644 index 00000000000..d28ee2e53ea --- /dev/null +++ b/src/test/java/blackjack/domain/card/DeckTest.java @@ -0,0 +1,35 @@ +package blackjack.domain.card; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class DeckTest { + + @DisplayName("카드 한 장을 뽑을 수 있다") + @Test + void drawTest() { + Deck deck = Deck.createShuffledDeck(); + + assertThat(deck.draw()).isNotNull(); + } + + @DisplayName("카드는 최대 52장만 뽑을 수 있다.") + @Test + void drawTest_drawTooManyTimes_throwException() { + Deck deck = Deck.createShuffledDeck(); + drawRepeat(deck, 52); + + assertThatThrownBy(() -> deck.draw()) + .isInstanceOf(IllegalStateException.class) + .hasMessage("카드를 더 이상 뽑을 수 없습니다."); + } + + private void drawRepeat(Deck deck, int times) { + for (int i = 0; i < times; i++) { + deck.draw(); + } + } +} diff --git a/src/test/java/blackjack/domain/card/HandTest.java b/src/test/java/blackjack/domain/card/HandTest.java new file mode 100644 index 00000000000..e5b09d9274a --- /dev/null +++ b/src/test/java/blackjack/domain/card/HandTest.java @@ -0,0 +1,128 @@ +package blackjack.domain.card; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.fixture.CardsFixture; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class HandTest { + + @DisplayName("점수를 계산할 수 있다.") + @ParameterizedTest + @MethodSource("cardsAndScore") + void calculateScoreTest(List cards, int expected) { + Hand hand = new Hand(cards); + + assertThat(hand.calculateScore()).isEqualTo(expected); + } + + static Stream cardsAndScore() { + return Stream.of(Arguments.of(CardsFixture.BLACKJACK, 21), + Arguments.of(CardsFixture.TWO_ACE, 12), + Arguments.of(CardsFixture.CARDS_SCORE_16, 16)); + } + + @DisplayName("카드를 한 장 뽑는다") + @Test + void addTest() { + Hand hand = new Hand(CardsFixture.CARDS_SCORE_16); + + Card additionalCard = new Card(Value.ACE, Shape.HEART); + hand.add(additionalCard); + + assertThat(hand.getCards()) + .containsAll(CardsFixture.CARDS_SCORE_16) + .contains(additionalCard) + .hasSize(CardsFixture.CARDS_SCORE_16.size() + 1); + } + + @DisplayName("카드의 버스트 상태를 알 수 있다.") + @Nested + class BustTest { + + @DisplayName("21점이 넘으면 버스트이다.") + @Test + void whenBusted_returnTrue() { + Hand bustedHand = new Hand(CardsFixture.CARDS_SCORE_22); + + assertThat(bustedHand.isBusted()).isTrue(); + } + + @DisplayName("21점 이하 점수는 버스트가 아니다.") + @Test + void whenNotBusted_returnFalse() { + Hand hand = new Hand(CardsFixture.CARDS_SCORE_21); + + assertThat(hand.isBusted()).isFalse(); + } + } + + @DisplayName("카드의 블랙잭 상태를 판단할 수 있다.") + @Nested + class BlackjackTest { + + @DisplayName("21점이면서 2장의 카드라면 블랙잭이다.") + @Test + void whenBlackjack_returnTrue() { + Hand blackjackHand = new Hand(CardsFixture.BLACKJACK); + + assertThat(blackjackHand.isBlackjack()).isTrue(); + } + + @DisplayName("21점 미만 점수는 블랙잭이 아니다.") + @Test + void whenUnderScore_returnFalse() { + Hand blackjackHand = new Hand(List.of( + new Card(Value.KING, Shape.HEART), + new Card(Value.QUEEN, Shape.HEART) + )); + + assertThat(blackjackHand.isBlackjack()).isFalse(); + } + + @DisplayName("21점이지만 3장의 카드라면 블랙잭이 아니다.") + @Test + void whenOverSize_returnFalse() { + Hand blackjackHand = new Hand(CardsFixture.CARDS_SCORE_21); + + assertThat(blackjackHand.isBlackjack()).isFalse(); + } + + @DisplayName("21점 초과 점수는 블랙잭이 아니다.") + @Test + void whenOverScore_returnFalse() { + Hand blackjackHand = new Hand(CardsFixture.CARDS_SCORE_22); + + assertThat(blackjackHand.isBlackjack()).isFalse(); + } + } + + @DisplayName("빈 손패인지 확인할 수 있다.") + @Nested + class EmptyHandTest { + + @DisplayName("아무 카드도 존재하지 않으면 빈 손패이다.") + @Test + void whenEmptyHand_returnTrue() { + Hand emptyHand = new Hand(Collections.emptyList()); + + assertThat(emptyHand.isEmpty()).isTrue(); + } + + @DisplayName("카드가 한 장이라도 존재하면 빈 손패가 아니다.") + @Test + void whenCardExists_returnFalse() { + Hand emptyHand = new Hand(List.of(new Card(Value.ACE, Shape.HEART))); + + assertThat(emptyHand.isEmpty()).isFalse(); + } + } +} diff --git a/src/test/java/blackjack/domain/fixture/CardsFixture.java b/src/test/java/blackjack/domain/fixture/CardsFixture.java new file mode 100644 index 00000000000..8005fbde325 --- /dev/null +++ b/src/test/java/blackjack/domain/fixture/CardsFixture.java @@ -0,0 +1,44 @@ +package blackjack.domain.fixture; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Shape; +import blackjack.domain.card.Value; +import java.util.List; + +public class CardsFixture { + public static final List CARDS_SCORE_4 = List.of( + new Card(Value.TWO, Shape.HEART), + new Card(Value.TWO, Shape.SPADE) + ); + public static final List TWO_ACE = List.of( + new Card(Value.ACE, Shape.HEART), + new Card(Value.ACE, Shape.SPADE) + ); + public static final List CARDS_SCORE_16 = List.of( + new Card(Value.JACK, Shape.HEART), + new Card(Value.SIX, Shape.HEART) + ); + public static final List CARDS_SCORE_17 = List.of( + new Card(Value.JACK, Shape.HEART), + new Card(Value.SEVEN, Shape.HEART) + ); + public static final List CARDS_SCORE_21 = List.of( + new Card(Value.JACK, Shape.HEART), + new Card(Value.EIGHT, Shape.HEART), + new Card(Value.THREE, Shape.HEART) + ); + public static final List BLACKJACK = List.of( + new Card(Value.ACE, Shape.HEART), + new Card(Value.KING, Shape.HEART) + ); + public static final List CARDS_SCORE_22 = List.of( + new Card(Value.JACK, Shape.HEART), + new Card(Value.SEVEN, Shape.HEART), + new Card(Value.FIVE, Shape.HEART) + ); + public static final List BUSTED = List.of( + new Card(Value.KING, Shape.DIAMOND), + new Card(Value.QUEEN, Shape.DIAMOND), + new Card(Value.JACK, Shape.DIAMOND) + ); +} diff --git a/src/test/java/blackjack/domain/participant/DealerTest.java b/src/test/java/blackjack/domain/participant/DealerTest.java new file mode 100644 index 00000000000..f0e911ff9de --- /dev/null +++ b/src/test/java/blackjack/domain/participant/DealerTest.java @@ -0,0 +1,198 @@ +package blackjack.domain.participant; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Deck; +import blackjack.domain.card.Shape; +import blackjack.domain.card.Value; +import blackjack.domain.fixture.CardsFixture; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class DealerTest { + private static final Name DEFAULT_NAME = new Name("name"); + + @DisplayName("카드의 총 점수가 16을 넘지 않으면, 카드를 더 뽑을 수 있다") + @Test + void isDrawableTest_whenScoreIsUnderBound_returnTrue() { + Dealer dealer = new Dealer(CardsFixture.CARDS_SCORE_16); + + assertThat(dealer.isDrawable()).isTrue(); + } + + @DisplayName("카드의 총 점수가 17을 넘으면, 카드를 더 뽑을 수 없다") + @Test + void isDrawableTest_whenScoreIsOverBound_returnFalse() { + Dealer dealer = new Dealer(CardsFixture.CARDS_SCORE_17); + + assertThat(dealer.isDrawable()).isFalse(); + } + + @DisplayName("점수를 계산할 수 있다.") + @ParameterizedTest + @MethodSource("cardsAndScore") + void calculateScoreTest(List cards, int expected) { + Dealer dealer = new Dealer(cards); + + assertThat(dealer.calculateScore()).isEqualTo(expected); + } + + static Stream cardsAndScore() { + return Stream.of( + Arguments.of(CardsFixture.BLACKJACK, 21), + Arguments.of(CardsFixture.TWO_ACE, 12), + Arguments.of(CardsFixture.CARDS_SCORE_16, 16) + ); + } + + @DisplayName("게임을 시작할 때는 카드를 두 장 뽑는다.") + @Test + void drawStartCardsTest() { + Dealer dealer = new Dealer(); + Deck deck = Deck.createShuffledDeck(); + + dealer.drawStartCards(deck); + + assertThat(dealer.getCards()).hasSize(2); + } + + @DisplayName("이미 카드를 가지고 있는 경우, 시작 카드를 뽑을 수 없다.") + @Test + void drawStartCardsTest_whenAlreadyStarted_throwException() { + Dealer dealer = new Dealer(CardsFixture.CARDS_SCORE_16); + Deck deck = Deck.createShuffledDeck(); + + assertThatThrownBy(() -> dealer.drawStartCards(deck)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("이미 시작 카드를 뽑았습니다."); + } + + @DisplayName("카드의 총 점수가 16을 넘지 않으면, 카드를 한 장 뽑는다") + @Test + void addTest_whenScoreIsUnderBound() { + Dealer dealer = new Dealer(CardsFixture.CARDS_SCORE_16); + Card additionalCard = new Card(Value.ACE, Shape.HEART); + + dealer.add(additionalCard); + + assertThat(dealer.getCards()) + .containsAll(CardsFixture.CARDS_SCORE_16) + .contains(additionalCard) + .hasSize(3); + } + + @DisplayName("카드의 총 점수가 16을 넘으면, 카드를 뽑을 때 예외가 발생한다.") + @Test + void addTest_whenScoreIsOverBound_throwException() { + Dealer dealer = new Dealer(CardsFixture.CARDS_SCORE_17); + Card card = new Card(Value.ACE, Shape.HEART); + + assertThatThrownBy(() -> dealer.add(card)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("더 이상 카드를 추가할 수 없습니다."); + } + + @DisplayName("플레이어와의 승패를 판단할 수 있다.") + @Nested + class IsWinTest { + + @DisplayName("플레이어가 21을 넘을 경우, 딜러가 이긴다.") + @ParameterizedTest + @MethodSource("dealerCards") + void whenPlayerBusted_dealerWin(List cards) { + Dealer dealer = new Dealer(cards); + Player player = new Player(CardsFixture.BUSTED, DEFAULT_NAME); + + assertThat(dealer.isWin(player)).isTrue(); + } + + static Stream> dealerCards() { + return Stream.of( + CardsFixture.BLACKJACK, + CardsFixture.CARDS_SCORE_4, + CardsFixture.CARDS_SCORE_16, + CardsFixture.BUSTED + ); + } + + @DisplayName("딜러만 21을 넘길 경우, 플레이어가 이긴다.") + @ParameterizedTest + @MethodSource("playerCards") + void whenOnlyDealerBusted_playerWin(List cards) { + Dealer dealer = new Dealer(CardsFixture.BUSTED); + Player player = new Player(cards, DEFAULT_NAME); + + assertThat(dealer.isWin(player)).isFalse(); + } + + static Stream> playerCards() { + return Stream.of( + CardsFixture.BLACKJACK, + CardsFixture.CARDS_SCORE_4, + CardsFixture.CARDS_SCORE_16 + ); + } + + @DisplayName("플레이어만 블랙잭일 경우, 플레이어가 이긴다.") + @Test + void whenPlayerOnlyBlackjack_playerWin() { + Player player = new Player(CardsFixture.BLACKJACK, DEFAULT_NAME); + Dealer dealer = new Dealer(CardsFixture.CARDS_SCORE_21); + + assertThat(dealer.isWin(player)).isFalse(); + } + + @DisplayName("딜러만 블랙잭일 경우, 딜러가 이긴다.") + @Test + void whenDealerOnlyBlackjack_dealerWin() { + Player player = new Player(CardsFixture.CARDS_SCORE_21, DEFAULT_NAME); + Dealer dealer = new Dealer(CardsFixture.BLACKJACK); + + assertThat(dealer.isWin(player)).isTrue(); + } + + @DisplayName("둘 다 21을 넘지 않을 경우, 플레이어가 딜러의 숫자보다 같다면 딜러가 이긴다.") + @ParameterizedTest + @MethodSource("sameCards") + void whenPlayerScoreIsEqualToDealerScore_dealerWin(List cards) { + Player player = new Player(cards, DEFAULT_NAME); + Dealer dealer = new Dealer(cards); + + assertThat(dealer.isWin(player)).isTrue(); + } + + static Stream> sameCards() { + return Stream.of( + CardsFixture.BLACKJACK, + CardsFixture.CARDS_SCORE_21, + CardsFixture.CARDS_SCORE_16 + ); + } + + @DisplayName("둘 다 21보다 작을 경우, 플레이어가 딜러의 숫자보다 크다면 플레이어가 이긴다.") + @Test + void whenPlayerScoreIsBiggerThanDealerScore_playerWin() { + Player player = new Player(CardsFixture.CARDS_SCORE_17, DEFAULT_NAME); + Dealer dealer = new Dealer(CardsFixture.CARDS_SCORE_16); + + assertThat(dealer.isWin(player)).isFalse(); + } + + @DisplayName("둘 다 21보다 작을 경우, 플레이어가 딜러의 숫자보다 작다면 딜러가 이긴다.") + @Test + void whenPlayerScoreIsSmallerThanDealerScore_dealerWin() { + Player player = new Player(CardsFixture.CARDS_SCORE_16, DEFAULT_NAME); + Dealer dealer = new Dealer(CardsFixture.CARDS_SCORE_17); + + assertThat(dealer.isWin(player)).isTrue(); + } + } +} diff --git a/src/test/java/blackjack/domain/participant/NameTest.java b/src/test/java/blackjack/domain/participant/NameTest.java new file mode 100644 index 00000000000..486850bbef3 --- /dev/null +++ b/src/test/java/blackjack/domain/participant/NameTest.java @@ -0,0 +1,32 @@ +package blackjack.domain.participant; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +class NameTest { + + @DisplayName("이름은 적어도 한 글자를 가져야 한다.") + @ParameterizedTest + @NullAndEmptySource + @ValueSource(strings = {" ", "\n"}) + void validateTest_whenNameIsEmpty_throwException(String name) { + + assertThatThrownBy(() -> new Name(name)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이름은 적어도 한 글자 이상을 포함해야 합니다."); + } + + @DisplayName("이름의 앞 뒤 공백을 제거해준다.") + @ParameterizedTest + @ValueSource(strings = {" pobi", "pobi ", " pobi ", "\npobi\t"}) + void validateTest_nameHasStrippedName(String input) { + Name name = new Name(input); + + assertThat(name.getName()).isEqualTo("pobi"); + } +} diff --git a/src/test/java/blackjack/domain/participant/PlayerTest.java b/src/test/java/blackjack/domain/participant/PlayerTest.java new file mode 100644 index 00000000000..5eb9cd71901 --- /dev/null +++ b/src/test/java/blackjack/domain/participant/PlayerTest.java @@ -0,0 +1,125 @@ +package blackjack.domain.participant; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Deck; +import blackjack.domain.card.Shape; +import blackjack.domain.card.Value; +import blackjack.domain.fixture.CardsFixture; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class PlayerTest { + + private static final Name DEFAULT_NAME = new Name("name"); + + @DisplayName("점수를 계산할 수 있다.") + @ParameterizedTest + @MethodSource("cardsAndScore") + void calculateScoreTest(List cards, int expected) { + Player player = new Player(cards, DEFAULT_NAME); + + assertThat(player.calculateScore()).isEqualTo(expected); + } + + static Stream cardsAndScore() { + return Stream.of( + Arguments.of(CardsFixture.BLACKJACK, 21), + Arguments.of(CardsFixture.TWO_ACE, 12), + Arguments.of(CardsFixture.CARDS_SCORE_16, 16) + ); + } + + @DisplayName("ACE가 11로 계산되었을 때 버스트가 되지 않는다면, ACE를 그대로 계산한다.") + @Test + void calculateScoreTest_usingAceMaxScoreCase() { + Player player = new Player(List.of( + new Card(Value.ACE, Shape.HEART), + new Card(Value.TWO, Shape.HEART) + ), DEFAULT_NAME); + + assertThat(player.calculateScore()).isEqualTo(13); + } + + @DisplayName("ACE가 11로 계산되었을 때 버스트가 된다면, ACE를 1로 계산한다.") + @Test + void calculateScoreTest_usingAceMinScoreCase() { + Player player = new Player(List.of( + new Card(Value.ACE, Shape.HEART), + new Card(Value.KING, Shape.HEART), + new Card(Value.TWO, Shape.HEART) + ), DEFAULT_NAME); + + assertThat(player.calculateScore()).isEqualTo(13); + } + + @DisplayName("카드의 총 점수가 21을 넘지 않으면, 카드를 더 뽑을 수 있다") + @Test + void isDrawableTest_whenScoreIsUnderBound_returnTrue() { + Player player = new Player(CardsFixture.CARDS_SCORE_21, DEFAULT_NAME); + + assertThat(player.isDrawable()).isTrue(); + } + + @DisplayName("카드의 총 점수가 21을 넘으면, 카드를 더 뽑을 수 없다") + @Test + void isDrawableTest_whenScoreIsOverBound_returnFalse() { + Player player = new Player(CardsFixture.CARDS_SCORE_22, DEFAULT_NAME); + + assertThat(player.isDrawable()).isFalse(); + } + + @DisplayName("게임을 시작할 때는 카드를 두 장 뽑는다.") + @Test + void drawStartCardsTest() { + Player player = Player.from("name"); + Deck deck = Deck.createShuffledDeck(); + + player.drawStartCards(deck); + + assertThat(player.getCards()).hasSize(2); + } + + @DisplayName("이미 카드를 가지고 있는 경우, 시작 카드를 뽑을 수 없다.") + @Test + void drawStartCardsTest_whenAlreadyStarted_throwException() { + Player player = new Player(CardsFixture.CARDS_SCORE_16, DEFAULT_NAME); + Deck deck = Deck.createShuffledDeck(); + + assertThatThrownBy(() -> player.drawStartCards(deck)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("이미 시작 카드를 뽑았습니다."); + } + + @DisplayName("카드의 총 점수가 21을 넘지 않으면, 카드를 한 장 뽑는다") + @Test + void addTest_whenScoreIsUnderBound() { + Player player = new Player(CardsFixture.CARDS_SCORE_21, DEFAULT_NAME); + + Card additionalCard = new Card(Value.ACE, Shape.HEART); + player.add(additionalCard); + + assertThat(player.getCards()) + .containsAll(CardsFixture.CARDS_SCORE_21) + .contains(additionalCard) + .hasSize(4); + } + + @DisplayName("카드의 총 점수가 21을 넘으면, 카드를 뽑을 때 예외가 발생한다.") + @Test + void addTest_whenScoreIsOverBound_throwException() { + Player player = new Player(CardsFixture.CARDS_SCORE_22, DEFAULT_NAME); + Card card = new Card(Value.ACE, Shape.HEART); + + assertThatThrownBy(() -> player.add(card)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("더 이상 카드를 추가할 수 없습니다."); + } +} diff --git a/src/test/java/blackjack/domain/participant/PlayersTest.java b/src/test/java/blackjack/domain/participant/PlayersTest.java new file mode 100644 index 00000000000..a776e175789 --- /dev/null +++ b/src/test/java/blackjack/domain/participant/PlayersTest.java @@ -0,0 +1,57 @@ +package blackjack.domain.participant; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import blackjack.domain.card.Deck; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PlayersTest { + + @DisplayName("최소 한 명 이상의 플레이어가 존재해야 한다.") + @Test + void validateTest_countOfPlayersIsZero_throwException() { + assertThatThrownBy(() -> Players.from(Collections.emptyList())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("최소 한 명의 플레이어가 있어야 합니다."); + } + + @DisplayName("N 명 이상의 플레이어를 가지면 예외가 발생한다.") + @Test + void validateTest_tooManyPlayers_throwException() { + List manyNames = List.of("1", "2", "3", "4", "5"); + + assertThatThrownBy(() -> Players.from(manyNames)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("최대 4명의 플레이어만 참여 가능합니다."); + } + + @DisplayName("중복된 이름을 사용하면, 예외가 발생한다.") + @Test + void validateTest_whenNameIsDuplicated_throwException() { + List duplicatedNames = List.of("짱수", "커찬", "커찬"); + + assertThatThrownBy(() -> Players.from(duplicatedNames)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("중복된 이름을 사용할 수 없습니다."); + } + + @DisplayName("게임을 시작할 때 모든 플레이어들은 카드를 두 장 뽑는다.") + @Test + void drawStartCardsTest() { + Players players = Players.from(List.of("1", "2")); + Deck deck = Deck.createShuffledDeck(); + + players.drawStartCards(deck); + + List allPlayers = players.getPlayers(); + assertAll( + () -> assertThat(allPlayers.get(0).getCards()).hasSize(2), + () -> assertThat(allPlayers.get(1).getCards()).hasSize(2) + ); + } +} From a2adac39dbf522e069e655bd54b3dab95e88f695 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 10:56:39 +0900 Subject: [PATCH 02/41] =?UTF-8?q?refactor=20(Player)=20:=20`equals()`=20?= =?UTF-8?q?=EC=9D=98=EB=AF=B8=EC=97=90=20=EC=98=A4=ED=95=B4=EB=A5=BC=20?= =?UTF-8?q?=EB=B6=88=EB=9F=AC=EC=9D=BC=EC=9C=BC=ED=82=AC=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EC=96=B4,=20equals()=EB=A5=BC=20=EC=A0=9C=EA=B1=B0?= =?UTF-8?q?=ED=95=98=EA=B3=A0=20isEqualName()=20=EC=9D=84=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/domain/participant/Player.java | 22 +++++-------------- .../blackjack/domain/participant/Players.java | 9 +++++++- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/main/java/blackjack/domain/participant/Player.java b/src/main/java/blackjack/domain/participant/Player.java index a96d63e9e8a..85ad34cbe67 100644 --- a/src/main/java/blackjack/domain/participant/Player.java +++ b/src/main/java/blackjack/domain/participant/Player.java @@ -8,6 +8,7 @@ public class Player extends Participant { private static final int START_CARD_SIZE = 2; + private final Name name; Player(List cards, Name name) { @@ -23,6 +24,10 @@ public boolean isWin(Dealer dealer) { return !dealer.isWin(this); } + public boolean isEqualName(Player player) { + return name.equals(player.name); + } + @Override protected int getMaxDrawableScore() { return BLACKJACK_SCORE; @@ -36,21 +41,4 @@ protected int getStartCardSize() { public String getName() { return name.getName(); } - - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object == null || getClass() != object.getClass()) { - return false; - } - Player player = (Player) object; - return Objects.equals(name, player.name); - } - - @Override - public int hashCode() { - return Objects.hash(name); - } } diff --git a/src/main/java/blackjack/domain/participant/Players.java b/src/main/java/blackjack/domain/participant/Players.java index ca19558f47d..dd98556ac03 100644 --- a/src/main/java/blackjack/domain/participant/Players.java +++ b/src/main/java/blackjack/domain/participant/Players.java @@ -38,7 +38,14 @@ private void validateDistinct(List players) { } private boolean isDuplicated(List players) { - return players.size() != players.stream().distinct().count(); + return players.stream() + .anyMatch(player1 -> isDuplicated(players, player1)); + } + + private boolean isDuplicated(List players, Player player) { + return players.stream() + .filter(another -> player != another) + .anyMatch(another -> player.isEqualName(another)); } public void drawStartCards(Deck deck) { From 83151343d9472f9cdde9d0ccbb147bb7aa641576 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 11:02:12 +0900 Subject: [PATCH 03/41] =?UTF-8?q?refactor=20(Players)=20:=20=EB=B3=B5?= =?UTF-8?q?=EC=9E=A1=EB=8F=84=EB=A5=BC=20=EC=A6=9D=EA=B0=80=EC=8B=9C?= =?UTF-8?q?=ED=82=A4=EB=8A=94=20play()=20=ED=95=A8=EC=88=98=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Controller에서는 getPlayers()를 이용하여 각 플레이어의 턴을 진행 --- src/main/java/blackjack/BlackJackGame.java | 15 +++++++++++---- .../blackjack/domain/participant/Players.java | 6 ------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/blackjack/BlackJackGame.java b/src/main/java/blackjack/BlackJackGame.java index 9612c621328..8acbbc3330a 100644 --- a/src/main/java/blackjack/BlackJackGame.java +++ b/src/main/java/blackjack/BlackJackGame.java @@ -17,6 +17,7 @@ public void run() { Deck deck = Deck.createShuffledDeck(); Dealer dealer = new Dealer(); Players players = createPlayers(); + drawStartCards(dealer, players, deck); play(players, dealer, deck); printResult(dealer, players); @@ -34,11 +35,10 @@ private void drawStartCards(Dealer dealer, Players players, Deck deck) { } private void play(Players players, Dealer dealer, Deck deck) { - players.play(this::playTurn, deck); - while (dealer.isDrawable()) { - outputView.printDealerDraw(); - dealer.add(deck.draw()); + for (Player player : players.getPlayers()) { + playTurn(player, deck); } + playTurn(dealer, deck); } private void playTurn(Player player, Deck deck) { @@ -48,6 +48,13 @@ private void playTurn(Player player, Deck deck) { } } + private void playTurn(Dealer dealer, Deck deck) { + while (dealer.isDrawable()) { + outputView.printDealerDraw(); + dealer.add(deck.draw()); + } + } + private void printResult(Dealer dealer, Players players) { outputView.printEndingStatus(dealer, players); int winCount = dealer.calculateWinCount(players); diff --git a/src/main/java/blackjack/domain/participant/Players.java b/src/main/java/blackjack/domain/participant/Players.java index dd98556ac03..365d19bc0af 100644 --- a/src/main/java/blackjack/domain/participant/Players.java +++ b/src/main/java/blackjack/domain/participant/Players.java @@ -57,10 +57,4 @@ public void drawStartCards(Deck deck) { public List getPlayers() { return players; } - - public void play(PlayerTurn playTurn, Deck deck) { - for (Player player : players) { - playTurn.play(player, deck); - } - } } From 9ff2eb707d53be41c404733c2894441016e42307 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 11:27:47 +0900 Subject: [PATCH 04/41] =?UTF-8?q?docs=20(README)=20:=202=EB=8B=A8=EA=B3=84?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=B6=94=EA=B0=80=EB=90=9C=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=20=EC=82=AC=ED=95=AD=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7d9853dca80..ba538864d25 100644 --- a/README.md +++ b/README.md @@ -18,24 +18,41 @@ - 이름은 앞뒤 공백을 가질 수 없다. - 중복된 이름은 가질 수 없다. -### 승리 조건 +### 카드 승패에 따른 플레이어의 수익 -- 카드 숫자를 합쳐 가능한 21에 가깝게 만들면 이긴다. - - 처음 받은 2장 합쳐 21이 나오는 경우 블랙잭이 되며, 모든 카드조합 중에 가장 강력하다. - - 카드 숫자의 합이 21을 초과하게 되는 순간 '버스트'라고 하며 딜러의 결과에 관계없이 플레이어가 패배한다. - - 딜러와 플레이어의 점수와 블랙잭 여부가 동일한 경우, 딜러가 승리한다. +- 플레이어가 배팅 금액의 1.5배를 얻는 경우 + - 플레이어가 '블랙잭'이고, 딜러가 블랙잭이 아닌 경우 +- 플레이어가 배팅 금액만큼 얻는 경우 + - 플레이어가 '블랙잭'이 아니면서, 딜러보다 점수가 높은 경우 +- 플레이어의 수익이 0인 경우 + - 플레이어, 딜러 모두 '블랙잭'인 경우 + - 플레이어, 딜러 모두 '블랙잭'이 아니면서, +- 플레이러가 배팅 금액만큼 잃는 경우 + - + +#### 딜러의 수익 + +- 플레이어가 수익을 얻는 만큼 잃는다. +- 플레이어가 수익을 잃은 만큼 얻는다. +- 모든 플레이어와 비교한 뒤에, 딜러가 얻은 수익들을 모두 더한다. + +#### 관련 용어 + +- 블랙잭 : 카드 2장 만으로 21점을 만든 경우 (ex. A스페이드, K스페이드) +- 버스트 : 카드 점수의 합이 21을 초과한 경우 (ex. K스페이드, K다이아몬드, 2스페이드) ### 프로그램 진행 순서 1. 참가자 이름 입력한다. -2. 카드를 2장씩 나눠준 후에, 현재 카드 상태 출력한다. -3. 각 참가자들의 턴을 진행한다. +2. 각 참가자의 배팅 금액을 입력받는다. +3. 카드를 2장씩 나눠준 후에, 현재 카드 상태 출력한다. (딜러는 1장만 출력) +4. 각 참가자들의 턴을 진행한다. 1. 카드가 21을 넘은 경우, 턴이 종료된다. 2. 카드를 더 받을 지 여부를 입력한다. 3. 만약 더 받는다면, 카드를 한 장 추가하고 1으로 돌아간다. 4. 더 받지 않을 경우, 턴을 종료한다. -4. 딜러의 턴을 진행한다. +5. 딜러의 턴을 진행한다. 1. 카드가 16을 넘은 경우, 턴이 종료된다. 2. 카드를 한 장 더 추가하고, 1로 돌아간다. -5. 딜러 및 모든 참가자의 보유 카드들과 점수를 출력한다. -6. 딜러 및 모든 참가자의 승패를 출력한다. +6. 딜러 및 모든 참가자의 보유 카드들과 점수를 출력한다. +7. 딜러 및 모든 참가자의 최종 수익을 출력한다. From 489c68530439985bcf58edadb97dbb32fbd4f084 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 11:49:00 +0900 Subject: [PATCH 05/41] =?UTF-8?q?refactor=20:=20=EB=8D=94=20=EC=9D=B4?= =?UTF-8?q?=EC=83=81=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/blackjack/domain/participant/PlayerTurn.java | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 src/main/java/blackjack/domain/participant/PlayerTurn.java diff --git a/src/main/java/blackjack/domain/participant/PlayerTurn.java b/src/main/java/blackjack/domain/participant/PlayerTurn.java deleted file mode 100644 index 53ae62cc63c..00000000000 --- a/src/main/java/blackjack/domain/participant/PlayerTurn.java +++ /dev/null @@ -1,8 +0,0 @@ -package blackjack.domain.participant; - -import blackjack.domain.card.Deck; - -@FunctionalInterface -public interface PlayerTurn { - void play(Player player, Deck deck); -} From 150cc56aad16486ea47e46a905430809182f1f4f Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 11:51:48 +0900 Subject: [PATCH 06/41] =?UTF-8?q?feat=20(Money)=20:=20=EB=8F=88=EC=9D=80?= =?UTF-8?q?=20=ED=95=AD=EC=83=81=20=EC=96=91=EC=88=98=EB=A7=8C=20=EA=B0=80?= =?UTF-8?q?=EC=A7=80=EB=8F=84=EB=A1=9D=20=EA=B2=80=EC=A6=9D=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/blackjack/domain/money/Money.java | 17 ++++++++++++++++ .../blackjack/domain/money/MoneyTest.java | 20 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/main/java/blackjack/domain/money/Money.java create mode 100644 src/test/java/blackjack/domain/money/MoneyTest.java diff --git a/src/main/java/blackjack/domain/money/Money.java b/src/main/java/blackjack/domain/money/Money.java new file mode 100644 index 00000000000..f8da3391c60 --- /dev/null +++ b/src/main/java/blackjack/domain/money/Money.java @@ -0,0 +1,17 @@ +package blackjack.domain.money; + +public class Money { + + private final int value; + + public Money(int value) { + validatePositive(value); + this.value = value; + } + + private void validatePositive(int value) { + if (value <= 0) { + throw new IllegalArgumentException("금액은 양수이어야 합니다."); + } + } +} diff --git a/src/test/java/blackjack/domain/money/MoneyTest.java b/src/test/java/blackjack/domain/money/MoneyTest.java new file mode 100644 index 00000000000..f379f33d931 --- /dev/null +++ b/src/test/java/blackjack/domain/money/MoneyTest.java @@ -0,0 +1,20 @@ +package blackjack.domain.money; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class MoneyTest { + + @DisplayName("금액은 0 또는 음수가 될 수 없다") + @ParameterizedTest + @ValueSource(ints = {0, -1, -1000}) + void validateTest_whenValueIsNotPositive_throwException(int value) { + + assertThatThrownBy(() -> new Money(value)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("금액은 양수이어야 합니다."); + } +} From a121d1a2d64242bc66a516f5cd9ca2cd6242674a Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 13:02:31 +0900 Subject: [PATCH 07/41] =?UTF-8?q?feat=20(Monet)=20:=20=ED=95=B4=EB=8B=B9?= =?UTF-8?q?=20=EB=B0=B0=EC=88=98=EB=A7=8C=ED=81=BC=20=EA=B3=B1=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/money/Money.java | 9 +++++++++ .../java/blackjack/domain/money/MoneyTest.java | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/main/java/blackjack/domain/money/Money.java b/src/main/java/blackjack/domain/money/Money.java index f8da3391c60..621a6591431 100644 --- a/src/main/java/blackjack/domain/money/Money.java +++ b/src/main/java/blackjack/domain/money/Money.java @@ -14,4 +14,13 @@ private void validatePositive(int value) { throw new IllegalArgumentException("금액은 양수이어야 합니다."); } } + + public int toInt() { + return value; + } + + public Money multiply(double multiplier) { + int newValue = (int) Math.ceil(this.value * multiplier); + return new Money(newValue); + } } diff --git a/src/test/java/blackjack/domain/money/MoneyTest.java b/src/test/java/blackjack/domain/money/MoneyTest.java index f379f33d931..be69404d38e 100644 --- a/src/test/java/blackjack/domain/money/MoneyTest.java +++ b/src/test/java/blackjack/domain/money/MoneyTest.java @@ -1,9 +1,11 @@ package blackjack.domain.money; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; class MoneyTest { @@ -17,4 +19,16 @@ void validateTest_whenValueIsNotPositive_throwException(int value) { .isInstanceOf(IllegalArgumentException.class) .hasMessage("금액은 양수이어야 합니다."); } + + @DisplayName("금액은 해당 배수만큼 곱할 수 있다") + @ParameterizedTest + @CsvSource({"1.5, 1500", "2, 2000", "2.25, 2250"}) + void multiplyTest(double multiplier, int expected) { + Money money = new Money(1000); + + Money actual = money.multiply(multiplier); + + assertThat(actual.toInt()).isEqualTo(expected); + } + } From 106c03691ae09c47bfed2ff1a04716807ca23a12 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 13:05:47 +0900 Subject: [PATCH 08/41] =?UTF-8?q?feat=20(Money)=20:=20=EA=B8=88=EC=95=A1?= =?UTF-8?q?=EB=81=BC=EB=A6=AC=20=EC=84=9C=EB=A1=9C=20=EB=8D=94=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/money/Money.java | 4 ++++ src/test/java/blackjack/domain/money/MoneyTest.java | 13 ++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/blackjack/domain/money/Money.java b/src/main/java/blackjack/domain/money/Money.java index 621a6591431..34904ef5e40 100644 --- a/src/main/java/blackjack/domain/money/Money.java +++ b/src/main/java/blackjack/domain/money/Money.java @@ -19,6 +19,10 @@ public int toInt() { return value; } + public Money add(Money money) { + return new Money(this.value + money.value); + } + public Money multiply(double multiplier) { int newValue = (int) Math.ceil(this.value * multiplier); return new Money(newValue); diff --git a/src/test/java/blackjack/domain/money/MoneyTest.java b/src/test/java/blackjack/domain/money/MoneyTest.java index be69404d38e..6c3c501c5e8 100644 --- a/src/test/java/blackjack/domain/money/MoneyTest.java +++ b/src/test/java/blackjack/domain/money/MoneyTest.java @@ -20,6 +20,18 @@ void validateTest_whenValueIsNotPositive_throwException(int value) { .hasMessage("금액은 양수이어야 합니다."); } + @DisplayName("금액은 서로 더할 수 있다") + @ParameterizedTest + @CsvSource({"1500, 1500, 3000", "1234, 5555, 6789", "10, 1, 11"}) + void addTest(int value1, int value2, int expected) { + Money money1 = new Money(value1); + Money money2 = new Money(value2); + + Money actual = money1.add(money2); + + assertThat(actual.toInt()).isEqualTo(expected); + } + @DisplayName("금액은 해당 배수만큼 곱할 수 있다") @ParameterizedTest @CsvSource({"1.5, 1500", "2, 2000", "2.25, 2250"}) @@ -30,5 +42,4 @@ void multiplyTest(double multiplier, int expected) { assertThat(actual.toInt()).isEqualTo(expected); } - } From 0578c39ccf2c7c3dabbf2e69d202ba132d3366c6 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 14:54:54 +0900 Subject: [PATCH 09/41] =?UTF-8?q?feat=20(Blackjack)=20:=20=EB=B8=94?= =?UTF-8?q?=EB=9E=99=EC=9E=AD=20=EC=9D=BC=20=EB=95=8C=EB=A7=8C=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/card/status/Blackjack.java | 36 +++++++++++++++++++ .../domain/card/status/HandStatus.java | 12 +++++++ .../domain/card/status/SingleMatchResult.java | 9 +++++ .../domain/card/status/BlackjackTest.java | 35 ++++++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 src/main/java/blackjack/domain/card/status/Blackjack.java create mode 100644 src/main/java/blackjack/domain/card/status/HandStatus.java create mode 100644 src/main/java/blackjack/domain/card/status/SingleMatchResult.java create mode 100644 src/test/java/blackjack/domain/card/status/BlackjackTest.java diff --git a/src/main/java/blackjack/domain/card/status/Blackjack.java b/src/main/java/blackjack/domain/card/status/Blackjack.java new file mode 100644 index 00000000000..8d9084adc84 --- /dev/null +++ b/src/main/java/blackjack/domain/card/status/Blackjack.java @@ -0,0 +1,36 @@ +package blackjack.domain.card.status; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Hand; +import java.util.Objects; + +public class Blackjack implements HandStatus { + + private final Hand hand; + + public Blackjack(Hand hand) { + validateBlackjack(hand); + this.hand = hand; + } + + private void validateBlackjack(Hand hand) { + if (!hand.isBlackjack()) { + throw new IllegalArgumentException("해당 카드는 블랙잭이 아닙니다."); + } + } + + @Override + public HandStatus add(Card card) { + return null; + } + + @Override + public SingleMatchResult matchAtPlayer(HandStatus dealerHand) { + return null; + } + + @Override + public SingleMatchResult matchAtDealer(HandStatus playerHand) { + return null; + } +} diff --git a/src/main/java/blackjack/domain/card/status/HandStatus.java b/src/main/java/blackjack/domain/card/status/HandStatus.java new file mode 100644 index 00000000000..cc92ec95ade --- /dev/null +++ b/src/main/java/blackjack/domain/card/status/HandStatus.java @@ -0,0 +1,12 @@ +package blackjack.domain.card.status; + +import blackjack.domain.card.Card; + +public interface HandStatus { + + HandStatus add(Card card); + + SingleMatchResult matchAtPlayer(HandStatus dealerHand); + + SingleMatchResult matchAtDealer(HandStatus playerHand); +} diff --git a/src/main/java/blackjack/domain/card/status/SingleMatchResult.java b/src/main/java/blackjack/domain/card/status/SingleMatchResult.java new file mode 100644 index 00000000000..961c752518e --- /dev/null +++ b/src/main/java/blackjack/domain/card/status/SingleMatchResult.java @@ -0,0 +1,9 @@ +package blackjack.domain.card.status; + +public enum SingleMatchResult { + + PLAYER_BLACKJACK, + PLAYER_WIN, + DRAW, + DEALER_WIN; +} diff --git a/src/test/java/blackjack/domain/card/status/BlackjackTest.java b/src/test/java/blackjack/domain/card/status/BlackjackTest.java new file mode 100644 index 00000000000..89917de1d6b --- /dev/null +++ b/src/test/java/blackjack/domain/card/status/BlackjackTest.java @@ -0,0 +1,35 @@ +package blackjack.domain.card.status; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Hand; +import blackjack.domain.fixture.CardsFixture; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class BlackjackTest { + + @DisplayName("카드가 블랙잭이 아닌 경우, 예외를 던진다.") + @ParameterizedTest + @MethodSource("notBlackjack") + void validateTest_whenNotBlackjack_throwException(List cards) { + Hand hand = new Hand(cards); + + assertThatThrownBy(() -> new Blackjack(hand)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("해당 카드는 블랙잭이 아닙니다."); + } + + static Stream> notBlackjack() { + return Stream.of( + CardsFixture.CARDS_SCORE_17, + CardsFixture.CARDS_SCORE_21, + CardsFixture.CARDS_SCORE_22 + ); + } +} From 786608a6341db880412af33e83fbcfed7eb6b9db Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 15:10:53 +0900 Subject: [PATCH 10/41] =?UTF-8?q?refactor=20(Hand)=20:=20Hand=EB=A5=BC=20?= =?UTF-8?q?=EC=9A=A9=EC=9D=B4=ED=95=98=EA=B2=8C=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=B4=20=EB=B6=88=EB=B3=80=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/card/Hand.java | 11 +++++++---- .../blackjack/domain/participant/Participant.java | 4 ++-- src/test/java/blackjack/domain/card/HandTest.java | 4 ++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/blackjack/domain/card/Hand.java b/src/main/java/blackjack/domain/card/Hand.java index 6b07ecb2b09..504947ac7a4 100644 --- a/src/main/java/blackjack/domain/card/Hand.java +++ b/src/main/java/blackjack/domain/card/Hand.java @@ -12,11 +12,14 @@ public class Hand { private final List cards; public Hand(List cards) { - this.cards = new ArrayList<>(cards); + this.cards = List.copyOf(cards); } - public void add(Card card) { - cards.add(card); + public Hand add(Card card) { + List newCards = new ArrayList<>(cards); + newCards.add(card); + + return new Hand(newCards); } public int calculateScore() { @@ -58,7 +61,7 @@ public boolean isBlackjack() { } public List getCards() { - return Collections.unmodifiableList(cards); + return cards; } public boolean isEmpty() { diff --git a/src/main/java/blackjack/domain/participant/Participant.java b/src/main/java/blackjack/domain/participant/Participant.java index 53e21f9886e..52028aab9c9 100644 --- a/src/main/java/blackjack/domain/participant/Participant.java +++ b/src/main/java/blackjack/domain/participant/Participant.java @@ -10,7 +10,7 @@ public abstract class Participant { protected static final int BLACKJACK_SCORE = 21; private static final int START_CARDS_SIZE = 2; - private final Hand hand; + private Hand hand; protected Participant(List cards) { this.hand = new Hand(cards); @@ -45,7 +45,7 @@ public final void add(Card card) { if (!isDrawable()) { throw new IllegalStateException("더 이상 카드를 추가할 수 없습니다."); } - hand.add(card); + hand = hand.add(card); } public final List getStartCards() { diff --git a/src/test/java/blackjack/domain/card/HandTest.java b/src/test/java/blackjack/domain/card/HandTest.java index e5b09d9274a..5bc970c5856 100644 --- a/src/test/java/blackjack/domain/card/HandTest.java +++ b/src/test/java/blackjack/domain/card/HandTest.java @@ -36,9 +36,9 @@ void addTest() { Hand hand = new Hand(CardsFixture.CARDS_SCORE_16); Card additionalCard = new Card(Value.ACE, Shape.HEART); - hand.add(additionalCard); + Hand actual = hand.add(additionalCard); - assertThat(hand.getCards()) + assertThat(actual.getCards()) .containsAll(CardsFixture.CARDS_SCORE_16) .contains(additionalCard) .hasSize(CardsFixture.CARDS_SCORE_16.size() + 1); From dac6b9399518e8652dd413b7b94f91c6a6861bdb Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 15:13:59 +0900 Subject: [PATCH 11/41] =?UTF-8?q?feat=20(Blackjack)=20:=20=EB=B8=94?= =?UTF-8?q?=EB=9E=99=EC=9E=AD=EC=97=90=EC=84=9C=20=EC=B9=B4=EB=93=9C?= =?UTF-8?q?=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=A0=20=EA=B2=BD=EC=9A=B0,?= =?UTF-8?q?=20=EB=B2=84=EC=8A=A4=ED=8A=B8=EA=B0=80=20=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/card/status/Blackjack.java | 3 +- .../blackjack/domain/card/status/Busted.java | 28 +++++++++++++++++++ .../domain/card/status/BlackjackTest.java | 14 ++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 src/main/java/blackjack/domain/card/status/Busted.java diff --git a/src/main/java/blackjack/domain/card/status/Blackjack.java b/src/main/java/blackjack/domain/card/status/Blackjack.java index 8d9084adc84..eea094b20bb 100644 --- a/src/main/java/blackjack/domain/card/status/Blackjack.java +++ b/src/main/java/blackjack/domain/card/status/Blackjack.java @@ -2,7 +2,6 @@ import blackjack.domain.card.Card; import blackjack.domain.card.Hand; -import java.util.Objects; public class Blackjack implements HandStatus { @@ -21,7 +20,7 @@ private void validateBlackjack(Hand hand) { @Override public HandStatus add(Card card) { - return null; + return new Busted(hand.add(card)); } @Override diff --git a/src/main/java/blackjack/domain/card/status/Busted.java b/src/main/java/blackjack/domain/card/status/Busted.java new file mode 100644 index 00000000000..b3fa57aca27 --- /dev/null +++ b/src/main/java/blackjack/domain/card/status/Busted.java @@ -0,0 +1,28 @@ +package blackjack.domain.card.status; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Hand; + +public class Busted implements HandStatus { + + private final Hand hand; + + public Busted(Hand hand) { + this.hand = hand; + } + + @Override + public HandStatus add(Card card) { + return null; + } + + @Override + public SingleMatchResult matchAtPlayer(HandStatus dealerHand) { + return null; + } + + @Override + public SingleMatchResult matchAtDealer(HandStatus playerHand) { + return null; + } +} diff --git a/src/test/java/blackjack/domain/card/status/BlackjackTest.java b/src/test/java/blackjack/domain/card/status/BlackjackTest.java index 89917de1d6b..0d44baba9c0 100644 --- a/src/test/java/blackjack/domain/card/status/BlackjackTest.java +++ b/src/test/java/blackjack/domain/card/status/BlackjackTest.java @@ -1,9 +1,12 @@ package blackjack.domain.card.status; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import blackjack.domain.card.Card; import blackjack.domain.card.Hand; +import blackjack.domain.card.Shape; +import blackjack.domain.card.Value; import blackjack.domain.fixture.CardsFixture; import java.util.List; import java.util.stream.Stream; @@ -32,4 +35,15 @@ static Stream> notBlackjack() { CardsFixture.CARDS_SCORE_22 ); } + + @DisplayName("블랙잭에서 카드를 추가할 경우, 버스트 된다.") + @Test + void addTest() { + Hand hand = new Hand(CardsFixture.BLACKJACK); + HandStatus handStatus = new Blackjack(hand); + + HandStatus actual = handStatus.add(new Card(Value.TWO, Shape.HEART)); + + assertThat(actual).isExactlyInstanceOf(Busted.class); + } } From 6146caf7300bbef8e8163d58224ce534c15b551e Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 15:35:05 +0900 Subject: [PATCH 12/41] =?UTF-8?q?refactor=20(Card)=20:=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=EC=9D=98=20=EC=9B=90=ED=99=9C=ED=95=9C=20=ED=8A=B8?= =?UTF-8?q?=EB=9E=98=ED=82=B9=EC=9D=84=20=EC=9C=84=ED=95=B4,=20toString()?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/card/Card.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/blackjack/domain/card/Card.java b/src/main/java/blackjack/domain/card/Card.java index 416457c0642..cb3f97f6509 100644 --- a/src/main/java/blackjack/domain/card/Card.java +++ b/src/main/java/blackjack/domain/card/Card.java @@ -1,6 +1,7 @@ package blackjack.domain.card; import java.util.Objects; +import java.util.StringJoiner; public class Card { @@ -20,6 +21,18 @@ public int getMaxScore() { return value.getMaxScore(); } + public boolean isAce() { + return value.isAce(); + } + + public Value getValue() { + return value; + } + + public Shape getShape() { + return shape; + } + @Override public boolean equals(Object object) { if (this == object) { @@ -37,15 +50,11 @@ public int hashCode() { return Objects.hash(value, shape); } - public Value getValue() { - return value; - } - - public Shape getShape() { - return shape; - } - - public boolean isAce() { - return value.isAce(); + @Override + public String toString() { + return new StringJoiner(", ", Card.class.getSimpleName() + "[", "]") + .add("value=" + value) + .add("shape=" + shape) + .toString(); } } From 5f7b0be4f457bebec509a3b6e34a7c311440c155 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 17:32:21 +0900 Subject: [PATCH 13/41] =?UTF-8?q?feat=20(HandRankFactory)=20:=20=ED=98=84?= =?UTF-8?q?=EC=9E=AC=20=EC=86=90=ED=8C=A8=EC=97=90=20=EB=94=B0=EB=9D=BC=20?= =?UTF-8?q?=EB=8B=A4=EB=A5=B8=20HandRank=EA=B0=80=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/card/Hand.java | 1 - .../domain/card/status/Blackjack.java | 35 ------------ .../blackjack/domain/card/status/Busted.java | 28 ---------- .../domain/card/status/HandStatus.java | 12 ---- .../blackjack/domain/handrank/Blackjack.java | 18 ++++++ .../java/blackjack/domain/handrank/Bust.java | 19 +++++++ .../domain/handrank/HandRankFactory.java | 16 ++++++ .../blackjack/domain/handrank/HankRank.java | 10 ++++ .../blackjack/domain/handrank/NormalRank.java | 25 +++++++++ .../SingleMatchResult.java | 5 +- .../blackjack/domain/participant/Players.java | 1 + .../domain/card/status/BlackjackTest.java | 49 ---------------- .../domain/handrank/HandRankFactoryTest.java | 56 +++++++++++++++++++ 13 files changed, 147 insertions(+), 128 deletions(-) delete mode 100644 src/main/java/blackjack/domain/card/status/Blackjack.java delete mode 100644 src/main/java/blackjack/domain/card/status/Busted.java delete mode 100644 src/main/java/blackjack/domain/card/status/HandStatus.java create mode 100644 src/main/java/blackjack/domain/handrank/Blackjack.java create mode 100644 src/main/java/blackjack/domain/handrank/Bust.java create mode 100644 src/main/java/blackjack/domain/handrank/HandRankFactory.java create mode 100644 src/main/java/blackjack/domain/handrank/HankRank.java create mode 100644 src/main/java/blackjack/domain/handrank/NormalRank.java rename src/main/java/blackjack/domain/{card/status => handrank}/SingleMatchResult.java (60%) delete mode 100644 src/test/java/blackjack/domain/card/status/BlackjackTest.java create mode 100644 src/test/java/blackjack/domain/handrank/HandRankFactoryTest.java diff --git a/src/main/java/blackjack/domain/card/Hand.java b/src/main/java/blackjack/domain/card/Hand.java index 504947ac7a4..ec1867883c7 100644 --- a/src/main/java/blackjack/domain/card/Hand.java +++ b/src/main/java/blackjack/domain/card/Hand.java @@ -1,7 +1,6 @@ package blackjack.domain.card; import java.util.ArrayList; -import java.util.Collections; import java.util.List; public class Hand { diff --git a/src/main/java/blackjack/domain/card/status/Blackjack.java b/src/main/java/blackjack/domain/card/status/Blackjack.java deleted file mode 100644 index eea094b20bb..00000000000 --- a/src/main/java/blackjack/domain/card/status/Blackjack.java +++ /dev/null @@ -1,35 +0,0 @@ -package blackjack.domain.card.status; - -import blackjack.domain.card.Card; -import blackjack.domain.card.Hand; - -public class Blackjack implements HandStatus { - - private final Hand hand; - - public Blackjack(Hand hand) { - validateBlackjack(hand); - this.hand = hand; - } - - private void validateBlackjack(Hand hand) { - if (!hand.isBlackjack()) { - throw new IllegalArgumentException("해당 카드는 블랙잭이 아닙니다."); - } - } - - @Override - public HandStatus add(Card card) { - return new Busted(hand.add(card)); - } - - @Override - public SingleMatchResult matchAtPlayer(HandStatus dealerHand) { - return null; - } - - @Override - public SingleMatchResult matchAtDealer(HandStatus playerHand) { - return null; - } -} diff --git a/src/main/java/blackjack/domain/card/status/Busted.java b/src/main/java/blackjack/domain/card/status/Busted.java deleted file mode 100644 index b3fa57aca27..00000000000 --- a/src/main/java/blackjack/domain/card/status/Busted.java +++ /dev/null @@ -1,28 +0,0 @@ -package blackjack.domain.card.status; - -import blackjack.domain.card.Card; -import blackjack.domain.card.Hand; - -public class Busted implements HandStatus { - - private final Hand hand; - - public Busted(Hand hand) { - this.hand = hand; - } - - @Override - public HandStatus add(Card card) { - return null; - } - - @Override - public SingleMatchResult matchAtPlayer(HandStatus dealerHand) { - return null; - } - - @Override - public SingleMatchResult matchAtDealer(HandStatus playerHand) { - return null; - } -} diff --git a/src/main/java/blackjack/domain/card/status/HandStatus.java b/src/main/java/blackjack/domain/card/status/HandStatus.java deleted file mode 100644 index cc92ec95ade..00000000000 --- a/src/main/java/blackjack/domain/card/status/HandStatus.java +++ /dev/null @@ -1,12 +0,0 @@ -package blackjack.domain.card.status; - -import blackjack.domain.card.Card; - -public interface HandStatus { - - HandStatus add(Card card); - - SingleMatchResult matchAtPlayer(HandStatus dealerHand); - - SingleMatchResult matchAtDealer(HandStatus playerHand); -} diff --git a/src/main/java/blackjack/domain/handrank/Blackjack.java b/src/main/java/blackjack/domain/handrank/Blackjack.java new file mode 100644 index 00000000000..4a54a89f438 --- /dev/null +++ b/src/main/java/blackjack/domain/handrank/Blackjack.java @@ -0,0 +1,18 @@ +package blackjack.domain.handrank; + +public final class Blackjack implements HankRank { + @Override + public SingleMatchResult matchAtDealer(HankRank playerHandRank) { + return null; + } + + @Override + public boolean isBlackjack() { + return false; + } + + @Override + public boolean isBust() { + return false; + } +} diff --git a/src/main/java/blackjack/domain/handrank/Bust.java b/src/main/java/blackjack/domain/handrank/Bust.java new file mode 100644 index 00000000000..6e6442ec4a2 --- /dev/null +++ b/src/main/java/blackjack/domain/handrank/Bust.java @@ -0,0 +1,19 @@ +package blackjack.domain.handrank; + +public final class Bust implements HankRank { + + @Override + public SingleMatchResult matchAtDealer(HankRank playerHandRank) { + return null; + } + + @Override + public boolean isBlackjack() { + return false; + } + + @Override + public boolean isBust() { + return false; + } +} diff --git a/src/main/java/blackjack/domain/handrank/HandRankFactory.java b/src/main/java/blackjack/domain/handrank/HandRankFactory.java new file mode 100644 index 00000000000..6c062f13bd2 --- /dev/null +++ b/src/main/java/blackjack/domain/handrank/HandRankFactory.java @@ -0,0 +1,16 @@ +package blackjack.domain.handrank; + +import blackjack.domain.card.Hand; + +public final class HandRankFactory { + + public static HankRank from(Hand hand) { + if (hand.isBlackjack()) { + return new Blackjack(); + } + if (hand.isBusted()) { + return new Bust(); + } + return new NormalRank(hand.calculateScore()); + } +} diff --git a/src/main/java/blackjack/domain/handrank/HankRank.java b/src/main/java/blackjack/domain/handrank/HankRank.java new file mode 100644 index 00000000000..276860a9f1c --- /dev/null +++ b/src/main/java/blackjack/domain/handrank/HankRank.java @@ -0,0 +1,10 @@ +package blackjack.domain.handrank; + +public interface HankRank { + + SingleMatchResult matchAtDealer(HankRank playerHandRank); + + boolean isBlackjack(); + + boolean isBust(); +} diff --git a/src/main/java/blackjack/domain/handrank/NormalRank.java b/src/main/java/blackjack/domain/handrank/NormalRank.java new file mode 100644 index 00000000000..ca76a65af99 --- /dev/null +++ b/src/main/java/blackjack/domain/handrank/NormalRank.java @@ -0,0 +1,25 @@ +package blackjack.domain.handrank; + +public final class NormalRank implements HankRank { + + private final int score; + + public NormalRank(int score) { + this.score = score; + } + + @Override + public SingleMatchResult matchAtDealer(HankRank playerHandRank) { + return null; + } + + @Override + public boolean isBlackjack() { + return false; + } + + @Override + public boolean isBust() { + return false; + } +} diff --git a/src/main/java/blackjack/domain/card/status/SingleMatchResult.java b/src/main/java/blackjack/domain/handrank/SingleMatchResult.java similarity index 60% rename from src/main/java/blackjack/domain/card/status/SingleMatchResult.java rename to src/main/java/blackjack/domain/handrank/SingleMatchResult.java index 961c752518e..c1d3a042f27 100644 --- a/src/main/java/blackjack/domain/card/status/SingleMatchResult.java +++ b/src/main/java/blackjack/domain/handrank/SingleMatchResult.java @@ -1,9 +1,8 @@ -package blackjack.domain.card.status; +package blackjack.domain.handrank; public enum SingleMatchResult { - PLAYER_BLACKJACK, PLAYER_WIN, DRAW, - DEALER_WIN; + DEALER_WIN } diff --git a/src/main/java/blackjack/domain/participant/Players.java b/src/main/java/blackjack/domain/participant/Players.java index 365d19bc0af..5a3acfa9183 100644 --- a/src/main/java/blackjack/domain/participant/Players.java +++ b/src/main/java/blackjack/domain/participant/Players.java @@ -31,6 +31,7 @@ private void validateSize(List players) { } } + // TODO 같은 참조값을 가지는 경우 생각 private void validateDistinct(List players) { if (isDuplicated(players)) { throw new IllegalArgumentException("중복된 이름을 사용할 수 없습니다."); diff --git a/src/test/java/blackjack/domain/card/status/BlackjackTest.java b/src/test/java/blackjack/domain/card/status/BlackjackTest.java deleted file mode 100644 index 0d44baba9c0..00000000000 --- a/src/test/java/blackjack/domain/card/status/BlackjackTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package blackjack.domain.card.status; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import blackjack.domain.card.Card; -import blackjack.domain.card.Hand; -import blackjack.domain.card.Shape; -import blackjack.domain.card.Value; -import blackjack.domain.fixture.CardsFixture; -import java.util.List; -import java.util.stream.Stream; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -public class BlackjackTest { - - @DisplayName("카드가 블랙잭이 아닌 경우, 예외를 던진다.") - @ParameterizedTest - @MethodSource("notBlackjack") - void validateTest_whenNotBlackjack_throwException(List cards) { - Hand hand = new Hand(cards); - - assertThatThrownBy(() -> new Blackjack(hand)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("해당 카드는 블랙잭이 아닙니다."); - } - - static Stream> notBlackjack() { - return Stream.of( - CardsFixture.CARDS_SCORE_17, - CardsFixture.CARDS_SCORE_21, - CardsFixture.CARDS_SCORE_22 - ); - } - - @DisplayName("블랙잭에서 카드를 추가할 경우, 버스트 된다.") - @Test - void addTest() { - Hand hand = new Hand(CardsFixture.BLACKJACK); - HandStatus handStatus = new Blackjack(hand); - - HandStatus actual = handStatus.add(new Card(Value.TWO, Shape.HEART)); - - assertThat(actual).isExactlyInstanceOf(Busted.class); - } -} diff --git a/src/test/java/blackjack/domain/handrank/HandRankFactoryTest.java b/src/test/java/blackjack/domain/handrank/HandRankFactoryTest.java new file mode 100644 index 00000000000..824f80b55e3 --- /dev/null +++ b/src/test/java/blackjack/domain/handrank/HandRankFactoryTest.java @@ -0,0 +1,56 @@ +package blackjack.domain.handrank; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Hand; +import blackjack.domain.fixture.CardsFixture; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class HandRankFactoryTest { + + @DisplayName("손패가 블랙잭인 경우, 블랙잭 객체를 반환한다.") + @Test + void fromTest_whenBlackjack() { + Hand blackjack = new Hand(CardsFixture.BLACKJACK); + + HankRank actual = HandRankFactory.from(blackjack); + + assertThat(actual).isExactlyInstanceOf(Blackjack.class); + } + + @DisplayName("손패가 버스트가 된 경우, 버스트 객체를 반환한다.") + @ParameterizedTest + @MethodSource("bustCards") + void fromTest_whenBusted(List bust) { + Hand blackjack = new Hand(bust); + + HankRank actual = HandRankFactory.from(blackjack); + + assertThat(actual).isExactlyInstanceOf(Bust.class); + } + + static Stream> bustCards() { + return Stream.of(CardsFixture.BUSTED, CardsFixture.CARDS_SCORE_22); + } + + @DisplayName("손패가 버스트, 블랙잭이 아닌 경우, 일반 랭크 객체를 반환한다.") + @ParameterizedTest + @MethodSource("normalCards") + void fromTest_whenNotBlackjackAndNotBust(List cards) { + Hand blackjack = new Hand(cards); + + HankRank actual = HandRankFactory.from(blackjack); + + assertThat(actual).isExactlyInstanceOf(NormalRank.class); + } + + static Stream> normalCards() { + return Stream.of(CardsFixture.CARDS_SCORE_4, CardsFixture.CARDS_SCORE_17, CardsFixture.CARDS_SCORE_16); + } +} From f2708af4d25cf776db654b81a1f04c8ae543f3c4 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 17:33:19 +0900 Subject: [PATCH 14/41] =?UTF-8?q?refactor=20(Money)=20:=20=EC=BB=A8?= =?UTF-8?q?=EB=B2=A4=EC=85=98=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/money/Money.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/blackjack/domain/money/Money.java b/src/main/java/blackjack/domain/money/Money.java index 34904ef5e40..5a9312a51c8 100644 --- a/src/main/java/blackjack/domain/money/Money.java +++ b/src/main/java/blackjack/domain/money/Money.java @@ -15,10 +15,6 @@ private void validatePositive(int value) { } } - public int toInt() { - return value; - } - public Money add(Money money) { return new Money(this.value + money.value); } @@ -27,4 +23,8 @@ public Money multiply(double multiplier) { int newValue = (int) Math.ceil(this.value * multiplier); return new Money(newValue); } + + public int toInt() { + return value; + } } From 979de3751143419c27667bfd238f76fe1c8a79e5 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 17:42:16 +0900 Subject: [PATCH 15/41] =?UTF-8?q?feat=20(Blackjack)=20:=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=20=EB=9E=AD=ED=81=AC=EA=B0=80=20=EB=B8=94=EB=9E=99?= =?UTF-8?q?=EC=9E=AD,=20=EB=B2=84=EC=8A=A4=ED=8A=B8=EC=9D=B8=EC=A7=80=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/domain/handrank/Blackjack.java | 2 +- .../domain/handrank/BlackjackTest.java | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/test/java/blackjack/domain/handrank/BlackjackTest.java diff --git a/src/main/java/blackjack/domain/handrank/Blackjack.java b/src/main/java/blackjack/domain/handrank/Blackjack.java index 4a54a89f438..7d7f4def300 100644 --- a/src/main/java/blackjack/domain/handrank/Blackjack.java +++ b/src/main/java/blackjack/domain/handrank/Blackjack.java @@ -8,7 +8,7 @@ public SingleMatchResult matchAtDealer(HankRank playerHandRank) { @Override public boolean isBlackjack() { - return false; + return true; } @Override diff --git a/src/test/java/blackjack/domain/handrank/BlackjackTest.java b/src/test/java/blackjack/domain/handrank/BlackjackTest.java new file mode 100644 index 00000000000..030009fb653 --- /dev/null +++ b/src/test/java/blackjack/domain/handrank/BlackjackTest.java @@ -0,0 +1,25 @@ +package blackjack.domain.handrank; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class BlackjackTest { + + private final HankRank BLACKJACK = new Blackjack(); + + @DisplayName("해당 핸드 랭크는 블랙잭이다.") + @Test + void isBlackjackTest() { + + assertThat(BLACKJACK.isBlackjack()).isTrue(); + } + + @DisplayName("해당 핸드 랭크는 버스트가 아니다.") + @Test + void isBustTest() { + + assertThat(BLACKJACK.isBust()).isFalse(); + } +} From 7ed3ca092d64ce6ffc6142cfb5433dcea2532dcd Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 17:51:47 +0900 Subject: [PATCH 16/41] =?UTF-8?q?feat=20(Blackjack)=20:=20=EB=94=9C?= =?UTF-8?q?=EB=9F=AC=EA=B0=80=20=EB=B8=94=EB=9E=99=EC=9E=AD=EC=9D=BC=20?= =?UTF-8?q?=EB=95=8C,=20=EC=8A=B9=ED=8C=A8=20=ED=8C=90=EB=8B=A8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/domain/handrank/Blackjack.java | 6 ++++- .../domain/handrank/BlackjackTest.java | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/main/java/blackjack/domain/handrank/Blackjack.java b/src/main/java/blackjack/domain/handrank/Blackjack.java index 7d7f4def300..1fd10d5b5df 100644 --- a/src/main/java/blackjack/domain/handrank/Blackjack.java +++ b/src/main/java/blackjack/domain/handrank/Blackjack.java @@ -1,9 +1,13 @@ package blackjack.domain.handrank; public final class Blackjack implements HankRank { + @Override public SingleMatchResult matchAtDealer(HankRank playerHandRank) { - return null; + if (playerHandRank.isBlackjack()) { + return SingleMatchResult.DRAW; + } + return SingleMatchResult.DEALER_WIN; } @Override diff --git a/src/test/java/blackjack/domain/handrank/BlackjackTest.java b/src/test/java/blackjack/domain/handrank/BlackjackTest.java index 030009fb653..83df4d3b84a 100644 --- a/src/test/java/blackjack/domain/handrank/BlackjackTest.java +++ b/src/test/java/blackjack/domain/handrank/BlackjackTest.java @@ -2,13 +2,39 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class BlackjackTest { private final HankRank BLACKJACK = new Blackjack(); + @DisplayName("플레이어, 딜러 모두 블랙잭인 경우 비긴다.") + @Test + void matchTest_whenPlayerAndDealerBlackjack() { + HankRank dealerRank = BLACKJACK; + HankRank playerRank = BLACKJACK; + + assertThat(dealerRank.matchAtDealer(playerRank)).isEqualTo(SingleMatchResult.DRAW); + } + + @DisplayName("딜러만 블랙잭인 경우, 딜러가 이긴다.") + @ParameterizedTest + @MethodSource("notBlackjack") + void matchTest_whenOnlyDealerBlackjack(HankRank hankRank) { + HankRank dealerRank = BLACKJACK; + HankRank playerRank = hankRank; + + assertThat(dealerRank.matchAtDealer(playerRank)).isEqualTo(SingleMatchResult.DEALER_WIN); + } + + static Stream notBlackjack() { + return Stream.of(new NormalRank(12), new NormalRank(20), new Bust()); + } + @DisplayName("해당 핸드 랭크는 블랙잭이다.") @Test void isBlackjackTest() { From 124faa70252e2371d772fb93bd21c1de4a1cf050 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 18:01:40 +0900 Subject: [PATCH 17/41] =?UTF-8?q?feat=20(Bust)=20:=20=ED=95=B4=EB=8B=B9=20?= =?UTF-8?q?=EB=9E=AD=ED=81=AC=EA=B0=80=20=EB=B8=94=EB=9E=99=EC=9E=AD,=20?= =?UTF-8?q?=EB=B2=84=EC=8A=A4=ED=8A=B8=EC=9D=B8=EC=A7=80=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/blackjack/domain/handrank/Bust.java | 2 +- .../blackjack/domain/handrank/BustTest.java | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/test/java/blackjack/domain/handrank/BustTest.java diff --git a/src/main/java/blackjack/domain/handrank/Bust.java b/src/main/java/blackjack/domain/handrank/Bust.java index 6e6442ec4a2..7d8cb7501d9 100644 --- a/src/main/java/blackjack/domain/handrank/Bust.java +++ b/src/main/java/blackjack/domain/handrank/Bust.java @@ -14,6 +14,6 @@ public boolean isBlackjack() { @Override public boolean isBust() { - return false; + return true; } } diff --git a/src/test/java/blackjack/domain/handrank/BustTest.java b/src/test/java/blackjack/domain/handrank/BustTest.java new file mode 100644 index 00000000000..b46a73fb388 --- /dev/null +++ b/src/test/java/blackjack/domain/handrank/BustTest.java @@ -0,0 +1,25 @@ +package blackjack.domain.handrank; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class BustTest { + + private static HankRank BUST = new Bust(); + + @DisplayName("해당 핸드 랭크는 블랙잭이 아니다.") + @Test + void isBlackjackTest() { + + assertThat(BUST.isBlackjack()).isTrue(); + } + + @DisplayName("해당 핸드 랭크는 버스트이다.") + @Test + void isBustTest() { + + assertThat(BUST.isBust()).isFalse(); + } +} From ad9ae1ecd355e26edae8472424cad224bb2610f4 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 19:09:53 +0900 Subject: [PATCH 18/41] =?UTF-8?q?feat=20(Blackjack)=20:=20=EB=94=9C?= =?UTF-8?q?=EB=9F=AC=EA=B0=80=20=EB=B2=84=EC=8A=A4=ED=8A=B8=EC=9D=BC=20?= =?UTF-8?q?=EB=95=8C,=20=EC=8A=B9=ED=8C=A8=20=ED=8C=90=EB=8B=A8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/blackjack/domain/handrank/Bust.java | 8 +++- .../domain/handrank/BlackjackTest.java | 4 +- .../blackjack/domain/handrank/BustTest.java | 39 ++++++++++++++++++- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/main/java/blackjack/domain/handrank/Bust.java b/src/main/java/blackjack/domain/handrank/Bust.java index 7d8cb7501d9..72dd9ffa98d 100644 --- a/src/main/java/blackjack/domain/handrank/Bust.java +++ b/src/main/java/blackjack/domain/handrank/Bust.java @@ -4,7 +4,13 @@ public final class Bust implements HankRank { @Override public SingleMatchResult matchAtDealer(HankRank playerHandRank) { - return null; + if (playerHandRank.isBust()) { + return SingleMatchResult.DEALER_WIN; + } + if (playerHandRank.isBlackjack()) { + return SingleMatchResult.PLAYER_BLACKJACK; + } + return SingleMatchResult.PLAYER_WIN; } @Override diff --git a/src/test/java/blackjack/domain/handrank/BlackjackTest.java b/src/test/java/blackjack/domain/handrank/BlackjackTest.java index 83df4d3b84a..63012920e25 100644 --- a/src/test/java/blackjack/domain/handrank/BlackjackTest.java +++ b/src/test/java/blackjack/domain/handrank/BlackjackTest.java @@ -14,7 +14,7 @@ public class BlackjackTest { @DisplayName("플레이어, 딜러 모두 블랙잭인 경우 비긴다.") @Test - void matchTest_whenPlayerAndDealerBlackjack() { + void matchTest_whenPlayerAndDealerBlackjack_matchDraw() { HankRank dealerRank = BLACKJACK; HankRank playerRank = BLACKJACK; @@ -24,7 +24,7 @@ void matchTest_whenPlayerAndDealerBlackjack() { @DisplayName("딜러만 블랙잭인 경우, 딜러가 이긴다.") @ParameterizedTest @MethodSource("notBlackjack") - void matchTest_whenOnlyDealerBlackjack(HankRank hankRank) { + void matchTest_whenOnlyDealerBlackjack_dealerWin(HankRank hankRank) { HankRank dealerRank = BLACKJACK; HankRank playerRank = hankRank; diff --git a/src/test/java/blackjack/domain/handrank/BustTest.java b/src/test/java/blackjack/domain/handrank/BustTest.java index b46a73fb388..236828307ad 100644 --- a/src/test/java/blackjack/domain/handrank/BustTest.java +++ b/src/test/java/blackjack/domain/handrank/BustTest.java @@ -2,24 +2,59 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; class BustTest { private static HankRank BUST = new Bust(); + @DisplayName("플레이어, 딜러 모두 버스트인 경우 딜러가 이긴다.") + @Test + void matchTest_whenPlayerAndDealerBust_DealerWin() { + HankRank dealerRank = BUST; + HankRank playerRank = BUST; + + assertThat(dealerRank.matchAtDealer(playerRank)).isEqualTo(SingleMatchResult.DEALER_WIN); + } + + @DisplayName("딜러만 버스트인 경우, 플레이어가 이긴다.") + @ParameterizedTest + @MethodSource("normalRank") + void matchTest_whenOnlyDealerBlackjack_PlayerWin(HankRank hankRank) { + HankRank dealerRank = BUST; + HankRank playerRank = hankRank; + + assertThat(dealerRank.matchAtDealer(playerRank)).isEqualTo(SingleMatchResult.PLAYER_WIN); + } + + static Stream normalRank() { + return Stream.of(new NormalRank(12), new NormalRank(20), new NormalRank(21)); + } + + @DisplayName("딜러만 버스트이고 플레이어가 블랙잭인 경우, 플레이어 블랙잭으로 승리한다.") + @Test + void matchTest_whenOnlyDealerBlackjack_PlayerBlackjackWin() { + HankRank dealerRank = BUST; + HankRank playerRank = new Blackjack(); + + assertThat(dealerRank.matchAtDealer(playerRank)).isEqualTo(SingleMatchResult.PLAYER_BLACKJACK); + } + @DisplayName("해당 핸드 랭크는 블랙잭이 아니다.") @Test void isBlackjackTest() { - assertThat(BUST.isBlackjack()).isTrue(); + assertThat(BUST.isBlackjack()).isFalse(); } @DisplayName("해당 핸드 랭크는 버스트이다.") @Test void isBustTest() { - assertThat(BUST.isBust()).isFalse(); + assertThat(BUST.isBust()).isTrue(); } } From 35d89860ae242b8fefa6a771605582f22a562889 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 19:18:01 +0900 Subject: [PATCH 19/41] =?UTF-8?q?feat=20(HandRank)=20:=20=EC=9D=BC?= =?UTF-8?q?=EB=B0=98=20=EC=BC=80=EC=9D=B4=EC=8A=A4=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=91=90=20=EC=A0=90=EC=88=98=EB=A5=BC=20=EB=B9=84=EA=B5=90?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=B4,=20HandRank=EC=97=90?= =?UTF-8?q?=EC=84=9C=20Score=EB=A5=BC=20=EB=B0=98=ED=99=98=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/card/Hand.java | 2 +- .../java/blackjack/domain/handrank/Blackjack.java | 7 +++++++ src/main/java/blackjack/domain/handrank/Bust.java | 11 +++++++++++ .../blackjack/domain/handrank/HandRankFactory.java | 6 ++++-- src/main/java/blackjack/domain/handrank/HankRank.java | 2 ++ .../java/blackjack/domain/handrank/NormalRank.java | 5 +++++ .../java/blackjack/domain/handrank/BlackjackTest.java | 2 +- src/test/java/blackjack/domain/handrank/BustTest.java | 2 +- 8 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/main/java/blackjack/domain/card/Hand.java b/src/main/java/blackjack/domain/card/Hand.java index ec1867883c7..707fbee837c 100644 --- a/src/main/java/blackjack/domain/card/Hand.java +++ b/src/main/java/blackjack/domain/card/Hand.java @@ -5,7 +5,7 @@ public class Hand { - private static final int BLACKJACK_SCORE = 21; + public static final int BLACKJACK_SCORE = 21; private static final int BLACKJACK_SIZE = 2; private final List cards; diff --git a/src/main/java/blackjack/domain/handrank/Blackjack.java b/src/main/java/blackjack/domain/handrank/Blackjack.java index 1fd10d5b5df..3614a5082b6 100644 --- a/src/main/java/blackjack/domain/handrank/Blackjack.java +++ b/src/main/java/blackjack/domain/handrank/Blackjack.java @@ -1,5 +1,7 @@ package blackjack.domain.handrank; +import blackjack.domain.card.Hand; + public final class Blackjack implements HankRank { @Override @@ -10,6 +12,11 @@ public SingleMatchResult matchAtDealer(HankRank playerHandRank) { return SingleMatchResult.DEALER_WIN; } + @Override + public int getScore() { + return Hand.BLACKJACK_SCORE; + } + @Override public boolean isBlackjack() { return true; diff --git a/src/main/java/blackjack/domain/handrank/Bust.java b/src/main/java/blackjack/domain/handrank/Bust.java index 72dd9ffa98d..63ef715c94b 100644 --- a/src/main/java/blackjack/domain/handrank/Bust.java +++ b/src/main/java/blackjack/domain/handrank/Bust.java @@ -2,6 +2,12 @@ public final class Bust implements HankRank { + private final int score; + + public Bust(int score) { + this.score = score; + } + @Override public SingleMatchResult matchAtDealer(HankRank playerHandRank) { if (playerHandRank.isBust()) { @@ -13,6 +19,11 @@ public SingleMatchResult matchAtDealer(HankRank playerHandRank) { return SingleMatchResult.PLAYER_WIN; } + @Override + public int getScore() { + return score; + } + @Override public boolean isBlackjack() { return false; diff --git a/src/main/java/blackjack/domain/handrank/HandRankFactory.java b/src/main/java/blackjack/domain/handrank/HandRankFactory.java index 6c062f13bd2..c3a6453ac33 100644 --- a/src/main/java/blackjack/domain/handrank/HandRankFactory.java +++ b/src/main/java/blackjack/domain/handrank/HandRankFactory.java @@ -4,12 +4,14 @@ public final class HandRankFactory { + private static final HankRank BLACKJACK = new Blackjack(); + public static HankRank from(Hand hand) { if (hand.isBlackjack()) { - return new Blackjack(); + return BLACKJACK; } if (hand.isBusted()) { - return new Bust(); + return new Bust(hand.calculateScore()); } return new NormalRank(hand.calculateScore()); } diff --git a/src/main/java/blackjack/domain/handrank/HankRank.java b/src/main/java/blackjack/domain/handrank/HankRank.java index 276860a9f1c..803ae4f5f81 100644 --- a/src/main/java/blackjack/domain/handrank/HankRank.java +++ b/src/main/java/blackjack/domain/handrank/HankRank.java @@ -4,6 +4,8 @@ public interface HankRank { SingleMatchResult matchAtDealer(HankRank playerHandRank); + int getScore(); + boolean isBlackjack(); boolean isBust(); diff --git a/src/main/java/blackjack/domain/handrank/NormalRank.java b/src/main/java/blackjack/domain/handrank/NormalRank.java index ca76a65af99..350837ea89a 100644 --- a/src/main/java/blackjack/domain/handrank/NormalRank.java +++ b/src/main/java/blackjack/domain/handrank/NormalRank.java @@ -13,6 +13,11 @@ public SingleMatchResult matchAtDealer(HankRank playerHandRank) { return null; } + @Override + public int getScore() { + return score; + } + @Override public boolean isBlackjack() { return false; diff --git a/src/test/java/blackjack/domain/handrank/BlackjackTest.java b/src/test/java/blackjack/domain/handrank/BlackjackTest.java index 63012920e25..912e98bda33 100644 --- a/src/test/java/blackjack/domain/handrank/BlackjackTest.java +++ b/src/test/java/blackjack/domain/handrank/BlackjackTest.java @@ -32,7 +32,7 @@ void matchTest_whenOnlyDealerBlackjack_dealerWin(HankRank hankRank) { } static Stream notBlackjack() { - return Stream.of(new NormalRank(12), new NormalRank(20), new Bust()); + return Stream.of(new NormalRank(12), new NormalRank(20), new Bust(22)); } @DisplayName("해당 핸드 랭크는 블랙잭이다.") diff --git a/src/test/java/blackjack/domain/handrank/BustTest.java b/src/test/java/blackjack/domain/handrank/BustTest.java index 236828307ad..69ccaceb10c 100644 --- a/src/test/java/blackjack/domain/handrank/BustTest.java +++ b/src/test/java/blackjack/domain/handrank/BustTest.java @@ -10,7 +10,7 @@ class BustTest { - private static HankRank BUST = new Bust(); + private static HankRank BUST = new Bust(22); @DisplayName("플레이어, 딜러 모두 버스트인 경우 딜러가 이긴다.") @Test From a4e86a601c62af356523e69fe5109bc537b74f4f Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Wed, 13 Mar 2024 21:45:59 +0900 Subject: [PATCH 20/41] =?UTF-8?q?test=20(NormalRank)=20:=20=ED=95=B4?= =?UTF-8?q?=EB=8B=B9=20=EB=9E=AD=ED=81=AC=EA=B0=80=20=EB=B8=94=EB=9E=99?= =?UTF-8?q?=EC=9E=AD,=20=EB=B2=84=EC=8A=A4=ED=8A=B8=EC=9D=B8=EC=A7=80=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/handrank/NormalRankTest.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/test/java/blackjack/domain/handrank/NormalRankTest.java diff --git a/src/test/java/blackjack/domain/handrank/NormalRankTest.java b/src/test/java/blackjack/domain/handrank/NormalRankTest.java new file mode 100644 index 00000000000..08f86045ad6 --- /dev/null +++ b/src/test/java/blackjack/domain/handrank/NormalRankTest.java @@ -0,0 +1,25 @@ +package blackjack.domain.handrank; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class NormalRankTest { + + private final HankRank NORMAL_RANK = new NormalRank(20); + + @DisplayName("해당 핸드 랭크는 블랙잭이 아니다.") + @Test + void isBlackjackTest() { + + assertThat(NORMAL_RANK.isBlackjack()).isFalse(); + } + + @DisplayName("해당 핸드 랭크는 버스트가 아니다.") + @Test + void isBustTest() { + + assertThat(NORMAL_RANK.isBust()).isFalse(); + } +} From 4cbebebb83c0a9f7892f6ad157a632e8c24b2f83 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 09:11:28 +0900 Subject: [PATCH 21/41] =?UTF-8?q?style=20(Stand)=20:=20NormalRank=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D=EC=9D=B4=20=EC=B6=94=EC=83=81?= =?UTF-8?q?=EC=A0=81=EC=9D=B4=EC=96=B4=EC=84=9C,=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=9A=A9=EC=96=B4=20=EC=A4=91=20=ED=95=98=EB=82=98?= =?UTF-8?q?=EC=9D=B8=20Stand=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/handrank/HandRankFactory.java | 2 +- .../blackjack/domain/handrank/{NormalRank.java => Stand.java} | 4 ++-- src/test/java/blackjack/domain/handrank/BlackjackTest.java | 2 +- src/test/java/blackjack/domain/handrank/BustTest.java | 4 ++-- .../java/blackjack/domain/handrank/HandRankFactoryTest.java | 2 +- .../domain/handrank/{NormalRankTest.java => StandTest.java} | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) rename src/main/java/blackjack/domain/handrank/{NormalRank.java => Stand.java} (83%) rename src/test/java/blackjack/domain/handrank/{NormalRankTest.java => StandTest.java} (84%) diff --git a/src/main/java/blackjack/domain/handrank/HandRankFactory.java b/src/main/java/blackjack/domain/handrank/HandRankFactory.java index c3a6453ac33..551883a828a 100644 --- a/src/main/java/blackjack/domain/handrank/HandRankFactory.java +++ b/src/main/java/blackjack/domain/handrank/HandRankFactory.java @@ -13,6 +13,6 @@ public static HankRank from(Hand hand) { if (hand.isBusted()) { return new Bust(hand.calculateScore()); } - return new NormalRank(hand.calculateScore()); + return new Stand(hand.calculateScore()); } } diff --git a/src/main/java/blackjack/domain/handrank/NormalRank.java b/src/main/java/blackjack/domain/handrank/Stand.java similarity index 83% rename from src/main/java/blackjack/domain/handrank/NormalRank.java rename to src/main/java/blackjack/domain/handrank/Stand.java index 350837ea89a..bff3c9853cc 100644 --- a/src/main/java/blackjack/domain/handrank/NormalRank.java +++ b/src/main/java/blackjack/domain/handrank/Stand.java @@ -1,10 +1,10 @@ package blackjack.domain.handrank; -public final class NormalRank implements HankRank { +public final class Stand implements HankRank { private final int score; - public NormalRank(int score) { + public Stand(int score) { this.score = score; } diff --git a/src/test/java/blackjack/domain/handrank/BlackjackTest.java b/src/test/java/blackjack/domain/handrank/BlackjackTest.java index 912e98bda33..cfe8a423fd1 100644 --- a/src/test/java/blackjack/domain/handrank/BlackjackTest.java +++ b/src/test/java/blackjack/domain/handrank/BlackjackTest.java @@ -32,7 +32,7 @@ void matchTest_whenOnlyDealerBlackjack_dealerWin(HankRank hankRank) { } static Stream notBlackjack() { - return Stream.of(new NormalRank(12), new NormalRank(20), new Bust(22)); + return Stream.of(new Stand(12), new Stand(20), new Bust(22)); } @DisplayName("해당 핸드 랭크는 블랙잭이다.") diff --git a/src/test/java/blackjack/domain/handrank/BustTest.java b/src/test/java/blackjack/domain/handrank/BustTest.java index 69ccaceb10c..9a16a4dd5e4 100644 --- a/src/test/java/blackjack/domain/handrank/BustTest.java +++ b/src/test/java/blackjack/domain/handrank/BustTest.java @@ -10,7 +10,7 @@ class BustTest { - private static HankRank BUST = new Bust(22); + private static final HankRank BUST = new Bust(22); @DisplayName("플레이어, 딜러 모두 버스트인 경우 딜러가 이긴다.") @Test @@ -32,7 +32,7 @@ void matchTest_whenOnlyDealerBlackjack_PlayerWin(HankRank hankRank) { } static Stream normalRank() { - return Stream.of(new NormalRank(12), new NormalRank(20), new NormalRank(21)); + return Stream.of(new Stand(12), new Stand(20), new Stand(21)); } @DisplayName("딜러만 버스트이고 플레이어가 블랙잭인 경우, 플레이어 블랙잭으로 승리한다.") diff --git a/src/test/java/blackjack/domain/handrank/HandRankFactoryTest.java b/src/test/java/blackjack/domain/handrank/HandRankFactoryTest.java index 824f80b55e3..29439a907c8 100644 --- a/src/test/java/blackjack/domain/handrank/HandRankFactoryTest.java +++ b/src/test/java/blackjack/domain/handrank/HandRankFactoryTest.java @@ -47,7 +47,7 @@ void fromTest_whenNotBlackjackAndNotBust(List cards) { HankRank actual = HandRankFactory.from(blackjack); - assertThat(actual).isExactlyInstanceOf(NormalRank.class); + assertThat(actual).isExactlyInstanceOf(Stand.class); } static Stream> normalCards() { diff --git a/src/test/java/blackjack/domain/handrank/NormalRankTest.java b/src/test/java/blackjack/domain/handrank/StandTest.java similarity index 84% rename from src/test/java/blackjack/domain/handrank/NormalRankTest.java rename to src/test/java/blackjack/domain/handrank/StandTest.java index 08f86045ad6..e74ba3d5566 100644 --- a/src/test/java/blackjack/domain/handrank/NormalRankTest.java +++ b/src/test/java/blackjack/domain/handrank/StandTest.java @@ -5,9 +5,9 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -public class NormalRankTest { +class StandTest { - private final HankRank NORMAL_RANK = new NormalRank(20); + private final HankRank NORMAL_RANK = new Stand(20); @DisplayName("해당 핸드 랭크는 블랙잭이 아니다.") @Test From c74a4511c48d673e376fdca583e4c6da093cdd92 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 09:33:28 +0900 Subject: [PATCH 22/41] =?UTF-8?q?feat=20(Stand)=20:=20=EB=94=9C=EB=9F=AC?= =?UTF-8?q?=EA=B0=80=20Stand=EC=9D=BC=20=EB=95=8C,=20=EC=8A=B9=ED=8C=A8=20?= =?UTF-8?q?=ED=8C=90=EB=8B=A8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/blackjack/domain/handrank/Stand.java | 18 +++++- .../blackjack/domain/handrank/StandTest.java | 63 ++++++++++++++++++- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/src/main/java/blackjack/domain/handrank/Stand.java b/src/main/java/blackjack/domain/handrank/Stand.java index bff3c9853cc..a8c0e228ff1 100644 --- a/src/main/java/blackjack/domain/handrank/Stand.java +++ b/src/main/java/blackjack/domain/handrank/Stand.java @@ -10,7 +10,23 @@ public Stand(int score) { @Override public SingleMatchResult matchAtDealer(HankRank playerHandRank) { - return null; + if (playerHandRank.isBlackjack()) { + return SingleMatchResult.PLAYER_BLACKJACK; + } + if (playerHandRank.isBust()) { + return SingleMatchResult.DEALER_WIN; + } + return matchThroughScore(playerHandRank); + } + + private SingleMatchResult matchThroughScore(HankRank playerHandRank) { + if (playerHandRank.getScore() > this.getScore()) { + return SingleMatchResult.PLAYER_WIN; + } + if (playerHandRank.getScore() == this.getScore()) { + return SingleMatchResult.DRAW; + } + return SingleMatchResult.DEALER_WIN; } @Override diff --git a/src/test/java/blackjack/domain/handrank/StandTest.java b/src/test/java/blackjack/domain/handrank/StandTest.java index e74ba3d5566..4b4d3eb62db 100644 --- a/src/test/java/blackjack/domain/handrank/StandTest.java +++ b/src/test/java/blackjack/domain/handrank/StandTest.java @@ -3,23 +3,80 @@ import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; class StandTest { - private final HankRank NORMAL_RANK = new Stand(20); + private final HankRank STAND = new Stand(20); + + @DisplayName("딜러가 21점 이하 점수의 일반적인 경우") + @Nested + class MatchTest { + @DisplayName("플레이어가 블랙잭인 경우, 플레이어가 블랙잭으로 승리한다.") + @Test + void whenOnlyPlayerBlackjack_playerBlackjack() { + HankRank dealer = new Stand(21); + HankRank player = new Blackjack(); + + assertThat(dealer.matchAtDealer(player)).isEqualTo(SingleMatchResult.PLAYER_BLACKJACK); + } + + @DisplayName("플레이어가 버스트인 경우, 딜러가 승리한다.") + @Test + void whenOnlyPlayerBust_DealerWin() { + HankRank dealer = STAND; + HankRank player = new Bust(22); + + assertThat(dealer.matchAtDealer(player)).isEqualTo(SingleMatchResult.DEALER_WIN); + } + + @DisplayName("플레이어가 딜러보다 점수가 높을 경우, 플레이러가 승리한다.") + @ParameterizedTest + @CsvSource({"20, 21", "17, 18", "17, 21"}) + void whenPlayerScoreIsMoreThanDealerScore_PlayerWin(int dealerScore, int playerScore) { + HankRank dealer = new Stand(dealerScore); + HankRank player = new Stand(playerScore); + + assertThat(dealer.matchAtDealer(player)).isEqualTo(SingleMatchResult.PLAYER_WIN); + } + + @DisplayName("플레이어와 딜러의 점수가 같을 경우, 비긴다.") + @ParameterizedTest + @ValueSource(ints = {17, 18, 21}) + void whenPlayerScoreIsEqualToDealerScore_Draw(int sameScore) { + HankRank dealer = new Stand(sameScore); + HankRank player = new Stand(sameScore); + + assertThat(dealer.matchAtDealer(player)).isEqualTo(SingleMatchResult.DRAW); + } + + @DisplayName("플레이어가 딜러보다 점수가 높을 경우, 플레이러가 승리한다.") + @ParameterizedTest + @CsvSource({"21, 20", "17, 4", "17, 16"}) + void whenPlayerScoreIsLessThanDealerScore_Dealer(int dealerScore, int playerScore) { + HankRank dealer = new Stand(dealerScore); + HankRank player = new Stand(playerScore); + + assertThat(dealer.matchAtDealer(player)).isEqualTo(SingleMatchResult.DEALER_WIN); + } + } + @DisplayName("해당 핸드 랭크는 블랙잭이 아니다.") @Test void isBlackjackTest() { - assertThat(NORMAL_RANK.isBlackjack()).isFalse(); + assertThat(STAND.isBlackjack()).isFalse(); } @DisplayName("해당 핸드 랭크는 버스트가 아니다.") @Test void isBustTest() { - assertThat(NORMAL_RANK.isBust()).isFalse(); + assertThat(STAND.isBust()).isFalse(); } } From 14c2d9bf7e81566189156cecac365ce1756c2db1 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 10:51:54 +0900 Subject: [PATCH 23/41] =?UTF-8?q?style=20(BetAomunt)=20:=20=EB=8B=A8?= =?UTF-8?q?=EC=88=9C=ED=9E=88=20=EC=95=A0=EB=A7=A4=ED=95=9C=20=EB=8F=88=20?= =?UTF-8?q?=EC=9D=B4=EB=9D=BC=EB=8A=94=20=ED=91=9C=ED=98=84=EB=B3=B4?= =?UTF-8?q?=EB=8B=A4,=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EC=9A=A9=EC=96=B4?= =?UTF-8?q?=EC=9D=B8=20=EB=B0=B0=ED=8C=85=20=EA=B8=88=EC=95=A1=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/money/{Money.java => BetAmount.java} | 12 ++++++------ .../money/{MoneyTest.java => BetAmountTest.java} | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) rename src/main/java/blackjack/domain/money/{Money.java => BetAmount.java} (63%) rename src/test/java/blackjack/domain/money/{MoneyTest.java => BetAmountTest.java} (77%) diff --git a/src/main/java/blackjack/domain/money/Money.java b/src/main/java/blackjack/domain/money/BetAmount.java similarity index 63% rename from src/main/java/blackjack/domain/money/Money.java rename to src/main/java/blackjack/domain/money/BetAmount.java index 5a9312a51c8..36742c257f4 100644 --- a/src/main/java/blackjack/domain/money/Money.java +++ b/src/main/java/blackjack/domain/money/BetAmount.java @@ -1,10 +1,10 @@ package blackjack.domain.money; -public class Money { +public class BetAmount { private final int value; - public Money(int value) { + public BetAmount(int value) { validatePositive(value); this.value = value; } @@ -15,13 +15,13 @@ private void validatePositive(int value) { } } - public Money add(Money money) { - return new Money(this.value + money.value); + public BetAmount add(BetAmount betAmount) { + return new BetAmount(this.value + betAmount.value); } - public Money multiply(double multiplier) { + public BetAmount multiply(double multiplier) { int newValue = (int) Math.ceil(this.value * multiplier); - return new Money(newValue); + return new BetAmount(newValue); } public int toInt() { diff --git a/src/test/java/blackjack/domain/money/MoneyTest.java b/src/test/java/blackjack/domain/money/BetAmountTest.java similarity index 77% rename from src/test/java/blackjack/domain/money/MoneyTest.java rename to src/test/java/blackjack/domain/money/BetAmountTest.java index 6c3c501c5e8..2866b2451f0 100644 --- a/src/test/java/blackjack/domain/money/MoneyTest.java +++ b/src/test/java/blackjack/domain/money/BetAmountTest.java @@ -8,14 +8,14 @@ import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; -class MoneyTest { +class BetAmountTest { @DisplayName("금액은 0 또는 음수가 될 수 없다") @ParameterizedTest @ValueSource(ints = {0, -1, -1000}) void validateTest_whenValueIsNotPositive_throwException(int value) { - assertThatThrownBy(() -> new Money(value)) + assertThatThrownBy(() -> new BetAmount(value)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("금액은 양수이어야 합니다."); } @@ -24,10 +24,10 @@ void validateTest_whenValueIsNotPositive_throwException(int value) { @ParameterizedTest @CsvSource({"1500, 1500, 3000", "1234, 5555, 6789", "10, 1, 11"}) void addTest(int value1, int value2, int expected) { - Money money1 = new Money(value1); - Money money2 = new Money(value2); + BetAmount betAmount1 = new BetAmount(value1); + BetAmount betAmount2 = new BetAmount(value2); - Money actual = money1.add(money2); + BetAmount actual = betAmount1.add(betAmount2); assertThat(actual.toInt()).isEqualTo(expected); } @@ -36,9 +36,9 @@ void addTest(int value1, int value2, int expected) { @ParameterizedTest @CsvSource({"1.5, 1500", "2, 2000", "2.25, 2250"}) void multiplyTest(double multiplier, int expected) { - Money money = new Money(1000); + BetAmount betAmount = new BetAmount(1000); - Money actual = money.multiply(multiplier); + BetAmount actual = betAmount.multiply(multiplier); assertThat(actual.toInt()).isEqualTo(expected); } From cf300b754d624b0076ea90d9aab7548434bbda26 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 11:12:08 +0900 Subject: [PATCH 24/41] =?UTF-8?q?feat=20(Profit)=20:=20=EB=B0=B0=ED=8C=85?= =?UTF-8?q?=20=EA=B8=88=EC=95=A1=EA=B3=BC=20=EC=8A=B9=ED=8C=A8=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=9D=BC=20=EC=9D=B4=EC=9D=B5=EC=9D=84=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/domain/money/BetAmount.java | 2 +- .../java/blackjack/domain/money/Profit.java | 45 +++++++++++++++++++ .../blackjack/domain/money/BetAmountTest.java | 8 ++-- .../blackjack/domain/money/ProfitTest.java | 44 ++++++++++++++++++ 4 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 src/main/java/blackjack/domain/money/Profit.java create mode 100644 src/test/java/blackjack/domain/money/ProfitTest.java diff --git a/src/main/java/blackjack/domain/money/BetAmount.java b/src/main/java/blackjack/domain/money/BetAmount.java index 36742c257f4..86f561e124c 100644 --- a/src/main/java/blackjack/domain/money/BetAmount.java +++ b/src/main/java/blackjack/domain/money/BetAmount.java @@ -11,7 +11,7 @@ public BetAmount(int value) { private void validatePositive(int value) { if (value <= 0) { - throw new IllegalArgumentException("금액은 양수이어야 합니다."); + throw new IllegalArgumentException("배팅 금액은 양수이어야 합니다."); } } diff --git a/src/main/java/blackjack/domain/money/Profit.java b/src/main/java/blackjack/domain/money/Profit.java new file mode 100644 index 00000000000..9f9f2e6f0d5 --- /dev/null +++ b/src/main/java/blackjack/domain/money/Profit.java @@ -0,0 +1,45 @@ +package blackjack.domain.money; + +import java.util.Objects; + +public class Profit { + + private final int value; + + public Profit(int value) { + this.value = value; + } + + public static Profit win(BetAmount amount) { + return new Profit(amount.toInt()); + } + + public static Profit win(BetAmount amount, double multiplier) { + return new Profit((int) (amount.toInt() * multiplier)); + } + + public static Profit lose(BetAmount amount) { + return new Profit(-1 * amount.toInt()); + } + + public int toInt() { + return value; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + Profit profit = (Profit) object; + return value == profit.value; + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/test/java/blackjack/domain/money/BetAmountTest.java b/src/test/java/blackjack/domain/money/BetAmountTest.java index 2866b2451f0..013aab7a439 100644 --- a/src/test/java/blackjack/domain/money/BetAmountTest.java +++ b/src/test/java/blackjack/domain/money/BetAmountTest.java @@ -10,17 +10,17 @@ class BetAmountTest { - @DisplayName("금액은 0 또는 음수가 될 수 없다") + @DisplayName("배팅 금액은 0 또는 음수가 될 수 없다") @ParameterizedTest @ValueSource(ints = {0, -1, -1000}) void validateTest_whenValueIsNotPositive_throwException(int value) { assertThatThrownBy(() -> new BetAmount(value)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("금액은 양수이어야 합니다."); + .hasMessage("배팅 금액은 양수이어야 합니다."); } - @DisplayName("금액은 서로 더할 수 있다") + @DisplayName("배팅 금액은 서로 더할 수 있다") @ParameterizedTest @CsvSource({"1500, 1500, 3000", "1234, 5555, 6789", "10, 1, 11"}) void addTest(int value1, int value2, int expected) { @@ -32,7 +32,7 @@ void addTest(int value1, int value2, int expected) { assertThat(actual.toInt()).isEqualTo(expected); } - @DisplayName("금액은 해당 배수만큼 곱할 수 있다") + @DisplayName("배팅 금액은 해당 배수만큼 곱할 수 있다") @ParameterizedTest @CsvSource({"1.5, 1500", "2, 2000", "2.25, 2250"}) void multiplyTest(double multiplier, int expected) { diff --git a/src/test/java/blackjack/domain/money/ProfitTest.java b/src/test/java/blackjack/domain/money/ProfitTest.java new file mode 100644 index 00000000000..04fb2190710 --- /dev/null +++ b/src/test/java/blackjack/domain/money/ProfitTest.java @@ -0,0 +1,44 @@ +package blackjack.domain.money; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +public class ProfitTest { + + @DisplayName("주어진 배팅 금액 만큼 얻는 이익을 계산할 수 있다.") + @ParameterizedTest + @ValueSource(ints = {1, 10, 1000}) + void createTest_winMoney(int amount) { + BetAmount betAmount = new BetAmount(amount); + + Profit profit = Profit.win(betAmount); + + assertThat(profit.toInt()).isEqualTo(amount); + } + + @DisplayName("주어진 배팅 금액에 특정 배수만큼 얻는 이익을 계산할 수 있다.") + @ParameterizedTest + @CsvSource({"1000, 2, 2000", "20, 1.5, 30", "1111, 1.5, 1666"}) + void createTest_winMoneyWithMultiplier(int amount, double multiplier, int expected) { + BetAmount betAmount = new BetAmount(amount); + + Profit profit = Profit.win(betAmount, multiplier); + + assertThat(profit.toInt()).isEqualTo(expected); + } + + @DisplayName("주어진 배팅 금액 만큼 잃는 이익을 계산할 수 있다.") + @ParameterizedTest + @ValueSource(ints = {1, 10, 1000}) + void createTest_loseMoney(int amount) { + BetAmount betAmount = new BetAmount(amount); + + Profit profit = Profit.lose(betAmount); + + assertThat(profit.toInt()).isEqualTo(-1 * amount); + } +} From 2ddd8debd88e11502af437a372326cdfab3fc60e Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 11:21:12 +0900 Subject: [PATCH 25/41] =?UTF-8?q?feat=20(Profit)=20:=20=EB=91=90=20?= =?UTF-8?q?=EC=9D=B4=EC=9D=B5=EC=9D=84=20=EB=8D=94=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/money/Profit.java | 4 ++++ src/test/java/blackjack/domain/money/ProfitTest.java | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/main/java/blackjack/domain/money/Profit.java b/src/main/java/blackjack/domain/money/Profit.java index 9f9f2e6f0d5..c95e14ed362 100644 --- a/src/main/java/blackjack/domain/money/Profit.java +++ b/src/main/java/blackjack/domain/money/Profit.java @@ -22,6 +22,10 @@ public static Profit lose(BetAmount amount) { return new Profit(-1 * amount.toInt()); } + public Profit add(Profit profit) { + return new Profit(this.value + profit.value); + } + public int toInt() { return value; } diff --git a/src/test/java/blackjack/domain/money/ProfitTest.java b/src/test/java/blackjack/domain/money/ProfitTest.java index 04fb2190710..75f59a763fd 100644 --- a/src/test/java/blackjack/domain/money/ProfitTest.java +++ b/src/test/java/blackjack/domain/money/ProfitTest.java @@ -41,4 +41,16 @@ void createTest_loseMoney(int amount) { assertThat(profit.toInt()).isEqualTo(-1 * amount); } + + @DisplayName("두 이익을 더하여 계산할 수 있다.") + @ParameterizedTest + @CsvSource({"20, -10", "10, -20", "-20, -10", "20, 10"}) + void addTest(int value, int addedValue) { + Profit profit = new Profit(value); + Profit addedProfit = new Profit(addedValue); + + Profit actual = profit.add(addedProfit); + + assertThat(actual.toInt()).isEqualTo(value + addedValue); + } } From 58e99250235e3c07c2e521205b6283286377c30e Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 11:40:53 +0900 Subject: [PATCH 26/41] =?UTF-8?q?feat=20(Profit)=20:=20=EC=96=BB=EC=9D=80?= =?UTF-8?q?=20=EB=A7=8C=ED=81=BC=20=EC=9E=83=EC=9D=80=20=EC=9D=B4=EC=9D=B5?= =?UTF-8?q?=EC=9D=84,=20=EC=9E=83=EC=9D=80=20=EB=A7=8C=ED=81=BC=20?= =?UTF-8?q?=EC=96=BB=EC=9D=80=20=EC=9D=B4=EC=9D=B5=EC=9D=84=20=EA=B5=AC?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/money/Profit.java | 6 ++++++ src/test/java/blackjack/domain/money/ProfitTest.java | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/main/java/blackjack/domain/money/Profit.java b/src/main/java/blackjack/domain/money/Profit.java index c95e14ed362..25ef78d546c 100644 --- a/src/main/java/blackjack/domain/money/Profit.java +++ b/src/main/java/blackjack/domain/money/Profit.java @@ -4,6 +4,8 @@ public class Profit { + public static final Profit ZERO = new Profit(0); + private final int value; public Profit(int value) { @@ -26,6 +28,10 @@ public Profit add(Profit profit) { return new Profit(this.value + profit.value); } + public Profit reverse() { + return new Profit(-1 * value); + } + public int toInt() { return value; } diff --git a/src/test/java/blackjack/domain/money/ProfitTest.java b/src/test/java/blackjack/domain/money/ProfitTest.java index 75f59a763fd..4baeea1292b 100644 --- a/src/test/java/blackjack/domain/money/ProfitTest.java +++ b/src/test/java/blackjack/domain/money/ProfitTest.java @@ -53,4 +53,15 @@ void addTest(int value, int addedValue) { assertThat(actual.toInt()).isEqualTo(value + addedValue); } + + @DisplayName("얻은 만큼 잃은 이익을, 잃은 만큼 얻은 이익을 구할 수 있다.") + @ParameterizedTest + @CsvSource({"20, -20", "-10, 10", "-1, 1", "0, 0"}) + void reverseTest(int value, int expected) { + Profit profit = new Profit(value); + + Profit actual = profit.reverse(); + + assertThat(actual.toInt()).isEqualTo(expected); + } } From 51e5232d63b884350dac4d088f92d47f25ab8926 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 11:45:29 +0900 Subject: [PATCH 27/41] =?UTF-8?q?feat=20(SingleMatchResult)=20:=20?= =?UTF-8?q?=EB=A7=A4=EC=B9=98=20=EA=B2=B0=EA=B3=BC=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=9D=BC=20=ED=94=8C=EB=A0=88=EC=9D=B4=EC=96=B4,=20=EB=94=9C?= =?UTF-8?q?=EB=9F=AC=EC=9D=98=20=EC=9D=B4=EC=9D=B5=EC=9D=84=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/handrank/SingleMatchResult.java | 27 +++++++++-- .../handrank/SingleMatchResultTest.java | 45 +++++++++++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 src/test/java/blackjack/domain/handrank/SingleMatchResultTest.java diff --git a/src/main/java/blackjack/domain/handrank/SingleMatchResult.java b/src/main/java/blackjack/domain/handrank/SingleMatchResult.java index c1d3a042f27..03e8e46b8f7 100644 --- a/src/main/java/blackjack/domain/handrank/SingleMatchResult.java +++ b/src/main/java/blackjack/domain/handrank/SingleMatchResult.java @@ -1,8 +1,27 @@ package blackjack.domain.handrank; +import blackjack.domain.money.BetAmount; +import blackjack.domain.money.Profit; +import java.util.function.Function; + public enum SingleMatchResult { - PLAYER_BLACKJACK, - PLAYER_WIN, - DRAW, - DEALER_WIN + + PLAYER_BLACKJACK(amount -> Profit.win(amount, 1.5)), + PLAYER_WIN(Profit::win), + DRAW(amount -> Profit.ZERO), + DEALER_WIN(Profit::lose); + + private final Function playerProfitFunction; + + SingleMatchResult(Function playerProfitFunction) { + this.playerProfitFunction = playerProfitFunction; + } + + public Profit calculatePlayerProfit(BetAmount betAmount) { + return playerProfitFunction.apply(betAmount); + } + + public Profit calculateDealerProfit(BetAmount betAmount) { + return calculatePlayerProfit(betAmount).reverse(); + } } diff --git a/src/test/java/blackjack/domain/handrank/SingleMatchResultTest.java b/src/test/java/blackjack/domain/handrank/SingleMatchResultTest.java new file mode 100644 index 00000000000..40854108b40 --- /dev/null +++ b/src/test/java/blackjack/domain/handrank/SingleMatchResultTest.java @@ -0,0 +1,45 @@ +package blackjack.domain.handrank; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.money.BetAmount; +import blackjack.domain.money.Profit; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class SingleMatchResultTest { + + + @DisplayName("승패 결과에 따라, 플레이어의 이익을 반환할 수 있다.") + @ParameterizedTest + @CsvSource({ + "1000, PLAYER_BLACKJACK, 1500", + "1000, PLAYER_WIN, 1000", + "1000, DRAW, 0", + "1000, DEALER_WIN, -1000" + }) + void calculatePlayerProfitTest(int amount, SingleMatchResult result, int expected) { + BetAmount betAmount = new BetAmount(amount); + + Profit actual = result.calculatePlayerProfit(betAmount); + + assertThat(actual.toInt()).isEqualTo(expected); + } + + @DisplayName("승패 결과에 따라, 딜러의 이익을 반환할 수 있다.") + @ParameterizedTest + @CsvSource({ + "1000, PLAYER_BLACKJACK, -1500", + "1000, PLAYER_WIN, -1000", + "1000, DRAW, 0", + "1000, DEALER_WIN, 1000" + }) + void calculateDealerProfitTest(int amount, SingleMatchResult result, int expected) { + BetAmount betAmount = new BetAmount(amount); + + Profit actual = result.calculateDealerProfit(betAmount); + + assertThat(actual.toInt()).isEqualTo(expected); + } +} From c707523e20537f3965ce15ec6a58667a5f4a8f2b Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 12:00:16 +0900 Subject: [PATCH 28/41] =?UTF-8?q?feat=20(Dealer)=20:=20=ED=94=8C=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=EB=93=A4=EC=9D=98=20=EC=9D=B4=EC=9D=B5=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=EB=A5=BC=20=EC=B6=9C=EB=A0=A5=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/domain/participant/Dealer.java | 11 ++++++++++ .../domain/participant/Participant.java | 6 ++++++ .../blackjack/domain/participant/Player.java | 20 +++++++++++++++++- .../blackjack/domain/participant/Players.java | 2 +- .../domain/participant/DealerTest.java | 21 +++++++++++++++++++ 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/main/java/blackjack/domain/participant/Dealer.java b/src/main/java/blackjack/domain/participant/Dealer.java index 95d651262f2..0f318bc0ba2 100644 --- a/src/main/java/blackjack/domain/participant/Dealer.java +++ b/src/main/java/blackjack/domain/participant/Dealer.java @@ -1,8 +1,11 @@ package blackjack.domain.participant; import blackjack.domain.card.Card; +import blackjack.domain.money.Profit; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; public class Dealer extends Participant { @@ -31,6 +34,14 @@ private boolean isLose(Player player) { return !isWin(player); } + public Map calculatePlayersProfit(Players players) { + Map totalResult = new LinkedHashMap<>(); + for (Player player : players.getPlayers()) { + totalResult.put(player, player.match(this)); + } + return totalResult; + } + public int calculateWinCount(Players players) { return (int) players.getPlayers().stream() .filter(this::isWin) diff --git a/src/main/java/blackjack/domain/participant/Participant.java b/src/main/java/blackjack/domain/participant/Participant.java index 52028aab9c9..ef92f4ba024 100644 --- a/src/main/java/blackjack/domain/participant/Participant.java +++ b/src/main/java/blackjack/domain/participant/Participant.java @@ -3,6 +3,8 @@ import blackjack.domain.card.Card; import blackjack.domain.card.Deck; import blackjack.domain.card.Hand; +import blackjack.domain.handrank.HandRankFactory; +import blackjack.domain.handrank.HankRank; import java.util.List; public abstract class Participant { @@ -48,6 +50,10 @@ public final void add(Card card) { hand = hand.add(card); } + protected final HankRank getHandRank() { + return HandRankFactory.from(hand); + } + public final List getStartCards() { return getCards().subList(0, getStartCardSize()); } diff --git a/src/main/java/blackjack/domain/participant/Player.java b/src/main/java/blackjack/domain/participant/Player.java index 85ad34cbe67..21af77f8d2f 100644 --- a/src/main/java/blackjack/domain/participant/Player.java +++ b/src/main/java/blackjack/domain/participant/Player.java @@ -1,6 +1,9 @@ package blackjack.domain.participant; import blackjack.domain.card.Card; +import blackjack.domain.handrank.SingleMatchResult; +import blackjack.domain.money.BetAmount; +import blackjack.domain.money.Profit; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -10,20 +13,35 @@ public class Player extends Participant { private static final int START_CARD_SIZE = 2; private final Name name; + private final BetAmount betAmount; - Player(List cards, Name name) { + Player(List cards, Name name, BetAmount betAmount) { super(cards); this.name = Objects.requireNonNull(name); + this.betAmount = Objects.requireNonNull(betAmount); + } + + Player(List cards, Name name) { + this(cards, name, new BetAmount(1)); } public static Player from(String name) { return new Player(Collections.emptyList(), new Name(name)); } + public static Player from(String name, int money) { + return new Player(Collections.emptyList(), new Name(name), new BetAmount(money)); + } + public boolean isWin(Dealer dealer) { return !dealer.isWin(this); } + public Profit match(Dealer dealer) { + SingleMatchResult result = dealer.getHandRank().matchAtDealer(this.getHandRank()); + return result.calculatePlayerProfit(betAmount); + } + public boolean isEqualName(Player player) { return name.equals(player.name); } diff --git a/src/main/java/blackjack/domain/participant/Players.java b/src/main/java/blackjack/domain/participant/Players.java index 5a3acfa9183..cf5915ae001 100644 --- a/src/main/java/blackjack/domain/participant/Players.java +++ b/src/main/java/blackjack/domain/participant/Players.java @@ -9,7 +9,7 @@ public class Players { private final List players; - private Players(List players) { + Players(List players) { validateSize(players); validateDistinct(players); this.players = players; diff --git a/src/test/java/blackjack/domain/participant/DealerTest.java b/src/test/java/blackjack/domain/participant/DealerTest.java index f0e911ff9de..91bd5438e87 100644 --- a/src/test/java/blackjack/domain/participant/DealerTest.java +++ b/src/test/java/blackjack/domain/participant/DealerTest.java @@ -8,7 +8,10 @@ import blackjack.domain.card.Shape; import blackjack.domain.card.Value; import blackjack.domain.fixture.CardsFixture; +import blackjack.domain.money.BetAmount; +import blackjack.domain.money.Profit; import java.util.List; +import java.util.Map; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -195,4 +198,22 @@ void whenPlayerScoreIsSmallerThanDealerScore_dealerWin() { assertThat(dealer.isWin(player)).isTrue(); } } + + @DisplayName("플레이어와 승패 판단을 할 수 있다.") + @Test + void calculatePlayersProfitTest() { + Player blackjackPlayer = new Player(CardsFixture.BLACKJACK, new Name("black"), new BetAmount(1_000)); + Player winPlayer = new Player(CardsFixture.CARDS_SCORE_21, new Name("win"), new BetAmount(2_000)); + Player losePlayer = new Player(CardsFixture.CARDS_SCORE_16, new Name("lose"), new BetAmount(3_000)); + Players players = new Players(List.of(blackjackPlayer, winPlayer, losePlayer)); + Dealer dealer = new Dealer(CardsFixture.CARDS_SCORE_17); + + Map matchResult = dealer.calculatePlayersProfit(players); + + assertThat(matchResult).containsExactly( + Map.entry(blackjackPlayer, new Profit(1_500)), + Map.entry(winPlayer, new Profit(2_000)), + Map.entry(losePlayer, new Profit(-3_000)) + ); + } } From 1ff93c78af3c8307f8e64befe3ab15cf97b7acdf Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 13:25:42 +0900 Subject: [PATCH 29/41] =?UTF-8?q?feat=20(Dealer)=20:=20=ED=94=8C=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=EB=93=A4=EA=B3=BC=20=EB=8C=80=EA=B2=B0=20?= =?UTF-8?q?=ED=9B=84,=20=EB=94=9C=EB=9F=AC=EC=9D=98=20=EC=9D=B4=EC=9D=B5?= =?UTF-8?q?=EC=9D=84=20=EA=B3=84=EC=82=B0=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/domain/participant/Dealer.java | 9 +++++++++ .../domain/participant/DealerTest.java | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main/java/blackjack/domain/participant/Dealer.java b/src/main/java/blackjack/domain/participant/Dealer.java index 0f318bc0ba2..c22f2f0b191 100644 --- a/src/main/java/blackjack/domain/participant/Dealer.java +++ b/src/main/java/blackjack/domain/participant/Dealer.java @@ -34,6 +34,15 @@ private boolean isLose(Player player) { return !isWin(player); } + public Profit calculateDealerProfit(Players players) { + Profit profit = Profit.ZERO; + for (Player player : players.getPlayers()) { + Profit dealerProfit = player.match(this).reverse(); + profit = profit.add(dealerProfit); + } + return profit; + } + public Map calculatePlayersProfit(Players players) { Map totalResult = new LinkedHashMap<>(); for (Player player : players.getPlayers()) { diff --git a/src/test/java/blackjack/domain/participant/DealerTest.java b/src/test/java/blackjack/domain/participant/DealerTest.java index 91bd5438e87..404559c8ffd 100644 --- a/src/test/java/blackjack/domain/participant/DealerTest.java +++ b/src/test/java/blackjack/domain/participant/DealerTest.java @@ -199,7 +199,7 @@ void whenPlayerScoreIsSmallerThanDealerScore_dealerWin() { } } - @DisplayName("플레이어와 승패 판단을 할 수 있다.") + @DisplayName("플레이어들의 이익을 계산할 수 있다.") @Test void calculatePlayersProfitTest() { Player blackjackPlayer = new Player(CardsFixture.BLACKJACK, new Name("black"), new BetAmount(1_000)); @@ -216,4 +216,19 @@ void calculatePlayersProfitTest() { Map.entry(losePlayer, new Profit(-3_000)) ); } + + @DisplayName("딜러의 이익을 계산할 수 있다.") + @Test + void calculateDealerProfitTest() { + Player blackjackPlayer = new Player(CardsFixture.BLACKJACK, new Name("black"), new BetAmount(1_000)); + Player winPlayer = new Player(CardsFixture.CARDS_SCORE_21, new Name("win"), new BetAmount(2_000)); + Player losePlayer = new Player(CardsFixture.CARDS_SCORE_16, new Name("lose"), new BetAmount(3_000)); + Players players = new Players(List.of(blackjackPlayer, winPlayer, losePlayer)); + Dealer dealer = new Dealer(CardsFixture.CARDS_SCORE_17); + int expected = -1_500 - 2_000 + 3_000; + + Profit dealerProfit = dealer.calculateDealerProfit(players); + + assertThat(dealerProfit.toInt()).isEqualTo(expected); + } } From 25408c2faf37d12f6d92f76296a4e044a209f8f9 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 13:31:36 +0900 Subject: [PATCH 30/41] =?UTF-8?q?feat=20(InputView)=20:=20=EB=B0=B0?= =?UTF-8?q?=ED=8C=85=20=EA=B8=88=EC=95=A1=EC=9D=84=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=EB=B0=9B=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/view/InputView.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java index eb40910c95f..b4efc96358b 100644 --- a/src/main/java/blackjack/view/InputView.java +++ b/src/main/java/blackjack/view/InputView.java @@ -16,6 +16,19 @@ public List inputPlayerNames() { return List.of(names.split(NAME_DELIMITER, -1)); } + public int inputBetAmount(String name) { + System.out.printf("%n%s의 배팅 금액은?%n", name); + return inputInt(); + } + + private int inputInt() { + try { + return Integer.parseInt(SCANNER.nextLine()); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException("숫자를 입력하여야 합니다.", exception); + } + } + public boolean isPlayerWantDraw(String name) { System.out.printf("%s는 한장의 카드를 더 받겠습니까?(예는 %s, 아니오는 %s)%n", name, WANT_DRAW_INPUT, WANT_NOT_DRAW_INPUT); String input = SCANNER.nextLine(); From 8e7452052c1315c35c9db67c0fb3d3d44d9deda3 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 13:36:36 +0900 Subject: [PATCH 31/41] =?UTF-8?q?feat=20(OutputView)=20:=20=EB=94=9C?= =?UTF-8?q?=EB=9F=AC=20=EB=B0=8F=20=ED=94=8C=EB=A0=88=EC=9D=B4=EC=96=B4?= =?UTF-8?q?=EB=93=A4=EC=9D=98=20=EC=B5=9C=EC=A2=85=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/view/OutputView.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/blackjack/view/OutputView.java b/src/main/java/blackjack/view/OutputView.java index c8174e591a9..70fa4c87b58 100644 --- a/src/main/java/blackjack/view/OutputView.java +++ b/src/main/java/blackjack/view/OutputView.java @@ -3,11 +3,13 @@ import blackjack.domain.card.Card; import blackjack.domain.card.Shape; import blackjack.domain.card.Value; +import blackjack.domain.money.Profit; import blackjack.domain.participant.Dealer; import blackjack.domain.participant.Player; import blackjack.domain.participant.Players; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.stream.Collectors; public class OutputView { @@ -120,4 +122,14 @@ public void printPlayerMatchResult(String name, boolean isWin) { } System.out.println(name + ": 패"); } + + public void printMatchResult(Profit dealerProfit, Map playersProfit) { + System.out.println("## 최종 승패"); + System.out.println("딜러: " + dealerProfit.toInt()); + for (Entry profitEntry : playersProfit.entrySet()) { + String name = profitEntry.getKey().getName(); + int profit = profitEntry.getValue().toInt(); + System.out.println(name + ": " + profit); + } + } } From 8e13c476faa43fdb7a22db262cc19fc2d7c0948a Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 13:48:57 +0900 Subject: [PATCH 32/41] =?UTF-8?q?feat=20(BlackjackGame)=20:=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EB=90=9C=20=EC=9A=94=EA=B5=AC=20=EC=82=AC=ED=95=AD?= =?UTF-8?q?=EC=9D=84=20=EB=B0=98=EC=98=81=ED=95=98=EC=97=AC,=20=EC=A0=84?= =?UTF-8?q?=EB=B0=98=EC=A0=81=EC=9D=B8=20=EC=A0=9C=EC=96=B4=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/BlackJackGame.java | 17 ++++++++++------- .../blackjack/domain/participant/Players.java | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/blackjack/BlackJackGame.java b/src/main/java/blackjack/BlackJackGame.java index 8acbbc3330a..3ee1204d424 100644 --- a/src/main/java/blackjack/BlackJackGame.java +++ b/src/main/java/blackjack/BlackJackGame.java @@ -1,12 +1,14 @@ package blackjack; import blackjack.domain.card.Deck; +import blackjack.domain.money.Profit; import blackjack.domain.participant.Dealer; import blackjack.domain.participant.Player; import blackjack.domain.participant.Players; import blackjack.view.InputView; import blackjack.view.OutputView; import java.util.List; +import java.util.Map; public class BlackJackGame { @@ -25,7 +27,10 @@ public void run() { private Players createPlayers() { List names = inputView.inputPlayerNames(); - return Players.from(names); + List players = names.stream() + .map(name -> Player.from(name, inputView.inputBetAmount(name))) + .toList(); + return new Players(players); } private void drawStartCards(Dealer dealer, Players players, Deck deck) { @@ -57,11 +62,9 @@ private void playTurn(Dealer dealer, Deck deck) { private void printResult(Dealer dealer, Players players) { outputView.printEndingStatus(dealer, players); - int winCount = dealer.calculateWinCount(players); - int loseCount = dealer.calculateLoseCount(players); - outputView.printDealerMatchResult(winCount, loseCount); - for (Player player : players.getPlayers()) { - outputView.printPlayerMatchResult(player.getName(), player.isWin(dealer)); - } + + Profit dealerProfit = dealer.calculateDealerProfit(players); + Map playersProfit = dealer.calculatePlayersProfit(players); + outputView.printMatchResult(dealerProfit, playersProfit); } } diff --git a/src/main/java/blackjack/domain/participant/Players.java b/src/main/java/blackjack/domain/participant/Players.java index cf5915ae001..5bf6b00b563 100644 --- a/src/main/java/blackjack/domain/participant/Players.java +++ b/src/main/java/blackjack/domain/participant/Players.java @@ -9,7 +9,7 @@ public class Players { private final List players; - Players(List players) { + public Players(List players) { validateSize(players); validateDistinct(players); this.players = players; From 69045b81e6425e4e459cd3790cd20dc198ac6956 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 13:54:54 +0900 Subject: [PATCH 33/41] =?UTF-8?q?refactor=20(OutputView)=20:=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20=EC=9D=BC?= =?UTF-8?q?=EB=B6=80=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC=20?= =?UTF-8?q?=EC=8B=A4=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/view/OutputView.java | 27 ++++++++------------ 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/main/java/blackjack/view/OutputView.java b/src/main/java/blackjack/view/OutputView.java index 70fa4c87b58..f18fa21acdb 100644 --- a/src/main/java/blackjack/view/OutputView.java +++ b/src/main/java/blackjack/view/OutputView.java @@ -110,26 +110,21 @@ public void printDealerDraw() { System.out.print("딜러는 16이하라 한장의 카드를 더 받았습니다."); } - public void printDealerMatchResult(int winCount, int loseCount) { + public void printMatchResult(Profit dealerProfit, Map playersProfit) { System.out.println("## 최종 승패"); - System.out.printf("딜러 : %d승 %d패%n", winCount, loseCount); - } - - public void printPlayerMatchResult(String name, boolean isWin) { - if (isWin) { - System.out.println(name + ": 승"); - return; + printDealerResult(dealerProfit); + for (Entry profitEntry : playersProfit.entrySet()) { + printPlayerResult(profitEntry.getKey(), profitEntry.getValue()); } - System.out.println(name + ": 패"); } - public void printMatchResult(Profit dealerProfit, Map playersProfit) { - System.out.println("## 최종 승패"); + private void printDealerResult(Profit dealerProfit) { System.out.println("딜러: " + dealerProfit.toInt()); - for (Entry profitEntry : playersProfit.entrySet()) { - String name = profitEntry.getKey().getName(); - int profit = profitEntry.getValue().toInt(); - System.out.println(name + ": " + profit); - } + } + + private void printPlayerResult(Player player, Profit profit) { + String name = player.getName(); + int profitValue = profit.toInt(); + System.out.println(name + ": " + profitValue); } } From 9d1e979d7b15756d1164e0a61d3c556267652726 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 14:48:05 +0900 Subject: [PATCH 34/41] =?UTF-8?q?feat=20(Players)=20:=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=20=ED=99=95=EC=9D=B8=20=EB=A1=9C=EC=A7=81=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20=EC=8B=A4=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/domain/participant/Players.java | 21 ++++++---------- .../domain/participant/PlayersTest.java | 25 +++++++++++-------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/main/java/blackjack/domain/participant/Players.java b/src/main/java/blackjack/domain/participant/Players.java index 5bf6b00b563..75d23dd67e8 100644 --- a/src/main/java/blackjack/domain/participant/Players.java +++ b/src/main/java/blackjack/domain/participant/Players.java @@ -2,6 +2,7 @@ import blackjack.domain.card.Deck; import java.util.List; +import java.util.stream.IntStream; public class Players { @@ -15,13 +16,6 @@ public Players(List players) { this.players = players; } - public static Players from(List names) { - List players = names.stream() - .map(Player::from) - .toList(); - return new Players(players); - } - private void validateSize(List players) { if (players.isEmpty()) { throw new IllegalArgumentException("최소 한 명의 플레이어가 있어야 합니다."); @@ -31,7 +25,6 @@ private void validateSize(List players) { } } - // TODO 같은 참조값을 가지는 경우 생각 private void validateDistinct(List players) { if (isDuplicated(players)) { throw new IllegalArgumentException("중복된 이름을 사용할 수 없습니다."); @@ -39,14 +32,14 @@ private void validateDistinct(List players) { } private boolean isDuplicated(List players) { - return players.stream() - .anyMatch(player1 -> isDuplicated(players, player1)); + return IntStream.range(0, players.size()) + .anyMatch(index -> isDuplicated(players, index)); } - private boolean isDuplicated(List players, Player player) { - return players.stream() - .filter(another -> player != another) - .anyMatch(another -> player.isEqualName(another)); + private boolean isDuplicated(List players, int index) { + return IntStream.range(0, players.size()) + .filter(currentIndex -> currentIndex != index) + .anyMatch(currentIndex -> players.get(currentIndex).isEqualName(players.get(index))); } public void drawStartCards(Deck deck) { diff --git a/src/test/java/blackjack/domain/participant/PlayersTest.java b/src/test/java/blackjack/domain/participant/PlayersTest.java index a776e175789..543e576e3fa 100644 --- a/src/test/java/blackjack/domain/participant/PlayersTest.java +++ b/src/test/java/blackjack/domain/participant/PlayersTest.java @@ -12,20 +12,26 @@ class PlayersTest { + private static final Player PLAYER1 = Player.from("1", 1000); + private static final Player PLAYER2 = Player.from("2", 2000); + private static final Player PLAYER3 = Player.from("3", 3000); + private static final Player PLAYER4 = Player.from("4", 4000); + private static final Player PLAYER5 = Player.from("5", 5000); + @DisplayName("최소 한 명 이상의 플레이어가 존재해야 한다.") @Test void validateTest_countOfPlayersIsZero_throwException() { - assertThatThrownBy(() -> Players.from(Collections.emptyList())) + assertThatThrownBy(() -> new Players(Collections.emptyList())) .isInstanceOf(IllegalArgumentException.class) .hasMessage("최소 한 명의 플레이어가 있어야 합니다."); } - @DisplayName("N 명 이상의 플레이어를 가지면 예외가 발생한다.") + @DisplayName("4명 이상의 플레이어를 가지면 예외가 발생한다.") @Test void validateTest_tooManyPlayers_throwException() { - List manyNames = List.of("1", "2", "3", "4", "5"); + List manyPlayers = List.of(PLAYER1, PLAYER2, PLAYER3, PLAYER4, PLAYER5); - assertThatThrownBy(() -> Players.from(manyNames)) + assertThatThrownBy(() -> new Players(manyPlayers)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("최대 4명의 플레이어만 참여 가능합니다."); } @@ -33,9 +39,9 @@ void validateTest_tooManyPlayers_throwException() { @DisplayName("중복된 이름을 사용하면, 예외가 발생한다.") @Test void validateTest_whenNameIsDuplicated_throwException() { - List duplicatedNames = List.of("짱수", "커찬", "커찬"); + List duplicatedPlayers = List.of(PLAYER1, PLAYER1, PLAYER2); - assertThatThrownBy(() -> Players.from(duplicatedNames)) + assertThatThrownBy(() -> new Players(duplicatedPlayers)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("중복된 이름을 사용할 수 없습니다."); } @@ -43,15 +49,14 @@ void validateTest_whenNameIsDuplicated_throwException() { @DisplayName("게임을 시작할 때 모든 플레이어들은 카드를 두 장 뽑는다.") @Test void drawStartCardsTest() { - Players players = Players.from(List.of("1", "2")); + Players players = new Players(List.of(PLAYER1, PLAYER2)); Deck deck = Deck.createShuffledDeck(); players.drawStartCards(deck); - List allPlayers = players.getPlayers(); assertAll( - () -> assertThat(allPlayers.get(0).getCards()).hasSize(2), - () -> assertThat(allPlayers.get(1).getCards()).hasSize(2) + () -> assertThat(PLAYER1.getCards()).hasSize(2), + () -> assertThat(PLAYER2.getCards()).hasSize(2) ); } } From ddad41a214f8c48d2ea715be1fba9ea58211ae4c Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 15:17:02 +0900 Subject: [PATCH 35/41] =?UTF-8?q?refactor=20(Participant)=20:=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackjack/domain/participant/Dealer.java | 30 +----- .../domain/participant/Participant.java | 8 -- .../blackjack/domain/participant/Player.java | 10 +- .../domain/participant/DealerTest.java | 97 ------------------- .../domain/participant/PlayerTest.java | 2 +- 5 files changed, 4 insertions(+), 143 deletions(-) diff --git a/src/main/java/blackjack/domain/participant/Dealer.java b/src/main/java/blackjack/domain/participant/Dealer.java index c22f2f0b191..06d75111e8c 100644 --- a/src/main/java/blackjack/domain/participant/Dealer.java +++ b/src/main/java/blackjack/domain/participant/Dealer.java @@ -20,24 +20,10 @@ public Dealer() { super(cards); } - public boolean isWin(Player player) { - if (player.isBusted() || this.isBlackjack()) { - return true; - } - if (this.isBusted() || player.isBlackjack()) { - return false; - } - return this.calculateScore() >= player.calculateScore(); - } - - private boolean isLose(Player player) { - return !isWin(player); - } - public Profit calculateDealerProfit(Players players) { Profit profit = Profit.ZERO; for (Player player : players.getPlayers()) { - Profit dealerProfit = player.match(this).reverse(); + Profit dealerProfit = player.calculateProfit(this).reverse(); profit = profit.add(dealerProfit); } return profit; @@ -46,23 +32,11 @@ public Profit calculateDealerProfit(Players players) { public Map calculatePlayersProfit(Players players) { Map totalResult = new LinkedHashMap<>(); for (Player player : players.getPlayers()) { - totalResult.put(player, player.match(this)); + totalResult.put(player, player.calculateProfit(this)); } return totalResult; } - public int calculateWinCount(Players players) { - return (int) players.getPlayers().stream() - .filter(this::isWin) - .count(); - } - - public int calculateLoseCount(Players players) { - return (int) players.getPlayers().stream() - .filter(this::isLose) - .count(); - } - @Override protected int getMaxDrawableScore() { return DRAWABLE_MAX_SCORE; diff --git a/src/main/java/blackjack/domain/participant/Participant.java b/src/main/java/blackjack/domain/participant/Participant.java index ef92f4ba024..d66308212e9 100644 --- a/src/main/java/blackjack/domain/participant/Participant.java +++ b/src/main/java/blackjack/domain/participant/Participant.java @@ -26,14 +26,6 @@ public final boolean isDrawable() { return calculateScore() <= getMaxDrawableScore(); } - public boolean isBlackjack() { - return hand.isBlackjack(); - } - - public final boolean isBusted() { - return hand.isBusted(); - } - public final void drawStartCards(Deck deck) { if (!hand.isEmpty()) { throw new IllegalStateException("이미 시작 카드를 뽑았습니다."); diff --git a/src/main/java/blackjack/domain/participant/Player.java b/src/main/java/blackjack/domain/participant/Player.java index 21af77f8d2f..e04aa84d775 100644 --- a/src/main/java/blackjack/domain/participant/Player.java +++ b/src/main/java/blackjack/domain/participant/Player.java @@ -25,19 +25,11 @@ public class Player extends Participant { this(cards, name, new BetAmount(1)); } - public static Player from(String name) { - return new Player(Collections.emptyList(), new Name(name)); - } - public static Player from(String name, int money) { return new Player(Collections.emptyList(), new Name(name), new BetAmount(money)); } - public boolean isWin(Dealer dealer) { - return !dealer.isWin(this); - } - - public Profit match(Dealer dealer) { + public Profit calculateProfit(Dealer dealer) { SingleMatchResult result = dealer.getHandRank().matchAtDealer(this.getHandRank()); return result.calculatePlayerProfit(betAmount); } diff --git a/src/test/java/blackjack/domain/participant/DealerTest.java b/src/test/java/blackjack/domain/participant/DealerTest.java index 404559c8ffd..8c90059fe9f 100644 --- a/src/test/java/blackjack/domain/participant/DealerTest.java +++ b/src/test/java/blackjack/domain/participant/DealerTest.java @@ -14,7 +14,6 @@ import java.util.Map; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -103,102 +102,6 @@ void addTest_whenScoreIsOverBound_throwException() { .hasMessage("더 이상 카드를 추가할 수 없습니다."); } - @DisplayName("플레이어와의 승패를 판단할 수 있다.") - @Nested - class IsWinTest { - - @DisplayName("플레이어가 21을 넘을 경우, 딜러가 이긴다.") - @ParameterizedTest - @MethodSource("dealerCards") - void whenPlayerBusted_dealerWin(List cards) { - Dealer dealer = new Dealer(cards); - Player player = new Player(CardsFixture.BUSTED, DEFAULT_NAME); - - assertThat(dealer.isWin(player)).isTrue(); - } - - static Stream> dealerCards() { - return Stream.of( - CardsFixture.BLACKJACK, - CardsFixture.CARDS_SCORE_4, - CardsFixture.CARDS_SCORE_16, - CardsFixture.BUSTED - ); - } - - @DisplayName("딜러만 21을 넘길 경우, 플레이어가 이긴다.") - @ParameterizedTest - @MethodSource("playerCards") - void whenOnlyDealerBusted_playerWin(List cards) { - Dealer dealer = new Dealer(CardsFixture.BUSTED); - Player player = new Player(cards, DEFAULT_NAME); - - assertThat(dealer.isWin(player)).isFalse(); - } - - static Stream> playerCards() { - return Stream.of( - CardsFixture.BLACKJACK, - CardsFixture.CARDS_SCORE_4, - CardsFixture.CARDS_SCORE_16 - ); - } - - @DisplayName("플레이어만 블랙잭일 경우, 플레이어가 이긴다.") - @Test - void whenPlayerOnlyBlackjack_playerWin() { - Player player = new Player(CardsFixture.BLACKJACK, DEFAULT_NAME); - Dealer dealer = new Dealer(CardsFixture.CARDS_SCORE_21); - - assertThat(dealer.isWin(player)).isFalse(); - } - - @DisplayName("딜러만 블랙잭일 경우, 딜러가 이긴다.") - @Test - void whenDealerOnlyBlackjack_dealerWin() { - Player player = new Player(CardsFixture.CARDS_SCORE_21, DEFAULT_NAME); - Dealer dealer = new Dealer(CardsFixture.BLACKJACK); - - assertThat(dealer.isWin(player)).isTrue(); - } - - @DisplayName("둘 다 21을 넘지 않을 경우, 플레이어가 딜러의 숫자보다 같다면 딜러가 이긴다.") - @ParameterizedTest - @MethodSource("sameCards") - void whenPlayerScoreIsEqualToDealerScore_dealerWin(List cards) { - Player player = new Player(cards, DEFAULT_NAME); - Dealer dealer = new Dealer(cards); - - assertThat(dealer.isWin(player)).isTrue(); - } - - static Stream> sameCards() { - return Stream.of( - CardsFixture.BLACKJACK, - CardsFixture.CARDS_SCORE_21, - CardsFixture.CARDS_SCORE_16 - ); - } - - @DisplayName("둘 다 21보다 작을 경우, 플레이어가 딜러의 숫자보다 크다면 플레이어가 이긴다.") - @Test - void whenPlayerScoreIsBiggerThanDealerScore_playerWin() { - Player player = new Player(CardsFixture.CARDS_SCORE_17, DEFAULT_NAME); - Dealer dealer = new Dealer(CardsFixture.CARDS_SCORE_16); - - assertThat(dealer.isWin(player)).isFalse(); - } - - @DisplayName("둘 다 21보다 작을 경우, 플레이어가 딜러의 숫자보다 작다면 딜러가 이긴다.") - @Test - void whenPlayerScoreIsSmallerThanDealerScore_dealerWin() { - Player player = new Player(CardsFixture.CARDS_SCORE_16, DEFAULT_NAME); - Dealer dealer = new Dealer(CardsFixture.CARDS_SCORE_17); - - assertThat(dealer.isWin(player)).isTrue(); - } - } - @DisplayName("플레이어들의 이익을 계산할 수 있다.") @Test void calculatePlayersProfitTest() { diff --git a/src/test/java/blackjack/domain/participant/PlayerTest.java b/src/test/java/blackjack/domain/participant/PlayerTest.java index 5eb9cd71901..ff78164aa33 100644 --- a/src/test/java/blackjack/domain/participant/PlayerTest.java +++ b/src/test/java/blackjack/domain/participant/PlayerTest.java @@ -79,7 +79,7 @@ void isDrawableTest_whenScoreIsOverBound_returnFalse() { @DisplayName("게임을 시작할 때는 카드를 두 장 뽑는다.") @Test void drawStartCardsTest() { - Player player = Player.from("name"); + Player player = Player.from("name", 1000); Deck deck = Deck.createShuffledDeck(); player.drawStartCards(deck); From 4a1481bd066e02573a829a4273fa1f7a21ba3862 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 15:24:43 +0900 Subject: [PATCH 36/41] =?UTF-8?q?refactor=20:=20SingleMatchResult,=20BetAm?= =?UTF-8?q?ount=EC=97=90=EC=84=9C=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80?= =?UTF-8?q?=20=EC=95=8A=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/handrank/SingleMatchResult.java | 4 --- .../blackjack/domain/money/BetAmount.java | 9 ------- .../handrank/SingleMatchResultTest.java | 16 ------------ .../blackjack/domain/money/BetAmountTest.java | 25 ------------------- 4 files changed, 54 deletions(-) diff --git a/src/main/java/blackjack/domain/handrank/SingleMatchResult.java b/src/main/java/blackjack/domain/handrank/SingleMatchResult.java index 03e8e46b8f7..b770fd3874a 100644 --- a/src/main/java/blackjack/domain/handrank/SingleMatchResult.java +++ b/src/main/java/blackjack/domain/handrank/SingleMatchResult.java @@ -20,8 +20,4 @@ public enum SingleMatchResult { public Profit calculatePlayerProfit(BetAmount betAmount) { return playerProfitFunction.apply(betAmount); } - - public Profit calculateDealerProfit(BetAmount betAmount) { - return calculatePlayerProfit(betAmount).reverse(); - } } diff --git a/src/main/java/blackjack/domain/money/BetAmount.java b/src/main/java/blackjack/domain/money/BetAmount.java index 86f561e124c..a94118e4f55 100644 --- a/src/main/java/blackjack/domain/money/BetAmount.java +++ b/src/main/java/blackjack/domain/money/BetAmount.java @@ -15,15 +15,6 @@ private void validatePositive(int value) { } } - public BetAmount add(BetAmount betAmount) { - return new BetAmount(this.value + betAmount.value); - } - - public BetAmount multiply(double multiplier) { - int newValue = (int) Math.ceil(this.value * multiplier); - return new BetAmount(newValue); - } - public int toInt() { return value; } diff --git a/src/test/java/blackjack/domain/handrank/SingleMatchResultTest.java b/src/test/java/blackjack/domain/handrank/SingleMatchResultTest.java index 40854108b40..4c3a07c0ea1 100644 --- a/src/test/java/blackjack/domain/handrank/SingleMatchResultTest.java +++ b/src/test/java/blackjack/domain/handrank/SingleMatchResultTest.java @@ -26,20 +26,4 @@ void calculatePlayerProfitTest(int amount, SingleMatchResult result, int expecte assertThat(actual.toInt()).isEqualTo(expected); } - - @DisplayName("승패 결과에 따라, 딜러의 이익을 반환할 수 있다.") - @ParameterizedTest - @CsvSource({ - "1000, PLAYER_BLACKJACK, -1500", - "1000, PLAYER_WIN, -1000", - "1000, DRAW, 0", - "1000, DEALER_WIN, 1000" - }) - void calculateDealerProfitTest(int amount, SingleMatchResult result, int expected) { - BetAmount betAmount = new BetAmount(amount); - - Profit actual = result.calculateDealerProfit(betAmount); - - assertThat(actual.toInt()).isEqualTo(expected); - } } diff --git a/src/test/java/blackjack/domain/money/BetAmountTest.java b/src/test/java/blackjack/domain/money/BetAmountTest.java index 013aab7a439..e5c31068379 100644 --- a/src/test/java/blackjack/domain/money/BetAmountTest.java +++ b/src/test/java/blackjack/domain/money/BetAmountTest.java @@ -1,11 +1,9 @@ package blackjack.domain.money; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; class BetAmountTest { @@ -19,27 +17,4 @@ void validateTest_whenValueIsNotPositive_throwException(int value) { .isInstanceOf(IllegalArgumentException.class) .hasMessage("배팅 금액은 양수이어야 합니다."); } - - @DisplayName("배팅 금액은 서로 더할 수 있다") - @ParameterizedTest - @CsvSource({"1500, 1500, 3000", "1234, 5555, 6789", "10, 1, 11"}) - void addTest(int value1, int value2, int expected) { - BetAmount betAmount1 = new BetAmount(value1); - BetAmount betAmount2 = new BetAmount(value2); - - BetAmount actual = betAmount1.add(betAmount2); - - assertThat(actual.toInt()).isEqualTo(expected); - } - - @DisplayName("배팅 금액은 해당 배수만큼 곱할 수 있다") - @ParameterizedTest - @CsvSource({"1.5, 1500", "2, 2000", "2.25, 2250"}) - void multiplyTest(double multiplier, int expected) { - BetAmount betAmount = new BetAmount(1000); - - BetAmount actual = betAmount.multiply(multiplier); - - assertThat(actual.toInt()).isEqualTo(expected); - } } From 1d595662b53ea2a2bad3c45937d36029b6f5eaa6 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 15:32:16 +0900 Subject: [PATCH 37/41] =?UTF-8?q?refactor=20(Profit)=20:=20=EC=A0=95?= =?UTF-8?q?=EC=A0=81=20=ED=8C=A9=ED=86=A0=EB=A6=AC=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EA=B0=80=20=ED=8E=B8=EC=9D=98=EC=84=B1=EC=9D=B4=20?= =?UTF-8?q?=EB=96=A8=EC=96=B4=EC=A0=B8,=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EC=9A=A9=EC=9D=B4=ED=95=9C=20=ED=95=98=EB=82=98?= =?UTF-8?q?=EC=9D=98=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A1=A4=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/handrank/SingleMatchResult.java | 17 +++++----- .../java/blackjack/domain/money/Profit.java | 10 +----- .../handrank/SingleMatchResultTest.java | 1 - .../blackjack/domain/money/ProfitTest.java | 33 +++++-------------- 4 files changed, 17 insertions(+), 44 deletions(-) diff --git a/src/main/java/blackjack/domain/handrank/SingleMatchResult.java b/src/main/java/blackjack/domain/handrank/SingleMatchResult.java index b770fd3874a..f75956567a3 100644 --- a/src/main/java/blackjack/domain/handrank/SingleMatchResult.java +++ b/src/main/java/blackjack/domain/handrank/SingleMatchResult.java @@ -2,22 +2,21 @@ import blackjack.domain.money.BetAmount; import blackjack.domain.money.Profit; -import java.util.function.Function; public enum SingleMatchResult { - PLAYER_BLACKJACK(amount -> Profit.win(amount, 1.5)), - PLAYER_WIN(Profit::win), - DRAW(amount -> Profit.ZERO), - DEALER_WIN(Profit::lose); + PLAYER_BLACKJACK(1.5), + PLAYER_WIN(1), + DRAW(0), + DEALER_WIN(-1); - private final Function playerProfitFunction; + private final double playerMultiplier; - SingleMatchResult(Function playerProfitFunction) { - this.playerProfitFunction = playerProfitFunction; + SingleMatchResult(double playerMultiplier) { + this.playerMultiplier = playerMultiplier; } public Profit calculatePlayerProfit(BetAmount betAmount) { - return playerProfitFunction.apply(betAmount); + return Profit.of(betAmount, playerMultiplier); } } diff --git a/src/main/java/blackjack/domain/money/Profit.java b/src/main/java/blackjack/domain/money/Profit.java index 25ef78d546c..6b1ca27ac47 100644 --- a/src/main/java/blackjack/domain/money/Profit.java +++ b/src/main/java/blackjack/domain/money/Profit.java @@ -12,18 +12,10 @@ public Profit(int value) { this.value = value; } - public static Profit win(BetAmount amount) { - return new Profit(amount.toInt()); - } - - public static Profit win(BetAmount amount, double multiplier) { + public static Profit of(BetAmount amount, double multiplier) { return new Profit((int) (amount.toInt() * multiplier)); } - public static Profit lose(BetAmount amount) { - return new Profit(-1 * amount.toInt()); - } - public Profit add(Profit profit) { return new Profit(this.value + profit.value); } diff --git a/src/test/java/blackjack/domain/handrank/SingleMatchResultTest.java b/src/test/java/blackjack/domain/handrank/SingleMatchResultTest.java index 4c3a07c0ea1..c6c250aa1fb 100644 --- a/src/test/java/blackjack/domain/handrank/SingleMatchResultTest.java +++ b/src/test/java/blackjack/domain/handrank/SingleMatchResultTest.java @@ -10,7 +10,6 @@ class SingleMatchResultTest { - @DisplayName("승패 결과에 따라, 플레이어의 이익을 반환할 수 있다.") @ParameterizedTest @CsvSource({ diff --git a/src/test/java/blackjack/domain/money/ProfitTest.java b/src/test/java/blackjack/domain/money/ProfitTest.java index 4baeea1292b..02849fad858 100644 --- a/src/test/java/blackjack/domain/money/ProfitTest.java +++ b/src/test/java/blackjack/domain/money/ProfitTest.java @@ -5,43 +5,26 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import org.junit.jupiter.params.provider.ValueSource; public class ProfitTest { - @DisplayName("주어진 배팅 금액 만큼 얻는 이익을 계산할 수 있다.") - @ParameterizedTest - @ValueSource(ints = {1, 10, 1000}) - void createTest_winMoney(int amount) { - BetAmount betAmount = new BetAmount(amount); - - Profit profit = Profit.win(betAmount); - - assertThat(profit.toInt()).isEqualTo(amount); - } - @DisplayName("주어진 배팅 금액에 특정 배수만큼 얻는 이익을 계산할 수 있다.") @ParameterizedTest - @CsvSource({"1000, 2, 2000", "20, 1.5, 30", "1111, 1.5, 1666"}) + @CsvSource({ + "1000, 2, 2000", + "20, 1.5, 30", + "1111, 1.5, 1666", + "100, -1, -100", + "3333, 0, 0" + }) void createTest_winMoneyWithMultiplier(int amount, double multiplier, int expected) { BetAmount betAmount = new BetAmount(amount); - Profit profit = Profit.win(betAmount, multiplier); + Profit profit = Profit.of(betAmount, multiplier); assertThat(profit.toInt()).isEqualTo(expected); } - @DisplayName("주어진 배팅 금액 만큼 잃는 이익을 계산할 수 있다.") - @ParameterizedTest - @ValueSource(ints = {1, 10, 1000}) - void createTest_loseMoney(int amount) { - BetAmount betAmount = new BetAmount(amount); - - Profit profit = Profit.lose(betAmount); - - assertThat(profit.toInt()).isEqualTo(-1 * amount); - } - @DisplayName("두 이익을 더하여 계산할 수 있다.") @ParameterizedTest @CsvSource({"20, -10", "10, -20", "-20, -10", "20, 10"}) From 329a6e8da258b0f4e3fa670feb65c1b0a30ecb69 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 16:37:22 +0900 Subject: [PATCH 38/41] =?UTF-8?q?style=20(Test)=20:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=EB=B0=8F=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A0=91?= =?UTF-8?q?=EA=B7=BC=20=EC=A0=9C=EC=96=B4=EC=9E=90=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/blackjack/domain/handrank/BlackjackTest.java | 2 +- src/test/java/blackjack/domain/money/ProfitTest.java | 2 +- src/test/java/blackjack/domain/participant/DealerTest.java | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/java/blackjack/domain/handrank/BlackjackTest.java b/src/test/java/blackjack/domain/handrank/BlackjackTest.java index cfe8a423fd1..0549722fd97 100644 --- a/src/test/java/blackjack/domain/handrank/BlackjackTest.java +++ b/src/test/java/blackjack/domain/handrank/BlackjackTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -public class BlackjackTest { +class BlackjackTest { private final HankRank BLACKJACK = new Blackjack(); diff --git a/src/test/java/blackjack/domain/money/ProfitTest.java b/src/test/java/blackjack/domain/money/ProfitTest.java index 02849fad858..8159d971675 100644 --- a/src/test/java/blackjack/domain/money/ProfitTest.java +++ b/src/test/java/blackjack/domain/money/ProfitTest.java @@ -6,7 +6,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -public class ProfitTest { +class ProfitTest { @DisplayName("주어진 배팅 금액에 특정 배수만큼 얻는 이익을 계산할 수 있다.") @ParameterizedTest diff --git a/src/test/java/blackjack/domain/participant/DealerTest.java b/src/test/java/blackjack/domain/participant/DealerTest.java index 8c90059fe9f..b2e962fff19 100644 --- a/src/test/java/blackjack/domain/participant/DealerTest.java +++ b/src/test/java/blackjack/domain/participant/DealerTest.java @@ -20,7 +20,6 @@ import org.junit.jupiter.params.provider.MethodSource; class DealerTest { - private static final Name DEFAULT_NAME = new Name("name"); @DisplayName("카드의 총 점수가 16을 넘지 않으면, 카드를 더 뽑을 수 있다") @Test From 795a5d3289a0f07989ae26b7087ab849b24ea7f9 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 16:39:25 +0900 Subject: [PATCH 39/41] =?UTF-8?q?refactor=20(HandRankFactory)=20:=20static?= =?UTF-8?q?=20method=EB=A7=8C=20=EC=9E=88=EB=8A=94=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=EB=A5=BC=20=EA=B0=9D=EC=B2=B4=EB=A1=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=ED=95=98=EC=A7=80=20=EB=AA=BB=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20private=20=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/BlackJackApplication.java | 4 ++++ src/main/java/blackjack/domain/handrank/HandRankFactory.java | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/main/java/blackjack/BlackJackApplication.java b/src/main/java/blackjack/BlackJackApplication.java index c508abef182..e2759f47eec 100644 --- a/src/main/java/blackjack/BlackJackApplication.java +++ b/src/main/java/blackjack/BlackJackApplication.java @@ -1,6 +1,10 @@ package blackjack; public class BlackJackApplication { + + private BlackJackApplication() { + } + public static void main(String[] args) { BlackJackGame blackJackGame = new BlackJackGame(); blackJackGame.run(); diff --git a/src/main/java/blackjack/domain/handrank/HandRankFactory.java b/src/main/java/blackjack/domain/handrank/HandRankFactory.java index 551883a828a..270ecbc72c9 100644 --- a/src/main/java/blackjack/domain/handrank/HandRankFactory.java +++ b/src/main/java/blackjack/domain/handrank/HandRankFactory.java @@ -6,6 +6,9 @@ public final class HandRankFactory { private static final HankRank BLACKJACK = new Blackjack(); + private HandRankFactory() { + } + public static HankRank from(Hand hand) { if (hand.isBlackjack()) { return BLACKJACK; From 45428158894b828ee58c5ffaedf6dccefe0b23d5 Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 16:44:16 +0900 Subject: [PATCH 40/41] =?UTF-8?q?refactor=20(Players)=20:=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=EB=B0=9B=EB=8A=94=20List=EA=B0=80=20null=EC=9D=BC=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EC=98=88=EC=99=B8=EB=A5=BC=20=EB=8D=98?= =?UTF-8?q?=EC=A7=80=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/participant/Players.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/blackjack/domain/participant/Players.java b/src/main/java/blackjack/domain/participant/Players.java index 75d23dd67e8..710f47ead9d 100644 --- a/src/main/java/blackjack/domain/participant/Players.java +++ b/src/main/java/blackjack/domain/participant/Players.java @@ -2,6 +2,7 @@ import blackjack.domain.card.Deck; import java.util.List; +import java.util.Objects; import java.util.stream.IntStream; public class Players { @@ -17,6 +18,7 @@ public Players(List players) { } private void validateSize(List players) { + Objects.requireNonNull(players); if (players.isEmpty()) { throw new IllegalArgumentException("최소 한 명의 플레이어가 있어야 합니다."); } From 243e8f87451a8eb8064192627073482f92456cbf Mon Sep 17 00:00:00 2001 From: "DESKTOP-E5VRH4Q\\cnddk" Date: Thu, 14 Mar 2024 16:45:03 +0900 Subject: [PATCH 41/41] =?UTF-8?q?refactor(HandRank)=20:=20=EA=B0=81=20?= =?UTF-8?q?=ED=95=B8=EB=93=9C=20=EA=B2=B0=EA=B3=BC=EB=A7=88=EB=8B=A4=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=ED=95=9C=20=EC=A0=90=EC=88=98=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/blackjack/domain/handrank/Bust.java | 9 +++++++++ src/main/java/blackjack/domain/handrank/Stand.java | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/src/main/java/blackjack/domain/handrank/Bust.java b/src/main/java/blackjack/domain/handrank/Bust.java index 63ef715c94b..f295fdccfca 100644 --- a/src/main/java/blackjack/domain/handrank/Bust.java +++ b/src/main/java/blackjack/domain/handrank/Bust.java @@ -1,13 +1,22 @@ package blackjack.domain.handrank; +import blackjack.domain.card.Hand; + public final class Bust implements HankRank { private final int score; public Bust(int score) { + validate(score); this.score = score; } + private void validate(int score) { + if (score <= Hand.BLACKJACK_SCORE) { + throw new IllegalArgumentException("버스트 점수는 블랙잭 점수보다 높아야 합니다."); + } + } + @Override public SingleMatchResult matchAtDealer(HankRank playerHandRank) { if (playerHandRank.isBust()) { diff --git a/src/main/java/blackjack/domain/handrank/Stand.java b/src/main/java/blackjack/domain/handrank/Stand.java index a8c0e228ff1..4e1641bcfc3 100644 --- a/src/main/java/blackjack/domain/handrank/Stand.java +++ b/src/main/java/blackjack/domain/handrank/Stand.java @@ -1,13 +1,22 @@ package blackjack.domain.handrank; +import blackjack.domain.card.Hand; + public final class Stand implements HankRank { private final int score; public Stand(int score) { + validate(score); this.score = score; } + private void validate(int score) { + if (score > Hand.BLACKJACK_SCORE) { + throw new IllegalArgumentException("스탠드 점수는 블랙잭 점수보다 낮거나 같아야 합니다."); + } + } + @Override public SingleMatchResult matchAtDealer(HankRank playerHandRank) { if (playerHandRank.isBlackjack()) {