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
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,29 @@
# java-baseball-precourse
# 숫자 야구 게임
1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 맞추는 게임.

## 기능 정리

### 게임 진행
- 사용자는 3개의 숫자를 입력하여 컴퓨터의 수를 맞추기
- 3개의 숫자를 모두 맞히면 게임이 종료
- 게임 종료 후 재시작(1)하거나 완전히 종료(2)

### 컴퓨터의 수 맞추기 검증
- 위 과정을 반복해 컴퓨터가 선택한 3개의 숫자를 모두 맞히면 게임 종료

### 입력 및 출력
- 게임 시작 문구, 결과, 오류 메시지 등을 출력

### 힌트
- 같은 수가 같은 자리에 있으면 `스트라이크`
- 같은 수가 다른 자리에 있으면 `볼`
- 같은 수가 전혀 없으면 `낫싱`

### 예외 처리
- 사용자가 잘못된 값을 입력할 경우 에러 발생
- `[ERROR]`로 시작하는 에러 메시지를 출력
- 에러 발생 후에도 게임을 계속 진행할 수 있도록 구현
- 3자리가 아닌 경우
- 숫자가 아닌 문자가 포함된 경우
- 중복된 숫자가 있는 경우
- 1~9 범위를 벗어난 경우
9 changes: 9 additions & 0 deletions src/main/java/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import controller.BaseballGame;
import java.io.IOException;

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

import model.Computer;
import model.GameResult;
import model.Numbers;
import view.InputView;
import view.OutputView;

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

public class BaseballGame {
private final InputView inputView;
private final OutputView outputView;
private final Computer computer;

public BaseballGame() {
this.inputView = new InputView();
this.outputView = new OutputView();
this.computer = new Computer();
}

public void run() {
boolean isRunning = true;
while (isRunning) {
playRound();
isRunning = checkRestart();
}
}

private void playRound() {
Numbers target = computer.generate();
boolean isCorrect = false;
while (!isCorrect) {
isCorrect = playTurn(target);
}
outputView.printGameEnd();
}

private boolean playTurn(Numbers target) {
try {
Numbers userNumbers = getUserInput();
GameResult result = calculateResult(target, userNumbers);
outputView.printResult(result);
return result.isThreeStrike();
} catch (IllegalArgumentException e) {
outputView.printError(e.getMessage());
return false;
}
}

private Numbers getUserInput() {
String input = inputView.inputNumber();
return new Numbers(parse(input));
}

private List<Integer> parse(String input) {
List<Integer> numbers = new ArrayList<>();
for (char c : input.toCharArray()) {
numbers.add(c - '0');
}
return numbers;
}

private GameResult calculateResult(Numbers target, Numbers user) {
int strike = target.countStrikes(user);
int ball = target.countBalls(user);
return new GameResult(strike, ball);
}

private boolean checkRestart() {
while (true) {
String input = inputView.askRestart();
if (input.equals("1")) {
return true;
}
if (input.equals("2")) {
return false;
}
outputView.printError("[ERROR] 1 또는 2만 입력 가능합니다.");
}
}
}
23 changes: 23 additions & 0 deletions src/main/java/model/Computer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package model;

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

public class Computer {
private static final int NUMBER_SIZE = 3;
private static final int MIN_NUMBER = 1;
private static final int MAX_NUMBER = 9;

public Numbers generate() {
Random random = new Random();
List<Integer> computer = new ArrayList<>();
while (computer.size() < NUMBER_SIZE) {
int randomNumber = random.nextInt(MAX_NUMBER) + MIN_NUMBER;
if (!computer.contains(randomNumber)) {
computer.add(randomNumber);
}
}
return new Numbers(computer);
}
}
30 changes: 30 additions & 0 deletions src/main/java/model/GameResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package model;

public class GameResult {
private final int strike;
private final int ball;

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

public boolean isThreeStrike() {
return strike == 3;
}

@Override
public String toString() {
if (strike == 0 && ball == 0) {
return "낫싱";
}
String result = "";
if (ball > 0) {
result += ball + "볼 ";
}
if (strike > 0) {
result += strike + "스트라이크";
}
return result.trim();
}
}
65 changes: 65 additions & 0 deletions src/main/java/model/Numbers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package model;

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

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

public Numbers(List<Integer> numbers) {
validate(numbers);
this.numbers = numbers;
}

private void validate(List<Integer> numbers) {
if (numbers.size() != 3) {
throw new IllegalArgumentException("[ERROR] 3자리의 숫자를 입력해야 합니다.");
}
for (int number : numbers) {
validateRange(number);
}
validateUnique(numbers);
}

private void validateRange(int number) {
if (number < 1 || number > 9) {
throw new IllegalArgumentException("[ERROR] 1~9 사이의 숫자만 입력 가능합니다.");
}
}

private void validateUnique(List<Integer> numbers) {
Set<Integer> uniqueNumbers = new HashSet<>(numbers);
if (uniqueNumbers.size() != numbers.size()) {
throw new IllegalArgumentException("[ERROR] 중복된 숫자는 입력할 수 없습니다.");
}
}

public int countStrikes(Numbers other) {
int count = 0;
for (int i = 0; i < numbers.size(); i++) {
if (numbers.get(i).equals(other.get(i))) {
count++;
}
}
return count;
}

public int countBalls(Numbers other) {
int count = 0;
for (int i = 0; i < numbers.size(); i++) {
if (!numbers.get(i).equals(other.get(i)) && numbers.contains(other.get(i))) {
count++;
}
}
return count;
}

public Integer get(int index) {
return numbers.get(index);
}

public boolean contains(int number) {
return numbers.contains(number);
}
}
31 changes: 31 additions & 0 deletions src/main/java/view/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package view;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class InputView {
private final BufferedReader br;

public InputView() {
this.br = new BufferedReader(new InputStreamReader(System.in));
}

public String inputNumber() {
System.out.print("숫자를 입력해주세요 : ");
try {
return br.readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public String askRestart() {
System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.");
try {
return br.readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
17 changes: 17 additions & 0 deletions src/main/java/view/OutputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package view;

import model.GameResult;

public class OutputView {
public void printResult(GameResult result) {
System.out.println(result.toString());
}

public void printGameEnd() {
System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 끝");
}

public void printError(String message) {
System.out.println(message);
}
}
23 changes: 23 additions & 0 deletions src/test/java/model/GameResultTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package model;

import org.junit.jupiter.api.Test;

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

public class GameResultTest {

@Test
void 결과_출력_형식_확인() {
GameResult result = new GameResult(1, 1);
assertEquals("1볼 1스트라이크", result.toString());

result = new GameResult(3, 0);
assertEquals("3스트라이크", result.toString());

result = new GameResult(0, 3);
assertEquals("3볼", result.toString());

result = new GameResult(0, 0);
assertEquals("낫싱", result.toString());
}
}
64 changes: 64 additions & 0 deletions src/test/java/model/NumbersTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package model;

import org.junit.jupiter.api.Test;

import java.util.Arrays;

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

public class NumbersTest {
@Test
void 숫자_리스트_생성_정상() {
assertDoesNotThrow(() -> new Numbers(Arrays.asList(1, 2, 3)));
}

@Test
void 예외_테스트_숫자_길이오류() {
assertThrows(IllegalArgumentException.class, () -> new Numbers(Arrays.asList(1, 2)));
assertThrows(IllegalArgumentException.class, () -> new Numbers(Arrays.asList(1, 2, 3, 4)));
}

@Test
void 예외_테스트_숫자_범위오류() {
assertThrows(IllegalArgumentException.class, () -> new Numbers(Arrays.asList(1, 0, 3)));
}

@Test
void 예외_테스트_숫자_중복오류() {
assertThrows(IllegalArgumentException.class, () -> new Numbers(Arrays.asList(1, 2, 1)));
}

@Test
void 스트라이크_계산_3스트라이크() {
Numbers computer = new Numbers(Arrays.asList(1, 2, 3));
Numbers user = new Numbers(Arrays.asList(1, 2, 3));
assertEquals(3, computer.countStrikes(user));
assertEquals(0, computer.countBalls(user));
}

@Test
void 볼_계산_3볼() {
Numbers computer = new Numbers(Arrays.asList(1, 2, 3));
Numbers user = new Numbers(Arrays.asList(2, 3, 1));
assertEquals(0, computer.countStrikes(user));
assertEquals(3, computer.countBalls(user));
}

@Test
void 낫싱_계산() {
Numbers computer = new Numbers(Arrays.asList(1, 2, 3));
Numbers user = new Numbers(Arrays.asList(4, 5, 6));
assertEquals(0, computer.countStrikes(user));
assertEquals(0, computer.countBalls(user));
}

@Test
void 스트라이크_1_볼_1_계산() {
Numbers computer = new Numbers(Arrays.asList(1, 2, 3));
Numbers user = new Numbers(Arrays.asList(1, 3, 4));
assertEquals(1, computer.countStrikes(user));
assertEquals(1, computer.countBalls(user));
}
}