From 2827f6b61762557a1989d05816572a19f453765f Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Tue, 20 Dec 2022 17:52:52 +0900 Subject: [PATCH 01/17] =?UTF-8?q?docs:=20=EA=B5=AC=ED=98=84=ED=95=A0=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EB=AA=A9=EB=A1=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index fcf3f057..7b235a61 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,24 @@ ## 과제 제출 과정 * [과제 제출 방법](https://github.com/next-step/nextstep-docs/tree/master/ent-precourse) + +## 구현할 기능 +- 상대방이 무작위 3개의 숫자 선택 + - 선택한 3개의 숫자는 서로 달라야한다. +- 플레이어로부터 입력을 받는다. + - 입력과 정답은 같은 갯수의 숫자로 이루어져야한다. + - 입력 받은 숫자들은 서로 달라야한다. +- 플레이어의 입력과 정답으로부터 스코어를 계산한다. + - 스코어 계산은 입력의 각 자리수마다 따로 계산한다. + - 입력 받은 수가 정답과 같은 자리에 있으면 스트라이크(STRIKE)로 계산한다. + - 입력 받은 수가 정답에 존재하지만 다른 자리에 있으면 볼(BALL)로 계산한다. + - 입력 받은 수가 정답에 존재하지 않으면 낫싱(NOTHING)으로 계산한다. +- 플레이어가 입력한 숫자에 대한 결과를 출력한다. 스트라이크가 `s`개, 볼이 `b`개인 경우 출력은 다음과 같다. + - `s >= 1, b >= 1` + - `{s} 스트라이크 {b} 볼` 출력 + - `s >= 1, b == 0` + - `{s} 스트라이크` 출력 + - `s == 0, b >= 1` + - `{b} 볼` 출력 + - `s == 0, b == 0` + - `낫싱` 출력 \ No newline at end of file From ba691ad759badfbe741aba89b0827bb1685e16c3 Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Wed, 21 Dec 2022 17:27:43 +0900 Subject: [PATCH 02/17] =?UTF-8?q?feat:=20main=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/MainApplication.java | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main/java/MainApplication.java diff --git a/src/main/java/MainApplication.java b/src/main/java/MainApplication.java new file mode 100644 index 00000000..4582d694 --- /dev/null +++ b/src/main/java/MainApplication.java @@ -0,0 +1,6 @@ +public class MainApplication { + + public static void main(String[] args) { + + } +} From ae6888d1c701d51ecafe2154a70cfbdc1639ae58 Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Wed, 21 Dec 2022 17:47:53 +0900 Subject: [PATCH 03/17] =?UTF-8?q?feat:=20=EB=AC=B4=EC=9E=91=EC=9C=84=20?= =?UTF-8?q?=EC=88=AB=EC=9E=90=20=EC=84=A0=ED=83=9D=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/opponent/Opponent.java | 27 +++++++++++++++++++ src/test/java/opponent/OpponentTest.java | 34 ++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/main/java/opponent/Opponent.java create mode 100644 src/test/java/opponent/OpponentTest.java diff --git a/src/main/java/opponent/Opponent.java b/src/main/java/opponent/Opponent.java new file mode 100644 index 00000000..9f2fec5c --- /dev/null +++ b/src/main/java/opponent/Opponent.java @@ -0,0 +1,27 @@ +package opponent; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Opponent { + private final List answer; + + public Opponent() { + answer = Collections.unmodifiableList(chooseAnswer()); + } + + public List getAnswer() { + return answer; + } + + protected List chooseAnswer() { + ArrayList possibleNumbers = new ArrayList<>(); + for (int i = 1; i < 10; i++) { + possibleNumbers.add(i); + } + Collections.shuffle(possibleNumbers); + return possibleNumbers.subList(0, 3); + } + +} diff --git a/src/test/java/opponent/OpponentTest.java b/src/test/java/opponent/OpponentTest.java new file mode 100644 index 00000000..a1f0ee13 --- /dev/null +++ b/src/test/java/opponent/OpponentTest.java @@ -0,0 +1,34 @@ +package opponent; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.List; + +class OpponentTest { + + Opponent opponent = new Opponent(); + + @Test + @DisplayName("chooseAnswer로 반환된 리스트는 크기가 3이어야 한다.") + void chooseAnswer는_크기가_3인_리스트를_반환한다() { + // given when + List answer = opponent.chooseAnswer(); + + // then + Assertions.assertThat(answer).hasSize(3); + } + + @Test + @DisplayName("chooseAnswer로 반환된 리스트는 서로 다른 숫자로 이루어져 있다.") + void chooseAnswer는_중복되지_않는_숫자로_이루어진_리스트를_반환한다() { + // given when + List answer = opponent.chooseAnswer(); + HashSet answerSet = new HashSet<>(answer); + + // then + Assertions.assertThat(answer).hasSameSizeAs(answerSet); + } +} \ No newline at end of file From 5adb224b091b40bb53270aeb1de8fa8fee90bdc0 Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Wed, 21 Dec 2022 21:23:42 +0900 Subject: [PATCH 04/17] =?UTF-8?q?feat:=20=EC=9E=85=EB=A0=A5=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InputLengthValidationException.java | 10 +++ .../InputNumberDuplicationException.java | 10 +++ src/main/java/input/InputValidator.java | 27 ++++++ src/test/java/input/InputValidatorTest.java | 86 +++++++++++++++++++ 4 files changed, 133 insertions(+) create mode 100644 src/main/java/exception/InputLengthValidationException.java create mode 100644 src/main/java/exception/InputNumberDuplicationException.java create mode 100644 src/main/java/input/InputValidator.java create mode 100644 src/test/java/input/InputValidatorTest.java diff --git a/src/main/java/exception/InputLengthValidationException.java b/src/main/java/exception/InputLengthValidationException.java new file mode 100644 index 00000000..eb6295fc --- /dev/null +++ b/src/main/java/exception/InputLengthValidationException.java @@ -0,0 +1,10 @@ +package exception; + +public class InputLengthValidationException extends RuntimeException { + + public static final String INPUT_LENGTH_VALIDATION_EXCEPTION_MESSAGE = "잘못된 길이의 입력입니다."; + + public InputLengthValidationException() { + super(INPUT_LENGTH_VALIDATION_EXCEPTION_MESSAGE); + } +} diff --git a/src/main/java/exception/InputNumberDuplicationException.java b/src/main/java/exception/InputNumberDuplicationException.java new file mode 100644 index 00000000..84003b3b --- /dev/null +++ b/src/main/java/exception/InputNumberDuplicationException.java @@ -0,0 +1,10 @@ +package exception; + +public class InputNumberDuplicationException extends RuntimeException { + + public static final String INPUT_NUMBER_DUPLICATION_EXCEPTION_MESSAGE = "입력에는 중복된 숫자가 존재할 수 없습니다."; + + public InputNumberDuplicationException() { + super(INPUT_NUMBER_DUPLICATION_EXCEPTION_MESSAGE); + } +} diff --git a/src/main/java/input/InputValidator.java b/src/main/java/input/InputValidator.java new file mode 100644 index 00000000..91a2522d --- /dev/null +++ b/src/main/java/input/InputValidator.java @@ -0,0 +1,27 @@ +package input; + +import exception.InputLengthValidationException; +import exception.InputNumberDuplicationException; + +import java.util.HashSet; +import java.util.List; + +public class InputValidator { + + public void validateInput(List input) { + if (!isValidSize(input)) { + throw new InputLengthValidationException(); + } + if (hasDuplicatedNumbers(input)) { + throw new InputNumberDuplicationException(); + } + } + + private boolean isValidSize(List input) { + return input.size() == 3; + } + + private boolean hasDuplicatedNumbers(List input) { + return new HashSet<>(input).size() != input.size(); + } +} diff --git a/src/test/java/input/InputValidatorTest.java b/src/test/java/input/InputValidatorTest.java new file mode 100644 index 00000000..55d3cd33 --- /dev/null +++ b/src/test/java/input/InputValidatorTest.java @@ -0,0 +1,86 @@ +package input; + +import exception.InputLengthValidationException; +import exception.InputNumberDuplicationException; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class InputValidatorTest { + + static InputValidator inputValidator; + + @BeforeAll + static void beforeAll() { + inputValidator = new InputValidator(); + } + + @ParameterizedTest + @MethodSource("prepareInputOfInvalidSize") + void validateInput_입력_길이가_올바르지_않으면_예외_발생(List input) { + assertThatExceptionOfType(InputLengthValidationException.class) + .isThrownBy(() -> inputValidator.validateInput(input)) + .withMessage(InputLengthValidationException.INPUT_LENGTH_VALIDATION_EXCEPTION_MESSAGE); + } + + Stream prepareInputOfInvalidSize() { + return Stream.of( + Arguments.of(List.of(1)), + Arguments.of(List.of(1, 2)), + Arguments.of(List.of(1, 2, 3, 4)) + ); + } + + @ParameterizedTest + @MethodSource("prepareInputOfValidSize") + void validateInput_입력_길이가_올바르면_정상_실행(List input) { + assertDoesNotThrow(() -> inputValidator.validateInput(input)); + } + + Stream prepareInputOfValidSize() { + return Stream.of( + Arguments.of(List.of(1, 2, 3)), + Arguments.of(List.of(2, 3, 4)), + Arguments.of(List.of(4, 5, 6)) + ); + } + + @ParameterizedTest + @MethodSource("prepareInputHavingDuplicatedNumbers") + void validateInput_입력에_중복된_숫자가_존재하면_예외_발생(List input) { + assertThatExceptionOfType(InputNumberDuplicationException.class) + .isThrownBy(() -> inputValidator.validateInput(input)) + .withMessage(InputNumberDuplicationException.INPUT_NUMBER_DUPLICATION_EXCEPTION_MESSAGE); + } + + Stream prepareInputHavingDuplicatedNumbers() { + return Stream.of( + Arguments.of(List.of(1, 1, 1)), + Arguments.of(List.of(1, 1, 2)), + Arguments.of(List.of(1, 4, 4)) + ); + } + + @ParameterizedTest + @MethodSource("prepareInputNotHavingDuplicatedNumbers") + void validateInput_입력에_중복된_숫자가_없으면_정상_실행(List input) { + assertDoesNotThrow(() -> inputValidator.validateInput(input)); + } + + Stream prepareInputNotHavingDuplicatedNumbers() { + return Stream.of( + Arguments.of(List.of(1, 2, 3)), + Arguments.of(List.of(1, 4, 5)), + Arguments.of(List.of(2, 3, 4)) + ); + } +} From 441e78700a0846a39759083d562ff1c67ae04b29 Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Wed, 21 Dec 2022 21:32:31 +0900 Subject: [PATCH 05/17] =?UTF-8?q?feat:=20=EC=9E=85=EB=A0=A5=EC=9D=84=20?= =?UTF-8?q?=EC=A0=95=EC=88=98=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=ED=99=98=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/input/InputParser.java | 15 ++++++++++++++ src/test/java/input/InputParserTest.java | 25 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/main/java/input/InputParser.java create mode 100644 src/test/java/input/InputParserTest.java diff --git a/src/main/java/input/InputParser.java b/src/main/java/input/InputParser.java new file mode 100644 index 00000000..a5f8b244 --- /dev/null +++ b/src/main/java/input/InputParser.java @@ -0,0 +1,15 @@ +package input; + +import java.util.ArrayList; +import java.util.List; + +public class InputParser { + + public List toIntegerList(String input) throws NumberFormatException { + ArrayList result = new ArrayList<>(); + for (String element : input.split("")) { + result.add(Integer.parseInt(element)); + } + return result; + } +} diff --git a/src/test/java/input/InputParserTest.java b/src/test/java/input/InputParserTest.java new file mode 100644 index 00000000..3897c823 --- /dev/null +++ b/src/test/java/input/InputParserTest.java @@ -0,0 +1,25 @@ +package input; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +class InputParserTest { + + InputParser inputParser = new InputParser(); + + @ParameterizedTest + @ValueSource(strings = {"a123", "abc", "12a34"}) + void toIntegerList_숫자가_아닌_입력시_예외_발생(String input) { + assertThatExceptionOfType(NumberFormatException.class) + .isThrownBy(() -> inputParser.toIntegerList(input)); + } + + @ParameterizedTest + @ValueSource(strings = {"123", "1234", "1231241"}) + void toIntegerList_숫자만_있는_입력시_정상_실행(String input) { + assertDoesNotThrow(() -> inputParser.toIntegerList(input)); + } +} From f30adbb283c22c6b33f439dff7c927a426395d52 Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Wed, 21 Dec 2022 21:33:00 +0900 Subject: [PATCH 06/17] =?UTF-8?q?refactor:=20BeforeAll=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/input/InputValidatorTest.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/test/java/input/InputValidatorTest.java b/src/test/java/input/InputValidatorTest.java index 55d3cd33..216865dc 100644 --- a/src/test/java/input/InputValidatorTest.java +++ b/src/test/java/input/InputValidatorTest.java @@ -2,7 +2,6 @@ import exception.InputLengthValidationException; import exception.InputNumberDuplicationException; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -17,12 +16,7 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class InputValidatorTest { - static InputValidator inputValidator; - - @BeforeAll - static void beforeAll() { - inputValidator = new InputValidator(); - } + InputValidator inputValidator = new InputValidator(); @ParameterizedTest @MethodSource("prepareInputOfInvalidSize") From e318d5ed76c8248a7e5509818242415f2c9bf443 Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Wed, 21 Dec 2022 21:38:58 +0900 Subject: [PATCH 07/17] =?UTF-8?q?feat:=20=EC=9E=85=EB=A0=A5=20UI=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/ui/InputManager.java | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/main/java/ui/InputManager.java diff --git a/src/main/java/ui/InputManager.java b/src/main/java/ui/InputManager.java new file mode 100644 index 00000000..4e721ed1 --- /dev/null +++ b/src/main/java/ui/InputManager.java @@ -0,0 +1,35 @@ +package ui; + +import input.InputParser; +import input.InputValidator; + +import java.io.InputStream; +import java.util.List; +import java.util.Scanner; + +public class InputManager { + + private final Scanner sc; + private final InputValidator inputValidator; + private final InputParser inputParser; + + public InputManager() { + sc = new Scanner(System.in); + inputValidator = new InputValidator(); + inputParser = new InputParser(); + } + + private InputManager(InputStream inputStream) { + sc = new Scanner(inputStream); + inputValidator = new InputValidator(); + inputParser = new InputParser(); + } + + public List getInputNumbers() { + String inputString = sc.next(); + List input = inputParser.toIntegerList(inputString); + inputValidator.validateInput(input); + + return input; + } +} From a13041816c1520c408e1906f46c3ae55f4aaf2ea Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Wed, 21 Dec 2022 22:20:32 +0900 Subject: [PATCH 08/17] =?UTF-8?q?feat:=20=EC=9E=85=EB=A0=A5=EC=9D=98=20?= =?UTF-8?q?=EA=B0=81=20=EC=88=AB=EC=9E=90=EC=97=90=20=EB=8C=80=ED=95=B4=20?= =?UTF-8?q?=EC=A0=90=EC=88=98=20=ED=8C=90=EB=8B=A8=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/score/BaseBallCriterion.java | 9 ++ src/main/java/score/BaseBallJudgement.java | 25 +++++ .../java/score/BaseBallJudgementTest.java | 99 +++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 src/main/java/score/BaseBallCriterion.java create mode 100644 src/main/java/score/BaseBallJudgement.java create mode 100644 src/test/java/score/BaseBallJudgementTest.java diff --git a/src/main/java/score/BaseBallCriterion.java b/src/main/java/score/BaseBallCriterion.java new file mode 100644 index 00000000..9a6ec4a6 --- /dev/null +++ b/src/main/java/score/BaseBallCriterion.java @@ -0,0 +1,9 @@ +package score; + +import java.util.List; + +@FunctionalInterface +public interface BaseBallCriterion { + + boolean judge(List answer, Integer userNumber, Integer pos); +} diff --git a/src/main/java/score/BaseBallJudgement.java b/src/main/java/score/BaseBallJudgement.java new file mode 100644 index 00000000..850ac61a --- /dev/null +++ b/src/main/java/score/BaseBallJudgement.java @@ -0,0 +1,25 @@ +package score; + +import java.util.List; + +public enum BaseBallJudgement { + + STRIKE((answer, userNumber, pos) -> answer.get(pos).equals(userNumber)), + BALL((answer, userNumber, pos) -> { + if (STRIKE.hit(answer, userNumber, pos)) { + return false; + } + return answer.contains(userNumber); + }), + NOTHING((answer, userNumber, pos) -> !answer.contains(userNumber)); + + private final BaseBallCriterion criterion; + + BaseBallJudgement(BaseBallCriterion criterion) { + this.criterion = criterion; + } + + public boolean hit(List answer, Integer userNumber, Integer pos) { + return criterion.judge(answer, userNumber, pos); + } +} diff --git a/src/test/java/score/BaseBallJudgementTest.java b/src/test/java/score/BaseBallJudgementTest.java new file mode 100644 index 00000000..2fa549df --- /dev/null +++ b/src/test/java/score/BaseBallJudgementTest.java @@ -0,0 +1,99 @@ +package score; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static score.BaseBallJudgement.*; + +class BaseBallJudgementTest { + + static Stream prepareStrikeTrueData() { + return Stream.of( + Arguments.of(List.of(1, 2, 3), 1, 0), + Arguments.of(List.of(1, 2, 3), 2, 1), + Arguments.of(List.of(1, 2, 3), 3, 2) + ); + } + + static Stream prepareStrikeFalseData() { + return Stream.of( + Arguments.of(List.of(1, 2, 3), 2, 0), + Arguments.of(List.of(1, 2, 3), 2, 2), + Arguments.of(List.of(1, 2, 3), 3, 1) + ); + } + + static Stream prepareBallTrueData() { + return Stream.of( + Arguments.of(List.of(1, 2, 3), 2, 0), + Arguments.of(List.of(1, 2, 3), 2, 2), + Arguments.of(List.of(1, 2, 3), 3, 1) + ); + } + + static Stream prepareBallFalseData() { + return Stream.of( + Arguments.of(List.of(1, 2, 3), 2, 1), + Arguments.of(List.of(1, 2, 3), 4, 2), + Arguments.of(List.of(1, 2, 3), 5, 1) + ); + } + + static Stream prepareNothingTrueData() { + return Stream.of( + Arguments.of(List.of(1, 2, 3), 4, 0), + Arguments.of(List.of(1, 2, 3), 5, 2), + Arguments.of(List.of(1, 2, 3), 6, 1) + ); + } + + static Stream prepareNothingFalseData() { + return Stream.of( + Arguments.of(List.of(1, 2, 3), 2, 0), + Arguments.of(List.of(1, 2, 3), 2, 2), + Arguments.of(List.of(1, 2, 3), 3, 1) + ); + } + + @ParameterizedTest + @MethodSource("prepareStrikeTrueData") + void STRIKE_같은_위치에_같은_숫자가_있으면_스트라이크_true(List answer, Integer userNum, Integer pos) { + assertTrue(STRIKE.hit(answer, userNum, pos)); + } + + @ParameterizedTest + @MethodSource("prepareStrikeFalseData") + void STRIKE_같은_위치에_같은_숫자가_있으면_스트라이크_false(List answer, Integer userNum, Integer pos) { + assertFalse(STRIKE.hit(answer, userNum, pos)); + } + + @ParameterizedTest + @MethodSource("prepareBallTrueData") + void BALL_다른_위치에_같은_숫자가_있으면_볼_true(List answer, Integer userNum, Integer pos) { + assertTrue(BALL.hit(answer, userNum, pos)); + } + + @ParameterizedTest + @MethodSource("prepareBallFalseData") + void BALL_다른_위치에_같은_숫자가_있으면_볼_false(List answer, Integer userNum, Integer pos) { + assertFalse(BALL.hit(answer, userNum, pos)); + } + + @ParameterizedTest + @MethodSource("prepareNothingTrueData") + void NOTHING_입력한_숫자가_존재하지_않으면_낫싱_true(List answer, Integer userNum, Integer pos) { + assertTrue(NOTHING.hit(answer, userNum, pos)); + } + + @ParameterizedTest + @MethodSource("prepareNothingFalseData") + void NOTHING_입력한_숫자가_존재하지_않으면_낫싱_false(List answer, Integer userNum, Integer pos) { + assertFalse(NOTHING.hit(answer, userNum, pos)); + } +} From 02fe7cba8616e6015b7f5cb9611a313401166f25 Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Wed, 21 Dec 2022 22:45:06 +0900 Subject: [PATCH 09/17] =?UTF-8?q?feat:=20=EC=9E=85=EB=A0=A5=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/score/Score.java | 50 +++++++++++++++++++++++++++++ src/main/java/ui/OutputManager.java | 14 ++++++++ 2 files changed, 64 insertions(+) create mode 100644 src/main/java/score/Score.java create mode 100644 src/main/java/ui/OutputManager.java diff --git a/src/main/java/score/Score.java b/src/main/java/score/Score.java new file mode 100644 index 00000000..8185ffab --- /dev/null +++ b/src/main/java/score/Score.java @@ -0,0 +1,50 @@ +package score; + +import java.util.Objects; + +public class Score { + + private final Integer strikeCount; + + private final Integer ballCount; + + public Score(Integer strikeCount, Integer ballCount) { + this.strikeCount = strikeCount; + this.ballCount = ballCount; + } + + public Integer getStrikeCount() { + return strikeCount; + } + + public Integer getBallCount() { + return ballCount; + } + + public String strikeCountToString() { + if (strikeCount == 0) { + return ""; + } + return String.format("%d 스트라이크", strikeCount); + } + + public String ballCountToString() { + if (ballCount == 0) { + return ""; + } + return String.format("%d 볼", ballCount); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Score score = (Score) o; + return Objects.equals(strikeCount, score.strikeCount) && Objects.equals(ballCount, score.ballCount); + } + + @Override + public int hashCode() { + return Objects.hash(strikeCount, ballCount); + } +} diff --git a/src/main/java/ui/OutputManager.java b/src/main/java/ui/OutputManager.java new file mode 100644 index 00000000..d3de527a --- /dev/null +++ b/src/main/java/ui/OutputManager.java @@ -0,0 +1,14 @@ +package ui; + +import score.Score; + +public class OutputManager { + + public void printResult(Score score) { + if (score.getStrikeCount() == 0 && score.getBallCount() == 0) { + System.out.println("낫싱"); + return; + } + System.out.println(score.strikeCountToString() + score.ballCountToString()); + } +} From 35d58247ed8c178a9f9472abb9739bcaed1c164c Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Wed, 21 Dec 2022 22:56:20 +0900 Subject: [PATCH 10/17] =?UTF-8?q?refactor:=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=20=EC=B1=85=EC=9E=84=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/score/Score.java | 14 -------------- src/main/java/ui/OutputManager.java | 29 +++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/main/java/score/Score.java b/src/main/java/score/Score.java index 8185ffab..83c6eadf 100644 --- a/src/main/java/score/Score.java +++ b/src/main/java/score/Score.java @@ -21,20 +21,6 @@ public Integer getBallCount() { return ballCount; } - public String strikeCountToString() { - if (strikeCount == 0) { - return ""; - } - return String.format("%d 스트라이크", strikeCount); - } - - public String ballCountToString() { - if (ballCount == 0) { - return ""; - } - return String.format("%d 볼", ballCount); - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/ui/OutputManager.java b/src/main/java/ui/OutputManager.java index d3de527a..772b53f7 100644 --- a/src/main/java/ui/OutputManager.java +++ b/src/main/java/ui/OutputManager.java @@ -2,13 +2,38 @@ import score.Score; +import java.io.OutputStream; +import java.io.PrintWriter; + public class OutputManager { + private final PrintWriter printWriter; + + public OutputManager(OutputStream os) { + printWriter = new PrintWriter(os); + } + public void printResult(Score score) { if (score.getStrikeCount() == 0 && score.getBallCount() == 0) { - System.out.println("낫싱"); + printWriter.println("낫싱"); return; } - System.out.println(score.strikeCountToString() + score.ballCountToString()); + String strikeResultMessage = strikeCountToString(score.getStrikeCount()); + String ballResultMessage = ballCountToString(score.getBallCount()); + printWriter.println(strikeResultMessage + ballResultMessage); + } + + private String strikeCountToString(int strikeCount) { + if (strikeCount == 0) { + return ""; + } + return String.format("%d 스트라이크 ", strikeCount); + } + + private String ballCountToString(int ballCount) { + if (ballCount == 0) { + return ""; + } + return String.format("%d 볼 ", ballCount); } } From 654c844d31c7364e4333c76b1774daba2bad91cd Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Wed, 21 Dec 2022 22:59:01 +0900 Subject: [PATCH 11/17] =?UTF-8?q?feat=20:=20=EC=9E=85=EB=A0=A5=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80,=20=EA=B2=8C=EC=9E=84=20=EC=98=A4=EB=B2=84?= =?UTF-8?q?=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=B6=9C=EB=A0=A5=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/ui/OutputManager.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/ui/OutputManager.java b/src/main/java/ui/OutputManager.java index 772b53f7..0146e55e 100644 --- a/src/main/java/ui/OutputManager.java +++ b/src/main/java/ui/OutputManager.java @@ -13,6 +13,15 @@ public OutputManager(OutputStream os) { printWriter = new PrintWriter(os); } + public void printInputMessage() { + printWriter.print("숫자를 입력해주세요 : "); + } + + public void printGameOverMessage() { + printWriter.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료"); + printWriter.println("게임을 새로 시작하려면 1, 종료하려 2를 입력하세요."); + } + public void printResult(Score score) { if (score.getStrikeCount() == 0 && score.getBallCount() == 0) { printWriter.println("낫싱"); From 1a091bccdb221cf1ab55346e6130ec9e3270ac07 Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Thu, 22 Dec 2022 00:46:54 +0900 Subject: [PATCH 12/17] =?UTF-8?q?fix:=20=EC=B6=9C=EB=A0=A5=EC=9D=B4=20?= =?UTF-8?q?=EC=A0=95=EC=83=81=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=A3=A8=EC=96=B4=EC=A7=80=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/ui/OutputManager.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/ui/OutputManager.java b/src/main/java/ui/OutputManager.java index 0146e55e..f7763418 100644 --- a/src/main/java/ui/OutputManager.java +++ b/src/main/java/ui/OutputManager.java @@ -6,7 +6,6 @@ import java.io.PrintWriter; public class OutputManager { - private final PrintWriter printWriter; public OutputManager(OutputStream os) { @@ -15,21 +14,25 @@ public OutputManager(OutputStream os) { public void printInputMessage() { printWriter.print("숫자를 입력해주세요 : "); + printWriter.flush(); } public void printGameOverMessage() { printWriter.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료"); printWriter.println("게임을 새로 시작하려면 1, 종료하려 2를 입력하세요."); + printWriter.flush(); } public void printResult(Score score) { if (score.getStrikeCount() == 0 && score.getBallCount() == 0) { printWriter.println("낫싱"); + printWriter.flush(); return; } String strikeResultMessage = strikeCountToString(score.getStrikeCount()); String ballResultMessage = ballCountToString(score.getBallCount()); printWriter.println(strikeResultMessage + ballResultMessage); + printWriter.flush(); } private String strikeCountToString(int strikeCount) { From e1c35de60d65ef31329b79bf1bdba37419f45929 Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Thu, 22 Dec 2022 00:47:25 +0900 Subject: [PATCH 13/17] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=EA=B3=84?= =?UTF-8?q?=EC=86=8D=20=EC=A7=84=ED=96=89=20=EC=97=AC=EB=B6=80=EB=A5=BC=20?= =?UTF-8?q?=EB=AC=BB=EB=8A=94=20=EC=BB=A4=EB=A9=98=EB=93=9C=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/ui/InputManager.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/ui/InputManager.java b/src/main/java/ui/InputManager.java index 4e721ed1..3da1448b 100644 --- a/src/main/java/ui/InputManager.java +++ b/src/main/java/ui/InputManager.java @@ -1,5 +1,6 @@ package ui; +import game.GameCommand; import input.InputParser; import input.InputValidator; @@ -19,7 +20,7 @@ public InputManager() { inputParser = new InputParser(); } - private InputManager(InputStream inputStream) { + public InputManager(InputStream inputStream) { sc = new Scanner(inputStream); inputValidator = new InputValidator(); inputParser = new InputParser(); @@ -32,4 +33,9 @@ public List getInputNumbers() { return input; } + + public GameCommand getInputGameCommand() { + int gameCommand = sc.nextInt(); + return GameCommand.getCommand(gameCommand); + } } From bca974873086021482a68912aefae1952715dee5 Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Thu, 22 Dec 2022 00:47:46 +0900 Subject: [PATCH 14/17] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=EC=9D=98=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=EC=9D=84=20=EB=8B=B4=EB=8B=B9=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/settings/GameSetting.java | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/main/java/settings/GameSetting.java diff --git a/src/main/java/settings/GameSetting.java b/src/main/java/settings/GameSetting.java new file mode 100644 index 00000000..4864e22b --- /dev/null +++ b/src/main/java/settings/GameSetting.java @@ -0,0 +1,32 @@ +package settings; + +import ui.InputManager; +import ui.OutputManager; + +public class GameSetting { + + private final InputManager inputManager; + + private final OutputManager outputManager; + + private GameSetting() { + inputManager = new InputManager(System.in); + outputManager = new OutputManager(System.out); + } + + public static GameSetting getInstance() { + return GameSettingHolder.GAME_SETTING; + } + + public InputManager getInputManager() { + return inputManager; + } + + public OutputManager getOutputManager() { + return outputManager; + } + + private static class GameSettingHolder { + private static final GameSetting GAME_SETTING = new GameSetting(); + } +} From f60bebfce45acad594e775d66a0167572d1a5a74 Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Thu, 22 Dec 2022 00:48:14 +0900 Subject: [PATCH 15/17] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=EC=BB=A4?= =?UTF-8?q?=EB=A9=98=EB=93=9C=20enum=20=EA=B0=9D=EC=B2=B4=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GameCommandNoutFoundException.java | 10 +++++ src/main/java/game/GameCommand.java | 41 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/main/java/exception/GameCommandNoutFoundException.java create mode 100644 src/main/java/game/GameCommand.java diff --git a/src/main/java/exception/GameCommandNoutFoundException.java b/src/main/java/exception/GameCommandNoutFoundException.java new file mode 100644 index 00000000..46af5a94 --- /dev/null +++ b/src/main/java/exception/GameCommandNoutFoundException.java @@ -0,0 +1,10 @@ +package exception; + +public class GameCommandNoutFoundException extends RuntimeException { + + public static final String GAME_COMMAND_NOT_FOUND_EXCEPTION = "잘못된 커멘드 입력입니다."; + + public GameCommandNoutFoundException() { + super(GAME_COMMAND_NOT_FOUND_EXCEPTION); + } +} diff --git a/src/main/java/game/GameCommand.java b/src/main/java/game/GameCommand.java new file mode 100644 index 00000000..8cc63e09 --- /dev/null +++ b/src/main/java/game/GameCommand.java @@ -0,0 +1,41 @@ +package game; + +import exception.GameCommandNoutFoundException; + +import java.util.HashMap; +import java.util.Map; + +public enum GameCommand { + + RESTART(1), + END(2); + + private static final Map gameCommandMapping = new HashMap<>(); + + static { + for (GameCommand gameCommand : GameCommand.values()) { + gameCommandMapping.put( + gameCommand.getCommandNumber(), + gameCommand + ); + } + } + + private final int commandNumber; + + GameCommand(int commandNumber) { + this.commandNumber = commandNumber; + } + + public static GameCommand getCommand(int commandNumber) { + if (!gameCommandMapping.containsKey(commandNumber)) { + throw new GameCommandNoutFoundException(); + } + + return gameCommandMapping.get(commandNumber); + } + + public int getCommandNumber() { + return commandNumber; + } +} From a64a2812aef0ba55b97d4423a1aefa7e9224756f Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Thu, 22 Dec 2022 00:49:03 +0900 Subject: [PATCH 16/17] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=EB=9D=BC?= =?UTF-8?q?=EC=9A=B4=EB=93=9C=20=EC=A7=84=ED=96=89=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/game/BaseBallGame.java | 43 ++++++++++++++++++++++++++++ src/main/java/score/Score.java | 19 ++++++++++-- 2 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 src/main/java/game/BaseBallGame.java diff --git a/src/main/java/game/BaseBallGame.java b/src/main/java/game/BaseBallGame.java new file mode 100644 index 00000000..ad51f46d --- /dev/null +++ b/src/main/java/game/BaseBallGame.java @@ -0,0 +1,43 @@ +package game; + +import opponent.Opponent; +import score.Score; +import settings.GameSetting; +import ui.InputManager; +import ui.OutputManager; + +import java.util.List; + +public class BaseBallGame { + + private final Opponent opponent; + + public BaseBallGame(Opponent opponent) { + this.opponent = opponent; + } + + public void start() { + Score score; + do { + score = nextStage(); + } while (!isGameOver(score)); + GameSetting.getInstance().getOutputManager().printGameOverMessage(); + } + + private boolean isGameOver(Score score) { + return score.getStrikeCount() == 3; + } + + private Score nextStage() { + Score score = new Score(0, 0); + OutputManager outputManager = GameSetting.getInstance().getOutputManager(); + InputManager inputManager = GameSetting.getInstance().getInputManager(); + outputManager.printInputMessage(); + List inputNumbers = inputManager.getInputNumbers(); + for (int i = 0; i < inputNumbers.size(); i++) { + score.updateScore(opponent.getAnswer(), inputNumbers.get(i), i); + } + outputManager.printResult(score); + return score; + } +} diff --git a/src/main/java/score/Score.java b/src/main/java/score/Score.java index 83c6eadf..39a91c66 100644 --- a/src/main/java/score/Score.java +++ b/src/main/java/score/Score.java @@ -1,12 +1,16 @@ package score; +import java.util.List; import java.util.Objects; +import static score.BaseBallJudgement.BALL; +import static score.BaseBallJudgement.STRIKE; + public class Score { - private final Integer strikeCount; + private Integer strikeCount; - private final Integer ballCount; + private Integer ballCount; public Score(Integer strikeCount, Integer ballCount) { this.strikeCount = strikeCount; @@ -21,6 +25,17 @@ public Integer getBallCount() { return ballCount; } + public void updateScore(List answer, Integer userNumber, Integer pos) { + if (STRIKE.hit(answer, userNumber, pos)) { + strikeCount += 1; + return; + } + if (BALL.hit(answer, userNumber, pos)) { + ballCount += 1; + return; + } + } + @Override public boolean equals(Object o) { if (this == o) return true; From dcc6ea02e66766574fc80124b3daa7840acbdcba Mon Sep 17 00:00:00 2001 From: jungin0507 Date: Thu, 22 Dec 2022 00:49:12 +0900 Subject: [PATCH 17/17] =?UTF-8?q?feat:=20=ED=94=8C=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/MainApplication.java | 5 ++++- src/main/java/player/Player.java | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 src/main/java/player/Player.java diff --git a/src/main/java/MainApplication.java b/src/main/java/MainApplication.java index 4582d694..64ca1c2a 100644 --- a/src/main/java/MainApplication.java +++ b/src/main/java/MainApplication.java @@ -1,6 +1,9 @@ +import player.Player; + public class MainApplication { public static void main(String[] args) { - + Player player = new Player(); + player.start(); } } diff --git a/src/main/java/player/Player.java b/src/main/java/player/Player.java new file mode 100644 index 00000000..d2a013c2 --- /dev/null +++ b/src/main/java/player/Player.java @@ -0,0 +1,25 @@ +package player; + +import game.BaseBallGame; +import game.GameCommand; +import opponent.Opponent; +import settings.GameSetting; +import ui.InputManager; + +public class Player { + + public void start() { + InputManager inputManager = GameSetting.getInstance().getInputManager(); + do { + playRound(); + } while (inputManager.getInputGameCommand() == GameCommand.RESTART); + } + + protected void playRound() { + Opponent opponent = new Opponent(); + BaseBallGame baseBallGame = new BaseBallGame(opponent); + baseBallGame.start(); + } + + +}