diff --git a/README.md b/README.md index 2d9bc12..bbd4924 100644 --- a/README.md +++ b/README.md @@ -1 +1,45 @@ # java-blackjack + +# 구현 기능 목록 + +-[x] 게임 + - [x] 플레이어들의 이름을 입력받는다. + - [x] 최소 플레이어는 1명으로, 딜러와 게임을 진행한다. + - [x] ,를 기준으로 분리 + + - [x] 시작시 각 플레이어가 두 장의 카드를 지급받는다. + - [x] 딜러는 처음에 받은 2장의 합계가 16이하이면 반드시 1장의 카드를 뽑는다. + - [x] 딜러는 처음에 받은 2장의 합계가 17이상이면 추가로 받을 수 없다. + - [x] 플레이어는 카드 숫자의 합이 21이 초과하지 않는 동안 원하는 만큼 카드를 뽑을 수 있다. + - [x] 카드를 추가로 뽑아 21을 초과할 경우 배팅 금액을 모두 잃게 된다. + - [x] Ace를 가진 플레이어는 에이스의 숫자값으로 1 혹은 11을 선택할 수 있다. + - [x] King, Queen, Jack의 숫자값은 각각 10으로 취급한다. + +-[x] 출력 + - [x] 게임 시작 시 각 플레이어가 받은 2장의 카드를 출력한다. + - [x] 각 플레이어에게 추가로 카드를 받을 것인지 묻는다. + - [x] 카드를 추가로 받으면 해당 플레이어가 가진 카드 목록을 출력한다. + - [x] 딜러는 카드값의 합이 16이하면 카드를 추가로 받았다는 메시지를 출력한다. + + - [x] 모든 플레이어가 더이상 카드를 뽑지 않으면 게임이 종료된다. + - [x] 플레이어별 카드의 목록을 출력한다. + - [x] 플레이어별 카드 숫자의 합을 계산하여 결과로 출력한다. + + - [x] 게임을 완료한 후 각 플레이어별로 승패를 출력한다. + - [x] 딜러는 각 플레이어와의 승패를 누적해서 출력한다. + - [x] 플레이어는 딜러와의 승패를 출력한다. + + +- [x] 예외 + - 이름 + - [x] 플레이어의 이름은 공백, null 값이 될 수 없다. + - [x] 플레이어의 이름은 특수문자를 포함할 수 없다. + - [x] 플레이어의 이름은 "딜러"가 될 수 없다. + - [x] 플레이어는 서로 중복된 이름을 가질 수 없다. + + - 카드 덱 + - [x] 하나의 카드덱에서는 카드를 52장 초과하여 뽑을 수 없다. + + - 플레이어에게 카드를 더 받을 것인지 질문 + - [x] y, n가 아닌 형태로 대답할 수 없다. + - [x] 대문자 Y, N도 올바른 답으로 인식한다. \ No newline at end of file diff --git a/build.gradle b/build.gradle index 092a1c0..8775d68 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,14 @@ repositories { dependencies { testImplementation('org.junit.jupiter:junit-jupiter:5.6.0') testImplementation('org.assertj:assertj-core:3.15.0') + + compileOnly 'org.projectlombok:lombok:1.18.20' + annotationProcessor 'org.projectlombok:lombok:1.18.20' + + testCompileOnly 'org.projectlombok:lombok:1.18.20' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.20' + + implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.0' } test { diff --git a/memo.md b/memo.md new file mode 100644 index 0000000..95bde51 --- /dev/null +++ b/memo.md @@ -0,0 +1,113 @@ +domain + +- Card, Deck, Player, Dealer, Game, Statistics + - Game : 카드 분배 역할 (카드 분배 조건도 이 객체에서 판단) + - Statistics : 카드분배가 완료된 후 점수계산 및 승패 결과 도출 + +View + +- 플레이어 이름을 입력 받고 결과를 출력 + +Controller + +- 게임 전체 흐름 제어 (입력, 게임실행, 출력) + +### Enum + +1. 게임컨트롤러 (카드분배- 질문) + - 답(YES, NO) + +2. 게임결과 (결과 승패를 판단) + - 승(플레이어 점수, 딜러점수, 승(플), 패(딜러)) + - 패(플레이어 점수, 딜러점수, 패(플), 승(딜러)) + - 무승부(플레이어 점수, 딜러점수, 무(플,딜러)) + +domain + +- Card, Deck, Player, Dealer, Score(계산), GameResult(승패도출) +- Answer(질문답), Results(승패무) + +View + +- inputview: 입력받는 것(질문에 대한 답을 받는 부분) +- outputview: 출력하는 것 + +Controller + +- 카드를 분배(drawCard), 더 받을지 질문, 하나카드를 분배... +- 흐름제어 + +# Ace 카드 처리 로직 + +Player -> 21 한계치 Dealer -> 17 한계치 + +모든 게임스코어합은 -> 21이 게임의 종료 지점 + +Score 계산 && 카드를 더 뽑게할지말지 + +- 카드의 Score가 계산로직으로 21(Player) 17(Dealer) 되기전까지 카드를 더 뽑을지 질문 + +## 계산로직 + +1. 카드 목록에 포함된 에이스 카드의 숫자를 추출한다. -> n +2. n+1의 길이를 가지는 배열을 생성하고, 에이스 카드를 1로 계산한 카드합과 거기에 차례대로 10을 더한 값을 배열에 입력한다. +3. 배열의 숫자들 중 21을 초과하지 않으면서 가장 큰 값을 취한다. + +ex) Ace, Ace, 8일 경우 int[3] = {10, 20, 30} -> 20을 숫자합으로 계산 + +### 게임종료 + +카드를 한 장 뽑을 때마다 모든 경우의 수를 계산. Ace를 1로, 혹은 11로 계산하는 경우의 수를 다 생각해서 모든 경우의 수 중 카드합의 최소값이 한계치를 넘는 경우에 카드를 그만 뽑도록. + +Ace 7, 9 + +27을 취한다. 카드를 더 못뽑음. + +17 + +Ace, 7, 9, 4 + +-> 21 + +## 카드 뽑기 + +<딜러> +처음에 2장의 카드를 받고 카드합이 16 이하이면 한 장을 더 뽑는다. 에이스 1장은 11로 계산하고 나머지 에이스는 1로 계산한다. -> 에이스가 포함되어 있을 경우 카드합에 10을 더해준다 + +<에이스가 포함되어 있지 않을 경우> + +카드합이 16 이하이면 카드를 뽑음. + +<에이스가 포함되어 있는 경우> + +카드합에 10을 더한 값이 16 이하이면 카드를 뽑음. + +## 카드 뽑기 및 에이스가 여러개 포함될 가능성이 있는 카드덱의 합 계산 + +// 카드값의 합(ace가 11) +int scoreOfAceAsEleven; // 에이스의 개수 int aceCount; // Ace카드값의 차이 int DiFFERENCE_OF_ACE_VALUE = 10; + + calcaluateResult(){ + while (canCountAceAsOne) { + 카드값의 합 scoreOfAceAsEleven = scoreOfAceAsOne(scoreOfAsAsEleven); + aceCount--; + } + + return scoreOfAceAsEleven; + } + + boolean canCountAceAsOne(int 카드값의 합, 에이스의 개수){ + 에이스가 하나라도 있음 > 0 && 카드 합이 21(상수선언)을 넘음(ace를 1로 취급가능)} + } + + int scoreOfAceAsOne(int 카드값의 합){ + (ace를 11로 취급한) scoreOfAceAsEleven 카드값의 합 - 10 + } + +- 17 dealer = threshould + +컨트롤러에서 딜러에게 boolean Drawble(){ return calculateResult() < 17 } drawCard() + +- 21 player 컨트롤러에서 플레이어에게 더 받을건지 묻는 질문 boolean 플레이어의 답 Yes && Drawable() { return calculateResult() < 21 } drawCard() + + diff --git a/src/main/java/blackjack/BlackjackApplication.java b/src/main/java/blackjack/BlackjackApplication.java new file mode 100644 index 0000000..8afae58 --- /dev/null +++ b/src/main/java/blackjack/BlackjackApplication.java @@ -0,0 +1,14 @@ +package blackjack; + +import blackjack.controller.BlackJackController; + +public class BlackjackApplication { + + public static void main(String[] args) { + try { + new BlackJackController().run(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/blackjack/controller/BlackJackController.java b/src/main/java/blackjack/controller/BlackJackController.java new file mode 100644 index 0000000..e0e080a --- /dev/null +++ b/src/main/java/blackjack/controller/BlackJackController.java @@ -0,0 +1,66 @@ +package blackjack.controller; + +import blackjack.domain.card.Deck; +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; +import blackjack.domain.participant.PlayersFactory; +import blackjack.domain.result.GameResult; +import blackjack.dto.DrawCardRequestDto; +import blackjack.dto.PlayersNameInputDto; +import blackjack.view.InputView; +import blackjack.view.OutputView; + +import java.util.List; + +public class BlackJackController { + private final InputView inputView = new InputView(); + private final OutputView outputView = new OutputView(); + + public void run() { + PlayersNameInputDto namesInput = inputView.getPlayersName(); + Dealer dealer = new Dealer(); + Deck deck = new Deck(); + List players = PlayersFactory.createPlayers(namesInput.getPlayersName()); + + drawTowCards(dealer, players, deck); + drawCardToPlayers(players, deck); + drawCardToDealer(dealer, deck); + outputView.printCardsResult(dealer, players); + outputView.printGameResult(GameResult.of(dealer, players)); + } + + private void drawTowCards(Dealer dealer, List players, Deck deck) { + for (Player player : players) { + player.receiveCard(deck.drawCard()); + player.receiveCard(deck.drawCard()); + } + dealer.receiveCard(deck.drawCard()); + dealer.receiveCard(deck.drawCard()); + outputView.printFirstCardsGiven(players, dealer); + outputView.printDealerCard(dealer); + outputView.printPlayersCard(players); + } + + private void drawCardToPlayers(List players, Deck deck) { + for (Player player : players) { + drawCardToPlayer(player, deck); + } + } + + private void drawCardToPlayer(Player player, Deck deck) { + DrawCardRequestDto drawCardRequest = inputView.getPlayersResponse(player); + while (player.drawable() && drawCardRequest.isYes()) { + player.receiveCard(deck.drawCard()); + + outputView.printCards(player); + drawCardRequest = inputView.getPlayersResponse(player); + } + } + + private void drawCardToDealer(Dealer dealer, Deck deck) { + while (dealer.drawable()) { + dealer.receiveCard(deck.drawCard()); + outputView.printDealerCardGiven(); + } + } +} 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 0000000..1a55b03 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Card.java @@ -0,0 +1,46 @@ +package blackjack.domain.card; + +import lombok.Getter; + +import java.util.Objects; + +@Getter +public class Card { + + private final Denomination denomination; + private final Type type; + + public Card(Denomination denomination, Type type) { + this.denomination = denomination; + this.type = type; + } + + public boolean isAce() { + return denomination.equals(Denomination.ACE); + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Card)) { + return false; + } + Card card = (Card) o; + return denomination == card.denomination && + type == card.type; + } + + @Override + public int hashCode() { + return Objects.hash(denomination, type); + } + + @Override + public String toString() { + return denomination.getDenominationName() + type.getType(); + + } +} 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 0000000..f593afb --- /dev/null +++ b/src/main/java/blackjack/domain/card/Deck.java @@ -0,0 +1,32 @@ +package blackjack.domain.card; + +import blackjack.domain.card.strategy.DrawStrategy; +import blackjack.domain.card.strategy.RandomDrawStrategy; +import lombok.Getter; + +import java.util.LinkedList; +import java.util.List; + +public class Deck { + + private static final String ALERT_NO_CARD_LEFT = "사용 가능한 카드를 모두 소진하였습니다."; + + @Getter + private final List deck; + + public Deck() { + this.deck = new LinkedList<>(DeckFactory.createDeck()); + } + + public Card drawCard() { + if (deck.isEmpty()) { + throw new RuntimeException(ALERT_NO_CARD_LEFT); + } + return deck.remove(generateNextDrawCardIndex(new RandomDrawStrategy())); + } + + private int generateNextDrawCardIndex(DrawStrategy drawStrategy) { + return drawStrategy.getNextIndex(deck); + } + +} diff --git a/src/main/java/blackjack/domain/card/DeckFactory.java b/src/main/java/blackjack/domain/card/DeckFactory.java new file mode 100644 index 0000000..5ef322c --- /dev/null +++ b/src/main/java/blackjack/domain/card/DeckFactory.java @@ -0,0 +1,39 @@ +package blackjack.domain.card; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class DeckFactory { + + private static final List cardsDeck; + + static { + cardsDeck = createCardsDeck(); + } + + private static List createCardsDeck() { + List cards = new ArrayList<>(); + + for (Denomination denomination : Denomination.values()) { + cards.addAll(createCards(denomination)); + } + return cards; + } + + private static List createCards(Denomination denomination) { + List cards = new ArrayList<>(); + + for (Type type : Type.values()) { + cards.add(new Card(denomination, type)); + } + + return cards; + } + + public static List createDeck() { + return Collections.unmodifiableList(cardsDeck); + } +} + + diff --git a/src/main/java/blackjack/domain/card/Denomination.java b/src/main/java/blackjack/domain/card/Denomination.java new file mode 100644 index 0000000..27458f7 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Denomination.java @@ -0,0 +1,29 @@ +package blackjack.domain.card; + +import lombok.Getter; + +@Getter +public enum Denomination { + + ACE(11, "A"), + 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, "J"), + QUEEN(10, "Q"), + KING(10, "K"); + + private final String denominationName; + private final int score; + + Denomination(int score, String denominationName) { + this.denominationName = denominationName; + this.score = score; + } +} diff --git a/src/main/java/blackjack/domain/card/Type.java b/src/main/java/blackjack/domain/card/Type.java new file mode 100644 index 0000000..7d3dbeb --- /dev/null +++ b/src/main/java/blackjack/domain/card/Type.java @@ -0,0 +1,18 @@ +package blackjack.domain.card; + +public enum Type { + SPADE("스페이드"), + HEART("하트"), + DIAMOND("다이아몬드"), + CLUB("클로버"); + + private final String type; + + Type(String type) { + this.type = type; + } + + public String getType() { + return type; + } +} diff --git a/src/main/java/blackjack/domain/card/strategy/DrawStrategy.java b/src/main/java/blackjack/domain/card/strategy/DrawStrategy.java new file mode 100644 index 0000000..a64c530 --- /dev/null +++ b/src/main/java/blackjack/domain/card/strategy/DrawStrategy.java @@ -0,0 +1,9 @@ +package blackjack.domain.card.strategy; + +import blackjack.domain.card.Card; + +import java.util.List; + +public interface DrawStrategy { + int getNextIndex(List deck); +} diff --git a/src/main/java/blackjack/domain/card/strategy/RandomDrawStrategy.java b/src/main/java/blackjack/domain/card/strategy/RandomDrawStrategy.java new file mode 100644 index 0000000..0826c36 --- /dev/null +++ b/src/main/java/blackjack/domain/card/strategy/RandomDrawStrategy.java @@ -0,0 +1,15 @@ +package blackjack.domain.card.strategy; + +import blackjack.domain.card.Card; + +import java.util.List; +import java.util.Random; + +public class RandomDrawStrategy implements DrawStrategy { + private static final Random RANDOM = new Random(); + + @Override + public int getNextIndex(List deck) { + return RANDOM.nextInt(deck.size()); + } +} 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 0000000..c7983f0 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Dealer.java @@ -0,0 +1,40 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Deck; +import lombok.Getter; + +import java.util.stream.Collectors; + +@Getter +public class Dealer extends Participant { + + private static final String DEALER_NAME = "딜러"; + private static final int DEALER_DRAW_THRESHOLD = 17; + + public Dealer() { + super(DEALER_NAME); + } + + public void drawOrNot(Deck deck) { + if (drawable()) { + receiveCard(deck.drawCard()); + } + } + + @Override + public boolean drawable() { + return getCardsSum() < DEALER_DRAW_THRESHOLD; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(DEALER_NAME) + .append(": ") + .append(getCards().stream() + .map(Card::toString) + .collect(Collectors.joining(", "))); + return sb.toString(); + } +} 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 0000000..06db613 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Participant.java @@ -0,0 +1,102 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Denomination; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Getter +public abstract class Participant { + private static final int DIFFERENCE_OF_ACE_SCORE = 10; + private static final int BLACKJACK = 21; + private static final int ZERO = 0; + private static final String CHECK_NULL_OR_EMPTY = "이름이 빈 칸 혹은 null 값이 아닌지 확인해주세요."; + private static final String CHECK_CONTAINING_ONLY_LETTERS_AND_DIGITS = "이름은 특수문자를 포함하지 않은 문자와 숫자로 지정해주세요."; + + private final String name; + private final List cards; + + protected Participant(String name) { + validateName(name); + + this.name = name; + this.cards = new ArrayList<>(); + } + + private void validateName(String name) { + validateNullOrEmpty(name); + validateAlphaNumeric(name); + } + + private void validateNullOrEmpty(String name) { + if (StringUtils.isBlank(name)) { + throw new IllegalArgumentException(CHECK_NULL_OR_EMPTY); + } + } + + private void validateAlphaNumeric(String name) { + if (!StringUtils.isAlphanumericSpace(name)) { + throw new IllegalArgumentException(CHECK_CONTAINING_ONLY_LETTERS_AND_DIGITS); + } + } + + protected abstract boolean drawable(); + + public void receiveCard(Card card) { + cards.add(card); + } + + public int getCardsSum() { + int scoreOfAceAsEleven = sumOfCardsScore(); + int aceCount = getAceCount(); + + while (canCountAceAsOne(scoreOfAceAsEleven, aceCount)) { + scoreOfAceAsEleven = scoreOfAceAsOne(scoreOfAceAsEleven); + aceCount--; + } + + return scoreOfAceAsEleven; + } + + private int scoreOfAceAsOne(int scoreOfAceAsEleven) { + return scoreOfAceAsEleven - DIFFERENCE_OF_ACE_SCORE; + } + + private boolean canCountAceAsOne(int scoreOfAceAsEleven, int aceCount) { + return scoreOfAceAsEleven > BLACKJACK && aceCount > ZERO; + } + + private int getAceCount() { + return (int) cards.stream() + .filter(Card::isAce) + .count(); + } + + private int sumOfCardsScore() { + return cards.stream() + .map(Card::getDenomination) + .mapToInt(Denomination::getScore) + .sum(); + } + + public boolean isBust() { + return getCardsSum() > BLACKJACK; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Participant that = (Participant) o; + return Objects.equals(name, that.name) && Objects.equals(cards, that.cards); + } + + @Override + public int hashCode() { + return Objects.hash(name, cards); + } +} 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 0000000..117c5ba --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Player.java @@ -0,0 +1,27 @@ +package blackjack.domain.participant; + +import lombok.Getter; + +@Getter +public class Player extends Participant { + + private static final int PLAYER_DRAW_THRESHOLD = 21; + private static final String DEALER_NAME = "딜러"; + private static final String CHECK_SAME_NAME_AS_DEALER = "이름은 딜러와 같을 수 없으니 다른 이름을 지정해주세요."; + + public Player(String name) { + super(name); + validateDealerNameDuplicated(name); + } + + private void validateDealerNameDuplicated(String name) { + if (name.equals(DEALER_NAME)) { + throw new IllegalArgumentException(CHECK_SAME_NAME_AS_DEALER); + } + } + + @Override + public boolean drawable() { + return getCardsSum() < PLAYER_DRAW_THRESHOLD; + } +} diff --git a/src/main/java/blackjack/domain/participant/PlayersFactory.java b/src/main/java/blackjack/domain/participant/PlayersFactory.java new file mode 100644 index 0000000..f468682 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/PlayersFactory.java @@ -0,0 +1,33 @@ +package blackjack.domain.participant; + +import blackjack.utils.StringUtil; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class PlayersFactory { + + private static final String CHECK_DUPLICATED_PLAYER_NAME = "플레이어가 중복된 이름을 가지고 있는지 확인해주세요."; + + public static List createPlayers(String input) { + List playerNames = StringUtil.splitByComma(input); + checkDuplication(playerNames); + + List players = playerNames.stream() + .map(Player::new) + .collect(Collectors.toList()); + return Collections.unmodifiableList(players); + } + + private static void checkDuplication(List playerNames) { + int distinctCount = (int) playerNames.stream() + .distinct() + .count(); + + if (playerNames.size() != distinctCount) { + throw new IllegalArgumentException(CHECK_DUPLICATED_PLAYER_NAME); + } + + } +} diff --git a/src/main/java/blackjack/domain/result/GameResult.java b/src/main/java/blackjack/domain/result/GameResult.java new file mode 100644 index 0000000..83c642b --- /dev/null +++ b/src/main/java/blackjack/domain/result/GameResult.java @@ -0,0 +1,42 @@ +package blackjack.domain.result; + +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; + +import java.util.*; + +public class GameResult { + + private final Map playersResult = new LinkedHashMap<>(); + private final Map dealerResult = new LinkedHashMap<>(); + + private GameResult(Dealer dealer, List players) { + // 플레이어 기준 승패를 가리기 + // winningResult를 넣은 Map에다가 키 값으로 숫자(승패 숫자) + Arrays.stream(WinningResult.values()) + .forEach(winningResult -> + dealerResult.put(winningResult, 0)); + + // 플레이어 점수랑 딜러 점수 비교하는 로직 + // 플레이어 기준으로 승패가 Map에 입력이 된 상태 + players.forEach(player -> playersResult.put(player, Rule.resultPlayerVersusDealer(player, dealer))); + + // 넣어둔 value가 있으면 apply = computeIfPresent + // 딜러기준으로 승패를 바꿔주고, 승패 숫자를 하나씩 올려줌 + playersResult.values() + .forEach(winningResult -> dealerResult.computeIfPresent(winningResult.reverse(), (key, value) -> value + 1)); + } + + public static GameResult of(Dealer dealer, List players) { + return new GameResult(dealer, players); + } + + public Map getPlayersResult() { + return Collections.unmodifiableMap(playersResult); + } + + public Map getDealerResult() { + return Collections.unmodifiableMap(dealerResult); + } +} + diff --git a/src/main/java/blackjack/domain/result/Rule.java b/src/main/java/blackjack/domain/result/Rule.java new file mode 100644 index 0000000..f67d0da --- /dev/null +++ b/src/main/java/blackjack/domain/result/Rule.java @@ -0,0 +1,50 @@ +package blackjack.domain.result; + +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; +import lombok.Getter; + +import java.util.Arrays; +import java.util.function.BiFunction; + +public enum Rule{ + // 카드 값의 합이 21을 넘으면 승패 결정 (플레이어 기준으로 선언) + PLAYER_BUST(((player, dealer) -> player.isBust()), WinningResult.LOSE, 1), + DEALER_BUST(((player, dealer) -> dealer.isBust()), WinningResult.WIN, 2), + + // 카드 값의 합이 21을 넘지 않으면서, 값이 더 큰쪽이 승패결정(이미 여기서 결정, BlackJack 체크 필요없음) + PLAYER_HIGHER(((player, dealer) -> player.getCardsSum() > dealer.getCardsSum()), WinningResult.WIN, 3), + DEALER_HIGHER(((player, dealer) -> player.getCardsSum() < dealer.getCardsSum()), WinningResult.LOSE, 4), + + // 무승부 결정 + TIES(((player, dealer) -> player.getCardsSum() == dealer.getCardsSum()), WinningResult.TIE, 5); + + private BiFunction compare; + private WinningResult winningResult; + @Getter + private int verificationOrder; + + Rule(BiFunction compare, WinningResult winningResult, int verificationOrder) { + this.compare = compare; + this.winningResult = winningResult; + this.verificationOrder = verificationOrder; + } + + public Boolean findMatchingRule(Player player, Dealer dealer) { + return compare.apply(player, dealer); + } + + public WinningResult getWinningResult() { + return winningResult; + } + + public static WinningResult resultPlayerVersusDealer(Player player, Dealer dealer) { + + return Arrays.stream(Rule.values()) + .sorted(new RuleComparator()) + .filter(rule -> rule.findMatchingRule(player, dealer)) + .findFirst() + .orElseThrow(() -> new RuntimeException("일치하는 결과를 찾을 수 없습니다.")) + .getWinningResult(); + } +} diff --git a/src/main/java/blackjack/domain/result/RuleComparator.java b/src/main/java/blackjack/domain/result/RuleComparator.java new file mode 100644 index 0000000..e9d321b --- /dev/null +++ b/src/main/java/blackjack/domain/result/RuleComparator.java @@ -0,0 +1,13 @@ +package blackjack.domain.result; + +import java.util.Comparator; + +public class RuleComparator implements Comparator { + @Override + public int compare(Rule rule1, Rule rule2) { + int order1 = rule1.getVerificationOrder(); + int order2 = rule2.getVerificationOrder(); + + return Integer.compare(order1, order2); + } +} diff --git a/src/main/java/blackjack/domain/result/WinningResult.java b/src/main/java/blackjack/domain/result/WinningResult.java new file mode 100644 index 0000000..c03f83c --- /dev/null +++ b/src/main/java/blackjack/domain/result/WinningResult.java @@ -0,0 +1,27 @@ +package blackjack.domain.result; + +import lombok.Getter; + +@Getter +public enum WinningResult { + + WIN("승"), + LOSE("패"), + TIE("무승부"); + + private final String result; + + WinningResult(String result) { + this.result = result; + } + + public WinningResult reverse() { + if (this == WIN) { + return LOSE; + } + if (this == LOSE) { + return WIN; + } + return TIE; + } +} diff --git a/src/main/java/blackjack/dto/DrawCardRequestDto.java b/src/main/java/blackjack/dto/DrawCardRequestDto.java new file mode 100644 index 0000000..aa36fb5 --- /dev/null +++ b/src/main/java/blackjack/dto/DrawCardRequestDto.java @@ -0,0 +1,24 @@ +package blackjack.dto; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class DrawCardRequestDto { + private static final String RESPONSE_RESTRICT_MESSAGE = "y 혹은 n 만 입력할 수 있습니다."; + + private final String response; + + public boolean isYes() { + validateYesOrNo(); + return response.trim().equalsIgnoreCase("y"); + } + + public void validateYesOrNo() { + String trimmedResponse = response.trim(); + if (!(trimmedResponse.equals("Y") || trimmedResponse.equals("y") || trimmedResponse.equals("N") || trimmedResponse.equals("n"))) { + throw new IllegalArgumentException(RESPONSE_RESTRICT_MESSAGE); + } + } +} diff --git a/src/main/java/blackjack/dto/PlayersNameInputDto.java b/src/main/java/blackjack/dto/PlayersNameInputDto.java new file mode 100644 index 0000000..c0379fd --- /dev/null +++ b/src/main/java/blackjack/dto/PlayersNameInputDto.java @@ -0,0 +1,11 @@ +package blackjack.dto; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class PlayersNameInputDto { + + private final String playersName; +} diff --git a/src/main/java/blackjack/utils/StringUtil.java b/src/main/java/blackjack/utils/StringUtil.java new file mode 100644 index 0000000..268fd3e --- /dev/null +++ b/src/main/java/blackjack/utils/StringUtil.java @@ -0,0 +1,17 @@ +package blackjack.utils; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class StringUtil { + private static final String COMMA = ","; + private static final String RESPONSE_RESTRICT_MESSAGE = "y 혹은 n 만 입력할 수 있습니다."; + + public static List splitByComma(String input) { + List names = Arrays.asList(input.split(COMMA)); + return names.stream() + .map(String::trim) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java new file mode 100644 index 0000000..77b5998 --- /dev/null +++ b/src/main/java/blackjack/view/InputView.java @@ -0,0 +1,28 @@ +package blackjack.view; + +import blackjack.domain.participant.Player; +import blackjack.dto.DrawCardRequestDto; +import blackjack.dto.PlayersNameInputDto; + +import java.util.Scanner; + +public class InputView { + private static final Scanner scanner = new Scanner(System.in); + private static final String PLAYER_NAMES_INPUT_MESSAGE = "게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"; + private static final String DRAW_CARD_RESPONSE_INPUT_MESSAGE = "는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)"; + + public PlayersNameInputDto getPlayersName() { + System.out.println(PLAYER_NAMES_INPUT_MESSAGE); + String input = scanner.nextLine(); + return new PlayersNameInputDto(input); + } + + public DrawCardRequestDto getPlayersResponse(Player player) { + StringBuilder sb = new StringBuilder(); + sb.append(player.getName()) + .append(DRAW_CARD_RESPONSE_INPUT_MESSAGE); + System.out.println(sb); + String response = scanner.nextLine(); + return new DrawCardRequestDto(response); + } +} diff --git a/src/main/java/blackjack/view/OutputView.java b/src/main/java/blackjack/view/OutputView.java new file mode 100644 index 0000000..513084f --- /dev/null +++ b/src/main/java/blackjack/view/OutputView.java @@ -0,0 +1,121 @@ +package blackjack.view; + +import blackjack.domain.card.Card; +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; +import blackjack.domain.result.GameResult; +import blackjack.domain.result.WinningResult; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class OutputView { + private static final String AND = "와 "; + private static final String TWO_CARDS_GIVEN = "에게 2장의 카드를 나누었습니다."; + private static final String NEW_LINE = System.lineSeparator(); + private static final String DEALER_DRAW_CARD = "딜러는 16이하라 한장의 카드를 더 받았습니다."; + private static final String RESULT = " - 결과: "; + private static final String GAME_RESULT = "\n## 최종승패"; + private static final String DEALER = "딜러: "; + private static final String BLANK = " "; + private static final String COLON = ": "; + + public void printFirstCardsGiven(List players, Dealer dealer) { + StringBuilder sb = new StringBuilder(); + sb.append(dealer.getName()) + .append(AND); + sb.append(players.stream() + .map(Player::getName) + .collect(Collectors.joining(", "))); + sb.append(TWO_CARDS_GIVEN); + System.out.println(sb); + + } + + public void printDealerCard(Dealer dealer) { + System.out.println(dealer); + } + + public void printPlayersCard(List players) { + for (Player player : players) { + System.out.println(playerResultToString(player)); + } + System.out.println(); + } + + public void printCards(Player player) { + System.out.println(playerResultToString(player)); + } + + public void printDealerCardGiven() { + System.out.println(DEALER_DRAW_CARD); + } + + public void printCardsResult(Dealer dealer, List players) { + printDealerCardsResult(dealer); + + for (Player player : players) { + printPlayerCardsResult(player); + } + } + + private void printDealerCardsResult(Dealer dealer) { + StringBuilder sb = new StringBuilder(); + sb.append(NEW_LINE) + .append(dealer.toString()) + .append(RESULT) + .append(dealer.getCardsSum()); + + System.out.println(sb); + } + + private void printPlayerCardsResult(Player player) { + StringBuilder sb = new StringBuilder(playerResultToString(player)); + sb.append(RESULT); + sb.append(player.getCardsSum()); + + System.out.println(sb); + } + + public void printGameResult(GameResult gameResult) { + System.out.println(GAME_RESULT); + printDealerResult(gameResult.getDealerResult()); + printPlayersResult(gameResult.getPlayersResult()); + + } + + private void printDealerResult(Map dealerResult) { + StringBuilder sb = new StringBuilder(DEALER); + sb.append(dealerResult.entrySet() + .stream() + .filter(winningResult -> winningResult.getValue() > 0) + .map(winningResult -> winningResult.getValue() + winningResult.getKey().getResult()) + .collect(Collectors.joining(BLANK))); + + System.out.println(sb); + + } + + private void printPlayersResult(Map playersResult) { + StringBuilder sb = new StringBuilder(); + sb.append(playersResult.entrySet() + .stream() + .map(playerResult -> playerResult.getKey().getName() + COLON + playerResult.getValue().getResult()) + .collect(Collectors.joining(NEW_LINE))); + + System.out.println(sb); + } + + private String playerResultToString(Player player) { + StringBuilder sb = new StringBuilder(); + sb.append(player.getName()) + .append("카드: ") + .append(player.getCards().stream() + .map(Card::toString) + .collect(Collectors.joining(", "))); + return sb.toString(); + } + + +} diff --git a/src/main/java/empty.txt b/src/main/java/empty.txt deleted file mode 100644 index e69de29..0000000 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 0000000..3647cf2 --- /dev/null +++ b/src/test/java/blackjack/domain/card/CardTest.java @@ -0,0 +1,38 @@ +package blackjack.domain.card; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CardTest { + + @DisplayName("카드 객체의 숫자와 종류값이 같으면 같은 객체로 판단한다") + @Test + void equalsTest() { + //given, when, then + assertThat(new Card(Denomination.TWO, Type.DIAMOND)) + .isEqualTo(new Card(Denomination.TWO, Type.DIAMOND)); + + } + + @DisplayName("카드 객체의 숫자와 종류를 출력한다") + @Test + void printCardTwoDiamond() { + //given + Card card = new Card(Denomination.TWO, Type.DIAMOND); + + //when, then + assertThat(card).hasToString("2다이아몬드"); + } + + @DisplayName("카드 객체의 숫자와 종류를 출력한다") + @Test + void printCardAceHeart() { + //given + Card card = new Card(Denomination.ACE, Type.HEART); + + //when, then + assertThat(card.toString()).isEqualTo("A하트"); + } +} diff --git a/src/test/java/blackjack/domain/card/DeckFactoryTest.java b/src/test/java/blackjack/domain/card/DeckFactoryTest.java new file mode 100644 index 0000000..1c9e995 --- /dev/null +++ b/src/test/java/blackjack/domain/card/DeckFactoryTest.java @@ -0,0 +1,23 @@ +package blackjack.domain.card; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class DeckFactoryTest { + + @DisplayName("생성된 덱에 포함된 카드 숫자가 52장인지 확인한다") + @Test + void createTest() { + //given, when, then + assertThat(DeckFactory.createDeck()).hasSize(52); + } + + @Test + @DisplayName("같은 Denomination, 같은 type을 가진 카드는 같다.") + void equalsTest() { + //given, when, then + assertThat(DeckFactory.createDeck()).isEqualTo(DeckFactory.createDeck()); + } +} \ No newline at end of file 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 0000000..eb750d6 --- /dev/null +++ b/src/test/java/blackjack/domain/card/DeckTest.java @@ -0,0 +1,53 @@ +package blackjack.domain.card; + +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Participant; +import blackjack.domain.participant.Player; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +class DeckTest { + + @DisplayName("생성된 덱에 스페이드 에이스가 포함되어 있다.") + @Test + void testDeckHasSpadeAce() { + //given + Deck deck = new Deck(); + + //when, then + assertTrue(deck.getDeck().contains(new Card(Denomination.ACE, Type.SPADE))); + } + + @DisplayName("덱에서 플레이어에게 카드를 한 장 넘겨준다") + @Test + void giveCardToPlayer() { + //given + Deck deck = new Deck(); + Participant player = new Player("John Doe"); + + //when + player.receiveCard(deck.drawCard()); + + //then + assertThat(deck.getDeck()).hasSize(51); + assertThat(player.getCards()).hasSize(1); + } + + @DisplayName("덱에서 딜러에게 카드를 한 장 넘겨준다") + @Test + void giveCardToDealer() { + //given + Deck deck = new Deck(); + Participant dealer = new Dealer(); + + //when + dealer.receiveCard(deck.drawCard()); + + //then + assertThat(deck.getDeck()).hasSize(51); + assertThat(dealer.getCards()).hasSize(1); + } +} \ No newline at end of file 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 0000000..5dfadb2 --- /dev/null +++ b/src/test/java/blackjack/domain/participant/DealerTest.java @@ -0,0 +1,58 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Deck; +import blackjack.domain.card.Denomination; +import blackjack.domain.card.Type; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class DealerTest { + + @DisplayName("처음에 받은 두 장의 카드 합이 16 이하이면 카드를 한 장 받는다.") + @Test + void drawCardTest() { + //given + Dealer dealer = new Dealer(); + Deck deck = new Deck(); + dealer.receiveCard((new Card(Denomination.EIGHT, Type.CLUB))); + dealer.receiveCard((new Card(Denomination.EIGHT, Type.HEART))); + + //when + dealer.drawOrNot(deck); + + //then + assertThat(dealer.getCards().size()).isEqualTo(3); + } + + @DisplayName("처음에 받은 두 장의 카드 합이 16 초과이면 카드를 더 받지 않는다.") + @Test + void NotDrawCardTest() { + //given + Dealer dealer = new Dealer(); + Deck deck = new Deck(); + dealer.receiveCard(new Card(Denomination.NINE, Type.CLUB)); + dealer.receiveCard((new Card(Denomination.EIGHT, Type.HEART))); + + //when + dealer.drawOrNot(deck); + + //then + assertThat(dealer.getCards().size()).isEqualTo(2); + } + + @DisplayName("딜러가 갖고 있는 카드를 출력한다.") + @Test + void DealerPrintTest() { + //given + Dealer dealer = new Dealer(); + Deck deck = new Deck(); + dealer.receiveCard(new Card(Denomination.NINE, Type.CLUB)); + dealer.receiveCard((new Card(Denomination.EIGHT, Type.HEART))); + + //when, then + assertThat(dealer.toString()).isEqualTo("딜러: 9클로버, 8하트"); + } +} \ No newline at end of file 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 0000000..be6d000 --- /dev/null +++ b/src/test/java/blackjack/domain/participant/PlayerTest.java @@ -0,0 +1,107 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Denomination; +import blackjack.domain.card.Type; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class PlayerTest { + + @DisplayName("이름을 입력받아 플레이어 객체를 생성한다.") + @Test + void createPlayer() { + //given + String name = "John Doe"; + Player player = new Player(name); + + //when //then + assertThat(player.getName()).isEqualTo("John Doe"); + } + + @DisplayName("플레이어가 카드를 지급받아 갖고 있는지 확인") + @Test + void receiveCard() { + //given + Player player = new Player("John Doe"); + Card card1 = new Card(Denomination.EIGHT, Type.DIAMOND); + Card card2 = new Card(Denomination.FOUR, Type.SPADE); + + //when + player.receiveCard(card1); + player.receiveCard(card2); + + //then + assertThat(player.getCards()).contains(card1); + assertThat(player.getCards()).contains(card2); + } + + @Test + @DisplayName("Ace, Ace, 8이 포함된 카드 덱의 합이 20이다.") + void getCardsSumTest1() { + //given + Player player = new Player("name"); + + player.getCards().add(new Card(Denomination.ACE, Type.CLUB)); + player.getCards().add(new Card(Denomination.ACE, Type.HEART)); + player.getCards().add(new Card(Denomination.EIGHT, Type.CLUB)); + + //when + int result = player.getCardsSum(); + + //then + assertThat(result).isEqualTo(20); + } + + @Test + @DisplayName("Ace가 4장 포함된 카드 덱의 합은 14이다.") + void getCardsSumTest2() { + //given + Player player = new Player("name"); + + player.getCards().add(new Card(Denomination.ACE, Type.SPADE)); + player.getCards().add(new Card(Denomination.ACE, Type.CLUB)); + player.getCards().add(new Card(Denomination.ACE, Type.HEART)); + player.getCards().add(new Card(Denomination.ACE, Type.DIAMOND)); + + //when + int result = player.getCardsSum(); + + //then + assertThat(result).isEqualTo(14); + } + + @Test + @DisplayName("Ace 3장과 7이 포함된 카드 덱의 합은 20이다.") + void getCardsSumTest3() { + //given + Player player = new Player("name"); + + player.getCards().add(new Card(Denomination.ACE, Type.SPADE)); + player.getCards().add(new Card(Denomination.ACE, Type.CLUB)); + player.getCards().add(new Card(Denomination.ACE, Type.HEART)); + player.getCards().add(new Card(Denomination.SEVEN, Type.HEART)); + + //when + int result = player.getCardsSum(); + + //then + assertThat(result).isEqualTo(20); + } + + @Test + @DisplayName("플레이어의 이름은 '딜러'가 될 수 없다.") + void dealerNameDuplicatedTest() { + //given + String playerName = "딜러"; + + //when //then + assertThatThrownBy(() -> new Player(playerName)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이름은 딜러와 같을 수 없으니 다른 이름을 지정해주세요."); + } + +} diff --git a/src/test/java/blackjack/domain/participant/PlayersFactoryTest.java b/src/test/java/blackjack/domain/participant/PlayersFactoryTest.java new file mode 100644 index 0000000..6a7408a --- /dev/null +++ b/src/test/java/blackjack/domain/participant/PlayersFactoryTest.java @@ -0,0 +1,52 @@ +package blackjack.domain.participant; + +import blackjack.utils.StringUtil; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class PlayersFactoryTest { + + @ParameterizedTest + @ValueSource(strings = {"Jane Doe, John Doe, Jim Smith", "Alice, Brown, Chris, Dean"}) + @DisplayName("이름목록을 입력하면 플레이어 리스트가 생성되는지를 테스트한다.") + void playerGenerateTest(String input) { + //when + List players = PlayersFactory.createPlayers(input); + List names = StringUtil.splitByComma(input); + + //then + for (String name : names) { + assertThat(players).contains(new Player(name)); + } + } + + @Test + @DisplayName("이미 생성된 플레이어의 목록을 바꿀 수 없는지를 확인한다.") + void playersUnmodifiableTest() { + //given + List players = PlayersFactory.createPlayers("Jane, John, Ariel"); + + //when, then + assertThatThrownBy(() -> { + players.add(new Player("ChungHyeon")); + }).isInstanceOf(RuntimeException.class); + } + + @Test + @DisplayName("중복이 있는 이름목록을 입력하면 플레이어 리스트가 생성되는지 않는지 테스트한다.") + void playerDeDuplicateTest() { + //given, when, then + assertThatThrownBy(() -> { + List players = PlayersFactory.createPlayers("John, John, Ariel"); + }).isInstanceOf(IllegalArgumentException.class) + .hasMessage("플레이어가 중복된 이름을 가지고 있는지 확인해주세요."); + + } +} \ No newline at end of file diff --git a/src/test/java/blackjack/domain/result/GameResultTest.java b/src/test/java/blackjack/domain/result/GameResultTest.java new file mode 100644 index 0000000..e99991a --- /dev/null +++ b/src/test/java/blackjack/domain/result/GameResultTest.java @@ -0,0 +1,84 @@ +package blackjack.domain.result; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Denomination; +import blackjack.domain.card.Type; +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; +import blackjack.domain.participant.PlayersFactory; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +class GameResultTest { + + @ParameterizedTest + @EnumSource(WinningResult.class) + @DisplayName("각 플레이어의 승패를 확인한다.") + void playerResultTest(WinningResult winningResult) { + //given + Dealer dealer = new Dealer(); + List players = PlayersFactory.createPlayers("Chris, Matilda, Jenny"); + + dealer.receiveCard(new Card(Denomination.NINE, Type.CLUB)); + + players.get(0) + .receiveCard((new Card(Denomination.FOUR, Type.DIAMOND))); // 플레이어 패 + + players.get(1) + .receiveCard((new Card(Denomination.NINE, Type.HEART))); // 플레이어 무승부 + + players.get(2) + .receiveCard((new Card(Denomination.QUEEN, Type.SPADE))); // 플레이어 승 + + //when + GameResult gameResult = GameResult.of(dealer, players); + + //then + assertThat(gameResult.getPlayersResult()).containsValues(winningResult); + + } + + @ParameterizedTest + @MethodSource("dealerResultTest") + @DisplayName("딜러의 승패를 확인한다.") + void dealerResultTest(WinningResult winningResult, int result) { + //given + Dealer dealer = new Dealer(); + List players = PlayersFactory.createPlayers("Chris, Matilda, Jenny"); + + dealer.receiveCard(new Card(Denomination.NINE, Type.CLUB)); + + players.get(0) + .receiveCard((new Card(Denomination.FOUR, Type.DIAMOND))); // 플레이어 패 = 딜러 승1 + + players.get(1) + .receiveCard((new Card(Denomination.JACK, Type.HEART))); // 플레이어 승 = 딜러 패1 + + players.get(2) + .receiveCard((new Card(Denomination.QUEEN, Type.SPADE))); // 플레이어 승 = 딜러 패2 + + //when + GameResult gameResult = GameResult.of(dealer, players); + + //then + assertThat(gameResult.getDealerResult()).contains(entry(winningResult, result)); + } + + private static Stream dealerResultTest() { + return Stream.of( + Arguments.of(WinningResult.WIN, 1), + Arguments.of(WinningResult.LOSE, 2), + Arguments.of(WinningResult.TIE, 0) + ); + } + +} diff --git a/src/test/java/blackjack/domain/result/WinningResultTest.java b/src/test/java/blackjack/domain/result/WinningResultTest.java new file mode 100644 index 0000000..7d8a66d --- /dev/null +++ b/src/test/java/blackjack/domain/result/WinningResultTest.java @@ -0,0 +1,45 @@ +package blackjack.domain.result; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.*; + +class WinningResultTest { + + @ParameterizedTest + @MethodSource("createWinningResult") + @DisplayName("승패 결과를 가져온다.") + void getWinningResultTest(WinningResult winningResult, String expected) { + //when //then + assertThat(winningResult.getResult()).isEqualTo(expected); + } + + private static Stream createWinningResult() { + return Stream.of( + Arguments.of(WinningResult.WIN, "승"), + Arguments.of(WinningResult.LOSE, "패"), + Arguments.of(WinningResult.TIE, "무승부") + ); + } + + @ParameterizedTest + @MethodSource("createReverseWinningResult") + @DisplayName("승패 결과의 반전된 값을 가져온다.") + void getWinningResultReverseTest(WinningResult winningResult, WinningResult expected) { + //when //then + assertThat(winningResult.reverse()).isEqualTo(expected); + } + + private static Stream createReverseWinningResult() { + return Stream.of( + Arguments.of(WinningResult.WIN, WinningResult.LOSE), + Arguments.of(WinningResult.LOSE, WinningResult.WIN), + Arguments.of(WinningResult.TIE, WinningResult.TIE) + ); + } +} \ No newline at end of file diff --git a/src/test/java/blackjack/dto/DrawCardRequestDtoTest.java b/src/test/java/blackjack/dto/DrawCardRequestDtoTest.java new file mode 100644 index 0000000..8b5308e --- /dev/null +++ b/src/test/java/blackjack/dto/DrawCardRequestDtoTest.java @@ -0,0 +1,64 @@ +package blackjack.dto; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class DrawCardRequestDtoTest { + @Test + @DisplayName("카드를 더 받을지에 대한 응답으로 Y를 입력받으면 정상 처리된다.") + void receiveCardRequestValidTest1() { + //given + DrawCardRequestDto drawCardRequestDto = new DrawCardRequestDto("Y"); + + //when, then + //예외가 터지지 않으면 통과하는 테스트 + drawCardRequestDto.validateYesOrNo(); + } + + @Test + @DisplayName("카드를 더 받을지에 대한 응답으로 y를 입력받으면 정상 처리된다.") + void receiveCardRequestValidTest2() { + //given + DrawCardRequestDto drawCardRequestDto = new DrawCardRequestDto("y"); + + //when, then + //예외가 터지지 않으면 통과하는 테스트 + drawCardRequestDto.validateYesOrNo(); + } + + @Test + @DisplayName("카드를 더 받을지에 대한 응답으로 N을 입력받으면 정상 처리된다.") + void receiveCardRequestValidTest3() { + //given + DrawCardRequestDto drawCardRequestDto = new DrawCardRequestDto("N"); + + //when, then + //예외가 터지지 않으면 통과하는 테스트 + drawCardRequestDto.validateYesOrNo(); + } + + @Test + @DisplayName("카드를 더 받을지에 대한 응답으로 n을 입력받으면 정상 처리된다.") + void receiveCardRequestValidTest4() { + //given + DrawCardRequestDto drawCardRequestDto = new DrawCardRequestDto("n"); + + //when, then + //예외가 터지지 않으면 통과하는 테스트 + drawCardRequestDto.validateYesOrNo(); + } + + @Test + @DisplayName("카드를 더 받을지에 대한 응답으로 Y y N n 이 아닌 문자열을 입력 시 예외를 던진다.") + void receiveCardRequestInvalidTest() { + //given + DrawCardRequestDto drawCardRequestDto = new DrawCardRequestDto("U"); + + //when, then + assertThatThrownBy(() -> drawCardRequestDto.validateYesOrNo()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("y 혹은 n 만 입력할 수 있습니다."); + } +} diff --git a/src/test/java/blackjack/utils/StringUtilTest.java b/src/test/java/blackjack/utils/StringUtilTest.java new file mode 100644 index 0000000..c7f837b --- /dev/null +++ b/src/test/java/blackjack/utils/StringUtilTest.java @@ -0,0 +1,28 @@ +package blackjack.utils; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class StringUtilTest { + + @Test + @DisplayName("문자열을 , 구분자를 기준으로 분리한다.") + void splitByCommaTest() { + //given + String input = "Jane Doe, John Doe, Jack Smith "; + List expectedResult = Arrays.asList("Jane Doe", "John Doe", "Jack Smith"); + + //when + List result = StringUtil.splitByComma(input); + + //then + for (int i = 0; i < result.size(); i++) { + assertThat(result.get(i)).isEqualTo(expectedResult.get(i)); + } + } +} \ No newline at end of file diff --git a/src/test/java/empty.txt b/src/test/java/empty.txt deleted file mode 100644 index e69de29..0000000