diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 1c4e3cef883..71bbfc612fb 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -12,24 +12,6 @@ - [ ] 미션의 필수 요구사항을 모두 구현했나요? - [ ] Gradle `test`를 실행했을 때, 모든 테스트가 정상적으로 통과했나요? - [ ] 애플리케이션이 정상적으로 실행되나요? -- [ ] [프롤로그](https://prolog.techcourse.co.kr)에 셀프 체크를 작성했나요? - - - - -## 객체지향 생활체조 요구사항을 얼마나 잘 충족했다고 생각하시나요? - -### 1~5점 중에서 선택해주세요. - -- [ ] 1 (전혀 충족하지 못함) -- [ ] 2 -- [ ] 3 (보통) -- [ ] 4 -- [ ] 5 (완벽하게 충족) - -### 선택한 점수의 이유를 적어주세요. - - - ## 어떤 부분에 집중하여 리뷰해야 할까요? diff --git a/README.md b/README.md index cc4bddd883c..131c0dc615e 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,93 @@ + + ## 미션 중 기록 +### Step1 - 규칙을 적용해서 변경한 코드 1곳 이상 - Player 클래스에 있는 addCard() 메서드를 기능이 아닌 내부 로직으로만 사용해서, 테스트코드 작성을 하지 않았는데, 기능으로 바라보게 되면서 해당 관련 코드와 테스트코드를 모두 수정함 - 테스트 작성이 어려웠던 코드 1곳 이상 - 16 이하이면 딜러가 계속 카드를 받는 상황에서, 완결된 상태의 딜러의 손패를 적용해서 계산하고 싶은데, 입출력 로직과 섞여서 테스트를 하기 어려웠음. - GameManagerTest로 승패 계산하는 로직을 테스트 하려고 했으나, GameManager가 알아야 하는 given이 너무 많아서 작성이 어려웠음. - 막힌 순간 1회 이상 - - 카드 생성 기능 부분에 대한 테스트 작성 시, 카드의 숫자는 A~10, J, Q, K여야 한다는 도메인 규칙까지 테스트를 해야하는지 의문? → Enum으로 정의하면, 당연히 동작해야 될 내용에 대해서는 테스트코드가 필요하지 않다는 생각이 들었음 - + - 카드 생성 기능 부분에 대한 테스트 작성 시, 카드의 숫자는 A~10, J, Q, K여야 한다는 도메인 규칙까지 테스트를 해야하는지 의문이었다. Enum으로 정의하면, 당연히 동작해야 될 내용에 대해서는 테스트코드가 필요하지 않다는 생각이 들었었다. + +### Step2 +- 기능 추가로 인해 수정한 위치 개수 + - InputView, OutputView + - GameController + - GameService + - Player + - Profit + - Money + +- 사이클1 때보다 수정 범위가 줄었는가/늘었는가 + - 코드를 생성했던 양보다는, 수정을 할 범위가 그렇게 많지 않았습니다. 대부분의 도메인은 기존의 코드를 그대로 따르고 있고, 입출력과, 결과 계산을 하는 부분, 그리고 이를 행하는 주체 정도만 수정을 하면 되었습니다. + - 상태 패턴을 통해 if문을 줄이고 다형성을 활용해 보고 싶어, 코드 흐름 및 점수 로직을 바꾸긴 했습니다. 하지만, step1에서 step2로 기능을 변경을 할 때는 바뀐 비즈니스 규칙 정도만 변경되는 것을 확인할 수 있었습니다. +- 규칙 적용으로 변경한 코드 1곳 + - 객체의 책임 할당으로 인해 변경되었습니다. 수익은 자신이 돈과, 수익률을 잘 알고 있으므로 수익 금액을 계산할 수 있습니다. 이를 원래 밖에서 처리를 해서 dto로 바로 넘겼는데, Profit에서 계산하도록 하였습니다. +- 테스트가 설계를 도운 순간 1회 + - 수익률 계산에서 좀 더 헤매지 않고, 명확히 구분을 해서 진행해볼 수 있었습니다. --- ## 토론에서 정한 규칙 -1. 테스트 단위 기준 - 무엇을 단위로 테스트하는가 - - 만약 테스트를 작성한다면 기능 단위로 작성한다. 이 때 기능은 한 가지 역할만을 담당한다. - -2. 테스트 제외 기준 - 무엇을 테스트하지 않는가 - - 인풋&아웃풋과 테스트 가능한 public 메서드에서 활용되는 private메서드는 테스트하지 않아도 된다. - - 만약 테스트코드를 통해 private 메서드가 검증되지 않는다면 해당 메서드는 기능을 재정의 한다. - -3. 테스트 어려움 대응 - 테스트가 어려우면 어떻게 하는가 - - 테스트가 어려울 경우 도메인 설계를 점검하고 메서드가 단일 책임인지 다시 고려해본다. - -4. 리팩터링 우선순위 - 여러 문제가 있을 때 뭘 먼저 하는가 - - 아래의 우선순위대로 리팩토링을 진행한다.
- (1) 역할 분리
- (2) 조건문 분기 축소
- (3) 중복 로직 제거
- (4) 메서드/변수명(테스트 포함) 점검
- (5) 상수화 - +### 테스트 단위 기준 - 무엇을 단위로 테스트하는가 +만약 테스트를 작성한다면 기능 단위로 작성한다. 이 때 기능은 한 가지 역할만을 담당한다. + +### 테스트 제외 기준 - 무엇을 테스트하지 않는가 +인풋&아웃풋과 테스트 가능한 public 메서드에서 활용되는 private 메서드는 테스트하지 않아도 된다. +만약 테스트코드를 통해 private 메서드가 검증되지 않는다면 해당 메서드는 기능을 재정의 한다. + +### 테스트 어려움 대응 - 테스트가 어려우면 어떻게 하는가 +테스트가 어려울 경우 도메인 설계를 점검하고 메서드가 단일 책임인지 다시 고려해본다. + +### 리팩터링 우선순위 - 여러 문제가 있을 때 뭘 먼저 하는가 +아래의 우선순위대로 리팩토링을 진행한다. +- 역할 분리 +- 조건문 분기 축소 +- 중복 로직 제거 +- 메서드/변수명(테스트 포함) 점검 +- 상수화 + +### 테스트의 범위와 대상 - 무엇을 핵심으로 검증하는가 +- 테스트는 개별 메서드가 아니라 객체의 행위와 협력 객체 간 메시지 흐름을 기준으로 정의한다. 즉, "어떤 결과를 만든다"보다 "어떤 책임을 수행한다"를 검증한다. +- 랜덤 값, 시간 등 제어할 수 없는 요소는 테스트에서 통제 가능한 형태로 분리한다. + +### 객체의 책임 할당 - 누구에게 역할을 맡길 것인가 +- 객체는 자신의 상태를 기반으로 스스로 행동하도록 책임을 가진다. 데이터를 꺼내 판단하지 말고 "묻지 말고 시켜라(Tell, Don't Ask)" 원칙을 따른다. 즉, 행동은 해당 정보를 가장 잘 아는 객체가 수행한다. +- 객체가 올바른 책임을 가졌는지 어떻게 아는가 + - 클래스 상단의 import 문을 통해 책임 침범 여부를 점검한다. + - 도메인 객체가 UI, 입출력, 프레임워크 의존성을 가지면 책임이 섞인 신호로 판단한다. + - 일급 컬렉션이 담당해야 할 로직이 외부 서비스나 컨트롤러에 존재하지 않는지 확인한다. + - 도메인은 항상 도메인 규칙과 상태 관리에만 집중하도록 유지한다. + +### 테스트의 실패 피드백 - 깨진 테스트 코드는 무엇을 의미하는가 +- 비즈니스 규칙이 변경되어 테스트가 깨지는 경우는 건강한 실패이다. 이는 시스템의 규칙이 바뀌었음을 알리는 유효한 피드백이므로 테스트를 함께 수정한다. +- 내부 구조 리팩터링만 했는데 테스트가 깨지면 경고성 실패로 본다. 이 경우 테스트가 구현 세부사항에 과도하게 의존하고 있는지 점검한다. + +### 변경에 유연한 구조 - 어떻게 설계해야 사이드 이펙트를 줄일 수 있는가 +- 의미 있는 값은 원시값 포장(Value Object)과 일급컬렉션을 통해 명확한 개념으로 표현한다. VO는 관련 규칙과 검증을 함께 가지며 응집도를 높이기 때문이다. +- 객체는 상태와 행동을 함께 숨기고 외부에서 직접 상태를 변경할 수 없도록 캡슐화한다. 이를 통해 객체 간 의존을 줄이고 변경 시 발생하는 사이드 이펙트를 최소화한다. +- 일급컬렉션을 사용하여 외부에서 컬렉션을 임의로 조작하지 못하도록 막고 불변성을 보장하여, 시스템 전반의 안정성을 높인다. + --- +## 기능 시나리오 +1. 딜러와 플레이어를 생성한다. + 1. 플레이어의 이름을 입력받는다. +2. 플레이어는 금액을 베팅한다. +3. 플레이어에게 카드를 2장씩 나누어준다. 이때 딜러도 받아야한다. +4. 딜러가 블랙잭인 경우 그 즉시 플레이를 멈추고 점수를 계산한다. +5. 딜러가 블랙잭이 아닌 경우 정상 흐름으로 게임이 진행된다. +6. 각 플레이어는 버스트가 되지 않는 한 계속 뽑을 수 있다. + 1. 이때 히트 옵션을 받는 작업을 계속 수행해야하며, 손패의 점수를 출력한다. +7. 딜러는 16이하인 경우 계속 히트한다. +8. 결과 값을 출력한다. + 1. 딜러의 수익을 계산한다. + 2. 플레이어의 수익을 계산한다. +--- ## 구현할 기능 목록 ### 기본 입출력 -- [x] 게임에 참여할 이름 입력받는다. +- [x] 게임에 참여할 이름을 입력받는다. +- [x] 플레이어 당 베팅 금액을 입력받는다. - [x] 한 장의 카드를 추가로 받을지 여부를 입력받는다. - [x] 플레이어의 현재 보유 카드를 출력한다. - [x] 게임의 진행상황을 출력한다. @@ -48,6 +103,7 @@ - [x] 카드를 받는다. - [x] 초기에 카드 2장을 받는다. - [x] 버스트가 되지 않은 경우 원한다면 얼마든지 카드를 계속 뽑을 수 있다. +- [x] 베팅을 한다. ### 딜러 - [x] 카드를 받는다. @@ -58,15 +114,19 @@ - [x] 카드의 숫자 계산은 카드 숫자를 기본으로 한다. - [x] King, Queen, Jack은 각각 10으로 계산한다. - [x] 예외로 Ace는 1 또는 11을 유리한대로 계산한다. - - [x] Ace를 11로 계산하고, 카드의 합이 21을 초과하면 10을 뺀다. + - [x] Ace를 11로 계산하고, 카드의 합이 21을 초과하면 10을 뺀다. -### 버스트를 판정한다. +### 핸드를 판정한다. +- [x] 카드의 합계가 21이고 카드의 장수가 2장이면 블랙잭이다. - [x] 카드의 합계가 21을 초과하면 버스트다. ### 승패를 계산한다. - [x] 게임을 완료한 후 각 플레이어별로 총 점수를 출력한다. - - [x] 버스트일 경우, 버스트로 출력한다. -- [x] 게임을 완료한 후 각 플레이어별로 승패를 출력한다. + +### 베팅 결과를 계산한다. +- [x] 게임을 완료한 후 각 플레이어별로 결과 별 수익을 출력하여야한다. + - [x] 카드를 추가로 뽑아 21을 초과할 경우 배팅 금액을 모두 잃게 된다. +- [x] 딜러의 수익을 계산한다. --- @@ -75,8 +135,14 @@ 게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리) pobi,jason +pobi의 배팅 금액은? +10000 + +jason의 배팅 금액은? +20000 + 딜러와 pobi, jason에게 2장을 나누었습니다. -딜러카드: 3다이아몬드 +딜러: 3다이아몬드 pobi카드: 2하트, 8스페이드 jason카드: 7클로버, K스페이드 @@ -85,26 +151,26 @@ y pobi카드: 2하트, 8스페이드, A클로버 pobi는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) n -jason는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) +pobi카드: 2하트, 8스페이드, A클로버 +jason은 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n) n jason카드: 7클로버, K스페이드 딜러는 16이하라 한장의 카드를 더 받았습니다. -딜러카드: 3다이아몬드, 9클로버, 8다이아몬드 - 결과: 20 +딜러 카드: 3다이아몬드, 9클로버, 8다이아몬드 - 결과: 20 pobi카드: 2하트, 8스페이드, A클로버 - 결과: 21 jason카드: 7클로버, K스페이드 - 결과: 17 - -## 최종 승패 -딜러: 1승 1패 -pobi: 승 -jason: 패 +## 최종 수익 +딜러: 10000 +pobi: 10000 +jason: -20000 ``` ## **프로그래밍 요구 사항** - [x] 자바 코드 컨벤션을 지키면서 프로그래밍한다. - - [ ] 기본적으로 [Java Style Guide](https://github.com/woowacourse/woowacourse-docs/tree/master/styleguide/java)을 원칙으로 한다. + - [x] 기본적으로 [Java Style Guide](https://github.com/woowacourse/woowacourse-docs/tree/master/styleguide/java)을 원칙으로 한다. - [ ] indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다. - [ ] 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. - [ ] 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다. @@ -113,14 +179,14 @@ jason: 패 - [x] else 예약어를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다. - [x] 힌트: if문에서 값을 반환하는 방식으로 구현하면 else 예약어를 사용하지 않아도 된다. - [x] 모든 기능을 TDD로 구현해 단위 테스트가 존재해야 한다. 단, UI(System.out, System.in) 로직은 제외 - - [ ] 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다. + - [x] 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다. - [x] UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다. - [ ] 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다. - [ ] 함수(또는 메소드)가 한 가지 일만 하도록 최대한 작게 만들어라. - [x] 배열 대신 컬렉션을 사용한다. - [ ] 모든 원시 값과 문자열을 포장한다. - [x] 줄여 쓰지 않는다(축약 금지). -- [ ] 일급 컬렉션을 쓴다. +- [x] 일급 컬렉션을 쓴다. ### **추가된 요구 사항** - [x] 모든 엔티티를 작게 유지한다. diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index d36c0ee1209..3be26f61a54 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -1,10 +1,14 @@ package controller; -import domain.Players; +import domain.participant.Players; +import domain.betting.Money; +import domain.participant.Dealer; import domain.participant.Player; +import dto.PlayerHandDto; +import service.BettingCalculateService; import service.GameService; import util.HitOption; -import util.InputHitOptionParser; +import util.InputBettingParser; import view.InputView; import view.OutputView; @@ -19,13 +23,20 @@ public GameController(InputView inputView, OutputView outputView) { public void run() { Players players = inputPlayers(); - GameService gameService = new GameService(players); + Dealer dealer = new Dealer(); + GameService gameService = new GameService(players, dealer); + BettingCalculateService bettingCalculateService = new BettingCalculateService(players, dealer); + + playerBetting(players); outputView.printStartGame(gameService.startGame()); - processGame(gameService); + boolean dealerIsBlackJack = processDealerBlackJack(gameService); + if (!dealerIsBlackJack) { + processGame(gameService); + } outputView.printScore(gameService.getTotalScore()); - outputView.printResults(gameService.calculateResults()); + outputView.printBettingResults(bettingCalculateService.getBettingResult()); } private Players inputPlayers() { @@ -34,7 +45,7 @@ private Players inputPlayers() { String rawPlayerNames = inputView.readPlayerNames(); return Players.fromString(rawPlayerNames); } catch (IllegalArgumentException exception) { - outputView.printErrorMessage(exception.getMessage()); + outputView.printInputErrorMessage(exception.getMessage()); } } } @@ -43,9 +54,20 @@ private HitOption inputHitOption(Player player) { while (true) { try { String rawHitOption = inputView.readHitOption(player.getName()); - return InputHitOptionParser.parseHitOption(rawHitOption); + return HitOption.of(rawHitOption); + } catch (IllegalArgumentException exception) { + outputView.printInputErrorMessage(exception.getMessage()); + } + } + } + + private Money inputBettingMoney(Player player) { + while (true) { + try { + String rawBettingMoney = inputView.readBetting(player.getName()); + return InputBettingParser.parseBettingMoney(rawBettingMoney); } catch (IllegalArgumentException exception) { - outputView.printErrorMessage(exception.getMessage()); + outputView.printInputErrorMessage(exception.getMessage()); } } } @@ -57,19 +79,44 @@ private void processGame(GameService gameService) { dealerTurn(gameService); } + private boolean processDealerBlackJack(GameService gameService) { + Dealer dealer = gameService.getDealer(); + if (dealer.isBlackJack()) { + gameService.endGameImmediately(); + outputView.printDealerBlackJack(); + return true; + } + return false; + } + private void playerTurn(Player player, GameService gameService) { - while (!player.isBust() && inputHitOption(player) == HitOption.YES) { - outputView.printHandCard(gameService.playerHit(player)); + while (player.isRunning() && inputHitOption(player) == HitOption.YES) { + gameService.hit(player); + outputView.printHandCard(PlayerHandDto.from(player)); } - if (!player.isBust()) { - outputView.printHandCard(gameService.getCurrentHand(player)); + + if (player.isRunning()) { + gameService.stay(player); + outputView.printHandCard(PlayerHandDto.from(player)); } } private void dealerTurn(GameService gameService) { - while (gameService.getDealer().isReceiveCard()) { - gameService.dealerHit(); + Dealer dealer = gameService.getDealer(); + + while (dealer.isReceiveCard()) { + gameService.hit(dealer); outputView.printDealerReceiveCard(); } + + if (dealer.isRunning()) { + gameService.stay(dealer); + } + } + + private void playerBetting(Players players) { + for (Player player : players) { + player.bettingMoney(inputBettingMoney(player)); + } } } diff --git a/src/main/java/domain/BlackJackInfo.java b/src/main/java/domain/BlackJackInfo.java new file mode 100644 index 00000000000..611cdd7eadf --- /dev/null +++ b/src/main/java/domain/BlackJackInfo.java @@ -0,0 +1,8 @@ +package domain; + +public final class BlackJackInfo { + public static final int BLACKJACK_SCORE = 21; + public static final int SOFT_HAND_PROCESS_SCORE = 10; + public static final int DEALER_THRESHOLD_SCORE = 16; + public static final int FIRST_CARD_COUNT = 2; +} diff --git a/src/main/java/domain/PlayerGameResult.java b/src/main/java/domain/PlayerGameResult.java deleted file mode 100644 index bd2a3f42607..00000000000 --- a/src/main/java/domain/PlayerGameResult.java +++ /dev/null @@ -1,45 +0,0 @@ -package domain; - -import domain.participant.Dealer; -import domain.participant.Player; - -public enum PlayerGameResult { - WIN("승"), DRAW("무"), LOSE("패"); - - private final String value; - - PlayerGameResult(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - public static PlayerGameResult from(Player player, Dealer dealer) { - if (player.isBust() || dealer.isBust()) { - return bustResult(player.isBust(), dealer.isBust()); - } - return compareScore(player.calculateScore(), dealer.calculateScore()); - } - - private static PlayerGameResult bustResult(boolean playerIsBust, boolean dealerIsBust) { - if (playerIsBust && dealerIsBust) { - return DRAW; - } - if (dealerIsBust) { - return WIN; - } - return LOSE; - } - - private static PlayerGameResult compareScore(int playerScore, int dealerScore) { - if (playerScore > dealerScore) { - return WIN; - } - if (playerScore == dealerScore) { - return DRAW; - } - return LOSE; - } -} diff --git a/src/main/java/domain/Players.java b/src/main/java/domain/Players.java deleted file mode 100644 index cb4b70ac826..00000000000 --- a/src/main/java/domain/Players.java +++ /dev/null @@ -1,45 +0,0 @@ -package domain; - -import domain.card.Card; -import domain.card.Deck; -import domain.participant.Player; -import util.InputNameParser; -import util.InputNameValidator; - -import java.util.Iterator; -import java.util.List; - -public class Players implements Iterable{ - - private final List players; - - private Players(List players) { - this.players = players; - } - - public static Players fromString(String rawPlayerNames) { - List playerNames = InputNameParser.parsePlayerNames(rawPlayerNames); - InputNameValidator.validateInputNames(playerNames); - - List players = playerNames.stream().map(Player::new).toList(); - return new Players(players); - } - - public List getPlayerNames() { - return players.stream() - .map(Player::getName) - .toList(); - } - - public void receiveInitialCards(Deck deck) { - for (Player player : players) { - List firstHandCards = deck.dealFirstHandCards(); - player.receiveInitialCards(firstHandCards); - } - } - - @Override - public Iterator iterator() { - return players.iterator(); - } -} diff --git a/src/main/java/domain/betting/BettingResult.java b/src/main/java/domain/betting/BettingResult.java new file mode 100644 index 00000000000..cce548a08a1 --- /dev/null +++ b/src/main/java/domain/betting/BettingResult.java @@ -0,0 +1,54 @@ +package domain.betting; + +import domain.participant.Dealer; +import domain.participant.Player; + +public enum BettingResult { + WIN_WITH_BLACKJACK(150), + WIN(100), + DRAW(0), + LOSE(-100); + + private final int earningRate; + + BettingResult(int earningRate) { + this.earningRate = earningRate; + } + + public static BettingResult judge(Player player, Dealer dealer) { + if (player.isBust()) { + return LOSE; + } + if (player.isBlackJack()) { + return judgeBlackJack(dealer); + } + if (dealer.isBust()) { + return WIN; + } + return judgeScore(player, dealer); + } + + private static BettingResult judgeBlackJack(Dealer dealer) { + if (dealer.isBlackJack()) { + return DRAW; + } + return WIN_WITH_BLACKJACK; + } + + private static BettingResult judgeScore(Player player, Dealer dealer) { + int playerScore = player.getScore(); + int dealerScore = dealer.getScore(); + + if (playerScore > dealerScore) { + return WIN; + } + if (playerScore == dealerScore) { + return DRAW; + } + return LOSE; + } + + public int getEarningRate() { + return earningRate; + } +} diff --git a/src/main/java/domain/betting/Money.java b/src/main/java/domain/betting/Money.java new file mode 100644 index 00000000000..692cc5a00b9 --- /dev/null +++ b/src/main/java/domain/betting/Money.java @@ -0,0 +1,47 @@ +package domain.betting; + +import java.util.Objects; + +public class Money { + private final long value; + private final int BETTING_UNIT = 1000; + private final long MAX_BETTING_MONEY = 100_000_000L; + + public Money(long value) { + validateMoney(value); + this.value = value; + } + + public long getValue() { + return value; + } + + private void validateMoney(long value) { + if (value <= 0) { + throw new IllegalArgumentException("베팅 금액은 양수입니다."); + } + if (value % BETTING_UNIT != 0) { + throw new IllegalArgumentException(String.format("베팅 금액의 단위는 %s원 입니다.", BETTING_UNIT)); + } + if (value > MAX_BETTING_MONEY) { + throw new IllegalArgumentException(String.format("최대 베팅 액수는 %s원 입니다.", MAX_BETTING_MONEY)); + } + } + + public Money sum(Money other) { + return new Money(this.value + other.value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Money money = (Money) o; + return value == money.value; + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } +} diff --git a/src/main/java/domain/betting/Profit.java b/src/main/java/domain/betting/Profit.java new file mode 100644 index 00000000000..d771bcb8aca --- /dev/null +++ b/src/main/java/domain/betting/Profit.java @@ -0,0 +1,28 @@ +package domain.betting; + +public class Profit { + private final String name; + private final Money money; + private final BettingResult bettingResult; + + public Profit(String name, Money money, BettingResult bettingResult) { + this.name = name; + this.money = money; + this.bettingResult = bettingResult; + } + + public String getName() { + return name; + } + + public long calculateProfit() { + long profit = bettingResult.getEarningRate() * money.getValue() / 100; + + long mod = bettingResult.getEarningRate() * money.getValue() % 100; + if (mod >= 50) { + profit += 1; + } + + return profit; + } +} diff --git a/src/main/java/domain/card/Deck.java b/src/main/java/domain/card/Deck.java index 5e3d5bcf1cf..c3a2b929ff8 100644 --- a/src/main/java/domain/card/Deck.java +++ b/src/main/java/domain/card/Deck.java @@ -1,6 +1,5 @@ package domain.card; - import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -26,7 +25,7 @@ private Deque initialize() { return new ArrayDeque<>(cards); } - public List dealFirstHandCards() { + public List drawInitialCards() { List firstHandCards = new ArrayList<>(); for (int index = 0; index < 2; index++) { firstHandCards.add(drawCard()); diff --git a/src/main/java/domain/participant/Dealer.java b/src/main/java/domain/participant/Dealer.java index b9b67cac512..042f91d06d8 100644 --- a/src/main/java/domain/participant/Dealer.java +++ b/src/main/java/domain/participant/Dealer.java @@ -1,21 +1,17 @@ package domain.participant; +import domain.BlackJackInfo; import domain.card.Card; -public class Dealer extends Participant { +public final class Dealer extends Participant { public Dealer() { - super("딜러"); - } - - public Dealer(HandCards handCards) { - super("딜러", handCards); } public Card getFirstCard() { - return getHandCards().getFirst(); + return status.getFirstCard(); } public boolean isReceiveCard() { - return calculateScore() <= 16; + return getScore() <= BlackJackInfo.DEALER_THRESHOLD_SCORE; } } diff --git a/src/main/java/domain/participant/HandCards.java b/src/main/java/domain/participant/HandCards.java index e0d445351da..67ceb26450c 100644 --- a/src/main/java/domain/participant/HandCards.java +++ b/src/main/java/domain/participant/HandCards.java @@ -1,5 +1,6 @@ package domain.participant; +import domain.BlackJackInfo; import domain.card.Card; import domain.card.CardNumber; @@ -7,29 +8,20 @@ import java.util.ArrayList; public class HandCards { - List cards; + private final List cards; public HandCards() { this.cards = new ArrayList<>(); } - public HandCards(List cards) { - this.cards = cards; - } - public void receiveInitialCards(List firstHandCards) { cards.addAll(firstHandCards); - cards = firstHandCards; } public void receiveHitCard(Card card) { cards.add(card); } - public boolean isBust() { - return calculateScore() > 21; - } - public int calculateScore() { int baseCardScore = cards.stream() .map(Card::getBaseScore) @@ -42,8 +34,8 @@ private int processAceCard(int baseCardScore) { int score = baseCardScore; boolean isAceExist = cards.stream() .anyMatch(handCard -> handCard.getCardNumber() == CardNumber.ACE); - if (isAceExist && (baseCardScore + 10) <= 21) { - score += 10; + if (isAceExist && (baseCardScore + BlackJackInfo.SOFT_HAND_PROCESS_SCORE) <= BlackJackInfo.BLACKJACK_SCORE) { + score += BlackJackInfo.SOFT_HAND_PROCESS_SCORE; } return score; } @@ -51,4 +43,16 @@ private int processAceCard(int baseCardScore) { public List getCards() { return List.copyOf(cards); } + + public boolean isBust() { + return calculateScore() > BlackJackInfo.BLACKJACK_SCORE; + } + + public boolean isBlackJack() { + return cards.size() == BlackJackInfo.FIRST_CARD_COUNT && calculateScore() == BlackJackInfo.BLACKJACK_SCORE; + } + + public Card getFirst() { + return cards.getFirst(); + } } diff --git a/src/main/java/domain/participant/Name.java b/src/main/java/domain/participant/Name.java new file mode 100644 index 00000000000..a526bb2df2d --- /dev/null +++ b/src/main/java/domain/participant/Name.java @@ -0,0 +1,36 @@ +package domain.participant; + +import java.util.Objects; + +public class Name { + private final String value; + + public Name(String value) { + value = value.strip(); + validateEmptyNames(value); + this.value = value; + } + + private static void validateEmptyNames(String playerName) { + if (playerName.isBlank()) { + throw new IllegalArgumentException("플레이어의 이름은 공백이 될 수 없습니다."); + } + } + + public String getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Name name = (Name) o; + return Objects.equals(value, name.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } +} diff --git a/src/main/java/domain/participant/Participant.java b/src/main/java/domain/participant/Participant.java index f2e1c6f059a..085f735d52e 100644 --- a/src/main/java/domain/participant/Participant.java +++ b/src/main/java/domain/participant/Participant.java @@ -1,45 +1,48 @@ package domain.participant; import domain.card.Card; +import domain.status.Start; +import domain.status.Status; import java.util.List; public abstract class Participant { - private final String name; - private final HandCards handCards; + protected Status status; - public Participant(String name) { - this.name = name; - this.handCards = new HandCards(); + public Participant() { + this.status = new Start(new HandCards()); } - public Participant(String name, HandCards handCards) { - this.name = name; - this.handCards = handCards; + public void drawInitialCards(List cards) { + this.status = status.drawInitialCards(cards); } - public void receiveInitialCards(List firstHandCards) { - handCards.receiveInitialCards(firstHandCards); + public void draw(Card card) { + this.status = this.status.draw(card); } - public void receiveHitCard(Card card) { - handCards.receiveHitCard(card); + public void stay() { + this.status = this.status.stay(); } public boolean isBust() { - return handCards.isBust(); + return this.status.isBust(); } - public int calculateScore() { - return handCards.calculateScore(); + public boolean isBlackJack() { + return this.status.isBlackJack(); } - public List getHandCards() { - return handCards.getCards(); + public boolean isRunning() { + return this.status.isRunning(); + } + + public int getScore() { + return this.status.score(); } - public String getName() { - return name; + public List getHandCards() { + return this.status.getCards(); } } diff --git a/src/main/java/domain/participant/Player.java b/src/main/java/domain/participant/Player.java index 5b78622a5e6..0a8c42416a9 100644 --- a/src/main/java/domain/participant/Player.java +++ b/src/main/java/domain/participant/Player.java @@ -1,13 +1,25 @@ package domain.participant; -public class Player extends Participant { +import domain.betting.Money; - public Player(String name) { - super(name); +public final class Player extends Participant { + + private final Name name; + private Money money; + + public Player(Name name) { + this.name = name; } - public Player(String name, HandCards handCards) { - super(name, handCards); + public void bettingMoney(Money money) { + this.money = money; } + public Money getBettingMoney() { + return money; + } + + public String getName() { + return name.getValue(); + } } diff --git a/src/main/java/domain/participant/Players.java b/src/main/java/domain/participant/Players.java new file mode 100644 index 00000000000..c7308185a6b --- /dev/null +++ b/src/main/java/domain/participant/Players.java @@ -0,0 +1,75 @@ +package domain.participant; + +import domain.card.Deck; +import util.InputNameParser; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +public class Players implements Iterable { + + private static final int MAX_PLAYERS = 8; + + private final List players; + + private Players(List players) { + this.players = players; + } + + public static Players fromString(String rawPlayerNames) { + List playerNames = InputNameParser.parsePlayerNames(rawPlayerNames); + + validateInputNames(playerNames); + + List players = playerNames.stream().map(Player::new).toList(); + return new Players(players); + } + + public List getPlayerNames() { + return players.stream() + .map(Player::getName) + .toList(); + } + + public void drawInitialCards(Deck deck) { + for (Player player : players) { + player.drawInitialCards(deck.drawInitialCards()); + } + } + + public void endGameImmediately() { + for (Player player : players) { + forceStay(player); + } + } + + private void forceStay(Player player) { + if (player.isRunning()) { + player.stay(); + } + } + + private static void validateInputNames(List playerNames) { + validateDuplicateNames(playerNames); + validatePlayerCounts(playerNames); + + } + + private static void validateDuplicateNames(List playerNames) { + if (playerNames.size() != (new HashSet<>(playerNames)).size()) { + throw new IllegalArgumentException("플레이어의 이름은 중복되지 않아야 합니다."); + } + } + + private static void validatePlayerCounts(List playerNames) { + if (playerNames.isEmpty() || playerNames.size() > MAX_PLAYERS) { + throw new IllegalArgumentException(String.format("플레이어의 수는 1명 이상 %s명 이하여야 합니다.", MAX_PLAYERS)); + } + } + + @Override + public Iterator iterator() { + return players.iterator(); + } +} diff --git a/src/main/java/domain/status/Blackjack.java b/src/main/java/domain/status/Blackjack.java new file mode 100644 index 00000000000..cc1735f6457 --- /dev/null +++ b/src/main/java/domain/status/Blackjack.java @@ -0,0 +1,14 @@ +package domain.status; + +import domain.participant.HandCards; + +public final class Blackjack extends Finished { + public Blackjack(final HandCards cards) { + super(cards); + } + + @Override + public boolean isBlackJack() { + return true; + } +} diff --git a/src/main/java/domain/status/Bust.java b/src/main/java/domain/status/Bust.java new file mode 100644 index 00000000000..3c1a8868bb5 --- /dev/null +++ b/src/main/java/domain/status/Bust.java @@ -0,0 +1,14 @@ +package domain.status; + +import domain.participant.HandCards; + +public final class Bust extends Finished { + public Bust(HandCards cards) { + super(cards); + } + + @Override + public boolean isBust() { + return true; + } +} diff --git a/src/main/java/domain/status/Finished.java b/src/main/java/domain/status/Finished.java new file mode 100644 index 00000000000..85c4f574c87 --- /dev/null +++ b/src/main/java/domain/status/Finished.java @@ -0,0 +1,27 @@ +package domain.status; + +import domain.card.Card; +import domain.participant.HandCards; + +import java.util.List; + +public abstract class Finished extends Status { + public Finished(HandCards cards) { + super(cards); + } + + @Override + public Status drawInitialCards(List cards) { + throw new IllegalStateException("게임이 이미 시작되었습니다."); + } + + @Override + public Status draw(Card card) { + throw new IllegalStateException("이미 턴이 끝난 상태에서는 카드를 뽑을 수 없습니다."); + } + + @Override + public Status stay() { + throw new IllegalStateException("이미 턴이 끝난 상태입니다."); + } +} diff --git a/src/main/java/domain/status/Hit.java b/src/main/java/domain/status/Hit.java new file mode 100644 index 00000000000..401c9996460 --- /dev/null +++ b/src/main/java/domain/status/Hit.java @@ -0,0 +1,28 @@ +package domain.status; + +import domain.BlackJackInfo; +import domain.participant.HandCards; +import domain.card.Card; + +public final class Hit extends Running { + public Hit(final HandCards cards) { + super(cards); + } + + @Override + public Status draw(final Card card) { + cards.receiveHitCard(card); + if (cards.isBust()) { + return new Bust(cards); + } + if (cards.calculateScore() == BlackJackInfo.BLACKJACK_SCORE) { + return new Stay(cards); + } + return new Hit(cards); + } + + @Override + public Status stay() { + return new Stay(cards); + } +} diff --git a/src/main/java/domain/status/Running.java b/src/main/java/domain/status/Running.java new file mode 100644 index 00000000000..f6a679a1274 --- /dev/null +++ b/src/main/java/domain/status/Running.java @@ -0,0 +1,26 @@ +package domain.status; + +import domain.card.Card; +import domain.participant.HandCards; + +import java.util.List; + +public abstract class Running extends Status { + public Running(HandCards cards) { + super(cards); + } + + @Override + public Status drawInitialCards(List cards) { + throw new IllegalStateException("이미 게임이 진행 중입니다. 초기 카드를 받을 수 없습니다."); + } + + @Override + public boolean isRunning() { + return true; + } + + public abstract Status draw(Card card); + + public abstract Status stay(); +} diff --git a/src/main/java/domain/status/Start.java b/src/main/java/domain/status/Start.java new file mode 100644 index 00000000000..6e77e029517 --- /dev/null +++ b/src/main/java/domain/status/Start.java @@ -0,0 +1,32 @@ +package domain.status; + +import domain.card.Card; +import domain.participant.HandCards; + +import java.util.List; + +public final class Start extends Status{ + + public Start(final HandCards cards) { + super(cards); + } + + @Override + public Status drawInitialCards(List initCards) { + cards.receiveInitialCards(initCards); + if(cards.isBlackJack()) { + return new Blackjack(cards); + } + return new Hit(cards); + } + + @Override + public Status draw(Card card) { + throw new IllegalStateException("게임이 시작되지 않았습니다."); + } + + @Override + public Status stay() { + throw new IllegalStateException("게임이 시작되지 않았습니다."); + } +} diff --git a/src/main/java/domain/status/Status.java b/src/main/java/domain/status/Status.java new file mode 100644 index 00000000000..eb3171bc68b --- /dev/null +++ b/src/main/java/domain/status/Status.java @@ -0,0 +1,44 @@ +package domain.status; + +import domain.card.Card; +import domain.participant.HandCards; + +import java.util.List; + +public abstract class Status { + protected final HandCards cards; + + public Status(HandCards cards) { + this.cards = cards; + } + + public abstract Status drawInitialCards(List cards); + + public abstract Status draw(Card card); + + public abstract Status stay(); + + public boolean isRunning() { + return false; + } + + public boolean isBust() { + return false; + } + + public boolean isBlackJack() { + return false; + } + + public int score() { + return cards.calculateScore(); + } + + public Card getFirstCard() { + return cards.getFirst(); + } + + public List getCards() { + return cards.getCards(); + } +} diff --git a/src/main/java/domain/status/Stay.java b/src/main/java/domain/status/Stay.java new file mode 100644 index 00000000000..52fd79ee9ea --- /dev/null +++ b/src/main/java/domain/status/Stay.java @@ -0,0 +1,9 @@ +package domain.status; + +import domain.participant.HandCards; + +public final class Stay extends Finished { + public Stay(HandCards cards) { + super(cards); + } +} diff --git a/src/main/java/dto/BettingResultDto.java b/src/main/java/dto/BettingResultDto.java new file mode 100644 index 00000000000..02e827e2083 --- /dev/null +++ b/src/main/java/dto/BettingResultDto.java @@ -0,0 +1,6 @@ +package dto; + +import java.util.List; + +public record BettingResultDto(List playerProfitDtos, long dealerProfit) { +} diff --git a/src/main/java/dto/DealerHandScoreDto.java b/src/main/java/dto/DealerHandScoreDto.java new file mode 100644 index 00000000000..41ac4dda653 --- /dev/null +++ b/src/main/java/dto/DealerHandScoreDto.java @@ -0,0 +1,18 @@ +package dto; + +import domain.participant.Dealer; +import util.HandCardProcessor; + +import java.util.List; + +public record DealerHandScoreDto(List handCards, int score, boolean isBust, boolean isBlackJack) { + + public static DealerHandScoreDto from(Dealer dealer) { + return new DealerHandScoreDto( + HandCardProcessor.processHandCards(dealer.getHandCards()), + dealer.getScore(), + dealer.isBust(), + dealer.isBlackJack() + ); + } +} diff --git a/src/main/java/dto/DealerInitialHandDTO.java b/src/main/java/dto/DealerInitialHandDTO.java deleted file mode 100644 index 28c2a52d3b4..00000000000 --- a/src/main/java/dto/DealerInitialHandDTO.java +++ /dev/null @@ -1,10 +0,0 @@ -package dto; - -import domain.participant.Dealer; -import util.CardMapper; - -public record DealerInitialHandDTO(String firstHandCard) { - public static DealerInitialHandDTO from(Dealer dealer) { - return new DealerInitialHandDTO(CardMapper.cardToKorean(dealer.getFirstCard())); - } -} diff --git a/src/main/java/dto/DealerInitialHandDto.java b/src/main/java/dto/DealerInitialHandDto.java new file mode 100644 index 00000000000..2072ed8f698 --- /dev/null +++ b/src/main/java/dto/DealerInitialHandDto.java @@ -0,0 +1,10 @@ +package dto; + +import domain.participant.Dealer; +import util.CardMapper; + +public record DealerInitialHandDto(String firstHandCard) { + public static DealerInitialHandDto from(Dealer dealer) { + return new DealerInitialHandDto(CardMapper.cardToKorean(dealer.getFirstCard())); + } +} diff --git a/src/main/java/dto/DealerResultDTO.java b/src/main/java/dto/DealerResultDTO.java deleted file mode 100644 index 630bf82a527..00000000000 --- a/src/main/java/dto/DealerResultDTO.java +++ /dev/null @@ -1,4 +0,0 @@ -package dto; - -public record DealerResultDTO (int win, int draw, int lose) { -} diff --git a/src/main/java/dto/GameResultDTO.java b/src/main/java/dto/GameResultDTO.java deleted file mode 100644 index f18dcaf7bc9..00000000000 --- a/src/main/java/dto/GameResultDTO.java +++ /dev/null @@ -1,6 +0,0 @@ -package dto; - -import java.util.List; - -public record GameResultDTO(List playerResultDTOs, DealerResultDTO dealerResultDTO) { -} diff --git a/src/main/java/dto/GameScoreDTO.java b/src/main/java/dto/GameScoreDTO.java deleted file mode 100644 index a8d51cfde55..00000000000 --- a/src/main/java/dto/GameScoreDTO.java +++ /dev/null @@ -1,20 +0,0 @@ -package dto; - -import domain.Players; -import domain.participant.Dealer; -import domain.participant.Player; - -import java.util.ArrayList; -import java.util.List; - -public record GameScoreDTO(List players, HandScoreDTO dealer) { - - public static GameScoreDTO from(Players players, Dealer dealer) { - List playerHandDTOs = new ArrayList<>(); - for (Player player : players) { - playerHandDTOs.add(HandScoreDTO.from(player)); - } - HandScoreDTO dealerHandDTO = HandScoreDTO.from(dealer); - return new GameScoreDTO(playerHandDTOs, dealerHandDTO); - } -} diff --git a/src/main/java/dto/GameScoreDto.java b/src/main/java/dto/GameScoreDto.java new file mode 100644 index 00000000000..028e34f277c --- /dev/null +++ b/src/main/java/dto/GameScoreDto.java @@ -0,0 +1,21 @@ +package dto; + +import domain.participant.Players; +import domain.participant.Dealer; +import domain.participant.Player; + +import java.util.ArrayList; +import java.util.List; + +public record GameScoreDto(List players, DealerHandScoreDto dealer) { + + public static GameScoreDto from(Players players, Dealer dealer) { + List playerHandDtos = new ArrayList<>(); + for (Player player : players) { + playerHandDtos.add(PlayerHandScoreDto.from(player)); + } + DealerHandScoreDto dealerHandDto = DealerHandScoreDto.from(dealer); + return new GameScoreDto(playerHandDtos, dealerHandDto); + } +} + diff --git a/src/main/java/dto/GameStartDTO.java b/src/main/java/dto/GameStartDTO.java deleted file mode 100644 index b54e5eebc4d..00000000000 --- a/src/main/java/dto/GameStartDTO.java +++ /dev/null @@ -1,24 +0,0 @@ -package dto; - -import domain.Players; -import domain.participant.Dealer; -import domain.participant.Player; - -import java.util.ArrayList; -import java.util.List; - -public record GameStartDTO(List players, DealerInitialHandDTO dealer, List playerNames) { - - public static GameStartDTO from(Players players, Dealer dealer) { - List playerHandDTOs = new ArrayList<>(); - for (Player player : players) { - playerHandDTOs.add(HandDTO.from(player)); - } - DealerInitialHandDTO dealerHandDTO = DealerInitialHandDTO.from(dealer); - return new GameStartDTO(playerHandDTOs, dealerHandDTO, getPlayerNames(players)); - } - - private static List getPlayerNames(Players players) { - return players.getPlayerNames(); - } -} diff --git a/src/main/java/dto/GameStartDto.java b/src/main/java/dto/GameStartDto.java new file mode 100644 index 00000000000..17e418accbe --- /dev/null +++ b/src/main/java/dto/GameStartDto.java @@ -0,0 +1,24 @@ +package dto; + +import domain.participant.Players; +import domain.participant.Dealer; +import domain.participant.Player; + +import java.util.ArrayList; +import java.util.List; + +public record GameStartDto(List players, DealerInitialHandDto dealer, List playerNames) { + + public static GameStartDto from(Players players, Dealer dealer) { + List playerHandDtos = new ArrayList<>(); + for (Player player : players) { + playerHandDtos.add(PlayerHandDto.from(player)); + } + DealerInitialHandDto dealerHandDto = DealerInitialHandDto.from(dealer); + return new GameStartDto(playerHandDtos, dealerHandDto, getPlayerNames(players)); + } + + private static List getPlayerNames(Players players) { + return players.getPlayerNames(); + } +} diff --git a/src/main/java/dto/HandDTO.java b/src/main/java/dto/HandDTO.java deleted file mode 100644 index 8018e3ec8f0..00000000000 --- a/src/main/java/dto/HandDTO.java +++ /dev/null @@ -1,16 +0,0 @@ -package dto; - -import domain.participant.Participant; -import util.CardMapper; - -import java.util.List; - -public record HandDTO(String name, List handCards) { - - public static HandDTO from(Participant participant) { - return new HandDTO( - participant.getName(), participant.getHandCards().stream() - .map(CardMapper::cardToKorean) - .toList()); - } -} diff --git a/src/main/java/dto/HandScoreDTO.java b/src/main/java/dto/HandScoreDTO.java deleted file mode 100644 index f23fffbacac..00000000000 --- a/src/main/java/dto/HandScoreDTO.java +++ /dev/null @@ -1,23 +0,0 @@ -package dto; - -import domain.participant.Participant; -import util.CardMapper; - -import java.util.List; - -public record HandScoreDTO(String name, List handCards, String score) { - - public static HandScoreDTO from(Participant participant) { - return new HandScoreDTO( - participant.getName(), participant.getHandCards().stream() - .map(CardMapper::cardToKorean) - .toList(), getStringScore(participant)); - } - - private static String getStringScore(Participant participant) { - if (participant.isBust()) { - return "버스트"; - } - return String.valueOf(participant.calculateScore()); - } -} diff --git a/src/main/java/dto/PlayerHandDto.java b/src/main/java/dto/PlayerHandDto.java new file mode 100644 index 00000000000..df36a191954 --- /dev/null +++ b/src/main/java/dto/PlayerHandDto.java @@ -0,0 +1,16 @@ +package dto; + +import domain.participant.Player; +import util.HandCardProcessor; + +import java.util.List; + +public record PlayerHandDto(String name, List handCards) { + + public static PlayerHandDto from(Player player) { + return new PlayerHandDto( + player.getName(), + HandCardProcessor.processHandCards(player.getHandCards()) + ); + } +} diff --git a/src/main/java/dto/PlayerHandScoreDto.java b/src/main/java/dto/PlayerHandScoreDto.java new file mode 100644 index 00000000000..921dcdf2a2b --- /dev/null +++ b/src/main/java/dto/PlayerHandScoreDto.java @@ -0,0 +1,19 @@ +package dto; + +import domain.participant.Player; +import util.HandCardProcessor; + +import java.util.List; + +public record PlayerHandScoreDto(String name, List handCards, int score, boolean isBust, boolean isBlackJack) { + + public static PlayerHandScoreDto from(Player player) { + return new PlayerHandScoreDto( + player.getName(), + HandCardProcessor.processHandCards(player.getHandCards()), + player.getScore(), + player.isBust(), + player.isBlackJack() + ); + } +} diff --git a/src/main/java/dto/PlayerProfitDto.java b/src/main/java/dto/PlayerProfitDto.java new file mode 100644 index 00000000000..6e884302e30 --- /dev/null +++ b/src/main/java/dto/PlayerProfitDto.java @@ -0,0 +1,9 @@ +package dto; + +import domain.betting.Profit; + +public record PlayerProfitDto(String name, long profit) { + public static PlayerProfitDto from(Profit profit) { + return new PlayerProfitDto(profit.getName(), profit.calculateProfit()); + } +} diff --git a/src/main/java/dto/PlayerResultDTO.java b/src/main/java/dto/PlayerResultDTO.java deleted file mode 100644 index a0bddea7185..00000000000 --- a/src/main/java/dto/PlayerResultDTO.java +++ /dev/null @@ -1,4 +0,0 @@ -package dto; - -public record PlayerResultDTO (String name, String result){ -} diff --git a/src/main/java/service/BettingCalculateService.java b/src/main/java/service/BettingCalculateService.java new file mode 100644 index 00000000000..0fad0d044b0 --- /dev/null +++ b/src/main/java/service/BettingCalculateService.java @@ -0,0 +1,54 @@ +package service; + +import domain.betting.BettingResult; +import domain.participant.Players; +import domain.betting.Money; +import domain.betting.Profit; +import domain.participant.Dealer; +import domain.participant.Player; +import dto.BettingResultDto; +import dto.PlayerProfitDto; + +import java.util.ArrayList; +import java.util.List; + +public class BettingCalculateService { + private final Players players; + private final Dealer dealer; + + public BettingCalculateService(Players players, Dealer dealer) { + this.players = players; + this.dealer = dealer; + } + + private List calculatePlayersProfit() { + List playersProfit = new ArrayList<>(); + for (Player player : players) { + Money bettingMoney = player.getBettingMoney(); + BettingResult bettingResult = BettingResult.judge(player, dealer); + playersProfit.add(new Profit(player.getName(), bettingMoney, bettingResult)); + } + return playersProfit; + } + + public BettingResultDto getBettingResult() { + return new BettingResultDto(getPlayersProfit(), getDealerProfit()); + } + + private List getPlayersProfit() { + List profits = calculatePlayersProfit(); + List playersProfit = new ArrayList<>(); + for (Profit profit : profits) { + playersProfit.add(PlayerProfitDto.from(profit)); + } + + return playersProfit; + } + + private long getDealerProfit() { + List profits = calculatePlayersProfit(); + return -profits.stream() + .mapToLong(Profit::calculateProfit) + .sum(); + } +} diff --git a/src/main/java/service/GameService.java b/src/main/java/service/GameService.java index 9c9453ae496..a00c51f7425 100644 --- a/src/main/java/service/GameService.java +++ b/src/main/java/service/GameService.java @@ -1,20 +1,14 @@ package service; -import java.util.ArrayList; -import java.util.List; +import domain.participant.Players; -import domain.PlayerGameResult; -import domain.Players; import domain.card.Deck; import domain.participant.Dealer; -import domain.participant.Player; +import domain.participant.Participant; + +import dto.GameScoreDto; +import dto.GameStartDto; -import dto.GameStartDTO; -import dto.HandDTO; -import dto.GameScoreDTO; -import dto.PlayerResultDTO; -import dto.GameResultDTO; -import dto.DealerResultDTO; public class GameService { private final Players players; @@ -22,56 +16,27 @@ public class GameService { private final Deck deck = new Deck(); - public GameService(Players players) { + public GameService(Players players, Dealer dealer) { this.players = players; - this.dealer = new Dealer(); - } - - public GameStartDTO startGame() { - players.receiveInitialCards(deck); - dealer.receiveInitialCards(deck.dealFirstHandCards()); - return GameStartDTO.from(players, dealer); - } - - public HandDTO playerHit(Player player) { - player.receiveHitCard(deck.drawCard()); - return getCurrentHand(player); + this.dealer = dealer; } - public HandDTO getCurrentHand(Player player) { - return HandDTO.from(player); + public GameStartDto startGame() { + players.drawInitialCards(deck); + dealer.drawInitialCards(deck.drawInitialCards()); + return GameStartDto.from(players, dealer); } - public void dealerHit() { - dealer.receiveHitCard(deck.drawCard()); + public void hit(Participant participant) { + participant.draw(deck.drawCard()); } - public GameScoreDTO getTotalScore() { - return GameScoreDTO.from(players, dealer); + public void stay(Participant participant) { + participant.stay(); } - public GameResultDTO calculateResults() { - List playerResultDTOs = new ArrayList<>(); - int dealerWinCount = 0; - int dealerDrawCount = 0; - int dealerLoseCount = 0; - - for (Player player : players) { - PlayerGameResult playerResult = PlayerGameResult.from(player, dealer); - if (playerResult == PlayerGameResult.WIN) { - dealerLoseCount++; - } - if (playerResult == PlayerGameResult.DRAW) { - dealerDrawCount++; - } - if (playerResult == PlayerGameResult.LOSE) { - dealerWinCount++; - } - playerResultDTOs.add(new PlayerResultDTO(player.getName(), playerResult.getValue())); - } - - return new GameResultDTO(playerResultDTOs, new DealerResultDTO(dealerWinCount, dealerDrawCount, dealerLoseCount)); - + public void endGameImmediately() { + players.endGameImmediately(); } public Players getPlayers() { @@ -81,4 +46,8 @@ public Players getPlayers() { public Dealer getDealer() { return dealer; } + + public GameScoreDto getTotalScore() { + return GameScoreDto.from(players, dealer); + } } diff --git a/src/main/java/util/HandCardProcessor.java b/src/main/java/util/HandCardProcessor.java new file mode 100644 index 00000000000..07deb853b96 --- /dev/null +++ b/src/main/java/util/HandCardProcessor.java @@ -0,0 +1,16 @@ +package util; + +import domain.card.Card; + +import java.util.List; + +public class HandCardProcessor { + private HandCardProcessor() { + } + + public static List processHandCards(List handCards) { + return handCards.stream() + .map(CardMapper::cardToKorean) + .toList(); + } +} diff --git a/src/main/java/util/HitOption.java b/src/main/java/util/HitOption.java index baff0900a15..dec43ec9240 100644 --- a/src/main/java/util/HitOption.java +++ b/src/main/java/util/HitOption.java @@ -16,7 +16,7 @@ public String getValue() { } public static HitOption of(String value) { - String lowerCaseValue = value.toLowerCase(); + String lowerCaseValue = value.toLowerCase().trim(); return Arrays.stream(HitOption.values()) .filter(hitOption -> lowerCaseValue.equals(hitOption.getValue())) .findFirst() diff --git a/src/main/java/util/InputBettingParser.java b/src/main/java/util/InputBettingParser.java new file mode 100644 index 00000000000..e91c23640a6 --- /dev/null +++ b/src/main/java/util/InputBettingParser.java @@ -0,0 +1,20 @@ +package util; + +import domain.betting.Money; + +public class InputBettingParser { + private InputBettingParser() { + } + + public static Money parseBettingMoney(String inputBettingMoney) { + try { + long bettingMoney = Long.parseLong(inputBettingMoney.trim()); + return new Money(bettingMoney); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException("숫자로만 입력해야합니다."); + } + catch (IllegalArgumentException exception) { + throw new IllegalArgumentException(exception.getMessage()); + } + } +} diff --git a/src/main/java/util/InputHitOptionParser.java b/src/main/java/util/InputHitOptionParser.java deleted file mode 100644 index f21e2f6132b..00000000000 --- a/src/main/java/util/InputHitOptionParser.java +++ /dev/null @@ -1,11 +0,0 @@ -package util; - -public class InputHitOptionParser { - - private InputHitOptionParser() { - } - - public static HitOption parseHitOption(String inputHitOption) { - return HitOption.of(inputHitOption); - } -} diff --git a/src/main/java/util/InputNameParser.java b/src/main/java/util/InputNameParser.java index b8e29aab52a..360a3b21b81 100644 --- a/src/main/java/util/InputNameParser.java +++ b/src/main/java/util/InputNameParser.java @@ -1,21 +1,22 @@ package util; +import domain.participant.Name; + import java.util.Arrays; import java.util.List; -import java.util.regex.PatternSyntaxException; public class InputNameParser { + private static final String DELIMITER = ","; + private InputNameParser() { } - public static List parsePlayerNames(String inputNames) { + public static List parsePlayerNames(String inputNames) { try { - List names = Arrays.stream(inputNames.split(",", -1)) - .map(String::strip) + return Arrays.stream(inputNames.split(DELIMITER, -1)) + .map(Name::new) .toList(); - InputNameValidator.validateInputNames(names); - return names; } catch (IllegalArgumentException exception) { throw new IllegalArgumentException(exception.getMessage()); } diff --git a/src/main/java/util/InputNameValidator.java b/src/main/java/util/InputNameValidator.java deleted file mode 100644 index 0047ad866cb..00000000000 --- a/src/main/java/util/InputNameValidator.java +++ /dev/null @@ -1,35 +0,0 @@ -package util; - -import java.util.HashSet; -import java.util.List; - -public class InputNameValidator { - - private InputNameValidator() { - } - - public static void validateInputNames(List playerNames) { - validateDuplicateNames(playerNames); - validateEmptyNames(playerNames); - validatePlayerCounts(playerNames); - } - - private static void validateDuplicateNames(List playerNames) { - if (playerNames.size() != (new HashSet<>(playerNames)).size()) { - throw new IllegalArgumentException("플레이어의 이름은 중복되지 않아야 합니다."); - } - } - - private static void validateEmptyNames(List playerNames) { - boolean isBlank = playerNames.stream().anyMatch(String::isBlank); - if (isBlank) { - throw new IllegalArgumentException("플레이어의 이름은 공백이 될 수 없습니다."); - } - } - - private static void validatePlayerCounts(List playerNames) { - if (playerNames.isEmpty() || playerNames.size() > 8) { - throw new IllegalArgumentException("플레이어의 수는 1명 이상 8명 이하여야 합니다."); - } - } -} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index db53a556553..967b61e2ef8 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -7,11 +7,18 @@ public class InputView { public String readPlayerNames() { System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); - return scanner.next(); + return scanner.nextLine(); } public String readHitOption(String playerName) { System.out.println(playerName + "는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)"); - return scanner.next(); + return scanner.nextLine(); } + + public String readBetting(String playerName) { + System.out.println(); + System.out.println(playerName + "의 배팅 금액은?"); + return scanner.nextLine(); + } + } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 3e43b5125d9..e008d76cf23 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,63 +1,92 @@ package view; -import dto.*; +import dto.BettingResultDto; +import dto.DealerHandScoreDto; +import dto.DealerInitialHandDto; +import dto.GameScoreDto; +import dto.GameStartDto; +import dto.PlayerHandDto; +import dto.PlayerHandScoreDto; +import dto.PlayerProfitDto; import java.util.List; public class OutputView { - public void printStartGame(GameStartDTO gameStartDTO) { - System.out.println("딜러와 " + convertListToString(gameStartDTO.playerNames()) + "에게 2장을 나누었습니다."); - printDealerInitialHandCard(gameStartDTO.dealer()); + public void printStartGame(GameStartDto gameStartDto) { + System.out.println("딜러와 " + convertListToString(gameStartDto.playerNames()) + "에게 2장을 나누었습니다."); + printDealerInitialHandCard(gameStartDto.dealer()); - List players = gameStartDTO.players(); - for (HandDTO player : players) { + List players = gameStartDto.players(); + for (PlayerHandDto player : players) { printHandCard(player); } System.out.println(); } - private void printDealerInitialHandCard(DealerInitialHandDTO dealerInitialHandDTO) { - System.out.println("딜러카드: " + dealerInitialHandDTO.firstHandCard()); + private void printDealerInitialHandCard(DealerInitialHandDto dealerInitialHandDto) { + System.out.println("딜러: " + dealerInitialHandDto.firstHandCard()); } - public void printHandCard(HandDTO playerHandDTO) { - System.out.println(playerHandDTO.name() + "카드: " + convertListToString(playerHandDTO.handCards())); + public void printHandCard(PlayerHandDto playerHandDto) { + System.out.println(playerHandDto.name() + "카드: " + convertListToString(playerHandDto.handCards())); } - public void printHandCardWithScore(HandScoreDTO handScoreDTO) { - System.out.println(handScoreDTO.name() + "카드: " + convertListToString(handScoreDTO.handCards()) + " - 결과: " + handScoreDTO.score()); + private void printDealerHandCardWithScore(DealerHandScoreDto handScoreDto) { + System.out.println("딜러 카드: " + convertListToString(handScoreDto.handCards()) + " - 결과: " + printScore(handScoreDto.isBlackJack(), handScoreDto.isBust(), handScoreDto.score())); + } + + private void printPlayerHandCardWithScore(PlayerHandScoreDto handScoreDto) { + System.out.println(handScoreDto.name() + "카드: " + convertListToString(handScoreDto.handCards()) + " - 결과: " + printScore(handScoreDto.isBlackJack(), handScoreDto.isBust(), handScoreDto.score())); + } + + private String printScore(boolean isBlackJack, boolean isBust, int score) { + if (isBlackJack) { + return "블랙잭"; + } + if (isBust) { + return "버스트"; + } + return String.valueOf(score); } public void printDealerReceiveCard() { System.out.println("딜러는 16이하라 한장의 카드를 더 받았습니다."); } - public void printScore(GameScoreDTO gameResultDTO) { + public void printScore(GameScoreDto gameResultDto) { System.out.println(); - printHandCardWithScore(gameResultDTO.dealer()); + printDealerHandCardWithScore(gameResultDto.dealer()); - List players = gameResultDTO.players(); - for (HandScoreDTO player : players) { - printHandCardWithScore(player); + List players = gameResultDto.players(); + for (PlayerHandScoreDto player : players) { + printPlayerHandCardWithScore(player); } } - public void printResults(GameResultDTO gameResultDTO) { - DealerResultDTO dealerResultDTO = gameResultDTO.dealerResultDTO(); - List playerResultDTOs = gameResultDTO.playerResultDTOs(); + + public void printBettingResults(BettingResultDto bettingResultDto) { + long dealerProfit = bettingResultDto.dealerProfit(); + + List playerProfitDtos = bettingResultDto.playerProfitDtos(); + System.out.println(); - System.out.println("## 최종 승패"); - System.out.println("딜러: " + dealerResultDTO.win() + "승 " + dealerResultDTO.draw() + "무 " + dealerResultDTO.lose() + "패"); + System.out.println("## 최종 수익"); + System.out.println("딜러: " + dealerProfit); - for (PlayerResultDTO playerResultDTO : playerResultDTOs) { - System.out.println(playerResultDTO.name() + ": " + playerResultDTO.result()); + for (PlayerProfitDto playerProfitDto : playerProfitDtos) { + System.out.println(playerProfitDto.name() + ": " + playerProfitDto.profit()); } } + private String convertListToString(List list) { return String.join(", ", list); } - public void printErrorMessage(String errorMessage) { + public void printInputErrorMessage(String errorMessage) { System.out.printf("%s 다시 입력해주세요\n", errorMessage); } + + public void printDealerBlackJack() { + System.out.println("딜러가 블랙잭입니다. 즉시 점수 계산을 시작합니다."); + } } diff --git a/src/test/java/domain/PlayerGameResultTest.java b/src/test/java/domain/PlayerGameResultTest.java deleted file mode 100644 index 9705cf5907f..00000000000 --- a/src/test/java/domain/PlayerGameResultTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package domain; - -import domain.card.Card; -import domain.card.CardNumber; -import domain.card.CardShape; -import domain.participant.Dealer; -import domain.participant.HandCards; -import domain.participant.Player; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -class PlayerGameResultTest { - @Test - @DisplayName("플레이어와 딜러 모두 버스트가 아니라면, 딜러보다 21에 가까워야 이긴다.") - void playerNumberWinTest() { - // Given - HandCards playerHands = new HandCards( - List.of(new Card(CardNumber.ACE, CardShape.CLUB), new Card(CardNumber.JACK, CardShape.DIAMOND)) - ); - HandCards dealerHands = new HandCards( - List.of(new Card(CardNumber.KING, CardShape.HEART), new Card(CardNumber.JACK, CardShape.SPADE)) - ); - - Player player = new Player("플레이어 1", playerHands); - Dealer dealer = new Dealer(dealerHands); - - // When - PlayerGameResult playerResult = PlayerGameResult.from(player, dealer); - - // Then - assertThat(playerResult).isEqualTo(PlayerGameResult.WIN); - } - - - @Test - @DisplayName("플레이어가 버스트이고 딜러가 버스트가 아니라면 플레이어가 패배한다.") - void playerBustLoseTest() { - // Given - HandCards playerHands = new HandCards( - List.of(new Card(CardNumber.TWO, CardShape.CLUB), new Card(CardNumber.JACK, CardShape.DIAMOND), new Card(CardNumber.QUEEN, CardShape.CLUB)) - ); - HandCards dealerHands = new HandCards( - List.of(new Card(CardNumber.KING, CardShape.HEART), new Card(CardNumber.JACK, CardShape.SPADE)) - ); - - Player player = new Player("플레이어 1", playerHands); - Dealer dealer = new Dealer(dealerHands); - - // When - PlayerGameResult playerResult = PlayerGameResult.from(player, dealer); - - // Then - assertThat(playerResult).isEqualTo(PlayerGameResult.LOSE); - } - - @Test - @DisplayName("플레이어와 딜러 모두 버스트라면 무승부이다.") - void playerWinningTest() { - // Given - HandCards playerHands = new HandCards( - List.of(new Card(CardNumber.TWO, CardShape.CLUB), new Card(CardNumber.JACK, CardShape.DIAMOND), new Card(CardNumber.QUEEN, CardShape.CLUB)) - ); - HandCards dealerHands = new HandCards( - List.of(new Card(CardNumber.KING, CardShape.HEART), new Card(CardNumber.JACK, CardShape.SPADE), new Card(CardNumber.EIGHT, CardShape.CLUB)) - ); - - Player player = new Player("플레이어 1", playerHands); - Dealer dealer = new Dealer(dealerHands); - - // When - PlayerGameResult playerResult = PlayerGameResult.from(player, dealer); - - // Then - assertThat(playerResult).isEqualTo(PlayerGameResult.DRAW); - } -} \ No newline at end of file diff --git a/src/test/java/domain/betting/BettingResultTest.java b/src/test/java/domain/betting/BettingResultTest.java new file mode 100644 index 00000000000..3f7b1d4f63c --- /dev/null +++ b/src/test/java/domain/betting/BettingResultTest.java @@ -0,0 +1,193 @@ +package domain.betting; + +import domain.card.Card; +import domain.card.CardNumber; +import domain.card.CardShape; +import domain.participant.Dealer; +import domain.participant.Name; +import domain.participant.Player; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class BettingResultTest { + + private Player player; + private Dealer dealer; + + @BeforeEach + void setUp() { + player = new Player(new Name("플레이어1")); + dealer = new Dealer(); + } + + @Test + @DisplayName("플레이어와 딜러 모두 버스트가 아닐 때, 딜러보다 21에 가까우면, 이긴다.") + void playerStayWinTest() { + // Given + List playerInitHands = List.of(new Card(CardNumber.EIGHT, CardShape.CLUB), new Card(CardNumber.ACE, CardShape.DIAMOND)); + List dealerInitHands = List.of(new Card(CardNumber.FIVE, CardShape.CLUB), new Card(CardNumber.FOUR, CardShape.DIAMOND)); + + player.drawInitialCards(playerInitHands); + player.stay(); + + dealer.drawInitialCards(dealerInitHands); + + dealer.draw(new Card(CardNumber.EIGHT, CardShape.CLUB)); + dealer.stay(); + + // When + BettingResult result = BettingResult.judge(player, dealer); + + // Then + assertThat(result).isEqualTo(BettingResult.WIN); + } + + @Test + @DisplayName("플레이어와 딜러 모두 버스트가 아닐 때, 딜러가 21에 가까우면 진다.") + void playerStayLoseTest() { + // Given + List playerInitHands = List.of(new Card(CardNumber.EIGHT, CardShape.CLUB), new Card(CardNumber.ACE, CardShape.DIAMOND)); + List dealerInitHands = List.of(new Card(CardNumber.FIVE, CardShape.CLUB), new Card(CardNumber.FOUR, CardShape.DIAMOND)); + + player.drawInitialCards(playerInitHands); + player.stay(); + + dealer.drawInitialCards(dealerInitHands); + + dealer.draw(new Card(CardNumber.ACE, CardShape.CLUB)); + dealer.stay(); + + // When + BettingResult result = BettingResult.judge(player, dealer); + + // Then + assertThat(result).isEqualTo(BettingResult.LOSE); + } + + @Test + @DisplayName("플레이어와 딜러 모두 버스트가 아닐 때, 딜러와 플레이어의 점수가 같다면 무승부이다.") + void playerDrawTest() { + // Given + List playerInitHands = List.of(new Card(CardNumber.EIGHT, CardShape.CLUB), new Card(CardNumber.ACE, CardShape.DIAMOND)); + List dealerInitHands = List.of(new Card(CardNumber.FIVE, CardShape.CLUB), new Card(CardNumber.FOUR, CardShape.DIAMOND)); + + player.drawInitialCards(playerInitHands); + player.stay(); + + dealer.drawInitialCards(dealerInitHands); + + dealer.draw(new Card(CardNumber.JACK, CardShape.CLUB)); + dealer.stay(); + + // When + BettingResult result = BettingResult.judge(player, dealer); + + // Then + assertThat(result).isEqualTo(BettingResult.DRAW); + } + + + @Test + @DisplayName("플레이어가 버스트이면 베팅한 돈을 잃는다.") + void playerBustTest() { + // Given + List playerInitHands = List.of(new Card(CardNumber.EIGHT, CardShape.CLUB), new Card(CardNumber.TEN, CardShape.DIAMOND)); + List dealerInitHands = List.of(new Card(CardNumber.FIVE, CardShape.CLUB), new Card(CardNumber.FOUR, CardShape.DIAMOND)); + + player.drawInitialCards(playerInitHands); + player.draw(new Card(CardNumber.FIVE, CardShape.CLUB)); + + dealer.drawInitialCards(dealerInitHands); + + dealer.draw(new Card(CardNumber.EIGHT, CardShape.CLUB)); + + // When + BettingResult result = BettingResult.judge(player, dealer); + + // Then + assertThat(result).isEqualTo(BettingResult.LOSE); + } + + @Test + @DisplayName("플레이어가 블랙잭이고, 딜러가 블랙잭이 아니라면, 베팅한 금액의 1.5배의 돈을 받는다.") + void playerBlackJackWinTest() { + // Given + List playerInitHands = List.of(new Card(CardNumber.ACE, CardShape.CLUB), new Card(CardNumber.TEN, CardShape.DIAMOND)); + List dealerInitHands = List.of(new Card(CardNumber.FIVE, CardShape.CLUB), new Card(CardNumber.FOUR, CardShape.DIAMOND)); + + player.drawInitialCards(playerInitHands); + dealer.drawInitialCards(dealerInitHands); + + dealer.draw(new Card(CardNumber.EIGHT, CardShape.CLUB)); + + // When + BettingResult result = BettingResult.judge(player, dealer); + + // Then + assertThat(result).isEqualTo(BettingResult.WIN_WITH_BLACKJACK); + } + + @Test + @DisplayName("플레이어와 딜러 둘다 블랙잭이라면, 무승부이다.") + void playerBlackJackDrawTest() { + // Given + List playerInitHands = List.of(new Card(CardNumber.ACE, CardShape.CLUB), new Card(CardNumber.JACK, CardShape.DIAMOND)); + List dealerInitHands = List.of(new Card(CardNumber.QUEEN, CardShape.CLUB), new Card(CardNumber.ACE, CardShape.DIAMOND)); + + player.drawInitialCards(playerInitHands); + dealer.drawInitialCards(dealerInitHands); + + // When + BettingResult result = BettingResult.judge(player, dealer); + + // Then + assertThat(result).isEqualTo(BettingResult.DRAW); + } + + @Test + @DisplayName("플레이어가 버스트가 아니고 딜러가 버스트라면 이긴다.") + void dealerBustTest() { + // Given + List playerInitHands = List.of(new Card(CardNumber.EIGHT, CardShape.CLUB), new Card(CardNumber.ACE, CardShape.DIAMOND)); + List dealerInitHands = List.of(new Card(CardNumber.FIVE, CardShape.CLUB), new Card(CardNumber.FOUR, CardShape.DIAMOND)); + + player.drawInitialCards(playerInitHands); + player.stay(); + + dealer.drawInitialCards(dealerInitHands); + + dealer.draw(new Card(CardNumber.SIX, CardShape.CLUB)); + dealer.draw(new Card(CardNumber.TEN, CardShape.CLUB)); + + + // When + BettingResult result = BettingResult.judge(player, dealer); + + // Then + assertThat(result).isEqualTo(BettingResult.WIN); + } + + @Test + @DisplayName("플레이어가 블랙잭이 아니고 딜러 둘다 블랙잭이라면 진다.") + void dealerBlackJackTest() { + // Given + List playerInitHands = List.of(new Card(CardNumber.EIGHT, CardShape.CLUB), new Card(CardNumber.ACE, CardShape.DIAMOND)); + List dealerInitHands = List.of(new Card(CardNumber.QUEEN, CardShape.CLUB), new Card(CardNumber.ACE, CardShape.DIAMOND)); + + player.drawInitialCards(playerInitHands); + player.stay(); + + dealer.drawInitialCards(dealerInitHands); + + // When + BettingResult result = BettingResult.judge(player, dealer); + + // Then + assertThat(result).isEqualTo(BettingResult.LOSE); + } +} diff --git a/src/test/java/domain/betting/MoneyTest.java b/src/test/java/domain/betting/MoneyTest.java new file mode 100644 index 00000000000..48f92dbbb8a --- /dev/null +++ b/src/test/java/domain/betting/MoneyTest.java @@ -0,0 +1,58 @@ +package domain.betting; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class MoneyTest { + @Test + @DisplayName("베팅 액수는 양수이다.") + void validValueTest() { + Money money = new Money(1000); + long value = money.getValue(); + assertThat(value).isEqualTo(1000); + } + + @Test + @DisplayName("베팅 액수는 0이 될 수 없다.") + void zeroTest() { + assertThrows(IllegalArgumentException.class, () -> new Money(0)); + } + + @Test + @DisplayName("베팅 액수가 양수가 아니면 오류를 반환한다.") + void invalidValueTest() { + assertThrows(IllegalArgumentException.class, () -> new Money(-1000)); + } + + @Test + @DisplayName("금액을 합산할 수 있어야 한다.") + void addTest() { + Money money = new Money(1000); + Money other = new Money(2000); + + assertThat(money.sum(other)).isEqualTo(new Money(3000)); + } + + @Test + @DisplayName("금액은 1000원 단위로 베팅할 수 있다.") + void bettingUnitTest() { + assertThrows(IllegalArgumentException.class, () -> new Money(10231230)); + } + + @Test + @DisplayName("최대 베팅 액수는 1억이다.") + void maximumMoneyTest() { + Money money = new Money(100_000_000); + assertThat(money.getValue()).isEqualTo(100_000_000); + } + + @Test + @DisplayName("최대 베팅 액수가 넘어가면 오류를 반환한다.") + void invalidMaximumMoneyTest() { + assertThrows(IllegalArgumentException.class, () -> new Money(100_001_000)); + } + +} diff --git a/src/test/java/domain/betting/NameTest.java b/src/test/java/domain/betting/NameTest.java new file mode 100644 index 00000000000..170a19bb8cd --- /dev/null +++ b/src/test/java/domain/betting/NameTest.java @@ -0,0 +1,29 @@ +package domain.betting; + +import domain.participant.Name; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class NameTest { + + @Test + @DisplayName("양 옆 공백에 대한 케이스는 양옆의 공백을 없앤다.") + void testTrimNameParseCase() { + String playerName = " 1 "; + Name name = new Name(playerName); + + assertThat(name).isEqualTo(new Name("1")); + } + + + @Test + @DisplayName("공백 입력에 대한 케이스에 대해서는 예외를 반환한다.") + void testEmptyNameParseCase() { + String playerName = " "; + Assertions.assertThrows(IllegalArgumentException.class, () -> new Name(playerName)); + } + +} diff --git a/src/test/java/domain/betting/ProfitTest.java b/src/test/java/domain/betting/ProfitTest.java new file mode 100644 index 00000000000..4c94780628c --- /dev/null +++ b/src/test/java/domain/betting/ProfitTest.java @@ -0,0 +1,64 @@ +package domain.betting; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ProfitTest { + @Test + @DisplayName("이겼을 때 정상적으로 돈을 주는지 확인한다.") + void winProfitTest() { + // Given + Money money = new Money(1000); + Profit profit = new Profit("플레이어1", money, BettingResult.WIN); + + // When + long bettingResultMoney = profit.calculateProfit(); + + // Then + Assertions.assertThat(bettingResultMoney).isEqualTo(1000); + } + + @Test + @DisplayName("비겼을 때 돈을 다시 돌려받는지 확인한다.") + void drawProfitTest() { + // Given + Money money = new Money(3000); + Profit profit = new Profit("플레이어1", money, BettingResult.DRAW); + + // When + long bettingResultMoney = profit.calculateProfit(); + + // Then + Assertions.assertThat(bettingResultMoney).isEqualTo(0); + } + + + @Test + @DisplayName("졌을 때 손해인지 확인한다.") + void loseProfitTest() { + // Given + Money money = new Money(3000); + Profit profit = new Profit("플레이어1", money, BettingResult.LOSE); + + // When + long bettingResultMoney = profit.calculateProfit(); + + // Then + Assertions.assertThat(bettingResultMoney).isEqualTo(-3000); + } + + @Test + @DisplayName("블랙잭으로 이겼을 때 1.5배로 받는지 확인한다.") + void blackJackWinProfitTest() { + // Given + Money money = new Money(3000); + Profit profit = new Profit("플레이어1", money, BettingResult.WIN_WITH_BLACKJACK); + + // When + long bettingResultMoney = profit.calculateProfit(); + + // Then + Assertions.assertThat(bettingResultMoney).isEqualTo(4500); + } +} diff --git a/src/test/java/domain/card/CardTest.java b/src/test/java/domain/card/CardTest.java index da26fd42f59..900d3e7673f 100644 --- a/src/test/java/domain/card/CardTest.java +++ b/src/test/java/domain/card/CardTest.java @@ -32,5 +32,4 @@ void cardAceTest() { assertThat(baseScore).isEqualTo(1); } - } diff --git a/src/test/java/domain/card/DeckTest.java b/src/test/java/domain/card/DeckTest.java index 3846aebaf54..66b8daef325 100644 --- a/src/test/java/domain/card/DeckTest.java +++ b/src/test/java/domain/card/DeckTest.java @@ -21,7 +21,7 @@ void createCardsTest() { @DisplayName("초기 카드는 두 장씩 나누어준다.") void dealFirstHandCardsTest() { Deck deck = new Deck(); - List cards = deck.dealFirstHandCards(); + List cards = deck.drawInitialCards(); assertThat(cards.size()).isEqualTo(2); } diff --git a/src/test/java/domain/participant/DealerTest.java b/src/test/java/domain/participant/DealerTest.java index eb53abafd0c..326c285eac4 100644 --- a/src/test/java/domain/participant/DealerTest.java +++ b/src/test/java/domain/participant/DealerTest.java @@ -18,16 +18,36 @@ void receiveCardTest() { // Given List cards = List.of( new Card(CardNumber.EIGHT, CardShape.CLUB), - new Card(CardNumber.FOUR, CardShape.CLUB) + new Card(CardNumber.SIX, CardShape.CLUB) ); - HandCards handCards = new HandCards(cards); - Dealer dealer = new Dealer(handCards); + Dealer dealer = new Dealer(); + dealer.drawInitialCards(cards); // When boolean isReceiveCard = dealer.isReceiveCard(); // Then assertThat(isReceiveCard).isTrue(); } + + @Test + @DisplayName("딜러는 점수의 합이 17 이상이라면 카드를 받지 않는다..") + void notReceiveCardTest() { + // Given + List cards = List.of( + new Card(CardNumber.SIX, CardShape.CLUB), + new Card(CardNumber.ACE, CardShape.CLUB) + ); + + Dealer dealer = new Dealer(); + + dealer.drawInitialCards(cards); + // When + boolean isReceiveCard = dealer.isReceiveCard(); + + // Then + assertThat(isReceiveCard).isFalse(); + } } + diff --git a/src/test/java/domain/participant/HandCardsTest.java b/src/test/java/domain/participant/HandCardsTest.java index a6411a541d5..d959ac909b9 100644 --- a/src/test/java/domain/participant/HandCardsTest.java +++ b/src/test/java/domain/participant/HandCardsTest.java @@ -35,7 +35,7 @@ void calculateScoreTest() { new Card(CardNumber.FOUR, CardShape.CLUB) ); - HandCards handCards = new HandCards(cards); + HandCards handCards = makeHandCards(cards); // When int score = handCards.calculateScore(); @@ -54,8 +54,7 @@ void judgeAceAsOneTest() { new Card(CardNumber.ACE, CardShape.CLUB) ); - HandCards handCards = new HandCards(cards); - + HandCards handCards = makeHandCards(cards); // When int score = handCards.calculateScore(); @@ -72,7 +71,7 @@ void judgeAceAsElevenTest() { new Card(CardNumber.ACE, CardShape.CLUB) ); - HandCards handCards = new HandCards(cards); + HandCards handCards = makeHandCards(cards); // When int score = handCards.calculateScore(); @@ -89,9 +88,9 @@ void judgeManyAceTest() { new Card(CardNumber.EIGHT, CardShape.CLUB), new Card(CardNumber.ACE, CardShape.CLUB), new Card(CardNumber.ACE, CardShape.HEART) - ); - HandCards handCards = new HandCards(cards); + ); + HandCards handCards = makeHandCards(cards); // When int score = handCards.calculateScore(); @@ -99,6 +98,7 @@ void judgeManyAceTest() { assertThat(score).isEqualTo(20); } + @Test @DisplayName("21을 초과하면 버스트이다.") void judgeBustTest() { @@ -108,7 +108,7 @@ void judgeBustTest() { new Card(CardNumber.FOUR, CardShape.CLUB), new Card(CardNumber.KING, CardShape.HEART) ); - HandCards handCards = new HandCards(cards); + HandCards handCards = makeHandCards(cards); // When boolean isBust = handCards.isBust(); @@ -125,7 +125,7 @@ void notBustTest() { new Card(CardNumber.JACK, CardShape.CLUB), new Card(CardNumber.KING, CardShape.HEART) ); - HandCards handCards = new HandCards(cards); + HandCards handCards = makeHandCards(cards); // When boolean isBust = handCards.isBust(); @@ -133,4 +133,53 @@ void notBustTest() { // Then assertThat(isBust).isFalse(); } -} \ No newline at end of file + + @Test + @DisplayName("블랙잭인지 확인한다.") + void blackJackTest() { + // Given + List cards = List.of( + new Card(CardNumber.ACE, CardShape.CLUB), + new Card(CardNumber.KING, CardShape.HEART) + ); + HandCards handCards = makeHandCards(cards); + + // When + boolean isBlackJack = handCards.isBlackJack(); + + // Then + assertThat(isBlackJack).isTrue(); + } + + @Test + @DisplayName("합이 21이더라도 카드가 2장이 아닌 경우 블랙잭이 아니다.") + void blackJackScoreButNotBlackJackTest() { + // Given + List cards = List.of( + new Card(CardNumber.SEVEN, CardShape.CLUB), + new Card(CardNumber.KING, CardShape.HEART), + new Card(CardNumber.FOUR, CardShape.HEART) + ); + + HandCards handCards = makeHandCards(cards); + + // When + boolean isBlackJack = handCards.isBlackJack(); + + // Then + assertThat(isBlackJack).isFalse(); + } + + + + private HandCards makeHandCards(List cards) { + HandCards handCards = new HandCards(); + handCards.receiveInitialCards(cards.subList(0, 2)); + + List hitCards = cards.subList(2, cards.size()); + for (Card hitCard : hitCards) { + handCards.receiveHitCard(hitCard); + } + return handCards; + } +} diff --git a/src/test/java/domain/participant/PlayerTest.java b/src/test/java/domain/participant/PlayerTest.java new file mode 100644 index 00000000000..7966b5b782d --- /dev/null +++ b/src/test/java/domain/participant/PlayerTest.java @@ -0,0 +1,18 @@ +package domain.participant; + +import domain.betting.Money; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PlayerTest { + @Test + @DisplayName("플레이어는 게임이 시작 전 베팅을 진행한다.") + void bettingTest() { + Player player = new Player(new Name("플레이어1")); + player.bettingMoney(new Money(1000)); + Money money = player.getBettingMoney(); + + Assertions.assertThat(money).isEqualTo(new Money(1000)); + } +} diff --git a/src/test/java/domain/participant/PlayersTest.java b/src/test/java/domain/participant/PlayersTest.java new file mode 100644 index 00000000000..72598ca4f7a --- /dev/null +++ b/src/test/java/domain/participant/PlayersTest.java @@ -0,0 +1,38 @@ +package domain.participant; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + + +class PlayersTest { + @Test + @DisplayName("중복된 이름입력에 대해 입력했을 때 예외를 반환한다.") + void testSuccessParseCase() { + String inputNames = "pobi, woni"; + Players players = Players.fromString(inputNames); + + List playerNames = players.getPlayerNames(); + + assertThat(playerNames).isEqualTo(List.of("pobi", "woni")); + } + + + @Test + @DisplayName("중복된 이름입력에 대해 입력했을 때 예외를 반환한다.") + void testDuplicateNameParseCase() { + String inputNames = "pobi, pobi"; + assertThrows(IllegalArgumentException.class, () -> Players.fromString(inputNames)); + } + + @Test + @DisplayName("플레이어의 수는 1명 이상 8명 이하여야 한다.") + void testPlayerCountsParseCase() { + String inputNames = "유저1, 유저2, 유저3, 유저4, 유저5, 유저6, 유저7, 유저8, 유저9"; + assertThrows(IllegalArgumentException.class, () -> Players.fromString(inputNames)); + } +} diff --git a/src/test/java/domain/status/FinishedTest.java b/src/test/java/domain/status/FinishedTest.java new file mode 100644 index 00000000000..129a6cf71b1 --- /dev/null +++ b/src/test/java/domain/status/FinishedTest.java @@ -0,0 +1,41 @@ +package domain.status; + +import domain.card.Card; +import domain.card.CardNumber; +import domain.card.CardShape; +import domain.participant.HandCards; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +class FinishedTest { + @Test + @DisplayName("이미 종료된 상태(Stay)에서 시작 카드를 뽑는 경우 예외가 발생한다.") + void finishedDoNotReceiveInitCards() { + HandCards cards = new HandCards(); + List initHands = List.of(new Card(CardNumber.EIGHT, CardShape.CLUB), new Card(CardNumber.ACE, CardShape.DIAMOND)); + Status stayStatus = new Stay(cards); + Assertions.assertThrows(IllegalStateException.class, () -> stayStatus.drawInitialCards(initHands)); + } + + @Test + @DisplayName("이미 종료된 상태(Stay)에서 카드를 뽑으려 하면 예외가 발생한다.") + void finishedDoNotDraw() { + HandCards cards = new HandCards(); + Status stayStatus = new Stay(cards); + Card newCard = new Card(CardNumber.TWO, CardShape.SPADE); + + Assertions.assertThrows(IllegalStateException.class, () -> stayStatus.draw(newCard)); + } + + @Test + @DisplayName("이미 종료된 상태(Stay)에서 또 스테이를 하면 예외가 발생한다.") + void finishedDoNotStay() { + HandCards cards = new HandCards(); + Status stayStatus = new Stay(cards); + Assertions.assertThrows(IllegalStateException.class, stayStatus::stay); + } + +} diff --git a/src/test/java/domain/status/RunningTest.java b/src/test/java/domain/status/RunningTest.java new file mode 100644 index 00000000000..4bbcc24d893 --- /dev/null +++ b/src/test/java/domain/status/RunningTest.java @@ -0,0 +1,62 @@ +package domain.status; + +import domain.card.Card; +import domain.card.CardNumber; +import domain.card.CardShape; +import domain.participant.HandCards; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class RunningTest { + @Test + @DisplayName("진행 중인(Hit)에서 시작 카드를 뽑는 경우 예외가 발생한다.") + void hitDoNotReceiveInitCards() { + HandCards cards = new HandCards(); + List initHands = List.of(new Card(CardNumber.EIGHT, CardShape.CLUB), new Card(CardNumber.ACE, CardShape.DIAMOND)); + Status hitStatus = new Hit(cards); + Assertions.assertThrows(IllegalStateException.class, () -> hitStatus.drawInitialCards(initHands)); + } + + @Test + @DisplayName("스테이를 하면, Stay상태가 반환된다.") + void stayTest() { + HandCards cards = new HandCards(); + Status hitStatus = new Hit(cards); + Status stay = hitStatus.stay(); + assertThat(stay).isInstanceOf(Stay.class); + } + + @Test + @DisplayName("히트를 한 후 버스트라면 Bust상태가 반환된다.") + void bustTest() { + HandCards cards = new HandCards(); + + List initHands = List.of(new Card(CardNumber.EIGHT, CardShape.CLUB), new Card(CardNumber.TEN, CardShape.DIAMOND)); + Status startStatus = new Start(cards); + + Status initHandStatus = startStatus.drawInitialCards(initHands); + Status bustStatus = initHandStatus.draw(new Card(CardNumber.EIGHT, CardShape.HEART)); + + assertThat(bustStatus).isInstanceOf(Bust.class); + } + + + @Test + @DisplayName("히트를 하고 버스트가 아니라면 Hit상태가 반환된다.") + void hitTest() { + HandCards cards = new HandCards(); + + List initHands = List.of(new Card(CardNumber.EIGHT, CardShape.CLUB), new Card(CardNumber.ACE, CardShape.DIAMOND)); + Status startStatus = new Start(cards); + + Status initHandStatus = startStatus.drawInitialCards(initHands); + Status hitStatus = initHandStatus.draw(new Card(CardNumber.EIGHT, CardShape.HEART)); + + assertThat(hitStatus).isInstanceOf(Hit.class); + } +} diff --git a/src/test/java/domain/status/StartTest.java b/src/test/java/domain/status/StartTest.java new file mode 100644 index 00000000000..dad35bcf601 --- /dev/null +++ b/src/test/java/domain/status/StartTest.java @@ -0,0 +1,58 @@ +package domain.status; + +import domain.card.Card; +import domain.card.CardNumber; +import domain.card.CardShape; +import domain.participant.HandCards; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class StartTest { + + @Test + @DisplayName("시작 카드를 뽑는 경우(블랙잭이 아닐 때) Hit를 반환한다.") + void startNotBlackJackTest() { + HandCards cards = new HandCards(); + List initHands = List.of(new Card(CardNumber.EIGHT, CardShape.CLUB), new Card(CardNumber.ACE, CardShape.DIAMOND)); + Status startStatus = new Start(cards); + Status hit = startStatus.drawInitialCards(initHands); + + assertThat(hit).isInstanceOf(Hit.class); + } + + @Test + @DisplayName("블랙잭을 뽑을 때 BlackJack를 반환한다.") + void startBlackJackTest() { + HandCards cards = new HandCards(); + List initHands = List.of(new Card(CardNumber.KING, CardShape.CLUB), new Card(CardNumber.ACE, CardShape.DIAMOND)); + Status startStatus = new Start(cards); + Status blackJack = startStatus.drawInitialCards(initHands); + + assertThat(blackJack).isInstanceOf(Blackjack.class); + } + + + @Test + @DisplayName("시작에서 카드를 뽑으려 하면 예외가 발생한다.") + void finishedDoNotDraw() { + HandCards cards = new HandCards(); + Status startStatus = new Start(cards); + Card newCard = new Card(CardNumber.TWO, CardShape.SPADE); + + Assertions.assertThrows(IllegalStateException.class, () -> startStatus.draw(newCard)); + } + + @Test + @DisplayName("시작에서 스테이를 하면 예외가 발생한다.") + void finishedDoNotStay() { + HandCards cards = new HandCards(); + Status startStatus = new Start(cards); + Assertions.assertThrows(IllegalStateException.class, startStatus::stay); + } + +} diff --git a/src/test/java/util/InputBettingParserTest.java b/src/test/java/util/InputBettingParserTest.java new file mode 100644 index 00000000000..b9ccbcc2aaf --- /dev/null +++ b/src/test/java/util/InputBettingParserTest.java @@ -0,0 +1,45 @@ +package util; + +import domain.betting.Money; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class InputBettingParserTest { + @Test + @DisplayName("정상 입력에 대해서는 정상적으로 파싱한다.") + void testSuccessCase() { + Money money = InputBettingParser.parseBettingMoney("1000"); + assertThat(money).isEqualTo(new Money(1000)); + } + + @Test + @DisplayName("음수 입력에 대해서는 오류를 반환한다.") + void testNegativeNumberParse() { + org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, + () -> InputBettingParser.parseBettingMoney("-1000")); + } + + @Test + @DisplayName("0 입력에 대해서는 오류를 반환한다.") + void testZeroParse() { + org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, + () -> InputBettingParser.parseBettingMoney("0")); + } + + @Test + @DisplayName("공백이 숫자 앞뒤로 붙은 경우 정상적으로 파싱한다.") + void trimParse() { + Money money = InputBettingParser.parseBettingMoney(" 1000"); + assertThat(money).isEqualTo(new Money(1000)); + } + + @Test + @DisplayName("숫자가 아닌 다른 문자가 섞였을 때는 오류를 반환한다.") + void notNumberParse() { + org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, + () -> InputBettingParser.parseBettingMoney("1000원")); + } +} diff --git a/src/test/java/util/InputNameParserTest.java b/src/test/java/util/InputNameParserTest.java index a054fcbd46c..d752dc9bc71 100644 --- a/src/test/java/util/InputNameParserTest.java +++ b/src/test/java/util/InputNameParserTest.java @@ -1,5 +1,6 @@ package util; +import domain.participant.Name; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -21,12 +22,8 @@ void testNameParseCase(String input, List expected) { static Stream inputNameProvider() { return Stream.of( - Arguments.of("pobi, woni", List.of("pobi", "woni")), - Arguments.of("jun, jason", List.of("jun", "jason")) + Arguments.of("pobi, woni", List.of(new Name("pobi"), new Name("woni"))), + Arguments.of("jun, jason", List.of(new Name("jun"), new Name("jason"))) ); } - - - - } diff --git a/src/test/java/util/InputNameValidatorTest.java b/src/test/java/util/InputNameValidatorTest.java deleted file mode 100644 index 3859d05ae13..00000000000 --- a/src/test/java/util/InputNameValidatorTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package util; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import java.util.List; - - -class InputNameValidatorTest { - - @Test - @DisplayName("중복된 이름입력에 대한 테스트 케이스에 대해서는 예외를 반환한다.") - void testDuplicateNameParseCase() { - List inputNames = List.of("pobi", "pobi"); - Assertions.assertThrows(IllegalArgumentException.class, () -> InputNameValidator.validateInputNames(inputNames)); - } - - - @Test - @DisplayName("공백 입력에 대한 테스트 케이스에 대해서는 예외를 반환한다.") - void testEmptyNameParseCase() { - List inputNames = List.of("pobi", "", "woni"); - Assertions.assertThrows(IllegalArgumentException.class, () -> InputNameValidator.validateInputNames(inputNames)); - } - - @Test - @DisplayName("플레이어의 수는 1명 이상 8명 이하여야 한다.") - void testPlayerCountsParseCase() { - List inputNames = List.of("유저1", "유저2", "유저3", "유저4", "유저5", "유저6", "유저7", "유저8", "유저9"); - Assertions.assertThrows(IllegalArgumentException.class, () -> InputNameValidator.validateInputNames(inputNames)); - } - - -}