diff --git a/README.md b/README.md
index 16874b3f2bd..3dba08911fb 100644
--- a/README.md
+++ b/README.md
@@ -2,62 +2,87 @@
자바 언어를 활용하여 구현한 객체 지향 블랙잭 게임 프로젝트입니다.
----
-
## 🚀 프로젝트 개요
-이 프로젝트는 **MVC(Model-View-Controller) 패턴**을 기반으로 설계되었으며, 블랙잭 게임의 복잡한 도메인 로직을 객체 간의 협력으로 해결하는 데 중점을 두었습니다.
-리뷰어분들이 전체적인 도메인 설계를 쉽게 이해할 수 있도록 구조와 규칙을 정리했습니다.
+
+이 프로젝트는 MVC(Model–View–Controller) 패턴을 기반으로 설계되었습니다.
+
+* Model : 블랙잭 게임의 핵심 도메인 로직
+* View : 사용자 입출력 처리
+* Controller : 게임 흐름 제어s
### ⚖️ 핵심 도메인 규칙
-1. **점수 계산**:
+
+1. **점수 계산**
- K, Q, J는 모두 10으로 계산합니다.
- 에이스(Ace)는 기본 11로 계산하되, 총합이 21을 초과하면 1로 자동 전환하여 최적의 점수를 산출합니다.
-2. **승패 판정**:
- - 21을 초과(Burst)한 참가자는 패배합니다.
+2. **승패 판정**
+ - 21을 초과하면 Burst
- 딜러가 Burst되면 남은 플레이어는 모두 승리합니다.
- 딜러와 점수가 같을 경우, 현재 로직상 딜러가 승리하는 규칙을 따릅니다 (무승부 없음).
-
----
+ - 딜러 카드 합이 **16 이하이면 카드 추가**
+ - 딜러 카드 합이 **17 이상이면 카드 추가 불가**
+3. **배팅 규칙**
+ - 플레이어는 게임 시작 시 **배팅 금액을 입력**
+ - **Burst 발생 시 배팅 금액을 모두 잃음**
+ - **블랙잭(처음 두 장이 21)**
+ → 배팅 금액의 **1.5배 수익**
+ - **딜러와 플레이어 모두 블랙잭**
+ → 배팅 금액 **환불**
+ - **딜러 Burst**
+ → 남아있는 플레이어는 **배팅 금액만큼 승리**
## 📋 기능 요구사항
### 1. 게임 준비 단계
-- 참가자 이름을 쉼표 기준으로 입력받는다.
-- 이름 입력값의 앞 뒤 공백을 제거한다. (trim)
-- **[Exception]** 이름을 빈칸으로 입력할 경우 예외를 던진다.
-- **[Exception]** 이름은 2글자 이상 5글자 이하만 허용한다.
-- **[Exception]** 중복되는 이름은 허용하지 않는다.
-- 52장의 중복 없는 트럼프 카드 덱을 생성한다.
-- 트럼프 카드를 무작위로 섞는다.
-- 줄어든 카드를 관리하는 덱이 있어야 한다.
-- 참가자에게 2장의 카드를 나눠준다.
-- 나눠 준 카드를 출력한다.
+
+- [x] 참가자 이름을 쉼표 기준으로 입력받는다.
+- [x] 이름 입력값의 앞뒤 공백을 제거한다. (`trim`)
+- [x] 이름이 빈 값일 경우 예외를 발생시킨다.
+- [x] 이름은 **2자 이상 5자 이하**만 허용한다.
+- [x] 중복되는 이름은 허용하지 않는다.
+
+- [x] 52장의 중복 없는 트럼프 카드 덱을 생성한다.
+- [x] 트럼프 카드를 무작위로 섞는다.
+- [x] 카드를 뽑을 때마다 덱에서 제거된다.
+
+- [x] 참가자에게 **배팅 금액을 입력받는다**
+ ```shell
+ pobi의 배팅 금액은?
+ 10000
+ ```
+- [X] 배팅 금액은 **음수가 될 수 없다**
+
+- [x] 참가자에게 **2장의 카드를 나눠준다**
+- [x] 나눠준 카드를 출력한다
### 2. 게임 진행 단계
-- 참가자에게 카드 속행 여부를 출력한다.
-- 참가자에게 카드 속행 여부를 입력받는다. (`y` / `n`)
-- **[Exception]** `y`, `n` 이외의 입력이 들어올 경우 예외를 던진다.
-- 카드를 새로 받을 경우 새로 뽑은 카드를 합쳐 출력한다.
-- 두 장의 카드 합계가 21을 넘지 않을 경우, 원한다면 얼마든지 카드를 계속 뽑을 수 있다.
-- 참가자의 모든 턴이 끝난 후, 딜러의 카드 합이 **16 이하**이면 1장을 더 받는다.
-- 딜러가 한 장 더 받았다는 결과를 출력한다.
-- 딜러의 카드 합이 **17 이상**이면 추가로 받을 수 없다.
-### 3. 결과 집계 단계
-- 딜러 → 참가자 순으로 최종 결과를 출력한다.
- - 보유 카드 목록과 카드 합계를 함께 출력한다.
-- 승패 통계를 함께 출력한다.
+- [x] 참가자에게 카드 추가 여부를 출력한다
+- [x] 참가자에게 카드 추가 여부를 입력받는다 (`y / n`)
+- [x] `y`, `n` 이외의 입력이 들어오면 예외를 발생시킨다
+
+- [x] 카드를 새로 받을 경우 기존 카드와 함께 출력한다
+- [x] 카드 합이 21 이하이면 원하는 만큼 카드를 받을 수 있다
----
+- [x] 모든 플레이어 턴 종료 후 딜러 턴 진행
+- [x] 딜러 카드 합이 **16 이하이면 카드 1장 추가**
+- [x] 딜러 카드 합이 **17 이상이면 카드 추가 불가**
-## ✨ 설계 의도 및 특징
+- [x] 플레이어가 추가로 카드를 뽑아 21을 초과할 경우(Burst될 경우) 배팅 금액을 모두 잃는다.
+- [x] 처음 두 장의 카드 합이 21일 경우 블랙잭이되면 베팅 금액의 1.5배를 딜러에게 받는다.
+- [x] 딜러와 플레이어가 모두 동시에 블랙잭인 경우 플레이어는 베팅한 금액을 돌려받는다.
+- [x] 딜러가 21을 초과하면 그 시점까지 남아있던 플레이어들은 승리해 베팅 금액을 받는다.
-### 1. MVC 패턴 및 DTO 활용
-- `Controller`가 도메인 모델과 뷰 사이의 흐름을 제어합니다.
-- `ParticipantCardsDto` (Record)를 활용하여 도메인 객체의 캡슐화를 깨뜨리지 않고 UI 계층에 데이터를 안전하게 전달합니다.
+### 3. 결과 집계 단계
-### 2. 원시값 포장 (Value Object)
-- `Name`, `Shape`, `Number` 등 도메인에서 중요한 의미를 갖는 값들을 객체로 포장하여 유효성 검사를 수행하고 자기 자신의 상태를 책임지게 했습니다.
+- [x] 딜러 → 플레이어 순으로 카드와 점수를 출력한다
+- [x] 각 참가자의 승패를 판정한다
+- [x] 배팅 금액을 기준으로 **최종 수익을 계산한다**
+- [x] 참가자들의 최종 수익을 출력한다
+ ```shell
+ ## 최종 수익
+ 딜러: 10000
+ pobi: 10000
+ jason: -20000
+ ```
-### 3. 전략적인 테스트 코드
-- `CardsTest`, `DeckTest`, `ResultTest` 등을 통해 핵심 도메인 로직(점수 계산, 승패 판정)의 신뢰성을 확보했습니다.
diff --git a/gradlew.bat b/gradlew.bat
index 9b42019c791..9d21a21834d 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,94 +1,94 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-@rem SPDX-License-Identifier: Apache-2.0
-@rem
-
-@if "%DEBUG%"=="" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%"=="" set DIRNAME=.
-@rem This is normally unused
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if %ERRORLEVEL% equ 0 goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if %ERRORLEVEL% equ 0 goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-set EXIT_CODE=%ERRORLEVEL%
-if %EXIT_CODE% equ 0 set EXIT_CODE=1
-if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
-exit /b %EXIT_CODE%
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/src/main/java/Application.java b/src/main/java/Application.java
index 27474ebbed1..863d606dc28 100644
--- a/src/main/java/Application.java
+++ b/src/main/java/Application.java
@@ -4,6 +4,5 @@ public class Application {
public static void main(String[] args) {
BlackJackGameController blackJackGameController = new BlackJackGameController();
blackJackGameController.run();
-
}
}
diff --git a/src/main/java/controller/BlackJackGameController.java b/src/main/java/controller/BlackJackGameController.java
index 7ff9d7e527b..2f4210cfcd3 100644
--- a/src/main/java/controller/BlackJackGameController.java
+++ b/src/main/java/controller/BlackJackGameController.java
@@ -1,7 +1,19 @@
package controller;
-import domain.*;
+import domain.betting.BettingAmount;
+import domain.betting.BettingAmounts;
+import domain.betting.CalculateProfit;
+import domain.betting.Revenue;
+import domain.game.GameManager;
+import domain.game.GameResultManager;
+import domain.participant.Dealer;
+import domain.participant.Name;
+import domain.participant.Participant;
+import domain.participant.Player;
+import domain.participant.Players;
import dto.ParticipantCardsDto;
+import dto.ParticipantRevenueDto;
+import java.util.HashMap;
import view.InputView;
import view.OutputView;
@@ -10,6 +22,7 @@
import java.util.Map;
import static view.OutputView.printCards;
+import static view.OutputView.printFinalCards;
public class BlackJackGameController {
@@ -17,26 +30,44 @@ public BlackJackGameController() {
}
public void run() {
- List players = initPlayer();
+ Players players = initPlayer();
GameManager gameManager = new GameManager(players);
-
List playersNames = getPlayerNames(players);
+ BettingAmounts bettingAmounts = initBettingManager(players);
OutputView.printGameInitialMessage(playersNames);
-
gameManager.distributeInitialCards();
- printParticipantCards(gameManager);
-
+ printParticipantCards(gameManager.getDealer(), players);
playGame(players, gameManager);
+ CalculateProfit calculateProfit = new CalculateProfit(bettingAmounts);
+ GameResultManager gameResultManager =
+ new GameResultManager(calculateProfit, players, gameManager.getDealer());
+ Map profits = gameResultManager.getParticipantsProfit();
+ List revenueDtos = toParticipantRevenueDtos(profits);
+ endGame(gameManager, players, revenueDtos);
+ }
- Map gameResult = gameManager.getGameResult();
-
- endGame(gameManager, players, gameResult);
+ private BettingAmounts initBettingManager(Players players) {
+ Map bettingAmounts = new HashMap<>();
+ players.forEach(player -> {
+ int amount = InputView.askBettingAmount(player.getName().getName());
+ bettingAmounts.put(player.getName(), new BettingAmount(amount));
+ });
+ return new BettingAmounts(bettingAmounts);
}
- private void playGame(List players, GameManager gameManager) {
- for (Player player : players) {
- playGameWithPlayer(player, gameManager);
+ private List toParticipantRevenueDtos(Map profits) {
+ List revenueDtos = new ArrayList<>();
+ for (Map.Entry entry : profits.entrySet()) {
+ revenueDtos.add(new ParticipantRevenueDto(
+ entry.getKey().getName(),
+ entry.getValue().getMoney()
+ ));
}
+ return revenueDtos;
+ }
+
+ private void playGame(Players players, GameManager gameManager) {
+ players.forEach(player -> playGameWithPlayer(player, gameManager));
playGameWithDealer(gameManager);
}
@@ -56,45 +87,42 @@ public void playGameWithPlayer(Player player, GameManager gameManager) {
break;
}
gameManager.drawCardTo(player);
- printCards(player.getParticipantCardsDto());
+ printCards(toParticipantCardsDto(player));
}
}
private boolean isStopGame(Player player) {
- String response = InputView.askContinue(player.getName());
+ String response = InputView.askContinue(player.getName().getName());
if (response.equals("n")) {
- printCards(player.getParticipantCardsDto());
+ printCards(toParticipantCardsDto(player));
return true;
}
return false;
}
- private void printParticipantCards(GameManager gameManager) {
- ParticipantCardsDto dealerDto = gameManager.getDealerDto();
- OutputView.printCards(dealerDto);
- List playerDtos = gameManager.getPlayerDtos();
- for (ParticipantCardsDto playerDto : playerDtos) {
- OutputView.printCards(playerDto);
- }
+ private void printParticipantCards(Dealer dealer, Players players) {
+ OutputView.printCards(toParticipantCardsDto(dealer));
+ players.forEach(player -> OutputView.printCards(toParticipantCardsDto(player)));
}
- private void endGame(GameManager gameManager, List players, Map gameResult) {
- OutputView.printFinalCards(gameManager.getDealerDto());
+ private void endGame(GameManager gameManager, Players players, List participantRevenueDtos) {
+ OutputView.printFinalCards(toParticipantCardsDto(gameManager.getDealer()));
printFinalScores(players);
- OutputView.printGameResult(gameResult);
+ OutputView.printParticipantRevenues(participantRevenueDtos);
}
- private void printFinalScores(List players) {
- for (Player player : players) {
- OutputView.printFinalCards(player.getParticipantCardsDto());
- }
+ private void printFinalScores(Players players) {
+ players.forEach(player -> printFinalCards(toParticipantCardsDto(player)));
}
- private List getPlayerNames(List players) {
- return players.stream().map(Participant::getName).toList();
+ private List getPlayerNames(Players players) {
+ List playerNames = new ArrayList<>();
+ players.forEach(player -> playerNames.add(player.getName().getName()));
+ return playerNames;
+
}
- private List initPlayer() {
+ private Players initPlayer() {
List players = new ArrayList<>();
List playerNames = getPlayerNames();
@@ -102,10 +130,18 @@ private List initPlayer() {
Name playerName = new Name(name);
players.add(new Player(playerName));
}
- return players;
+ return new Players(players);
}
private List getPlayerNames() {
return InputView.askPlayerNames();
}
+
+ private ParticipantCardsDto toParticipantCardsDto(Participant participant) {
+ return new ParticipantCardsDto(
+ participant.getName().getName(),
+ participant.getCardsInfo(),
+ participant.getScore()
+ );
+ }
}
diff --git a/src/main/java/domain/Cards.java b/src/main/java/domain/Cards.java
deleted file mode 100644
index db0a7b6f230..00000000000
--- a/src/main/java/domain/Cards.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package domain;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class Cards {
- private final List cards;
- private int changeAvailableAceCount;
-
- public Cards(List cards) {
- this.cards = new ArrayList<>(cards);
- changeAvailableAceCount = 0;
- }
-
- public Integer getSize() {
- return this.cards.size();
- }
-
- public List getCards() {
- return cards.stream()
- .toList();
- }
-
- public List getCardsInfo() {
- return cards.stream()
- .map(Card::getCardInfo)
- .toList();
- }
-
- public Card removeFirst() {
- return cards.removeFirst();
- }
-
- public int calculateScore() {
- int sum = 0;
- for (Card card : cards) {
- sum += card.getScore();
- }
-
- int availableAceCount = changeAvailableAceCount;
- while (availableAceCount > 0 && sum > 21) {
- sum -= 10;
- availableAceCount -= 1;
- }
-
- return sum;
- }
-
- public void addCard(Card card) {
- if (card.isAce()) {
- changeAvailableAceCount += 1;
- }
- cards.add(card);
- }
-}
diff --git a/src/main/java/domain/GameManager.java b/src/main/java/domain/GameManager.java
deleted file mode 100644
index 83a40c19128..00000000000
--- a/src/main/java/domain/GameManager.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package domain;
-
-import dto.ParticipantCardsDto;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class GameManager {
- private static final int MAX_PLAYER = 8;
-
- private final Dealer dealer;
- private final List players;
- private final Deck deck;
-
- public GameManager(List players) {
- this.dealer = initDealer();
- this.players = players;
- this.deck = new Deck();
- }
-
- public Dealer getDealer() {
- return dealer;
- }
-
- private Dealer initDealer() {
- Name name = new Name("딜러");
- Dealer dealer = new Dealer(name);
- return dealer;
- }
-
- public void distributeInitialCards() {
- distributeCardToDealer(dealer);
- distributeCardToPlayers(players);
- }
-
- private void distributeCardToDealer(Dealer dealer) {
- distributeInitialCards(dealer);
- }
-
- private void distributeCardToPlayers(List players) {
- for (Player player : players) {
- distributeInitialCards(player);
- }
- }
-
- private void distributeInitialCards(Participant participant) {
- drawCardTo(participant);
- drawCardTo(participant);
- }
-
- public ParticipantCardsDto getDealerDto() {
- return dealer.getParticipantCardsDto();
- }
-
- public List getPlayerDtos() {
- List participantCardsDtos = new ArrayList<>();
- for (Player player : players) {
- participantCardsDtos.add(player.getParticipantCardsDto());
- }
-
- return participantCardsDtos;
- }
-
- public void drawCardTo(Participant participant) {
- Card card = deck.drawCard();
- participant.receiveCard(card);
- }
-
- public Map getGameResult() {
- Map gameResult = new HashMap<>();
- for (Player player : players) {
- GameResult result = player.judgeResult(this.dealer);
- gameResult.put(player.getName(), result);
- }
- return gameResult;
- }
-
- public static void validatePlayersNumber(List playerNames) {
- validateMinimumPlayers(playerNames);
- validateMaximumPlayers(playerNames);
- validateDuplicateName(playerNames);
- }
-
- private static void validateMaximumPlayers(List playerNames) {
- if (playerNames.size() > MAX_PLAYER) {
- throw new IllegalArgumentException("플레이어의 수는 8명을 초과할 수 없습니다.");
- }
- }
-
- private static void validateMinimumPlayers(List playerNames) {
- if (playerNames.isEmpty()) {
- throw new IllegalArgumentException("플레이어의 수는 1명 이상이어야 합니다.");
- }
- }
-
- private static void validateDuplicateName(List names) {
- Set uniqueNames = new HashSet<>(names);
- if (names.size() != uniqueNames.size()) {
- throw new IllegalArgumentException("중복된 참가자 이름이 있습니다!");
- }
- }
-}
diff --git a/src/main/java/domain/Participant.java b/src/main/java/domain/Participant.java
deleted file mode 100644
index 52dd61c1bf1..00000000000
--- a/src/main/java/domain/Participant.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package domain;
-
-import dto.ParticipantCardsDto;
-
-import java.util.ArrayList;
-
-public abstract class Participant {
- private static final int BURST_THRESHOLD = 21;
- private final Name name;
- protected final Cards cards;
-
- public Participant(Name name) {
- this.name = name;
- cards = new Cards(new ArrayList<>());
- }
-
- public void receiveCard(Card card) {
- cards.addCard(card);
- }
-
- public abstract boolean isContinueGame();
-
- public String getName() {
- return name.getName();
- }
-
- public int getScore() {
- return cards.calculateScore();
- }
-
- public boolean isBust() {
- if (this.getScore() > BURST_THRESHOLD) {
- return true;
- }
- return false;
- }
-
- public ParticipantCardsDto getParticipantCardsDto() {
- return new ParticipantCardsDto(name.getName(), cards.getCardsInfo(), getScore());
- }
-}
diff --git a/src/main/java/domain/betting/BettingAmount.java b/src/main/java/domain/betting/BettingAmount.java
new file mode 100644
index 00000000000..5cff42e143e
--- /dev/null
+++ b/src/main/java/domain/betting/BettingAmount.java
@@ -0,0 +1,36 @@
+package domain.betting;
+
+import java.util.Objects;
+
+public class BettingAmount {
+ private final int money;
+
+ public BettingAmount(Integer bettingAmount) {
+ validateMinus(bettingAmount);
+ this.money = bettingAmount;
+ }
+
+ private static void validateMinus(Integer bettingAmount) {
+ if (bettingAmount < 0) {
+ throw new IllegalArgumentException("베팅 금액은 음수일 없습니다.");
+ }
+ }
+
+ public Integer getMoney() {
+ return money;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ BettingAmount that = (BettingAmount) o;
+ return Objects.equals(money, that.money);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(money);
+ }
+}
diff --git a/src/main/java/domain/betting/BettingAmounts.java b/src/main/java/domain/betting/BettingAmounts.java
new file mode 100644
index 00000000000..ac4a66b15cc
--- /dev/null
+++ b/src/main/java/domain/betting/BettingAmounts.java
@@ -0,0 +1,17 @@
+package domain.betting;
+
+import domain.participant.Name;
+import java.util.HashMap;
+import java.util.Map;
+
+public class BettingAmounts {
+ private final Map bettingAmounts;
+
+ public BettingAmounts(Map playerBettingAmountMap) {
+ this.bettingAmounts = new HashMap<>(playerBettingAmountMap);
+ }
+
+ public BettingAmount getAmount(Name name) {
+ return bettingAmounts.get(name);
+ }
+}
diff --git a/src/main/java/domain/betting/CalculateProfit.java b/src/main/java/domain/betting/CalculateProfit.java
new file mode 100644
index 00000000000..da7e2026eb7
--- /dev/null
+++ b/src/main/java/domain/betting/CalculateProfit.java
@@ -0,0 +1,31 @@
+package domain.betting;
+
+import domain.game.GameResult;
+import domain.participant.Name;
+
+public class CalculateProfit {
+ private static final double PROFIT_RATE = 1.5;
+ private final BettingAmounts bettingAmounts;
+
+ public CalculateProfit(BettingAmounts bettingAmounts) {
+ this.bettingAmounts = bettingAmounts;
+ }
+
+ public Revenue calculate(Name name, GameResult gameResult) {
+ BettingAmount amount = bettingAmounts.getAmount(name);
+ return calculateRevenue(gameResult, amount);
+ }
+
+ private static Revenue calculateRevenue(GameResult gameResult, BettingAmount amount) {
+ if (gameResult == GameResult.BLACKJACK) {
+ return new Revenue((int) (amount.getMoney() * PROFIT_RATE));
+ }
+ if (gameResult == GameResult.WIN) {
+ return new Revenue(amount.getMoney());
+ }
+ if (gameResult == GameResult.LOSE) {
+ return new Revenue(-amount.getMoney());
+ }
+ return new Revenue(0);
+ }
+}
diff --git a/src/main/java/domain/betting/Revenue.java b/src/main/java/domain/betting/Revenue.java
new file mode 100644
index 00000000000..d7c76f52838
--- /dev/null
+++ b/src/main/java/domain/betting/Revenue.java
@@ -0,0 +1,29 @@
+package domain.betting;
+
+import java.util.Objects;
+
+public class Revenue {
+ private final int money;
+
+ public Revenue(int money) {
+ this.money = money;
+ }
+
+ public int getMoney() {
+ return money;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Revenue revenue = (Revenue) o;
+ return money == revenue.money;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(money);
+ }
+}
diff --git a/src/main/java/domain/betting/Revenues.java b/src/main/java/domain/betting/Revenues.java
new file mode 100644
index 00000000000..c1ebfaa1062
--- /dev/null
+++ b/src/main/java/domain/betting/Revenues.java
@@ -0,0 +1,13 @@
+package domain.betting;
+
+import domain.participant.Name;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Revenues {
+ private final Map finalRevenue;
+
+ public Revenues(Map finalRevenue) {
+ this.finalRevenue = new HashMap<>(finalRevenue);
+ }
+}
diff --git a/src/main/java/domain/Card.java b/src/main/java/domain/card/Card.java
similarity index 95%
rename from src/main/java/domain/Card.java
rename to src/main/java/domain/card/Card.java
index baef574abbb..d5fc180c8f1 100644
--- a/src/main/java/domain/Card.java
+++ b/src/main/java/domain/card/Card.java
@@ -1,4 +1,4 @@
-package domain;
+package domain.card;
public class Card {
private final Shape shape;
diff --git a/src/main/java/domain/card/Cards.java b/src/main/java/domain/card/Cards.java
new file mode 100644
index 00000000000..6bd76436abe
--- /dev/null
+++ b/src/main/java/domain/card/Cards.java
@@ -0,0 +1,36 @@
+package domain.card;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Cards {
+ private final List cards;
+
+ public Cards(List cards) {
+ this.cards = new ArrayList<>(cards);
+ }
+
+ public int getSize() {
+ return cards.size();
+ }
+
+ public List getCardsInfo() {
+ return cards.stream()
+ .map(Card::getCardInfo)
+ .toList();
+ }
+
+ public Card removeFirst() {
+ return cards.removeFirst();
+ }
+
+ public int sumScore() {
+ return cards.stream()
+ .mapToInt(Card::getScore)
+ .sum();
+ }
+
+ public void add(Card card) {
+ cards.add(card);
+ }
+}
diff --git a/src/main/java/domain/Deck.java b/src/main/java/domain/card/Deck.java
similarity index 97%
rename from src/main/java/domain/Deck.java
rename to src/main/java/domain/card/Deck.java
index f7763694003..ee49ce2ae1b 100644
--- a/src/main/java/domain/Deck.java
+++ b/src/main/java/domain/card/Deck.java
@@ -1,4 +1,4 @@
-package domain;
+package domain.card;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/src/main/java/domain/Number.java b/src/main/java/domain/card/Number.java
similarity index 96%
rename from src/main/java/domain/Number.java
rename to src/main/java/domain/card/Number.java
index c07e5d803ed..6417764467c 100644
--- a/src/main/java/domain/Number.java
+++ b/src/main/java/domain/card/Number.java
@@ -1,4 +1,4 @@
-package domain;
+package domain.card;
public enum Number {
ACE(11, "A"),
diff --git a/src/main/java/domain/Shape.java b/src/main/java/domain/card/Shape.java
similarity index 94%
rename from src/main/java/domain/Shape.java
rename to src/main/java/domain/card/Shape.java
index 364bc0402fd..46cc1150a34 100644
--- a/src/main/java/domain/Shape.java
+++ b/src/main/java/domain/card/Shape.java
@@ -1,4 +1,4 @@
-package domain;
+package domain.card;
public enum Shape {
HEART("하트"),
diff --git a/src/main/java/domain/game/GameManager.java b/src/main/java/domain/game/GameManager.java
new file mode 100644
index 00000000000..e52eeb71a99
--- /dev/null
+++ b/src/main/java/domain/game/GameManager.java
@@ -0,0 +1,43 @@
+package domain.game;
+
+import domain.card.Card;
+import domain.participant.Dealer;
+import domain.card.Deck;
+import domain.participant.Participant;
+import domain.participant.Players;
+
+public class GameManager {
+
+ private final Dealer dealer;
+ private final Players players;
+ private final Deck deck;
+
+ public GameManager(Players players) {
+ this.dealer = new Dealer();
+ this.players = players;
+ this.deck = new Deck();
+ }
+
+ public Dealer getDealer() {
+ return dealer;
+ }
+
+ public void distributeInitialCards() {
+ distributeInitialCards(dealer);
+ distributeCardToPlayers(players);
+ }
+
+ private void distributeCardToPlayers(Players players) {
+ players.forEach(this::distributeInitialCards);
+ }
+
+ private void distributeInitialCards(Participant participant) {
+ drawCardTo(participant);
+ drawCardTo(participant);
+ }
+
+ public void drawCardTo(Participant participant) {
+ Card card = deck.drawCard();
+ participant.receiveCard(card);
+ }
+}
diff --git a/src/main/java/domain/GameResult.java b/src/main/java/domain/game/GameResult.java
similarity index 81%
rename from src/main/java/domain/GameResult.java
rename to src/main/java/domain/game/GameResult.java
index 853f9a75b29..da192c8bb5e 100644
--- a/src/main/java/domain/GameResult.java
+++ b/src/main/java/domain/game/GameResult.java
@@ -1,9 +1,10 @@
-package domain;
+package domain.game;
public enum GameResult {
WIN("승"),
LOSE("패"),
- DRAW("무");
+ DRAW("무"),
+ BLACKJACK("블랙잭");
private final String resultKoreanName;
diff --git a/src/main/java/domain/game/GameResultManager.java b/src/main/java/domain/game/GameResultManager.java
new file mode 100644
index 00000000000..c8f3ffd61e8
--- /dev/null
+++ b/src/main/java/domain/game/GameResultManager.java
@@ -0,0 +1,52 @@
+package domain.game;
+
+import domain.betting.CalculateProfit;
+import domain.betting.Revenue;
+import domain.participant.Dealer;
+import domain.participant.Name;
+import domain.participant.Players;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class GameResultManager {
+ private final CalculateProfit calculateProfit;
+ private final Players players;
+ private final Dealer dealer;
+
+ public GameResultManager(CalculateProfit calculateProfit, Players players, Dealer dealer) {
+ this.calculateProfit = calculateProfit;
+ this.players = players;
+ this.dealer = dealer;
+ }
+
+ public Map getGameResult() {
+ Map gameResult = new HashMap<>();
+ players.forEach(player -> {
+ GameResult result = player.judgeResult(dealer);
+ gameResult.put(player.getName().getName(), result);
+ });
+ return gameResult;
+ }
+
+ public LinkedHashMap getParticipantsProfit() {
+ LinkedHashMap finalRevenues = new LinkedHashMap<>();
+ players.forEach(player -> {
+ GameResult result = player.judgeResult(dealer);
+ Revenue revenue = calculateProfit.calculate(player.getName(), result);
+ finalRevenues.put(player.getName(), revenue);
+ });
+ finalRevenues.put(dealer.getName(), calculateDealerRevenue(finalRevenues));
+ return finalRevenues;
+ }
+
+ private Revenue calculateDealerRevenue(Map revenues) {
+ int totalPlayerRevenue = revenues.values().stream()
+ .mapToInt(Revenue::getMoney)
+ .sum();
+ int dealerRevenue = Math.negateExact(totalPlayerRevenue);
+
+ return new Revenue(dealerRevenue);
+ }
+
+}
diff --git a/src/main/java/domain/Dealer.java b/src/main/java/domain/participant/Dealer.java
similarity index 50%
rename from src/main/java/domain/Dealer.java
rename to src/main/java/domain/participant/Dealer.java
index 1a7d540f378..ca35605906b 100644
--- a/src/main/java/domain/Dealer.java
+++ b/src/main/java/domain/participant/Dealer.java
@@ -1,14 +1,15 @@
-package domain;
+package domain.participant;
public class Dealer extends Participant {
private static final int CAN_RECEIVE_CARD_THRESHOLD = 17;
+ private static final String DEALER = "딜러";
- public Dealer(Name name) {
- super(name);
+ public Dealer() {
+ super(new Name(DEALER));
}
public boolean isContinueGame() {
- if (cards.calculateScore() >= CAN_RECEIVE_CARD_THRESHOLD) {
+ if (participantCards.calculateScore() >= CAN_RECEIVE_CARD_THRESHOLD) {
return false;
}
return true;
diff --git a/src/main/java/domain/Name.java b/src/main/java/domain/participant/Name.java
similarity index 97%
rename from src/main/java/domain/Name.java
rename to src/main/java/domain/participant/Name.java
index f5cc277df88..253ed055683 100644
--- a/src/main/java/domain/Name.java
+++ b/src/main/java/domain/participant/Name.java
@@ -1,4 +1,4 @@
-package domain;
+package domain.participant;
import java.util.Objects;
diff --git a/src/main/java/domain/participant/Participant.java b/src/main/java/domain/participant/Participant.java
new file mode 100644
index 00000000000..6ec3ea50dfb
--- /dev/null
+++ b/src/main/java/domain/participant/Participant.java
@@ -0,0 +1,46 @@
+package domain.participant;
+
+import domain.card.Card;
+import domain.card.Cards;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class Participant {
+ private static final int BUST_THRESHOLD = 21;
+ private final Name name;
+ protected final ParticipantCards participantCards;
+
+ public Participant(Name name) {
+ this.name = name;
+ participantCards = new ParticipantCards(new Cards(new ArrayList<>()));
+ }
+
+ public void receiveCard(Card card) {
+ participantCards.addCard(card);
+ }
+
+ public abstract boolean isContinueGame();
+
+ public Name getName() {
+ return this.name;
+ }
+
+ public int getScore() {
+ return participantCards.calculateScore();
+ }
+
+ public boolean isBust() {
+ if (this.getScore() > BUST_THRESHOLD) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isBlackJack() {
+ return participantCards.getCardSize() == 2 && getScore() == 21;
+ }
+
+ public List getCardsInfo() {
+ return participantCards.getCardsInfo();
+ }
+}
diff --git a/src/main/java/domain/participant/ParticipantCards.java b/src/main/java/domain/participant/ParticipantCards.java
new file mode 100644
index 00000000000..6e414959481
--- /dev/null
+++ b/src/main/java/domain/participant/ParticipantCards.java
@@ -0,0 +1,40 @@
+package domain.participant;
+
+import domain.card.Card;
+import domain.card.Cards;
+import java.util.List;
+
+public class ParticipantCards {
+ private final Cards cards;
+ private int changeAvailableAceCount;
+
+ public ParticipantCards(Cards cards) {
+ this.cards = cards;
+ this.changeAvailableAceCount = 0;
+ }
+
+ public List getCardsInfo() {
+ return cards.getCardsInfo();
+ }
+
+ public int calculateScore() {
+ int sum = cards.sumScore();
+ int availableAceCount = changeAvailableAceCount;
+ while (availableAceCount > 0 && sum > 21) {
+ sum -= 10;
+ availableAceCount -= 1;
+ }
+ return sum;
+ }
+
+ public void addCard(Card card) {
+ if (card.isAce()) {
+ changeAvailableAceCount += 1;
+ }
+ cards.add(card);
+ }
+
+ public int getCardSize() {
+ return cards.getSize();
+ }
+}
diff --git a/src/main/java/domain/Player.java b/src/main/java/domain/participant/Player.java
similarity index 68%
rename from src/main/java/domain/Player.java
rename to src/main/java/domain/participant/Player.java
index 86e7174e085..77e2861174c 100644
--- a/src/main/java/domain/Player.java
+++ b/src/main/java/domain/participant/Player.java
@@ -1,4 +1,6 @@
-package domain;
+package domain.participant;
+
+import domain.game.GameResult;
public class Player extends Participant {
private static final int CAN_RECEIVE_CARD_THRESHOLD = 21;
@@ -8,13 +10,19 @@ public Player(Name name) {
}
public boolean isContinueGame() {
- if (cards.calculateScore() >= CAN_RECEIVE_CARD_THRESHOLD) {
+ if (participantCards.calculateScore() >= CAN_RECEIVE_CARD_THRESHOLD) {
return false;
}
return true;
}
public GameResult judgeResult(Dealer dealer) {
+ if (this.isBlackJack() && dealer.isBlackJack()) {
+ return GameResult.DRAW;
+ }
+ if (this.isBlackJack()) {
+ return GameResult.BLACKJACK;
+ }
if (this.isBust()) {
return GameResult.LOSE;
}
diff --git a/src/main/java/domain/participant/Players.java b/src/main/java/domain/participant/Players.java
new file mode 100644
index 00000000000..e9cb70a1647
--- /dev/null
+++ b/src/main/java/domain/participant/Players.java
@@ -0,0 +1,49 @@
+package domain.participant;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class Players {
+ private static final int MAX_PLAYER = 8;
+
+ private final List players;
+
+ public Players(List players) {
+ validatePlayersNumber(players);
+ this.players = new ArrayList<>(players);
+ }
+
+ public void forEach(Consumer action) {
+ players.forEach(action);
+ }
+
+ public static void validatePlayersNumber(List players) {
+ validateMinimumPlayers(players);
+ validateMaximumPlayers(players);
+ validateDuplicateName(players);
+ }
+
+ private static void validateMaximumPlayers(List players) {
+ if (players.size() > MAX_PLAYER) {
+ throw new IllegalArgumentException("플레이어의 수는 8명을 초과할 수 없습니다.");
+ }
+ }
+
+ private static void validateMinimumPlayers(List players) {
+ if (players.isEmpty()) {
+ throw new IllegalArgumentException("플레이어의 수는 1명 이상이어야 합니다.");
+ }
+ }
+
+ private static void validateDuplicateName(List players) {
+ long uniqueCount = players.stream()
+ .map(Player::getName)
+ .distinct()
+ .count();
+
+ if (players.size() != uniqueCount) {
+ throw new IllegalArgumentException("중복된 참가자 이름이 있습니다!");
+ }
+ }
+}
diff --git a/src/main/java/dto/ParticipantCardsDto.java b/src/main/java/dto/ParticipantCardsDto.java
index 175a3e11f0e..2c7444ff2cd 100644
--- a/src/main/java/dto/ParticipantCardsDto.java
+++ b/src/main/java/dto/ParticipantCardsDto.java
@@ -2,5 +2,8 @@
import java.util.List;
-public record ParticipantCardsDto(String name, List cardsInfo, int totalScore) {
+public record ParticipantCardsDto(
+ String name,
+ List cardsInfo,
+ int totalScore) {
};
diff --git a/src/main/java/dto/ParticipantRevenueDto.java b/src/main/java/dto/ParticipantRevenueDto.java
new file mode 100644
index 00000000000..fcdfd4f04e1
--- /dev/null
+++ b/src/main/java/dto/ParticipantRevenueDto.java
@@ -0,0 +1,7 @@
+package dto;
+
+public record ParticipantRevenueDto(
+ String name,
+ int revenue
+) {
+}
diff --git a/src/main/java/view/InputValidator.java b/src/main/java/view/InputValidator.java
new file mode 100644
index 00000000000..c8cac249b42
--- /dev/null
+++ b/src/main/java/view/InputValidator.java
@@ -0,0 +1,9 @@
+package view;
+
+public class InputValidator {
+ public static void validateContinueResponse(String input) {
+ if (!input.matches("[yn]")) {
+ throw new IllegalArgumentException("응답은 y와 n만 허용됩니다.");
+ }
+ }
+}
diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java
index 6546ee02854..d88abbd8993 100644
--- a/src/main/java/view/InputView.java
+++ b/src/main/java/view/InputView.java
@@ -1,7 +1,5 @@
package view;
-import static domain.GameManager.validatePlayersNumber;
-
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
@@ -13,16 +11,23 @@ public static List askPlayerNames() {
Scanner sc = new Scanner(System.in);
String input = sc.nextLine();
List playerNames = getPlayerNames(input);
- validatePlayersNumber(playerNames);
-
return playerNames;
}
public static String askContinue(String player) {
+ System.out.println();
System.out.println(player + "는 한장의 카드를 더 받겠습니까? (예는 y, 아니오는 n)");
Scanner sc = new Scanner(System.in);
String input = sc.nextLine();
- validateContinueResponse(input);
+ InputValidator.validateContinueResponse(input);
+ return input;
+ }
+
+ public static Integer askBettingAmount(String player) {
+ System.out.println();
+ System.out.println(player + "의 배팅 금액은?");
+ Scanner sc = new Scanner(System.in);
+ Integer input = sc.nextInt();
return input;
}
@@ -32,10 +37,4 @@ private static List getPlayerNames(String input) {
.toList();
return playerNames;
}
-
- private static void validateContinueResponse(String input) {
- if (!input.matches("[yn]")) {
- throw new IllegalArgumentException("응답은 y와 n만 허용됩니다.");
- }
- }
}
diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java
index 85e8338303e..d6705bf96ba 100644
--- a/src/main/java/view/OutputView.java
+++ b/src/main/java/view/OutputView.java
@@ -1,8 +1,9 @@
package view;
-import domain.GameResult;
+import domain.game.GameResult;
import dto.ParticipantCardsDto;
+import dto.ParticipantRevenueDto;
import java.util.List;
import java.util.Map;
@@ -11,6 +12,7 @@ public class OutputView {
public static void printGameInitialMessage(List playersNames) {
String playersNamesMessage = String.join(",", playersNames);
+ System.out.println();
System.out.printf(DEALER + "와 %s에게 2장을 나누었습니다.%n", playersNamesMessage);
}
@@ -26,6 +28,7 @@ public static void printFinalCards(ParticipantCardsDto participantCardsDto) {
}
public static void printDealerMessage() {
+ System.out.println();
System.out.println(DEALER + "는 16이하라 한장의 카드를 더 받았습니다.");
}
@@ -44,12 +47,23 @@ public static void printGameResult(Map gameResult) {
resultMessage.append(entry.getKey()).append(": ").append("패%n");
loseCount += 1;
}
-
+ System.out.println();
System.out.println("## 최종 승패");
System.out.println(DEALER + ": " + loseCount + "승" + " " + winCount + "패");
System.out.printf(resultMessage.toString());
}
+ public static void printParticipantRevenues(List participantRevenueDtos) {
+ System.out.println();
+ System.out.println("## 최종 수익");
+
+ for (ParticipantRevenueDto participantRevenueDto : participantRevenueDtos) {
+ System.out.println(
+ participantRevenueDto.name() + ": " + participantRevenueDto.revenue()
+ );
+ }
+ }
+
private static void printInitialPlayerCards(ParticipantCardsDto participantCardsDto) {
if (!participantCardsDto.name().equals(DEALER)) {
String cardsInfoMessage = String.join(",", participantCardsDto.cardsInfo());
diff --git a/src/test/java/domain/CardsTest.java b/src/test/java/domain/CardsTest.java
deleted file mode 100644
index 749beeeefc5..00000000000
--- a/src/test/java/domain/CardsTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package domain;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.util.ArrayList;
-import java.util.List;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-
-class CardsTest {
-
- @Test
- @DisplayName("카드 합계를 계산한다.")
- void calculateScore_ContainsNonAce_ReturnSum() {
- Card card1 = new Card(Shape.SPADE, Number.EIGHT);
- Card card2 = new Card(Shape.HEART, Number.EIGHT);
-
- List cards = new ArrayList<>();
- Cards testCards = new Cards(cards);
-
- testCards.addCard(card1);
- testCards.addCard(card2);
-
- assertEquals(16, testCards.calculateScore());
- }
-
- @Test
- @DisplayName("ACE가 포함되었을 때 카드 합계를 계산한다.")
- void calculateScore_ContainsAce_ReturnSum() {
- Card card1 = new Card(Shape.SPADE, Number.ACE);
- Card card2 = new Card(Shape.HEART, Number.FIVE);
- Card card3 = new Card(Shape.CLUB, Number.EIGHT);
-
- List cards = new ArrayList<>();
- Cards testCards = new Cards(cards);
-
- testCards.addCard(card1);
- testCards.addCard(card2);
- testCards.addCard(card3);
-
- assertEquals(14, testCards.calculateScore());
- }
-}
diff --git a/src/test/java/domain/DealerTest.java b/src/test/java/domain/DealerTest.java
deleted file mode 100644
index 17ec814c6a8..00000000000
--- a/src/test/java/domain/DealerTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package domain;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-
-class DealerTest {
-
- @Test
- @DisplayName("딜러는 점수가 16점 이하일 때 카드를 더 받을 수 있다.")
- void isContinueGame_UnderScore16_ReturnTrue() {
-
- Dealer dealer = new Dealer(new Name("딜러"));
- dealer.receiveCard(new Card(Shape.SPADE, Number.TEN));
- dealer.receiveCard(new Card(Shape.HEART, Number.SIX));
-
- boolean result = dealer.isContinueGame();
- assertEquals(true, result);
- }
-
- @Test
- @DisplayName("딜러는 점수가 17점 이상일 때 카드를 더 받을 수 없다.")
- void isContinueGame_OverScore17_ReturnFalse() {
-
- Dealer dealer = new Dealer(new Name("딜러"));
- dealer.receiveCard(new Card(Shape.SPADE, Number.TEN));
- dealer.receiveCard(new Card(Shape.HEART, Number.SEVEN));
-
- boolean result = dealer.isContinueGame();
- assertEquals(false, result);
- }
-}
diff --git a/src/test/java/domain/GameManagerTest.java b/src/test/java/domain/GameManagerTest.java
deleted file mode 100644
index 6a37cf9b1fb..00000000000
--- a/src/test/java/domain/GameManagerTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package domain;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-
-class GameManagerTest {
- @Test
- @DisplayName("플레이어들의 승패 결과를 딜러의 점수와 비교하여 정확히 Map으로 반환한다.")
- void getGameResult_ReturnMap() {
- Player pobi = new Player(new Name("pobi"));
- Player crong = new Player(new Name("crong"));
- GameManager gameManager = new GameManager(List.of(pobi, crong));
-
- Dealer dealer = gameManager.getDealer();
-
- dealer.receiveCard(new Card(Shape.SPADE, Number.TEN));
- dealer.receiveCard(new Card(Shape.HEART, Number.EIGHT));
-
- pobi.receiveCard(new Card(Shape.DIAMOND, Number.TEN));
- pobi.receiveCard(new Card(Shape.CLUB, Number.TEN));
-
- crong.receiveCard(new Card(Shape.SPADE, Number.NINE));
- crong.receiveCard(new Card(Shape.HEART, Number.SEVEN));
-
- Map result = gameManager.getGameResult();
-
- GameResult pobiResult = result.get("pobi");
- GameResult crongResult = result.get("crong");
-
- assertEquals(GameResult.WIN, pobiResult);
- assertEquals(GameResult.LOSE, crongResult);
- }
-
- @Test
- @DisplayName("플레이어들의 이름에 중복이 있는지 검사한다.")
- void validateDuplicateName_DuplicateName_ThrowsException() {
- List name = new ArrayList<>();
- name.add("pobi");
- name.add("jason");
- name.add("pobi");
- IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
- GameManager.validatePlayersNumber(name);
- });
- assertEquals("중복된 참가자 이름이 있습니다!", exception.getMessage());
- }
-}
diff --git a/src/test/java/domain/NameTest.java b/src/test/java/domain/NameTest.java
deleted file mode 100644
index 26f46f826de..00000000000
--- a/src/test/java/domain/NameTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package domain;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-
-class NameTest {
-
- @Test
- @DisplayName("이름은 비어있거나 공백일 수 없다.")
- void validateBlank_InputBlank_ThrowsException() {
- String name = " ";
- IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
- new Name(name);
- });
- assertEquals("이름은 비어있거나 공백일 수 없습니다.", exception.getMessage());
- }
-
- @Test
- @DisplayName("이름은 2글자 이상이어야한다.")
- void validateLength_InputOneLength_ThrowsException() {
- String name = "a";
- IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
- new Name(name);
- });
- assertEquals("이름은 2~5글자만 허용됩니다.", exception.getMessage());
- }
-
- @Test
- @DisplayName("이름은 5글자 이하여야한다.")
- void validateLength_InputSixLength_ThrowsException() {
- String name = "abcdefg";
- IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
- new Name(name);
- });
- assertEquals("이름은 2~5글자만 허용됩니다.", exception.getMessage());
- }
-}
diff --git a/src/test/java/domain/PlayerTest.java b/src/test/java/domain/PlayerTest.java
deleted file mode 100644
index e2395d3962b..00000000000
--- a/src/test/java/domain/PlayerTest.java
+++ /dev/null
@@ -1,149 +0,0 @@
-package domain;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-
-class PlayerTest {
-
- @Test
- @DisplayName("플레이어의 점수가 21점을 초과하면 isBust는 true를 반환한다.")
- void isBust_Score22_ReturnTrue() {
- Player player = new Player(new Name("pobi"));
- player.receiveCard(new Card(Shape.SPADE, Number.TEN));
- player.receiveCard(new Card(Shape.HEART, Number.JACK));
- player.receiveCard(new Card(Shape.DIAMOND, Number.TWO));
-
- boolean result = player.isBust();
-
- assertEquals(true, result);
- }
-
- @Test
- @DisplayName("플레이어의 점수가 21점이면 isBust는 false를 반환한다.")
- void isBust_Score21_ReturnFalse() {
- Player player = new Player(new Name("pobi"));
- player.receiveCard(new Card(Shape.SPADE, Number.NINE));
- player.receiveCard(new Card(Shape.HEART, Number.JACK));
- player.receiveCard(new Card(Shape.DIAMOND, Number.TWO));
-
- boolean result = player.isBust();
-
- assertEquals(false, result);
- }
-
- @Test
- @DisplayName("플레이어는 점수가 21점 미만일 때 카드를 더 받을 수 있다.")
- void isContinueGame_UnderScore21_ReturnTrue() {
-
- Player player = new Player(new Name("pobi"));
- player.receiveCard(new Card(Shape.SPADE, Number.EIGHT));
- player.receiveCard(new Card(Shape.HEART, Number.JACK));
- player.receiveCard(new Card(Shape.DIAMOND, Number.TWO));
-
- boolean result = player.isContinueGame();
-
- assertEquals(true, result);
- }
-
- @Test
- @DisplayName("플레이어는 점수가 21점 이상일 때 카드를 더 받을 수 없다.")
- void isContinueGame_OverScore21_ReturnFalse() {
-
- Player player = new Player(new Name("pobi"));
- player.receiveCard(new Card(Shape.SPADE, Number.NINE));
- player.receiveCard(new Card(Shape.HEART, Number.JACK));
- player.receiveCard(new Card(Shape.DIAMOND, Number.TWO));
-
- boolean result = player.isContinueGame();
-
- assertEquals(false, result);
- }
-
- @Test
- @DisplayName("둘 다 버스트되지 않고, 플레이어 점수가 딜러 점수보다 높으면 플레이어가 승리한다.")
- void judgeResult_PlayerScoreHigher_ReturnTrue() {
- Player player = new Player(new Name("pobi"));
- Dealer dealer = new Dealer(new Name("딜러"));
-
- player.receiveCard(new Card(Shape.SPADE, Number.TEN));
- player.receiveCard(new Card(Shape.HEART, Number.JACK));
-
- dealer.receiveCard(new Card(Shape.DIAMOND, Number.TEN));
- dealer.receiveCard(new Card(Shape.CLUB, Number.EIGHT));
-
- GameResult result = player.judgeResult(dealer);
-
- assertEquals(GameResult.WIN, result);
- }
-
- @Test
- @DisplayName("둘 다 버스트되지 않고, 플레이어 점수와 딜러 점수가 같으면 비긴다.")
- void judgeResult_PlayerScoreSame_ReturnTrue() {
- Player player = new Player(new Name("pobi"));
- Dealer dealer = new Dealer(new Name("딜러"));
-
- player.receiveCard(new Card(Shape.SPADE, Number.TEN));
- player.receiveCard(new Card(Shape.HEART, Number.JACK));
-
- dealer.receiveCard(new Card(Shape.DIAMOND, Number.TEN));
- dealer.receiveCard(new Card(Shape.CLUB, Number.JACK));
-
- GameResult result = player.judgeResult(dealer);
-
- assertEquals(GameResult.DRAW, result);
- }
-
- @Test
- @DisplayName("둘 다 버스트되지 않고, 플레이어 점수보다 딜러 점수가 높으면 플레이어가 패배한다.")
- void judgeResult_DealerScoreHigher_ReturnFalse() {
- Player player = new Player(new Name("pobi"));
- Dealer dealer = new Dealer(new Name("딜러"));
-
- player.receiveCard(new Card(Shape.SPADE, Number.TEN));
-
- dealer.receiveCard(new Card(Shape.DIAMOND, Number.TEN));
- dealer.receiveCard(new Card(Shape.CLUB, Number.JACK));
-
- GameResult result = player.judgeResult(dealer);
-
- assertEquals(GameResult.LOSE, result);
- }
-
- @Test
- @DisplayName("플레이어가 버스트 되면, 딜러 점수 상관없이 패배한다.")
- void judgeResult_PlayerBurst_ReturnFalse() {
- Player player = new Player(new Name("pobi"));
- Dealer dealer = new Dealer(new Name("딜러"));
-
- player.receiveCard(new Card(Shape.SPADE, Number.TWO));
- player.receiveCard(new Card(Shape.HEART, Number.JACK));
- player.receiveCard(new Card(Shape.CLUB, Number.JACK));
-
- dealer.receiveCard(new Card(Shape.DIAMOND, Number.TEN));
- dealer.receiveCard(new Card(Shape.CLUB, Number.EIGHT));
-
- GameResult result = player.judgeResult(dealer);
-
- assertEquals(GameResult.LOSE, result);
- }
-
- @Test
- @DisplayName("플레이어가 버스트 되지 않을 때, 딜러가 버스트되면 플레이어가 승리한다.")
- void judgeResult_DealerBurst_ReturnTrue() {
- Player player = new Player(new Name("pobi"));
- Dealer dealer = new Dealer(new Name("딜러"));
-
- player.receiveCard(new Card(Shape.SPADE, Number.TEN));
- player.receiveCard(new Card(Shape.HEART, Number.JACK));
-
- dealer.receiveCard(new Card(Shape.DIAMOND, Number.TEN));
- dealer.receiveCard(new Card(Shape.CLUB, Number.TWO));
- dealer.receiveCard(new Card(Shape.HEART, Number.TEN));
-
- GameResult result = player.judgeResult(dealer);
-
- assertEquals(GameResult.WIN, result);
- }
-}
diff --git a/src/test/java/domain/betting/BettingAmountTest.java b/src/test/java/domain/betting/BettingAmountTest.java
new file mode 100644
index 00000000000..4f44a7096f5
--- /dev/null
+++ b/src/test/java/domain/betting/BettingAmountTest.java
@@ -0,0 +1,24 @@
+package domain.betting;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+class BettingAmountTest {
+
+ @Test
+ void 베팅_금액이_음수면_예외가_발생한다() {
+ Integer testBettingAmount = -1000;
+ assertThrows(IllegalArgumentException.class, () -> {
+ new BettingAmount(testBettingAmount);
+ });
+ }
+
+ @Test
+ void 베팅_금액이_양수이면_생성된다() {
+ Integer testBettingAmount = 1000;
+ assertDoesNotThrow(() -> {
+ new BettingAmount(testBettingAmount);
+ });
+ }
+}
diff --git a/src/test/java/domain/betting/CalculateProfitTest.java b/src/test/java/domain/betting/CalculateProfitTest.java
new file mode 100644
index 00000000000..ed6804e36d2
--- /dev/null
+++ b/src/test/java/domain/betting/CalculateProfitTest.java
@@ -0,0 +1,61 @@
+package domain.betting;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import domain.game.GameResult;
+import domain.participant.Name;
+import domain.participant.Player;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+class CalculateProfitTest {
+
+ @Test
+ void 참가자가_패배하면_수익은_배팅금액만큼_감소한다() {
+ Name name = new Name("pobi");
+ Player player = new Player(name);
+ Map amountMap = new HashMap<>();
+ amountMap.put(player.getName(), new BettingAmount(1000));
+ BettingAmounts bettingAmounts = new BettingAmounts(amountMap);
+ CalculateProfit calculateProfit = new CalculateProfit(bettingAmounts);
+ int expected = -1000;
+ assertEquals(expected, calculateProfit.calculate(name, GameResult.LOSE).getMoney());
+ }
+
+ @Test
+ void 참가자가_승리하면_수익은_배팅금액만큼_증가한다() {
+ Name name = new Name("pobi");
+ Player player = new Player(name);
+ Map amountMap = new HashMap<>();
+ amountMap.put(player.getName(), new BettingAmount(1000));
+ BettingAmounts bettingAmounts = new BettingAmounts(amountMap);
+ CalculateProfit calculateProfit = new CalculateProfit(bettingAmounts);
+ int expected = 1000;
+ assertEquals(expected, calculateProfit.calculate(name, GameResult.WIN).getMoney());
+ }
+
+ @Test
+ void 참가자가_블랙잭이면_수익은_배팅금액의_1_5배가_된다() {
+ Name name = new Name("pobi");
+ Player player = new Player(name);
+ Map amountMap = new HashMap<>();
+ amountMap.put(player.getName(), new BettingAmount(1000));
+ BettingAmounts bettingAmounts = new BettingAmounts(amountMap);
+ CalculateProfit calculateProfit = new CalculateProfit(bettingAmounts);
+ int expected = 1500;
+ assertEquals(expected, calculateProfit.calculate(name, GameResult.BLACKJACK).getMoney());
+ }
+
+ @Test
+ void 참가자가_비기면_수익은_0원이다() {
+ Name name = new Name("pobi");
+ Player player = new Player(name);
+ Map amountMap = new HashMap<>();
+ amountMap.put(player.getName(), new BettingAmount(1000));
+ BettingAmounts bettingAmounts = new BettingAmounts(amountMap);
+ CalculateProfit calculateProfit = new CalculateProfit(bettingAmounts);
+ int expected = 0;
+ assertEquals(expected, calculateProfit.calculate(name, GameResult.DRAW).getMoney());
+ }
+}
diff --git a/src/test/java/domain/DeckTest.java b/src/test/java/domain/card/DeckTest.java
similarity index 65%
rename from src/test/java/domain/DeckTest.java
rename to src/test/java/domain/card/DeckTest.java
index e1b47e1c3bf..3710b9384c3 100644
--- a/src/test/java/domain/DeckTest.java
+++ b/src/test/java/domain/card/DeckTest.java
@@ -1,16 +1,14 @@
-package domain;
+package domain.card;
import static org.junit.jupiter.api.Assertions.*;
-import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
class DeckTest {
private final static Integer DECK_SIZE = 52;
@Test
- @DisplayName("덱에서는 52장까지 뽑을 수 있고, 53번째에는 예외가 발생한다.")
- void drawCard_After52Draws_ThrowsException() {
+ void 카드를_52장_초과해_뽑으면_예외가_발생한다() {
Deck deck = new Deck();
for (int i = 0; i < DECK_SIZE; i++) {
assertDoesNotThrow(deck::drawCard);
diff --git a/src/test/java/domain/game/GameResultManagerTest.java b/src/test/java/domain/game/GameResultManagerTest.java
new file mode 100644
index 00000000000..1f4d0f3cd01
--- /dev/null
+++ b/src/test/java/domain/game/GameResultManagerTest.java
@@ -0,0 +1,50 @@
+package domain.game;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import domain.betting.BettingAmount;
+import domain.betting.BettingAmounts;
+import domain.betting.CalculateProfit;
+import domain.card.Card;
+import domain.card.Number;
+import domain.card.Shape;
+import domain.participant.Dealer;
+import domain.participant.Name;
+import domain.participant.Player;
+import domain.participant.Players;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+class GameResultManagerTest {
+ @Test
+ void 플레이어들의_점수를_딜러와_비교해_승패를_반환한다() {
+ Player pobi = new Player(new Name("pobi"));
+ Player crong = new Player(new Name("crong"));
+ Players players = new Players(List.of(pobi, crong));
+ GameManager gameManager = new GameManager(players);
+ Dealer dealer = gameManager.getDealer();
+
+ dealer.receiveCard(new Card(Shape.SPADE, domain.card.Number.TEN));
+ dealer.receiveCard(new Card(Shape.HEART, domain.card.Number.EIGHT));
+
+ pobi.receiveCard(new Card(Shape.DIAMOND, domain.card.Number.TEN));
+ pobi.receiveCard(new Card(Shape.CLUB, domain.card.Number.TEN));
+
+ crong.receiveCard(new Card(Shape.SPADE, domain.card.Number.NINE));
+ crong.receiveCard(new Card(Shape.HEART, Number.SEVEN));
+
+ Map bettingAmounts = new HashMap<>();
+ players.forEach(player -> bettingAmounts.put(player.getName(), new BettingAmount(1000)));
+
+ BettingAmounts bettingManager = new BettingAmounts(bettingAmounts);
+ CalculateProfit calculateProfit = new CalculateProfit(bettingManager);
+ GameResultManager gameResultManager = new GameResultManager(calculateProfit, players, dealer);
+
+ Map result = gameResultManager.getGameResult();
+
+ assertEquals(GameResult.WIN, result.get("pobi"));
+ assertEquals(GameResult.LOSE, result.get("crong"));
+ }
+}
diff --git a/src/test/java/domain/participant/DealerTest.java b/src/test/java/domain/participant/DealerTest.java
new file mode 100644
index 00000000000..7bb9b7c0e01
--- /dev/null
+++ b/src/test/java/domain/participant/DealerTest.java
@@ -0,0 +1,31 @@
+package domain.participant;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import domain.card.Card;
+import domain.card.Number;
+import domain.card.Shape;
+import org.junit.jupiter.api.Test;
+
+class DealerTest {
+
+ @Test
+ void 점수가_16점_이하이면_카드를_추가로_받을_수_있다() {
+ Dealer dealer = new Dealer();
+ dealer.receiveCard(new Card(Shape.SPADE, domain.card.Number.TEN));
+ dealer.receiveCard(new Card(Shape.HEART, domain.card.Number.SIX));
+
+ boolean result = dealer.isContinueGame();
+ assertEquals(true, result);
+ }
+
+ @Test
+ void 점수가_17점_이상이면_카드를_추가로_받을_수_없다() {
+ Dealer dealer = new Dealer();
+ dealer.receiveCard(new Card(Shape.SPADE, domain.card.Number.TEN));
+ dealer.receiveCard(new Card(Shape.HEART, Number.SEVEN));
+
+ boolean result = dealer.isContinueGame();
+ assertEquals(false, result);
+ }
+}
diff --git a/src/test/java/domain/participant/NameTest.java b/src/test/java/domain/participant/NameTest.java
new file mode 100644
index 00000000000..15799e40078
--- /dev/null
+++ b/src/test/java/domain/participant/NameTest.java
@@ -0,0 +1,32 @@
+package domain.participant;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+class NameTest {
+ // TODO: 나중에 커스텀 예외로 변경. 현재는 예외가 터지는 것만 검증됨.
+ @Test
+ void 이름이_비어있거나_공백이면_예외가_발생한다() {
+ String name = " ";
+ assertThrows(IllegalArgumentException.class, () -> {
+ new Name(name);
+ });
+ }
+
+ @Test
+ void 이름이_2글자_미만이면_예외가_발생한다() {
+ String name = "a";
+ assertThrows(IllegalArgumentException.class, () -> {
+ new Name(name);
+ });
+ }
+
+ @Test
+ void 이름이_5글자를_초과하면_예외가_발생한다() {
+ String name = "abcdefg";
+ assertThrows(IllegalArgumentException.class, () -> {
+ new Name(name);
+ });
+ }
+}
diff --git a/src/test/java/domain/participant/ParticipantCardsTest.java b/src/test/java/domain/participant/ParticipantCardsTest.java
new file mode 100644
index 00000000000..3d0448d799c
--- /dev/null
+++ b/src/test/java/domain/participant/ParticipantCardsTest.java
@@ -0,0 +1,41 @@
+package domain.participant;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import domain.card.Card;
+import domain.card.Cards;
+import domain.card.Number;
+import domain.card.Shape;
+import java.util.ArrayList;
+import org.junit.jupiter.api.Test;
+
+class ParticipantCardsTest {
+
+ @Test
+ void 에이스가_없으면_카드_숫자의_합을_반환한다() {
+ Card card1 = new Card(Shape.SPADE, domain.card.Number.EIGHT);
+ Card card2 = new Card(Shape.HEART, domain.card.Number.EIGHT);
+
+ ParticipantCards testParticipantCards = new ParticipantCards(new Cards(new ArrayList<>()));
+
+ testParticipantCards.addCard(card1);
+ testParticipantCards.addCard(card2);
+
+ assertEquals(16, testParticipantCards.calculateScore());
+ }
+
+ @Test
+ void 에이스가_있으면_상황에_맞는_최대_합을_반환한다() {
+ Card card1 = new Card(Shape.SPADE, domain.card.Number.ACE);
+ Card card2 = new Card(Shape.HEART, domain.card.Number.FIVE);
+ Card card3 = new Card(Shape.CLUB, Number.EIGHT);
+
+ ParticipantCards testParticipantCards = new ParticipantCards(new Cards(new ArrayList<>()));
+
+ testParticipantCards.addCard(card1);
+ testParticipantCards.addCard(card2);
+ testParticipantCards.addCard(card3);
+
+ assertEquals(14, testParticipantCards.calculateScore());
+ }
+}
diff --git a/src/test/java/domain/participant/PlayerTest.java b/src/test/java/domain/participant/PlayerTest.java
new file mode 100644
index 00000000000..f52ffd3acae
--- /dev/null
+++ b/src/test/java/domain/participant/PlayerTest.java
@@ -0,0 +1,175 @@
+package domain.participant;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import domain.card.Card;
+import domain.card.Number;
+import domain.card.Shape;
+import domain.game.GameResult;
+import org.junit.jupiter.api.Test;
+
+class PlayerTest {
+
+ @Test
+ void 점수가_21점을_초과하면_버스트이다() {
+ Player player = new Player(new Name("pobi"));
+ player.receiveCard(new Card(Shape.SPADE, domain.card.Number.TEN));
+ player.receiveCard(new Card(Shape.HEART, domain.card.Number.JACK));
+ player.receiveCard(new Card(Shape.DIAMOND, domain.card.Number.TWO));
+
+ boolean result = player.isBust();
+
+ assertEquals(true, result);
+ }
+
+ @Test
+ void 점수가_21점이면_버스트가_아니다() {
+ Player player = new Player(new Name("pobi"));
+ player.receiveCard(new Card(Shape.SPADE, domain.card.Number.NINE));
+ player.receiveCard(new Card(Shape.HEART, domain.card.Number.JACK));
+ player.receiveCard(new Card(Shape.DIAMOND, domain.card.Number.TWO));
+
+ boolean result = player.isBust();
+
+ assertEquals(false, result);
+ }
+
+ @Test
+ void 에이스와_10점카드를_받으면_블랙잭이다() {
+ Player player = new Player(new Name("pobi"));
+ player.receiveCard(new Card(Shape.SPADE, domain.card.Number.ACE));
+ player.receiveCard(new Card(Shape.HEART, domain.card.Number.JACK));
+
+ boolean result = player.isBlackJack();
+
+ assertEquals(true, result);
+ }
+
+ @Test
+ void 두장의_합이_21이아니면_블랙잭이_아니다() {
+ Player player = new Player(new Name("pobi"));
+ player.receiveCard(new Card(Shape.SPADE, domain.card.Number.JACK));
+ player.receiveCard(new Card(Shape.HEART, domain.card.Number.JACK));
+
+ boolean result = player.isBlackJack();
+
+ assertEquals(false, result);
+ }
+
+ @Test
+ void 세장의_합이_21이어도_블랙잭이_아니다() {
+ Player player = new Player(new Name("pobi"));
+ player.receiveCard(new Card(Shape.SPADE, domain.card.Number.EIGHT));
+ player.receiveCard(new Card(Shape.HEART, domain.card.Number.JACK));
+ player.receiveCard(new Card(Shape.HEART, domain.card.Number.THREE));
+
+ boolean result = player.isBlackJack();
+
+ assertEquals(false, result);
+ }
+
+ @Test
+ void 점수가_21점_미만이면_카드를_추가로_받을_수_있다() {
+ Player player = new Player(new Name("pobi"));
+ player.receiveCard(new Card(Shape.SPADE, domain.card.Number.EIGHT));
+ player.receiveCard(new Card(Shape.HEART, domain.card.Number.JACK));
+ player.receiveCard(new Card(Shape.DIAMOND, domain.card.Number.TWO));
+
+ boolean result = player.isContinueGame();
+
+ assertEquals(true, result);
+ }
+
+ @Test
+ void 점수가_21점_이상이면_카드를_추가로_받을_수_없다() {
+ Player player = new Player(new Name("pobi"));
+ player.receiveCard(new Card(Shape.SPADE, domain.card.Number.NINE));
+ player.receiveCard(new Card(Shape.HEART, domain.card.Number.JACK));
+ player.receiveCard(new Card(Shape.DIAMOND, domain.card.Number.TWO));
+
+ boolean result = player.isContinueGame();
+
+ assertEquals(false, result);
+ }
+
+ @Test
+ void 딜러보다_점수가_높으면_승리한다() {
+ Player player = new Player(new Name("pobi"));
+ Dealer dealer = new Dealer();
+
+ player.receiveCard(new Card(Shape.SPADE, domain.card.Number.TEN));
+ player.receiveCard(new Card(Shape.HEART, domain.card.Number.JACK));
+
+ dealer.receiveCard(new Card(Shape.DIAMOND, domain.card.Number.TEN));
+ dealer.receiveCard(new Card(Shape.CLUB, domain.card.Number.EIGHT));
+
+ GameResult result = player.judgeResult(dealer);
+
+ assertEquals(GameResult.WIN, result);
+ }
+
+ @Test
+ void 딜러와_점수가_같으면_무승부이다() {
+ Player player = new Player(new Name("pobi"));
+ Dealer dealer = new Dealer();
+
+ player.receiveCard(new Card(Shape.SPADE, domain.card.Number.TEN));
+ player.receiveCard(new Card(Shape.HEART, domain.card.Number.JACK));
+
+ dealer.receiveCard(new Card(Shape.DIAMOND, domain.card.Number.TEN));
+ dealer.receiveCard(new Card(Shape.CLUB, domain.card.Number.JACK));
+
+ GameResult result = player.judgeResult(dealer);
+
+ assertEquals(GameResult.DRAW, result);
+ }
+
+ @Test
+ void 딜러보다_점수가_낮으면_패배한다() {
+ Player player = new Player(new Name("pobi"));
+ Dealer dealer = new Dealer();
+
+ player.receiveCard(new Card(Shape.SPADE, domain.card.Number.TEN));
+
+ dealer.receiveCard(new Card(Shape.DIAMOND, domain.card.Number.TEN));
+ dealer.receiveCard(new Card(Shape.CLUB, domain.card.Number.JACK));
+
+ GameResult result = player.judgeResult(dealer);
+
+ assertEquals(GameResult.LOSE, result);
+ }
+
+ @Test
+ void 플레이어가_버스트이면_패배한다() {
+ Player player = new Player(new Name("pobi"));
+ Dealer dealer = new Dealer();
+
+ player.receiveCard(new Card(Shape.SPADE, domain.card.Number.TWO));
+ player.receiveCard(new Card(Shape.HEART, domain.card.Number.JACK));
+ player.receiveCard(new Card(Shape.CLUB, domain.card.Number.JACK));
+
+ dealer.receiveCard(new Card(Shape.DIAMOND, domain.card.Number.TEN));
+ dealer.receiveCard(new Card(Shape.CLUB, domain.card.Number.EIGHT));
+
+ GameResult result = player.judgeResult(dealer);
+
+ assertEquals(GameResult.LOSE, result);
+ }
+
+ @Test
+ void 딜러가_버스트이면_승리한다() {
+ Player player = new Player(new Name("pobi"));
+ Dealer dealer = new Dealer();
+
+ player.receiveCard(new Card(Shape.SPADE, domain.card.Number.TEN));
+ player.receiveCard(new Card(Shape.HEART, domain.card.Number.JACK));
+
+ dealer.receiveCard(new Card(Shape.DIAMOND, domain.card.Number.TEN));
+ dealer.receiveCard(new Card(Shape.CLUB, domain.card.Number.TWO));
+ dealer.receiveCard(new Card(Shape.HEART, Number.TEN));
+
+ GameResult result = player.judgeResult(dealer);
+
+ assertEquals(GameResult.WIN, result);
+ }
+}
diff --git a/src/test/java/domain/participant/PlayersTest.java b/src/test/java/domain/participant/PlayersTest.java
new file mode 100644
index 00000000000..f7e05596621
--- /dev/null
+++ b/src/test/java/domain/participant/PlayersTest.java
@@ -0,0 +1,50 @@
+package domain.participant;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+class PlayersTest {
+ // TODO: 나중에 커스텀 예외로 변경. 현재는 예외가 터지는 것만 검증됨.
+ @Test
+ void 플레이어가_0명이면_예외가_발생한다() {
+ List players = new ArrayList<>();
+ assertThrows(IllegalArgumentException.class, () -> {
+ new Players(players);
+ });
+ }
+
+ @Test
+ void 플레이어가_8명을_초과하면_예외가_발생한다() {
+ List players = List.of(
+ new Player(new Name("aa")), new Player(new Name("bb")), new Player(new Name("cc")),
+ new Player(new Name("dd")), new Player(new Name("ee")), new Player(new Name("ff")),
+ new Player(new Name("gg")), new Player(new Name("hh")), new Player(new Name("ii"))
+ );
+ assertThrows(IllegalArgumentException.class, () -> {
+ new Players(players);
+ });
+ }
+
+ @Test
+ void 플레이어_이름이_중복되면_예외가_발생한다() {
+ List players = List.of(
+ new Player(new Name("aa")), new Player(new Name("bb")), new Player(new Name("aa"))
+ );
+ assertThrows(IllegalArgumentException.class, () -> {
+ new Players(players);
+ });
+ }
+
+ @Test
+ void 정상적인_플레이어_목록이면_생성된다() {
+ List players = List.of(
+ new Player(new Name("aa")), new Player(new Name("bb")), new Player(new Name("cc"))
+ );
+ assertDoesNotThrow(() -> {
+ new Players(players);
+ });
+ }
+}
diff --git a/src/test/java/view/InputValidatorTest.java b/src/test/java/view/InputValidatorTest.java
new file mode 100644
index 00000000000..d8bd4a7f7eb
--- /dev/null
+++ b/src/test/java/view/InputValidatorTest.java
@@ -0,0 +1,14 @@
+package view;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+class InputValidatorTest {
+ @Test
+ void 게임_진행_응답이_y_또는_n이_아니면_예외가_발생한다() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ InputValidator.validateContinueResponse("dongkey");
+ });
+ }
+}
diff --git a/src/test/java/view/InputViewTest.java b/src/test/java/view/InputViewTest.java
deleted file mode 100644
index f82c99447d5..00000000000
--- a/src/test/java/view/InputViewTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package view;
-
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class InputViewTest {
-
- private void command(String input) {
- InputStream in = new ByteArrayInputStream(input.getBytes());
- System.setIn(in);
- }
-
- @Test
- @DisplayName("플레이어의 수는 8명을 넘을 수 없다.")
- void askPlayerNames_NinePlayers_ThrowsException() {
- command("가,나,다,라,마,바,사,아,차");
- assertThrows(IllegalArgumentException.class, () -> {
- InputView.askPlayerNames();
- });
- }
-
- @Test
- @DisplayName("입력은 y와 n만 허용한다.")
- void askContinue_InputOtherThanYAndN_ThrowsException() {
- command("dongkey");
- String player = "JeongKong";
- assertThrows(IllegalArgumentException.class, () -> {
- InputView.askContinue(player);
- });
- }
-}