Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6839d1c
docs: 기능 목록 작성
mirageoasis Jan 30, 2026
53fa97b
chore: 프로그램 초기화
mirageoasis Jan 30, 2026
a5cf7e6
다이어 그램 추가
mirageoasis Jan 31, 2026
31ce830
feat: 기능 추가
mirageoasis Jan 31, 2026
f4e577d
refactor: 객체 이름 수정
mirageoasis Jan 31, 2026
98577e1
test: GameResultModel 테스트 추가 완료
mirageoasis Jan 31, 2026
32f8c8d
test: HintCalculatorModel 테스트 추가 완료
mirageoasis Jan 31, 2026
380f066
test: HintResultModelTest 테스트 추가 완료
mirageoasis Jan 31, 2026
2710d2b
test: PlayerInputValidator테스트 완료
mirageoasis Jan 31, 2026
f05fda0
fix: 스트라이크가 볼 앞에 오도록 수정
mirageoasis Jan 31, 2026
8323177
test: SecretNumberModelTest 추가
mirageoasis Jan 31, 2026
d33a5b3
refactor: InputValidator에서 입력 제거
mirageoasis Jan 31, 2026
69665ff
docs: 변경 내용 다이어그램에 반영
mirageoasis Jan 31, 2026
cc600df
fix: 정답 스포일러 코드 제거
mirageoasis Jan 31, 2026
760bcc4
refactor: 입력 로직 분리
mirageoasis Jan 31, 2026
dd7f658
refactor: 출력 중앙화
mirageoasis Jan 31, 2026
92ca8e7
refactor: 클래스 이름 변경
mirageoasis Jan 31, 2026
a9ef6b8
refactor: 클래스 이름 변경
mirageoasis Jan 31, 2026
cb65de4
refactor: 테스트도 이름 변경
mirageoasis Jan 31, 2026
e0e66e6
test: InputTest 추가
mirageoasis Jan 31, 2026
d935656
refactor: Input에서 Scanner 의존성 주입 받기
mirageoasis Jan 31, 2026
3629a71
docs: 문서 수정
mirageoasis Jan 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 96 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,96 @@
# java-baseball-precourse
# 숫자 야구 게임 기능 구현 목록

## 게임 초기화 (컴퓨터)

* 1에서 9까지의 서로 다른 임의의 수 3개를 생성한다.

## 사용자 입력

* 플레이어로부터 서로 다른 3자리의 수를 입력받는다.
* 게임 종료 후 재시작(1) 또는 종료(2) 여부를 입력받는다.

## 입력 유효성 검사

* 사용자가 잘못된 값을 입력할 경우 [ERROR]로 시작하는 에러 메시지를 출력한다.
* 에러 메시지 출력 후 게임을 종료하지 않고 다시 입력을 받는다.

## 게임 결과 판정 (볼/스트라이크 계산)

* 컴퓨터의 수와 플레이어의 수를 비교하여 결과를 계산한다.
* 같은 수가 같은 자리에 있으면 스트라이크.
* 같은 수가 다른 자리에 있으면 볼.
* 같은 수가 전혀 없으면 낫싱.

## 결과 출력

* 계산된 볼, 스트라이크 개수를 출력한다 (예: 1볼 1스트라이크, 낫싱).
* 3개의 숫자를 모두 맞히면(3스트라이크) 게임 종료 문구를 출력한다.

```mermaid
classDiagram
class Application {
<<Entrypoint>>
+main(args: String[]): void
}

class GameFlowController {
<<Controller>>
-secretModel: SecretNumberModel
-playerInputValidator: PlayerInputValidator
-hintModel: HintCalculatorModel
-gameOutput: GameOutput
-gameInput: GameInput
+start(): void
-askRestart(): boolean
}

class GameInput {
<<IO>>
-scanner: java.util.Scanner
+readLine(): String
}

class PlayerInputValidator {
<<Validator>>
+isValidInput(input: String): boolean
+toDigits(input: String): java.util.List<Integer>
}

class SecretNumberModel {
<<Model>>
-secret: java.util.List<Integer>
+generateSecret(): void
+getSecret(): java.util.List<Integer>
}

class HintCalculatorModel {
<<Model>>
+calculate(secret: java.util.List<Integer>, guess: java.util.List<Integer>): HintResultModel
}

class HintResultModel {
<<ValueObject>>
-strikes: int
-balls: int
+getStrikes(): int
+toString(): String
}

class GameOutput {
<<View>>
+showPromptForGuess(): void
+showHint(result: HintResultModel): void
+showWinAndPromptRestart(): void
+showError(message: String): void
+showExitMessage(): void
}

Application --> GameFlowController: starts
GameFlowController --> SecretNumberModel: "정답 생성 요청"
GameFlowController --> PlayerInputValidator: "입력 검증/숫자 변환 요청"
GameFlowController --> HintCalculatorModel: "판정 로직 실행"
GameFlowController --> GameOutput: "결과/메시지 출력 요청"
GameFlowController --> GameInput: "입력(readLine) 요청"
HintCalculatorModel ..> HintResultModel: returns
GameOutput ..> HintResultModel: displays
```
Empty file removed src/main/java/.gitkeep
Empty file.
7 changes: 7 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import baseball.GameFlowController;

public class Application {
public static void main(String[] args) {
new GameFlowController().start();
}
}
54 changes: 54 additions & 0 deletions src/main/java/baseball/GameFlowController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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(new Scanner(System.in));

private boolean askRestart() {
while (true) {
String choice = gameInput.readLine();
if ("1".equals(choice))
return true;
if ("2".equals(choice))
return false;
gameOutput.showError("1 또는 2만 입력 가능합니다.");
}
}

public void start() {
boolean running = true;
while (running) {
secretModel.generateSecret();
List<Integer> secret = secretModel.getSecret();

while (true) {
gameOutput.showPromptForGuess();
String line = gameInput.readLine();
if (!playerInputValidator.isValidInput(line)) {
gameOutput.showError("입력은 서로 다른 3개의 숫자(1-9)여야 합니다.");
continue;
}
List<Integer> guess = playerInputValidator.toDigits(line);
HintResultModel hint = hintModel.calculate(secret, guess);
gameOutput.showHint(hint);
if (hint.getStrikes() == 3) {
gameOutput.showWinAndPromptRestart();
boolean restart = askRestart();
if (restart) {
break;
} else {
running = false;
break;
}
}
}
}
gameOutput.showExitMessage();
}
}
16 changes: 16 additions & 0 deletions src/main/java/baseball/GameInput.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package baseball;

import java.util.Scanner;

public class GameInput {

private final Scanner scanner;

public GameInput(Scanner scanner) {
this.scanner = scanner;
}

public String readLine() {
return scanner.nextLine().strip();
}
}
25 changes: 25 additions & 0 deletions src/main/java/baseball/GameOutput.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package baseball;

public class GameOutput {

public void showHint(HintResultModel 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를 입력하세요.");
}

public void showPromptForGuess() {
System.out.println("서로 다른 3자리 숫자를 입력하세요 (각 자리 1-9):");
}

public void showExitMessage() {
System.out.println("게임을 종료합니다.");
}
}
18 changes: 18 additions & 0 deletions src/main/java/baseball/HintCalculatorModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package baseball;

import java.util.List;

public class HintCalculatorModel {
public HintResultModel calculate(List<Integer> secret, List<Integer> 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);
}
}
32 changes: 32 additions & 0 deletions src/main/java/baseball/HintResultModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package baseball;

public class HintResultModel {
private final int strikes;
private final int balls;

public HintResultModel(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 (strikes > 0) {
sb.append(strikes).append("스트라이크");
}
if (balls > 0) {
if (strikes > 0) {
sb.append(" ");
}
sb.append(balls).append("볼");
}
return sb.toString();
}
}
34 changes: 34 additions & 0 deletions src/main/java/baseball/PlayerInputValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package baseball;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class PlayerInputValidator {
public boolean isValidInput(String s) {
if (s == null)
return false;
if (s.length() != 3)
return false;
HashSet<Character> 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<Integer> toDigits(String s) {
List<Integer> list = new ArrayList<>();
for (char c : s.toCharArray())
list.add(c - '0');
return list;
}
}
21 changes: 21 additions & 0 deletions src/main/java/baseball/SecretNumberModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package baseball;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SecretNumberModel {
private List<Integer> secret = new ArrayList<>();

public void generateSecret() {
List<Integer> 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<Integer> getSecret() {
return new ArrayList<>(secret);
}
}
Empty file removed src/test/java/.gitkeep
Empty file.
29 changes: 29 additions & 0 deletions src/test/java/baseball/GameInputTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package baseball;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.Scanner;

import org.junit.jupiter.api.Test;

class GameInputTest {

// 트림 기능 테스트
@Test
void readLine_trims_leading_and_trailing_spaces() {
Scanner scanner = new Scanner(" 123 ");
GameInput input = new GameInput(scanner);

String result = input.readLine();
assertEquals("123", result);
}

// 일반 입력 테스트
@Test
void readLine_returns_input_as_is() {
Scanner scanner = new Scanner("456");
GameInput input = new GameInput(scanner);
String result = input.readLine();
assertEquals("456", result);
}
}
Loading