From 163b9970caa9b2b2e4b084f5cbf6ef1edb1d2357 Mon Sep 17 00:00:00 2001 From: "herbi.sf8" Date: Mon, 12 Dec 2022 19:21:38 +0900 Subject: [PATCH 1/6] feat($generate): generate answer numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit new feature: - 정답 문자열 생성(3자리 숫자) - 테스트 메서드 구현 - 생성된 문자열이 3자리인가? - 생성된 문자열이 숫자로 구성돼있는가? - 중복된 숫자는 없는가? - 0이 포함되지 않는가? --- src/main/java/study/BaseballGame.java | 30 ++++++++++++ src/test/java/study/BaseballGameTest.java | 60 +++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/main/java/study/BaseballGame.java create mode 100644 src/test/java/study/BaseballGameTest.java diff --git a/src/main/java/study/BaseballGame.java b/src/main/java/study/BaseballGame.java new file mode 100644 index 00000000..2f630567 --- /dev/null +++ b/src/main/java/study/BaseballGame.java @@ -0,0 +1,30 @@ +package study; + +import java.util.HashSet; +import java.util.Random; +import java.util.Set; + +public class BaseballGame { + + private final String answer; + public BaseballGame() { + Set hashSet = new HashSet(); + String numbers = ""; + int DIGITS = 3; + for (int i =0; i hashSet) { + Random random = new Random(); + int phase = hashSet.size(); + int randomNum = -1; + while (phase == hashSet.size()) { + randomNum = random.nextInt(8) + 1; // 1 ~ 9까지의 숫자 1개 추출 + hashSet.add(randomNum); + } + return Integer.toString(randomNum); + } +} diff --git a/src/test/java/study/BaseballGameTest.java b/src/test/java/study/BaseballGameTest.java new file mode 100644 index 00000000..df5608bf --- /dev/null +++ b/src/test/java/study/BaseballGameTest.java @@ -0,0 +1,60 @@ +package study; + +import org.assertj.core.api.Assertions; +import org.assertj.core.error.ShouldBeFalse; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.util.HashSet; +import java.util.Set; + +public class BaseballGameTest { + private final String answer; + private BaseballGame bg; + private final int DIGITS = 3; + + public BaseballGameTest() throws NoSuchFieldException, IllegalAccessException { + bg = new BaseballGame(); + Field answerField = bg.getClass().getDeclaredField("answer"); + answerField.setAccessible(true); + answer = String.valueOf(answerField.get(bg)); + System.out.println("answer = " + answer); + } + @RepeatedTest(100) + @DisplayName("정답생성: 숫자가 3자리로 이루어져있는가??") + void numberGenerateTest1() { + Assertions.assertThat(answer.length()).isEqualTo(DIGITS); + } + + @RepeatedTest(100) + @DisplayName("정답생성: 숫자로만 이루어져있는가?") + void numberGenerateTest2() { + boolean flag = true; + for(int i =0; i '9' || answer.charAt(i) < '0') { + flag = false; + break; + } + } + Assertions.assertThat(flag).isTrue(); + } + + @RepeatedTest(100) + @DisplayName("정답생성: 중복되는 숫자가 없는가?") + void numberGenerateTest3() { + Set hashSet = new HashSet(); + for(int i =0; i Date: Tue, 13 Dec 2022 00:24:14 +0900 Subject: [PATCH 2/6] feat($userInput): validate user input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit new feature: - 사용자 input 받기 - input 검증 - 입력이 3자리인가? - 입력이 숫자(1~9)로 이루어져있는가? - 입력에 중복된 숫자가 없는가? --- src/main/java/study/BaseballGame.java | 57 +++++++++++++++++++- src/test/java/study/BaseballGameTest.java | 64 +++++++++++++++++++++++ 2 files changed, 119 insertions(+), 2 deletions(-) diff --git a/src/main/java/study/BaseballGame.java b/src/main/java/study/BaseballGame.java index 2f630567..c22635cf 100644 --- a/src/main/java/study/BaseballGame.java +++ b/src/main/java/study/BaseballGame.java @@ -2,19 +2,25 @@ import java.util.HashSet; import java.util.Random; +import java.util.Scanner; import java.util.Set; public class BaseballGame { private final String answer; + private final int DIGITS; + public BaseballGame() { + DIGITS = 3; + answer = generateAnswer(); + } + private String generateAnswer() { Set hashSet = new HashSet(); String numbers = ""; - int DIGITS = 3; for (int i =0; i hashSet) { @@ -27,4 +33,51 @@ private String generateNumber(Set hashSet) { } return Integer.toString(randomNum); } + + public void getUserInput() { + System.out.println("숫자를 입력해주세요: "); + Scanner scanner = new Scanner(System.in); + String userInput = scanner.nextLine(); + boolean result = validateUserInput(userInput); + scanner.close(); + } + + private boolean validateUserInput(String userInput) { + if (!validate_length(userInput)) { + throw new IllegalArgumentException("입력이 3자리가 아닙니다."); + } + else if (!validate_consistOfNumbers(userInput)) { + throw new IllegalArgumentException("입력이 숫자로 구성돼있지 않습니다."); + } + else if (!validate_duplicateNumbers(userInput)) { + throw new IllegalArgumentException("중복된 숫자가 입력됐습니다."); + } + return true; + } + + private boolean validate_duplicateNumbers(String userInput) { + Set hashSet = new HashSet(); + for(int i =0; i= '1' && c <= '9') + return 1; + return 0; + } } diff --git a/src/test/java/study/BaseballGameTest.java b/src/test/java/study/BaseballGameTest.java index df5608bf..e5621d19 100644 --- a/src/test/java/study/BaseballGameTest.java +++ b/src/test/java/study/BaseballGameTest.java @@ -2,12 +2,19 @@ import org.assertj.core.api.Assertions; import org.assertj.core.error.ShouldBeFalse; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.reflect.Field; import java.util.HashSet; +import java.util.Scanner; import java.util.Set; public class BaseballGameTest { @@ -22,6 +29,7 @@ public BaseballGameTest() throws NoSuchFieldException, IllegalAccessException { answer = String.valueOf(answerField.get(bg)); System.out.println("answer = " + answer); } + @RepeatedTest(100) @DisplayName("정답생성: 숫자가 3자리로 이루어져있는가??") void numberGenerateTest1() { @@ -57,4 +65,60 @@ void numberGenerateTest3() { void numberGenerateTest4() { Assertions.assertThat(answer.contains("0")).isFalse(); } + + @ParameterizedTest + @ValueSource(strings = {"01", "dsada", "dw", "fwqf", "\n", " ", "d"}) + @DisplayName("유저입력: 입력이 3자리가 아닐 때") + void userInputTest1(String input) { + InputStream in; + + in = new ByteArrayInputStream(input.getBytes()); + System.setIn(in); + + Assertions.assertThatThrownBy(() -> { + bg.getUserInput(); + }).isInstanceOf(IllegalArgumentException.class).hasMessage("입력이 3자리가 아닙니다."); + } + + @ParameterizedTest + @ValueSource(strings = {"12a", "aaa", "13a", "a21"}) + @DisplayName("유저입력: 입력이 숫자로 구성돼있지 않을 때") + void userInputTest2(String input) { + InputStream in; + + in = new ByteArrayInputStream(input.getBytes()); + System.setIn(in); + + Assertions.assertThatThrownBy(() -> { + bg.getUserInput(); + }).isInstanceOf(IllegalArgumentException.class).hasMessage("입력이 숫자로 구성돼있지 않습니다."); + } + + @ParameterizedTest + @ValueSource(strings = {"121", "333", "133", "225"}) + @DisplayName("유저입력: 중복된 숫자가 입력됐을 때") + void userInputTest3(String input) { + InputStream in; + + in = new ByteArrayInputStream(input.getBytes()); + System.setIn(in); + + Assertions.assertThatThrownBy(() -> { + bg.getUserInput(); + }).isInstanceOf(IllegalArgumentException.class).hasMessage("중복된 숫자가 입력됐습니다."); + } + + @ParameterizedTest + @ValueSource(strings = {"123", "532", "198", "236"}) + @DisplayName("유저입력: 정상적인 숫자가 입력됐을 때") + void userInputTest4(String input) { + InputStream in; + + in = new ByteArrayInputStream(input.getBytes()); + System.setIn(in); + + Assertions.assertThatNoException().isThrownBy(() -> { + bg.getUserInput(); + }); + } } From 70cdf2d796fb7746b128983378df28dbb06da84b Mon Sep 17 00:00:00 2001 From: "herbi.sf8" Date: Tue, 13 Dec 2022 16:07:12 +0900 Subject: [PATCH 3/6] feat($proceedRound): judge user input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit new feature: - 사용자 입력이랑 answer 비교 - strike, ball 출력 - 일치하는 수가 없으면 낫싱 출력 --- src/main/java/study/BaseballGame.java | 49 +++++++- src/main/java/study/GameResultSet.java | 34 ++++++ src/test/java/study/BaseballGameTest.java | 134 ++++++++++++++++++++++ 3 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 src/main/java/study/GameResultSet.java diff --git a/src/main/java/study/BaseballGame.java b/src/main/java/study/BaseballGame.java index c22635cf..16828141 100644 --- a/src/main/java/study/BaseballGame.java +++ b/src/main/java/study/BaseballGame.java @@ -20,6 +20,7 @@ private String generateAnswer() { for (int i =0; i hashSet) { return Integer.toString(randomNum); } - public void getUserInput() { + public String getUserInput() { System.out.println("숫자를 입력해주세요: "); Scanner scanner = new Scanner(System.in); String userInput = scanner.nextLine(); - boolean result = validateUserInput(userInput); + validateUserInput(userInput); scanner.close(); + return userInput; } private boolean validateUserInput(String userInput) { @@ -80,4 +82,47 @@ private int validate_number(char c) { return 1; return 0; } + + + public GameResultSet proceedRound() { + String userInput = getUserInput(); + Set hashSet = new HashSet(); + GameResultSet resultSet = new GameResultSet(); + for (int i = 0; i < answer.length(); i++) { + hashSet.add(answer.charAt(i)); + } + for (int i = 0; i < userInput.length(); i++) { + judgeNumber(userInput.charAt(i), answer.charAt(i), hashSet, resultSet); + } + printScore(resultSet); + return resultSet; + } + + private void judgeNumber(char ui, char aw, Set hashSet, GameResultSet resultSet) { + if (ui == aw) { + resultSet.strike += 1; + } + else if (hashSet.contains(ui)) { + resultSet.ball += 1; + } + } + + private void printScore(GameResultSet resultSet) { + if (resultSet.strike == 0 && resultSet.ball == 0) { + System.out.println("낫싱"); + } + else if (resultSet.strike == 0) { + System.out.println(resultSet.ball + "볼"); + } + else if (resultSet.ball == 0) { + System.out.println(resultSet.strike + "스트라이크"); + } + else if (resultSet.strike != 0 && resultSet.ball != 0) { + System.out.println(resultSet.strike + "스트라이크 " + resultSet.ball + "볼"); + } + } + + private boolean isCorrectAnswer(GameResultSet resultSet) { + return resultSet.strike == DIGITS && resultSet.ball == 0; + } } diff --git a/src/main/java/study/GameResultSet.java b/src/main/java/study/GameResultSet.java new file mode 100644 index 00000000..0fad5b62 --- /dev/null +++ b/src/main/java/study/GameResultSet.java @@ -0,0 +1,34 @@ +package study; + +import java.util.Objects; + +public class GameResultSet { + public int strike; + public int ball; + + public GameResultSet() { + this.strike = 0; + this.ball = 0; + } + + public GameResultSet(int strike, int ball) { + this.strike = strike; + this.ball = ball; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GameResultSet that = (GameResultSet) o; + return strike == that.strike && ball == that.ball; + } + + @Override + public String toString() { + return "GameResultSet{" + + "strike=" + strike + + ", ball=" + ball + + '}'; + } +} diff --git a/src/test/java/study/BaseballGameTest.java b/src/test/java/study/BaseballGameTest.java index e5621d19..f3fdd877 100644 --- a/src/test/java/study/BaseballGameTest.java +++ b/src/test/java/study/BaseballGameTest.java @@ -7,6 +7,8 @@ import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import java.io.ByteArrayInputStream; @@ -16,6 +18,7 @@ import java.util.HashSet; import java.util.Scanner; import java.util.Set; +import java.util.stream.Stream; public class BaseballGameTest { private final String answer; @@ -121,4 +124,135 @@ void userInputTest4(String input) { bg.getUserInput(); }); } + + + void setAnswer(String s) throws NoSuchFieldException, IllegalAccessException { + Field answerField = bg.getClass().getDeclaredField("answer"); + answerField.setAccessible(true); + answerField.set(bg, s); + } + @ParameterizedTest + @DisplayName("input 판정: 3strike") + @MethodSource("provideStringsFor3Strike") + void resultSetTest(String aw, String tg) throws NoSuchFieldException, IllegalAccessException { + setAnswer(aw); + InputStream in; + in = new ByteArrayInputStream(tg.getBytes()); + System.setIn(in); + GameResultSet rs = bg.proceedRound(); + Assertions.assertThat(rs).isEqualTo(new GameResultSet(3, 0)); + } + + private static Stream provideStringsFor3Strike() { + return Stream.of( + Arguments.of("123", "123"), + Arguments.of("475", "475"), + Arguments.of("432", "432"), + Arguments.of("215", "215") + ); + } + + @ParameterizedTest + @DisplayName("input 판정: 2strike") + @MethodSource("provideStringsFor2Strike") + void resultSetTest2(String aw, String tg) throws NoSuchFieldException, IllegalAccessException { + setAnswer(aw); + InputStream in; + in = new ByteArrayInputStream(tg.getBytes()); + System.setIn(in); + GameResultSet rs = bg.proceedRound(); + Assertions.assertThat(rs).isEqualTo(new GameResultSet(2, 0)); + } + + private static Stream provideStringsFor2Strike() { + return Stream.of( + Arguments.of("123", "124"), + Arguments.of("475", "375"), + Arguments.of("432", "482"), + Arguments.of("215", "216") + ); + } + + @ParameterizedTest + @DisplayName("input 판정: 1strike 2Ball") + @MethodSource("provideStringsFor1Strike2Ball") + void resultSetTest3(String aw, String tg) throws NoSuchFieldException, IllegalAccessException { + setAnswer(aw); + InputStream in; + in = new ByteArrayInputStream(tg.getBytes()); + System.setIn(in); + GameResultSet rs = bg.proceedRound(); + Assertions.assertThat(rs).isEqualTo(new GameResultSet(1, 2)); + } + + private static Stream provideStringsFor1Strike2Ball() { + return Stream.of( + Arguments.of("123", "132"), + Arguments.of("475", "745"), + Arguments.of("432", "234"), + Arguments.of("215", "512") + ); + } + + @ParameterizedTest + @DisplayName("input 판정: 3Ball") + @MethodSource("provideStringsFor3Ball") + void resultSetTest4(String aw, String tg) throws NoSuchFieldException, IllegalAccessException { + setAnswer(aw); + InputStream in; + in = new ByteArrayInputStream(tg.getBytes()); + System.setIn(in); + GameResultSet rs = bg.proceedRound(); + Assertions.assertThat(rs).isEqualTo(new GameResultSet(0, 3)); + } + + private static Stream provideStringsFor3Ball() { + return Stream.of( + Arguments.of("123", "312"), + Arguments.of("475", "754"), + Arguments.of("432", "324"), + Arguments.of("215", "521") + ); + } + + @ParameterizedTest + @DisplayName("input 판정: 1Ball") + @MethodSource("provideStringsFor1Ball") + void resultSetTest5(String aw, String tg) throws NoSuchFieldException, IllegalAccessException { + setAnswer(aw); + InputStream in; + in = new ByteArrayInputStream(tg.getBytes()); + System.setIn(in); + GameResultSet rs = bg.proceedRound(); + Assertions.assertThat(rs).isEqualTo(new GameResultSet(0, 1)); + } + + private static Stream provideStringsFor1Ball() { + return Stream.of( + Arguments.of("123", "451"), + Arguments.of("475", "723"), + Arguments.of("432", "526"), + Arguments.of("215", "539") + ); + } + @ParameterizedTest + @DisplayName("input 판정: Nothing") + @MethodSource("provideStringsForNothing") + void resultSetTest6(String aw, String tg) throws NoSuchFieldException, IllegalAccessException { + setAnswer(aw); + InputStream in; + in = new ByteArrayInputStream(tg.getBytes()); + System.setIn(in); + GameResultSet rs = bg.proceedRound(); + Assertions.assertThat(rs).isEqualTo(new GameResultSet(0, 0)); + } + + private static Stream provideStringsForNothing() { + return Stream.of( + Arguments.of("123", "456"), + Arguments.of("475", "362"), + Arguments.of("432", "569"), + Arguments.of("215", "698") + ); + } } From 66bbe867b6d391fc8a355e4db0dceca5674a6031 Mon Sep 17 00:00:00 2001 From: "herbi.sf8" Date: Tue, 13 Dec 2022 17:19:55 +0900 Subject: [PATCH 4/6] feat($play): play game MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit new feature: - 게임 실행 기능 - 정답 맞췄을 때 y / n 입력을 통헤 재시작 여부 결정 --- src/main/java/study/BaseballGame.java | 57 +++++++++++++++++++---- src/test/java/study/BaseballGameTest.java | 11 +++-- 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/src/main/java/study/BaseballGame.java b/src/main/java/study/BaseballGame.java index 16828141..2333d729 100644 --- a/src/main/java/study/BaseballGame.java +++ b/src/main/java/study/BaseballGame.java @@ -7,9 +7,14 @@ public class BaseballGame { - private final String answer; + private String answer; private final int DIGITS; + public static void main(String[] args) { + BaseballGame bg = new BaseballGame(); + bg.play(); + } + public BaseballGame() { DIGITS = 3; answer = generateAnswer(); @@ -35,12 +40,39 @@ private String generateNumber(Set hashSet) { return Integer.toString(randomNum); } + public void play() { + boolean flag = true; + while(flag) { + flag = playGame(); + answer = generateAnswer(); + } + } + + private boolean playGame() { + GameResultSet resultSet = new GameResultSet(); + while(!(resultSet.strike == DIGITS && resultSet.ball == 0)) { + resultSet = proceedRound(); + } + boolean flag = continueGame(); + return flag; + } + + private boolean continueGame() { + System.out.println("게임을 계속 계속하시겠습니까? (y / n)"); + Scanner scanner = new Scanner(System.in); + String input = scanner.next(); + if( input.equals("y") || input.equals("Y")) { + return true; + } + return false; + } + + public String getUserInput() { - System.out.println("숫자를 입력해주세요: "); + System.out.print("숫자를 입력해주세요: "); Scanner scanner = new Scanner(System.in); String userInput = scanner.nextLine(); validateUserInput(userInput); - scanner.close(); return userInput; } @@ -85,16 +117,21 @@ private int validate_number(char c) { public GameResultSet proceedRound() { - String userInput = getUserInput(); Set hashSet = new HashSet(); GameResultSet resultSet = new GameResultSet(); - for (int i = 0; i < answer.length(); i++) { - hashSet.add(answer.charAt(i)); - } - for (int i = 0; i < userInput.length(); i++) { - judgeNumber(userInput.charAt(i), answer.charAt(i), hashSet, resultSet); + try{ + String userInput = getUserInput(); + for (int i = 0; i < answer.length(); i++) { + hashSet.add(answer.charAt(i)); + } + for (int i = 0; i < userInput.length(); i++) { + judgeNumber(userInput.charAt(i), answer.charAt(i), hashSet, resultSet); + } + printScore(resultSet); + + }catch(IllegalArgumentException e) { + System.out.println(e); } - printScore(resultSet); return resultSet; } diff --git a/src/test/java/study/BaseballGameTest.java b/src/test/java/study/BaseballGameTest.java index f3fdd877..f2bdf491 100644 --- a/src/test/java/study/BaseballGameTest.java +++ b/src/test/java/study/BaseballGameTest.java @@ -1,11 +1,8 @@ package study; import org.assertj.core.api.Assertions; -import org.assertj.core.error.ShouldBeFalse; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.RepeatedTest; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -13,10 +10,8 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; -import java.io.OutputStream; import java.lang.reflect.Field; import java.util.HashSet; -import java.util.Scanner; import java.util.Set; import java.util.stream.Stream; @@ -255,4 +250,10 @@ private static Stream provideStringsForNothing() { Arguments.of("215", "698") ); } + +// @Test +// @DisplayName("게임 전체 테스트") +// void testGame() { +// bg.play(); +// } } From a52ffd01b2bd35dd0ba8e723525777d577aa95ff Mon Sep 17 00:00:00 2001 From: "herbi.sf8" Date: Wed, 21 Dec 2022 20:49:57 +0900 Subject: [PATCH 5/6] refactor($proceedRound, $printScore): divide logic Refactor logic in $ProceedRound, $printScore - comply code rules(each function must have no more than 10 lines.) --- src/main/java/study/BaseballGame.java | 44 +++++++++++++++------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/main/java/study/BaseballGame.java b/src/main/java/study/BaseballGame.java index 2333d729..e7b0bdb6 100644 --- a/src/main/java/study/BaseballGame.java +++ b/src/main/java/study/BaseballGame.java @@ -25,7 +25,6 @@ private String generateAnswer() { for (int i =0; i hashSet = new HashSet(); + Set hashSet = generateAnswerSet(); GameResultSet resultSet = new GameResultSet(); try{ String userInput = getUserInput(); - for (int i = 0; i < answer.length(); i++) { - hashSet.add(answer.charAt(i)); - } - for (int i = 0; i < userInput.length(); i++) { - judgeNumber(userInput.charAt(i), answer.charAt(i), hashSet, resultSet); - } + judgeNumbers(userInput, hashSet, resultSet); printScore(resultSet); - }catch(IllegalArgumentException e) { System.out.println(e); } return resultSet; } - private void judgeNumber(char ui, char aw, Set hashSet, GameResultSet resultSet) { + private void judgeNumbers(String userInput, Set hashSet, GameResultSet resultSet) { + for (int i = 0; i < userInput.length(); i++) { + judgeOneNumber(userInput.charAt(i), answer.charAt(i), hashSet, resultSet); + } + } + + private Set generateAnswerSet() { + Set hashSet = new HashSet(); + for (int i = 0; i < answer.length(); i++) { + hashSet.add(answer.charAt(i)); + } + return hashSet; + } + + private void judgeOneNumber(char ui, char aw, Set hashSet, GameResultSet resultSet) { if (ui == aw) { resultSet.strike += 1; } @@ -145,18 +151,16 @@ else if (hashSet.contains(ui)) { } private void printScore(GameResultSet resultSet) { - if (resultSet.strike == 0 && resultSet.ball == 0) { - System.out.println("낫싱"); - } - else if (resultSet.strike == 0) { - System.out.println(resultSet.ball + "볼"); + if (resultSet.strike != 0) { + System.out.print(resultSet.strike + "스트라이크 "); } - else if (resultSet.ball == 0) { - System.out.println(resultSet.strike + "스트라이크"); + if (resultSet.ball != 0) { + System.out.print(resultSet.ball + "볼"); } - else if (resultSet.strike != 0 && resultSet.ball != 0) { - System.out.println(resultSet.strike + "스트라이크 " + resultSet.ball + "볼"); + else if (resultSet.strike == 0 && resultSet.ball == 0) { + System.out.print("낫싱"); } + System.out.println(); } private boolean isCorrectAnswer(GameResultSet resultSet) { From 9e068e50316da92cace84e7170710673140b58e7 Mon Sep 17 00:00:00 2001 From: "herbi.sf8" Date: Wed, 21 Dec 2022 21:03:40 +0900 Subject: [PATCH 6/6] docs: README.md add Added new README file - feature description - core function code - Questions that arise from developing - retrospection --- README.md | 262 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 256 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fcf3f057..347fb581 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,257 @@ -# 숫자 야구 게임 -## 진행 방법 -* 숫자 야구 게임 요구사항을 파악한다. -* 요구사항에 대한 구현을 완료한 후 자신의 github 아이디에 해당하는 브랜치에 Pull Request(이하 PR)를 통해 과제를 제출한다. +# README.md -## 과제 제출 과정 -* [과제 제출 방법](https://github.com/next-step/nextstep-docs/tree/master/ent-precourse) +## 구현 기능 + +1. 1~9까지 서로 다른 수로 이루어진 3자리의 수를 생성한다. +- java.util의 random 클래스 활용 +- hashSet을 활용해 중복 숫자 생성 방지 + +1. 3자리 숫자를 입력받고 입력 값을 검증한다. +- 3자리인지 검사 +- 숫자인지 검사 +- 중복된 숫자 없는 지 검사 + +1. 정답과 입력받은 숫자를 비교해서 결과를 출력한다. +- 위치, 수가 일치하면 Strike +- 수만 일치하면 Ball +- 3Strike → 정답 처리 +- 0Strike 0Ball → 낫싱 처리 + +4. 정답을 맞추면 게임을 다시 시작하거나, 완전히 종료할 수 있도록 처리한다. + +- y 입력하면 게임 계속 +- n 입력하면 게임 중단 + +## 기능별 핵심 로직 코드 + +--- + +1. 1~9까지 서로 다른 수로 이루어진 3자리의 수를 생성한다. + + ```java + private String generateAnswer() { + Set hashSet = new HashSet(); // 중복 숫자 생성 방지를 위한 HashSet + String numbers = ""; + for (int i =0; i hashSet) { + Random random = new Random(); + int phase = hashSet.size(); + int randomNum = -1; + while (phase == hashSet.size()) { // 원래 hashSet의 크기가 달라지지 않았다면 반복 + randomNum = random.nextInt(8) + 1; // 1 ~ 9까지의 숫자 1개 추출 + hashSet.add(randomNum); + } + return Integer.toString(randomNum); + } + ``` + + +2. 3자리 숫자를 입력받고 입력 값을 검증한다. + + ```java + public String getUserInput() { + System.out.print("숫자를 입력해주세요: "); + Scanner scanner = new Scanner(System.in); + String userInput = scanner.nextLine(); + validateUserInput(userInput); // 입력값 검증 + return userInput; + } + + private boolean validateUserInput(String userInput) { + if (!validate_length(userInput)) { // 길이 검증 + throw new IllegalArgumentException("입력이 3자리가 아닙니다."); + } + else if (!validate_consistOfNumbers(userInput)) { // 숫자 구성 검증 + throw new IllegalArgumentException("입력이 숫자로 구성돼있지 않습니다."); + } + else if (!validate_duplicateNumbers(userInput)) { // 중복 숫자 검증 + throw new IllegalArgumentException("중복된 숫자가 입력됐습니다."); + } + return true; + } + + private boolean validate_duplicateNumbers(String userInput) { + Set hashSet = new HashSet(); + for(int i =0; i= '1' && c <= '9') + return 1; + return 0; + } + ``` + +3. 정답과 입력받은 숫자를 비교해서 결과를 출력한다. + + ```java + public GameResultSet proceedRound() { + Set hashSet = generateAnswerSet(); + GameResultSet resultSet = new GameResultSet(); + try{ + String userInput = getUserInput(); + judgeNumbers(userInput, hashSet, resultSet); + printScore(resultSet); + }catch(IllegalArgumentException e) { + System.out.println(e); + } + return resultSet; + } + + private void judgeNumbers(String userInput, Set hashSet, GameResultSet resultSet) { + for (int i = 0; i < userInput.length(); i++) { + judgeOneNumber(userInput.charAt(i), answer.charAt(i), hashSet, resultSet); + } + } + + private Set generateAnswerSet() { + Set hashSet = new HashSet(); + for (int i = 0; i < answer.length(); i++) { + hashSet.add(answer.charAt(i)); + } + return hashSet; + } + + private void judgeOneNumber(char ui, char aw, Set hashSet, GameResultSet resultSet) { + if (ui == aw) { + resultSet.strike += 1; + } + else if (hashSet.contains(ui)) { + resultSet.ball += 1; + } + } + + private void printScore(GameResultSet resultSet) { + if (resultSet.strike == 0 && resultSet.ball == 0) { + System.out.println("낫싱"); + } + else if (resultSet.strike == 0) { + System.out.println(resultSet.ball + "볼"); + } + else if (resultSet.ball == 0) { + System.out.println(resultSet.strike + "스트라이크"); + } + else if (resultSet.strike != 0 && resultSet.ball != 0) { + System.out.println(resultSet.strike + "스트라이크 " + resultSet.ball + "볼"); + } + } + ``` + + ```java + public class GameResultSet { + public int strike; + public int ball; + + public GameResultSet() { + this.strike = 0; + this.ball = 0; + } + + public GameResultSet(int strike, int ball) { + this.strike = strike; + this.ball = ball; + } + } + ``` + +4. 정답을 맞추면 게임을 다시 시작하거나, 완전히 종료할 수 있도록 처리한다. + + ```java + public void play() { + boolean flag = true; + while(flag) { + flag = playGame(); + answer = generateAnswer(); + } + } + + private boolean playGame() { + GameResultSet resultSet = new GameResultSet(); + while(!(resultSet.strike == DIGITS && resultSet.ball == 0)) { + resultSet = proceedRound(); + } + boolean flag = continueGame(); + return flag; + } + + private boolean continueGame() { + System.out.println("게임을 계속 계속하시겠습니까? (y / n)"); + Scanner scanner = new Scanner(System.in); + String input = scanner.next(); + if( input.equals("y") || input.equals("Y")) { + return true; + } + return false; + } + ``` + +## 개발하면서 생긴 궁금증 + +--- + +1. private 속성 Junit으로 검증하기 + - 처음에 숫자를 만들 때 private 인스턴스 변수로 만들려고 하는데 JUnit으로 어떻게 검증을 할 수 있을까? + + → getDeclaredField 속성을 통해 값 얻을 수 있음 + +2. 최대 depth 1인 것 관련 + - 3자리 랜덤 숫자를 생성하려면 숫자를 하나 만들때마다 이전에 만든 숫자와 겹치는지 확인하는 로직이 필요함. 그런데 그러려면 `반복문 + 조건문`이 필요한데 그럼 depth 2가 되버린다.. + + → hashSet을 통해 add했을 때 사이즈가 증가했는 지 여부를 반복문의 조건으로 넣어버려서 해결 (`phase == hashSet.size()`) + +3. Test Class에서 생성자 활용 문제 + - 정답 숫자를 만들고 검증하는 과정에서 여러 테스트 메서드가 있는데, 객체를 생성하고 privte field를 가져오는 과정의 코드가 중복이 발생해서 이를 테스트 클래스의 생성자에서 인스턴스를 생성해줌으로써 중복을 해결했다. + - 하지만 Test Class에서 생성자를 활용하는 것이 좋은 방법인 지는 잘 모르겠다. + +1. JUnit Test Method에서는 userInput을 받지 못하는 문제 + - 전체 로직을 테스트하려면 user input을 매번 받아야 하는데, Test는 Input을 받는 기능을 지원하지 않는다. + - Input에 따라 테스트 결과가 달라지며, CI/CD 환경을 구축하려면 Input을 받는 테스트는 지원할 수 없기 때문인 것 같다. + - 일단은 Game 클래스에 main메서드로 게임을 실행했다. + - 다른 사람들은 어떻게 Test했는지 물어봐야겠다. + +## JUnit관련 새로 알게된 점 + +--- + +- @RepeatedTest를 통해 한 테스트 메서드를 여러번 반복실행할 수 있다. + - 정답 생성 로직에서 랜덤으로 생성되는 숫자가 정답이 될 수 있는 수인지 검증하는 과정에서 반복적으로 테스트할 필요가 있다. + - 이를 어노테이션을 통해 반복해서 테스트할 수 있다. +- @ParameterizedTest를 통해 테스트메서드의 파라미터에 특정 값을 넣어서 테스트를 진행할 수 있다. + - @ValueSource를 활용하면 파라미터에 들어갈 값을 배열 형태로 넣을 수 있다. + + ``` + @ParameterizedTest + @ValueSource(strings = {"01", "dsada", "dw", "fwqf", "\n", " ", "d"}) + ``` + + + +## 느낀점 + +--- + +- 사전과제를 수행하며 메서드당 10줄 이내, depth 2 미만으로 작성하는 것이 어려워서 지키지 못했다. +- 피드백을 받을 때 다른 크루들의 코드를 봐서 참고해서 다시 수정해봐야겠다는 생각을 했다. +- TDD를 활용해서 프로그램을 만들었는데 테스트를 어떤 범위로 해야하는 지 아직 개념이 명확하지 않다고 느꼈다. +- Input을 받아서 테스트해야하는 경우에도 어떻게 테스트를 진행해야 하는 지 아직 개념이 명확하지 않다. +- 여러 미팅을 진행하며 새로운 용어를 많이 알게됐다. 위키를 참고해서 더 공부해야겠다는 생각을 했다. \ No newline at end of file