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));
- }
-
-
-}