diff --git a/README.md b/README.md index 556099c4de3..7f90631694f 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,55 @@ 블랙잭 미션 저장소 -## 우아한테크코스 코드리뷰 +## 페어와 지킬 컨벤션 +1. 클래스 정의 다음 줄은 공백으로 한다. +2. test code에 사용하는 메서드는 `static import`한다. -- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) +## 요구사항 +### 카드 +- [x] 카드는 모양과 수로 이루어져 있다. + - [x] 모양으로는 스페이드, 다이아, 하트, 클로버가 있다. + - [x] 수로는 Ace, King, Queen, Jack과 2 이상 10 이하의 정수가 있다. +- [x] 카드의 숫자 계산은 카드 숫자를 기본으로 한다. + - [x] Ace는 1 또는 11로 계산할 수 있다. + - [x] King, Queen, Jack은 각각 10으로 계산한다. + +### 덱 +- [x] 맨 위의 카드 한 장을 뽑는다. +- [x] 덱에 카드가 없는 경우, 예외를 발생한다. + +### 플레이어 +- [x] 덱에서 카드를 한 장 뽑는다. +- [x] 지금까지 뽑은 카드의 점수를 계산한다. + +### 이름 +- [x] 이름은 2글자 이상 5글자 이하이다. +- [x] 이름은 공백으로만 구성될 수 없다. + +### 플레이어들 +- [x] 플레이어들의 이름은 중복될 수 없다. +- [x] 플레이어들의 인원수는 1명 이상 10명 이하이다. + +### 딜러 +- [x] 플레이어의 기능을 상속한다. +- [x] 17점 이상이 될 때까지 카드를 계속 뽑는다. + +### 게임 결과 +- [x] 게임 결과를 아래의 순서대로 올바르게 판단한다. + 1. 플레이어의 점수가 21점을 초과하면 딜러가 승리한다. + 2. 플레이어의 점수가 21점을 초과하지 않으면서 딜러의 점수가 21점을 초과하면 플레이어가 승리한다. + 3. 딜러와 플레이어의 점수를 비교해 승패를 결정한다. + 4. 점수가 같으면 무승부이다. +- [x] 각 플레이어별 게임 결과를 저장한다. + +--- + +### 게임 흐름 +- [x] (hit, stand) 21을 넘지 않을 경우 원한다면 카드를 계속 뽑을 수 있다. +- [x] 게임이 시작할 때 딜러와 플레이어는 두 장의 카드를 지급 받는다. +- [x] 플레이어의 카드는 모두에게 공개된다. +- [x] 딜러의 두 번째 카드는 공개되지 않는다. +- [x] 플레이어가 카드를 새로 뽑을 때마다 카드 현황을 공개한다. +- [x] 각 플레이어는 딜러와만 승패를 겨룬다. +- [x] 딜러는 모든 플레이어와 승패를 겨룬다. +- [x] 게임을 완료한 후 딜러를 포함한 모든 플레이어의 승패를 확인한다. diff --git a/src/main/java/.gitkeep b/src/main/java/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/main/java/blackjack/BlackJackMain.java b/src/main/java/blackjack/BlackJackMain.java new file mode 100644 index 00000000000..9be8abc1041 --- /dev/null +++ b/src/main/java/blackjack/BlackJackMain.java @@ -0,0 +1,19 @@ +package blackjack; + +import blackjack.game.BlackJackGame; +import blackjack.view.InputView; +import blackjack.view.OutputView; + +public class BlackJackMain { + + public static void main(String[] args) { + InputView inputView = new InputView(); + OutputView outputView = new OutputView(); + BlackJackGame blackJackGame = new BlackJackGame(inputView, outputView); + try { + blackJackGame.play(); + } catch (Exception e) { + outputView.printExceptionMessage(e); + } + } +} diff --git a/src/main/java/blackjack/card/Card.java b/src/main/java/blackjack/card/Card.java new file mode 100644 index 00000000000..0c0992c14be --- /dev/null +++ b/src/main/java/blackjack/card/Card.java @@ -0,0 +1,48 @@ +package blackjack.card; + +import blackjack.player.Score; +import java.util.Objects; + +public class Card { + + private final Shape shape; + private final Rank rank; + + public Card(Shape shape, Rank rank) { + this.shape = shape; + this.rank = rank; + } + + public boolean isAce() { + return rank == Rank.ACE; + } + + public Score getScore() { + return rank.getScore(); + } + + public Shape getShape() { + return shape; + } + + public Rank getRank() { + return rank; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Card card = (Card) o; + return shape == card.shape && rank == card.rank; + } + + @Override + public int hashCode() { + return Objects.hash(shape, rank); + } +} diff --git a/src/main/java/blackjack/card/Deck.java b/src/main/java/blackjack/card/Deck.java new file mode 100644 index 00000000000..9ed021cc265 --- /dev/null +++ b/src/main/java/blackjack/card/Deck.java @@ -0,0 +1,44 @@ +package blackjack.card; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class Deck { + + private final Queue cards; + + Deck(List cards) { + this.cards = new LinkedList<>(cards); + } + + public static Deck createShuffledFullDeck() { + List cards = new LinkedList<>(); + for (Shape shape : Shape.values()) { + cards.addAll(createNumberCardsOf(shape)); + } + Collections.shuffle(cards); + return new Deck(cards); + } + + static List createNumberCardsOf(Shape shape) { + List cards = new ArrayList<>(); + for (Rank rank : Rank.values()) { + cards.add(new Card(shape, rank)); + } + return cards; + } + + public Card draw() { + if (cards.isEmpty()) { + throw new IllegalStateException("[ERROR] 덱이 비어있습니다."); + } + return cards.poll(); + } + + int size() { + return cards.size(); + } +} diff --git a/src/main/java/blackjack/card/Rank.java b/src/main/java/blackjack/card/Rank.java new file mode 100644 index 00000000000..826c73b8cd3 --- /dev/null +++ b/src/main/java/blackjack/card/Rank.java @@ -0,0 +1,30 @@ +package blackjack.card; + +import blackjack.player.Score; + +public enum Rank { + + ACE(1), + TWO(2), + THREE(3), + FOUR(4), + FIVE(5), + SIX(6), + SEVEN(7), + EIGHT(8), + NINE(9), + TEN(10), + JACK(10), + QUEEN(10), + KING(10); + + private final Score score; + + Rank(int score) { + this.score = new Score(score); + } + + Score getScore() { + return this.score; + } +} diff --git a/src/main/java/blackjack/card/Shape.java b/src/main/java/blackjack/card/Shape.java new file mode 100644 index 00000000000..4ebd32ecc64 --- /dev/null +++ b/src/main/java/blackjack/card/Shape.java @@ -0,0 +1,9 @@ +package blackjack.card; + +public enum Shape { + + HEART, + SPADE, + CLOVER, + DIAMOND +} diff --git a/src/main/java/blackjack/game/BlackJackGame.java b/src/main/java/blackjack/game/BlackJackGame.java new file mode 100644 index 00000000000..473bc42d0df --- /dev/null +++ b/src/main/java/blackjack/game/BlackJackGame.java @@ -0,0 +1,130 @@ +package blackjack.game; + +import blackjack.card.Deck; +import blackjack.player.Dealer; +import blackjack.player.Player; +import blackjack.player.Players; +import blackjack.view.InputView; +import blackjack.view.OutputView; +import java.util.List; + +public class BlackJackGame { + + private final InputView inputView; + private final OutputView outputView; + + public BlackJackGame(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void play() { + Deck deck = Deck.createShuffledFullDeck(); + Dealer dealer = new Dealer(); + + Players players = createPlayers(); + initializeGame(deck, dealer, players); + proceedPlayersTurn(deck, players); + proceedDealerTurn(deck, dealer); + + showCardsWithScore(dealer, players); + showMatchResult(dealer, players); + } + + private Players createPlayers() { + outputView.printNamesRequest(); + List names = inputView.readNames(); + Players players = new Players(names); + outputView.printNewLine(); + return players; + } + + private void initializeGame(Deck deck, Dealer dealer, Players players) { + players.doInitialDraw(deck); + dealer.doInitialDraw(deck); + outputView.printInitializeBlackJack(players.getNames()); + showInitialCard(dealer, players); + } + + private void showInitialCard(Dealer dealer, Players players) { + outputView.printDealerFirstCard(dealer.getFirstCard()); + + for (Player player : players.getPlayers()) { + outputView.printPlayerCards(player.getName(), player.getCards()); + } + outputView.printNewLine(); + } + + private void proceedPlayersTurn(Deck deck, Players players) { + for (Player player : players.getPlayers()) { + proceedPlayerTurn(deck, player); + } + outputView.printNewLine(); + } + + private void proceedPlayerTurn(Deck deck, Player player) { + Command command = askPlayerToDrawMore(player); + if (command.isNo()) { + return; + } + player.drawCard(deck); + outputView.printPlayerCards(player.getName(), player.getCards()); + + if (player.hasDrawableScore()) { + proceedPlayerTurn(deck, player); + } + } + + private Command askPlayerToDrawMore(Player player) { + outputView.printDrawMoreCardRequest(player.getName()); + String input = inputView.readCommand(); + return Command.from(input); + } + + private void proceedDealerTurn(Deck deck, Dealer dealer) { + while (dealer.hasDrawableScore()) { + dealer.drawCard(deck); + outputView.printDealerDrawCard(); + outputView.printNewLine(); + } + } + + private void showCardsWithScore(Dealer dealer, Players players) { + outputView.printDealerCardsWithScore(dealer.getCards(), dealer.getScore()); + for (Player player : players.getPlayers()) { + outputView.printPlayerCardsWithScore(player.getName(), player.getCards(), player.getScore()); + } + outputView.printNewLine(); + } + + private void showMatchResult(Dealer dealer, Players players) { + MatchResults matchResults = calculateMatchResults(dealer, players); + outputView.printResultStart(); + showDealerResult(matchResults); + showPlayersResult(players, matchResults); + } + + private MatchResults calculateMatchResults(Dealer dealer, Players players) { + MatchResults matchResults = new MatchResults(); + for (Player player : players.getPlayers()) { + matchResults.addResult(player.getName(), player.getScore(), dealer.getScore()); + } + return matchResults; + } + + private void showDealerResult(MatchResults matchResults) { + outputView.printDealerResult( + matchResults.getResultCount(MatchResult.DEALER_WIN), + matchResults.getResultCount(MatchResult.TIE), + matchResults.getResultCount(MatchResult.PLAYER_WIN) + ); + } + + private void showPlayersResult(Players players, MatchResults matchResults) { + for (Player player : players.getPlayers()) { + String playerName = player.getName(); + MatchResult result = matchResults.getResultByName(playerName); + outputView.printPlayerResult(playerName, result); + } + } +} diff --git a/src/main/java/blackjack/game/Command.java b/src/main/java/blackjack/game/Command.java new file mode 100644 index 00000000000..1dcece9cbf1 --- /dev/null +++ b/src/main/java/blackjack/game/Command.java @@ -0,0 +1,26 @@ +package blackjack.game; + +import java.util.Arrays; +import java.util.Objects; + +public enum Command { + YES("y"), + NO("n"); + + private final String value; + + Command(String value) { + this.value = value; + } + + public static Command from(String value) { + return Arrays.stream(values()) + .filter(command -> command.value.equals(value)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("[ERROR] 존재하지 않는 명령어입니다.")); + } + + public boolean isNo() { + return Objects.equals(value, Command.NO.value); + } +} diff --git a/src/main/java/blackjack/game/MatchResult.java b/src/main/java/blackjack/game/MatchResult.java new file mode 100644 index 00000000000..e5dd19c8270 --- /dev/null +++ b/src/main/java/blackjack/game/MatchResult.java @@ -0,0 +1,34 @@ +package blackjack.game; + +import blackjack.player.Score; + +public enum MatchResult { + + DEALER_WIN, + PLAYER_WIN, + TIE; + + public static MatchResult chooseWinner(Score playerScore, Score dealerScore) { + if (isPlayerWinningCondition(playerScore, dealerScore)) { + return PLAYER_WIN; + } + if (isDealerWinningCondition(playerScore, dealerScore)) { + return DEALER_WIN; + } + return TIE; + } + + private static boolean isPlayerWinningCondition(Score playerScore, Score dealerScore) { + if (playerScore.isBust()) { + return false; + } + return dealerScore.isBust() || playerScore.isLargerThan(dealerScore); + } + + private static boolean isDealerWinningCondition(Score playerScore, Score dealerScore) { + if (playerScore.isBust()) { + return true; + } + return dealerScore.isNotBust() && dealerScore.isLargerThan(playerScore); + } +} diff --git a/src/main/java/blackjack/game/MatchResults.java b/src/main/java/blackjack/game/MatchResults.java new file mode 100644 index 00000000000..6446f6656e8 --- /dev/null +++ b/src/main/java/blackjack/game/MatchResults.java @@ -0,0 +1,33 @@ +package blackjack.game; + +import blackjack.player.Score; +import java.util.HashMap; +import java.util.Map; + +public class MatchResults { + + private final Map results; + + public MatchResults() { + this.results = new HashMap<>(); + } + + public void addResult(String playerName, Score playerScore, Score dealerScore) { + MatchResult result = MatchResult.chooseWinner(playerScore, dealerScore); + results.put(playerName, result); + } + + public MatchResult getResultByName(String playerName) { + if (!results.containsKey(playerName)) { + throw new IllegalArgumentException("[ERROR] 존재하지 않는 이름입니다."); + } + return results.get(playerName); + } + + public int getResultCount(MatchResult result) { + return (int) results.values() + .stream() + .filter(matchResult -> matchResult == result) + .count(); + } +} diff --git a/src/main/java/blackjack/player/Dealer.java b/src/main/java/blackjack/player/Dealer.java new file mode 100644 index 00000000000..76c67ec1deb --- /dev/null +++ b/src/main/java/blackjack/player/Dealer.java @@ -0,0 +1,30 @@ +package blackjack.player; + +import blackjack.card.Card; +import java.util.List; + +public class Dealer extends Player { + + private static final int MAX_DRAWABLE_SCORE = 16; + + public Dealer() { + super("딜러"); + } + + Dealer(Hand hand) { + super("딜러", hand); + } + + public Card getFirstCard() { + List cards = super.getCards(); + if (cards.isEmpty()) { + throw new IllegalStateException("[ERROR] 딜러가 카드를 가지고 있지 않습니다."); + } + return cards.get(0); + } + + @Override + public boolean hasDrawableScore() { + return getScore().isSmallerThanOrEqualTo(new Score(MAX_DRAWABLE_SCORE)); + } +} diff --git a/src/main/java/blackjack/player/Hand.java b/src/main/java/blackjack/player/Hand.java new file mode 100644 index 00000000000..ac3acbdaedc --- /dev/null +++ b/src/main/java/blackjack/player/Hand.java @@ -0,0 +1,46 @@ +package blackjack.player; + +import blackjack.card.Card; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Hand { + + private final List cards; + + Hand(List cards) { + this.cards = cards; + } + + Hand() { + this(new ArrayList<>()); + } + + public Score calculateScore() { + Score minimumScore = calculateMinimumScore(); + if (!hasAce()) { + return minimumScore; + } + return minimumScore.changeToLargeAceScore(); + } + + private Score calculateMinimumScore() { + return cards.stream() + .map(Card::getScore) + .reduce(new Score(0), Score::add); + } + + private boolean hasAce() { + return cards.stream() + .anyMatch(Card::isAce); + } + + public void addCard(Card card) { + cards.add(card); + } + + public List getCards() { + return Collections.unmodifiableList(cards); + } +} diff --git a/src/main/java/blackjack/player/Name.java b/src/main/java/blackjack/player/Name.java new file mode 100644 index 00000000000..93a4bf79fe9 --- /dev/null +++ b/src/main/java/blackjack/player/Name.java @@ -0,0 +1,37 @@ +package blackjack.player; + +public class Name { + + private static final int MIN_NAME_LENGTH = 2; + private static final int MAX_NAME_LENGTH = 5; + + private final String name; + + Name(String name) { + validateName(name); + this.name = name; + } + + private void validateName(String name) { + validateNonBlankName(name); + validateNameLength(name); + } + + private void validateNameLength(String name) { + if (name.length() < MIN_NAME_LENGTH || name.length() > MAX_NAME_LENGTH) { + throw new IllegalArgumentException( + "[ERROR] 이름은 " + MIN_NAME_LENGTH + "글자 이상 " + MAX_NAME_LENGTH + "글자 이하여야 합니다." + ); + } + } + + private void validateNonBlankName(String name) { + if (name == null || name.isBlank()) { + throw new IllegalArgumentException("[ERROR] 이름은 공백일 수 없습니다."); + } + } + + String getName() { + return name; + } +} diff --git a/src/main/java/blackjack/player/Player.java b/src/main/java/blackjack/player/Player.java new file mode 100644 index 00000000000..e61fde1b2d9 --- /dev/null +++ b/src/main/java/blackjack/player/Player.java @@ -0,0 +1,45 @@ +package blackjack.player; + +import blackjack.card.Card; +import blackjack.card.Deck; +import java.util.List; + +public class Player { + + private final Name name; + private final Hand hand; + + Player(String name, Hand hand) { + this.name = new Name(name); + this.hand = hand; + } + + Player(String name) { + this(name, new Hand()); + } + + public void drawCard(Deck deck) { + hand.addCard(deck.draw()); + } + + public void doInitialDraw(Deck deck) { + drawCard(deck); + drawCard(deck); + } + + public boolean hasDrawableScore() { + return hand.calculateScore().isNotBust(); + } + + public String getName() { + return name.getName(); + } + + public Score getScore() { + return hand.calculateScore(); + } + + public List getCards() { + return hand.getCards(); + } +} diff --git a/src/main/java/blackjack/player/Players.java b/src/main/java/blackjack/player/Players.java new file mode 100644 index 00000000000..ed188819125 --- /dev/null +++ b/src/main/java/blackjack/player/Players.java @@ -0,0 +1,60 @@ +package blackjack.player; + +import blackjack.card.Deck; +import java.util.List; +import java.util.Set; + +public class Players { + + private static final int MIN_PLAYER_COUNT = 1; + private static final int MAX_PLAYER_COUNT = 10; + + private final List players; + + public Players(List playerNames) { + validatePlayers(playerNames); + this.players = playerNames.stream() + .map(Player::new) + .toList(); + } + + private void validatePlayers(List playerNames) { + validateNotNull(playerNames); + validateSize(playerNames); + validateUniqueNames(playerNames); + } + + private void validateNotNull(List playerNames) { + if (playerNames == null) { + throw new IllegalArgumentException("[ERROR] 플레이어로 null이 전달되었습니다."); + } + } + + private void validateSize(List playerNames) { + if (playerNames.size() < MIN_PLAYER_COUNT || playerNames.size() > MAX_PLAYER_COUNT) { + throw new IllegalArgumentException( + "[ERROR] 플레이어의 수는 " + MIN_PLAYER_COUNT + "명 이상 " + MAX_PLAYER_COUNT + "명 이하여야 합니다." + ); + } + } + + private void validateUniqueNames(List playerNames) { + if (Set.copyOf(playerNames).size() != playerNames.size()) { + throw new IllegalArgumentException("[ERROR] 이름은 중복될 수 없습니다."); + } + } + + public void doInitialDraw(Deck deck) { + players.forEach(player -> player.doInitialDraw(deck)); + } + + public List getNames() { + return players.stream() + .map(Player::getName) + .toList(); + } + + public List getPlayers() { + return players; + } +} diff --git a/src/main/java/blackjack/player/Score.java b/src/main/java/blackjack/player/Score.java new file mode 100644 index 00000000000..3ee3d2dfc41 --- /dev/null +++ b/src/main/java/blackjack/player/Score.java @@ -0,0 +1,68 @@ +package blackjack.player; + +import java.util.Objects; + +public class Score { + + private static final int BLACKJACK = 21; + private static final int ADDITIONAL_ACE = 10; + + private int score; + + public Score(int score) { + this.score = score; + } + + public Score add(int score) { + return add(new Score(score)); + } + + public Score add(Score other) { + return new Score(this.score + other.score); + } + + public boolean isBust() { + return score > BLACKJACK; + } + + public boolean isNotBust() { + return !isBust(); + } + + public boolean isLargerThan(Score other) { + return this.score > other.score; + } + + public boolean isSmallerThanOrEqualTo(Score other) { + return !isLargerThan(other); + } + + public Score changeToLargeAceScore() { + Score largeAceScore = add(ADDITIONAL_ACE); + if (largeAceScore.isBust()) { + return this; + } + return largeAceScore; + } + + public int getScore() { + return score; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Score score1 = (Score) o; + return score == score1.score; + } + + @Override + public int hashCode() { + return Objects.hash(score); + } +} diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java new file mode 100644 index 00000000000..ea46be171b2 --- /dev/null +++ b/src/main/java/blackjack/view/InputView.java @@ -0,0 +1,22 @@ +package blackjack.view; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + +public class InputView { + + private static final String NAME_DELIMITER = ","; + + private final Scanner scanner = new Scanner(System.in); + + public List readNames() { + String input = scanner.nextLine(); + String[] names = input.split(NAME_DELIMITER); + return Arrays.stream(names).toList(); + } + + public String readCommand() { + return scanner.nextLine(); + } +} diff --git a/src/main/java/blackjack/view/OutputView.java b/src/main/java/blackjack/view/OutputView.java new file mode 100644 index 00000000000..62f42f5d442 --- /dev/null +++ b/src/main/java/blackjack/view/OutputView.java @@ -0,0 +1,81 @@ +package blackjack.view; + +import blackjack.card.Card; +import blackjack.game.MatchResult; +import blackjack.player.Score; +import blackjack.view.display.CardRankDisplay; +import blackjack.view.display.CardShapeDisplay; +import blackjack.view.display.PlayerResultDisplay; +import java.util.List; + +public class OutputView { + + private static final String NAME_SEPARATOR = ", "; + private static final String CARD_SEPARATOR = ", "; + + public void printNamesRequest() { + System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); + } + + public void printInitializeBlackJack(List names) { + System.out.println("딜러와 " + String.join(NAME_SEPARATOR, names) + "에게 2장을 나누었습니다."); + } + + public void printDealerFirstCard(Card card) { + System.out.println("딜러: " + convertCard(card)); + } + + public void printPlayerCards(String name, List cards) { + System.out.println(name + "카드: " + convertCards(cards)); + } + + public void printDrawMoreCardRequest(String name) { + System.out.println(name + "은(는) 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)"); + } + + public void printDealerDrawCard() { + System.out.println("딜러는 16이하라 한장의 카드를 더 받았습니다."); + } + + public void printDealerCardsWithScore(List cards, Score score) { + printPlayerCardsWithScore("딜러", cards, score); + } + + public void printPlayerCardsWithScore(String name, List cards, Score score) { + System.out.println(name + " 카드: " + convertCards(cards) + " - 결과: " + score.getScore() + "점"); + } + + public void printResultStart() { + System.out.println("## 최종 승패"); + } + + public void printDealerResult(int winCount, int tieCount, int loseCount) { + System.out.println("딜러: " + winCount + "승 " + tieCount + "무 " + loseCount + "패"); + } + + public void printPlayerResult(String name, MatchResult result) { + String resultDisplay = PlayerResultDisplay.getDisplayByResult(result).getDisplay(); + System.out.println(name + ": " + resultDisplay); + } + + public void printNewLine() { + System.out.println(); + } + + private String convertCard(Card card) { + String convertedRank = CardRankDisplay.getDisplayByRank(card.getRank()).getDisplay(); + String convertedShape = CardShapeDisplay.getDisplayByShape(card.getShape()).getDisplay(); + return convertedRank + convertedShape; + } + + private String convertCards(List cards) { + List convertedCards = cards.stream() + .map(this::convertCard) + .toList(); + return String.join(CARD_SEPARATOR, convertedCards); + } + + public void printExceptionMessage(Exception e) { + System.out.println(e.getMessage()); + } +} diff --git a/src/main/java/blackjack/view/display/CardRankDisplay.java b/src/main/java/blackjack/view/display/CardRankDisplay.java new file mode 100644 index 00000000000..e23a8c0638e --- /dev/null +++ b/src/main/java/blackjack/view/display/CardRankDisplay.java @@ -0,0 +1,40 @@ +package blackjack.view.display; + +import blackjack.card.Rank; +import java.util.Arrays; + +public enum CardRankDisplay { + + ACE(Rank.ACE, "A"), + TWO(Rank.TWO, "2"), + THREE(Rank.THREE, "3"), + FOUR(Rank.FOUR, "4"), + FIVE(Rank.FIVE, "5"), + SIX(Rank.SIX, "6"), + SEVEN(Rank.SEVEN, "7"), + EIGHT(Rank.EIGHT, "8"), + NINE(Rank.NINE, "9"), + TEN(Rank.TEN, "10"), + JACK(Rank.JACK, "J"), + QUEEN(Rank.QUEEN, "Q"), + KING(Rank.KING, "K"); + + private final Rank rank; + private final String display; + + CardRankDisplay(Rank rank, String display) { + this.rank = rank; + this.display = display; + } + + public static CardRankDisplay getDisplayByRank(Rank rank) { + return Arrays.stream(CardRankDisplay.values()) + .filter(displayNumber -> displayNumber.rank == rank) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("[ERROR] 존재하지 않는 수입니다.")); + } + + public String getDisplay() { + return display; + } +} diff --git a/src/main/java/blackjack/view/display/CardShapeDisplay.java b/src/main/java/blackjack/view/display/CardShapeDisplay.java new file mode 100644 index 00000000000..91226a42230 --- /dev/null +++ b/src/main/java/blackjack/view/display/CardShapeDisplay.java @@ -0,0 +1,31 @@ +package blackjack.view.display; + +import blackjack.card.Shape; +import java.util.Arrays; + +public enum CardShapeDisplay { + + HEART(Shape.HEART, "하트"), + SPADE(Shape.SPADE, "스페이드"), + CLOVER(Shape.CLOVER, "클로버"), + DIAMOND(Shape.DIAMOND, "다이아몬드"); + + private final Shape shape; + private final String display; + + CardShapeDisplay(Shape shape, String display) { + this.shape = shape; + this.display = display; + } + + public static CardShapeDisplay getDisplayByShape(Shape shape) { + return Arrays.stream(CardShapeDisplay.values()) + .filter(displayShape -> displayShape.shape == shape) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("[ERROR] 존재하지 않는 모양입니다.")); + } + + public String getDisplay() { + return display; + } +} diff --git a/src/main/java/blackjack/view/display/PlayerResultDisplay.java b/src/main/java/blackjack/view/display/PlayerResultDisplay.java new file mode 100644 index 00000000000..ba320eb1620 --- /dev/null +++ b/src/main/java/blackjack/view/display/PlayerResultDisplay.java @@ -0,0 +1,29 @@ +package blackjack.view.display; + +import blackjack.game.MatchResult; +import java.util.Arrays; + +public enum PlayerResultDisplay { + PLAYER_WIN(MatchResult.PLAYER_WIN, "승"), + PLAYER_LOSE(MatchResult.DEALER_WIN, "패"), + TIE(MatchResult.TIE, "무"); + + private final MatchResult result; + private final String display; + + PlayerResultDisplay(MatchResult result, String display) { + this.result = result; + this.display = display; + } + + public static PlayerResultDisplay getDisplayByResult(MatchResult result) { + return Arrays.stream(values()) + .filter(displayResult -> displayResult.result == result) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("[ERROR] 존재하지 않는 결과입니다.")); + } + + public String getDisplay() { + return display; + } +} diff --git a/src/test/java/.gitkeep b/src/test/java/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/test/java/blackjack/card/CardTest.java b/src/test/java/blackjack/card/CardTest.java new file mode 100644 index 00000000000..eca88ade625 --- /dev/null +++ b/src/test/java/blackjack/card/CardTest.java @@ -0,0 +1,30 @@ +package blackjack.card; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import blackjack.player.Score; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CardTest { + + @Test + @DisplayName("카드의 점수를 정확하게 계산한다.") + void calculateCardTest() { + Card card = new Card(Shape.SPADE, Rank.ACE); + assertThat(card.getScore()).isEqualTo(new Score(1)); + } + + @Test + @DisplayName("카드가 에이스인지 확인한다.") + void isAceTest() { + Card aceCard = new Card(Shape.HEART, Rank.ACE); + Card nonAceCard = new Card(Shape.CLOVER, Rank.JACK); + + assertAll( + () -> assertThat(aceCard.isAce()).isTrue(), + () -> assertThat(nonAceCard.isAce()).isFalse() + ); + } +} diff --git a/src/test/java/blackjack/card/DeckTest.java b/src/test/java/blackjack/card/DeckTest.java new file mode 100644 index 00000000000..0ff6dab5911 --- /dev/null +++ b/src/test/java/blackjack/card/DeckTest.java @@ -0,0 +1,51 @@ +package blackjack.card; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class DeckTest { + + @Test + @DisplayName("덱에서 카드를 뽑는다.") + void createDeckTest() { + // given + List cards = List.of( + new Card(Shape.HEART, Rank.ACE), + new Card(Shape.CLOVER, Rank.EIGHT), + new Card(Shape.DIAMOND, Rank.JACK) + ); + Deck deck = new Deck(cards); + Card expected = new Card(Shape.HEART, Rank.ACE); + // when, then + assertThat(deck.draw()).isEqualTo(expected); + } + + @Test + @DisplayName("덱에 카드가 없을 때 뽑는다면 예외를 발생시킨다.") + void emptyDeckDrawTest() { + // given + Deck deck = new Deck(List.of()); + // when, then + assertThatThrownBy(deck::draw) + .isInstanceOf(IllegalStateException.class) + .hasMessage("[ERROR] 덱이 비어있습니다."); + } + + @Test + @DisplayName("원하는 모양의 카드 전체가 생성된다.") + void createNumberCardsOfShapeTest() { + assertThat(Deck.createNumberCardsOf(Shape.CLOVER).size()).isEqualTo(13); + } + + @Test + @DisplayName("카드 전체가 생성된다.") + void createFullDeckTest() { + Deck deck = Deck.createShuffledFullDeck(); + + assertThat(deck.size()).isEqualTo(52); + } +} diff --git a/src/test/java/blackjack/game/CommandTest.java b/src/test/java/blackjack/game/CommandTest.java new file mode 100644 index 00000000000..6924de65f14 --- /dev/null +++ b/src/test/java/blackjack/game/CommandTest.java @@ -0,0 +1,32 @@ +package blackjack.game; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CommandTest { + + @Test + @DisplayName("명령어를 올바르게 변환한다.") + void convertCommandTest() { + // when + Command yes = Command.from("y"); + Command no = Command.from("n"); + // then + assertAll( + () -> assertThat(yes).isEqualTo(Command.YES), + () -> assertThat(no).isEqualTo(Command.NO) + ); + } + + @Test + @DisplayName("존재하지 않는 명령어가 주어지면 예외를 발생시킨다.") + void commandNotFoundTest() { + assertThatThrownBy(() -> Command.from("hi")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 존재하지 않는 명령어입니다."); + } +} diff --git a/src/test/java/blackjack/game/MatchResultTest.java b/src/test/java/blackjack/game/MatchResultTest.java new file mode 100644 index 00000000000..b5236a1a4c2 --- /dev/null +++ b/src/test/java/blackjack/game/MatchResultTest.java @@ -0,0 +1,41 @@ +package blackjack.game; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.player.Score; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class MatchResultTest { + + @ParameterizedTest + @CsvSource(value = {"20, 22", "20, 19", "21, 20"}) + @DisplayName("플레이어가 이기는 경우를 올바르게 판단한다.") + void playerWinningTest(int playerScore, int dealerScore) { + // when + MatchResult result = MatchResult.chooseWinner(new Score(playerScore), new Score(dealerScore)); + // then + assertThat(result).isEqualTo(MatchResult.PLAYER_WIN); + } + + @ParameterizedTest + @CsvSource(value = {"22, 20", "22, 22", "20, 21", "14, 18"}) + @DisplayName("딜러가 이기는 경우를 올바르게 판단한다.") + void dealerWinningTest(int playerScore, int dealerScore) { + // when + MatchResult result = MatchResult.chooseWinner(new Score(playerScore), new Score(dealerScore)); + // then + assertThat(result).isEqualTo(MatchResult.DEALER_WIN); + } + + @ParameterizedTest + @CsvSource(value = {"21, 21", "20, 20"}) + @DisplayName("무승부인 경우를 올바르게 판단한다.") + void tieTest(int playerScore, int dealerScore) { + // when + MatchResult result = MatchResult.chooseWinner(new Score(playerScore), new Score(dealerScore)); + // then + assertThat(result).isEqualTo(MatchResult.TIE); + } +} diff --git a/src/test/java/blackjack/game/MatchResultsTest.java b/src/test/java/blackjack/game/MatchResultsTest.java new file mode 100644 index 00000000000..5b3c999b091 --- /dev/null +++ b/src/test/java/blackjack/game/MatchResultsTest.java @@ -0,0 +1,52 @@ +package blackjack.game; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import blackjack.player.Score; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class MatchResultsTest { + + @Test + @DisplayName("이름으로 결과를 올바르게 가져온다.") + void getResultByNameTest() { + MatchResults matchResults = new MatchResults(); + matchResults.addResult("aru", new Score(20), new Score(21)); + assertThat(matchResults.getResultByName("aru")).isEqualTo(MatchResult.DEALER_WIN); + } + + @Test + @DisplayName("이름이 존재하지 않는 경우 예외를 발생시킨다.") + void nameNotFoundTest() { + // given + MatchResults matchResults = new MatchResults(); + // when, then + assertThatThrownBy(() -> matchResults.getResultByName("pobi")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 존재하지 않는 이름입니다."); + } + + @Test + @DisplayName("원하는 결과의 개수를 구한다.") + void getDesiredResultCountTest() { + // given + MatchResults matchResults = new MatchResults(); + matchResults.addResult("aru", new Score(10), new Score(20)); + matchResults.addResult("pobi", new Score(10), new Score(20)); + matchResults.addResult("atto", new Score(10), new Score(10)); + matchResults.addResult("jazz", new Score(20), new Score(10)); + // when + int playerWinCount = matchResults.getResultCount(MatchResult.PLAYER_WIN); + int tieCount = matchResults.getResultCount(MatchResult.TIE); + int dealerWinCount = matchResults.getResultCount(MatchResult.DEALER_WIN); + // then + assertAll( + () -> assertThat(playerWinCount).isEqualTo(1), + () -> assertThat(tieCount).isEqualTo(1), + () -> assertThat(dealerWinCount).isEqualTo(2) + ); + } +} diff --git a/src/test/java/blackjack/player/DealerTest.java b/src/test/java/blackjack/player/DealerTest.java new file mode 100644 index 00000000000..d9897d57b51 --- /dev/null +++ b/src/test/java/blackjack/player/DealerTest.java @@ -0,0 +1,73 @@ +package blackjack.player; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import blackjack.card.Card; +import blackjack.card.Rank; +import blackjack.card.Shape; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class DealerTest { + + @Test + @DisplayName("딜러는 16점 이하이면 추가 드로우가 가능하다.") + void ableToDrawTest() { + // given + List cards = List.of( + new Card(Shape.HEART, Rank.JACK), + new Card(Shape.DIAMOND, Rank.SIX) + ); + Hand hand = new Hand(cards); + Dealer dealer = new Dealer(hand); + // when + boolean isDrawable = dealer.hasDrawableScore(); + // then + assertThat(isDrawable).isTrue(); + } + + @Test + @DisplayName("딜러는 16점 초과이면 추가 드로우가 불가능하다.") + void unableToDrawTest() { + // given + List cards = List.of( + new Card(Shape.HEART, Rank.JACK), + new Card(Shape.DIAMOND, Rank.SEVEN) + ); + Hand hand = new Hand(cards); + Dealer dealer = new Dealer(hand); + // when + boolean isDrawable = dealer.hasDrawableScore(); + // then + assertThat(isDrawable).isFalse(); + } + + @Test + @DisplayName("딜러의 첫 번째 카드를 가져온다.") + void getFirstCardTest() { + // given + List cards = List.of( + new Card(Shape.HEART, Rank.JACK), + new Card(Shape.DIAMOND, Rank.SEVEN) + ); + Hand hand = new Hand(cards); + Dealer dealer = new Dealer(hand); + // when, then + assertThat(dealer.getFirstCard()).isEqualTo(new Card(Shape.HEART, Rank.JACK)); + } + + @Test + @DisplayName("딜러가 카드를 가지고 있지 않을 때 가져가는 것을 시도하면 예외를 발생시킨다.") + void getFirstCardOnEmptyHandTest() { + // given + List cards = List.of(); + Hand hand = new Hand(cards); + Dealer dealer = new Dealer(hand); + // when, then + assertThatThrownBy(dealer::getFirstCard) + .isInstanceOf(IllegalStateException.class) + .hasMessage("[ERROR] 딜러가 카드를 가지고 있지 않습니다."); + } +} diff --git a/src/test/java/blackjack/player/HandTest.java b/src/test/java/blackjack/player/HandTest.java new file mode 100644 index 00000000000..7e0b01b150c --- /dev/null +++ b/src/test/java/blackjack/player/HandTest.java @@ -0,0 +1,53 @@ +package blackjack.player; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.card.Card; +import blackjack.card.Rank; +import blackjack.card.Shape; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class HandTest { + + @Test + @DisplayName("플레이어의 점수를 계산한다.") + void calculateScoreTest() { + // given + List cards = List.of( + new Card(Shape.SPADE, Rank.KING), + new Card(Shape.HEART, Rank.EIGHT) + ); + Hand hand = new Hand(cards); + // when, then + assertThat(hand.calculateScore()).isEqualTo(new Score(18)); + } + + @Test + @DisplayName("플레이어의 점수를 계산할 때, Ace의 점수를 유리한 방향(11)으로 결정한다.") + void calculateAceAsElevenTest() { + // given + List cards = List.of( + new Card(Shape.HEART, Rank.TEN), + new Card(Shape.CLOVER, Rank.ACE) + ); + Hand hand = new Hand(cards); + // when, then + assertThat(hand.calculateScore()).isEqualTo(new Score(21)); + } + + @Test + @DisplayName("플레이어의 점수를 계산할 때, Ace의 점수를 유리한 방향(1)으로 결정한다.") + void calculateAceAsOneTest() { + // given + List cards = List.of( + new Card(Shape.HEART, Rank.TEN), + new Card(Shape.CLOVER, Rank.ACE), + new Card(Shape.DIAMOND, Rank.TEN) + ); + Hand hand = new Hand(cards); + // when, then + assertThat(hand.calculateScore()).isEqualTo(new Score(21)); + } +} diff --git a/src/test/java/blackjack/player/NameTest.java b/src/test/java/blackjack/player/NameTest.java new file mode 100644 index 00000000000..dc205b7b23a --- /dev/null +++ b/src/test/java/blackjack/player/NameTest.java @@ -0,0 +1,37 @@ +package blackjack.player; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +class NameTest { + + @ParameterizedTest + @ValueSource(strings = {"12", "12345"}) + @DisplayName("이름이 잘 생성된다.") + void validNameTest(String name) { + assertDoesNotThrow(() -> new Name(name)); + } + + @ParameterizedTest + @NullAndEmptySource + @DisplayName("이름이 공백으로 이루어진 경우 예외를 발생시킨다.") + void blankNameTest(String name) { + assertThatThrownBy(() -> new Name(name)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 이름은 공백일 수 없습니다."); + } + + @ParameterizedTest + @ValueSource(strings = {"a", "abcdef"}) + @DisplayName("이름이 길이 제한을 어기는 경우 예외를 발생시킨다.") + void longNameTest(String name) { + assertThatThrownBy(() -> new Name(name)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 이름은 2글자 이상 5글자 이하여야 합니다."); + } +} diff --git a/src/test/java/blackjack/player/PlayerTest.java b/src/test/java/blackjack/player/PlayerTest.java new file mode 100644 index 00000000000..140bca5001f --- /dev/null +++ b/src/test/java/blackjack/player/PlayerTest.java @@ -0,0 +1,46 @@ +package blackjack.player; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.card.Card; +import blackjack.card.Rank; +import blackjack.card.Shape; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PlayerTest { + + @Test + @DisplayName("플레이어는 21점 미만이면 추가 드로우가 가능하다.") + void ableToDrawTest() { + // given + List cards = List.of( + new Card(Shape.HEART, Rank.JACK), + new Card(Shape.DIAMOND, Rank.TEN) + ); + Hand hand = new Hand(cards); + Player player = new Player("aru", hand); + // when + boolean isDrawable = player.hasDrawableScore(); + // then + assertThat(isDrawable).isTrue(); + } + + @Test + @DisplayName("플렝이어는 21점 초과이면 추가 드로우가 불가능하다.") + void unableToDrawTest() { + // given + List cards = List.of( + new Card(Shape.HEART, Rank.JACK), + new Card(Shape.DIAMOND, Rank.TEN), + new Card(Shape.DIAMOND, Rank.TWO) + ); + Hand hand = new Hand(cards); + Player player = new Player("atto", hand); + // when + boolean isDrawable = player.hasDrawableScore(); + // then + assertThat(isDrawable).isFalse(); + } +} diff --git a/src/test/java/blackjack/player/PlayersTest.java b/src/test/java/blackjack/player/PlayersTest.java new file mode 100644 index 00000000000..92bf1268d25 --- /dev/null +++ b/src/test/java/blackjack/player/PlayersTest.java @@ -0,0 +1,51 @@ +package blackjack.player; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PlayersTest { + + @Test + @DisplayName("플레이어의 이름이 중복될 경우 예외를 발생시킨다") + void duplicateNameTest() { + // given + List names = List.of("aru", "atto", "aru"); + // when, then + assertThatThrownBy(() -> new Players(names)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 이름은 중복될 수 없습니다."); + } + + @Test + @DisplayName("플레이어의 수가 제한을 초과하는 경우 예외를 발생시킨다.") + void exceedingPlayersSizeTest() { + // given + List playerNames = List.of("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"); + // when, then + assertThatThrownBy(() -> new Players(playerNames)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 플레이어의 수는 1명 이상 10명 이하여야 합니다."); + } + + @Test + @DisplayName("플레이어가 없는 경우 예외를 발생시킨다.") + void emptyPlayersSizeTest() { + // given + List playerNames = List.of(); + // when, then + assertThatThrownBy(() -> new Players(playerNames)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 플레이어의 수는 1명 이상 10명 이하여야 합니다."); + } + + @Test + @DisplayName("플레이어로 null이 전달되는 경우 예외를 발생시킨다.") + void nullPlayerTest() { + assertThatThrownBy(() -> new Players(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 플레이어로 null이 전달되었습니다."); + } +} diff --git a/src/test/java/blackjack/player/ScoreTest.java b/src/test/java/blackjack/player/ScoreTest.java new file mode 100644 index 00000000000..c3141344aae --- /dev/null +++ b/src/test/java/blackjack/player/ScoreTest.java @@ -0,0 +1,76 @@ +package blackjack.player; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ScoreTest { + + @Test + @DisplayName("점수를 더할 수 있다.") + void addScoreTest() { + Score score = new Score(2); + + assertThat(score.add(3)).isEqualTo(new Score(5)); + } + + @Test + @DisplayName("점수가 버스트인지 판단한다.") + void isBustTest() { + Score bustScore = new Score(22); + Score notBustScore = new Score(21); + + assertAll( + () -> assertThat(bustScore.isBust()).isTrue(), + () -> assertThat(notBustScore.isBust()).isFalse() + ); + } + + @Test + @DisplayName("점수가 버스트인지 판단한다.") + void isNotBustTest() { + Score bustScore = new Score(22); + Score notBustScore = new Score(21); + + assertAll( + () -> assertThat(bustScore.isNotBust()).isFalse(), + () -> assertThat(notBustScore.isNotBust()).isTrue() + ); + } + + @Test + @DisplayName("점수의 대소비교가 가능하다.") + void isLargerThanTest() { + Score score = new Score(15); + + assertAll( + () -> assertThat(score.isLargerThan(new Score(14))).isTrue(), + () -> assertThat(score.isLargerThan(new Score(16))).isFalse() + ); + } + + @Test + @DisplayName("점수의 대소비교가 가능하다.") + void isSmallerThanEqualTest() { + Score score = new Score(15); + + assertAll( + () -> assertThat(score.isSmallerThanOrEqualTo(new Score(14))).isFalse(), + () -> assertThat(score.isSmallerThanOrEqualTo(new Score(16))).isTrue() + ); + } + + @Test + @DisplayName("보정된 에이스 점수를 계산한다.") + void changeToLargeAceTest() { + Score canChangeScore = new Score(11); + Score cantChangeScore = new Score(12); + + assertAll( + () -> assertThat(canChangeScore.changeToLargeAceScore()).isEqualTo(new Score(21)), + () -> assertThat(cantChangeScore.changeToLargeAceScore()).isEqualTo(new Score(12)) + ); + } +}