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