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
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,43 @@
# java-baseball-precourse
# java-baseball-precourse

## 기능 목록

역할과 책임에 따라 **Controller, View, Domain** 세 개의 계층으로 분리되어 있습니다.

- **Controller (`BaseballController`)**
- 게임의 전체적인 흐름(시작, 재시작, 종료)을 관리합니다.
- 사용자 입력에 따라 View와 Domain 객체 간의 상호작용을 조율합니다.


- **View (`InputView`, `OutputView`)**
- 사용자와의 모든 상호작용(콘솔 입출력)을 담당합니다.
- `InputView`: 사용자로부터 입력을 받습니다.
- `OutputView`: 게임 진행 상황, 결과, 오류 메시지 등을 출력합니다.


- **Domain (`baseball.domain.*`)**

- **`BaseballGame`**
- 한 판의 게임 세션을 관리하며, 게임의 핵심 진행을 책임집니다.
- `Computer`를 내부적으로 생성하여 정답 번호를 가집니다.
- `playRound(Player)`: 플레이어의 추측을 받아 결과를 `Score`로 반환합니다.

- **`Computer`**
- 서로 다른 1~9 사이의 임의의 수 3개(정답 번호)를 생성합니다.

- **`Player`**
- 사용자가 입력한 세 자리 숫자를 보유합니다.
- **입력값 검증**: 생성 시점에서 입력값이 3자리의 숫자인지, 중복된 숫자가 없는지 등을 검증하고, 유효하지 않으면 `IllegalArgumentException`을 발생시킵니다.

- **`Referee`**
- `Computer`의 숫자와 `Player`의 숫자를 비교하여 스트라이크와 볼의 개수를 계산하는 정적(static) 유틸리티 메소드를 제공합니다.

- **`Score`**
- 한 라운드의 결과(스트라이크, 볼의 개수)를 저장합니다.
- `isFinished()`: 3스트라이크인지 여부를 판단하여 게임 종료 조건을 캡슐화합니다.
- `toString()`: "1볼 1스트라이크", "낫싱" 등 출력용 문자열로 변환하는 기능을 제공합니다.

- **`GameStatus`**
- 게임 종료 후 사용자의 선택(재시작/완전 종료) 상태를 관리하는 열거형(Enum)입니다.


Empty file removed src/main/java/.gitkeep
Empty file.
12 changes: 12 additions & 0 deletions src/main/java/baseball/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package baseball;

import baseball.controller.BaseballController;

public class Application {

public static void main(String[] args) {
BaseballController controller = new BaseballController();
controller.play();
}

}
49 changes: 49 additions & 0 deletions src/main/java/baseball/controller/BaseballController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package baseball.controller;

import baseball.domain.*;
import baseball.view.*;
import baseball.domain.BaseballGame;

public class BaseballController {
private final InputView inputView = new InputView();
private final OutputView outputView = new OutputView();

public void play() {
do {
runGame();
} while (isRestartRequested());
}

private void runGame() {
BaseballGame game = new BaseballGame();
Score score;
do {
Player player = enrollPlayer();
score = game.playRound(player);
outputView.printScore(score.toString());
} while (!score.isFinished());

outputView.printGameEnd();
}

private Player enrollPlayer() {
try {
outputView.printInputRequest();
return new Player(inputView.readNumbers());
} catch (IllegalArgumentException e) {
outputView.printError(e.getMessage());
return enrollPlayer();
}
}

private boolean isRestartRequested() {
try {
String input = inputView.readNumbers();
GameStatus status = GameStatus.from(input);
return status.isRestart();
} catch (IllegalArgumentException e) {
outputView.printError(e.getMessage());
return isRestartRequested();
}
}
}
17 changes: 17 additions & 0 deletions src/main/java/baseball/domain/BaseballGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package baseball.domain;

import java.util.List;

public class BaseballGame {
private final Computer computer;

public BaseballGame() {
this.computer = new Computer();
}

public Score playRound(Player player) {
List<Integer> computerNumbers = computer.getNumbers();
List<Integer> playerNumbers = player.getNumbers();
return Referee.compare(computerNumbers, playerNumbers);
}
}
16 changes: 16 additions & 0 deletions src/main/java/baseball/domain/Computer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package baseball.domain;

import java.util.List;
import baseball.utils.RandomNumberGenerator;

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

public Computer() {
this.numbers = RandomNumberGenerator.generate();
}

public List<Integer> getNumbers() {
return numbers;
}
}
25 changes: 25 additions & 0 deletions src/main/java/baseball/domain/GameStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package baseball.domain;

public enum GameStatus {
RESTART("1"),
EXIT("2");

private final String command;

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

public static GameStatus from(String input) {
for (GameStatus status : values()) {
if (status.command.equals(input)) {
return status;
}
}
throw new IllegalArgumentException("[ERROR] 1 또는 2만 입력 가능합니다.");
}

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

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

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

public Player(String input) {
validate(input);
this.numbers = parse(input);
}

private void validate(String input) {
if (!input.matches("^[1-9]{3}$")) {
throw new IllegalArgumentException("[ERROR] 1-9 사이의 숫자 3개를 입력해야 합니다.");
}
if (hasDuplicate(input)) {
throw new IllegalArgumentException("[ERROR] 중복된 숫자가 있습니다.");
}
}

private boolean hasDuplicate(String input) {
return input.chars().distinct().count() != input.length();
}

private List<Integer> parse(String input) {
List<Integer> list = new ArrayList<>();
for (char c : input.toCharArray()) {
list.add(Character.getNumericValue(c));
}
return list;
}

public List<Integer> getNumbers() {
return numbers;
}
}
29 changes: 29 additions & 0 deletions src/main/java/baseball/domain/Referee.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package baseball.domain;

import java.util.List;
import java.util.Objects;

public class Referee {
public static Score compare(List<Integer> computer, List<Integer> player) {
int strike = 0;
int ball = 0;

for (int i = 0; i < 3; i++) {
strike += countStrike(computer.get(i), player.get(i));
ball += countBall(computer, player, i);
}
return new Score(strike, ball);
}

private static int countStrike(int computerNum, int playerNum) {
if (computerNum == playerNum) return 1;
return 0;
}

private static int countBall(List<Integer> computer, List<Integer> player, int index) {
if (computer.contains(player.get(index)) && !Objects.equals(computer.get(index), player.get(index))) {
return 1;
}
return 0;
}
}
27 changes: 27 additions & 0 deletions src/main/java/baseball/domain/Score.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package baseball.domain;

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

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

public int getStrike() {
return strike;
}

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

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

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

public class RandomNumberGenerator {

public static List<Integer> generate() {
List<Integer> numbers = new ArrayList<>();
Random random = new Random();

while (numbers.size() < 3) {
int randomNumber = random.nextInt(9) + 1;
addIfUnique(numbers, randomNumber);
}
return numbers;
}

private static void addIfUnique(List<Integer> list, int number) {
if (!list.contains(number)) {
list.add(number);
}
}
}
13 changes: 13 additions & 0 deletions src/main/java/baseball/view/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package baseball.view;

import java.util.Scanner;

public class InputView {

Scanner scanner = new Scanner(System.in);

public String readNumbers() {

return scanner.nextLine();
}
}
21 changes: 21 additions & 0 deletions src/main/java/baseball/view/OutputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package baseball.view;

public class OutputView {
public void printError(String message) {
System.out.println(message);
}

public void printInputRequest() {
System.out.print("숫자를 입력해 주세요 : ");
}

public void printScore(String string) {
System.out.println(string);
}

public void printGameEnd() {
System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 끝\n" +
"게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.");

}
}
Empty file removed src/test/java/.gitkeep
Empty file.
20 changes: 20 additions & 0 deletions src/test/java/baseball/domain/BaseballGameTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package baseball.domain;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class BaseballGameTest {

@Test
@DisplayName("playRound는 플레이어의 숫자를 기반으로 정확한 Score를 반환해야 한다.")
void playRoundShouldReturnCorrectScore() {
BaseballGame game = new BaseballGame();
Player player = new Player("123");

Score score = game.playRound(player);

assertThat(score).isNotNull();
}
}
23 changes: 23 additions & 0 deletions src/test/java/baseball/domain/ComputerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package baseball.domain;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

class ComputerTest {

@RepeatedTest(100)
@DisplayName("컴퓨터는 3자리의 중복되지 않는 숫자를 생성한다.")
void createComputerShouldGenerateThreeUniqueNumbers() {

Computer computer = new Computer();
List<Integer> numbers = computer.getNumbers();

assertThat(numbers).hasSize(3);
assertThat(numbers).doesNotHaveDuplicates();
}
}
Loading