Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 49 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,49 @@
# java-baseball-precourse
# java-baseball-precourse

## 개요
컴퓨터가 생성한 3자리 수를 맞히는 콘솔 게임이다.

## 규칙
- 컴퓨터는 1부터 9까지의 서로 다른 수로 이루어진 3개의 수를 선택한다.
- 사용자는 3자리 수를 입력한다.
- 같은 숫자가 같은 자리에 있으면 스트라이크를 출력한다.
- 같은 숫자가 다른 자리에 있으면 볼을 출력한다.
- 같은 숫자가 하나도 없으면 낫싱을 출력한다.

## 기능 목록
- 1부터 9까지의 서로 다른 3개의 수로 이루어진 두 개의 3자리 수를 비교할 수 있다.
- 같은 숫자가 같은 자리에 있으면 스트라이크이다.
- 같은 숫자가 다른 자리에 있으면 볼이다.
- 같은 숫자가 하나도 없으면 낫싱이다.
- 1부터 9까지의 서로 다른 3개의 수를 랜덤으로 생성한다.
- 게임은 시작 시점에 자신의 3자리 수를 생성한다.
- 게임은 초기화 기능을 제공하고, 초기화시 새로운 3자리 수를 생성한다.
- 게임은 3자리 수를 입력받았을 때 자신이 생성한 숫자와 비교하여 결과를 반환한다.
- 사용자는 3자리 수를 입력할 수 있다.
- 입력 안내 메시지는 `숫자를 입력해주세요 : `이다.
- 입력값에 대한 결과를 스트라이크/볼/낫싱으로 출력한다.
- 3개의 숫자를 모두 맞히면 게임을 종료한다.
- 게임 종료 메시지는 `3개의 숫자를 모두 맞히셨습니다! 게임 끝`이다.
- 게임을 종료한 후 게임을 다시 시작하거나 완전히 종료할 수 있다.
- 안내 메시지는 `게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.`이며, 사용자의 입력을 다음 줄에 받는다.
- 사용자가 잘못된 값을 입력할 경우, [ERROR]로 시작하는 에러 메시지를 출력하고 게임을 계속 진행할 수 있어야 한다.

## 프로그램 실행 예시
```text
숫자를 입력해주세요 : 123
1스트라이크 1볼
숫자를 입력해주세요 : 145
1볼
숫자를 입력해주세요 : 671
2볼
숫자를 입력해주세요 : 216
1스트라이크
숫자를 입력해주세요 : 713
3스트라이크
3개의 숫자를 모두 맞히셨습니다! 게임 끝
게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.
1
숫자를 입력해주세요 : 123
1볼
...
```
Empty file removed src/main/java/.gitkeep
Empty file.
24 changes: 24 additions & 0 deletions src/main/java/baseball/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package baseball;

import java.util.Scanner;

import baseball.controller.GameController;
import baseball.model.Game;
import baseball.model.generator.AbstractDigitsGenerator;
import baseball.model.generator.RandomDigitsGenerator;
import baseball.view.InputView;
import baseball.view.OutputView;

public class Application {

public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
InputView inputView = new InputView(scanner);
OutputView outputView = new OutputView();
AbstractDigitsGenerator generator = new RandomDigitsGenerator();
Game game = new Game(3, generator);
GameController controller = new GameController(game, inputView, outputView);

controller.play();
}
}
96 changes: 96 additions & 0 deletions src/main/java/baseball/controller/GameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package baseball.controller;

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

import baseball.model.CompareResult;
import baseball.model.Game;
import baseball.view.InputView;
import baseball.view.OutputView;

public class GameController {

private final Game game;
private final InputView inputView;
private final OutputView outputView;

public GameController(Game game, InputView inputView, OutputView outputView) {
this.game = game;
this.inputView = inputView;
this.outputView = outputView;
}

public void play() {
while (true) {
playSingleGame();
if (shouldExit()) {
return;
}
game.reset();
}
}

private void playSingleGame() {
while (true) {
if (guess()) {
outputView.printGameEnd(game.getDigitsLength());
return;
}
}
}

private boolean guess() {
try {
CompareResult result = compareInput();
outputView.printResult(result);
return isGameEnd(result);
} catch (RuntimeException exception) {
outputView.printError(exception.getMessage());
}
return false;
}

private CompareResult compareInput() {
String input = inputView.readDigits();
List<Integer> digits = parseDigits(input);
return game.compare(digits);
}

private List<Integer> parseDigits(String input) {
List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < input.length(); i++) {
char digit = input.charAt(i);
validateDigit(digit);
numbers.add(digit - '0');
}
return numbers;
}

private void validateDigit(char digit) {
if (digit < '0' || digit > '9') {
throw new RuntimeException("숫자만 입력해 주세요");
}
}

private boolean isGameEnd(CompareResult result) {
return result.strike() == result.length();
}

private boolean shouldExit() {
while (true) {
try {
int command = inputView.readGameCommand();
validateGameCommand(command);
return command == 2;
} catch (RuntimeException exception) {
outputView.printError(exception.getMessage());
}
}
}

private void validateGameCommand(int command) {
if (command != 1 && command != 2) {
throw new RuntimeException("1 또는 2를 입력해 주세요.");
}
}
}
17 changes: 17 additions & 0 deletions src/main/java/baseball/model/CompareResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package baseball.model;

public record CompareResult(int length, int strike, int ball) {

public CompareResult {
validate(length, strike, ball);
}

private void validate(int length, int strike, int ball) {
if (strike < 0 || ball < 0) {
throw new RuntimeException("야구 숫자 비교 결과가 음수이면 안됩니다.");
}
if (length < strike || length < ball) {
throw new RuntimeException("strike, ball 개수가 숫자 개수보다 많으면 안됩니다.");
}
}
}
71 changes: 71 additions & 0 deletions src/main/java/baseball/model/Digits.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package baseball.model;

import java.util.Arrays;
import java.util.List;

public class Digits {

private final List<Integer> numbers;

public Digits(List<Integer> numbers) {
boolean[] isExist = new boolean[10];
Arrays.fill(isExist, false);

for (int number : numbers) {
validate(isExist, number);
isExist[number] = true;
}
this.numbers = List.copyOf(numbers);
}

public CompareResult compareTo(Digits digits) {
validateSameLength(digits);

int strike = 0;
int ball = 0;
for (int i = 0; i < numbers.size(); i++) {
if (digits.isMatch(i, numbers.get(i))) {
strike++;
continue;
}
if (digits.isInside(numbers.get(i))) {
ball++;
}
}

return new CompareResult(numbers.size(), strike, ball);
}

public int getLength() {
return numbers.size();
}

private void validate(boolean[] isExist, int number) {
if (number < 1 || number > 9) {
throw new RuntimeException("1부터 9까지의 숫자가 아닙니다. 중복되지 않는 1부터 9까지의 숫자를 입력해 주세요.");
}

if (isExist[number]) {
throw new RuntimeException("중복된 숫자가 있습니다. 중복되지 않는 1부터 9까지의 숫자를 입력해 주세요.");
}
}

private boolean isInside(int target) {
for (int number : numbers) {
if (number == target) {
return true;
}
}
return false;
}

private boolean isMatch(int index, int target) {
return numbers.get(index) == target;
}

private void validateSameLength(Digits digits) {
if (digits.getLength() != numbers.size()) {
throw new RuntimeException("비교할 숫자 길이가 다릅니다.");
}
}
}
45 changes: 45 additions & 0 deletions src/main/java/baseball/model/Game.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package baseball.model;

import java.util.List;

import baseball.model.generator.AbstractDigitsGenerator;

public class Game {

private final int digitsLength;
private final AbstractDigitsGenerator generator;
private Digits target;

public Game(int digitsLength, AbstractDigitsGenerator generator) {
validate(digitsLength);
this.digitsLength = digitsLength;
this.generator = generator;
this.target = this.generator.generate(this.digitsLength);
}

public CompareResult compare(List<Integer> input) {
validateLength(input);
Digits digits = new Digits(input);
return target.compareTo(digits);
}

public void reset() {
target = generator.generate(digitsLength);
}

public int getDigitsLength() {
return digitsLength;
}

private void validate(int digitsLength) {
if (digitsLength < 1 || digitsLength > 9) {
throw new RuntimeException("야구 게임의 자리수는 1부터 9사이어야 합니다.");
}
}

private void validateLength(List<Integer> input) {
if (input.size() != digitsLength) {
throw new RuntimeException("숫자 개수가 올바르지 않습니다. " + digitsLength + "자리 숫자를 입력해 주세요.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package baseball.model.generator;

import baseball.model.Digits;

public abstract class AbstractDigitsGenerator {

public final Digits generate(int length) {
validateLength(length);
return doGenerate(length);
}

protected abstract Digits doGenerate(int length);

private void validateLength(int length) {
if (length < 1 || length > 9) {
throw new RuntimeException("숫자 길이는 1부터 9까지만 가능합니다.");
}
}
}
25 changes: 25 additions & 0 deletions src/main/java/baseball/model/generator/RandomDigitsGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package baseball.model.generator;

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

import baseball.model.Digits;

public class RandomDigitsGenerator extends AbstractDigitsGenerator {

@Override
protected Digits doGenerate(int length) {
List<Integer> numbers = createShuffledNumbers();
return new Digits(numbers.subList(0, length));
}

private List<Integer> createShuffledNumbers() {
List<Integer> numbers = new ArrayList<>();
for (int i = 1; i <= 9; i++) {
numbers.add(i);
}
Collections.shuffle(numbers);
return numbers;
}
}
33 changes: 33 additions & 0 deletions src/main/java/baseball/view/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package baseball.view;

import java.util.Scanner;

public class InputView {

private static final String INPUT_MESSAGE = "숫자를 입력해주세요 : ";
private static final String GAME_COMMAND_MESSAGE = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.";

private final Scanner scanner;

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

public String readDigits() {
System.out.print(INPUT_MESSAGE);
return scanner.nextLine();
}

public int readGameCommand() {
System.out.println(GAME_COMMAND_MESSAGE);
return parseInteger(scanner.nextLine());
}

private int parseInteger(String input) {
try {
return Integer.parseInt(input);
} catch (NumberFormatException exception) {
throw new RuntimeException("1 또는 2를 입력해 주세요.");
}
}
}
Loading