diff --git a/README.md b/README.md index 3bcfc257847..8bb99765166 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,27 @@ # java-blackjack -블랙잭 게임 미션 저장소 - -## 우아한테크코스 코드리뷰 -* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) \ No newline at end of file +## 기능 구현 + - 플레이어 이름을 입력받는다 + - 영문자 이외의 값이 입력되면 안된다.(대소문자 구별 안한다) + - 첫턴에는 딜러와 등록된 플레이어에게 2장의 카드를 나눠준다. + - 딜러는 한장의 카드만 출력한다. + - 플레이어 들은 모든 카드를 출력한다. + - 모든 플레이어들은 원하는 만큼 카드를 입력받는다. + - 21이 넘어갈경우 더이상 카드를 받지 못하고 버스트, 다음 플레이어로 넘어간다. + - 딜러는 정해진 규칙에 따라 카드를 추가 지급 받는다. + - 16이하일 경우 한장의 카드를 추가 지급 받는다. + - 21초과할 경우 딜러는 버스트한다. + - 17이상일 경우 더이상 지급받지 않는다. + - 각 플레이어별 점수를 계산한다. + - ACE는 1또는 11로 계산한다. + - 11로 계산했을때 21이 초과하지 않으면 11로 계산한다. + - 21이 초과하면 1로 계산한다. + - 승패를 정한다. + - 플레이어가 버스트 될경우 무조건 플레이어가 패배한다. + - 플레이어가 버스트 되지 않고 딜러가 버스트 되면 플레이어 승리한다. + - 둘다 아닐 경우 점수를 계산하여 승패를 정한다. + - 플레이어에게 배팅할 금액을 입력받는다. + - 숫자 이외의 값을 읽으면 예외처리 + - 음수를 입력받을 경우 예외처리 + - 플레이어 배팅액을 참고하여 게임 승패에 따른 수익을 보여준다. + - 블랙잭으로 이길 경우 배팅액의 1.5배, 정상적으로 이길 경우 1배, 비길 경우 0배, 질 경우 -1배를 출력한다. + - 딜러는 플레이어의 수익 결과를 합한 값의 - 값을 출력한다. diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 00000000000..0a4db9f4284 --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,13 @@ +import java.util.Scanner; + +import controller.BlackJackController; +import view.InputView; +import view.OutputView; + +public class Application { + public static void main(String[] args) { + BlackJackController blackJackController = new BlackJackController(new InputView(new Scanner(System.in)), + new OutputView()); + blackJackController.run(); + } +} diff --git a/src/main/java/controller/BlackJackController.java b/src/main/java/controller/BlackJackController.java new file mode 100644 index 00000000000..e7ba703fe72 --- /dev/null +++ b/src/main/java/controller/BlackJackController.java @@ -0,0 +1,76 @@ +package controller; + +import static java.util.stream.Collectors.*; + +import java.util.List; + +import domain.card.deck.Deck; +import domain.card.deck.DeckFactory; +import domain.card.deck.RandomShuffledDeckFactory; +import domain.gamer.Dealer; +import domain.gamer.Player; +import domain.gamer.Players; +import dto.GamerDto; +import dto.GamerEarningDto; +import dto.GamerWithScoreDto; +import dto.PlayerInfo; +import view.InputView; +import view.OutputView; + +public class BlackJackController { + private final InputView inputView; + private final OutputView outputView; + + public BlackJackController(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void run() { + Deck deck = getDeck(); + Players players = getPlayers(deck); + Dealer dealer = Dealer.of(deck); + playBlackJack(deck, players, dealer); + printResult(players, dealer); + } + + private void playBlackJack(Deck deck, Players players, Dealer dealer) { + outputView.printGamersCards(GamerDto.listOf(dealer, players.getPlayers())); + for (Player player : players.getPlayers()) { + decideHitOrStay(deck, player); + } + decideDealerHit(deck, dealer); + } + + private void decideDealerHit(Deck deck, Dealer dealer) { + while (dealer.canHit()) { + outputView.printDealerHit(); + dealer.hit(deck); + } + } + + private void decideHitOrStay(Deck deck, Player player) { + while (player.canHit()) { + YesOrNo yesOrNo = YesOrNo.findByShortName(inputView.inputYesOrNo(player.getName())); + yesOrNo.execute(player, deck); + outputView.printGamerCards(GamerDto.of(player)); + } + } + + private Players getPlayers(Deck deck) { + List playerInfos = inputView.inputPlayerInfos(); + return playerInfos.stream() + .map(playerInfo -> Player.of(playerInfo.getName(), deck, playerInfo.getBattingMoney())) + .collect(collectingAndThen(toList(), Players::new)); + } + + private Deck getDeck() { + DeckFactory randomShuffledDeckFactory = new RandomShuffledDeckFactory(); + return randomShuffledDeckFactory.create(); + } + + private void printResult(Players players, Dealer dealer) { + outputView.printGamersCardsWithScore(GamerWithScoreDto.listOf(dealer, players.getPlayers())); + outputView.printEarning(GamerEarningDto.listOf(dealer, players)); + } +} diff --git a/src/main/java/controller/YesOrNo.java b/src/main/java/controller/YesOrNo.java new file mode 100644 index 00000000000..5906159963e --- /dev/null +++ b/src/main/java/controller/YesOrNo.java @@ -0,0 +1,31 @@ +package controller; + +import java.util.Arrays; +import java.util.function.BiConsumer; + +import domain.card.deck.Deck; +import domain.gamer.Player; + +public enum YesOrNo { + YES("y", Player::hit), + NO("n", ((player, deck) -> player.stay())); + + private final String shortName; + private final BiConsumer playerBehavior; + + YesOrNo(String shortName, BiConsumer playerBehavior) { + this.shortName = shortName; + this.playerBehavior = playerBehavior; + } + + public static YesOrNo findByShortName(String name) { + return Arrays.stream(values()) + .filter(yesOrNo -> yesOrNo.shortName.equalsIgnoreCase(name)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("잘못된 명령어입니다")); + } + + public void execute(Player player, Deck deck) { + playerBehavior.accept(player, deck); + } +} diff --git a/src/main/java/domain/card/Card.java b/src/main/java/domain/card/Card.java new file mode 100644 index 00000000000..c2e195988ef --- /dev/null +++ b/src/main/java/domain/card/Card.java @@ -0,0 +1,45 @@ +package domain.card; + +import java.util.Objects; + +public class Card { + private final Rank rank; + private final Suit suit; + + public Card(Rank rank, Suit suit) { + this.rank = rank; + this.suit = suit; + } + + public boolean isAce() { + return rank == Rank.ACE; + } + + public int getScore() { + return rank.getValue(); + } + + public String getRank() { + return rank.getPattern(); + } + + public String getSuit() { + return suit.getPattern(); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Card card = (Card)o; + return rank == card.rank && + suit == card.suit; + } + + @Override + public int hashCode() { + return Objects.hash(rank, suit); + } +} diff --git a/src/main/java/domain/card/Rank.java b/src/main/java/domain/card/Rank.java new file mode 100644 index 00000000000..f78f091c712 --- /dev/null +++ b/src/main/java/domain/card/Rank.java @@ -0,0 +1,33 @@ +package domain.card; + +public enum Rank { + ACE(1, "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 int value; + private final String pattern; + + Rank(int value, String pattern) { + this.value = value; + this.pattern = pattern; + } + + public int getValue() { + return value; + } + + public String getPattern() { + return pattern; + } +} diff --git a/src/main/java/domain/card/Suit.java b/src/main/java/domain/card/Suit.java new file mode 100644 index 00000000000..bb569db1b4e --- /dev/null +++ b/src/main/java/domain/card/Suit.java @@ -0,0 +1,18 @@ +package domain.card; + +public enum Suit { + CLUB("♣"), + DIAMOND("♦"), + HEART("♥"), + SPADE("♠"); + + private final String pattern; + + Suit(String pattern) { + this.pattern = pattern; + } + + public String getPattern() { + return pattern; + } +} diff --git a/src/main/java/domain/card/deck/AbstractDeckFactory.java b/src/main/java/domain/card/deck/AbstractDeckFactory.java new file mode 100644 index 00000000000..8b50fadf3bb --- /dev/null +++ b/src/main/java/domain/card/deck/AbstractDeckFactory.java @@ -0,0 +1,30 @@ +package domain.card.deck; + +import java.util.Arrays; +import java.util.List; +import java.util.Stack; +import java.util.stream.Collectors; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; + +public abstract class AbstractDeckFactory implements DeckFactory, CardFactory { + @Override + public List createCards() { + return Arrays.stream(Rank.values()) + .flatMap((rank -> Arrays.stream(Suit.values()) + .map(suit -> new Card(rank, suit)))) + .collect(Collectors.toList()); + } + + @Override + public Deck create() { + List cards = handleCards(createCards()); + Stack newCards = new Stack<>(); + newCards.addAll(cards); + return new Deck(newCards); + } + + public abstract List handleCards(List cards); +} diff --git a/src/main/java/domain/card/deck/CardFactory.java b/src/main/java/domain/card/deck/CardFactory.java new file mode 100644 index 00000000000..f64e38e55cf --- /dev/null +++ b/src/main/java/domain/card/deck/CardFactory.java @@ -0,0 +1,9 @@ +package domain.card.deck; + +import java.util.List; + +import domain.card.Card; + +public interface CardFactory { + List createCards(); +} diff --git a/src/main/java/domain/card/deck/Deck.java b/src/main/java/domain/card/deck/Deck.java new file mode 100644 index 00000000000..266b465eab2 --- /dev/null +++ b/src/main/java/domain/card/deck/Deck.java @@ -0,0 +1,17 @@ +package domain.card.deck; + +import java.util.Stack; + +import domain.card.Card; + +public class Deck { + private final Stack cards; + + public Deck(Stack cards) { + this.cards = cards; + } + + public Card pop() { + return cards.pop(); + } +} diff --git a/src/main/java/domain/card/deck/DeckFactory.java b/src/main/java/domain/card/deck/DeckFactory.java new file mode 100644 index 00000000000..3e864234ef5 --- /dev/null +++ b/src/main/java/domain/card/deck/DeckFactory.java @@ -0,0 +1,5 @@ +package domain.card.deck; + +public interface DeckFactory { + Deck create(); +} diff --git a/src/main/java/domain/card/deck/RandomShuffledDeckFactory.java b/src/main/java/domain/card/deck/RandomShuffledDeckFactory.java new file mode 100644 index 00000000000..13ce935c858 --- /dev/null +++ b/src/main/java/domain/card/deck/RandomShuffledDeckFactory.java @@ -0,0 +1,14 @@ +package domain.card.deck; + +import java.util.Collections; +import java.util.List; + +import domain.card.Card; + +public class RandomShuffledDeckFactory extends AbstractDeckFactory { + @Override + public List handleCards(List cards) { + Collections.shuffle(cards); + return cards; + } +} diff --git a/src/main/java/domain/gamer/BattingMoney.java b/src/main/java/domain/gamer/BattingMoney.java new file mode 100644 index 00000000000..386056860d7 --- /dev/null +++ b/src/main/java/domain/gamer/BattingMoney.java @@ -0,0 +1,20 @@ +package domain.gamer; + +public class BattingMoney { + private final int money; + + public BattingMoney(int money) { + validate(money); + this.money = money; + } + + private void validate(int money) { + if (money < 0) { + throw new IllegalArgumentException("음수는 허용되지 않습니다. input : " + money); + } + } + + public int getMoney() { + return money; + } +} diff --git a/src/main/java/domain/gamer/Dealer.java b/src/main/java/domain/gamer/Dealer.java new file mode 100644 index 00000000000..6c31f87dad0 --- /dev/null +++ b/src/main/java/domain/gamer/Dealer.java @@ -0,0 +1,33 @@ +package domain.gamer; + +import java.util.List; + +import domain.card.Card; +import domain.card.deck.Deck; +import domain.state.State; +import domain.state.StateFactory; + +public class Dealer extends Gamer { + private static final int HIT_THRESHOLD = 17; + + private Dealer(State state) { + super(new Name("딜러"), state); + } + + public static Dealer of(Deck deck) { + return new Dealer(StateFactory.create(deck)); + } + + @Override + public boolean canHit() { + return !state.isFinished() && state.calculateScore() < HIT_THRESHOLD; + } + + public int calculateEarning(Players players) { + return -1 * players.calculateTotalEarning(this); + } + + public List getOneCards() { + return getState().getCards().subList(0, 1); + } +} diff --git a/src/main/java/domain/gamer/Gamer.java b/src/main/java/domain/gamer/Gamer.java new file mode 100644 index 00000000000..01e3e7f360c --- /dev/null +++ b/src/main/java/domain/gamer/Gamer.java @@ -0,0 +1,47 @@ +package domain.gamer; + +import java.util.List; + +import domain.card.Card; +import domain.card.deck.Deck; +import domain.state.State; + +public abstract class Gamer { + protected final Name name; + protected State state; + + protected Gamer(Name name, State state) { + this.name = name; + this.state = state; + } + + public void hit(Deck deck) { + state = state.hit(deck); + } + + public void stay() { + state = state.stay(); + } + + public boolean isFinished() { + return state.isFinished(); + } + + public int calculateScore() { + return state.calculateScore(); + } + + public String getName() { + return name.getName(); + } + + public State getState() { + return state; + } + + public List getCards() { + return state.getCards(); + } + + public abstract boolean canHit(); +} diff --git a/src/main/java/domain/gamer/Hand.java b/src/main/java/domain/gamer/Hand.java new file mode 100644 index 00000000000..8f827af310c --- /dev/null +++ b/src/main/java/domain/gamer/Hand.java @@ -0,0 +1,52 @@ +package domain.gamer; + +import java.util.Collections; +import java.util.List; + +import domain.card.Card; + +public class Hand { + private static final int BLACKJACK_SCORE = 21; + private static final int ACE_BONUS = 10; + + private final List cards; + + public Hand(List cards) { + this.cards = cards; + } + + public void add(Card card) { + cards.add(card); + } + + public int calculateScore() { + return cards.stream() + .filter(Card::isAce) + .reduce(calculateRawScore(), (subTotal, card) -> addAceBonus(subTotal), Integer::sum); + } + + private int addAceBonus(int subTotal) { + if (subTotal + ACE_BONUS <= BLACKJACK_SCORE) { + return subTotal + ACE_BONUS; + } + return subTotal; + } + + private int calculateRawScore() { + return cards.stream() + .mapToInt(Card::getScore) + .sum(); + } + + public boolean isBust() { + return calculateScore() > BLACKJACK_SCORE; + } + + public boolean isBlackJackScore() { + return calculateScore() == BLACKJACK_SCORE; + } + + public List getCards() { + return Collections.unmodifiableList(cards); + } +} diff --git a/src/main/java/domain/gamer/Name.java b/src/main/java/domain/gamer/Name.java new file mode 100644 index 00000000000..e19b40d460a --- /dev/null +++ b/src/main/java/domain/gamer/Name.java @@ -0,0 +1,21 @@ +package domain.gamer; + +public class Name { + private static final String SPACE_PATTERN = ".*\\s.*"; + private final String name; + + public Name(String name) { + validate(name); + this.name = name; + } + + private void validate(String name) { + if (name.matches(SPACE_PATTERN)) { + throw new IllegalArgumentException("이름에 공백이 들어가면 안됩니다."); + } + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/domain/gamer/Player.java b/src/main/java/domain/gamer/Player.java new file mode 100644 index 00000000000..6d00ddab25b --- /dev/null +++ b/src/main/java/domain/gamer/Player.java @@ -0,0 +1,33 @@ +package domain.gamer; + +import domain.card.deck.Deck; +import domain.state.State; +import domain.state.StateFactory; + +public class Player extends Gamer { + private final BattingMoney battingMoney; + + private Player(Name name, State state, BattingMoney battingMoney) { + super(name, state); + this.battingMoney = battingMoney; + } + + public static Player of(String name, Deck deck, int battingMoney) { + return new Player(new Name(name), StateFactory.create(deck), new BattingMoney(battingMoney)); + } + + public int calculateEarning(Dealer dealer) { + if (state.isSameResult(dealer.state)) { + return 0; + } + if (state.isWin(dealer.state)) { + return (int)(battingMoney.getMoney() * state.getEarningRate()); + } + return -1 * battingMoney.getMoney(); + } + + @Override + public boolean canHit() { + return !state.isFinished(); + } +} diff --git a/src/main/java/domain/gamer/Players.java b/src/main/java/domain/gamer/Players.java new file mode 100644 index 00000000000..bd718f2a5d4 --- /dev/null +++ b/src/main/java/domain/gamer/Players.java @@ -0,0 +1,29 @@ +package domain.gamer; + +import java.util.Collections; +import java.util.List; + +public class Players { + private final List players; + + public Players(List players) { + validate(players); + this.players = players; + } + + private void validate(List players) { + if (players.isEmpty()) { + throw new IllegalArgumentException("플레이어가 비어있습니다."); + } + } + + public int calculateTotalEarning(Dealer dealer) { + return players.stream() + .mapToInt(player -> player.calculateEarning(dealer)) + .sum(); + } + + public List getPlayers() { + return Collections.unmodifiableList(players); + } +} diff --git a/src/main/java/domain/state/BlackJack.java b/src/main/java/domain/state/BlackJack.java new file mode 100644 index 00000000000..463591a6471 --- /dev/null +++ b/src/main/java/domain/state/BlackJack.java @@ -0,0 +1,26 @@ +package domain.state; + +import domain.gamer.Hand; + +public class BlackJack extends Finished { + private static final double BLACKJACK_RATE = 1.5; + + public BlackJack(Hand hand) { + super(hand); + } + + @Override + public boolean isWin(State state) { + return !isSameResult(state); + } + + @Override + public boolean isSameResult(State state) { + return state instanceof BlackJack; + } + + @Override + public double getEarningRate() { + return BLACKJACK_RATE; + } +} diff --git a/src/main/java/domain/state/Bust.java b/src/main/java/domain/state/Bust.java new file mode 100644 index 00000000000..157e43a774e --- /dev/null +++ b/src/main/java/domain/state/Bust.java @@ -0,0 +1,24 @@ +package domain.state; + +import domain.gamer.Hand; + +public class Bust extends Finished { + public Bust(Hand hand) { + super(hand); + } + + @Override + public boolean isWin(State state) { + return false; + } + + @Override + public boolean isSameResult(State state) { + return state instanceof Bust; + } + + @Override + public double getEarningRate() { + return 0; + } +} diff --git a/src/main/java/domain/state/Finished.java b/src/main/java/domain/state/Finished.java new file mode 100644 index 00000000000..0bd53c6f339 --- /dev/null +++ b/src/main/java/domain/state/Finished.java @@ -0,0 +1,25 @@ +package domain.state; + +import domain.card.deck.Deck; +import domain.gamer.Hand; + +public abstract class Finished extends GamerState { + public Finished(Hand hand) { + super(hand); + } + + @Override + public State hit(Deck deck) { + throw new UnsupportedOperationException(); + } + + @Override + public State stay() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isFinished() { + return true; + } +} diff --git a/src/main/java/domain/state/GamerState.java b/src/main/java/domain/state/GamerState.java new file mode 100644 index 00000000000..dd292d12cef --- /dev/null +++ b/src/main/java/domain/state/GamerState.java @@ -0,0 +1,25 @@ +package domain.state; + +import java.util.Collections; +import java.util.List; + +import domain.card.Card; +import domain.gamer.Hand; + +public abstract class GamerState implements State { + protected final Hand hand; + + public GamerState(Hand hand) { + this.hand = hand; + } + + @Override + public List getCards() { + return Collections.unmodifiableList(hand.getCards()); + } + + public int calculateScore() { + return hand.calculateScore(); + } + +} diff --git a/src/main/java/domain/state/Playing.java b/src/main/java/domain/state/Playing.java new file mode 100644 index 00000000000..11bd2294992 --- /dev/null +++ b/src/main/java/domain/state/Playing.java @@ -0,0 +1,44 @@ +package domain.state; + +import domain.card.deck.Deck; +import domain.gamer.Hand; + +public class Playing extends GamerState { + public Playing(Hand hand) { + super(hand); + } + + @Override + public State hit(Deck deck) { + hand.add(deck.pop()); + if (hand.isBust()) { + return new Bust(hand); + } + return new Playing(hand); + } + + @Override + public State stay() { + return new Stay(hand); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isWin(State state) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSameResult(State state) { + throw new UnsupportedOperationException(); + } + + @Override + public double getEarningRate() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/domain/state/State.java b/src/main/java/domain/state/State.java new file mode 100644 index 00000000000..2b04de28ffe --- /dev/null +++ b/src/main/java/domain/state/State.java @@ -0,0 +1,24 @@ +package domain.state; + +import java.util.List; + +import domain.card.Card; +import domain.card.deck.Deck; + +public interface State { + State hit(Deck deck); + + State stay(); + + int calculateScore(); + + boolean isFinished(); + + boolean isWin(State state); + + boolean isSameResult(State state); + + double getEarningRate(); + + List getCards(); +} diff --git a/src/main/java/domain/state/StateFactory.java b/src/main/java/domain/state/StateFactory.java new file mode 100644 index 00000000000..f9f03401661 --- /dev/null +++ b/src/main/java/domain/state/StateFactory.java @@ -0,0 +1,25 @@ +package domain.state; + +import java.util.ArrayList; +import java.util.Arrays; + +import domain.card.Card; +import domain.card.deck.Deck; +import domain.gamer.Hand; + +public class StateFactory { + private StateFactory() { + } + + public static State create(Deck deck) { + return createState(deck.pop(), deck.pop()); + } + + private static State createState(Card first, Card second) { + Hand hand = new Hand(new ArrayList<>(Arrays.asList(first, second))); + if (hand.isBlackJackScore()) { + return new BlackJack(hand); + } + return new Playing(hand); + } +} diff --git a/src/main/java/domain/state/Stay.java b/src/main/java/domain/state/Stay.java new file mode 100644 index 00000000000..8c3846dc1ee --- /dev/null +++ b/src/main/java/domain/state/Stay.java @@ -0,0 +1,29 @@ +package domain.state; + +import domain.gamer.Hand; + +public class Stay extends Finished { + private static final int NORMAL_RATE = 1; + + public Stay(Hand hand) { + super(hand); + } + + @Override + public boolean isWin(State state) { + if (state instanceof Stay) { + return calculateScore() > state.calculateScore(); + } + return !state.isWin(this); + } + + @Override + public boolean isSameResult(State state) { + return state instanceof Stay && calculateScore() == state.calculateScore(); + } + + @Override + public double getEarningRate() { + return NORMAL_RATE; + } +} diff --git a/src/main/java/dto/CardDto.java b/src/main/java/dto/CardDto.java new file mode 100644 index 00000000000..97c8dc2661d --- /dev/null +++ b/src/main/java/dto/CardDto.java @@ -0,0 +1,34 @@ +package dto; + +import java.util.List; +import java.util.stream.Collectors; + +import domain.card.Card; + +public class CardDto { + private String rank; + private String suit; + + public CardDto(String rank, String suit) { + this.rank = rank; + this.suit = suit; + } + + public static List listOf(List cards) { + return cards.stream() + .map(CardDto::of) + .collect(Collectors.toList()); + } + + private static CardDto of(Card card) { + return new CardDto(card.getRank(), card.getSuit()); + } + + public String getRank() { + return rank; + } + + public String getSuit() { + return suit; + } +} diff --git a/src/main/java/dto/GamerDto.java b/src/main/java/dto/GamerDto.java new file mode 100644 index 00000000000..a73d4e502ae --- /dev/null +++ b/src/main/java/dto/GamerDto.java @@ -0,0 +1,38 @@ +package dto; + +import java.util.List; +import java.util.stream.Collectors; + +import domain.gamer.Dealer; +import domain.gamer.Gamer; +import domain.gamer.Player; + +public class GamerDto { + private String name; + private List cards; + + public GamerDto(String name, List cards) { + this.name = name; + this.cards = cards; + } + + public static GamerDto of(Gamer gamer) { + return new GamerDto(gamer.getName(), CardDto.listOf(gamer.getCards())); + } + + public static List listOf(Dealer dealer, List players) { + List gamers = players.stream() + .map(GamerDto::of) + .collect(Collectors.toList()); + gamers.add(0, new GamerDto(dealer.getName(), CardDto.listOf(dealer.getOneCards()))); + return gamers; + } + + public String getName() { + return name; + } + + public List getCards() { + return cards; + } +} diff --git a/src/main/java/dto/GamerEarningDto.java b/src/main/java/dto/GamerEarningDto.java new file mode 100644 index 00000000000..b73adf68c2a --- /dev/null +++ b/src/main/java/dto/GamerEarningDto.java @@ -0,0 +1,38 @@ +package dto; + +import java.util.List; +import java.util.stream.Collectors; + +import domain.gamer.Dealer; +import domain.gamer.Player; +import domain.gamer.Players; + +public class GamerEarningDto { + private String name; + private int earningMoney; + + public GamerEarningDto(String name, int earningMoney) { + this.name = name; + this.earningMoney = earningMoney; + } + + public static List listOf(Dealer dealer, Players players) { + List gamers = players.getPlayers().stream() + .map(player -> GamerEarningDto.of(player, dealer)) + .collect(Collectors.toList()); + gamers.add(0, new GamerEarningDto(dealer.getName(), dealer.calculateEarning(players))); + return gamers; + } + + public static GamerEarningDto of(Player player, Dealer dealer) { + return new GamerEarningDto(player.getName(), player.calculateEarning(dealer)); + } + + public String getName() { + return name; + } + + public int getEarningMoney() { + return earningMoney; + } +} diff --git a/src/main/java/dto/GamerWithScoreDto.java b/src/main/java/dto/GamerWithScoreDto.java new file mode 100644 index 00000000000..d3e49345462 --- /dev/null +++ b/src/main/java/dto/GamerWithScoreDto.java @@ -0,0 +1,46 @@ +package dto; + +import java.util.List; +import java.util.stream.Collectors; + +import domain.gamer.Dealer; +import domain.gamer.Gamer; +import domain.gamer.Player; + +public class GamerWithScoreDto { + private String name; + private List cards; + private int score; + + public GamerWithScoreDto(String name, List cards, int score) { + this.name = name; + this.cards = cards; + this.score = score; + } + + public static List listOf(Dealer dealer, List players) { + List gamers = players.stream() + .map(GamerWithScoreDto::of) + .collect(Collectors.toList()); + gamers.add(0, new GamerWithScoreDto(dealer.getName(), CardDto.listOf(dealer.getCards()), + dealer.calculateScore())); + return gamers; + } + + private static GamerWithScoreDto of(Gamer gamer) { + return new GamerWithScoreDto(gamer.getName(), CardDto.listOf(gamer.getCards()), + gamer.calculateScore()); + } + + public String getName() { + return name; + } + + public List getCards() { + return cards; + } + + public int getScore() { + return score; + } +} diff --git a/src/main/java/dto/PlayerInfo.java b/src/main/java/dto/PlayerInfo.java new file mode 100644 index 00000000000..940c5d61f10 --- /dev/null +++ b/src/main/java/dto/PlayerInfo.java @@ -0,0 +1,19 @@ +package dto; + +public class PlayerInfo { + private String name; + private int battingMoney; + + public PlayerInfo(String name, int battingMoney) { + this.name = name; + this.battingMoney = battingMoney; + } + + public String getName() { + return name; + } + + public int getBattingMoney() { + return battingMoney; + } +} diff --git a/src/main/java/empty.txt b/src/main/java/empty.txt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 00000000000..7fe98761e9e --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,44 @@ +package view; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import java.util.stream.Collectors; + +import dto.PlayerInfo; + +public class InputView { + private static final String DELIMITER = ","; + private final Scanner scanner; + + public InputView(Scanner scanner) { + this.scanner = scanner; + } + + public List inputPlayerInfos() { + List playerInfos = new ArrayList<>(); + List names = inputName(); + for (String name : names) { + playerInfos.add(new PlayerInfo(name, inputBattingMoney(name))); + } + return playerInfos; + } + + private List inputName() { + System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); + return Arrays.stream(scanner.nextLine().split(DELIMITER)) + .map(String::trim) + .collect(Collectors.toList()); + } + + private int inputBattingMoney(String name) { + System.out.println(name + "의 배팅 금액은?"); + return Integer.parseInt(scanner.nextLine()); + } + + public String inputYesOrNo(String name) { + System.out.println(name + "은 한장의 카드를 더 받으시겠습니까?"); + return scanner.nextLine(); + } +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 00000000000..6dbb58878cf --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,58 @@ +package view; + +import java.util.List; +import java.util.stream.Collectors; + +import dto.CardDto; +import dto.GamerDto; +import dto.GamerEarningDto; +import dto.GamerWithScoreDto; + +public class OutputView { + private static final String DELIMITER = ", "; + + public void printGamersCards(List gamers) { + System.out.println(transferGamerNamesToString(gamers) + "에게 2장의 카드를 나누어 주었습니다."); + for (GamerDto gamer : gamers) { + printGamerCards(gamer); + } + } + + public void printGamerCards(GamerDto gamer) { + System.out.println(gamer.getName() + "카드: " + transferGamerCardsToString(gamer.getCards())); + } + + private String transferGamerNamesToString(List gamers) { + return gamers.stream() + .map(GamerDto::getName) + .collect(Collectors.joining(DELIMITER)); + } + + private String transferGamerCardsToString(List cards) { + return cards.stream() + .map(cardDto -> cardDto.getRank() + cardDto.getSuit()) + .collect(Collectors.joining(DELIMITER)); + } + + public void printDealerHit() { + System.out.println("딜러는 17 미만이라 한 장의 카드를 더 뽑았습니다."); + } + + public void printGamersCardsWithScore(List gamers) { + for (GamerWithScoreDto gamer : gamers) { + printGamerCardsWithScore(gamer); + } + } + + private void printGamerCardsWithScore(GamerWithScoreDto gamer) { + System.out.println( + gamer.getName() + "카드: " + transferGamerCardsToString(gamer.getCards()) + " - 결과 : " + gamer.getScore()); + } + + public void printEarning(List gamers) { + System.out.println("## 최종 수익"); + for (GamerEarningDto gamer : gamers) { + System.out.println(gamer.getName() + ": " + gamer.getEarningMoney()); + } + } +} diff --git a/src/test/java/domain/card/CardTest.java b/src/test/java/domain/card/CardTest.java new file mode 100644 index 00000000000..ccb5b055560 --- /dev/null +++ b/src/test/java/domain/card/CardTest.java @@ -0,0 +1,20 @@ +package domain.card; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class CardTest { + @Test + @DisplayName("생성 테스트") + void constructor() { + assertThat(new Card(Rank.ACE, Suit.SPADE)).isNotNull(); + } + + @Test + @DisplayName("Ace인지 확인") + void isAce() { + assertThat(new Card(Rank.ACE, Suit.SPADE).isAce()).isTrue(); + } +} diff --git a/src/test/java/domain/card/deck/CardFactoryTest.java b/src/test/java/domain/card/deck/CardFactoryTest.java new file mode 100644 index 00000000000..02a3db737ca --- /dev/null +++ b/src/test/java/domain/card/deck/CardFactoryTest.java @@ -0,0 +1,15 @@ +package domain.card.deck; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class CardFactoryTest { + @Test + @DisplayName("52장의 카드를 만드는 지 확인") + void createCards() { + CardFactory testDeckFactory = new TestDeckFactory(); + assertThat(testDeckFactory.createCards()).hasSize(52); + } +} diff --git a/src/test/java/domain/card/deck/DeckFactoryTest.java b/src/test/java/domain/card/deck/DeckFactoryTest.java new file mode 100644 index 00000000000..b6e18227471 --- /dev/null +++ b/src/test/java/domain/card/deck/DeckFactoryTest.java @@ -0,0 +1,27 @@ +package domain.card.deck; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; + +class DeckFactoryTest { + @Test + @DisplayName("Deck을 만드는 지 확인") + void create() { + DeckFactory testDeckFactory = new TestDeckFactory(); + assertThat(testDeckFactory.create()).isNotNull(); + } + + @Test + @DisplayName("DeckFactory가 카드를 원하는 방식으로 조절할 수 있는 지 확인") + void handleCards() { + DeckFactory testDeckFactory = new TestDeckFactory(); + Deck deck = testDeckFactory.create(); + assertThat(deck.pop()).isEqualTo(new Card(Rank.ACE, Suit.CLUB)); + } +} \ No newline at end of file diff --git a/src/test/java/domain/card/deck/DeckTest.java b/src/test/java/domain/card/deck/DeckTest.java new file mode 100644 index 00000000000..85849a81f7d --- /dev/null +++ b/src/test/java/domain/card/deck/DeckTest.java @@ -0,0 +1,24 @@ +package domain.card.deck; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Stack; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; + +class DeckTest { + @Test + @DisplayName("가장 마지막 카드를 내보내는 지 확인") + void pop() { + Stack cards = new Stack<>(); + cards.push(new Card(Rank.EIGHT, Suit.SPADE)); + cards.push(new Card(Rank.NINE, Suit.CLUB)); + Deck deck = new Deck(cards); + assertThat(deck.pop()).isEqualTo(new Card(Rank.NINE, Suit.CLUB)); + } +} \ No newline at end of file diff --git a/src/test/java/domain/card/deck/TestDeckFactory.java b/src/test/java/domain/card/deck/TestDeckFactory.java new file mode 100644 index 00000000000..3947666f337 --- /dev/null +++ b/src/test/java/domain/card/deck/TestDeckFactory.java @@ -0,0 +1,15 @@ +package domain.card.deck; + +import java.util.Arrays; +import java.util.List; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; + +public class TestDeckFactory extends AbstractDeckFactory { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.ACE, Suit.SPADE), new Card(Rank.ACE, Suit.CLUB)); + } +} diff --git a/src/test/java/domain/gamer/BattingMoneyTest.java b/src/test/java/domain/gamer/BattingMoneyTest.java new file mode 100644 index 00000000000..bce643d6a18 --- /dev/null +++ b/src/test/java/domain/gamer/BattingMoneyTest.java @@ -0,0 +1,23 @@ +package domain.gamer; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class +BattingMoneyTest { + @Test + @DisplayName("생성 테스트") + void constructor() { + assertThat(new BattingMoney(10)).isNotNull(); + } + + @Test + @DisplayName("생성 테스트 - 음수 시 예외") + void constructorException() { + assertThatThrownBy(() -> new BattingMoney(-10)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("음수"); + } +} diff --git a/src/test/java/domain/gamer/DealerTest.java b/src/test/java/domain/gamer/DealerTest.java new file mode 100644 index 00000000000..e9d06eebcd1 --- /dev/null +++ b/src/test/java/domain/gamer/DealerTest.java @@ -0,0 +1,45 @@ +package domain.gamer; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.card.deck.AbstractDeckFactory; +import domain.card.deck.Deck; +import domain.card.deck.TestDeckFactory; + +public class DealerTest { + @Test + @DisplayName("생성 테스트") + void constructor() { + Deck deck = new TestDeckFactory().create(); + assertThat(Dealer.of(deck)).isNotNull(); + } + + @Test + @DisplayName("플레이어들의 총 수익을 계산") + void calculateEarning() { + Deck deck = new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.ACE, Suit.CLUB), new Card(Rank.TEN, Suit.CLUB), + new Card(Rank.ACE, Suit.DIAMOND), new Card(Rank.ACE, Suit.CLUB)); + } + }.create(); + Player player = Player.of("사람", deck, 10_000); + player.stay(); + Players players = new Players( + Collections.singletonList(player)); + Dealer dealer = Dealer.of(deck); + + assertThat(dealer.calculateEarning(players)).isEqualTo(10_000); + } +} diff --git a/src/test/java/domain/gamer/GamerTest.java b/src/test/java/domain/gamer/GamerTest.java new file mode 100644 index 00000000000..b79f9030ab3 --- /dev/null +++ b/src/test/java/domain/gamer/GamerTest.java @@ -0,0 +1,47 @@ +package domain.gamer; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.deck.DeckFactory; +import domain.card.deck.TestDeckFactory; +import domain.state.Stay; + +public class GamerTest { + @Test + @DisplayName("게이머가 hit 할 수 있는지 확인") + void hit() { + DeckFactory testDeckFactory = new TestDeckFactory(); + Gamer gamer = Player.of("사람", testDeckFactory.create(), 10_000); + gamer.hit(testDeckFactory.create()); + assertThat(gamer.getState().getCards()).hasSize(3); + } + + @Test + @DisplayName("게이머가 stay 할 수 있는지 확인") + void stay() { + DeckFactory testDeckFactory = new TestDeckFactory(); + Gamer gamer = Player.of("사람", testDeckFactory.create(), 10_000); + gamer.stay(); + assertThat(gamer.getState()).isInstanceOf(Stay.class); + } + + @Test + @DisplayName("게이머의 턴이 끝났는지 확인") + void isFinished() { + DeckFactory testDeckFactory = new TestDeckFactory(); + Gamer gamer = Player.of("사람", testDeckFactory.create(), 10_000); + gamer.hit(testDeckFactory.create()); + assertThat(gamer.isFinished()).isFalse(); + } + + @Test + @DisplayName("게이머의 카드 스코어를 계산하는 지 확인") + void calculateScore() { + DeckFactory testDeckFactory = new TestDeckFactory(); + Gamer gamer = Player.of("사람", testDeckFactory.create(), 10_000); + assertThat(gamer.calculateScore()).isEqualTo(12); + } +} diff --git a/src/test/java/domain/gamer/HandTest.java b/src/test/java/domain/gamer/HandTest.java new file mode 100644 index 00000000000..f46f01a3590 --- /dev/null +++ b/src/test/java/domain/gamer/HandTest.java @@ -0,0 +1,80 @@ +package domain.gamer; + +import static org.assertj.core.api.Assertions.*; + +import java.util.ArrayList; +import java.util.Arrays; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; + +public class HandTest { + @Test + @DisplayName("생성 테스트") + void constructor() { + assertThat( + new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.EIGHT, Suit.CLUB)))).isNotNull(); + } + + @Test + @DisplayName("카드를 더할 수 있는 지 확인") + void addCard() { + Hand hand = new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.EIGHT, Suit.CLUB)))); + hand.add(new Card(Rank.ACE, Suit.SPADE)); + assertThat(hand.getCards()).hasSize(3); + } + + @Test + @DisplayName("현재 가지고 있는 카드의 점수를 계산하는지 확인") + void calculateScore1() { + Hand hand = new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.EIGHT, Suit.CLUB))); + assertThat(hand.calculateScore()).isEqualTo(16); + } + + @Test + @DisplayName("현재 가지고 있는 카드의 점수를 계산하는지 확인 - 에이스 보너스") + void calculateScore2() { + Hand hand = new Hand(Arrays.asList(new Card(Rank.ACE, Suit.SPADE), new Card(Rank.EIGHT, Suit.CLUB))); + assertThat(hand.calculateScore()).isEqualTo(19); + } + + @Test + @DisplayName("현재 가지고 있는 카드의 점수를 계산하는지 확인 - 에이스 보너스 적용 X") + void calculateScore3() { + Hand hand = new Hand(Arrays.asList(new Card(Rank.ACE, Suit.SPADE), + new Card(Rank.TEN, Suit.CLUB), + new Card(Rank.TEN, Suit.SPADE))); + assertThat(hand.calculateScore()).isEqualTo(21); + } + + @Test + @DisplayName("현재 가지고 있는 카드의 점수를 계산하는지 확인 - 에이스 보너스 일부 적용") + void calculateScore4() { + Hand hand = new Hand(Arrays.asList(new Card(Rank.ACE, Suit.SPADE), + new Card(Rank.ACE, Suit.CLUB), + new Card(Rank.NINE, Suit.SPADE))); + assertThat(hand.calculateScore()).isEqualTo(21); + } + + @Test + @DisplayName("버스트 여부 확인") + void isBust() { + Hand hand = new Hand(Arrays.asList(new Card(Rank.TEN, Suit.SPADE), + new Card(Rank.EIGHT, Suit.CLUB), + new Card(Rank.NINE, Suit.SPADE))); + assertThat(hand.isBust()).isTrue(); + } + + @Test + @DisplayName("블랙잭 스코어와 같은 지 확인") + void isBlackJackScore() { + Hand hand = new Hand(Arrays.asList(new Card(Rank.TEN, Suit.SPADE), + new Card(Rank.TWO, Suit.CLUB), + new Card(Rank.NINE, Suit.SPADE))); + assertThat(hand.isBlackJackScore()).isTrue(); + } +} diff --git a/src/test/java/domain/gamer/NameTest.java b/src/test/java/domain/gamer/NameTest.java new file mode 100644 index 00000000000..4c5944d14d6 --- /dev/null +++ b/src/test/java/domain/gamer/NameTest.java @@ -0,0 +1,21 @@ +package domain.gamer; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class NameTest { + @Test + @DisplayName("생성 테스트") + void constructor() { + assertThat(new Name("hi")).isNotNull(); + } + + @Test + void constructorException() { + assertThatThrownBy(() -> new Name("h i")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("공백"); + } +} diff --git a/src/test/java/domain/gamer/PlayerTest.java b/src/test/java/domain/gamer/PlayerTest.java new file mode 100644 index 00000000000..5afdfc30699 --- /dev/null +++ b/src/test/java/domain/gamer/PlayerTest.java @@ -0,0 +1,95 @@ +package domain.gamer; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.card.deck.AbstractDeckFactory; +import domain.card.deck.TestDeckFactory; + +public class PlayerTest { + @Test + @DisplayName("생성 테스트") + void constructor() { + assertThat(Player.of("사람", new TestDeckFactory().create(), 10_000)).isNotNull(); + } + + @Test + @DisplayName("게임 후 수익 결과 계산 - 무승부") + void calculateEarning1() { + Player player = Player.of("사람", new TestDeckFactory().create(), 10_000); + Dealer dealer = Dealer.of(new TestDeckFactory().create()); + + player.stay(); + dealer.stay(); + + assertThat(player.calculateEarning(dealer)).isEqualTo(0); + } + + @Test + @DisplayName("게임 후 수익 결과 계산 - 이기는 경우") + void calculateEarning2() { + Player player = Player.of("사람", new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.KING, Suit.CLUB), new Card(Rank.TEN, Suit.CLUB)); + } + }.create(), 10_000); + Dealer dealer = Dealer.of(new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.ACE, Suit.CLUB), new Card(Rank.SEVEN, Suit.CLUB)); + } + }.create()); + + player.stay(); + dealer.stay(); + + assertThat(player.calculateEarning(dealer)).isEqualTo(10_000); + } + + @Test + @DisplayName("게임 후 수익 결과 계산 - 이기는 경우") + void calculateEarning3() { + Player player = Player.of("사람", new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.KING, Suit.CLUB), new Card(Rank.ACE, Suit.CLUB)); + } + }.create(), 10_000); + Dealer dealer = Dealer.of(new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.ACE, Suit.CLUB), new Card(Rank.SEVEN, Suit.CLUB)); + } + }.create()); + + dealer.stay(); + + assertThat(player.calculateEarning(dealer)).isEqualTo(15_000); + } + + @Test + @DisplayName("게임 후 수익 결과 계산 - 지는 경우") + void calculateEarning4() { + Player player = Player.of("사람", new TestDeckFactory().create(), 10_000); + Dealer dealer = Dealer.of(new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.TEN, Suit.SPADE), new Card(Rank.TEN, Suit.CLUB)); + } + }.create()); + + player.stay(); + dealer.stay(); + + assertThat(player.calculateEarning(dealer)).isEqualTo(-10_000); + } +} diff --git a/src/test/java/domain/gamer/PlayersTest.java b/src/test/java/domain/gamer/PlayersTest.java new file mode 100644 index 00000000000..e75ddc650f9 --- /dev/null +++ b/src/test/java/domain/gamer/PlayersTest.java @@ -0,0 +1,53 @@ +package domain.gamer; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.card.deck.AbstractDeckFactory; +import domain.card.deck.Deck; +import domain.card.deck.TestDeckFactory; + +public class PlayersTest { + @Test + @DisplayName("생성 테스트") + void constructor() { + assertThat(new Players( + Collections.singletonList(Player.of("사람", new TestDeckFactory().create(), 10_000)))).isNotNull(); + } + + @Test + @DisplayName("생성 테스트 - 리스트가 비어있는 경우 예외 처리") + void constructorException() { + assertThatThrownBy(() -> new Players(Collections.emptyList())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("비어"); + } + + @Test + @DisplayName("플레이어들의 총 수익을 계산") + void calculateTotalEarning() { + Deck deck = new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.ACE, Suit.CLUB), new Card(Rank.TEN, Suit.CLUB), + new Card(Rank.ACE, Suit.DIAMOND), new Card(Rank.ACE, Suit.CLUB)); + } + }.create(); + Player player = Player.of("사람", deck, 10_000); + player.stay(); + Players players = new Players( + Collections.singletonList(player)); + Dealer dealer = Dealer.of(deck); + + assertThat(players.calculateTotalEarning(dealer)).isEqualTo(-10_000); + } +} diff --git a/src/test/java/domain/state/BlackJackTest.java b/src/test/java/domain/state/BlackJackTest.java new file mode 100644 index 00000000000..811eed39fef --- /dev/null +++ b/src/test/java/domain/state/BlackJackTest.java @@ -0,0 +1,64 @@ +package domain.state; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.gamer.Hand; + +public class BlackJackTest { + @Test + @DisplayName("승리시 얻는 상금 비율을 확인") + void getEarningRate() { + assertThat(new BlackJack( + new Hand( + Arrays.asList(new Card(Rank.TEN, Suit.SPADE), + new Card(Rank.ACE, Suit.CLUB)))).getEarningRate()).isEqualTo(1.5); + } + + @Test + @DisplayName("블랙잭이 이기는 경우 확인") + void isWin1() { + BlackJack blackJack = new BlackJack( + new Hand(Arrays.asList(new Card(Rank.TEN, Suit.SPADE), new Card(Rank.ACE, Suit.CLUB)))); + Stay stay = new Stay(new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.NINE, Suit.CLUB)))); + assertThat(blackJack.isWin(stay)).isTrue(); + } + + @Test + @DisplayName("블랙잭이 이기는게 아닌 경우 확인") + void isWin2() { + BlackJack blackJack1 = new BlackJack( + new Hand(Arrays.asList(new Card(Rank.TEN, Suit.SPADE), new Card(Rank.ACE, Suit.CLUB)))); + BlackJack blackJack2 = new BlackJack( + new Hand(Arrays.asList(new Card(Rank.KING, Suit.SPADE), new Card(Rank.ACE, Suit.CLUB)))); + + assertThat(blackJack1.isWin(blackJack2)).isFalse(); + } + + @Test + @DisplayName("블랙잭과 동일 결과가 아닌지 확인") + void isSameResult1() { + BlackJack blackJack = new BlackJack( + new Hand(Arrays.asList(new Card(Rank.TEN, Suit.SPADE), new Card(Rank.ACE, Suit.CLUB)))); + Stay stay = new Stay(new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.NINE, Suit.CLUB)))); + assertThat(blackJack.isSameResult(stay)); + } + + @Test + @DisplayName("블랙잭과 동일 결과인지 확인") + void isSameResult2() { + BlackJack blackJack1 = new BlackJack( + new Hand(Arrays.asList(new Card(Rank.TEN, Suit.SPADE), new Card(Rank.ACE, Suit.CLUB)))); + BlackJack blackJack2 = new BlackJack( + new Hand(Arrays.asList(new Card(Rank.KING, Suit.SPADE), new Card(Rank.ACE, Suit.CLUB)))); + + assertThat(blackJack1.isSameResult(blackJack2)).isTrue(); + } +} diff --git a/src/test/java/domain/state/BustTest.java b/src/test/java/domain/state/BustTest.java new file mode 100644 index 00000000000..a7e27f7e837 --- /dev/null +++ b/src/test/java/domain/state/BustTest.java @@ -0,0 +1,50 @@ +package domain.state; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.gamer.Hand; + +public class BustTest { + @Test + @DisplayName("승리시 얻는 상금 비율을 확인") + void getEarningRate() { + assertThat(new Bust( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.KING, Suit.CLUB), + new Card(Rank.NINE, Suit.CLUB)))).getEarningRate()).isEqualTo(0); + } + + @Test + @DisplayName("bust가 지는 경우 확인") + void isWin() { + Bust bust = new Bust( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.KING, Suit.CLUB), + new Card(Rank.NINE, Suit.CLUB)))); + assertThat(bust.isWin(new Bust( + new Hand( + Arrays.asList(new Card(Rank.SEVEN, Suit.SPADE), new Card(Rank.KING, Suit.CLUB), + new Card(Rank.NINE, Suit.CLUB)))))).isFalse(); + } + + @Test + @DisplayName("bust가 같은 결과가 아닌 경우 확인") + void isSameResult() { + Bust bust = new Bust( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.KING, Suit.CLUB), + new Card(Rank.NINE, Suit.CLUB)))); + assertThat(bust.isWin(new Bust( + new Hand( + Arrays.asList(new Card(Rank.SEVEN, Suit.SPADE), new Card(Rank.KING, Suit.CLUB), + new Card(Rank.NINE, Suit.CLUB)))))).isFalse(); + } +} diff --git a/src/test/java/domain/state/FinishedTest.java b/src/test/java/domain/state/FinishedTest.java new file mode 100644 index 00000000000..b91bf01a446 --- /dev/null +++ b/src/test/java/domain/state/FinishedTest.java @@ -0,0 +1,49 @@ +package domain.state; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.card.deck.TestDeckFactory; +import domain.gamer.Hand; + +public class FinishedTest { + @Test + @DisplayName("종료 상태인지 확인") + void isFinished() { + assertThat(new BlackJack( + new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB)))).isFinished()).isTrue(); + } + + @Test + @DisplayName("hit 요청 시 예외 처리") + void hit() { + assertThatThrownBy(() -> new Stay( + new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB)))).hit(new TestDeckFactory().create())) + .isInstanceOf(UnsupportedOperationException.class); + } + + @Test + @DisplayName("stay 요청 시 예외 처리") + void stay() { + assertThatThrownBy(() -> new Stay( + new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB)))).stay()) + .isInstanceOf(UnsupportedOperationException.class); + } + + @Test + void calculateScore() { + assertThat(new Stay( + new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB)))).calculateScore()).isEqualTo(17); + } +} diff --git a/src/test/java/domain/state/PlayingTest.java b/src/test/java/domain/state/PlayingTest.java new file mode 100644 index 00000000000..15e7c700b03 --- /dev/null +++ b/src/test/java/domain/state/PlayingTest.java @@ -0,0 +1,76 @@ +package domain.state; + +import static org.assertj.core.api.Assertions.*; + +import java.util.ArrayList; +import java.util.Arrays; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.card.deck.Deck; +import domain.card.deck.TestDeckFactory; +import domain.gamer.Hand; + +public class PlayingTest { + @Test + @DisplayName("종료 상태인지 확인") + void isFinished() { + assertThat(new Playing( + new Hand(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB)))).isFinished()).isFalse(); + } + + @Test + @DisplayName("hit 시 다음 상태를 제대로 가지고 오는 지 확인") + void hit() { + Deck deck = new TestDeckFactory().create(); + State state = new Playing( + new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB))))).hit(deck); + assertThat(state).isInstanceOf(Playing.class); + } + + @Test + @DisplayName("stay 시 다음 상태를 제대로 가지고 오는 지 확인") + void stay() { + Deck deck = new TestDeckFactory().create(); + State state = new Playing( + new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB))))).stay(); + assertThat(state).isInstanceOf(Stay.class); + } + + @Test + @DisplayName("EarningRate 요청 시 예외 처리") + void getEarningRate() { + assertThatThrownBy(() -> new Playing( + new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB))))).getEarningRate()) + .isInstanceOf(UnsupportedOperationException.class); + } + + @Test + @DisplayName("isSameResult 요청 시 예외 발생") + void isSameResult() { + assertThatThrownBy(() -> new Playing( + new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB))))).isSameResult( + new Stay(new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB))))))) + .isInstanceOf(UnsupportedOperationException.class); + } + @Test + @DisplayName("isSameResult 요청 시 예외 발생") + void isWin() { + assertThatThrownBy(() -> new Playing( + new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB))))).isWin( + new Stay(new Hand(new ArrayList<>(Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB))))))) + .isInstanceOf(UnsupportedOperationException.class); + } +} diff --git a/src/test/java/domain/state/StateFactoryTest.java b/src/test/java/domain/state/StateFactoryTest.java new file mode 100644 index 00000000000..15b6bcc2ad5 --- /dev/null +++ b/src/test/java/domain/state/StateFactoryTest.java @@ -0,0 +1,39 @@ +package domain.state; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.card.deck.AbstractDeckFactory; +import domain.card.deck.Deck; +import domain.card.deck.TestDeckFactory; + +public class StateFactoryTest { + @Test + @DisplayName("Deck을 이용해 초기 카드 상태를 만들어주는 지 확인") + void create1() { + Deck deck = new TestDeckFactory().create(); + State state = StateFactory.create(deck); + assertThat(state).isInstanceOf(Playing.class); + } + + @Test + @DisplayName("Deck을 이용해 초기 카드 상태를 만들어주는 지 확인 - 블랙잭") + void create2() { + Deck deck = new AbstractDeckFactory() { + @Override + public List handleCards(List cards) { + return Arrays.asList(new Card(Rank.ACE, Suit.CLUB), new Card(Rank.TEN, Suit.CLUB)); + } + }.create(); + State state = StateFactory.create(deck); + assertThat(state).isInstanceOf(BlackJack.class); + } +} diff --git a/src/test/java/domain/state/StayTest.java b/src/test/java/domain/state/StayTest.java new file mode 100644 index 00000000000..9e9a4dc964a --- /dev/null +++ b/src/test/java/domain/state/StayTest.java @@ -0,0 +1,70 @@ +package domain.state; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import domain.card.Card; +import domain.card.Rank; +import domain.card.Suit; +import domain.gamer.Hand; + +public class StayTest { + @Test + @DisplayName("승리시 얻는 상금 비율을 확인") + void getEarningRate() { + assertThat(new Stay( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), + new Card(Rank.NINE, Suit.CLUB)))).getEarningRate()).isEqualTo(1); + } + + @Test + @DisplayName("stay가 이기는 경우 확인") + void isWin1() { + Stay stay = new Stay( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.KING, Suit.CLUB)))); + assertThat(stay.isWin(new Bust( + new Hand( + Arrays.asList(new Card(Rank.SEVEN, Suit.SPADE), new Card(Rank.KING, Suit.CLUB), + new Card(Rank.NINE, Suit.CLUB)))))).isTrue(); + } + + @Test + @DisplayName("stay가 지는 경우 확인") + void isWin2() { + Stay stay = new Stay( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.KING, Suit.CLUB)))); + assertThat(stay.isWin(new BlackJack( + new Hand( + Arrays.asList(new Card(Rank.ACE, Suit.SPADE), new Card(Rank.KING, Suit.CLUB)))))).isFalse(); + } + + @Test + @DisplayName("stay가 stay에게 지는 경우 확인") + void isWin3() { + Stay stay = new Stay( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.KING, Suit.CLUB)))); + assertThat(stay.isWin(new Stay( + new Hand( + Arrays.asList(new Card(Rank.TEN, Suit.SPADE), new Card(Rank.KING, Suit.CLUB)))))).isFalse(); + } + + @Test + @DisplayName("자신과 동일한 결과일지 확인") + void isSameResult() { + Stay stay = new Stay( + new Hand( + Arrays.asList(new Card(Rank.EIGHT, Suit.SPADE), new Card(Rank.NINE, Suit.CLUB)))); + assertThat(stay.isSameResult(new Stay( + new Hand( + Arrays.asList(new Card(Rank.SEVEN, Suit.SPADE), new Card(Rank.KING, Suit.CLUB), + new Card(Rank.NINE, Suit.CLUB)))))).isFalse(); + } +} diff --git a/src/test/java/empty.txt b/src/test/java/empty.txt deleted file mode 100644 index e69de29bb2d..00000000000