diff --git a/src/main/java/BaseBallPlaygroundMain.java b/src/main/java/BaseBallPlaygroundMain.java new file mode 100644 index 000000000..05db40a36 --- /dev/null +++ b/src/main/java/BaseBallPlaygroundMain.java @@ -0,0 +1,7 @@ +public class BaseBallPlaygroundMain { + + public static void main(String[] args) { + NumberBaseballGamePlayService service = new NumberBaseballGamePlayService(); + service.play(); + } +} diff --git a/src/main/java/NumberBaseballGamePlayService.java b/src/main/java/NumberBaseballGamePlayService.java new file mode 100644 index 000000000..e85d84e56 --- /dev/null +++ b/src/main/java/NumberBaseballGamePlayService.java @@ -0,0 +1,21 @@ +import gameContinue.GameContinueService; +import input.GAME_PROGRESS_STATUS; + +import static input.GAME_PROGRESS_STATUS.isProgress; + +public class NumberBaseballGamePlayService { + + public void play() { + NumberBaseballGameService numberBaseballGameService = new NumberBaseballGameService(); + GameContinueService gameContinueService = new GameContinueService(); + + GAME_PROGRESS_STATUS gameProgressStatus = GAME_PROGRESS_STATUS.PROGRESS; + + while (isProgress(gameProgressStatus)) { + numberBaseballGameService.playOneGame(); + + gameProgressStatus = gameContinueService.chooseGameContinueOrTerminate(); + + } + } +} diff --git a/src/main/java/NumberBaseballGameService.java b/src/main/java/NumberBaseballGameService.java new file mode 100644 index 000000000..273167106 --- /dev/null +++ b/src/main/java/NumberBaseballGameService.java @@ -0,0 +1,31 @@ +import gameResult.GameResult; +import gameResult.GameResultVerifyService; +import answer.ComputerAnswerService; +import answer.ComputerAnswerServiceImpl; +import input.InputViewService; + +import static gameResult.GameResult.createDefaultGameResult; + +public class NumberBaseballGameService { + + void playOneGame() { + + // 3자리 서로다른 수 생성 + ComputerAnswerService computerAnswerService = new ComputerAnswerServiceImpl(); + int[] computeAnswer = computerAnswerService.makeAnswer(); + + InputViewService inputViewService = new InputViewService(); + GameResultVerifyService inputAnswerVerifyService = new GameResultVerifyService(); + GameResult gameResult = createDefaultGameResult(); + + while(!gameResult.isWin()) { + // 입력받기 + int[] input = inputViewService.processInput(); + + // 결과판단 + gameResult = inputAnswerVerifyService.verify(computeAnswer, input); + gameResult.printGameResult(); + + } + } +} diff --git a/src/main/java/answer/ComputerAnswerService.java b/src/main/java/answer/ComputerAnswerService.java new file mode 100644 index 000000000..f435198bc --- /dev/null +++ b/src/main/java/answer/ComputerAnswerService.java @@ -0,0 +1,7 @@ +package answer; + +public interface ComputerAnswerService { + + int[] makeAnswer(); + +} diff --git a/src/main/java/answer/ComputerAnswerServiceImpl.java b/src/main/java/answer/ComputerAnswerServiceImpl.java new file mode 100644 index 000000000..954ade2b7 --- /dev/null +++ b/src/main/java/answer/ComputerAnswerServiceImpl.java @@ -0,0 +1,31 @@ +package answer; + +import answer.check.CheckUseAnswer; + +import static answer.random.RandomNumber.generateRandomNumber; +import static utils.ConstantUtils.GAME_NUM_LENGTH; + +public class ComputerAnswerServiceImpl implements ComputerAnswerService { + + + @Override + public int[] makeAnswer() { + CheckUseAnswer checkUseAnswerArr = new CheckUseAnswer(); + int [] answer = new int[GAME_NUM_LENGTH]; + + for(int i = 0; i < GAME_NUM_LENGTH; ++i ){ + int answerNum = generateUniqueRandomNumber(checkUseAnswerArr); + answer[i] = answerNum; + checkUseAnswerArr.checkAnswer(answer[i]); + } + return answer; + } + + int generateUniqueRandomNumber(CheckUseAnswer checkUseAnswerArr) { + int randomNum = 0; + while (!checkUseAnswerArr.isNotCheckedAnswer(randomNum)) { + randomNum = generateRandomNumber(); + } + return randomNum; + } +} diff --git a/src/main/java/answer/check/CheckUseAnswer.java b/src/main/java/answer/check/CheckUseAnswer.java new file mode 100644 index 000000000..8e4ded91b --- /dev/null +++ b/src/main/java/answer/check/CheckUseAnswer.java @@ -0,0 +1,18 @@ +package answer.check; + +public class CheckUseAnswer { + + private boolean [] check; + + public CheckUseAnswer() { + this.check = new boolean[10]; + } + + public boolean isNotCheckedAnswer(int num) { + return !check[num]; + } + + public void checkAnswer(int num) { + check[num] = true; + } +} diff --git a/src/main/java/answer/random/RandomNumber.java b/src/main/java/answer/random/RandomNumber.java new file mode 100644 index 000000000..e7b15777b --- /dev/null +++ b/src/main/java/answer/random/RandomNumber.java @@ -0,0 +1,13 @@ +package answer.random; + +import java.util.concurrent.ThreadLocalRandom; + +public class RandomNumber { + + private static final int RANDOM_MIN = 1; + private static final int RANDOM_MAX = 10; + + public static int generateRandomNumber() { + return ThreadLocalRandom.current().nextInt(RANDOM_MIN, RANDOM_MAX); + } +} diff --git a/src/main/java/gameContinue/GameContinueService.java b/src/main/java/gameContinue/GameContinueService.java new file mode 100644 index 000000000..cd6f998d0 --- /dev/null +++ b/src/main/java/gameContinue/GameContinueService.java @@ -0,0 +1,17 @@ +package gameContinue; + +import input.GAME_PROGRESS_STATUS; + +import java.util.Scanner; + +public class GameContinueService { + + public GAME_PROGRESS_STATUS chooseGameContinueOrTerminate() { + Scanner sc = new Scanner(System.in); + + System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."); + + int userInput = sc.nextInt(); + return GAME_PROGRESS_STATUS.getGameProgressByInputCode(userInput); + } +} diff --git a/src/main/java/gameResult/BallCountService.java b/src/main/java/gameResult/BallCountService.java new file mode 100644 index 000000000..2c10d21c6 --- /dev/null +++ b/src/main/java/gameResult/BallCountService.java @@ -0,0 +1,30 @@ +package gameResult; + +import utils.ConstantUtils; + +public class BallCountService { + + public int ballCount(int [] answer, int [] input) { + + int totalBall = 0; + for(int answerIdx = 0; answerIdx < ConstantUtils.GAME_NUM_LENGTH; ++answerIdx){ + totalBall += countInputBallCompareAnswerElement(answer[answerIdx], input, answerIdx); + } + + return totalBall; + } + + int countInputBallCompareAnswerElement(int answer, int [] input, int answerIdx) { + int ballByOneAnswerCount = 0; + for(int inputIdx = 0; inputIdx < ConstantUtils.GAME_NUM_LENGTH; ++inputIdx){ + ballByOneAnswerCount += checkBall(answer, input[inputIdx], answerIdx, inputIdx); + } + + return ballByOneAnswerCount; + } + + int checkBall(int answerElement, int inputElement, int answerIdx, int inputIdx){ + if(answerElement == inputElement && answerIdx != inputIdx) return 1; + return 0; + } +} diff --git a/src/main/java/gameResult/GameResult.java b/src/main/java/gameResult/GameResult.java new file mode 100644 index 000000000..effd19ed0 --- /dev/null +++ b/src/main/java/gameResult/GameResult.java @@ -0,0 +1,57 @@ +package gameResult; + +import utils.ConstantUtils; + +public class GameResult { + + private int ball; + private int strike; + + public GameResult(int ball, int strike) { + this.ball = ball; + this.strike = strike; + } + + public GameResult() { + } + + public static GameResult createDefaultGameResult(){ + GameResult gameResult = new GameResult(); + gameResult.strike = 0; + gameResult.ball = 0; + return gameResult; + } + + public boolean isWin() { + return strike == ConstantUtils.GAME_NUM_LENGTH; + } + + public void printGameResult() { + if(ball == 0) { + System.out.println(strike + "스트라이크"); + printWhenWinGame(); + return; + } + + if(strike == 0) { + System.out.println(ball +"볼"); + return; + } + + System.out.println(ball + "볼 " + strike +"스트라이크"); + } + + void printWhenWinGame() { + if(isWin()) { + System.out.println(ConstantUtils.GAME_NUM_LENGTH + "개의 숫자를 모두 맞히셨습니다! 게임종료"); + } + } + + public int getBall(){ + return this.ball; + } + + public int getStrike() { + return this.strike; + } +} diff --git a/src/main/java/gameResult/GameResultVerifyService.java b/src/main/java/gameResult/GameResultVerifyService.java new file mode 100644 index 000000000..bd79982a1 --- /dev/null +++ b/src/main/java/gameResult/GameResultVerifyService.java @@ -0,0 +1,17 @@ +package gameResult; + + +public class GameResultVerifyService { + + public GameResult verify(int[] answer, int[] input){ + + StrikeCountService strikeCountService = new StrikeCountService(); + int strike = strikeCountService.countStrike(answer, input); + + BallCountService ballCountService = new BallCountService(); + int ball = ballCountService.ballCount(answer, input); + + return new GameResult(ball, strike); + } + +} diff --git a/src/main/java/gameResult/StrikeCountService.java b/src/main/java/gameResult/StrikeCountService.java new file mode 100644 index 000000000..a42f8f840 --- /dev/null +++ b/src/main/java/gameResult/StrikeCountService.java @@ -0,0 +1,20 @@ +package gameResult; + +import utils.ConstantUtils; + +public class StrikeCountService { + + public int countStrike(int[] answer, int[] input) { + int strike = 0; + for(int i = 0; i < ConstantUtils.GAME_NUM_LENGTH; ++i ) { + strike += checkStrike(answer[i], input[i]); + } + + return strike; + } + + int checkStrike(int answerElement, int inputElement) { + if(answerElement == inputElement) return 1; + return 0; + } +} diff --git a/src/main/java/input/GAME_PROGRESS_STATUS.java b/src/main/java/input/GAME_PROGRESS_STATUS.java new file mode 100644 index 000000000..23267625d --- /dev/null +++ b/src/main/java/input/GAME_PROGRESS_STATUS.java @@ -0,0 +1,30 @@ +package input; + +import java.util.Arrays; + +public enum GAME_PROGRESS_STATUS { + PROGRESS(1) + , TERMINATE(2) + ; + + private int inputCode; + + GAME_PROGRESS_STATUS(int inputCode) { + this.inputCode = inputCode; + } + + public int getInputCode() { + return inputCode; + } + + public static GAME_PROGRESS_STATUS getGameProgressByInputCode(int inputCode) { + return Arrays.stream(GAME_PROGRESS_STATUS.values()) + .filter(status -> status.getInputCode() == inputCode) + .findAny() + .orElseThrow(IllegalArgumentException::new); + } + + public static boolean isProgress(GAME_PROGRESS_STATUS status){ + return status == PROGRESS; + } +} diff --git a/src/main/java/input/InputViewGameProgressService.java b/src/main/java/input/InputViewGameProgressService.java new file mode 100644 index 000000000..2adc02a24 --- /dev/null +++ b/src/main/java/input/InputViewGameProgressService.java @@ -0,0 +1,17 @@ +package input; + +import java.util.Scanner; + +import static input.GAME_PROGRESS_STATUS.getGameProgressByInputCode; + +public class InputViewGameProgressService { + + public GAME_PROGRESS_STATUS getGameProgressInput() { + System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."); + + Scanner scanner = new Scanner(System.in); + int userInput = scanner.nextInt(); + + return getGameProgressByInputCode(userInput); + } +} diff --git a/src/main/java/input/InputViewService.java b/src/main/java/input/InputViewService.java new file mode 100644 index 000000000..a433ff547 --- /dev/null +++ b/src/main/java/input/InputViewService.java @@ -0,0 +1,34 @@ +package input; + +import utils.ConstantUtils; + +import java.util.Scanner; + +public class InputViewService { + + public int[] processInput() { + Scanner scanner = new Scanner(System.in); + System.out.print("숫자를 입력해 주세요 : "); + String [] input = scanner.nextLine().split(""); + int[] inputInt = new int[input.length]; + + if(inputInt.length != ConstantUtils.GAME_NUM_LENGTH) { + throw new IllegalArgumentException("숫자 3개만 입력 가능합니다. "); + } + + for(int i = 0; i < ConstantUtils.GAME_NUM_LENGTH; i++) { + inputInt[i] = parseToInteger(input[i]); + } + + return inputInt; + } + + private Integer parseToInteger(String strInt) { + try { + return Integer.parseInt(strInt); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException("숫자만 입력해주세요. "); + } + } + +} diff --git a/src/main/java/utils/ConstantUtils.java b/src/main/java/utils/ConstantUtils.java new file mode 100644 index 000000000..1bfec60fa --- /dev/null +++ b/src/main/java/utils/ConstantUtils.java @@ -0,0 +1,6 @@ +package utils; + +public class ConstantUtils { + + public static final int GAME_NUM_LENGTH = 3; +} diff --git a/src/test/java/answer/ComputerAnswerServiceImplTest.java b/src/test/java/answer/ComputerAnswerServiceImplTest.java new file mode 100644 index 000000000..51d47d93c --- /dev/null +++ b/src/test/java/answer/ComputerAnswerServiceImplTest.java @@ -0,0 +1,37 @@ +package answer; + +import answer.check.CheckUseAnswer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ComputerAnswerServiceImplTest { + private ComputerAnswerServiceImpl computerAnswerService; + + @BeforeEach + public void setUp(){ + computerAnswerService = new ComputerAnswerServiceImpl(); + } + + @Test + @DisplayName("서로다른 랜덤 3자리 정답 만들기") + public void makeAnswer() { + int [] answer = computerAnswerService.makeAnswer(); + + assertThat(answer.length).isEqualTo(3); + assertThat(answer[0] != answer[1] && answer[1] != answer[2] && answer[2] != answer[0]).isTrue(); + } + + @Test + @DisplayName("중복되지 않은 랜덤숫자 만들기") + public void generateUniqueRandomNumber_success() { + CheckUseAnswer checkUseAnswer = new CheckUseAnswer(); + checkUseAnswer.checkAnswer(1); + + int uniqueRandomNum = computerAnswerService.generateUniqueRandomNumber(checkUseAnswer); + + assertThat(uniqueRandomNum).isNotEqualTo(1); + } +} diff --git a/src/test/java/answer/check/CheckUseAnswerTest.java b/src/test/java/answer/check/CheckUseAnswerTest.java new file mode 100644 index 000000000..affc0f38a --- /dev/null +++ b/src/test/java/answer/check/CheckUseAnswerTest.java @@ -0,0 +1,50 @@ +package answer.check; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class CheckUseAnswerTest { + + private final int USE_NUMBER = 7; + + @ParameterizedTest + @ValueSource(ints = {0, 1, 2, 3,4,5,6,7,8,9}) + @DisplayName("checkUserAnswer 10까지 배열 생성") + public void constructor_checkUserAnswer(int arg) { + CheckUseAnswer checkUseAnswer = new CheckUseAnswer(); + + assertThat(checkUseAnswer.isNotCheckedAnswer(arg)).isTrue(); + } + + @Test + @DisplayName("checkUserAnswer 10까지 배열 생성") + public void constructor_checkUserAnswer_size10() { + CheckUseAnswer checkUseAnswer = new CheckUseAnswer(); + + assertThatThrownBy(() ->checkUseAnswer.isNotCheckedAnswer(10)) + .isInstanceOf(IndexOutOfBoundsException.class); + } + + @Test + @DisplayName("사용한 숫자 체크 테스트") + public void checkUserAnswer_use() { + CheckUseAnswer checkUseAnswer = new CheckUseAnswer(); + checkUseAnswer.checkAnswer(USE_NUMBER); + + assertThat(checkUseAnswer.isNotCheckedAnswer(USE_NUMBER)).isFalse(); + } + + @Test + @DisplayName("사용하지 않은 숫자 체크 테스트") + public void checkUserAnswer_notUsed(){ + CheckUseAnswer checkUseAnswer = new CheckUseAnswer(); + + assertThat(checkUseAnswer.isNotCheckedAnswer(USE_NUMBER)).isTrue(); + } + +} diff --git a/src/test/java/answer/random/RandomNumberTest.java b/src/test/java/answer/random/RandomNumberTest.java new file mode 100644 index 000000000..fbf0cd293 --- /dev/null +++ b/src/test/java/answer/random/RandomNumberTest.java @@ -0,0 +1,25 @@ +package answer.random; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static answer.random.RandomNumber.generateRandomNumber; +import static org.assertj.core.api.Assertions.assertThat; + +public class RandomNumberTest { + + + @Test + @DisplayName("랜덤번호 int형 생성 ") + public void randomNumber_integer() { + assertThat(generateRandomNumber()).isInstanceOf(Integer.class); + } + + @Test + @DisplayName("1~9 사이 랜덤번호 생성") + public void randomNumber_oneToTen() { + int randomNumber = generateRandomNumber(); + Assertions.assertThat(randomNumber).isBetween(1, 9); + } +} diff --git a/src/test/java/gameResult/BallCountServiceTest.java b/src/test/java/gameResult/BallCountServiceTest.java new file mode 100644 index 000000000..e90b55c2e --- /dev/null +++ b/src/test/java/gameResult/BallCountServiceTest.java @@ -0,0 +1,122 @@ +package gameResult; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.in; + +public class BallCountServiceTest { + + private BallCountService ballCountService; + + @BeforeEach + public void setUp() { + ballCountService = new BallCountService(); + } + + @Test + @DisplayName("1볼 카운트 테스트") + public void ballCountTest_one() { + int [] answer = {3,2,8}; + int [] input = {2,5,8}; + int ball = 1; + + int result = ballCountService.ballCount(answer, input); + assertThat(result).isEqualTo(ball); + } + + @Test + @DisplayName("0볼 카운트 테스트") + public void ballCountTest_zero() { + int [] answer = {1,2,3}; + int [] input = {4,5,6}; + int ball = 0; + + int result = ballCountService.ballCount(answer, input); + assertThat(result).isEqualTo(ball); + } + + @Test + @DisplayName("2볼 카운트 테스트") + public void ballCountTest_two() { + int [] answer = {1,2,3}; + int [] input = {2,3,4}; + int ball = 2; + + int result = ballCountService.ballCount(answer, input); + assertThat(result).isEqualTo(ball); + } + + @Test + @DisplayName("3볼 카운트 테스트") + public void ballCountTest_three() { + int [] answer = {1,2,3}; + int [] input = {3,1,2}; + int ball = 3; + + int result = ballCountService.ballCount(answer, input); + assertThat(result).isEqualTo(ball); + } + + @Test + @DisplayName("input만 카운트") + public void countInputBallCompareAnswerElement() { + int answer = 1; + int answerIdx = 0; + int [] input = {9,2,1}; + int ballPerOneAnswer = 1; + + int result = ballCountService.countInputBallCompareAnswerElement(answer,input,answerIdx); + assertThat(result).isEqualTo(ballPerOneAnswer); + } + + @Test + @DisplayName("input만 카운트(strike만 있는 경우)") + public void countInputBallCompareAnswerElement_strike_case() { + int answer = 1; + int answerIdx = 2; + int [] input = {9,2,1}; + int ballPerOneAnswer = 0; + + int result = ballCountService.countInputBallCompareAnswerElement(answer,input,answerIdx); + assertThat(result).isEqualTo(ballPerOneAnswer); + } + + @Test + @DisplayName("볼인지 체크 (볼인 경우)") + public void checkBall_isBall(){ + int answerElement = 4; + int answerIdx = 0; + int inputElement = 4; + int inputIdx = 2; + + int ball = ballCountService.checkBall(answerElement,inputElement,answerIdx, inputIdx); + assertThat(ball).isEqualTo(1); + } + + @Test + @DisplayName("볼인지 체크 (볼 아닌 경우)") + public void checkBall_isNotBall(){ + int answerElement = 1; + int answerIdx = 0; + int inputElement = 4; + int inputIdx = 2; + + int ball = ballCountService.checkBall(answerElement,inputElement,answerIdx, inputIdx); + assertThat(ball).isEqualTo(0); + } + + @Test + @DisplayName("볼인지 체크 (볼 아닌 경우 - 숫자는 같지만 스트라이크인 케이스)") + public void checkBall_isNotBall_strike(){ + int answerElement = 4; + int answerIdx = 0; + int inputElement = 4; + int inputIdx = 0; + + int ball = ballCountService.checkBall(answerElement,inputElement,answerIdx, inputIdx); + assertThat(ball).isEqualTo(0); + } +} diff --git a/src/test/java/gameResult/GameResultTest.java b/src/test/java/gameResult/GameResultTest.java new file mode 100644 index 000000000..5bf2d8d4c --- /dev/null +++ b/src/test/java/gameResult/GameResultTest.java @@ -0,0 +1,119 @@ +package gameResult; + +import jdk.internal.org.jline.utils.Status; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class GameResultTest { + + private ByteArrayOutputStream outputStreamCaptor; + + @BeforeEach + void setUpStreams() { + outputStreamCaptor = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStreamCaptor)); + } + + protected String getOutput() { + // ByteArrayOutputStream의 toString은 기본 문자집합을 사용하여 버퍼의 내용을 문자열 디코딩 바이트로 변환해줍니다. + return outputStreamCaptor.toString(); + } + + @Test + @DisplayName("기본 게임결과 객체 생성") + public void createDefaultGameResult() { + GameResult gameResult = GameResult.createDefaultGameResult(); + + assertThat(gameResult.getBall()).isEqualTo(0); + assertThat(gameResult.getStrike()).isEqualTo(0); + } + + @Test + @DisplayName("3 스트라이느(이긴 경우) 테스트") + public void isWin_win(){ + GameResult gameResult = new GameResult(0,3); + + assertThat(gameResult.isWin()).isTrue(); + } + + @Test + @DisplayName("3 스트라이느(진 경우) 테스트") + public void isWin_loose(){ + GameResult gameResult = new GameResult(3,0); + + assertThat(gameResult.isWin()).isFalse(); + } + + @Test + @DisplayName("게임결과 출력 테스트_스트라이크만 있는 경우") + public void printGameResult_strikeOnly() { + int strike = 1; + int ball = 0; + GameResult gameResult = new GameResult(ball,strike); + + gameResult.printGameResult(); + + assertThat(getOutput()).contains(strike+"스트라이크"); + assertThat(getOutput()).doesNotContain("볼"); + } + + @Test + @DisplayName("게임결과 출력 테스트_스트라이크 있고 이긴 경우") + public void printGameResult_strikeOnlyWin() { + int strike = 3; + int ball = 0; + GameResult gameResult = new GameResult(ball ,strike); + + gameResult.printGameResult(); + + assertThat(getOutput()).contains(strike+"스트라이크"); + assertThat(getOutput()).contains("개의 숫자를 모두 맞히셨습니다! 게임종료"); + assertThat(getOutput()).doesNotContain("볼"); + } + + @Test + @DisplayName("게임결과 출력 테스트_볼만 있는 경우") + public void printGameResult_ballOnly() { + int strike = 0; + int ball = 1; + GameResult gameResult = new GameResult(ball,strike); + + gameResult.printGameResult(); + + assertThat(getOutput()).contains(ball+"볼"); + assertThat(getOutput()).doesNotContain("스트라이크"); + } + + @Test + @DisplayName("게임결과 출력 테스트_스트라이크, 볼 있는 경우") + public void printGameResult_strikeAndBall() { + int strike = 1; + int ball = 1; + GameResult gameResult = new GameResult(ball,strike); + + gameResult.printGameResult(); + + assertThat(getOutput()).contains(ball+"볼"); + assertThat(getOutput()).contains(strike+"스트라이크"); + } + + @Test + @DisplayName("이긴 경우 출력 테스트") + public void printWhenWinGame() { + int strike = 3; + int ball = 0; + GameResult gameResult = new GameResult(ball,strike); + + gameResult.printWhenWinGame(); + + assertThat(getOutput()).contains("개의 숫자를 모두 맞히셨습니다! 게임종료"); + } + +} diff --git a/src/test/java/gameResult/GameResultVerifyServiceTest.java b/src/test/java/gameResult/GameResultVerifyServiceTest.java new file mode 100644 index 000000000..45f23d85e --- /dev/null +++ b/src/test/java/gameResult/GameResultVerifyServiceTest.java @@ -0,0 +1,81 @@ +package gameResult; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class GameResultVerifyServiceTest { + + private GameResultVerifyService gameResultVerifyService = new GameResultVerifyService(); + + @BeforeEach + public void setUp() { + gameResultVerifyService = new GameResultVerifyService(); + } + + @Test + @DisplayName("컴퓨터정답과 입력값 비교 결과 - 3 ball") + public void testVerifyGameResult_threeBall() { + int [] answer = {1,2,3}; + int [] input = {2,3,1}; + + GameResult gameResult = gameResultVerifyService.verify(answer, input); + + Assertions.assertEquals(3, gameResult.getBall()); + Assertions.assertEquals(0, gameResult.getStrike()); + + } + + @Test + @DisplayName("컴퓨터정답과 입력값 비교 결과 - 3 strike") + public void testVerifyGameResult_threeStrike() { + int [] answer = {1,2,3}; + int [] input = {1,2,3}; + + GameResult gameResult = gameResultVerifyService.verify(answer, input); + + Assertions.assertEquals(0, gameResult.getBall()); + Assertions.assertEquals(3, gameResult.getStrike()); + + } + + @Test + @DisplayName("컴퓨터정답과 입력값 비교 결과 - 1 strike 2 ball") + public void testVerifyGameResult_oneStrikeAndTwoBall() { + int [] answer = {1,2,3}; + int [] input = {1,3,2}; + + GameResult gameResult = gameResultVerifyService.verify(answer, input); + + Assertions.assertEquals(2, gameResult.getBall()); + Assertions.assertEquals(1, gameResult.getStrike()); + + } + + @Test + @DisplayName("컴퓨터정답과 입력값 비교 결과 - 2 ball") + public void testVerifyGameResult_twoBall() { + int [] answer = {1,2,3}; + int [] input = {4,3,2}; + + GameResult gameResult = gameResultVerifyService.verify(answer, input); + + Assertions.assertEquals(2, gameResult.getBall()); + Assertions.assertEquals(0, gameResult.getStrike()); + + } + + @Test + @DisplayName("컴퓨터정답과 입력값 비교 결과 - 2 strike") + public void testVerifyGameResult_twoStrike() { + int [] answer = {1,2,3}; + int [] input = {1,9,3}; + + GameResult gameResult = gameResultVerifyService.verify(answer, input); + + Assertions.assertEquals(0, gameResult.getBall()); + Assertions.assertEquals(2, gameResult.getStrike()); + + } +} diff --git a/src/test/java/gameResult/StrikeCountServiceTest.java b/src/test/java/gameResult/StrikeCountServiceTest.java new file mode 100644 index 000000000..85c1a23c8 --- /dev/null +++ b/src/test/java/gameResult/StrikeCountServiceTest.java @@ -0,0 +1,75 @@ +package gameResult; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.in; + +public class StrikeCountServiceTest { + StrikeCountService strikeCountService; + + @BeforeEach + public void setUp() { + strikeCountService = new StrikeCountService(); + } + + @Test + @DisplayName("0스트라이크 카운트 테스트") + public void countStrike() { + int [] answer = {1,2,3}; + int [] input = {4,5,6}; + int strike = 0; + + assertThat(strike).isEqualTo(strikeCountService.countStrike(answer, input)); + } + + @Test + @DisplayName("1스트라이크 카운트 테스트") + public void countStrike_oneStrike() { + int [] answer = {1,2,3}; + int [] input = {1,5,6}; + int strike = 1; + + assertThat(strike).isEqualTo(strikeCountService.countStrike(answer, input)); + } + + @Test + @DisplayName("2스트라이크 카운트 테스트") + public void countStrike_twoStrike() { + int [] answer = {1,2,3}; + int [] input = {1,5,3}; + int strike = 2; + + assertThat(strike).isEqualTo(strikeCountService.countStrike(answer, input)); + } + + @Test + @DisplayName("3스트라이크 카운트 테스트") + public void countStrike_threeStrike() { + int [] answer = {1,2,3}; + int [] input = {1,2,3}; + int strike = 3; + + assertThat(strike).isEqualTo(strikeCountService.countStrike(answer, input)); + } + + @Test + @DisplayName("스트라이크 여부 확인_스트라이크인 경우") + public void checkStrike_strike() { + int answerElement = 1; + int inputElement = 1; + + assertThat(1).isEqualTo(strikeCountService.checkStrike(answerElement, inputElement)); + } + + @Test + @DisplayName("스트라이크 여부 확인_스트라이크 아닌 경우") + public void checkStrike_notStrike() { + int answerElement = 1; + int inputElement = 3; + + assertThat(0).isEqualTo(strikeCountService.checkStrike(answerElement, inputElement)); + } +} diff --git a/src/test/java/study/SetTest.java b/src/test/java/study/SetTest.java new file mode 100644 index 000000000..cc15054e5 --- /dev/null +++ b/src/test/java/study/SetTest.java @@ -0,0 +1,43 @@ +package study; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.HashSet; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SetTest { + private Set numbers; + + @BeforeEach + void setUp() { + numbers = new HashSet<>(); + numbers.add(1); + numbers.add(1); + numbers.add(2); + numbers.add(3); + } + + @Test + void size() { + final int expectedSize = 3; + assertThat(numbers.size()).isEqualTo(expectedSize); + } + + @ParameterizedTest + @ValueSource(ints = {1, 2, 3}) + void contains(int argument) { + assertThat(numbers.contains(argument)).isTrue(); + } + + @ParameterizedTest + @CsvSource(value = {"1:true", "2:true", "3:true", "4:false", "5:false" }, delimiter = ':') + void contains_containNotContain(int argument, boolean expected) { + assertThat(numbers.contains(argument)).isEqualTo(expected); + } +} diff --git a/src/test/java/study/StringCalculator/CALC_TYPE.java b/src/test/java/study/StringCalculator/CALC_TYPE.java new file mode 100644 index 000000000..926c86dd9 --- /dev/null +++ b/src/test/java/study/StringCalculator/CALC_TYPE.java @@ -0,0 +1,21 @@ +package study.StringCalculator; + +public enum CALC_TYPE { + + OPERAND, + OPERATOR, + INVALID; + + static CALC_TYPE checkCalcType(String op) { + if(op.equals("+") || op.equals("-") || op.equals("*") || op.equals("/")) + return CALC_TYPE.OPERATOR; + + try { + int num = Integer.parseInt(op); + return CALC_TYPE.OPERAND; + } catch (NumberFormatException e) { + return CALC_TYPE.INVALID; + } + } + +} diff --git a/src/test/java/study/StringCalculator/OPERATOR_TYPE.java b/src/test/java/study/StringCalculator/OPERATOR_TYPE.java new file mode 100644 index 000000000..a50cee996 --- /dev/null +++ b/src/test/java/study/StringCalculator/OPERATOR_TYPE.java @@ -0,0 +1,25 @@ +package study.StringCalculator; + +import java.util.Arrays; + +public enum OPERATOR_TYPE { + + PLUS("+") + , MINUS("-") + , MULTIPLY("*") + , DIVIDE("/") + ; + + String code; + + OPERATOR_TYPE(String code) { + this.code = code; + } + + static OPERATOR_TYPE getOpertorTypeByCode(String code) { + return Arrays.stream(OPERATOR_TYPE.values()) + .filter(op -> op.code.equals(code)). + findFirst() + .orElseThrow(IllegalArgumentException::new); + } +} diff --git a/src/test/java/study/StringCalculator/Splitter.java b/src/test/java/study/StringCalculator/Splitter.java new file mode 100644 index 000000000..7418d396e --- /dev/null +++ b/src/test/java/study/StringCalculator/Splitter.java @@ -0,0 +1,10 @@ +package study.StringCalculator; + +public class Splitter { + + private final static String DELIMETER = " "; + + public static String[] splitString(String input) { + return input.split(DELIMETER); + } +} diff --git a/src/test/java/study/StringCalculator/StringCalculator.java b/src/test/java/study/StringCalculator/StringCalculator.java new file mode 100644 index 000000000..7d853e9ce --- /dev/null +++ b/src/test/java/study/StringCalculator/StringCalculator.java @@ -0,0 +1,6 @@ +package study.StringCalculator; + +public interface StringCalculator { + + int calculator(String str); +} diff --git a/src/test/java/study/StringCalculator/StringCalculatorImpl.java b/src/test/java/study/StringCalculator/StringCalculatorImpl.java new file mode 100644 index 000000000..df9693a15 --- /dev/null +++ b/src/test/java/study/StringCalculator/StringCalculatorImpl.java @@ -0,0 +1,69 @@ +package study.StringCalculator; + + +import static study.StringCalculator.OPERATOR_TYPE.getOpertorTypeByCode; +import static study.StringCalculator.Splitter.splitString; + +class StringCalculatorImpl implements StringCalculator{ + + @Override + public int calculator(String input) { + String [] opArr = splitString(input); + + if(opArr.length % 2 == 0 ){ + throw new IllegalArgumentException("Invalid input"); + } + + if(CALC_TYPE.checkCalcType(opArr[0]) != CALC_TYPE.OPERAND) { + throw new IllegalArgumentException("Invalid operand:" + opArr[0]); + } + + int result = Integer.parseInt(opArr[0]); + + for(int i = 1; i < opArr.length; i += 2 ) { + String operator = opArr[i]; + String operand = opArr[i+1]; + + result = calculateEachElement(operator, operand, result); + } + + return result; + } + + private int calculateEachElement(String operator, String operand, int result) { + + CALC_TYPE operatorType = CALC_TYPE.checkCalcType(operator); + CALC_TYPE operandType = CALC_TYPE.checkCalcType(operand); + + if(isOperator(operatorType)) { + throw new IllegalArgumentException("Invalid operator: " + operator); + } + + if(isOperand(operandType)) { + throw new IllegalArgumentException("Invalid operand: " + operand); + } + + OPERATOR_TYPE operatorCd = getOpertorTypeByCode(operator); + result = calculate(result, operatorCd, Integer.parseInt(operand)); + + return result; + } + + private int calculate(int result, OPERATOR_TYPE operator, int operand) { + switch(operator) { + case PLUS: return result + operand; + case MINUS: return result - operand; + case MULTIPLY: return result * operand; + case DIVIDE: return result / operand; + } + throw new IllegalArgumentException("연산자는 더하기, 뺴기, 곱하기, 나누기만 가능합니다"); + } + + private boolean isOperator(CALC_TYPE type) { + return type == CALC_TYPE.OPERATOR; + } + + private boolean isOperand(CALC_TYPE type) { + return type == CALC_TYPE.OPERAND; + } +} diff --git a/src/test/java/study/StringCalculator/StringCalculatorTest.java b/src/test/java/study/StringCalculator/StringCalculatorTest.java new file mode 100644 index 000000000..ea0116f79 --- /dev/null +++ b/src/test/java/study/StringCalculator/StringCalculatorTest.java @@ -0,0 +1,75 @@ +package study.StringCalculator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class StringCalculatorTest { + + /* + 요구사항 + - 사용자가 입력한 문자열 값에 따라 사칙연산을 수행할 수 있는 계산기를 구현해야 한다. + - 문자열 계산기는 사칙연산의 계산 우선순위가 아닌 입력 값에 따라 계산 순서가 결정된다. 즉, 수학에서는 곱셈, 나눗셈이 덧셈, 뺄셈 보다 먼저 계산해야 하지만 이를 무시한다. + - 예를 들어 "2 + 3 * 4 / 2"와 같은 문자열을 입력할 경우 2 + 3 * 4 / 2 실행 결과인 10을 출력해야 한다. + */ + + private StringCalculator stringCalculator; + + @BeforeEach + public void setUp() { + stringCalculator = new StringCalculatorImpl(); + } + + @Test + @DisplayName("정상 케이스") + void calculate_success_test() { + final String given = "2 + 3 * 4 / 2"; + + int result = stringCalculator.calculator(given); + assertEquals(10, result); + } + + @Test + @DisplayName("마지막 피연산자 입력하지 않은 경우") + void calculate_fail_notContainOperand() { + final String given = "2 + 3 * 4 / "; + + assertThatThrownBy(() -> stringCalculator.calculator(given)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Invalid input"); + } + + @Test + @DisplayName("숫자, 연산자 외에 다른 문자열 입력한 경우") + void calculate_fail_containInvalidString() { + final String given = "2 + 3 * $ / "; + + assertThatThrownBy(() -> stringCalculator.calculator(given)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Invalid"); + } + + @Test + @DisplayName("피연산자 연속으로 입력한 경우") + void calculate_fail_consecutiveOperand() { + final String given = "2 2 3 * 4 / 2"; + + assertThatThrownBy(() -> stringCalculator.calculator(given)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Invalid operator: "); + } + + @Test + @DisplayName("연산자 연속으로 입력한 경우") + void calculate_fail_consecutiveOperator() { + final String given = "2 + + * 4 / 2"; + + assertThatThrownBy(() -> stringCalculator.calculator(given)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Invalid operand: "); + } + +} \ No newline at end of file diff --git a/src/test/java/study/StringCalculator/UserInputCalculator.java b/src/test/java/study/StringCalculator/UserInputCalculator.java new file mode 100644 index 000000000..4f5a39f87 --- /dev/null +++ b/src/test/java/study/StringCalculator/UserInputCalculator.java @@ -0,0 +1,21 @@ +package study.StringCalculator; + +import org.junit.jupiter.api.BeforeEach; + +import java.util.Scanner; + +public class UserInputCalculator { + + private final StringCalculator stringCalculator; + + UserInputCalculator(StringCalculator stringCalculator) { + this.stringCalculator = stringCalculator; + } + + int userInputCalculate(){ + Scanner scanner = new Scanner(System.in); + String input = scanner.nextLine(); + + return stringCalculator.calculator(input); + } +} diff --git a/src/test/java/study/StringTest.java b/src/test/java/study/StringTest.java index 43e47d90b..84d293177 100644 --- a/src/test/java/study/StringTest.java +++ b/src/test/java/study/StringTest.java @@ -1,8 +1,11 @@ package study; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertThrows; public class StringTest { @Test @@ -10,4 +13,68 @@ void replace() { String actual = "abc".replace("b", "d"); assertThat(actual).isEqualTo("adc"); } + + @Test + void split() { + String input = "1,2"; + final String [] expected = new String[]{"1","2"}; + +// testSplitRoutine(input, expected); + String [] result = input.split(","); + assertThat(result).containsExactly("1","2"); + + } + + @Test + void split_one_element() { + String input = "1"; + final String [] expected = new String[]{"1"}; + +// testSplitRoutine(input, expected); + String [] result = input.split(","); + assertThat(result).containsExactly("1"); + } + + private void testSplitRoutine(String input, String[] expected) { + String [] result = input.split(","); + + assertThat(result.length).isEqualTo(expected.length); + for(int i = 0 ; i < result.length ; i++){ + assertThat(result[i]).isEqualTo(expected[i]); + } + } + + @Test + void substring() { + String input = "(1,2)"; + final String expected = "1,2"; + + assertThat(removeParentheses(input)).isEqualTo(expected); + } + + private String removeParentheses(String input) { + return input.substring(1,input.length()-1); + } + + @Test + @DisplayName("i번째 글자 가져오는 charAt 테스트") + void charAt() { + String input = "abc"; + int inputIdx = 1; + char expected = 'b'; + + char actual = input.charAt(inputIdx); + assertThat(actual).isEqualTo(expected); + } + + @Test + @DisplayName("위치값 벗어난 경우 charAt 테스트") + void charAt_StringIndexOutOfBoundsException() { + String input = "abc"; + int inputIdx = -1; + + assertThatThrownBy(() -> input.charAt(inputIdx)) + .isInstanceOf(StringIndexOutOfBoundsException.class); + } + }