Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c2a7f38
docs(readme): 구현 기능 목록 작성
TaegeunYou Jan 28, 2026
59032d4
feat(InputView): 사용자 숫자 입력 기능 구현
TaegeunYou Jan 28, 2026
9ec5388
feat(InputView): 사용자 입력 검증 기능 구현
TaegeunYou Jan 28, 2026
ec0ec05
feat(OuputView): 에러를 반환한 경우에 에러 메시지 출력 및 다시 입력받도록 구현
TaegeunYou Jan 28, 2026
6776d0b
feat(ComputerNumbers): 컴퓨터의 숫자를 생성하는 기능 구현
TaegeunYou Jan 28, 2026
9c2bd7b
feat(Result): 결과를 판단하는 기능 구현
TaegeunYou Jan 28, 2026
e6ec55f
feat(OutputView): 스트라이크 및 볼 횟수를 출력하는 기능 구현
TaegeunYou Jan 28, 2026
4b09589
feat(BaseballController): 게임 종료 기능 구현
TaegeunYou Jan 28, 2026
52ca44a
refac(InputMessage): 숫자 입력 안내 메시지 수정
TaegeunYou Jan 28, 2026
a5e57b1
refac(InputView): 입력된 숫자 저장안되는 오류 해결
TaegeunYou Jan 28, 2026
d13b355
refac(InputView): 게임 종료시 입력받는 숫자 로직 에러 수정
TaegeunYou Jan 28, 2026
1c82674
refac(Numbers): 사용하지 않는 메소드 삭제
TaegeunYou Jan 28, 2026
7855fb5
refac(InputView): 메소드 위치 변경
TaegeunYou Jan 28, 2026
d0dcf1d
refac(OutputView): 기존 메소드 사용하도록 수정
TaegeunYou Jan 28, 2026
77ce253
refac(OutputView): 스트라이크 볼 출력 로직 수정
TaegeunYou Jan 28, 2026
57d0c38
refac(InputView): 메소드 분리
TaegeunYou Jan 28, 2026
f680c1f
refac(OutputView): 출력 로직 수정
TaegeunYou Jan 28, 2026
4452c1b
refac(OutputView): 출력 수정
TaegeunYou Feb 2, 2026
25e0a8a
test(NumberTest): 도메인 테스트 코드 생성
TaegeunYou Feb 6, 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
50 changes: 50 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# 💪 프로젝트 개요

숫자 야구 게임

# 게임 설명

- 기본적으로 1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 맞추는 게임이다.
- 같은 수가 같은 자리에 있으면 스트라이크, 다른 자리에 있으면 볼, 같은 수가 전혀 없으면 포볼 또는 낫싱이란 힌트를
얻고, 그 힌트를 이용해서 먼저 상대방(컴퓨터)의 수를 맞추면 승리한다.
- [예] 상대방(컴퓨터)의 수가 425일 때, 123을 제시한 경우 : 1스트라이크, 456을 제시한 경우 : 1스트라이크 1볼,
789를 제시한 경우 : 낫싱
- 위 숫자 야구게임에서 상대방의 역할을 컴퓨터가 한다. 컴퓨터는 1에서 9까지 서로 다른 임의의 수 3개를 선택한다. 게
임 플레이어는 컴퓨터가 생각하고 있는 3개의 숫자를 입력하고, 컴퓨터는 입력한 숫자에 대한 결과를 출력한다.
- 이 같은 과정을 반복해 컴퓨터가 선택한 3개의 숫자를 모두 맞히면 게임이 종료된다.
- 게임을 종료한 후 게임을 다시 시작하거나 완전히 종료할 수 있다.
- 사용자가 잘못된 값을 입력할 경우 [ERROR]로 시작하는 에러 메시지를 출력하고 게임을 계속 진행할 수 있어야 한다.

# 📝 구현 기능 목록

### 게임 준비 기능
- [x] 컴퓨터는 1에서 9까지 서로 다른 임의의 수 3개를 선택하고 저장한다.

### 사용자 입력 기능
- [x] `숫자를 입력해주세요 :`를 출력한다.
- [x] 사용자에게 입력을 받는다.
- [x] 입력값을 검증한다.
- [x] 빈 문자열이나 공백이면 에러를 반환한다.
- [x] 입력된 문자열의 길이가 3이 아니면 에러를 반환한다.
- [x] 3개 중에 하나라도 숫자가 아니면 에러를 반환한다.
- [x] 숫자에 0이 포함되어 있으면 에러를 반환한다.
- [x] 서로 다른 숫자가 아니면 에러를 반환한다.
- [x] 에러를 반환한 경우에는 에러 메시지를 출력한 후에 입력을 다시 받는다.

### 게임 기능
- [x] 결과를 판단한다.
- [x] 스트라이크와 볼 갯수를 센다.
- [x] 같은 수가 같은 자리에 있으면 스트라이크를 추가한다.
- [x] 같은 수가 다른 자리에 있으면 볼을 추가한다.
- [x] 스트라이크와 볼이 없으면 낫싱으로 판단한다.
- [x] 결과를 출력한다.
- [x] 정답이 아니면 `숫자를 입력해주세요 :`를 출력하고 사용자에게 다시 입력을 받는다.

### 게임 종료 기능
- [x] `3개의 숫자를 모두 맞히셨습니다! 게임 끝`를 출력하고 종료한다.
- [x] `게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.`를 출력한다.
- [x] 사용자에게 1 또는 2를 입력받는다.
- [x] 다른 값인 경우에는 에러를 반환한다.
- [x] 에러를 반환한 경우에는 에러 메시지를 출력한 후에 입력을 다시 받는다.
- [x] 1을 입력받은 경우 게임 준비 기능부터 게임을 새로 시작한다.
- [x] 2를 입력받은 경우 게임을 종료한다.
Empty file removed src/main/java/.gitkeep
Empty file.
15 changes: 15 additions & 0 deletions src/main/java/baseball/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package baseball;

import baseball.controller.BaseballController;
import baseball.view.InputView;
import baseball.view.OutputView;

public class Application {
public static void main(String[] args) {
BaseballController baseballController = new BaseballController(
new InputView(),
new OutputView()
);
baseballController.run();
}
}
50 changes: 50 additions & 0 deletions src/main/java/baseball/controller/BaseballController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package baseball.controller;

import baseball.domain.ComputerNumbers;
import baseball.domain.Numbers;
import baseball.domain.Result;
import baseball.domain.ResultType;
import baseball.global.exception.CustomException;
import baseball.global.message.InputMessage;
import baseball.view.InputView;
import baseball.view.OutputView;

public class BaseballController {
private final InputView inputView;
private final OutputView outputView;

public BaseballController(InputView inputView, OutputView outputView) {
this.inputView = inputView;
this.outputView = outputView;
}

public void run() {
ComputerNumbers computerNumbers = new ComputerNumbers();
process(computerNumbers);
}

public void process(ComputerNumbers computerNumbers) {
Numbers inputNumbers = requestNumbers();
Result result = new Result(computerNumbers, inputNumbers);
outputView.printlnStrikeAndBall(result);
if (result.getResultType() != ResultType.THREE_STRIKE) {
process(computerNumbers);
return;
}
outputView.printlnThreeStrike();
if (inputView.enterFinishType() == 1) {
run();
}
}

private Numbers requestNumbers() {
outputView.printMessage(InputMessage.INPUT_NUMBERS.getMessage());
try {
Numbers numbers = new Numbers(inputView.enterNumbers());
return numbers;
} catch (CustomException e) {
outputView.printlnMessage(e.getMessage());
return requestNumbers();
}
}
}
32 changes: 32 additions & 0 deletions src/main/java/baseball/domain/ComputerNumbers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package baseball.domain;

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

public class ComputerNumbers {
private final Numbers numbers;

public ComputerNumbers() {
int[] randomNumbers = generateRandomNumbers();
this.numbers = new Numbers(randomNumbers);
}

private int[] generateRandomNumbers() {
List<Integer> allNumbers = new ArrayList<>();
for (int i = 1; i <= 9; i++) {
allNumbers.add(i);
}
Collections.shuffle(allNumbers);
int[] result = {allNumbers.get(0), allNumbers.get(1), allNumbers.get(2)};
return result;
}

public Number get(int idx) {
return numbers.get(idx);
}

public boolean notContain(Number number) {
return numbers.notContain(number);
}
}
13 changes: 13 additions & 0 deletions src/main/java/baseball/domain/Number.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package baseball.domain;

public class Number {
private final int num;

public Number(int num) {
this.num = num;
}

public boolean isSameValue(Number number) {
return this.num == number.num;
}
}
32 changes: 32 additions & 0 deletions src/main/java/baseball/domain/Numbers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package baseball.domain;

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

public class Numbers {
private final List<Number> numbers;

public Numbers(int[] arr) {
numbers = new ArrayList<>();
for (int num : arr) {
numbers.add(new Number(num));
}
}

public Number get(int idx) {
return numbers.get(idx);
}

public boolean notContain(Number inputNumber) {
for (Number number: numbers) {
if (number.isSameValue(inputNumber)) {
return false;
}
}
return true;
}

public int size() {
return numbers.size();
}
}
39 changes: 39 additions & 0 deletions src/main/java/baseball/domain/Result.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package baseball.domain;

public class Result {

private final ResultType resultType;
private final int strike;
private final int ball;

public Result(ComputerNumbers computerNumbers, Numbers inputNumbers) {
int strike = 0;
int ball = 0;
for (int idx = 0; idx < inputNumbers.size(); idx++) {
Number number = inputNumbers.get(idx);
if (computerNumbers.notContain(number)) {
continue;
}
if (number.isSameValue(computerNumbers.get(idx))) {
strike++;
continue;
}
ball++;
}
this.strike = strike;
this.ball = ball;
this.resultType = ResultType.getResult(strike, ball);
}

public ResultType getResultType() {
return resultType;
}

public int getStrike() {
return strike;
}

public int getBall() {
return ball;
}
}
18 changes: 18 additions & 0 deletions src/main/java/baseball/domain/ResultType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package baseball.domain;

public enum ResultType {
THREE_STRIKE,
STRIKE_AND_BALL,
NOTHING
;

public static ResultType getResult(int strike, int ball) {
if (strike == 3) {
return THREE_STRIKE;
}
if (strike != 0 || ball != 0) {
return STRIKE_AND_BALL;
}
return NOTHING;
}
}
13 changes: 13 additions & 0 deletions src/main/java/baseball/global/exception/CustomException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package baseball.global.exception;

import baseball.global.message.ErrorMessage;

public class CustomException extends IllegalArgumentException {
private CustomException(ErrorMessage errorMessage) {
super(errorMessage.getMessage());
}

public static CustomException of(ErrorMessage errorMessage) {
return new CustomException(errorMessage);
}
}
22 changes: 22 additions & 0 deletions src/main/java/baseball/global/message/ErrorMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package baseball.global.message;

public enum ErrorMessage {
BLANK_INPUT("빈 문자열이 입력되었습니다."),
INVALID_INPUT_LENGTH("3개를 입력해주세요"),
NON_DIGIT_CHARACTER_FOUND("모두 숫자로 입력해주세요"),
NON_POSITIVE_NUMBER_FOUND("모두 0보다 큰 양수로 입력해주세요."),
DUPLICATE_NUMBER_FOUND("서로 다른 숫자로 입력해주세요."),
INVALID_FINISH_INPUT("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.")
;

private final static String PREFIX = "[ERROR]";
private final String message;

ErrorMessage(String message) {
this.message = message;
}

public String getMessage() {
return PREFIX + " " + this.message;
}
}
17 changes: 17 additions & 0 deletions src/main/java/baseball/global/message/InputMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package baseball.global.message;

public enum InputMessage {

INPUT_NUMBERS("숫자를 입력해주세요 : ")
;

private String message;

InputMessage(String message) {
this.message = message;
}

public String getMessage() {
return message;
}
}
85 changes: 85 additions & 0 deletions src/main/java/baseball/view/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package baseball.view;

import baseball.global.exception.CustomException;
import baseball.global.message.ErrorMessage;

import java.util.Scanner;

public class InputView {

public int[] enterNumbers() {
String input = readLine();
validateStringFormat(input);
int[] nums = validateNumberFormat(input);
return nums;
}

public int enterFinishType() {
String input = readLine();
int num = parseFinishType(input);
return num;
}

private int parseFinishType(String input) {
if (input.length() != 1 || !Character.isDigit(input.charAt(0))) {
throw CustomException.of(ErrorMessage.INVALID_FINISH_INPUT);
}
int num = input.charAt(0) - '0';
if (num != 1 && num != 2) {
throw CustomException.of(ErrorMessage.INVALID_FINISH_INPUT);
}
return num;
}

private void validateStringFormat(String str) {
if (isEmptyOrBlank(str)) {
throw CustomException.of(ErrorMessage.BLANK_INPUT);
}
if (str.length() != 3) {
throw CustomException.of(ErrorMessage.INVALID_INPUT_LENGTH);
}
}

private int[] validateNumberFormat(String str) {
int[] nums = new int[3];
for (int i = 0; i < 3; i++) {
validateDigit(str.charAt(i));
int num = str.charAt(i) - '0';
validatePositiveNumber(num);
nums[i] = num;
}
validateDuplicate(nums);
return nums;
}

private void validateDigit(char c) {
if (!Character.isDigit(c)) {
throw CustomException.of(ErrorMessage.NON_DIGIT_CHARACTER_FOUND);
}
}

private void validatePositiveNumber(int num) {
if (num <= 0) {
throw CustomException.of(ErrorMessage.NON_POSITIVE_NUMBER_FOUND);
}
}

private void validateDuplicate(int[] arr) {
if (hasDuplicate(arr)) {
throw CustomException.of(ErrorMessage.DUPLICATE_NUMBER_FOUND);
}
}

private boolean isEmptyOrBlank(String str) {
return str == null || str.isBlank();
}

private static boolean hasDuplicate(int[] arr) {
return arr[0] == arr[1] || arr[0] == arr[2] || arr[1] == arr[2];
}

private String readLine() {
Scanner scanner = new Scanner(System.in);
return scanner.nextLine();
}
}
Loading