Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6f36da9
docs: readme.md 작성
un-known0 Jan 28, 2026
0df65f8
feat: 기능 목록 클래스,함수 선언(미구현)
un-known0 Jan 28, 2026
bc72b7b
feat: CustomException 추가 밑 OutputView.printError(e)함수 매개변수 형태 수정
un-known0 Jan 28, 2026
8d15860
feat: RandomNumberGenerator에 생성자 추가
un-known0 Jan 28, 2026
8b43e46
feat: RandomNumberGenerator.pickNumber 구현
un-known0 Jan 28, 2026
461518d
feat: RandomNumberGenerator.pickUniqueNumbers 구현
un-known0 Jan 28, 2026
0820cd9
feat: BaseballGameJudge.validateLength 구현
un-known0 Jan 28, 2026
5bb501e
feat: BaseballGameJudge.countBall 구현
un-known0 Jan 28, 2026
f9ec4fd
feat: BaseballGameJudge.countStrike 구현
un-known0 Jan 28, 2026
d0a99e6
feat: BaseballGameJudge.judge 구현
un-known0 Jan 28, 2026
4811777
feat: BaseballGameJudge.judge에서 리스트 검증함수 호출
un-known0 Jan 28, 2026
fc8e002
feat: BaseballGameJudge 판정 로직 구현
un-known0 Jan 28, 2026
0919859
docs: isGameOver함수 위치 이동을 반영하여 readme 수정
un-known0 Jan 28, 2026
b0dba37
feat: InputValidator.parseToNumbers 구현
un-known0 Jan 28, 2026
24c51e8
feat: InputValidator.validateNoDuplicates 구현
un-known0 Jan 28, 2026
28d4ba3
feat: InputValidator.validateRestartInput 구현
un-known0 Jan 28, 2026
2deac30
feat: InputView에 생성자 추가
un-known0 Jan 28, 2026
eb11c5b
feat: InputView.readNumber 구현 및 input 함수 추가
un-known0 Jan 28, 2026
50e278d
feat: InputView.readRestartOption 구현
un-known0 Jan 28, 2026
be5eff8
feat: OutputView 출력 로직 구현
un-known0 Jan 28, 2026
4144f32
feat: GameController에 생성자 추가
un-known0 Jan 28, 2026
0558903
refactor: GameResult goal 제거 및 관련 View 수정
un-known0 Jan 29, 2026
75fbfcc
feat: GameController 게임 로직 구현
un-known0 Jan 29, 2026
d4704f7
feat: 게임 실행 호출 클래스 BaseballGame 추가
un-known0 Jan 29, 2026
6146e0b
chore: InputValidator가 문자열 내 앞뒤 공백을 무시할 수 있도록 수정
un-known0 Jan 29, 2026
8032de0
chore: InputView.input 함수에 flush를 추가하여 성능 개선
un-known0 Feb 1, 2026
76d6de2
fix: InputValidator.parseToNumbers가 trim 이후 empty 여부를 판단하도록 수정
un-known0 Feb 1, 2026
b5d958b
refector: validateLength를 InputValidator로 이동, validateRange 추가
un-known0 Feb 1, 2026
206db3e
fix: RandomNumberGenerator.pickUniqueNumbers에 잘못된 오류 설명 수정
un-known0 Feb 1, 2026
aee1cb5
docs: 구현 변경에 따른 readme 업데이트
un-known0 Feb 5, 2026
9aa606d
test: model 구현에 대한 테스트케이스 추가
un-known0 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
130 changes: 129 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,129 @@
# java-baseball-precourse
# java-baseball-precourse

## 목표

1부터 9까지 서로 다른 수로 이루어진 3자리 숫자를 맞추는 숫자 야구 게임을 구현한다.

### 게임 규칙
- 컴퓨터가 1~9 중 서로 다른 임의의 수 3개를 선택한다.
- 플레이어는 3자리 숫자를 입력하고, 컴퓨터는 힌트를 제공한다.
- **스트라이크**: 같은 수가 같은 자리에 있는 경우
- **볼**: 같은 수가 다른 자리에 있는 경우
- **낫싱**: 같은 수가 전혀 없는 경우
- 3스트라이크 시 게임이 종료되며, 재시작 또는 완전 종료를 선택할 수 있다.
- 잘못된 입력 시 `[ERROR]`로 시작하는 메시지를 출력하고 게임을 계속 진행한다.

---

## 기능 목록

### Model

#### RandomNumberGenerator (랜덤 숫자 생성)
1. `pickNumber` - 숫자 랜덤으로 하나 뽑는 함수
- 매개변수: 시작숫자, 종료숫자
- 반환값: 숫자
- 시작숫자가 종료숫자보다 큰 경우 오류

2. `pickUniqueNumbers` - 숫자를 중복 없이 N개 뽑는 함수
- 매개변수: 시작숫자, 종료숫자, 개수
- 반환값: 숫자 리스트
- 범위보다 요구 개수가 많거나 개수가 0 미만이면 오류

#### GameResult (게임 결과 객체)
- 필드: ball(볼 개수), strike(스트라이크 개수)

1. `isGameOver` - 게임 종료 여부 판단 함수
- 매개변수: 목표 개수
- 반환값: boolean

#### BaseballGameJudge (게임 판정)
1. `countBall` - 볼 검사 함수
- 매개변수: 숫자 리스트, 숫자 리스트
- 반환값: 숫자
- 두 리스트에서 위치는 다르지만 같은 숫자가 몇 개인지 검사

2. `countStrike` - 스트라이크 검사 함수
- 매개변수: 숫자 리스트, 숫자 리스트
- 반환값: 숫자
- 두 리스트에서 동일한 위치에 동일한 숫자가 몇 개인지 검사

3. `judge` - 야구게임 결과 함수
- 매개변수: 숫자 리스트, 숫자 리스트
- 반환값: GameResult 객체

#### InputValidator (입력 검증)
1. `parseToNumbers` - 문자열을 숫자 리스트로 파싱하는 함수
- 매개변수: 문자열, 최소값, 최대값, 길이
- 반환값: 숫자 리스트
- 숫자 외의 문자가 포함되어 있다면 [ERROR]
- 입력값이 비어있으면 [ERROR]
- 범위/길이/중복 검증을 함께 수행

2. `validateRange` - 숫자 범위 검증 함수
- 매개변수: 숫자 리스트, 최소값, 최대값
- 반환값: 없음
- 범위를 벗어난 숫자가 있으면 [ERROR]

3. `validateLength` - 길이 검사 함수
- 매개변수: 숫자 리스트, 길이
- 반환값: 없음
- 길이가 다르면 [ERROR]

4. `validateNoDuplicates` - 중복 없는 숫자 리스트인지 검증 함수
- 매개변수: 숫자 리스트
- 반환값: 없음
- 중복이 있으면 [ERROR]

5. `validateRestartInput` - 재시작 입력 검증 함수
- 매개변수: 문자열
- 반환값: boolean
- 잘못된 값이면 [ERROR]

---

### View

#### InputView (입력)
1. `readNumber` - 숫자 입력 함수
- 매개변수: 없음
- 반환값: 문자열
- 안내 문구 출력 필요

2. `readRestartOption` - 재시작/종료 입력 함수
- 매개변수: 없음
- 반환값: 문자열

#### OutputView (출력)
1. `printResult` - 게임 결과 출력 함수
- 매개변수: GameResult 객체
- 반환값: 없음
- 결과 문구 출력

2. `printGameEnd` - 게임 종료 메시지 출력 함수
- 매개변수: 목표 개수
- 반환값: 없음

3. `printError` - 에러 메시지 출력 함수
- 매개변수: exception 객체
- 반환값: 없음

---

### Controller

#### GameController (게임 제어)
1. `run` - 전체 진입 함수
- 매개변수: 없음
- 반환값: 없음
- 최상위 제어, 숫자 생성, 단건 함수 반복 호출, 재시작 함수 호출 등

2. `playRound` - 단건 함수
- 매개변수: 숫자 리스트
- 반환값: boolean
- 숫자 입력, 결과 출력(검사 로직 포함) 호출, 종료 여부 반환

3. `handleRestart` - 재시작 함수
- 매개변수: 없음
- 반환값: boolean
- 올바른 입력이 들어올 때까지 반복
8 changes: 8 additions & 0 deletions src/main/java/BaseballGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import controller.GameController;

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

import model.BaseballGameJudge;
import model.GameResult;
import model.InputValidator;
import model.RandomNumberGenerator;
import model.exception.UserException;
import view.InputView;
import view.OutputView;

import java.util.List;

public class GameController {

private static final int NUMBER_SIZE = 3;
private static final int MIN_NUMBER = 1;
private static final int MAX_NUMBER = 9;

private final RandomNumberGenerator randomNumberGenerator;
private final BaseballGameJudge baseballGameJudge;
private final InputValidator inputValidator;

private final InputView inputView;
private final OutputView outputView;

public GameController() {
this.randomNumberGenerator = new RandomNumberGenerator();
this.baseballGameJudge = new BaseballGameJudge();
this.inputValidator = new InputValidator();

this.inputView = new InputView();
this.outputView = new OutputView();
}

public void run() {
do{
List<Integer> result = randomNumberGenerator.pickUniqueNumbers(MIN_NUMBER, MAX_NUMBER, NUMBER_SIZE);
// List<Integer> result = List.of(1,2,3);
while(!playRound(result)){
}
outputView.printGameEnd(NUMBER_SIZE);
}while(handleRestart());
}

public boolean playRound(List<Integer> answer) {
try{
String input = inputView.readNumber();
List<Integer> inputList = inputValidator.parseToNumbers(input, MIN_NUMBER, MAX_NUMBER, NUMBER_SIZE);
GameResult judge = baseballGameJudge.judge(answer, inputList);
outputView.printResult(judge);
return judge.isGameOver(NUMBER_SIZE);
}catch(UserException e){
outputView.printError(e);
}
return false;
}

public boolean handleRestart() {
while(true){
try{
String s = inputView.readRestartOption();
return inputValidator.validateRestartInput(s);
}catch(UserException e){
outputView.printError(e);
}
}
}
}
37 changes: 37 additions & 0 deletions src/main/java/model/BaseballGameJudge.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package model;

import model.exception.InvalidInputException;

import java.util.List;

import static model.exception.InputErrorCode.*;


public class BaseballGameJudge {

int countBall(List<Integer> answer, List<Integer> guess) {
int ball = 0;
for (int i = 0; i < guess.size(); i++) {
int num = guess.get(i);
if (answer.contains(num) && !answer.get(i).equals(num)) {
ball++;
}
}
return ball;
}

int countStrike(List<Integer> answer, List<Integer> guess) {
int strike = 0;
for (int i = 0; i < guess.size(); i++) {
if (answer.get(i).equals(guess.get(i))) {
strike++;
}
}
return strike;
}

public GameResult judge(List<Integer> answer, List<Integer> guess) {
return new GameResult(countBall(answer, guess), countStrike(answer, guess));
}

}
25 changes: 25 additions & 0 deletions src/main/java/model/GameResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package model;

public class GameResult {

private int ball;
private int strike;

public GameResult(int ball, int strike) {
this.ball = ball;
this.strike = strike;
}

public int getBall() {
return ball;
}

public int getStrike() {
return strike;
}

public boolean isGameOver(int numberSize) {
return numberSize == strike;
}

}
70 changes: 70 additions & 0 deletions src/main/java/model/InputValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package model;

import model.exception.InvalidInputException;

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

import static model.exception.InputErrorCode.*;

public class InputValidator {

public List<Integer> parseToNumbers(String input, int min, int max, int size) {
if (input == null || input.trim().isEmpty()) {
throw new InvalidInputException(INVALID_INPUT_EMPTY);
}

List<Integer> numbers = new ArrayList<>();
for (char c : input.trim().toCharArray()) {
if (!Character.isDigit(c)) {
throw new InvalidInputException(INVALID_INPUT_FORMAT);
}
numbers.add(c - '0');
}

validateInput(numbers, min, max, size);
return numbers;
}

void validateInput(List<Integer> numbers, int min, int max, int size){
validateRange(numbers, min, max);
validateNoDuplicates(numbers);
validateLength(numbers, size);
}

void validateLength(List<Integer> numbers, int size) {
if(numbers.size() != size) throw new InvalidInputException(INVALID_LENGTH);
}

void validateRange(List<Integer> numbers, int min, int max) {
for (Integer num : numbers) {
if (num < min || num > max) {
throw new InvalidInputException(INVALID_NUMBER_RANGE);
}
}
}

void validateNoDuplicates(List<Integer> numbers) {
Set<Integer> seen = new HashSet<>();
for (Integer num : numbers) {
if (!seen.add(num)) {
throw new InvalidInputException(DUPLICATE_NUMBER);
}
}
}

public boolean validateRestartInput(String input) {
if (input.isEmpty()) {
throw new InvalidInputException(INVALID_INPUT_EMPTY);
}
if (input.trim().equals("1")) {
return true;
}
if (input.trim().equals("2")) {
return false;
}
throw new InvalidInputException(INVALID_RESTART_INPUT);
}
}
33 changes: 33 additions & 0 deletions src/main/java/model/RandomNumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package model;

import java.util.*;

public class RandomNumberGenerator {

private final Random random;

public RandomNumberGenerator(){
random = new Random();
}

public int pickNumber(int start, int end) {
if (start > end) throw new IllegalArgumentException("숫자의 범위가 올바르지 않습니다.");
return random.nextInt(end-start+1) + start;
}

public List<Integer> pickUniqueNumbers(int start, int end, int count) {
if(end-start+1 < count) throw new IllegalArgumentException("원하는 개수의 숫자를 뽑을 수 없습니다");
if(count<0) throw new IllegalArgumentException("0개 미만의 숫자를 뽑을 수 없습니다.");
List<Integer> list = new ArrayList<>();
Set<Integer> picks = new HashSet<>();

while(list.size() < count){
int pick = pickNumber(start, end);
if(picks.contains(pick)) continue;
list.add(pick);
picks.add(pick);
}

return list;
}
}
Loading