From 68d651dccb87624059bbf2b88fa9bfe2c1c31ae2 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Wed, 28 Jan 2026 16:33:37 +0900 Subject: [PATCH 01/29] docs(readme): update project description --- README.md | 68 +++++++++++++++++++++++++++++++++- src/main/java/Application.java | 0 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/main/java/Application.java diff --git a/README.md b/README.md index 8d7e8aee..22708bf0 100644 --- a/README.md +++ b/README.md @@ -1 +1,67 @@ -# java-baseball-precourse \ No newline at end of file +# java-baseball-precourse + +## ⚾ 숫자 야구 게임 구현 기능 목록 + +### 1. 게임 실행 +- 프로그램 시작 시 숫자 야구 게임을 실행한다. +- 사용자에게 게임 시작 안내 문구를 출력한다. + +--- + +### 2. 숫자 야구 게임 정답 생성 +- 컴퓨터는 1~9 사이의 서로 다른 숫자 3개를 랜덤으로 생성한다. +- 생성된 숫자는 게임이 끝날 때까지 정답으로 유지한다. + +--- + +### 3. 사용자 입력 처리 +- 사용자가 3자리 숫자를 입력한다. +- 입력은 한 번에 문자열 형태로 받는다. + +--- + +### 4. 입력 검증 +사용자의 입력이 올바른 형식인지 검증한다. + +#### 4-2. 입력 길이가 3인지 확인 +- 입력값이 정확히 3자리인지 검사한다. + +#### 4-2. 숫자만 입력되었는지 확인 +- 입력값이 1-9 사이의 숫자로만 구성되어 있는지 검사한다. + +#### 4-3. 중복 숫가 존재하는 지 확인 +- 입력값이 1-9 사이의 숫자로만 구성되어 있는지 검사한다. + +#### 4-4. 예외 발생 시 출력 +- 잘못된 입력이 들어오면 [ERROR]로 시작하는 메시지를 출력한다. + +--- + +### 5. 판정 로직 +입력값과 정답을 비교하여 결과를 판정한다. + +#### 5-1. Strike 판정 +- 숫자와 위치가 모두 일치하면 `strike`로 판단한다. + +#### 5-2. Ball 판정 +- 숫자는 포함되지만 위치가 다르면 `ball`로 판단한다. + +#### 5-3. Nothing 판정 +- 일치하는 숫자가 하나도 없다면 `낫싱`을 출력한다. + +--- + +### 6. 결과 출력 +- 판정 결과에 따라 `볼`, `스트라이크`, `낫싱`을 출력한다. +- 3 strike가 되면 게임 종료 문구를 출력한다. + +--- + +### 7. 재시작 및 종료 +게임이 종료된 후 사용자의 선택을 입력받는다. + +#### 7-1. 게임 재시작 +- 사용자가 `1`을 입력하면 새로운 게임을 시작한다. + +#### 7-2. 게임 종료 +- 사용자가 `2`를 입력하면 프로그램을 종료한다. diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 00000000..e69de29b From c5d5e9ceb94542373ece11f8d86db198ec975d54 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Wed, 28 Jan 2026 17:24:14 +0900 Subject: [PATCH 02/29] chore: set up initial project structure --- README.md | 6 ++++-- build.gradle | 5 +++++ src/main/java/Application.java | 7 +++++++ src/main/java/controller/GameController.java | 5 +++++ src/main/java/view/Message.java | 4 ++++ src/main/java/view/OutputView.java | 4 ++++ 6 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 src/main/java/controller/GameController.java create mode 100644 src/main/java/view/Message.java create mode 100644 src/main/java/view/OutputView.java diff --git a/README.md b/README.md index 22708bf0..483c5eca 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# java-baseball-precourse +~~# java-baseball-precourse ## ⚾ 숫자 야구 게임 구현 기능 목록 @@ -64,4 +64,6 @@ - 사용자가 `1`을 입력하면 새로운 게임을 시작한다. #### 7-2. 게임 종료 -- 사용자가 `2`를 입력하면 프로그램을 종료한다. +- 사용자가 `2`를 입력하면 프로그램을 종료한다.~~ + + diff --git a/build.gradle b/build.gradle index 20a92c9e..a620394e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,6 @@ plugins { id 'java' + id 'application' } group = 'camp.nextstep.edu' @@ -23,3 +24,7 @@ dependencies { test { useJUnitPlatform() } + +application { + mainClass = 'Application' +} diff --git a/src/main/java/Application.java b/src/main/java/Application.java index e69de29b..cee41f4f 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -0,0 +1,7 @@ +import controller.GameController; + +public class Application { + public static void main(String[] args) { + new GameController().run(); + } +} diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java new file mode 100644 index 00000000..da4e765d --- /dev/null +++ b/src/main/java/controller/GameController.java @@ -0,0 +1,5 @@ +package controller; + +public class GameController { + +} diff --git a/src/main/java/view/Message.java b/src/main/java/view/Message.java new file mode 100644 index 00000000..7900e337 --- /dev/null +++ b/src/main/java/view/Message.java @@ -0,0 +1,4 @@ +package view; + +public class Message { +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 00000000..d8f9743c --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,4 @@ +package view; + +public class OutputView { +} From f0f1a4236935f33dfbf036ded7b0eb2e28fecbc6 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Wed, 28 Jan 2026 17:30:43 +0900 Subject: [PATCH 03/29] feat(game): add start message output --- src/main/java/controller/GameController.java | 6 ++++++ src/main/java/view/Message.java | 1 + src/main/java/view/OutputView.java | 3 +++ 3 files changed, 10 insertions(+) diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index da4e765d..0e141b61 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -1,5 +1,11 @@ package controller; +import view.OutputView; + public class GameController { + private final OutputView outputView = new OutputView(); + public void run() { + outputView.printStartMessage(); + } } diff --git a/src/main/java/view/Message.java b/src/main/java/view/Message.java index 7900e337..5e7a48ad 100644 --- a/src/main/java/view/Message.java +++ b/src/main/java/view/Message.java @@ -1,4 +1,5 @@ package view; public class Message { + public static final String START_MESSAGE = "숫자 야구 게임 시작"; } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index d8f9743c..af0f3461 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,4 +1,7 @@ package view; public class OutputView { + public void printStartMessage() { + System.out.println(Message.START_MESSAGE); + } } From 996b98e9b120a4bccde871ea9430e47c0d896973 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Wed, 28 Jan 2026 18:06:49 +0900 Subject: [PATCH 04/29] feat(game) add random number generation --- .../java/service/RandomNumberGenerator.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/main/java/service/RandomNumberGenerator.java diff --git a/src/main/java/service/RandomNumberGenerator.java b/src/main/java/service/RandomNumberGenerator.java new file mode 100644 index 00000000..30352ee2 --- /dev/null +++ b/src/main/java/service/RandomNumberGenerator.java @@ -0,0 +1,18 @@ +package service; + +import java.util.concurrent.ThreadLocalRandom; + +public class RandomNumberGenerator { + static String makeAnswer() { + int[] d = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + for (int i = d.length - 1; i > 0; i--) { + int j = ThreadLocalRandom.current().nextInt(i + 1); + int tmp = d[i]; + d[i] = d[j]; + d[j] = tmp; + } + + return "" + d[0] + d[1] + d[2]; + } +} From 4c975608d619709bfb919235d05473f6c0fc423b Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Wed, 28 Jan 2026 18:34:34 +0900 Subject: [PATCH 05/29] feat(game) add GameController get answer --- src/main/java/controller/GameController.java | 9 +++++++++ src/main/java/domain/ComputerNumber.java | 13 +++++++++++++ src/main/java/service/ComputerNumberService.java | 13 +++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 src/main/java/domain/ComputerNumber.java create mode 100644 src/main/java/service/ComputerNumberService.java diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index 0e141b61..c60845f1 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -1,11 +1,20 @@ package controller; +import domain.ComputerNumber; +import service.ComputerNumberService; import view.OutputView; public class GameController { private final OutputView outputView = new OutputView(); + private final ComputerNumberService computerNumberService = + new ComputerNumberService(); + public void run() { outputView.printStartMessage(); + + ComputerNumber answer = computerNumberService.create(); + +// System.out.println(answer.getNumbers()); } } diff --git a/src/main/java/domain/ComputerNumber.java b/src/main/java/domain/ComputerNumber.java new file mode 100644 index 00000000..2adab77c --- /dev/null +++ b/src/main/java/domain/ComputerNumber.java @@ -0,0 +1,13 @@ +package domain; + +public class ComputerNumber { + private final String numbers; + + public ComputerNumber(String numbers) { + this.numbers = numbers; + } + + public String getNumbers() { + return numbers; + } +} diff --git a/src/main/java/service/ComputerNumberService.java b/src/main/java/service/ComputerNumberService.java new file mode 100644 index 00000000..c4347c15 --- /dev/null +++ b/src/main/java/service/ComputerNumberService.java @@ -0,0 +1,13 @@ +package service; + +import domain.ComputerNumber; + +public class ComputerNumberService { + public ComputerNumberService() { + } + + public ComputerNumber create() { + String numbers = RandomNumberGenerator.makeAnswer(); + return new ComputerNumber(numbers); + } +} From d289ad291292d87d3c66afa0407caf170aa1d690 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Sun, 1 Feb 2026 20:50:20 +0900 Subject: [PATCH 06/29] feat(game): add print enter usernumber --- src/main/java/controller/GameController.java | 7 ++++++- src/main/java/domain/ComputerNumber.java | 11 +---------- src/main/java/view/InputView.java | 14 ++++++++++++++ src/main/java/view/Message.java | 5 ----- src/main/java/view/OutputView.java | 2 +- 5 files changed, 22 insertions(+), 17 deletions(-) create mode 100644 src/main/java/view/InputView.java delete mode 100644 src/main/java/view/Message.java diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index c60845f1..7c08a662 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -2,10 +2,12 @@ import domain.ComputerNumber; import service.ComputerNumberService; +import view.InputView; import view.OutputView; public class GameController { private final OutputView outputView = new OutputView(); + private final InputView inputView = new InputView(); private final ComputerNumberService computerNumberService = new ComputerNumberService(); @@ -15,6 +17,9 @@ public void run() { ComputerNumber answer = computerNumberService.create(); -// System.out.println(answer.getNumbers()); + String guess = inputView.readGuess(); + +// System.out.println(answer.numbers()); +// System.out.println(guess); } } diff --git a/src/main/java/domain/ComputerNumber.java b/src/main/java/domain/ComputerNumber.java index 2adab77c..d1575983 100644 --- a/src/main/java/domain/ComputerNumber.java +++ b/src/main/java/domain/ComputerNumber.java @@ -1,13 +1,4 @@ package domain; -public class ComputerNumber { - private final String numbers; - - public ComputerNumber(String numbers) { - this.numbers = numbers; - } - - public String getNumbers() { - return numbers; - } +public record ComputerNumber(String numbers) { } diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 00000000..bc185bd1 --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,14 @@ +package view; + +import java.util.Scanner; + +public class InputView { + private static final Scanner scanner = new Scanner(System.in); + + public String readGuess() { + System.out.print("숫자를 입력해 주세요: "); + return scanner.nextLine(); + } + + +} diff --git a/src/main/java/view/Message.java b/src/main/java/view/Message.java deleted file mode 100644 index 5e7a48ad..00000000 --- a/src/main/java/view/Message.java +++ /dev/null @@ -1,5 +0,0 @@ -package view; - -public class Message { - public static final String START_MESSAGE = "숫자 야구 게임 시작"; -} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index af0f3461..f62ee0d4 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -2,6 +2,6 @@ public class OutputView { public void printStartMessage() { - System.out.println(Message.START_MESSAGE); + System.out.println("숫자 야구 게임 시작"); } } From 7eb46e66ece3b9f016cdea467aaa09f24284ad2c Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Sun, 1 Feb 2026 21:25:14 +0900 Subject: [PATCH 07/29] refactor: reorganize domain (add Numbers class) --- src/main/java/controller/GameController.java | 8 ++--- src/main/java/domain/ComputerNumber.java | 4 --- src/main/java/domain/Numbers.java | 30 +++++++++++++++++++ .../java/service/ComputerNumberService.java | 13 -------- src/main/java/service/GameService.java | 5 ++++ .../java/service/RandomNumberGenerator.java | 20 ++++++++----- 6 files changed, 50 insertions(+), 30 deletions(-) delete mode 100644 src/main/java/domain/ComputerNumber.java create mode 100644 src/main/java/domain/Numbers.java delete mode 100644 src/main/java/service/ComputerNumberService.java create mode 100644 src/main/java/service/GameService.java diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index 7c08a662..b6b996d0 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -1,7 +1,6 @@ package controller; -import domain.ComputerNumber; -import service.ComputerNumberService; +import domain.Numbers; import view.InputView; import view.OutputView; @@ -9,14 +8,11 @@ public class GameController { private final OutputView outputView = new OutputView(); private final InputView inputView = new InputView(); - private final ComputerNumberService computerNumberService = - new ComputerNumberService(); + public void run() { outputView.printStartMessage(); - ComputerNumber answer = computerNumberService.create(); - String guess = inputView.readGuess(); // System.out.println(answer.numbers()); diff --git a/src/main/java/domain/ComputerNumber.java b/src/main/java/domain/ComputerNumber.java deleted file mode 100644 index d1575983..00000000 --- a/src/main/java/domain/ComputerNumber.java +++ /dev/null @@ -1,4 +0,0 @@ -package domain; - -public record ComputerNumber(String numbers) { -} diff --git a/src/main/java/domain/Numbers.java b/src/main/java/domain/Numbers.java new file mode 100644 index 00000000..3ebccaca --- /dev/null +++ b/src/main/java/domain/Numbers.java @@ -0,0 +1,30 @@ +package domain; + +import java.util.ArrayList; +import java.util.List; + +public class Numbers { + private final List numbers; + + private Numbers(List numbers) { + this.numbers = numbers; + } + + public static Numbers from(String input) { + return new Numbers(parse(input)); + } + + private static List parse(String input) { + List nums = new ArrayList<>(3); + for (int i = 0; i < 3; i++) nums.add(input.charAt(i) - '0'); + return nums; + } + + public int get(int index) { + return numbers.get(index); + } + + public boolean contains(int number) { + return numbers.contains(number); + } +} \ No newline at end of file diff --git a/src/main/java/service/ComputerNumberService.java b/src/main/java/service/ComputerNumberService.java deleted file mode 100644 index c4347c15..00000000 --- a/src/main/java/service/ComputerNumberService.java +++ /dev/null @@ -1,13 +0,0 @@ -package service; - -import domain.ComputerNumber; - -public class ComputerNumberService { - public ComputerNumberService() { - } - - public ComputerNumber create() { - String numbers = RandomNumberGenerator.makeAnswer(); - return new ComputerNumber(numbers); - } -} diff --git a/src/main/java/service/GameService.java b/src/main/java/service/GameService.java new file mode 100644 index 00000000..4d549a6c --- /dev/null +++ b/src/main/java/service/GameService.java @@ -0,0 +1,5 @@ +package service; + +public class GameService { + +} diff --git a/src/main/java/service/RandomNumberGenerator.java b/src/main/java/service/RandomNumberGenerator.java index 30352ee2..94580d27 100644 --- a/src/main/java/service/RandomNumberGenerator.java +++ b/src/main/java/service/RandomNumberGenerator.java @@ -3,16 +3,22 @@ import java.util.concurrent.ThreadLocalRandom; public class RandomNumberGenerator { - static String makeAnswer() { - int[] d = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + public String numberGenerate() { + int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + shuffle(numbers); + return "" + numbers[0] + numbers[1] + numbers[2]; + } - for (int i = d.length - 1; i > 0; i--) { + private void shuffle(int[] arr) { + for (int i = arr.length - 1; i > 0; i--) { int j = ThreadLocalRandom.current().nextInt(i + 1); - int tmp = d[i]; - d[i] = d[j]; - d[j] = tmp; + swap(arr, i, j); } + } - return "" + d[0] + d[1] + d[2]; + private void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; } } From 67d20be7d85a681dcbed2afe446c3075e1e6d8c0 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Sun, 1 Feb 2026 21:51:59 +0900 Subject: [PATCH 08/29] refactor: add GameService, change GameController --- src/main/java/Application.java | 12 +++++++++++- src/main/java/controller/GameController.java | 16 +++++++++------- src/main/java/service/GameService.java | 11 +++++++++++ src/main/java/service/RandomNumberGenerator.java | 6 ++++-- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/main/java/Application.java b/src/main/java/Application.java index cee41f4f..69560bc3 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,7 +1,17 @@ import controller.GameController; +import service.GameService; +import view.InputView; +import view.OutputView; +import service.RandomNumberGenerator; public class Application { public static void main(String[] args) { - new GameController().run(); + InputView inputView = new InputView(); + OutputView outputView = new OutputView(); + GameService gameService = new GameService( + new RandomNumberGenerator() + ); + + new GameController(inputView, outputView, gameService).run(); } } diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index b6b996d0..aa513f34 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -1,21 +1,23 @@ package controller; import domain.Numbers; +import service.GameService; import view.InputView; import view.OutputView; public class GameController { - private final OutputView outputView = new OutputView(); - private final InputView inputView = new InputView(); - + private final InputView inputView; + private final OutputView outputView; + private final GameService gameService; + public GameController(InputView inputView, OutputView outputView, GameService gameService) { + this.inputView = inputView; + this.outputView = outputView; + this.gameService = gameService; + } public void run() { outputView.printStartMessage(); - String guess = inputView.readGuess(); - -// System.out.println(answer.numbers()); -// System.out.println(guess); } } diff --git a/src/main/java/service/GameService.java b/src/main/java/service/GameService.java index 4d549a6c..b7dfec2d 100644 --- a/src/main/java/service/GameService.java +++ b/src/main/java/service/GameService.java @@ -1,5 +1,16 @@ package service; +import domain.Numbers; + public class GameService { + private final RandomNumberGenerator randomNumberGenerator; + private Numbers answer; + + public GameService(RandomNumberGenerator randomNumberGenerator) { + this.randomNumberGenerator = randomNumberGenerator; + } + public void startNewGame() { + answer = randomNumberGenerator.numberGenerate(); + } } diff --git a/src/main/java/service/RandomNumberGenerator.java b/src/main/java/service/RandomNumberGenerator.java index 94580d27..0838d9f2 100644 --- a/src/main/java/service/RandomNumberGenerator.java +++ b/src/main/java/service/RandomNumberGenerator.java @@ -1,12 +1,14 @@ package service; +import domain.Numbers; + import java.util.concurrent.ThreadLocalRandom; public class RandomNumberGenerator { - public String numberGenerate() { + public Numbers numberGenerate() { int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9}; shuffle(numbers); - return "" + numbers[0] + numbers[1] + numbers[2]; + return Numbers.from("" + numbers[0] + numbers[1] + numbers[2]); } private void shuffle(int[] arr) { From 327bac102ad20d711c5d80210c4eac0760f373e4 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Sun, 1 Feb 2026 22:20:27 +0900 Subject: [PATCH 09/29] feat(game) add inputValidator - check input --- src/main/java/util/InputValidator.java | 46 ++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/main/java/util/InputValidator.java diff --git a/src/main/java/util/InputValidator.java b/src/main/java/util/InputValidator.java new file mode 100644 index 00000000..689444fb --- /dev/null +++ b/src/main/java/util/InputValidator.java @@ -0,0 +1,46 @@ +package util; + +import java.util.HashSet; +import java.util.Set; + +public class InputValidator { + private static final String ERROR_PREFIX = "[ERROR] "; + private static final int INPUT_LENGTH = 3; + private static final String RESTART_CODE = "1"; + private static final String EXIT_CODE = "2"; + + public static String guessError(String input) { + if (input.length() != INPUT_LENGTH) { + return errorMessage(INPUT_LENGTH + "자여야 합니다."); + } + return validateCharacters(input); + } + + private static String validateCharacters(String input) { + Set uniqueChars = new HashSet<>(); + + for (char c : input.toCharArray()) { + if (!Character.isDigit(c)) { + return errorMessage("숫자만 입력해야 합니다."); + } + if (c == '0') { + return errorMessage("0은 사용할 수 없습니다."); + } + if (!uniqueChars.add(c)) { + return errorMessage("중복된 숫자는 사용할 수 없습니다."); + } + } + return null; + } + + public static String restartError(String input) { + if (RESTART_CODE.equals(input) || EXIT_CODE.equals(input)) { + return null; + } + return errorMessage(String.format("재시작은 %s, 종료는 %s만 입력 가능합니다.", RESTART_CODE, EXIT_CODE)); + } + + private static String errorMessage(String message) { + return ERROR_PREFIX + message; + } +} From 2ffff42b0a7ab84983ed18ce6c924cc1e7a4e727 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Sun, 1 Feb 2026 22:30:14 +0900 Subject: [PATCH 10/29] feat(domain): add new domain model(JudgeCount) --- src/main/java/domain/JudgeCount.java | 33 +++++++++++++++++++++++++ src/main/java/service/JudgeService.java | 5 ++++ 2 files changed, 38 insertions(+) create mode 100644 src/main/java/domain/JudgeCount.java create mode 100644 src/main/java/service/JudgeService.java diff --git a/src/main/java/domain/JudgeCount.java b/src/main/java/domain/JudgeCount.java new file mode 100644 index 00000000..a619876f --- /dev/null +++ b/src/main/java/domain/JudgeCount.java @@ -0,0 +1,33 @@ +package domain; + +public class JudgeCount { + private final int ball; + private final int strike; + + public JudgeCount(int ball, int strike) { + this.ball = ball; + this.strike = strike; + } + + public boolean isThreeStrikes() { + return strike == 3; + } + + public String toMessage() { + if (ball == 0 && strike == 0) return "낫싱"; + StringBuilder sb = new StringBuilder(); + appendBall(sb); + appendStrike(sb); + return sb.toString().trim(); + } + + private void appendBall(StringBuilder sb) { + if (ball == 0) return; + sb.append(ball).append("볼 "); + } + + private void appendStrike(StringBuilder sb) { + if (strike == 0) return; + sb.append(strike).append("스트라이크"); + } +} diff --git a/src/main/java/service/JudgeService.java b/src/main/java/service/JudgeService.java new file mode 100644 index 00000000..df9dbeb1 --- /dev/null +++ b/src/main/java/service/JudgeService.java @@ -0,0 +1,5 @@ +package service; + +public class JudgeService { + public +} From 3f98fc31ef173a109ab1ff080430bbec5a2a1cb7 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Sun, 1 Feb 2026 22:47:19 +0900 Subject: [PATCH 11/29] feat(game): add count feature (add JudgeService) --- src/main/java/domain/Numbers.java | 4 ++++ src/main/java/service/JudgeService.java | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/domain/Numbers.java b/src/main/java/domain/Numbers.java index 3ebccaca..5413e6c8 100644 --- a/src/main/java/domain/Numbers.java +++ b/src/main/java/domain/Numbers.java @@ -27,4 +27,8 @@ public int get(int index) { public boolean contains(int number) { return numbers.contains(number); } + + public int size() { + return numbers.size(); + } } \ No newline at end of file diff --git a/src/main/java/service/JudgeService.java b/src/main/java/service/JudgeService.java index df9dbeb1..563829c7 100644 --- a/src/main/java/service/JudgeService.java +++ b/src/main/java/service/JudgeService.java @@ -1,5 +1,22 @@ package service; +import domain.JudgeCount; +import domain.Numbers; + public class JudgeService { - public + public JudgeCount judge(Numbers answer, Numbers guess) { + int ball = 0; + int strike = 0; + for (int i = 0; i < answer.size(); i++) { + int g = guess.get(i); + if (g == answer.get(i)) { + strike++; + continue; + } + if (answer.contains(g)) { + ball++; + } + } + return new JudgeCount(ball, strike); + } } From 9a950e9fe8261aee6dd0249b31cdefb08c88266c Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Sun, 1 Feb 2026 23:13:24 +0900 Subject: [PATCH 12/29] feat(add): add get user input (add InputView) --- src/main/java/view/InputView.java | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index bc185bd1..4b78a275 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -1,13 +1,36 @@ package view; +import util.InputValidator; + import java.util.Scanner; public class InputView { + private static final String GUESS_PROMPT = "숫자를 입력해주세요 : "; + private static final String RESTART_PROMPT = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."; private static final Scanner scanner = new Scanner(System.in); public String readGuess() { - System.out.print("숫자를 입력해 주세요: "); - return scanner.nextLine(); + while (true) { + System.out.print("숫자를 입력해주세요 : "); + String input = scanner.nextLine().trim(); + String error = InputValidator.guessError(input); + if (error == null) { + return input; + } + System.out.println(error); + } + } + + public int readRestartCommand() { + while (true) { + System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."); + String input = scanner.nextLine().trim(); + String error = InputValidator.restartError(input); + if (error == null) { + return Integer.parseInt(input); + } + System.out.println(error); + } } From 04d4bd6772262587a4106af670bf93f6752995b4 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Sun, 1 Feb 2026 23:15:55 +0900 Subject: [PATCH 13/29] refactor: inputview variable --- src/main/java/view/InputView.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 4b78a275..c9c302c2 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -11,7 +11,7 @@ public class InputView { public String readGuess() { while (true) { - System.out.print("숫자를 입력해주세요 : "); + System.out.print(GUESS_PROMPT); String input = scanner.nextLine().trim(); String error = InputValidator.guessError(input); if (error == null) { @@ -23,7 +23,7 @@ public String readGuess() { public int readRestartCommand() { while (true) { - System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."); + System.out.println(RESTART_PROMPT); String input = scanner.nextLine().trim(); String error = InputValidator.restartError(input); if (error == null) { From 0f7bb29a4421d89a9e1541a02e8c7805ca16a937 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Sun, 1 Feb 2026 23:19:25 +0900 Subject: [PATCH 14/29] refactor: InputView delete repetition --- src/main/java/view/InputView.java | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index c9c302c2..b2ce1241 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -3,6 +3,7 @@ import util.InputValidator; import java.util.Scanner; +import java.util.function.Function; public class InputView { private static final String GUESS_PROMPT = "숫자를 입력해주세요 : "; @@ -10,28 +11,24 @@ public class InputView { private static final Scanner scanner = new Scanner(System.in); public String readGuess() { - while (true) { - System.out.print(GUESS_PROMPT); - String input = scanner.nextLine().trim(); - String error = InputValidator.guessError(input); - if (error == null) { - return input; - } - System.out.println(error); - } + return readUntilValid(GUESS_PROMPT, InputValidator::guessError); } public int readRestartCommand() { + String input = readUntilValid(RESTART_PROMPT, InputValidator::restartError); + return Integer.parseInt(input); + } + + private String readUntilValid(String prompt, Function validator) { while (true) { - System.out.println(RESTART_PROMPT); + System.out.println(prompt); String input = scanner.nextLine().trim(); - String error = InputValidator.restartError(input); + String error = validator.apply(input); + if (error == null) { - return Integer.parseInt(input); + return input; } System.out.println(error); } } - - } From c8a2119ce4f38a0b7c5b0bdae576ab88b88962e3 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Sun, 1 Feb 2026 23:28:59 +0900 Subject: [PATCH 15/29] feat(game): add playing game service (add GameService) --- src/main/java/service/GameService.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/service/GameService.java b/src/main/java/service/GameService.java index b7dfec2d..c20a98c6 100644 --- a/src/main/java/service/GameService.java +++ b/src/main/java/service/GameService.java @@ -1,16 +1,33 @@ package service; +import domain.JudgeCount; import domain.Numbers; public class GameService { private final RandomNumberGenerator randomNumberGenerator; + private final JudgeService judgeService; + private Numbers answer; + private JudgeCount lastCount; - public GameService(RandomNumberGenerator randomNumberGenerator) { + public GameService(RandomNumberGenerator randomNumberGenerator, JudgeService judgeService) { this.randomNumberGenerator = randomNumberGenerator; + this.judgeService = judgeService; } public void startNewGame() { answer = randomNumberGenerator.numberGenerate(); + lastCount = null; + } + + public JudgeCount play(String guessInput) { + JudgeCount count = judgeService.judge(answer, Numbers.from(guessInput)); + lastCount = count; + return count; + } + + public boolean isGameOver() { + if (lastCount == null) return false; + return lastCount.isThreeStrikes(); } } From 99883cb3d8c071499d92505cd59d67ff586432ab Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Sun, 1 Feb 2026 23:34:49 +0900 Subject: [PATCH 16/29] feat(game): add print result message --- src/main/java/view/OutputView.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index f62ee0d4..dd038a6b 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,7 +1,20 @@ package view; +import domain.JudgeCount; + public class OutputView { + private static final String START_MESSAGE = "숫자 야구 게임 시작"; + private static final String END_MESSAGE = "3개의 숫자를 모두 맞히셨습니다! 게임 끝"; + public void printStartMessage() { - System.out.println("숫자 야구 게임 시작"); + System.out.println(START_MESSAGE); + } + + public void printResult(JudgeCount count) { + System.out.println(count.toMessage()); + } + + public void printGameEndMessage() { + System.out.println(END_MESSAGE); } } From 745bd4c34427ce3df0f581b4d335971b50335b4f Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Sun, 1 Feb 2026 23:59:17 +0900 Subject: [PATCH 17/29] =?UTF-8?q?feat(game)=20game=20=ED=95=9C=20=ED=9D=90?= =?UTF-8?q?=EB=A6=84=20=EC=A7=84=ED=96=89=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 4 ++- src/main/java/controller/GameController.java | 28 +++++++++++++++++++- src/main/java/view/InputView.java | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/main/java/Application.java b/src/main/java/Application.java index 69560bc3..ddc4ab1c 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,5 +1,6 @@ import controller.GameController; import service.GameService; +import service.JudgeService; import view.InputView; import view.OutputView; import service.RandomNumberGenerator; @@ -9,7 +10,8 @@ public static void main(String[] args) { InputView inputView = new InputView(); OutputView outputView = new OutputView(); GameService gameService = new GameService( - new RandomNumberGenerator() + new RandomNumberGenerator(), + new JudgeService() ); new GameController(inputView, outputView, gameService).run(); diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index aa513f34..56a434ba 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -1,5 +1,6 @@ package controller; +import domain.JudgeCount; import domain.Numbers; import service.GameService; import view.InputView; @@ -18,6 +19,31 @@ public GameController(InputView inputView, OutputView outputView, GameService ga public void run() { outputView.printStartMessage(); - String guess = inputView.readGuess(); + while (true) { + playGame(); + if (shouldStop()) { + return; + } + } + } + + private void playGame() { + gameService.startNewGame(); + playUntilWin(); + } + + private void playUntilWin() { + while (true) { + JudgeCount count = gameService.play(inputView.readGuess()); + outputView.printResult(count); + if (count.isThreeStrikes()) { + break; + } + } + outputView.printGameEndMessage(); + } + + private boolean shouldStop() { + return inputView.readRestartCommand() == 2; } } diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index b2ce1241..49e3f13d 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -8,6 +8,7 @@ public class InputView { private static final String GUESS_PROMPT = "숫자를 입력해주세요 : "; private static final String RESTART_PROMPT = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."; + private static final Scanner scanner = new Scanner(System.in); public String readGuess() { From b27b6a076d44dc6602b646a71e3471ff8557d9cb Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Mon, 2 Feb 2026 00:14:08 +0900 Subject: [PATCH 18/29] =?UTF-8?q?fix(input):=20prompt=20print=20println=20?= =?UTF-8?q?=EA=B5=AC=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/view/InputView.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 49e3f13d..240930de 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -3,6 +3,7 @@ import util.InputValidator; import java.util.Scanner; +import java.util.function.Consumer; import java.util.function.Function; public class InputView { @@ -12,17 +13,17 @@ public class InputView { private static final Scanner scanner = new Scanner(System.in); public String readGuess() { - return readUntilValid(GUESS_PROMPT, InputValidator::guessError); + return readUntilValid(GUESS_PROMPT, System.out::print, InputValidator::guessError); } public int readRestartCommand() { - String input = readUntilValid(RESTART_PROMPT, InputValidator::restartError); + String input = readUntilValid(RESTART_PROMPT, System.out::println, InputValidator::restartError); return Integer.parseInt(input); } - private String readUntilValid(String prompt, Function validator) { + private String readUntilValid(String prompt, Consumer printer, Function validator) { while (true) { - System.out.println(prompt); + printer.accept(prompt); String input = scanner.nextLine().trim(); String error = validator.apply(input); From 0931c9a2a9dd821a0cd5a093c692d99b8d4e251f Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Mon, 2 Feb 2026 17:45:27 +0900 Subject: [PATCH 19/29] feat(error): add error handling function at Numbers from --- src/main/java/controller/GameController.java | 5 +---- src/main/java/domain/Numbers.java | 6 ++++++ src/main/java/util/{InputValidator.java => Validator.java} | 2 +- src/main/java/view/InputView.java | 7 ++++--- 4 files changed, 12 insertions(+), 8 deletions(-) rename src/main/java/util/{InputValidator.java => Validator.java} (98%) diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index 56a434ba..0b56658d 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -33,12 +33,9 @@ private void playGame() { } private void playUntilWin() { - while (true) { + while (!gameService.isGameOver()) { JudgeCount count = gameService.play(inputView.readGuess()); outputView.printResult(count); - if (count.isThreeStrikes()) { - break; - } } outputView.printGameEndMessage(); } diff --git a/src/main/java/domain/Numbers.java b/src/main/java/domain/Numbers.java index 5413e6c8..c380136c 100644 --- a/src/main/java/domain/Numbers.java +++ b/src/main/java/domain/Numbers.java @@ -1,5 +1,7 @@ package domain; +import util.Validator; + import java.util.ArrayList; import java.util.List; @@ -11,6 +13,10 @@ private Numbers(List numbers) { } public static Numbers from(String input) { + String error = Validator.guessError(input); + if (error != null) { + throw new IllegalArgumentException(error); + } return new Numbers(parse(input)); } diff --git a/src/main/java/util/InputValidator.java b/src/main/java/util/Validator.java similarity index 98% rename from src/main/java/util/InputValidator.java rename to src/main/java/util/Validator.java index 689444fb..bbf864ae 100644 --- a/src/main/java/util/InputValidator.java +++ b/src/main/java/util/Validator.java @@ -3,7 +3,7 @@ import java.util.HashSet; import java.util.Set; -public class InputValidator { +public class Validator { private static final String ERROR_PREFIX = "[ERROR] "; private static final int INPUT_LENGTH = 3; private static final String RESTART_CODE = "1"; diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 240930de..a9addb74 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -1,6 +1,6 @@ package view; -import util.InputValidator; +import util.Validator; import java.util.Scanner; import java.util.function.Consumer; @@ -13,11 +13,11 @@ public class InputView { private static final Scanner scanner = new Scanner(System.in); public String readGuess() { - return readUntilValid(GUESS_PROMPT, System.out::print, InputValidator::guessError); + return readUntilValid(GUESS_PROMPT, System.out::print, Validator::guessError); } public int readRestartCommand() { - String input = readUntilValid(RESTART_PROMPT, System.out::println, InputValidator::restartError); + String input = readUntilValid(RESTART_PROMPT, System.out::println, Validator::restartError); return Integer.parseInt(input); } @@ -31,6 +31,7 @@ private String readUntilValid(String prompt, Consumer printer, Function< return input; } System.out.println(error); +// throw new IllegalArgumentException(error); } } } From 9221ebebc8e2556a4e2bd4ee1974585ea40d0dd7 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Mon, 2 Feb 2026 17:50:42 +0900 Subject: [PATCH 20/29] feat(domain): add new domain(Number) --- src/main/java/domain/Number.java | 25 +++++++++++++++++++++++++ src/main/java/util/Validator.java | 18 +++++++++++++----- src/main/java/view/InputView.java | 1 - 3 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 src/main/java/domain/Number.java diff --git a/src/main/java/domain/Number.java b/src/main/java/domain/Number.java new file mode 100644 index 00000000..b8c80a1b --- /dev/null +++ b/src/main/java/domain/Number.java @@ -0,0 +1,25 @@ +package domain; + +import util.Validator; + +public class Number { + private final int value; + + private Number(int value) { + this.value = value; + } + + public static Number fromChar(char c) { + String error = Validator.numberError(c); + if (error != null) throw new IllegalArgumentException(error); + return new Number(c - '0'); + } + + public int value() { + return value; + } + + public boolean same(int other) { + return value == other; + } +} diff --git a/src/main/java/util/Validator.java b/src/main/java/util/Validator.java index bbf864ae..fc5b5911 100644 --- a/src/main/java/util/Validator.java +++ b/src/main/java/util/Validator.java @@ -20,11 +20,9 @@ private static String validateCharacters(String input) { Set uniqueChars = new HashSet<>(); for (char c : input.toCharArray()) { - if (!Character.isDigit(c)) { - return errorMessage("숫자만 입력해야 합니다."); - } - if (c == '0') { - return errorMessage("0은 사용할 수 없습니다."); + String error = numberError(c); + if (error != null) { + return error; } if (!uniqueChars.add(c)) { return errorMessage("중복된 숫자는 사용할 수 없습니다."); @@ -33,6 +31,16 @@ private static String validateCharacters(String input) { return null; } + public static String numberError(char c) { + if (!Character.isDigit(c)) { + return errorMessage("숫자만 입력해야 합니다."); + } + if (c == '0') { + return errorMessage("0은 사용할 수 없습니다."); + } + return null; + } + public static String restartError(String input) { if (RESTART_CODE.equals(input) || EXIT_CODE.equals(input)) { return null; diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index a9addb74..f803d6cd 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -31,7 +31,6 @@ private String readUntilValid(String prompt, Consumer printer, Function< return input; } System.out.println(error); -// throw new IllegalArgumentException(error); } } } From 2e5539cf3f43617a0d63b7e660302703714185bf Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Mon, 2 Feb 2026 18:37:30 +0900 Subject: [PATCH 21/29] feat(domain): Number domain apply --- src/main/java/domain/Numbers.java | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main/java/domain/Numbers.java b/src/main/java/domain/Numbers.java index c380136c..2b33fa34 100644 --- a/src/main/java/domain/Numbers.java +++ b/src/main/java/domain/Numbers.java @@ -6,9 +6,10 @@ import java.util.List; public class Numbers { - private final List numbers; + private static final int LENGTH = 3; + private final List numbers; - private Numbers(List numbers) { + private Numbers(List numbers) { this.numbers = numbers; } @@ -20,18 +21,25 @@ public static Numbers from(String input) { return new Numbers(parse(input)); } - private static List parse(String input) { - List nums = new ArrayList<>(3); - for (int i = 0; i < 3; i++) nums.add(input.charAt(i) - '0'); + private static List parse(String input) { + List nums = new ArrayList<>(LENGTH); + for (char c : input.toCharArray()) { + nums.add(Number.fromChar(c)); + } return nums; } public int get(int index) { - return numbers.get(index); + return numbers.get(index).value(); } public boolean contains(int number) { - return numbers.contains(number); + for (Number n : numbers) { + if (n.same(number)) { + return true; + } + } + return false; } public int size() { From b2678eee3e0c1cd3fe4cab1a5e25cd4981e319fa Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Mon, 2 Feb 2026 20:02:42 +0900 Subject: [PATCH 22/29] =?UTF-8?q?test:=20testcase=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/GameController.java | 1 - src/test/java/domain/JudgeCountTest.java | 42 +++++++++++++++++ src/test/java/domain/NumberTest.java | 31 +++++++++++++ src/test/java/domain/NumbersTest.java | 48 ++++++++++++++++++++ src/test/java/service/JudgeServiceTest.java | 35 ++++++++++++++ src/test/java/util/ValidatorTest.java | 39 ++++++++++++++++ 6 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 src/test/java/domain/JudgeCountTest.java create mode 100644 src/test/java/domain/NumberTest.java create mode 100644 src/test/java/domain/NumbersTest.java create mode 100644 src/test/java/service/JudgeServiceTest.java create mode 100644 src/test/java/util/ValidatorTest.java diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index 0b56658d..8dae0260 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -1,7 +1,6 @@ package controller; import domain.JudgeCount; -import domain.Numbers; import service.GameService; import view.InputView; import view.OutputView; diff --git a/src/test/java/domain/JudgeCountTest.java b/src/test/java/domain/JudgeCountTest.java new file mode 100644 index 00000000..f791abdc --- /dev/null +++ b/src/test/java/domain/JudgeCountTest.java @@ -0,0 +1,42 @@ +package domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class JudgeCountTest { + @Test + void toMessageNothing() { + JudgeCount count = new JudgeCount(0, 0); + + assertThat(count.toMessage()).isEqualTo("낫싱"); + } + + @Test + void toMessageBallOnly() { + JudgeCount count = new JudgeCount(2, 0); + + assertThat(count.toMessage()).isEqualTo("2볼"); + } + + @Test + void toMessageStrikeOnly() { + JudgeCount count = new JudgeCount(0, 1); + + assertThat(count.toMessage()).isEqualTo("1스트라이크"); + } + + @Test + void toMessageBallAndStrike() { + JudgeCount count = new JudgeCount(1, 2); + + assertThat(count.toMessage()).isEqualTo("1볼 2스트라이크"); + } + + @Test + void isThreeStrikesTrueWhenThreeStrikes() { + JudgeCount count = new JudgeCount(0, 3); + + assertThat(count.isThreeStrikes()).isTrue(); + } +} diff --git a/src/test/java/domain/NumberTest.java b/src/test/java/domain/NumberTest.java new file mode 100644 index 00000000..f4230420 --- /dev/null +++ b/src/test/java/domain/NumberTest.java @@ -0,0 +1,31 @@ +package domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class NumberTest { + @Test + void fromCharValidDigitCreatesNumber() { + Number number = Number.fromChar('7'); + + assertThat(number.value()).isEqualTo(7); + assertThat(number.same(7)).isTrue(); + assertThat(number.same(5)).isFalse(); + } + + @Test + void fromCharNonDigitThrowsException() { + assertThatThrownBy(() -> Number.fromChar('a')) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 숫자만 입력해야 합니다."); + } + + @Test + void fromCharZeroThrowsException() { + assertThatThrownBy(() -> Number.fromChar('0')) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 0은 사용할 수 없습니다."); + } +} diff --git a/src/test/java/domain/NumbersTest.java b/src/test/java/domain/NumbersTest.java new file mode 100644 index 00000000..4fa7bd07 --- /dev/null +++ b/src/test/java/domain/NumbersTest.java @@ -0,0 +1,48 @@ +package domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class NumbersTest { + @Test + void fromValidInputParsesNumbers() { + Numbers numbers = Numbers.from("123"); + + assertThat(numbers.size()).isEqualTo(3); + assertThat(numbers.get(0)).isEqualTo(1); + assertThat(numbers.get(1)).isEqualTo(2); + assertThat(numbers.get(2)).isEqualTo(3); + assertThat(numbers.contains(2)).isTrue(); + assertThat(numbers.contains(9)).isFalse(); + } + + @Test + void fromWrongLengthThrowsException() { + assertThatThrownBy(() -> Numbers.from("12")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 3자여야 합니다."); + } + + @Test + void fromDuplicateDigitsThrowsException() { + assertThatThrownBy(() -> Numbers.from("112")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 중복된 숫자는 사용할 수 없습니다."); + } + + @Test + void fromNonDigitThrowsException() { + assertThatThrownBy(() -> Numbers.from("12a")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 숫자만 입력해야 합니다."); + } + + @Test + void fromContainsZeroThrowsException() { + assertThatThrownBy(() -> Numbers.from("120")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 0은 사용할 수 없습니다."); + } +} diff --git a/src/test/java/service/JudgeServiceTest.java b/src/test/java/service/JudgeServiceTest.java new file mode 100644 index 00000000..541d56cc --- /dev/null +++ b/src/test/java/service/JudgeServiceTest.java @@ -0,0 +1,35 @@ +package service; + +import domain.Numbers; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class JudgeServiceTest { + private final JudgeService judgeService = new JudgeService(); + + @Test + void judgeAllStrikes() { + Numbers answer = Numbers.from("123"); + Numbers guess = Numbers.from("123"); + + assertThat(judgeService.judge(answer, guess).isThreeStrikes()).isTrue(); + assertThat(judgeService.judge(answer, guess).toMessage()).isEqualTo("3스트라이크"); + } + + @Test + void judgeBallAndStrike() { + Numbers answer = Numbers.from("123"); + Numbers guess = Numbers.from("132"); + + assertThat(judgeService.judge(answer, guess).toMessage()).isEqualTo("2볼 1스트라이크"); + } + + @Test + void judgeNothing() { + Numbers answer = Numbers.from("123"); + Numbers guess = Numbers.from("456"); + + assertThat(judgeService.judge(answer, guess).toMessage()).isEqualTo("낫싱"); + } +} diff --git a/src/test/java/util/ValidatorTest.java b/src/test/java/util/ValidatorTest.java new file mode 100644 index 00000000..9b45a9af --- /dev/null +++ b/src/test/java/util/ValidatorTest.java @@ -0,0 +1,39 @@ +package util; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class ValidatorTest { + @Test + void guessErrorWrongLength() { + assertThat(Validator.guessError("12")).isEqualTo("[ERROR] 3자여야 합니다."); + } + + @Test + void guessErrorDuplicateDigits() { + assertThat(Validator.guessError("112")).isEqualTo("[ERROR] 중복된 숫자는 사용할 수 없습니다."); + } + + @Test + void guessErrorNonDigit() { + assertThat(Validator.guessError("12a")).isEqualTo("[ERROR] 숫자만 입력해야 합니다."); + } + + @Test + void numberErrorValidDigitReturnsNull() { + assertThat(Validator.numberError('7')).isNull(); + } + + @Test + void restartErrorValidInputsReturnNull() { + assertThat(Validator.restartError("1")).isNull(); + assertThat(Validator.restartError("2")).isNull(); + } + + @Test + void restartErrorInvalidInputReturnsMessage() { + assertThat(Validator.restartError("3")) + .isEqualTo("[ERROR] 재시작은 1, 종료는 2만 입력 가능합니다."); + } +} From bf29632305c66218cf02bd1e413dd9a83949d62b Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Mon, 2 Feb 2026 20:12:19 +0900 Subject: [PATCH 23/29] refactor(view): refactor input output view separate roles --- src/main/java/controller/GameController.java | 8 ++++++-- src/main/java/view/InputView.java | 9 +++++---- src/main/java/view/OutputView.java | 4 ++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index 8dae0260..48990b70 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -33,8 +33,12 @@ private void playGame() { private void playUntilWin() { while (!gameService.isGameOver()) { - JudgeCount count = gameService.play(inputView.readGuess()); - outputView.printResult(count); + try { + JudgeCount count = gameService.play(inputView.readGuess()); + outputView.printResult(count); + } catch (IllegalArgumentException e) { + outputView.printError(e.getMessage()); + } } outputView.printGameEndMessage(); } diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index f803d6cd..66fdf585 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -13,17 +13,18 @@ public class InputView { private static final Scanner scanner = new Scanner(System.in); public String readGuess() { - return readUntilValid(GUESS_PROMPT, System.out::print, Validator::guessError); + System.out.print(GUESS_PROMPT); + return scanner.nextLine().trim(); } public int readRestartCommand() { - String input = readUntilValid(RESTART_PROMPT, System.out::println, Validator::restartError); + String input = readUntilValid(System.out::println, Validator::restartError); return Integer.parseInt(input); } - private String readUntilValid(String prompt, Consumer printer, Function validator) { + private String readUntilValid(Consumer printer, Function validator) { while (true) { - printer.accept(prompt); + printer.accept(InputView.RESTART_PROMPT); String input = scanner.nextLine().trim(); String error = validator.apply(input); diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index dd038a6b..2767ba11 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -17,4 +17,8 @@ public void printResult(JudgeCount count) { public void printGameEndMessage() { System.out.println(END_MESSAGE); } + + public void printError(String message) { + System.out.println(message); + } } From 7b9b633f041f9459d64153c5a1a66421f70adf1f Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Mon, 2 Feb 2026 20:26:07 +0900 Subject: [PATCH 24/29] refactor: add game domain --- src/main/java/domain/Game.java | 24 ++++++++++++++++++++++++ src/main/java/service/GameService.java | 15 ++++++--------- 2 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 src/main/java/domain/Game.java diff --git a/src/main/java/domain/Game.java b/src/main/java/domain/Game.java new file mode 100644 index 00000000..d7b37fd6 --- /dev/null +++ b/src/main/java/domain/Game.java @@ -0,0 +1,24 @@ +package domain; + +import service.JudgeService; + +public class Game { + private final Numbers answer; + private final JudgeService judgeService; + private JudgeCount lastCount; + + public Game(Numbers answer, JudgeService judgeService) { + this.answer = answer; + this.judgeService = judgeService; + } + + public JudgeCount play(String guessInput) { + JudgeCount count = judgeService.judge(answer, Numbers.from(guessInput)); + lastCount = count; + return count; + } + + public boolean isOver() { + return lastCount != null && lastCount.isThreeStrikes(); + } +} diff --git a/src/main/java/service/GameService.java b/src/main/java/service/GameService.java index c20a98c6..285eab97 100644 --- a/src/main/java/service/GameService.java +++ b/src/main/java/service/GameService.java @@ -1,5 +1,6 @@ package service; +import domain.Game; import domain.JudgeCount; import domain.Numbers; @@ -7,8 +8,7 @@ public class GameService { private final RandomNumberGenerator randomNumberGenerator; private final JudgeService judgeService; - private Numbers answer; - private JudgeCount lastCount; + private Game currentGame; public GameService(RandomNumberGenerator randomNumberGenerator, JudgeService judgeService) { this.randomNumberGenerator = randomNumberGenerator; @@ -16,18 +16,15 @@ public GameService(RandomNumberGenerator randomNumberGenerator, JudgeService jud } public void startNewGame() { - answer = randomNumberGenerator.numberGenerate(); - lastCount = null; + Numbers answer = randomNumberGenerator.numberGenerate(); + currentGame = new Game(answer, judgeService); } public JudgeCount play(String guessInput) { - JudgeCount count = judgeService.judge(answer, Numbers.from(guessInput)); - lastCount = count; - return count; + return currentGame.play(guessInput); } public boolean isGameOver() { - if (lastCount == null) return false; - return lastCount.isThreeStrikes(); + return currentGame.isOver(); } } From 58f2b8de4d8fce2b427173ba9965425b6475ef1a Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Mon, 2 Feb 2026 20:44:39 +0900 Subject: [PATCH 25/29] =?UTF-8?q?refactor(domain):=20judgeservice=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20numbers=EC=97=90=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 4 +--- src/main/java/domain/Game.java | 8 ++----- src/main/java/domain/Numbers.java | 18 ++++++++++++++- src/main/java/service/GameService.java | 7 ++---- src/main/java/service/JudgeService.java | 22 ------------------- .../NumbersJudgeTest.java} | 15 +++++-------- 6 files changed, 28 insertions(+), 46 deletions(-) delete mode 100644 src/main/java/service/JudgeService.java rename src/test/java/{service/JudgeServiceTest.java => domain/NumbersJudgeTest.java} (50%) diff --git a/src/main/java/Application.java b/src/main/java/Application.java index ddc4ab1c..69560bc3 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,6 +1,5 @@ import controller.GameController; import service.GameService; -import service.JudgeService; import view.InputView; import view.OutputView; import service.RandomNumberGenerator; @@ -10,8 +9,7 @@ public static void main(String[] args) { InputView inputView = new InputView(); OutputView outputView = new OutputView(); GameService gameService = new GameService( - new RandomNumberGenerator(), - new JudgeService() + new RandomNumberGenerator() ); new GameController(inputView, outputView, gameService).run(); diff --git a/src/main/java/domain/Game.java b/src/main/java/domain/Game.java index d7b37fd6..219a5113 100644 --- a/src/main/java/domain/Game.java +++ b/src/main/java/domain/Game.java @@ -1,19 +1,15 @@ package domain; -import service.JudgeService; - public class Game { private final Numbers answer; - private final JudgeService judgeService; private JudgeCount lastCount; - public Game(Numbers answer, JudgeService judgeService) { + public Game(Numbers answer) { this.answer = answer; - this.judgeService = judgeService; } public JudgeCount play(String guessInput) { - JudgeCount count = judgeService.judge(answer, Numbers.from(guessInput)); + JudgeCount count = answer.judge(Numbers.from(guessInput)); lastCount = count; return count; } diff --git a/src/main/java/domain/Numbers.java b/src/main/java/domain/Numbers.java index 2b33fa34..af05168e 100644 --- a/src/main/java/domain/Numbers.java +++ b/src/main/java/domain/Numbers.java @@ -45,4 +45,20 @@ public boolean contains(int number) { public int size() { return numbers.size(); } -} \ No newline at end of file + + public JudgeCount judge(Numbers guess) { + int ball = 0; + int strike = 0; + for (int i = 0; i < size(); i++) { + int g = guess.get(i); + if (g == get(i)) { + strike++; + continue; + } + if (contains(g)) { + ball++; + } + } + return new JudgeCount(ball, strike); + } +} diff --git a/src/main/java/service/GameService.java b/src/main/java/service/GameService.java index 285eab97..38523283 100644 --- a/src/main/java/service/GameService.java +++ b/src/main/java/service/GameService.java @@ -6,18 +6,15 @@ public class GameService { private final RandomNumberGenerator randomNumberGenerator; - private final JudgeService judgeService; - private Game currentGame; - public GameService(RandomNumberGenerator randomNumberGenerator, JudgeService judgeService) { + public GameService(RandomNumberGenerator randomNumberGenerator) { this.randomNumberGenerator = randomNumberGenerator; - this.judgeService = judgeService; } public void startNewGame() { Numbers answer = randomNumberGenerator.numberGenerate(); - currentGame = new Game(answer, judgeService); + currentGame = new Game(answer); } public JudgeCount play(String guessInput) { diff --git a/src/main/java/service/JudgeService.java b/src/main/java/service/JudgeService.java deleted file mode 100644 index 563829c7..00000000 --- a/src/main/java/service/JudgeService.java +++ /dev/null @@ -1,22 +0,0 @@ -package service; - -import domain.JudgeCount; -import domain.Numbers; - -public class JudgeService { - public JudgeCount judge(Numbers answer, Numbers guess) { - int ball = 0; - int strike = 0; - for (int i = 0; i < answer.size(); i++) { - int g = guess.get(i); - if (g == answer.get(i)) { - strike++; - continue; - } - if (answer.contains(g)) { - ball++; - } - } - return new JudgeCount(ball, strike); - } -} diff --git a/src/test/java/service/JudgeServiceTest.java b/src/test/java/domain/NumbersJudgeTest.java similarity index 50% rename from src/test/java/service/JudgeServiceTest.java rename to src/test/java/domain/NumbersJudgeTest.java index 541d56cc..aebc92da 100644 --- a/src/test/java/service/JudgeServiceTest.java +++ b/src/test/java/domain/NumbersJudgeTest.java @@ -1,20 +1,17 @@ -package service; +package domain; -import domain.Numbers; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -class JudgeServiceTest { - private final JudgeService judgeService = new JudgeService(); - +class NumbersJudgeTest { @Test void judgeAllStrikes() { Numbers answer = Numbers.from("123"); Numbers guess = Numbers.from("123"); - assertThat(judgeService.judge(answer, guess).isThreeStrikes()).isTrue(); - assertThat(judgeService.judge(answer, guess).toMessage()).isEqualTo("3스트라이크"); + assertThat(answer.judge(guess).isThreeStrikes()).isTrue(); + assertThat(answer.judge(guess).toMessage()).isEqualTo("3스트라이크"); } @Test @@ -22,7 +19,7 @@ void judgeBallAndStrike() { Numbers answer = Numbers.from("123"); Numbers guess = Numbers.from("132"); - assertThat(judgeService.judge(answer, guess).toMessage()).isEqualTo("2볼 1스트라이크"); + assertThat(answer.judge(guess).toMessage()).isEqualTo("2볼 1스트라이크"); } @Test @@ -30,6 +27,6 @@ void judgeNothing() { Numbers answer = Numbers.from("123"); Numbers guess = Numbers.from("456"); - assertThat(judgeService.judge(answer, guess).toMessage()).isEqualTo("낫싱"); + assertThat(answer.judge(guess).toMessage()).isEqualTo("낫싱"); } } From 9a74fae3bcee30e96c30c1fed2d7a016083bcc1b Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Mon, 2 Feb 2026 21:03:56 +0900 Subject: [PATCH 26/29] =?UTF-8?q?refactor:=20result=20=ED=91=9C=EC=8B=9C?= =?UTF-8?q?=20outputview=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/JudgeCount.java | 27 +--------------------- src/main/java/view/OutputView.java | 17 +++++++++++++- src/test/java/domain/JudgeCountTest.java | 26 +++------------------ src/test/java/domain/NumbersJudgeTest.java | 9 +++++--- 4 files changed, 26 insertions(+), 53 deletions(-) diff --git a/src/main/java/domain/JudgeCount.java b/src/main/java/domain/JudgeCount.java index a619876f..bde4e81c 100644 --- a/src/main/java/domain/JudgeCount.java +++ b/src/main/java/domain/JudgeCount.java @@ -1,33 +1,8 @@ package domain; -public class JudgeCount { - private final int ball; - private final int strike; - - public JudgeCount(int ball, int strike) { - this.ball = ball; - this.strike = strike; - } +public record JudgeCount(int ball, int strike) { public boolean isThreeStrikes() { return strike == 3; } - - public String toMessage() { - if (ball == 0 && strike == 0) return "낫싱"; - StringBuilder sb = new StringBuilder(); - appendBall(sb); - appendStrike(sb); - return sb.toString().trim(); - } - - private void appendBall(StringBuilder sb) { - if (ball == 0) return; - sb.append(ball).append("볼 "); - } - - private void appendStrike(StringBuilder sb) { - if (strike == 0) return; - sb.append(strike).append("스트라이크"); - } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 2767ba11..f595ede5 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -11,7 +11,7 @@ public void printStartMessage() { } public void printResult(JudgeCount count) { - System.out.println(count.toMessage()); + System.out.println(formatResult(count)); } public void printGameEndMessage() { @@ -21,4 +21,19 @@ public void printGameEndMessage() { public void printError(String message) { System.out.println(message); } + + private String formatResult(JudgeCount count) { + int ball = count.ball(); + int strike = count.strike(); + if (ball == 0 && strike == 0) return "낫싱"; + + StringBuilder sb = new StringBuilder(); + if (ball > 0) { + sb.append(ball).append("볼 "); + } + if (strike > 0) { + sb.append(strike).append("스트라이크"); + } + return sb.toString().trim(); + } } diff --git a/src/test/java/domain/JudgeCountTest.java b/src/test/java/domain/JudgeCountTest.java index f791abdc..f7fa021e 100644 --- a/src/test/java/domain/JudgeCountTest.java +++ b/src/test/java/domain/JudgeCountTest.java @@ -6,31 +6,11 @@ class JudgeCountTest { @Test - void toMessageNothing() { - JudgeCount count = new JudgeCount(0, 0); - - assertThat(count.toMessage()).isEqualTo("낫싱"); - } - - @Test - void toMessageBallOnly() { - JudgeCount count = new JudgeCount(2, 0); - - assertThat(count.toMessage()).isEqualTo("2볼"); - } - - @Test - void toMessageStrikeOnly() { - JudgeCount count = new JudgeCount(0, 1); - - assertThat(count.toMessage()).isEqualTo("1스트라이크"); - } - - @Test - void toMessageBallAndStrike() { + void exposesBallAndStrike() { JudgeCount count = new JudgeCount(1, 2); - assertThat(count.toMessage()).isEqualTo("1볼 2스트라이크"); + assertThat(count.ball()).isEqualTo(1); + assertThat(count.strike()).isEqualTo(2); } @Test diff --git a/src/test/java/domain/NumbersJudgeTest.java b/src/test/java/domain/NumbersJudgeTest.java index aebc92da..c031fa9c 100644 --- a/src/test/java/domain/NumbersJudgeTest.java +++ b/src/test/java/domain/NumbersJudgeTest.java @@ -11,7 +11,8 @@ void judgeAllStrikes() { Numbers guess = Numbers.from("123"); assertThat(answer.judge(guess).isThreeStrikes()).isTrue(); - assertThat(answer.judge(guess).toMessage()).isEqualTo("3스트라이크"); + assertThat(answer.judge(guess).ball()).isEqualTo(0); + assertThat(answer.judge(guess).strike()).isEqualTo(3); } @Test @@ -19,7 +20,8 @@ void judgeBallAndStrike() { Numbers answer = Numbers.from("123"); Numbers guess = Numbers.from("132"); - assertThat(answer.judge(guess).toMessage()).isEqualTo("2볼 1스트라이크"); + assertThat(answer.judge(guess).ball()).isEqualTo(2); + assertThat(answer.judge(guess).strike()).isEqualTo(1); } @Test @@ -27,6 +29,7 @@ void judgeNothing() { Numbers answer = Numbers.from("123"); Numbers guess = Numbers.from("456"); - assertThat(answer.judge(guess).toMessage()).isEqualTo("낫싱"); + assertThat(answer.judge(guess).ball()).isEqualTo(0); + assertThat(answer.judge(guess).strike()).isEqualTo(0); } } From ecd367a344580b3a9889d5265fbc827fc6bca8d3 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Mon, 2 Feb 2026 21:13:31 +0900 Subject: [PATCH 27/29] =?UTF-8?q?refactor:=20restart=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=20gamecontroller=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/GameController.java | 10 ++++++++- src/main/java/util/Validator.java | 2 +- src/main/java/view/InputView.java | 23 +++----------------- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index 48990b70..7acaa50a 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -2,6 +2,7 @@ import domain.JudgeCount; import service.GameService; +import util.Validator; import view.InputView; import view.OutputView; @@ -44,6 +45,13 @@ private void playUntilWin() { } private boolean shouldStop() { - return inputView.readRestartCommand() == 2; + while (true) { + String input = inputView.readRestartCommand(); + String error = Validator.restartError(input); + if (error == null) { + return "2".equals(input); + } + outputView.printError(error); + } } } diff --git a/src/main/java/util/Validator.java b/src/main/java/util/Validator.java index fc5b5911..0b3de9af 100644 --- a/src/main/java/util/Validator.java +++ b/src/main/java/util/Validator.java @@ -45,7 +45,7 @@ public static String restartError(String input) { if (RESTART_CODE.equals(input) || EXIT_CODE.equals(input)) { return null; } - return errorMessage(String.format("재시작은 %s, 종료는 %s만 입력 가능합니다.", RESTART_CODE, EXIT_CODE)); + return errorMessage(String.format("%s(재시작), %s(종료)만 입력 가능합니다.", RESTART_CODE, EXIT_CODE)); } private static String errorMessage(String message) { diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 66fdf585..4e5ddbf7 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -1,10 +1,6 @@ package view; -import util.Validator; - import java.util.Scanner; -import java.util.function.Consumer; -import java.util.function.Function; public class InputView { private static final String GUESS_PROMPT = "숫자를 입력해주세요 : "; @@ -17,21 +13,8 @@ public String readGuess() { return scanner.nextLine().trim(); } - public int readRestartCommand() { - String input = readUntilValid(System.out::println, Validator::restartError); - return Integer.parseInt(input); - } - - private String readUntilValid(Consumer printer, Function validator) { - while (true) { - printer.accept(InputView.RESTART_PROMPT); - String input = scanner.nextLine().trim(); - String error = validator.apply(input); - - if (error == null) { - return input; - } - System.out.println(error); - } + public String readRestartCommand() { + System.out.println(RESTART_PROMPT); + return scanner.nextLine().trim(); } } From b18ee638c5faf195cbf2b3de73f60d26fd6326ae Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Mon, 2 Feb 2026 21:48:11 +0900 Subject: [PATCH 28/29] =?UTF-8?q?feat(game):=20=EC=8A=A4=ED=8A=B8=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=ED=81=AC=20=EB=B3=BC=20=EC=88=9C=EC=84=9C=20=EB=B0=94?= =?UTF-8?q?=EA=BE=B8=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/view/OutputView.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index f595ede5..0b3a3cae 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -28,11 +28,11 @@ private String formatResult(JudgeCount count) { if (ball == 0 && strike == 0) return "낫싱"; StringBuilder sb = new StringBuilder(); - if (ball > 0) { - sb.append(ball).append("볼 "); - } if (strike > 0) { - sb.append(strike).append("스트라이크"); + sb.append(strike).append("스트라이크 "); + } + if (ball > 0) { + sb.append(ball).append("볼"); } return sb.toString().trim(); } From 98fbea3d538e06b4b6737983f0892bff79631151 Mon Sep 17 00:00:00 2001 From: "frank.jeong" Date: Mon, 2 Feb 2026 22:11:12 +0900 Subject: [PATCH 29/29] =?UTF-8?q?refactor:=20java=20=EC=BB=A8=EB=B2=A4?= =?UTF-8?q?=EC=85=98=20=EB=B3=80=EC=88=98=20=EC=9D=B4=EB=A6=84=20=ED=95=9C?= =?UTF-8?q?=EA=B8=80=EC=9E=90=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/GameController.java | 4 ++-- src/main/java/domain/Number.java | 6 +++--- src/main/java/domain/Numbers.java | 20 +++++++++---------- .../java/service/RandomNumberGenerator.java | 16 +++++++-------- src/main/java/util/Validator.java | 12 +++++------ 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index 7acaa50a..2ba53453 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -37,8 +37,8 @@ private void playUntilWin() { try { JudgeCount count = gameService.play(inputView.readGuess()); outputView.printResult(count); - } catch (IllegalArgumentException e) { - outputView.printError(e.getMessage()); + } catch (IllegalArgumentException exception) { + outputView.printError(exception.getMessage()); } } outputView.printGameEndMessage(); diff --git a/src/main/java/domain/Number.java b/src/main/java/domain/Number.java index b8c80a1b..1674b1f2 100644 --- a/src/main/java/domain/Number.java +++ b/src/main/java/domain/Number.java @@ -9,10 +9,10 @@ private Number(int value) { this.value = value; } - public static Number fromChar(char c) { - String error = Validator.numberError(c); + public static Number fromChar(char digitChar) { + String error = Validator.numberError(digitChar); if (error != null) throw new IllegalArgumentException(error); - return new Number(c - '0'); + return new Number(digitChar - '0'); } public int value() { diff --git a/src/main/java/domain/Numbers.java b/src/main/java/domain/Numbers.java index af05168e..35be4abf 100644 --- a/src/main/java/domain/Numbers.java +++ b/src/main/java/domain/Numbers.java @@ -22,11 +22,11 @@ public static Numbers from(String input) { } private static List parse(String input) { - List nums = new ArrayList<>(LENGTH); - for (char c : input.toCharArray()) { - nums.add(Number.fromChar(c)); + List parsedNumbers = new ArrayList<>(LENGTH); + for (char digitChar : input.toCharArray()) { + parsedNumbers.add(Number.fromChar(digitChar)); } - return nums; + return parsedNumbers; } public int get(int index) { @@ -34,8 +34,8 @@ public int get(int index) { } public boolean contains(int number) { - for (Number n : numbers) { - if (n.same(number)) { + for (Number numberItem : numbers) { + if (numberItem.same(number)) { return true; } } @@ -49,13 +49,13 @@ public int size() { public JudgeCount judge(Numbers guess) { int ball = 0; int strike = 0; - for (int i = 0; i < size(); i++) { - int g = guess.get(i); - if (g == get(i)) { + for (int index = 0; index < size(); index++) { + int guessDigit = guess.get(index); + if (guessDigit == get(index)) { strike++; continue; } - if (contains(g)) { + if (contains(guessDigit)) { ball++; } } diff --git a/src/main/java/service/RandomNumberGenerator.java b/src/main/java/service/RandomNumberGenerator.java index 0838d9f2..638b47b4 100644 --- a/src/main/java/service/RandomNumberGenerator.java +++ b/src/main/java/service/RandomNumberGenerator.java @@ -11,16 +11,16 @@ public Numbers numberGenerate() { return Numbers.from("" + numbers[0] + numbers[1] + numbers[2]); } - private void shuffle(int[] arr) { - for (int i = arr.length - 1; i > 0; i--) { - int j = ThreadLocalRandom.current().nextInt(i + 1); - swap(arr, i, j); + private void shuffle(int[] numbers) { + for (int currentIndex = numbers.length - 1; currentIndex > 0; currentIndex--) { + int swapIndex = ThreadLocalRandom.current().nextInt(currentIndex + 1); + swap(numbers, currentIndex, swapIndex); } } - private void swap(int[] arr, int i, int j) { - int tmp = arr[i]; - arr[i] = arr[j]; - arr[j] = tmp; + private void swap(int[] numbers, int firstIndex, int secondIndex) { + int temp = numbers[firstIndex]; + numbers[firstIndex] = numbers[secondIndex]; + numbers[secondIndex] = temp; } } diff --git a/src/main/java/util/Validator.java b/src/main/java/util/Validator.java index 0b3de9af..ea7314d9 100644 --- a/src/main/java/util/Validator.java +++ b/src/main/java/util/Validator.java @@ -19,23 +19,23 @@ public static String guessError(String input) { private static String validateCharacters(String input) { Set uniqueChars = new HashSet<>(); - for (char c : input.toCharArray()) { - String error = numberError(c); + for (char digitChar : input.toCharArray()) { + String error = numberError(digitChar); if (error != null) { return error; } - if (!uniqueChars.add(c)) { + if (!uniqueChars.add(digitChar)) { return errorMessage("중복된 숫자는 사용할 수 없습니다."); } } return null; } - public static String numberError(char c) { - if (!Character.isDigit(c)) { + public static String numberError(char digitChar) { + if (!Character.isDigit(digitChar)) { return errorMessage("숫자만 입력해야 합니다."); } - if (c == '0') { + if (digitChar == '0') { return errorMessage("0은 사용할 수 없습니다."); } return null;