From 6839d1c99cc70d030f77782b0143338ddd4446e8 Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Fri, 30 Jan 2026 16:09:45 +0900 Subject: [PATCH 01/22] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 28 +++++++++++++++++++++++++++- src/main/java/Application.java | 4 ++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/main/java/Application.java diff --git a/README.md b/README.md index 8d7e8aee..01978827 100644 --- a/README.md +++ b/README.md @@ -1 +1,27 @@ -# java-baseball-precourse \ No newline at end of file +# 숫자 야구 게임 기능 구현 목록 + +## 게임 초기화 (컴퓨터) + +* 1에서 9까지의 서로 다른 임의의 수 3개를 생성한다. + +## 사용자 입력 + +* 플레이어로부터 서로 다른 3자리의 수를 입력받는다. +* 게임 종료 후 재시작(1) 또는 종료(2) 여부를 입력받는다. + +## 입력 유효성 검사 + +* 사용자가 잘못된 값을 입력할 경우 [ERROR]로 시작하는 에러 메시지를 출력한다. +* 에러 메시지 출력 후 게임을 종료하지 않고 다시 입력을 받는다. + +## 게임 결과 판정 (볼/스트라이크 계산) + +* 컴퓨터의 수와 플레이어의 수를 비교하여 결과를 계산한다. +* 같은 수가 같은 자리에 있으면 스트라이크. +* 같은 수가 다른 자리에 있으면 볼. +* 같은 수가 전혀 없으면 낫싱. + +## 결과 출력 + +* 계산된 볼, 스트라이크 개수를 출력한다 (예: 1볼 1스트라이크, 낫싱). +* 3개의 숫자를 모두 맞히면(3스트라이크) 게임 종료 문구를 출력한다. \ No newline at end of file diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 00000000..0a08323d --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,4 @@ +package PACKAGE_NAME; + +public class Application { +} From 53fa97b4234df18366f8173516eaad04a875202a Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Fri, 30 Jan 2026 16:10:26 +0900 Subject: [PATCH 02/22] =?UTF-8?q?chore:=20=ED=94=84=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EB=9E=A8=20=EC=B4=88=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/Application.java b/src/main/java/Application.java index 0a08323d..da12685b 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,4 +1,5 @@ -package PACKAGE_NAME; - public class Application { -} + public static void main(String[] args) { + System.out.println("Hello, World!"); + } +} \ No newline at end of file From a5cf7e6036e6a0ed69e0d8900b55d349fe7e76dd Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 00:06:29 +0900 Subject: [PATCH 03/22] =?UTF-8?q?=EB=8B=A4=EC=9D=B4=EC=96=B4=20=EA=B7=B8?= =?UTF-8?q?=EB=9E=A8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 01978827..d633e88b 100644 --- a/README.md +++ b/README.md @@ -24,4 +24,43 @@ ## 결과 출력 * 계산된 볼, 스트라이크 개수를 출력한다 (예: 1볼 1스트라이크, 낫싱). -* 3개의 숫자를 모두 맞히면(3스트라이크) 게임 종료 문구를 출력한다. \ No newline at end of file +* 3개의 숫자를 모두 맞히면(3스트라이크) 게임 종료 문구를 출력한다. + +```mermaid +classDiagram + class GameFlowController { + <> + +게임_전체_흐름_제어() + +재시작_여부_판단() + } + + class SecretNumberModel { + <> + -정답_숫자_데이터 + +랜덤_3자리_생성() + } + + class HintCalculatorModel { + <> + +스트라이크_볼_판정() + +결과_데이터_생성() + } + + class PlayerInputView { + <> + +숫자_입력_받기() + +입력_값_유효성_검증() + } + + class GameResultView { + <> + +힌트_메시지_출력() + +승리_및_종료_출력() + +에러_메시지_출력() + } + + GameFlowController --> SecretNumberModel: 정답 생성 요청 + GameFlowController --> PlayerInputView: 사용자 입력 요청 + GameFlowController --> HintCalculatorModel: 판정 로직 실행 + GameFlowController --> GameResultView: 화면 출력 요청 +``` \ No newline at end of file From 31ce83048936f568db7670c7c4a239a584b6d6db Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 01:08:12 +0900 Subject: [PATCH 04/22] =?UTF-8?q?feat:=20=EA=B8=B0=EB=8A=A5=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 --- src/main/java/.gitkeep | 0 src/main/java/Application.java | 6 ++- .../java/baseball/GameFlowController.java | 53 +++++++++++++++++++ src/main/java/baseball/GameResultView.java | 16 ++++++ .../java/baseball/HintCalculatorModel.java | 18 +++++++ src/main/java/baseball/HintResult.java | 30 +++++++++++ .../java/baseball/PlayerInputValidator.java | 41 ++++++++++++++ src/main/java/baseball/SecretNumberModel.java | 21 ++++++++ 8 files changed, 183 insertions(+), 2 deletions(-) delete mode 100644 src/main/java/.gitkeep create mode 100644 src/main/java/baseball/GameFlowController.java create mode 100644 src/main/java/baseball/GameResultView.java create mode 100644 src/main/java/baseball/HintCalculatorModel.java create mode 100644 src/main/java/baseball/HintResult.java create mode 100644 src/main/java/baseball/PlayerInputValidator.java create mode 100644 src/main/java/baseball/SecretNumberModel.java diff --git a/src/main/java/.gitkeep b/src/main/java/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/Application.java b/src/main/java/Application.java index da12685b..874a96b1 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,5 +1,7 @@ +import baseball.GameFlowController; + public class Application { public static void main(String[] args) { - System.out.println("Hello, World!"); + new GameFlowController().start(); } -} \ No newline at end of file +} diff --git a/src/main/java/baseball/GameFlowController.java b/src/main/java/baseball/GameFlowController.java new file mode 100644 index 00000000..7e053e94 --- /dev/null +++ b/src/main/java/baseball/GameFlowController.java @@ -0,0 +1,53 @@ +package baseball; + +import java.util.List; + +public class GameFlowController { + private final SecretNumberModel secretModel = new SecretNumberModel(); + private final PlayerInputValidator playerInputValidator = new PlayerInputValidator(); + private final HintCalculatorModel hintModel = new HintCalculatorModel(); + private final GameResultView resultView = new GameResultView(); + + public void start() { + boolean running = true; + while (running) { + secretModel.generateSecret(); + List secret = secretModel.getSecret(); + System.out.println("숫자의 값은: " + secretModel.getSecret()); + + while (true) { + System.out.println("서로 다른 3자리 숫자를 입력하세요 (각 자리 1-9):"); + String line = playerInputValidator.readLine(); + if (!playerInputValidator.isValidInput(line)) { + resultView.showError("입력은 서로 다른 3개의 숫자(1-9)여야 합니다."); + continue; + } + List guess = playerInputValidator.toDigits(line); + HintResult hint = hintModel.calculate(secret, guess); + resultView.showHint(hint); + if (hint.getStrikes() == 3) { + resultView.showWinAndPromptRestart(); + boolean restart = askRestart(); + if (restart) { + break; + } else { + running = false; + break; + } + } + } + } + System.out.println("게임을 종료합니다."); + } + + private boolean askRestart() { + while (true) { + String choice = playerInputValidator.readLine(); + if ("1".equals(choice)) + return true; + if ("2".equals(choice)) + return false; + resultView.showError("1 또는 2만 입력 가능합니다."); + } + } +} diff --git a/src/main/java/baseball/GameResultView.java b/src/main/java/baseball/GameResultView.java new file mode 100644 index 00000000..eaf0bd02 --- /dev/null +++ b/src/main/java/baseball/GameResultView.java @@ -0,0 +1,16 @@ +package baseball; + +public class GameResultView { + public void showHint(HintResult result) { + System.out.println(result.toString()); + } + + public void showError(String message) { + System.out.println("[ERROR] " + message); + } + + public void showWinAndPromptRestart() { + System.out.println("3스트라이크! 승리했습니다."); + System.out.println("게임을 재시작하려면 1, 종료하려면 2를 입력하세요."); + } +} diff --git a/src/main/java/baseball/HintCalculatorModel.java b/src/main/java/baseball/HintCalculatorModel.java new file mode 100644 index 00000000..451a6060 --- /dev/null +++ b/src/main/java/baseball/HintCalculatorModel.java @@ -0,0 +1,18 @@ +package baseball; + +import java.util.List; + +public class HintCalculatorModel { + public HintResult calculate(List secret, List guess) { + int strikes = 0; + int balls = 0; + for (int i = 0; i < 3; i++) { + if (guess.get(i).equals(secret.get(i))) { + strikes++; + } else if (secret.contains(guess.get(i))) { + balls++; + } + } + return new HintResult(strikes, balls); + } +} diff --git a/src/main/java/baseball/HintResult.java b/src/main/java/baseball/HintResult.java new file mode 100644 index 00000000..05da8989 --- /dev/null +++ b/src/main/java/baseball/HintResult.java @@ -0,0 +1,30 @@ +package baseball; + +public class HintResult { + private final int strikes; + private final int balls; + + public HintResult(int strikes, int balls) { + this.strikes = strikes; + this.balls = balls; + } + + public int getStrikes() { + return strikes; + } + + @Override + public String toString() { + if (strikes == 0 && balls == 0) + return "낫싱"; + StringBuilder sb = new StringBuilder(); + if (balls > 0) + sb.append(balls).append("볼"); + if (strikes > 0) { + if (sb.length() > 0) + sb.append(" "); + sb.append(strikes).append("스트라이크"); + } + return sb.toString(); + } +} diff --git a/src/main/java/baseball/PlayerInputValidator.java b/src/main/java/baseball/PlayerInputValidator.java new file mode 100644 index 00000000..672ccf33 --- /dev/null +++ b/src/main/java/baseball/PlayerInputValidator.java @@ -0,0 +1,41 @@ +package baseball; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Scanner; + +public class PlayerInputValidator { + private final Scanner scanner = new Scanner(System.in); + + public String readLine() { + return scanner.nextLine().trim(); + } + + public boolean isValidInput(String s) { + if (s == null) + return false; + if (s.length() != 3) + return false; + HashSet set = new HashSet<>(); + for (char c : s.toCharArray()) { + if (!Character.isDigit(c)) + return false; + int d = c - '0'; + if (d < 1 || d > 9) + return false; + set.add(c); + } + return set.size() == 3; + } + + /* + * 올바른 입력이 주어졌을 때만 호출되어야 합니다. + * */ + public List toDigits(String s) { + List list = new ArrayList<>(); + for (char c : s.toCharArray()) + list.add(c - '0'); + return list; + } +} diff --git a/src/main/java/baseball/SecretNumberModel.java b/src/main/java/baseball/SecretNumberModel.java new file mode 100644 index 00000000..652ea6a3 --- /dev/null +++ b/src/main/java/baseball/SecretNumberModel.java @@ -0,0 +1,21 @@ +package baseball; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class SecretNumberModel { + private List secret = new ArrayList<>(); + + public void generateSecret() { + List pool = new ArrayList<>(); + for (int i = 1; i <= 9; i++) + pool.add(i); + Collections.shuffle(pool); + secret = new ArrayList<>(pool.subList(0, 3)); + } + + public List getSecret() { + return new ArrayList<>(secret); + } +} From f4e577d1237546fe65ff24edd6d6558f718b2b06 Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 01:10:03 +0900 Subject: [PATCH 05/22] =?UTF-8?q?refactor:=20=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/baseball/GameFlowController.java | 4 ++-- .../baseball/{GameResultView.java => GameResultModel.java} | 4 ++-- src/main/java/baseball/HintCalculatorModel.java | 4 ++-- .../java/baseball/{HintResult.java => HintResultModel.java} | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) rename src/main/java/baseball/{GameResultView.java => GameResultModel.java} (81%) rename src/main/java/baseball/{HintResult.java => HintResultModel.java} (86%) diff --git a/src/main/java/baseball/GameFlowController.java b/src/main/java/baseball/GameFlowController.java index 7e053e94..a9b772b3 100644 --- a/src/main/java/baseball/GameFlowController.java +++ b/src/main/java/baseball/GameFlowController.java @@ -6,7 +6,7 @@ public class GameFlowController { private final SecretNumberModel secretModel = new SecretNumberModel(); private final PlayerInputValidator playerInputValidator = new PlayerInputValidator(); private final HintCalculatorModel hintModel = new HintCalculatorModel(); - private final GameResultView resultView = new GameResultView(); + private final GameResultModel resultView = new GameResultModel(); public void start() { boolean running = true; @@ -23,7 +23,7 @@ public void start() { continue; } List guess = playerInputValidator.toDigits(line); - HintResult hint = hintModel.calculate(secret, guess); + HintResultModel hint = hintModel.calculate(secret, guess); resultView.showHint(hint); if (hint.getStrikes() == 3) { resultView.showWinAndPromptRestart(); diff --git a/src/main/java/baseball/GameResultView.java b/src/main/java/baseball/GameResultModel.java similarity index 81% rename from src/main/java/baseball/GameResultView.java rename to src/main/java/baseball/GameResultModel.java index eaf0bd02..8ac5de70 100644 --- a/src/main/java/baseball/GameResultView.java +++ b/src/main/java/baseball/GameResultModel.java @@ -1,7 +1,7 @@ package baseball; -public class GameResultView { - public void showHint(HintResult result) { +public class GameResultModel { + public void showHint(HintResultModel result) { System.out.println(result.toString()); } diff --git a/src/main/java/baseball/HintCalculatorModel.java b/src/main/java/baseball/HintCalculatorModel.java index 451a6060..9f695ea7 100644 --- a/src/main/java/baseball/HintCalculatorModel.java +++ b/src/main/java/baseball/HintCalculatorModel.java @@ -3,7 +3,7 @@ import java.util.List; public class HintCalculatorModel { - public HintResult calculate(List secret, List guess) { + public HintResultModel calculate(List secret, List guess) { int strikes = 0; int balls = 0; for (int i = 0; i < 3; i++) { @@ -13,6 +13,6 @@ public HintResult calculate(List secret, List guess) { balls++; } } - return new HintResult(strikes, balls); + return new HintResultModel(strikes, balls); } } diff --git a/src/main/java/baseball/HintResult.java b/src/main/java/baseball/HintResultModel.java similarity index 86% rename from src/main/java/baseball/HintResult.java rename to src/main/java/baseball/HintResultModel.java index 05da8989..8d85c621 100644 --- a/src/main/java/baseball/HintResult.java +++ b/src/main/java/baseball/HintResultModel.java @@ -1,10 +1,10 @@ package baseball; -public class HintResult { +public class HintResultModel { private final int strikes; private final int balls; - public HintResult(int strikes, int balls) { + public HintResultModel(int strikes, int balls) { this.strikes = strikes; this.balls = balls; } From 98577e14e2d244d6fa1f330daa4d6c5bdd55e5be Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 01:19:14 +0900 Subject: [PATCH 06/22] =?UTF-8?q?test:=20GameResultModel=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/baseball/GameResultModelTest.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/test/java/baseball/GameResultModelTest.java diff --git a/src/test/java/baseball/GameResultModelTest.java b/src/test/java/baseball/GameResultModelTest.java new file mode 100644 index 00000000..d18b5e90 --- /dev/null +++ b/src/test/java/baseball/GameResultModelTest.java @@ -0,0 +1,54 @@ +package baseball; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class GameResultModelTest { + private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private final PrintStream originalOut = System.out; + private GameResultModel view; + + @BeforeEach + void setUp() { + System.setOut(new PrintStream(outContent)); + view = new GameResultModel(); + outContent.reset(); + } + + @AfterEach + void tearDown() { + System.setOut(originalOut); + } + + @Test + void showHint_prints_nothing_when_no_match() { + view.showHint(new HintResultModel(0, 0)); + assertEquals("낫싱\n", outContent.toString()); + } + + @Test + void showHint_prints_ball_and_strike() { + view.showHint(new HintResultModel(2, 1)); + assertEquals("1볼 2스트라이크\n", outContent.toString()); + } + + @Test + void showError_prefixes_message_with_error_tag() { + view.showError("입력 오류"); + assertEquals("[ERROR] 입력 오류\n", outContent.toString()); + } + + @Test + void showWinAndPromptRestart_prints_two_lines() { + view.showWinAndPromptRestart(); + String output = outContent.toString(); + assertTrue(output.contains("3스트라이크! 승리했습니다.")); + assertTrue(output.contains("게임을 재시작하려면 1, 종료하려면 2를 입력하세요.")); + } +} From 32f8c8d4aac7e45c80eacca496678edfa2376426 Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 01:29:02 +0900 Subject: [PATCH 07/22] =?UTF-8?q?test:=20HintCalculatorModel=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../baseball/HintCalculatorModelTest.java | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 src/test/java/baseball/HintCalculatorModelTest.java diff --git a/src/test/java/baseball/HintCalculatorModelTest.java b/src/test/java/baseball/HintCalculatorModelTest.java new file mode 100644 index 00000000..9404db1e --- /dev/null +++ b/src/test/java/baseball/HintCalculatorModelTest.java @@ -0,0 +1,109 @@ +package baseball; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +class HintCalculatorModelTest { + /* + * public HintResultModel calculate(List secret, List guess) { + int strikes = 0; + int balls = 0; + for (int i = 0; i < 3; i++) { + if (guess.get(i).equals(secret.get(i))) { + strikes++; + } else if (secret.contains(guess.get(i))) { + balls++; + } + } + return new HintResultModel(strikes, balls); + } + * */ + + private final HintCalculatorModel hintCalculator = new HintCalculatorModel(); + + // 1. 모든 숫자가 일치하는 경우 (3 스트라이크, 0 볼) + @Test + void calculate_all_match() { + HintResultModel result = hintCalculator.calculate( + List.of(1, 2, 3), + List.of(1, 2, 3) + ); + assert result.getStrikes() == 3; + assert result.toString().equals("3스트라이크"); + } + + // 2. 일부 숫자가 위치까지 일치하는 경우 (예: 2 스트라이크, 0 볼) + @Test + void calculate_partial_strike_match() { + HintResultModel result = hintCalculator.calculate( + List.of(1, 2, 3), + List.of(1, 2, 4) + ); + assert result.getStrikes() == 2; + assert result.toString().equals("2스트라이크"); + } + + @Test + void calculate_partial_strike_match_2() { + HintResultModel result = hintCalculator.calculate( + List.of(4, 5, 6), + List.of(4, 7, 6) + ); + assert result.getStrikes() == 2; + assert result.toString().equals("2스트라이크"); + } + + @Test + void calculate_partial_strike_match_3() { + HintResultModel result = hintCalculator.calculate( + List.of(7, 8, 9), + List.of(0, 8, 9) + ); + assert result.getStrikes() == 2; + assert result.toString().equals("2스트라이크"); + } + + // 3. 일부 숫자가 위치는 다르지만 포함되는 경우 (예: 0 스트라이크, 2 볼) + @Test + void calculate_partial_ball_match() { + HintResultModel result = hintCalculator.calculate( + List.of(1, 2, 3), + List.of(3, 1, 4) + ); + assert result.getStrikes() == 0; + assert result.toString().equals("2볼"); + } + + @Test + void calculate_partial_ball_match_2() { + HintResultModel result = hintCalculator.calculate( + List.of(4, 5, 6), + List.of(6, 4, 7) + ); + assert result.getStrikes() == 0; + assert result.toString().equals("2볼"); + } + + // 4. 숫자가 전혀 일치하지 않는 경우 (0 스트라이크, 0 볼) + @Test + void calculate_no_match() { + HintResultModel result = hintCalculator.calculate( + List.of(1, 2, 3), + List.of(4, 5, 6) + ); + assert result.getStrikes() == 0; + assert result.toString().equals("낫싱"); + } + + @Test + void calculate_no_match_2() { + HintResultModel result = hintCalculator.calculate( + List.of(7, 8, 9), + List.of(1, 2, 3) + ); + assert result.getStrikes() == 0; + assert result.toString().equals("낫싱"); + } + +} \ No newline at end of file From 380f066763eec44ecaf6ec3d0c83f14100223975 Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 01:43:18 +0900 Subject: [PATCH 08/22] =?UTF-8?q?test:=20HintResultModelTest=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/baseball/HintResultModelTest.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/test/java/baseball/HintResultModelTest.java diff --git a/src/test/java/baseball/HintResultModelTest.java b/src/test/java/baseball/HintResultModelTest.java new file mode 100644 index 00000000..2c6c9346 --- /dev/null +++ b/src/test/java/baseball/HintResultModelTest.java @@ -0,0 +1,68 @@ +package baseball; + +import org.junit.jupiter.api.Test; + +class HintResultModelTest { + // 0볼 0스트라이크 + @Test + void testNoBallsNoStrikes() { + HintResultModel hint = new HintResultModel(0, 0); + assert hint.toString().equals("낫싱"); + } + + // 0볼 1스트라이크 + @Test + void testNoBallsOneStrike() { + HintResultModel hint = new HintResultModel(1, 0); + assert hint.toString().equals("1스트라이크"); + } + + // 0볼 2스트라이크 + @Test + void testNoBallsTwoStrikes() { + HintResultModel hint = new HintResultModel(2, 0); + assert hint.toString().equals("2스트라이크"); + } + + // 1볼 0스트라이크 + @Test + void testOneBallNoStrikes() { + HintResultModel hint = new HintResultModel(0, 1); + assert hint.toString().equals("1볼"); + } + + // 1스트라이크 1볼 (스트라이크 먼저) + @Test + void testOneBallOneStrike() { + HintResultModel hint = new HintResultModel(1, 1); + assert hint.toString().equals("1스트라이크 1볼"); + } + + // 2볼 0스트라이크 + @Test + void testTwoBallsNoStrikes() { + HintResultModel hint = new HintResultModel(0, 2); + assert hint.toString().equals("2볼"); + } + + // 1스트라이크 2볼 (스트라이크 먼저) + @Test + void testTwoBallsOneStrike() { + HintResultModel hint = new HintResultModel(1, 2); + assert hint.toString().equals("1스트라이크 2볼"); + } + + // 3볼 0스트라이크 + @Test + void testThreeBallsNoStrikes() { + HintResultModel hint = new HintResultModel(0, 3); + assert hint.toString().equals("3볼"); + } + + // 0볼 3스트라이크 + @Test + void testNoBallsThreeStrikes() { + HintResultModel hint = new HintResultModel(3, 0); + assert hint.toString().equals("3스트라이크"); + } +} \ No newline at end of file From 2710d2b91811db69b1e792be72973beeec9e62f8 Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 02:06:02 +0900 Subject: [PATCH 09/22] =?UTF-8?q?test:=20PlayerInputValidator=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/.gitkeep | 0 .../baseball/PlayerInputValidatorTest.java | 52 +++++++++++++++++++ 2 files changed, 52 insertions(+) delete mode 100644 src/test/java/.gitkeep create mode 100644 src/test/java/baseball/PlayerInputValidatorTest.java diff --git a/src/test/java/.gitkeep b/src/test/java/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/test/java/baseball/PlayerInputValidatorTest.java b/src/test/java/baseball/PlayerInputValidatorTest.java new file mode 100644 index 00000000..4a3cf9ea --- /dev/null +++ b/src/test/java/baseball/PlayerInputValidatorTest.java @@ -0,0 +1,52 @@ +package baseball; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class PlayerInputValidatorTest { + private final PlayerInputValidator validator = new PlayerInputValidator(); + + // - null 입력 -> false 반환 + @Test + void testIsValidInput_NullInput() { + assertFalse(validator.isValidInput(null)); + } + + // - 길이 3이 아닌 입력 (예: "12", "12333", "1234") -> false 반환 + @Test + void testIsValidInput_InvalidLength() { + assertFalse(validator.isValidInput("12")); + assertFalse(validator.isValidInput("12333")); + assertFalse(validator.isValidInput("1234")); + } + + // - 숫자가 아닌 문자 포함 (예: "1a3", "12!") + @Test + void testIsValidInput_NonDigitCharacters() { + assertFalse(validator.isValidInput("1a3")); + } + + // - 1-9 범위 밖의 숫자 포함 (예: "023", "1450") + @Test + void testIsValidInput_OutOfRangeNumbers() { + assertFalse(validator.isValidInput("023")); + assertFalse(validator.isValidInput("1450")); + } + + // - 중복된 숫자 포함 (예: "112", "121") + @Test + void testIsValidInput_DuplicateNumbers() { + assertFalse(validator.isValidInput("112")); + assertFalse(validator.isValidInput("121")); + } + + // - 올바른 입력 (예: "123", "987") -> true + @Test + void testIsValidInput_ValidInput() { + assertTrue(validator.isValidInput("123")); + assertTrue(validator.isValidInput("987")); + } + +} \ No newline at end of file From f05fda07cd236b2ade71f84f2e5d6f4eada80464 Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 02:10:33 +0900 Subject: [PATCH 10/22] =?UTF-8?q?fix:=20=EC=8A=A4=ED=8A=B8=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=ED=81=AC=EA=B0=80=20=EB=B3=BC=20=EC=95=9E=EC=97=90=20?= =?UTF-8?q?=EC=98=A4=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/baseball/HintResultModel.java | 10 ++++++---- .../java/baseball/GameResultModelTest.java | 5 +++-- .../java/baseball/HintResultModelTest.java | 20 ++++++++++--------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/main/java/baseball/HintResultModel.java b/src/main/java/baseball/HintResultModel.java index 8d85c621..698b3889 100644 --- a/src/main/java/baseball/HintResultModel.java +++ b/src/main/java/baseball/HintResultModel.java @@ -18,13 +18,15 @@ public String toString() { if (strikes == 0 && balls == 0) return "낫싱"; StringBuilder sb = new StringBuilder(); - if (balls > 0) - sb.append(balls).append("볼"); if (strikes > 0) { - if (sb.length() > 0) - sb.append(" "); sb.append(strikes).append("스트라이크"); } + if (balls > 0) { + if (strikes > 0) { + sb.append(" "); + } + sb.append(balls).append("볼"); + } return sb.toString(); } } diff --git a/src/test/java/baseball/GameResultModelTest.java b/src/test/java/baseball/GameResultModelTest.java index d18b5e90..4000d988 100644 --- a/src/test/java/baseball/GameResultModelTest.java +++ b/src/test/java/baseball/GameResultModelTest.java @@ -1,6 +1,7 @@ package baseball; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayOutputStream; import java.io.PrintStream; @@ -35,7 +36,7 @@ void showHint_prints_nothing_when_no_match() { @Test void showHint_prints_ball_and_strike() { view.showHint(new HintResultModel(2, 1)); - assertEquals("1볼 2스트라이크\n", outContent.toString()); + assertEquals("2스트라이크 1볼\n", outContent.toString()); } @Test diff --git a/src/test/java/baseball/HintResultModelTest.java b/src/test/java/baseball/HintResultModelTest.java index 2c6c9346..7310ad36 100644 --- a/src/test/java/baseball/HintResultModelTest.java +++ b/src/test/java/baseball/HintResultModelTest.java @@ -1,5 +1,7 @@ package baseball; +import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.Test; class HintResultModelTest { @@ -7,62 +9,62 @@ class HintResultModelTest { @Test void testNoBallsNoStrikes() { HintResultModel hint = new HintResultModel(0, 0); - assert hint.toString().equals("낫싱"); + assertEquals("낫싱", hint.toString()); } // 0볼 1스트라이크 @Test void testNoBallsOneStrike() { HintResultModel hint = new HintResultModel(1, 0); - assert hint.toString().equals("1스트라이크"); + assertEquals("1스트라이크", hint.toString()); } // 0볼 2스트라이크 @Test void testNoBallsTwoStrikes() { HintResultModel hint = new HintResultModel(2, 0); - assert hint.toString().equals("2스트라이크"); + assertEquals("2스트라이크", hint.toString()); } // 1볼 0스트라이크 @Test void testOneBallNoStrikes() { HintResultModel hint = new HintResultModel(0, 1); - assert hint.toString().equals("1볼"); + assertEquals("1볼", hint.toString()); } // 1스트라이크 1볼 (스트라이크 먼저) @Test void testOneBallOneStrike() { HintResultModel hint = new HintResultModel(1, 1); - assert hint.toString().equals("1스트라이크 1볼"); + assertEquals("1스트라이크 1볼", hint.toString()); } // 2볼 0스트라이크 @Test void testTwoBallsNoStrikes() { HintResultModel hint = new HintResultModel(0, 2); - assert hint.toString().equals("2볼"); + assertEquals("2볼", hint.toString()); } // 1스트라이크 2볼 (스트라이크 먼저) @Test void testTwoBallsOneStrike() { HintResultModel hint = new HintResultModel(1, 2); - assert hint.toString().equals("1스트라이크 2볼"); + assertEquals("1스트라이크 2볼", hint.toString()); } // 3볼 0스트라이크 @Test void testThreeBallsNoStrikes() { HintResultModel hint = new HintResultModel(0, 3); - assert hint.toString().equals("3볼"); + assertEquals("3볼", hint.toString()); } // 0볼 3스트라이크 @Test void testNoBallsThreeStrikes() { HintResultModel hint = new HintResultModel(3, 0); - assert hint.toString().equals("3스트라이크"); + assertEquals("3스트라이크", hint.toString()); } } \ No newline at end of file From 832317765a2a9d6eb6e2335adf60ea6999ff1e50 Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 02:56:23 +0900 Subject: [PATCH 11/22] =?UTF-8?q?test:=20SecretNumberModelTest=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 --- .../java/baseball/SecretNumberModelTest.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/test/java/baseball/SecretNumberModelTest.java diff --git a/src/test/java/baseball/SecretNumberModelTest.java b/src/test/java/baseball/SecretNumberModelTest.java new file mode 100644 index 00000000..f73de1a4 --- /dev/null +++ b/src/test/java/baseball/SecretNumberModelTest.java @@ -0,0 +1,28 @@ +package baseball; + +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class SecretNumberModelTest { + @Test + void testGenerateSecret() { + SecretNumberModel model = new SecretNumberModel(); + model.generateSecret(); + // 테스트 코드는 여기에서 작성합니다. + // 예: 비어 있지 않은지, 3자리 숫자인지, 중복이 없는지 등을 확인할 수 있습니다. + + List test = model.getSecret(); + Assertions.assertEquals(3, test.size(), "시크릿 넘버는 3자리여야 합니다."); + for (Integer integer : test) { + Assertions.assertNotNull(integer, "시크릿 넘버의 각 자리는 null이 될 수 없습니다."); + Assertions.assertTrue(0 < integer && integer < 10, "시크릿 넘버의 각 자리는 한 자리 자연수여야 합니다."); + } + for (int i = 0; i < test.size(); i++) { + for (int j = i + 1; j < test.size(); j++) { + Assertions.assertNotEquals(test.get(i), test.get(j), "시크릿 넘버의 각 자리는 서로 달라야 합니다."); + } + } + } +} \ No newline at end of file From d33a5b31a4d70cde2043652e8c6af10c2c9cc3f4 Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 03:06:38 +0900 Subject: [PATCH 12/22] =?UTF-8?q?refactor:=20InputValidator=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=9E=85=EB=A0=A5=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/baseball/GameFlowController.java | 30 +++++++++++-------- .../java/baseball/PlayerInputValidator.java | 7 ----- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/main/java/baseball/GameFlowController.java b/src/main/java/baseball/GameFlowController.java index a9b772b3..2d1f72e4 100644 --- a/src/main/java/baseball/GameFlowController.java +++ b/src/main/java/baseball/GameFlowController.java @@ -1,12 +1,29 @@ package baseball; import java.util.List; +import java.util.Scanner; public class GameFlowController { private final SecretNumberModel secretModel = new SecretNumberModel(); private final PlayerInputValidator playerInputValidator = new PlayerInputValidator(); private final HintCalculatorModel hintModel = new HintCalculatorModel(); private final GameResultModel resultView = new GameResultModel(); + private final Scanner scanner = new Scanner(System.in); + + private String readLine() { + return scanner.nextLine().trim(); + } + + private boolean askRestart() { + while (true) { + String choice = readLine(); + if ("1".equals(choice)) + return true; + if ("2".equals(choice)) + return false; + resultView.showError("1 또는 2만 입력 가능합니다."); + } + } public void start() { boolean running = true; @@ -17,7 +34,7 @@ public void start() { while (true) { System.out.println("서로 다른 3자리 숫자를 입력하세요 (각 자리 1-9):"); - String line = playerInputValidator.readLine(); + String line = readLine(); if (!playerInputValidator.isValidInput(line)) { resultView.showError("입력은 서로 다른 3개의 숫자(1-9)여야 합니다."); continue; @@ -39,15 +56,4 @@ public void start() { } System.out.println("게임을 종료합니다."); } - - private boolean askRestart() { - while (true) { - String choice = playerInputValidator.readLine(); - if ("1".equals(choice)) - return true; - if ("2".equals(choice)) - return false; - resultView.showError("1 또는 2만 입력 가능합니다."); - } - } } diff --git a/src/main/java/baseball/PlayerInputValidator.java b/src/main/java/baseball/PlayerInputValidator.java index 672ccf33..584627f3 100644 --- a/src/main/java/baseball/PlayerInputValidator.java +++ b/src/main/java/baseball/PlayerInputValidator.java @@ -3,15 +3,8 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.Scanner; public class PlayerInputValidator { - private final Scanner scanner = new Scanner(System.in); - - public String readLine() { - return scanner.nextLine().trim(); - } - public boolean isValidInput(String s) { if (s == null) return false; From 69665ffd78f635a134a29b2ccee17ac7ce6bf0df Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 03:07:08 +0900 Subject: [PATCH 13/22] =?UTF-8?q?docs:=20=EB=B3=80=EA=B2=BD=20=EB=82=B4?= =?UTF-8?q?=EC=9A=A9=20=EB=8B=A4=EC=9D=B4=EC=96=B4=EA=B7=B8=EB=9E=A8?= =?UTF-8?q?=EC=97=90=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index d633e88b..d762f192 100644 --- a/README.md +++ b/README.md @@ -30,37 +30,45 @@ classDiagram class GameFlowController { <> - +게임_전체_흐름_제어() - +재시작_여부_판단() + +start(): void + +askRestart(): boolean + -readLine(): String } class SecretNumberModel { <> - -정답_숫자_데이터 - +랜덤_3자리_생성() + -secret: List + +generateSecret(): void + +getSecret(): List } class HintCalculatorModel { <> - +스트라이크_볼_판정() - +결과_데이터_생성() + +calculate(secret: List, guess: List): HintResultModel } - class PlayerInputView { - <> - +숫자_입력_받기() - +입력_값_유효성_검증() + class PlayerInputValidator { + <> + +isValidInput(input: String): boolean + +toDigits(input: String): List } - class GameResultView { + class GameResultModel { <> - +힌트_메시지_출력() - +승리_및_종료_출력() - +에러_메시지_출력() + +showHint(result: HintResultModel): void + +showWinAndPromptRestart(): void + +showError(message: String): void + } + + class HintResultModel { + <> + +strikes: int + +balls: int + +toString(): String } - GameFlowController --> SecretNumberModel: 정답 생성 요청 - GameFlowController --> PlayerInputView: 사용자 입력 요청 - GameFlowController --> HintCalculatorModel: 판정 로직 실행 - GameFlowController --> GameResultView: 화면 출력 요청 + GameFlowController --> SecretNumberModel: "정답 생성 요청" + GameFlowController --> PlayerInputValidator: "입력 유효성 검사/숫자 변환 요청" + GameFlowController --> HintCalculatorModel: "판정 로직 실행" + GameFlowController --> GameResultModel: "결과/메시지 출력 요청" ``` \ No newline at end of file From cc600df8d8a4f8bc09e767c25a7570ba8e3311e2 Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 03:08:05 +0900 Subject: [PATCH 14/22] =?UTF-8?q?fix:=20=EC=A0=95=EB=8B=B5=20=EC=8A=A4?= =?UTF-8?q?=ED=8F=AC=EC=9D=BC=EB=9F=AC=20=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/baseball/GameFlowController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/baseball/GameFlowController.java b/src/main/java/baseball/GameFlowController.java index 2d1f72e4..1fe8dfab 100644 --- a/src/main/java/baseball/GameFlowController.java +++ b/src/main/java/baseball/GameFlowController.java @@ -30,7 +30,6 @@ public void start() { while (running) { secretModel.generateSecret(); List secret = secretModel.getSecret(); - System.out.println("숫자의 값은: " + secretModel.getSecret()); while (true) { System.out.println("서로 다른 3자리 숫자를 입력하세요 (각 자리 1-9):"); From 760bcc4ce9018412bc0430194124724b93e82a68 Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 03:16:15 +0900 Subject: [PATCH 15/22] =?UTF-8?q?refactor:=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=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/baseball/GameFlowController.java | 11 +++-------- src/main/java/baseball/GameInput.java | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 src/main/java/baseball/GameInput.java diff --git a/src/main/java/baseball/GameFlowController.java b/src/main/java/baseball/GameFlowController.java index 1fe8dfab..9167cda3 100644 --- a/src/main/java/baseball/GameFlowController.java +++ b/src/main/java/baseball/GameFlowController.java @@ -1,22 +1,17 @@ package baseball; import java.util.List; -import java.util.Scanner; public class GameFlowController { private final SecretNumberModel secretModel = new SecretNumberModel(); private final PlayerInputValidator playerInputValidator = new PlayerInputValidator(); private final HintCalculatorModel hintModel = new HintCalculatorModel(); private final GameResultModel resultView = new GameResultModel(); - private final Scanner scanner = new Scanner(System.in); - - private String readLine() { - return scanner.nextLine().trim(); - } + private final GameInput gameInput = new GameInput(); private boolean askRestart() { while (true) { - String choice = readLine(); + String choice = gameInput.readLine(); if ("1".equals(choice)) return true; if ("2".equals(choice)) @@ -33,7 +28,7 @@ public void start() { while (true) { System.out.println("서로 다른 3자리 숫자를 입력하세요 (각 자리 1-9):"); - String line = readLine(); + String line = gameInput.readLine(); if (!playerInputValidator.isValidInput(line)) { resultView.showError("입력은 서로 다른 3개의 숫자(1-9)여야 합니다."); continue; diff --git a/src/main/java/baseball/GameInput.java b/src/main/java/baseball/GameInput.java new file mode 100644 index 00000000..11dc2a39 --- /dev/null +++ b/src/main/java/baseball/GameInput.java @@ -0,0 +1,16 @@ +package baseball; + +import java.util.Scanner; + +public class GameInput { + + private final Scanner scanner; + + public GameInput() { + this.scanner = new Scanner(System.in); + } + + public String readLine() { + return scanner.nextLine().trim(); + } +} From dd7f658ea778290349e18b0f47d76f9f05ae241f Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 03:24:13 +0900 Subject: [PATCH 16/22] =?UTF-8?q?refactor:=20=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=EC=A4=91=EC=95=99=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/baseball/GameFlowController.java | 4 ++-- src/main/java/baseball/GameResultModel.java | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/baseball/GameFlowController.java b/src/main/java/baseball/GameFlowController.java index 9167cda3..db369c35 100644 --- a/src/main/java/baseball/GameFlowController.java +++ b/src/main/java/baseball/GameFlowController.java @@ -27,7 +27,7 @@ public void start() { List secret = secretModel.getSecret(); while (true) { - System.out.println("서로 다른 3자리 숫자를 입력하세요 (각 자리 1-9):"); + resultView.showPromptForGuess(); String line = gameInput.readLine(); if (!playerInputValidator.isValidInput(line)) { resultView.showError("입력은 서로 다른 3개의 숫자(1-9)여야 합니다."); @@ -48,6 +48,6 @@ public void start() { } } } - System.out.println("게임을 종료합니다."); + resultView.showExitMessage(); } } diff --git a/src/main/java/baseball/GameResultModel.java b/src/main/java/baseball/GameResultModel.java index 8ac5de70..d9b2e1ad 100644 --- a/src/main/java/baseball/GameResultModel.java +++ b/src/main/java/baseball/GameResultModel.java @@ -1,6 +1,7 @@ package baseball; public class GameResultModel { + public void showHint(HintResultModel result) { System.out.println(result.toString()); } @@ -13,4 +14,12 @@ public void showWinAndPromptRestart() { System.out.println("3스트라이크! 승리했습니다."); System.out.println("게임을 재시작하려면 1, 종료하려면 2를 입력하세요."); } + + public void showPromptForGuess() { + System.out.println("서로 다른 3자리 숫자를 입력하세요 (각 자리 1-9):"); + } + + public void showExitMessage() { + System.out.println("게임을 종료합니다."); + } } From 92ca8e7e8c86c14d5cef2b4d67482121fa3aa90c Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 03:25:46 +0900 Subject: [PATCH 17/22] =?UTF-8?q?refactor:=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/baseball/{GameResultModel.java => GameOutput.java} | 0 .../{GameResultModelTest.java => GameResultPrinterTest.java} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/main/java/baseball/{GameResultModel.java => GameOutput.java} (100%) rename src/test/java/baseball/{GameResultModelTest.java => GameResultPrinterTest.java} (100%) diff --git a/src/main/java/baseball/GameResultModel.java b/src/main/java/baseball/GameOutput.java similarity index 100% rename from src/main/java/baseball/GameResultModel.java rename to src/main/java/baseball/GameOutput.java diff --git a/src/test/java/baseball/GameResultModelTest.java b/src/test/java/baseball/GameResultPrinterTest.java similarity index 100% rename from src/test/java/baseball/GameResultModelTest.java rename to src/test/java/baseball/GameResultPrinterTest.java From a9ef6b8d7fff79c69a66f22e71af5692b766ffbb Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 03:25:57 +0900 Subject: [PATCH 18/22] =?UTF-8?q?refactor:=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/baseball/GameFlowController.java | 14 +++++++------- src/main/java/baseball/GameOutput.java | 4 ++-- src/test/java/baseball/GameResultPrinterTest.java | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/baseball/GameFlowController.java b/src/main/java/baseball/GameFlowController.java index db369c35..7d28606d 100644 --- a/src/main/java/baseball/GameFlowController.java +++ b/src/main/java/baseball/GameFlowController.java @@ -6,7 +6,7 @@ public class GameFlowController { private final SecretNumberModel secretModel = new SecretNumberModel(); private final PlayerInputValidator playerInputValidator = new PlayerInputValidator(); private final HintCalculatorModel hintModel = new HintCalculatorModel(); - private final GameResultModel resultView = new GameResultModel(); + private final GameOutput gameOutput = new GameOutput(); private final GameInput gameInput = new GameInput(); private boolean askRestart() { @@ -16,7 +16,7 @@ private boolean askRestart() { return true; if ("2".equals(choice)) return false; - resultView.showError("1 또는 2만 입력 가능합니다."); + gameOutput.showError("1 또는 2만 입력 가능합니다."); } } @@ -27,17 +27,17 @@ public void start() { List secret = secretModel.getSecret(); while (true) { - resultView.showPromptForGuess(); + gameOutput.showPromptForGuess(); String line = gameInput.readLine(); if (!playerInputValidator.isValidInput(line)) { - resultView.showError("입력은 서로 다른 3개의 숫자(1-9)여야 합니다."); + gameOutput.showError("입력은 서로 다른 3개의 숫자(1-9)여야 합니다."); continue; } List guess = playerInputValidator.toDigits(line); HintResultModel hint = hintModel.calculate(secret, guess); - resultView.showHint(hint); + gameOutput.showHint(hint); if (hint.getStrikes() == 3) { - resultView.showWinAndPromptRestart(); + gameOutput.showWinAndPromptRestart(); boolean restart = askRestart(); if (restart) { break; @@ -48,6 +48,6 @@ public void start() { } } } - resultView.showExitMessage(); + gameOutput.showExitMessage(); } } diff --git a/src/main/java/baseball/GameOutput.java b/src/main/java/baseball/GameOutput.java index d9b2e1ad..0ae97bc8 100644 --- a/src/main/java/baseball/GameOutput.java +++ b/src/main/java/baseball/GameOutput.java @@ -1,7 +1,7 @@ package baseball; -public class GameResultModel { - +public class GameOutput { + public void showHint(HintResultModel result) { System.out.println(result.toString()); } diff --git a/src/test/java/baseball/GameResultPrinterTest.java b/src/test/java/baseball/GameResultPrinterTest.java index 4000d988..370e9a1f 100644 --- a/src/test/java/baseball/GameResultPrinterTest.java +++ b/src/test/java/baseball/GameResultPrinterTest.java @@ -10,15 +10,15 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -class GameResultModelTest { +class GameResultPrinterTest { private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); private final PrintStream originalOut = System.out; - private GameResultModel view; + private GameOutput view; @BeforeEach void setUp() { System.setOut(new PrintStream(outContent)); - view = new GameResultModel(); + view = new GameOutput(); outContent.reset(); } From cb65de489e2298f6f2e9e90fd7b7f2b81c769276 Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 03:27:02 +0900 Subject: [PATCH 19/22] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=8F=84=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{GameResultPrinterTest.java => GameOutputTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/test/java/baseball/{GameResultPrinterTest.java => GameOutputTest.java} (98%) diff --git a/src/test/java/baseball/GameResultPrinterTest.java b/src/test/java/baseball/GameOutputTest.java similarity index 98% rename from src/test/java/baseball/GameResultPrinterTest.java rename to src/test/java/baseball/GameOutputTest.java index 370e9a1f..3298a6b9 100644 --- a/src/test/java/baseball/GameResultPrinterTest.java +++ b/src/test/java/baseball/GameOutputTest.java @@ -10,7 +10,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -class GameResultPrinterTest { +class GameOutputTest { private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); private final PrintStream originalOut = System.out; private GameOutput view; From e0e66e67f7fedeb7e97ce231c4ae52583be839bf Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 03:29:15 +0900 Subject: [PATCH 20/22] =?UTF-8?q?test:=20InputTest=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/baseball/GameInputTest.java | 34 +++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/test/java/baseball/GameInputTest.java diff --git a/src/test/java/baseball/GameInputTest.java b/src/test/java/baseball/GameInputTest.java new file mode 100644 index 00000000..cac32c85 --- /dev/null +++ b/src/test/java/baseball/GameInputTest.java @@ -0,0 +1,34 @@ +package baseball; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class GameInputTest { + + // 트림 기능 테스트 + @Test + void readLine_trims_leading_and_trailing_spaces() { + GameInput input = new GameInput() { + @Override + public String readLine() { + return " 123 "; + } + }; + String result = input.readLine(); + assertEquals("123", result); + } + + // 일반 입력 테스트 + @Test + void readLine_returns_input_as_is() { + GameInput input = new GameInput() { + @Override + public String readLine() { + return "456"; + } + }; + String result = input.readLine(); + assertEquals("456", result); + } +} \ No newline at end of file From d935656a3770de35ad338de20f4dfb2faf052402 Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 03:38:21 +0900 Subject: [PATCH 21/22] =?UTF-8?q?refactor:=20Input=EC=97=90=EC=84=9C=20Sca?= =?UTF-8?q?nner=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A3=BC=EC=9E=85=20?= =?UTF-8?q?=EB=B0=9B=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/baseball/GameFlowController.java | 3 ++- src/main/java/baseball/GameInput.java | 6 +++--- src/test/java/baseball/GameInputTest.java | 19 +++++++------------ 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/main/java/baseball/GameFlowController.java b/src/main/java/baseball/GameFlowController.java index 7d28606d..93e362dc 100644 --- a/src/main/java/baseball/GameFlowController.java +++ b/src/main/java/baseball/GameFlowController.java @@ -1,13 +1,14 @@ package baseball; import java.util.List; +import java.util.Scanner; public class GameFlowController { private final SecretNumberModel secretModel = new SecretNumberModel(); private final PlayerInputValidator playerInputValidator = new PlayerInputValidator(); private final HintCalculatorModel hintModel = new HintCalculatorModel(); private final GameOutput gameOutput = new GameOutput(); - private final GameInput gameInput = new GameInput(); + private final GameInput gameInput = new GameInput(new Scanner(System.in)); private boolean askRestart() { while (true) { diff --git a/src/main/java/baseball/GameInput.java b/src/main/java/baseball/GameInput.java index 11dc2a39..1d464ba6 100644 --- a/src/main/java/baseball/GameInput.java +++ b/src/main/java/baseball/GameInput.java @@ -6,11 +6,11 @@ public class GameInput { private final Scanner scanner; - public GameInput() { - this.scanner = new Scanner(System.in); + public GameInput(Scanner scanner) { + this.scanner = scanner; } public String readLine() { - return scanner.nextLine().trim(); + return scanner.nextLine().strip(); } } diff --git a/src/test/java/baseball/GameInputTest.java b/src/test/java/baseball/GameInputTest.java index cac32c85..7359c759 100644 --- a/src/test/java/baseball/GameInputTest.java +++ b/src/test/java/baseball/GameInputTest.java @@ -2,6 +2,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.Scanner; + import org.junit.jupiter.api.Test; class GameInputTest { @@ -9,12 +11,9 @@ class GameInputTest { // 트림 기능 테스트 @Test void readLine_trims_leading_and_trailing_spaces() { - GameInput input = new GameInput() { - @Override - public String readLine() { - return " 123 "; - } - }; + Scanner scanner = new Scanner(" 123 "); + GameInput input = new GameInput(scanner); + String result = input.readLine(); assertEquals("123", result); } @@ -22,12 +21,8 @@ public String readLine() { // 일반 입력 테스트 @Test void readLine_returns_input_as_is() { - GameInput input = new GameInput() { - @Override - public String readLine() { - return "456"; - } - }; + Scanner scanner = new Scanner("456"); + GameInput input = new GameInput(scanner); String result = input.readLine(); assertEquals("456", result); } From 3629a7146ae8f23cdc29c5967d7828cbc6a7328d Mon Sep 17 00:00:00 2001 From: mirageoasis Date: Sun, 1 Feb 2026 03:47:14 +0900 Subject: [PATCH 22/22] =?UTF-8?q?docs:=20=EB=AC=B8=EC=84=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 60 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index d762f192..06d455dc 100644 --- a/README.md +++ b/README.md @@ -28,47 +28,69 @@ ```mermaid classDiagram + class Application { + <> + +main(args: String[]): void + } + class GameFlowController { <> + -secretModel: SecretNumberModel + -playerInputValidator: PlayerInputValidator + -hintModel: HintCalculatorModel + -gameOutput: GameOutput + -gameInput: GameInput +start(): void - +askRestart(): boolean - -readLine(): String + -askRestart(): boolean + } + + class GameInput { + <> + -scanner: java.util.Scanner + +readLine(): String + } + + class PlayerInputValidator { + <> + +isValidInput(input: String): boolean + +toDigits(input: String): java.util.List } class SecretNumberModel { <> - -secret: List + -secret: java.util.List +generateSecret(): void - +getSecret(): List + +getSecret(): java.util.List } class HintCalculatorModel { <> - +calculate(secret: List, guess: List): HintResultModel + +calculate(secret: java.util.List, guess: java.util.List): HintResultModel } - class PlayerInputValidator { - <> - +isValidInput(input: String): boolean - +toDigits(input: String): List + class HintResultModel { + <> + -strikes: int + -balls: int + +getStrikes(): int + +toString(): String } - class GameResultModel { + class GameOutput { <> + +showPromptForGuess(): void +showHint(result: HintResultModel): void +showWinAndPromptRestart(): void +showError(message: String): void + +showExitMessage(): void } - class HintResultModel { - <> - +strikes: int - +balls: int - +toString(): String - } - + Application --> GameFlowController: starts GameFlowController --> SecretNumberModel: "정답 생성 요청" - GameFlowController --> PlayerInputValidator: "입력 유효성 검사/숫자 변환 요청" + GameFlowController --> PlayerInputValidator: "입력 검증/숫자 변환 요청" GameFlowController --> HintCalculatorModel: "판정 로직 실행" - GameFlowController --> GameResultModel: "결과/메시지 출력 요청" + GameFlowController --> GameOutput: "결과/메시지 출력 요청" + GameFlowController --> GameInput: "입력(readLine) 요청" + HintCalculatorModel ..> HintResultModel: returns + GameOutput ..> HintResultModel: displays ``` \ No newline at end of file