Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a29f54b
docs(README): 기능 요구사항 작성
Yunseok-Nam Jan 28, 2026
b03097e
docs(README): 잘못된 입력 값 처리에 대한 요구사항 추가 작성
Yunseok-Nam Jan 28, 2026
fbb2be3
chore: editorconfig 추가
Yunseok-Nam Jan 28, 2026
7998aec
build: 인코딩 속성 지정
Yunseok-Nam Jan 28, 2026
697403a
chore: editorconfig 삭제
Yunseok-Nam Jan 28, 2026
a437454
feat: 사용자의 입력 값을 검증하는 로직 작성
Yunseok-Nam Feb 1, 2026
28e97ac
feat: 랜덤 수 생성 기능 구현
Yunseok-Nam Feb 1, 2026
7ea8b15
feat: 게임 결과 계산 기능 구현
Yunseok-Nam Feb 1, 2026
b939d4f
feat: 사용자 입력 숫자를 PlayerNumber로 분리
Yunseok-Nam Feb 1, 2026
63dd54a
feat: Result 출력 포맷을 toString으로 구현
Yunseok-Nam Feb 1, 2026
e8473ce
feat: InputView 작성
Yunseok-Nam Feb 1, 2026
362b0f3
feat: OutputView 작성
Yunseok-Nam Feb 1, 2026
1f76454
docs: 구현한 기능 체크
Yunseok-Nam Feb 1, 2026
f095e38
feat: 에러 메시지 출력 기능 구현
Yunseok-Nam Feb 1, 2026
503745e
feat: 게임 재시작 여부 판단하는 로직 구현
Yunseok-Nam Feb 1, 2026
22cb6a7
feat: BaseballGameController 구현
Yunseok-Nam Feb 1, 2026
1e7b45c
feat: BaseballGame 기능 구현
Yunseok-Nam Feb 1, 2026
9481aeb
refactor: model패키지 분리
Yunseok-Nam Feb 1, 2026
c7bddee
rename: PlayerNumber를 GameNumber로 네이밍 변경
Yunseok-Nam Feb 1, 2026
d664960
refactor: Computer가 GameNumber로 숫자를 관리하도록 변경
Yunseok-Nam Feb 1, 2026
e4ec5a3
test: ComputerTest 작성
Yunseok-Nam Feb 1, 2026
e082359
test: GameNumberTest 작성
Yunseok-Nam Feb 1, 2026
3fd4016
test: GameCommandTest 작성
Yunseok-Nam Feb 1, 2026
cae8027
test: ResultTest 작성
Yunseok-Nam Feb 1, 2026
c15b641
test: RandomNumberGeneratorTest 작성
Yunseok-Nam Feb 1, 2026
8f99a9f
test: 테스트 함수명 변경
Yunseok-Nam Feb 1, 2026
0c0c4f2
refactor: BaseballGameController 로직 변경
Yunseok-Nam Feb 1, 2026
84a8240
rename: 변수명 변경
Yunseok-Nam Feb 1, 2026
a9f2da1
refactor: 상수분리
Yunseok-Nam Feb 1, 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
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,28 @@
# java-baseball-precourse
# java-baseball-precourse

## Domain

- [x] 컴퓨터의 3자리의 수를 맞춰야 한다.
- [x] 3자리의 수는 1~9까지 서로 다른 수로 랜덤하게 이루어진다.
- [x] 같은 수가 같은 자리에 있으면 '스트라이크'
- [x] 같은 수가 다른 자리에 있으면 '볼'
- [x] 같은 수가 없으면 '낫싱'
- [x] 사용자가 잘못된 값을 입력해도 게임을 계속 진행할 수 있어야 한다.
- [x] 에러 메시지를 띄운다.
- [x] 다시 입력을 받는다.

## Input

- [x] 사용자는 컴퓨터가 생각하고 있는 3개의 숫자를 입력한다.
- [x] 게임 종료 시 재시작 또는 종료 여부를 입력한다.
- [x] '1' 입력 시 재시작
- [x] '2' 입력 시 종료

## Output

- [x] 컴퓨터는 플레이어가 입력한 숫자에 대한 결과를 출력한다.
- [x] 사용자가 잘못된 값을 입력할 경우 에러 메시지를 출력한다.
- [x] 에러 메시지는 [ERROR]로 시작한다.
- [x] 게임 종료 시 문구를 출력한다.
- [x] 게임 끝 문구 출력
- [x] 종료 또는 재시작을 선택할 수 있는 문구 출력
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ dependencies {
test {
useJUnitPlatform()
}

compileJava.options.encoding = 'UTF-8'
compileTestJava.options.encoding = 'UTF-8'
Empty file removed src/main/java/.gitkeep
Empty file.
16 changes: 16 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import controller.BaseballGameController;
import model.generator.NumberGenerator;
import model.generator.RandomNumberGenerator;
import view.InputView;
import view.OutputView;

public class Application {
public static void main(String[] args) {
InputView inputView = new InputView();
OutputView outputView = new OutputView();
NumberGenerator numberGenerator = new RandomNumberGenerator();

BaseballGameController gameController = new BaseballGameController(inputView, outputView, numberGenerator);
gameController.run();
}
}
65 changes: 65 additions & 0 deletions src/main/java/controller/BaseballGameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package controller;

import java.util.List;

import model.computer.Computer;
import model.game.GameCommand;
import model.game.Result;
import model.generator.NumberGenerator;
import model.player.Player;
import view.InputView;
import view.OutputView;

public class BaseballGameController {
private static final int GAME_CLEAR_STRIKES = 3;
private final InputView inputView;
private final OutputView outputView;
private final NumberGenerator numberGenerator;

public BaseballGameController(InputView inputView, OutputView outputView, NumberGenerator numberGenerator) {
this.inputView = inputView;
this.outputView = outputView;
this.numberGenerator = numberGenerator;
}

public void run() {
do {
start();
} while (shouldRestart());
}

private void start() {
Computer computer = new Computer(numberGenerator);
Result result;

do {
List<Integer> playerNumber = getPlayer().getNumber();
result = computer.calculate(playerNumber);
outputView.printResult(result);
} while (result.strikes() != GAME_CLEAR_STRIKES);

outputView.printGameClearMessage();
}

private boolean shouldRestart() {
while (true) {
try {
String input = inputView.readRestartOrQuit();
return GameCommand.from(input).isRestart();
} catch (IllegalArgumentException e) {
outputView.printErrorMessage(e.getMessage());
}
}
}

private Player getPlayer() {
while (true) {
try {
String input = inputView.readPlayerNumber();
return new Player(input);
} catch (IllegalArgumentException e) {
outputView.printErrorMessage(e.getMessage());
}
}
}
}
43 changes: 43 additions & 0 deletions src/main/java/model/computer/Computer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package model.computer;

import java.util.List;

import model.game.GameNumber;
import model.game.Result;
import model.generator.NumberGenerator;

public class Computer {
private static final int NUMBER_LENGTH = 3;
private final GameNumber gameNumber;

public Computer(NumberGenerator numberGenerator) {
this.gameNumber = new GameNumber(numberGenerator.generate());
}

public Result calculate(List<Integer> playerNumber) {
int strikes = countStrikes(playerNumber);
int balls = countBalls(playerNumber) - strikes;

return new Result(strikes, balls);
}

private int countStrikes(List<Integer> playerNumbers) {
int strikes = 0;
for (int i = 0; i < NUMBER_LENGTH; i++) {
if (gameNumber.getNumber().get(i).equals(playerNumbers.get(i))) {
strikes++;
}
}
return strikes;
}

private int countBalls(List<Integer> playerNumbers) {
int balls = 0;
for (int num : playerNumbers) {
if (gameNumber.getNumber().contains(num)) {
balls++;
}
}
return balls;
}
}
27 changes: 27 additions & 0 deletions src/main/java/model/game/GameCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package model.game;

public enum GameCommand {
RESTART("1"),
QUIT("2");

private static final String ERROR_INVALID_COMMAND = "1 또는 2를 입력해야 합니다.";

private final String command;

GameCommand(String command) {
this.command = command;
}

public static GameCommand from(String input) {
for (GameCommand command : values()) {
if (command.command.equals(input)) {
return command;
}
}
throw new IllegalArgumentException(ERROR_INVALID_COMMAND);
}

public boolean isRestart() {
return this == RESTART;
}
}
84 changes: 84 additions & 0 deletions src/main/java/model/game/GameNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package model.game;

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

public class GameNumber {
private static final int NUMBER_LENGTH = 3;
private static final int MIN_NUMBER = 1;
private static final int MAX_NUMBER = 9;

private static final String ERROR_INVALID_LENGTH = "[ERROR] 숫자는 3자리여야 합니다.";
private static final String ERROR_NOT_A_DIGIT = "[ERROR] 숫자만 입력해야 합니다.";
private static final String ERROR_INVALID_RANGE = "[ERROR] 숫자는 1부터 9까지만 가능합니다.";
private static final String ERROR_DUPLICATED_DIGIT = "[ERROR] 숫자는 서로 중복될 수 없습니다.";

private final List<Integer> number;

public GameNumber(List<Integer> number) {
validateNumber(number);

this.number = List.copyOf(number);
}

public GameNumber(String number) {
List<Integer> parsedNumber = toDigits(number);
validateNumber(parsedNumber);
this.number = parsedNumber;
}

public List<Integer> getNumber() {
return number;
}

private List<Integer> toDigits(String number) {
if (number == null) {
throw new IllegalArgumentException(ERROR_INVALID_LENGTH);
}

List<Integer> digits = new ArrayList<>(NUMBER_LENGTH);
for (char num : number.toCharArray()) {
validateIsNumber(num);
digits.add(num - '0');
}
return digits;
}

private static void validateNumber(List<Integer> number) {
validateLength(number);

Set<Integer> uniqueNumbers = new HashSet<>();
for (Integer num : number) {
validateNumRange(num);
uniqueNumbers.add(num);
}

validateDuplication(uniqueNumbers);
}

private static void validateLength(List<Integer> number) {
if (number.size() != NUMBER_LENGTH) {
throw new IllegalArgumentException(ERROR_INVALID_LENGTH);
}
}

private static void validateIsNumber(char num) {
if (!Character.isDigit(num)) {
throw new IllegalArgumentException(ERROR_NOT_A_DIGIT);
}
}

private static void validateNumRange(Integer num) {
if (num < MIN_NUMBER || num > MAX_NUMBER) {
throw new IllegalArgumentException(ERROR_INVALID_RANGE);
}
}

private static void validateDuplication(Set<Integer> uniqueNumbers) {
if (uniqueNumbers.size() != NUMBER_LENGTH) {
throw new IllegalArgumentException(ERROR_DUPLICATED_DIGIT);
}
}
}
33 changes: 33 additions & 0 deletions src/main/java/model/game/Result.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package model.game;

public record Result(int strikes, int balls) {

private static final String NOTHING_MESSAGE = "낫싱";
private static final String STRIKE_SUFFIX = "스트라이크";
private static final String BALL_SUFFIX = "볼";

@Override
public String toString() {
if (isNothing()) {
return NOTHING_MESSAGE;
}

StringBuilder sb = new StringBuilder();
if (strikes > 0) {
sb.append(strikes).append(STRIKE_SUFFIX);
}

if (balls > 0) {
if (!sb.isEmpty()) {
sb.append(" ");
}
sb.append(balls).append(BALL_SUFFIX);
}

return sb.toString();
}

public boolean isNothing() {
return strikes == 0 && balls == 0;
}
}
7 changes: 7 additions & 0 deletions src/main/java/model/generator/NumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package model.generator;

import java.util.List;

public interface NumberGenerator {
List<Integer> generate();
}
26 changes: 26 additions & 0 deletions src/main/java/model/generator/RandomNumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package model.generator;

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

public class RandomNumberGenerator implements NumberGenerator {

private static final int NUMBER_LENGTH = 3;
private static final int MIN_DIGIT = 1;
private static final int MAX_DIGIT = 9;

private final Random random = new Random();

@Override
public List<Integer> generate() {
List<Integer> availableDigits = new ArrayList<>();
for (int num = MIN_DIGIT; num <= MAX_DIGIT; num++) {
availableDigits.add(num);
}

Collections.shuffle(availableDigits, random);
return new ArrayList<>(availableDigits.subList(0, NUMBER_LENGTH));
}
}
17 changes: 17 additions & 0 deletions src/main/java/model/player/Player.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package model.player;

import java.util.List;

import model.game.GameNumber;

public class Player {
private final GameNumber gameNumber;

public Player(String number) {
this.gameNumber = new GameNumber(number);
}

public List<Integer> getNumber() {
return gameNumber.getNumber();
}
}
19 changes: 19 additions & 0 deletions src/main/java/view/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package view;

import java.util.Scanner;

public class InputView {
private static final String INPUT_NUMBER_MESSAGE = "숫자를 입력해주세요: ";
private static final String RESTART_OR_QUIT_MESSAGE = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.";
private static final Scanner SCANNER = new Scanner(System.in);

public String readPlayerNumber() {
System.out.print(INPUT_NUMBER_MESSAGE);
return SCANNER.nextLine();
}

public String readRestartOrQuit() {
System.out.println(RESTART_OR_QUIT_MESSAGE);
return SCANNER.nextLine();
}
}
Loading