From 88ae8efda5fb2afa9e09c196101953e9bbe24165 Mon Sep 17 00:00:00 2001 From: "jade.xyz" Date: Tue, 20 Dec 2022 11:10:20 +0900 Subject: [PATCH 1/8] =?UTF-8?q?docs(README.md):=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=20=EB=B6=84=EC=84=9D=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B3=A0=EB=A0=A4=EC=82=AC=ED=95=AD=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index fcf3f057..8c2c2643 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,23 @@ ## 과제 제출 과정 * [과제 제출 방법](https://github.com/next-step/nextstep-docs/tree/master/ent-precourse) + +## 기능 요구사항 분석 +- 임의의 정답 생성 + - 컴퓨터는 게임 시작 시 1부터 9까지 서로 다른 수로 이루어진 3자리 수를 임의로 생성한다. +- 정답 입력 + - 사용자는 1부터 9까지 서로 다른 수로 이루어진 3자리 수를 입력할 수 있다. + - 정답을 맞출 때까지 사용자는 반복해서 정답을 입력한다. +- 정답 비교 + - 사용자가 입력을 수행할 때마다 컴퓨터는 임의로 생성된 수와 비교하여 결과를 출력한다. + - 같은 수가 같은 자리에 있으면 **스트라이크** + - 같은 수가 다른 자리에 있으면 **볼** + - 같은 수가 전혀 없으면 **낫싱** + - 출력의 우선순위는 스트라이크가 볼보다 높다. ex) 1 스트라이크 1볼 +- 게임 재시작 or 종료 + - 사용자가 정답을 맞출 경우 게임을 재시작하거나 완전히 종료할 수 있다. + +## 고려사항 +- 정답의 길이가 항상 3일까? -> 3자리 이상의 숫자 야구 가능 +- 항상 정답을 임의의 수로만 생성할까? -> 직접 입력으로 변경 가능 +- 항상 서로 다른 수로만 이루어질까? -> 규칙이기 때문에 바뀌지 않음 \ No newline at end of file From 1da5185921ded10b22a688117c01fd4d3e6e51bf Mon Sep 17 00:00:00 2001 From: "jade.xyz" Date: Wed, 21 Dec 2022 15:38:00 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat(Main):=20=EC=9E=84=EC=9D=98=EC=9D=98?= =?UTF-8?q?=20=EC=A0=95=EB=8B=B5=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/AnswerMaker.java | 3 +++ src/main/java/GamePlayer.java | 16 ++++++++++++++++ src/main/java/GameStarter.java | 6 ++++++ src/main/java/RandomAnswerMaker.java | 18 ++++++++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 src/main/java/AnswerMaker.java create mode 100644 src/main/java/GamePlayer.java create mode 100644 src/main/java/GameStarter.java create mode 100644 src/main/java/RandomAnswerMaker.java diff --git a/src/main/java/AnswerMaker.java b/src/main/java/AnswerMaker.java new file mode 100644 index 00000000..a12c1b01 --- /dev/null +++ b/src/main/java/AnswerMaker.java @@ -0,0 +1,3 @@ +public interface AnswerMaker { + String makeAnswer(int length); +} diff --git a/src/main/java/GamePlayer.java b/src/main/java/GamePlayer.java new file mode 100644 index 00000000..caceeff7 --- /dev/null +++ b/src/main/java/GamePlayer.java @@ -0,0 +1,16 @@ +public class GamePlayer { + + private final int length; + + public GamePlayer(int length) { + this.length = length; + } + + public void start() { + // 정답 생성 + AnswerMaker answerMaker = new RandomAnswerMaker(); + String answer = answerMaker.makeAnswer(length); + } + + +} diff --git a/src/main/java/GameStarter.java b/src/main/java/GameStarter.java new file mode 100644 index 00000000..a1fb7868 --- /dev/null +++ b/src/main/java/GameStarter.java @@ -0,0 +1,6 @@ +public class GameStarter { + public static void main(String[] args) { + GamePlayer gamePlayer = new GamePlayer(3); + gamePlayer.start(); + } +} diff --git a/src/main/java/RandomAnswerMaker.java b/src/main/java/RandomAnswerMaker.java new file mode 100644 index 00000000..903a2030 --- /dev/null +++ b/src/main/java/RandomAnswerMaker.java @@ -0,0 +1,18 @@ +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class RandomAnswerMaker implements AnswerMaker { + + @Override + public String makeAnswer(int length) { + List numbers = new ArrayList<>(); + for (int i = 1; i < 10; i++) { + numbers.add(String.valueOf(i)); + } + Collections.shuffle(numbers); + + return numbers.subList(0, length).toString(); + } + +} From dc6e3aade1618b45d88e9be3b7b9b99e1e7c23bf Mon Sep 17 00:00:00 2001 From: "jade.xyz" Date: Wed, 21 Dec 2022 16:48:08 +0900 Subject: [PATCH 3/8] =?UTF-8?q?test(AnswerMaker):=20=EC=9E=84=EC=9D=98?= =?UTF-8?q?=EC=9D=98=20=EC=A0=95=EB=8B=B5=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/AnswerMakerTest.java | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/test/java/AnswerMakerTest.java diff --git a/src/test/java/AnswerMakerTest.java b/src/test/java/AnswerMakerTest.java new file mode 100644 index 00000000..18c6432a --- /dev/null +++ b/src/test/java/AnswerMakerTest.java @@ -0,0 +1,36 @@ +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AnswerMakerTest { + private final AnswerMaker answerMaker; + + public AnswerMakerTest() { + this.answerMaker = new RandomAnswerMaker(); + } + + @Test + @DisplayName("생성된 수는 모두 다른 수로 이루어져있는가?") + public void checkDuplicationInAnswer() { + String answer = answerMaker.makeAnswer(3); + + assertThat(checkDuplication(answer)).isTrue(); + } + + @Test + @DisplayName("생성된 수는 세자리 수인가?") + public void checkAnswerSize() { + String answer = answerMaker.makeAnswer(3); + + assertThat(answer.length()).isEqualTo(3); + } + + private boolean checkDuplication(String str) { + if (str.charAt(0) == str.charAt(1)) return false; + if (str.charAt(1) == str.charAt(2)) return false; + if (str.charAt(0) == str.charAt(2)) return false; + return true; + } + +} From 529a70997195daaaf25d7f8aa8c27c3554e030d1 Mon Sep 17 00:00:00 2001 From: "jade.xyz" Date: Wed, 21 Dec 2022 22:13:02 +0900 Subject: [PATCH 4/8] =?UTF-8?q?fix(AnswerMaker):=20=EC=A0=95=EB=8B=B5=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=ED=98=95=ED=83=9C=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/RandomAnswerMaker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/RandomAnswerMaker.java b/src/main/java/RandomAnswerMaker.java index 903a2030..60322358 100644 --- a/src/main/java/RandomAnswerMaker.java +++ b/src/main/java/RandomAnswerMaker.java @@ -12,7 +12,7 @@ public String makeAnswer(int length) { } Collections.shuffle(numbers); - return numbers.subList(0, length).toString(); + return String.join("", numbers.subList(0, length)); } } From 5861d0f3950bb8990966c703fc81ed7025f24428 Mon Sep 17 00:00:00 2001 From: "jade.xyz" Date: Wed, 21 Dec 2022 23:00:35 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat(Main):=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=20=EC=A0=95=EB=8B=B5=20=EC=9E=85=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/GamePlayer.java | 5 +++ src/main/java/UI/InputUI.java | 34 ++++++++++++++++++++ src/test/java/UI/InputUITest.java | 53 +++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 src/main/java/UI/InputUI.java create mode 100644 src/test/java/UI/InputUITest.java diff --git a/src/main/java/GamePlayer.java b/src/main/java/GamePlayer.java index caceeff7..7206cf77 100644 --- a/src/main/java/GamePlayer.java +++ b/src/main/java/GamePlayer.java @@ -1,3 +1,5 @@ +import UI.InputUI; + public class GamePlayer { private final int length; @@ -10,6 +12,9 @@ public void start() { // 정답 생성 AnswerMaker answerMaker = new RandomAnswerMaker(); String answer = answerMaker.makeAnswer(length); + + // 정답 입력 + String userInput = InputUI.getAnswerByUser(length); } diff --git a/src/main/java/UI/InputUI.java b/src/main/java/UI/InputUI.java new file mode 100644 index 00000000..51526b29 --- /dev/null +++ b/src/main/java/UI/InputUI.java @@ -0,0 +1,34 @@ +package UI; + +import java.util.Scanner; + +public class InputUI { + private static final Scanner sc = new Scanner(System.in); + + public static String getAnswerByUser(int length) { + System.out.print("서로 다른 수로 이루어진 세자리 수를 입력해주세요 : "); + String userInput = sc.next(); + while (userInput.length() != length || !isValidInput(userInput)) { + System.out.print("\n올바르지 않은 입력입니다.\n" + + "서로 다른 수로 이루어진 세자리 수를 입력해주세요 : "); + userInput = sc.next(); + } + + return userInput; + } + + private static boolean isValidInput(String input) { + StringBuilder sb = new StringBuilder(); + for (char c : input.toCharArray()) { + appendUniqueChar(sb, c); + } + + return input.equals(sb.toString()); + } + + private static void appendUniqueChar(StringBuilder sb, char c) { + if (!sb.toString().contains(String.valueOf(c)) && Character.isDigit(c)) { + sb.append(c); + } + } +} diff --git a/src/test/java/UI/InputUITest.java b/src/test/java/UI/InputUITest.java new file mode 100644 index 00000000..c7154c00 --- /dev/null +++ b/src/test/java/UI/InputUITest.java @@ -0,0 +1,53 @@ +package UI; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import static org.assertj.core.api.Assertions.assertThat; + +public class InputUITest { + + @Test + @DisplayName("사용자가 입력한 답은 숫자로만 이루어져 있는가?") + void checkUserInputIsNum() { + inputStringToStream("123"); + String userInput = InputUI.getAnswerByUser(3); + + for (int i = 0; i < userInput.length(); i++) { + assertThat(userInput.charAt(i)).isBetween('1', '9'); + } + } + + @Test + @DisplayName("사용자가 입력한 답은 세자리 수인가?") + void checkUserInput() { + inputStringToStream("456"); + String userInput = InputUI.getAnswerByUser(3); + + assertThat(userInput.length()).isEqualTo(3); + } + + @Test + @DisplayName("사용자가 입력한 답은 서로 다른 수로 이루어져 있는가?") + void test() { + inputStringToStream("789"); + String userInput = InputUI.getAnswerByUser(3); + + assertThat(checkDuplication(userInput)).isTrue(); + } + + private void inputStringToStream(String input) { + InputStream in = new ByteArrayInputStream(input.getBytes()); + System.setIn(in); + } + + private boolean checkDuplication(String str) { + if (str.charAt(0) == str.charAt(1)) return false; + if (str.charAt(1) == str.charAt(2)) return false; + if (str.charAt(0) == str.charAt(2)) return false; + return true; + } +} \ No newline at end of file From 7753bcf7a162b480d8498808df5d2def08c2f992 Mon Sep 17 00:00:00 2001 From: "jade.xyz" Date: Thu, 22 Dec 2022 12:44:43 +0900 Subject: [PATCH 6/8] =?UTF-8?q?feat(Main):=20=EC=A0=95=EB=8B=B5=20?= =?UTF-8?q?=EB=B9=84=EA=B5=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/GamePlayer.java | 53 ++++++++++++++++++++ src/test/java/GamePlayerTest.java | 82 +++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 src/test/java/GamePlayerTest.java diff --git a/src/main/java/GamePlayer.java b/src/main/java/GamePlayer.java index 7206cf77..e50aaef3 100644 --- a/src/main/java/GamePlayer.java +++ b/src/main/java/GamePlayer.java @@ -3,6 +3,8 @@ public class GamePlayer { private final int length; + private String answer; + private String userInput; public GamePlayer(int length) { this.length = length; @@ -17,5 +19,56 @@ public void start() { String userInput = InputUI.getAnswerByUser(length); } + public void start() { + System.out.println("게임을 시작합니다."); + // 정답 생성 + AnswerMaker answerMaker = new RandomAnswerMaker(); + answer = answerMaker.makeAnswer(length); + + boolean isCorrect = false; + while (!isCorrect) { + // 정답 입력 + userInput = InputUI.getAnswerByUser(length); + isCorrect = compareInputWithAnswer(); + } + } + + private boolean compareInputWithAnswer() { + int strikeCnt = 0; + int ballCnt = 0; + for (int i = 0; i < userInput.length(); i++) { + strikeCnt += isStrike(i); + ballCnt += isBall(i); + } + + printResult(strikeCnt, ballCnt); + if (strikeCnt == length) return true; + return false; + } + + private int isStrike(int idx) { + if (answer.indexOf(userInput.charAt(idx)) == idx) + return 1; + return 0; + } + private int isBall(int idx) { + if (answer.indexOf(userInput.charAt(idx)) != -1 && answer.indexOf(userInput.charAt(idx)) != idx) + return 1; + return 0; + } + + private void printResult(int strikeCnt, int ballCnt) { + if (strikeCnt > 0) + System.out.print(strikeCnt + "스트라이크 "); + if (ballCnt > 0) + System.out.print(ballCnt + "볼"); + if (strikeCnt == 0 && ballCnt == 0) + System.out.print("낫싱"); + System.out.println(); + + if (strikeCnt == length) { + System.out.println(length + "개의 숫자를 모두 맞히셨습니다! 게임 종료"); + } + } } diff --git a/src/test/java/GamePlayerTest.java b/src/test/java/GamePlayerTest.java new file mode 100644 index 00000000..ff744c94 --- /dev/null +++ b/src/test/java/GamePlayerTest.java @@ -0,0 +1,82 @@ +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import static org.assertj.core.api.Assertions.assertThat; + +class GamePlayerTest { + + private final ByteArrayOutputStream out = new ByteArrayOutputStream(); + + private final GamePlayer gamePlayer; + + public GamePlayerTest() { + this.gamePlayer = new GamePlayer(3); + } + + @Test + @DisplayName("정답 여부가 맞게 결정되는가?") + void test1() throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException { + // private 메서드 접근 허용 + Method testMethod = GamePlayer.class.getDeclaredMethod("compareInputWithAnswer"); + Field answerField = GamePlayer.class.getDeclaredField("answer"); + Field userInputField = GamePlayer.class.getDeclaredField("userInput"); + testMethod.setAccessible(true); + answerField.setAccessible(true); + userInputField.setAccessible(true); + + answerField.set(gamePlayer, "123"); + userInputField.set(gamePlayer, "321"); + + boolean actual = (boolean) testMethod.invoke(gamePlayer); + + assertThat(actual).isFalse(); + } + + @Test + @DisplayName("힌트가 맞게 출력되는가?") + void test2() throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException { + // private 메서드 접근 허용 + Method testMethod = GamePlayer.class.getDeclaredMethod("compareInputWithAnswer"); + Field answerField = GamePlayer.class.getDeclaredField("answer"); + Field userInputField = GamePlayer.class.getDeclaredField("userInput"); + testMethod.setAccessible(true); + answerField.setAccessible(true); + userInputField.setAccessible(true); + + System.setOut(new PrintStream(out)); + + answerField.set(gamePlayer, "123"); + userInputField.set(gamePlayer, "321"); + + testMethod.invoke(gamePlayer); + + assertThat(out.toString().strip()).isEqualTo("1스트라이크 2볼"); + } + + @Test + @DisplayName("일치하는 수가 없는 경우 '낫싱'이 출력되는가?") + void test3() throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException { + // private 메서드 접근 허용 + Method testMethod = GamePlayer.class.getDeclaredMethod("compareInputWithAnswer"); + Field answerField = GamePlayer.class.getDeclaredField("answer"); + Field userInputField = GamePlayer.class.getDeclaredField("userInput"); + testMethod.setAccessible(true); + answerField.setAccessible(true); + userInputField.setAccessible(true); + + System.setOut(new PrintStream(out)); + + answerField.set(gamePlayer, "123"); + userInputField.set(gamePlayer, "456"); + + testMethod.invoke(gamePlayer); + + assertThat(out.toString().strip()).isEqualTo("낫싱"); + } +} \ No newline at end of file From f044d604a2289e597a90ae7aa3e8d20f4fc7f5dc Mon Sep 17 00:00:00 2001 From: "jade.xyz" Date: Thu, 22 Dec 2022 12:56:45 +0900 Subject: [PATCH 7/8] =?UTF-8?q?feat(Main):=20=EA=B2=8C=EC=9E=84=20?= =?UTF-8?q?=EC=9E=AC=EC=8B=9C=EC=9E=91=20or=20=EC=A2=85=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/GamePlayer.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/GamePlayer.java b/src/main/java/GamePlayer.java index e50aaef3..81392933 100644 --- a/src/main/java/GamePlayer.java +++ b/src/main/java/GamePlayer.java @@ -11,15 +11,17 @@ public GamePlayer(int length) { } public void start() { - // 정답 생성 - AnswerMaker answerMaker = new RandomAnswerMaker(); - String answer = answerMaker.makeAnswer(length); - - // 정답 입력 - String userInput = InputUI.getAnswerByUser(length); + boolean isRestart = false; + while (!isRestart) { + playGame(); + String userInput = InputUI.getRestartFlag(); + isRestart = userInput.equals("2"); + System.out.println(); + } + System.out.println("게임을 완전히 종료합니다."); } - public void start() { + private void playGame() { System.out.println("게임을 시작합니다."); // 정답 생성 AnswerMaker answerMaker = new RandomAnswerMaker(); From af0fa3f651eafb148b9e8d8d0f8af614ac898245 Mon Sep 17 00:00:00 2001 From: "jade.xyz" Date: Thu, 22 Dec 2022 13:00:13 +0900 Subject: [PATCH 8/8] =?UTF-8?q?feat(Main):=20=EA=B2=8C=EC=9E=84=20?= =?UTF-8?q?=EC=9E=AC=EC=8B=9C=EC=9E=91=20or=20=EC=A2=85=EB=A3=8C=20-=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=9E=85=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/UI/InputUI.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/UI/InputUI.java b/src/main/java/UI/InputUI.java index 51526b29..1a252a3d 100644 --- a/src/main/java/UI/InputUI.java +++ b/src/main/java/UI/InputUI.java @@ -17,6 +17,17 @@ public static String getAnswerByUser(int length) { return userInput; } + public static String getRestartFlag() { + System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."); + String userInput = sc.next(); + while (!userInput.equals("1") && !userInput.equals("2")) { + System.out.println("올바르지 않은 입력입니다.\n" + + "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."); + userInput = sc.next(); + } + return userInput; + } + private static boolean isValidInput(String input) { StringBuilder sb = new StringBuilder(); for (char c : input.toCharArray()) {