Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[숫자 야구 게임] 김승진 미션 제출합니다. #7

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
94 changes: 94 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
## 기능 명세
1. 랜덤 정답 생성 기능
- 정답 저장 기능
2. 사용자 정답 입력 기능
- input 숫자 여부 검증 및 예외처리
- input 숫자의 길이 검증 및 예외처리
3. Strike와 Ball 개수 판단 기능
- 3 Strike인 경우 게임 종료 기능
- 3 Strike가 아닌 경우 결과 출력 기능
4. 정답인 경우 새 게임 진행 여부 입력 기능
- input 1 or 2 검증 및 예외처리
- input 1인 경우 새 게임 시작 기능
- input 0인 경우 종료 기능

## 구현 클래스 명세

### Application - 실행을 담당
- main() 어플리케이션의 실행

### BaseballController - 정답 관련 요청과 입력 검증을 담당
- startGame() 하나의 게임 실행
- askIsContinue() 게임 종료시 이어서 할 지에 대한 여부 검증
- playGame() 게임 메인 로직 실행
- getUserAttempt() 유저의 정답 시도 입력
- getResult() 결과 계산 로직 실행

### BaseballService - 정답 관련 서비스 로직 담당
- saveAnswer() 정답을 생성하여 저장
- calculateResult() 결과를 계산
- findCurrentAnswer() 현재 정답 정보를 조회

### BaseballRepository - 정답 정보 저장, 조회를 담당
- saveAnswer() 정답 저장
- findCurrentAnswer() 현재 정답 정보를 조회

### Answer - 정답 정보를 가진 불변클래스
- generateRandomNumbers() 랜덤한 숫자를 생성
- createAnswer() 불변클래스의 팩토리 메서드
- hasNum() 매개변수로 받는 숫자가 정답에 포함됐는지 확인

### ComponentFactory - 컴포넌트들의 생성을 담당
- 각 컴포넌트들을 생성

### InputValidator - 유저의 시도에 대한 입력값 검증
- validateUserAttempt() 유저의 시도에 대한 입력값 검증
- notValidAttemptString() 입력 String 이 유효한지 검증
- hasDuplicate() 중복된 숫자가 입력되었는지 검증
- validateIsContinue() 계속 진행할지 여부에 대한 입력 검증
- notValidContinueString() 계속 진행할지 여부에 대한 입력값 검증

### InputReader - 사용자의 입력값 받아와 검증
- readUserAttempt() 유저의 시도 입력 받기 및 입력값 검증
- readIsContinue() 게임 이어서 하기 여부 입력 받기 및 입력값 검증

### OutputWriter - 문자열 출력
- write() 문자열을 그대로 출력
- writeWithLine() 맨 끝에 개행문자와 함께 출력

### ResultProvider - Answer 객체와 유저의 입력을 받아 결과를 반환
- calculateResult() 최종 출력할 결과를 반환
- mapToStrikeBalls() StrikeBall의 리스트로 변환
- checkStrikeBall() 계산을 통해 StrikeBall 하나로 변환 및 반환
- checkBallOrNone() 같은 인덱스에 같은 값이 없는 경우 ball 인지 판단
- findAnswerNumByIndex() 정답 숫자에서 인덱스에 해당하는 숫자 반환
- convertToIntWithIndex() String 에서 인덱스에 해당하는 숫자를 int 로 변환

### ResultOutputFormatter - 출력할 결과 포맷을 만듦
- writeWithLine() 개행문자화 함께 출력
- write() 문자열 그대로 출력
- writeWithFormat() 결과 형태로 변환하여 출력

### Result - strike 개수롸 ball 개수를 갖는 dto
- getStrikeNum() strike 개수 반환
- getBallNum() ball 개수 반환

### ResultOutputFormatter - Result를 결과 문자열로 변환
- format() 결과 문자열로 변환
- getFormattedOutput() format 진행
- toCountResult() strike 혹은 ball 이 있을 경우 변환 진행
- appendStrikeIfExists() strike의 개수에 따라 분기하여 변환

## 구현 열거형 명세

### GameMessage
- 게임의 메시지들을 나타낸다.

### RandomNumberRange
- 랜덤한 숫자의 범위, 자릿수를 나타낸다.

### StrikeBall
- 스트라이크, 볼을 나타낸다.

### ResultMessage
- 결과 메시지를 나타낸다.
38 changes: 38 additions & 0 deletions src/main/java/baseball/Answer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package baseball;

import camp.nextstep.edu.missionutils.Randoms;

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

public final class Answer {
private final List<Integer> currentAnswer;

private Answer() {
this.currentAnswer = Collections.unmodifiableList(generateRandomNumbers());
}

public static Answer createAnswer() {
return new Answer();
}

private List<Integer> generateRandomNumbers() {
List<Integer> randomNumbers = new ArrayList<>();
while (randomNumbers.size() < 3) {
int randomNumber = Randoms.pickNumberInRange(1, 9);
if (!randomNumbers.contains(randomNumber)) {
randomNumbers.add(randomNumber);
}
}
return randomNumbers;
}

public Integer findByIndex(final Integer index) {
return currentAnswer.get(index);
}

public boolean hasNum(final int attemptNum) {
return currentAnswer.contains(attemptNum);
}
}
4 changes: 3 additions & 1 deletion src/main/java/baseball/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
final ComponentFactory componentFactory = new ComponentFactory();
final BaseballController baseballController = componentFactory.baseballController();
baseballController.startGame();
}
}
59 changes: 59 additions & 0 deletions src/main/java/baseball/BaseballController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package baseball;

public class BaseballController {
private static final int CONTINUE_COMMAND = 1;
private static final int ATTEMPT_SUCCESS = 3;
private final BaseballService baseballService;
private final OutputWriter outputWriter;
private final InputReader inputReader;

public BaseballController(
final BaseballService baseballService,
final OutputWriter outputWriter,
final InputReader inputReader) {

this.baseballService = baseballService;
this.outputWriter = outputWriter;
this.inputReader = inputReader;
}

public void startGame() {
boolean isContinue = true;
while (isContinue) {
outputWriter.writeWithLine(GameMessage.GAME_START);
baseballService.saveAnswer();
playGame();
isContinue = askIsContinue();
}
}

private boolean askIsContinue() {
outputWriter.writeWithLine(GameMessage.CREATE_NEW_GAME_OR_NOT);
final String input = inputReader.readIsContinue();
return Integer.parseInt(input) == CONTINUE_COMMAND;
}

private void playGame() {
boolean isContinue = true;
while (isContinue) {
final String userAttempt = getUserAttempt();
final Result result = getResult(userAttempt);

if (result.getStrikeNum() == ATTEMPT_SUCCESS) {
outputWriter.writeWithLine(GameMessage.GAME_FINISH);
isContinue = false;
}
}
}

private String getUserAttempt() {
outputWriter.write(GameMessage.INPUT_NEXT_NUMBER);
return inputReader.readUserAttempt();
}

private Result getResult(final String userAttempt) {
final Result result = baseballService.calculateResult(userAttempt);
outputWriter.writeWithFormat(result);
return result;
}
}
13 changes: 13 additions & 0 deletions src/main/java/baseball/BaseballRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package baseball;

public class BaseballRepository {
private Answer answer;

public void saveAnswer(final Answer answer) {
this.answer = answer;
}

public Answer findCurrentAnswer() {
return this.answer;
}
}
24 changes: 24 additions & 0 deletions src/main/java/baseball/BaseballService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package baseball;

public class BaseballService {
private final BaseballRepository baseballRepository;

public BaseballService(final BaseballRepository baseballRepository) {
this.baseballRepository = baseballRepository;
}

public void saveAnswer() {
final Answer answer = Answer.createAnswer();
baseballRepository.saveAnswer(answer);
}

public Result calculateResult(final String attempt) {
final Answer currentAnswer = findCurrentAnswer();
final ResultProvider resultProvider = new ResultProvider(currentAnswer, attempt);
return resultProvider.calculateResult();
}

private Answer findCurrentAnswer() {
return baseballRepository.findCurrentAnswer();
}
}
35 changes: 35 additions & 0 deletions src/main/java/baseball/ComponentFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package baseball;

public class ComponentFactory {

public BaseballController baseballController() {
return new BaseballController(
baseballService(),
outputWriter(),
inputReader());
}

private ResultOutputFormatter resultOutputFormatter() {
return new ResultOutputFormatter();
}

private InputReader inputReader() {
return new InputReader(inputValidator());
}

private OutputWriter outputWriter() {
return new OutputWriter(resultOutputFormatter());
}

private BaseballService baseballService() {
return new BaseballService(baseballRepository());
}

private BaseballRepository baseballRepository() {
return new BaseballRepository();
}

private InputValidator inputValidator() {
return new InputValidator();
}
}
18 changes: 18 additions & 0 deletions src/main/java/baseball/GameMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package baseball;

public enum GameMessage {
GAME_START("숫자 야구 게임을 시작합니다."),
INPUT_NEXT_NUMBER("숫자를 입력해주세요 : "),
GAME_FINISH("3개의 숫자를 모두 맞히셨습니다! 게임 종료"),
CREATE_NEW_GAME_OR_NOT("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.");
private final String message;

GameMessage(final String message) {
this.message = message;
}

@Override
public String toString() {
return this.message;
}
}
23 changes: 23 additions & 0 deletions src/main/java/baseball/InputReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package baseball;

import camp.nextstep.edu.missionutils.Console;

public class InputReader {
private final InputValidator inputValidator;

public InputReader(final InputValidator inputValidator) {
this.inputValidator = inputValidator;
}

public String readUserAttempt() {
final String input = Console.readLine();
inputValidator.validateUserAttempt(input);
return input;
}

public String readIsContinue() {
final String input = Console.readLine();
inputValidator.validateIsContinue(input);
return input;
}
}
43 changes: 43 additions & 0 deletions src/main/java/baseball/InputValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package baseball;

public class InputValidator {
private static final String NUMERIC_MATCHER = "-?\\d+";
private static final Integer IS_CONTINUE_LENGTH = 1;
private static final Integer CONTINUE = 1;
private static final Integer NOT_CONTINUE = 2;


public void validateUserAttempt(final String userAttempt) {
if (notValidAttemptString(userAttempt) || hasDuplicate(userAttempt)) {
throw new IllegalArgumentException();
}
}

private boolean notValidAttemptString(final String userAttempt) {
return userAttempt.isBlank()
|| !userAttempt.matches(NUMERIC_MATCHER)
|| userAttempt.length() != RandomNumberRange.CIPHER.getNum();
}

private boolean hasDuplicate(final String userAttempt) {
final long count = userAttempt.chars().distinct().count();
return count != 3;
}

public void validateIsContinue(final String isContinueInput) {
if (notValidIsContinueString(isContinueInput) || isOutOfRange(isContinueInput)) {
throw new IllegalArgumentException();
}
}

private boolean notValidIsContinueString(final String isContinueInput) {
return isContinueInput.isBlank()
|| !isContinueInput.matches(NUMERIC_MATCHER)
|| isContinueInput.length() != IS_CONTINUE_LENGTH;
}

private boolean isOutOfRange(final String isContinueInput) {
final int input = Integer.parseInt(isContinueInput);
return input != CONTINUE && input != NOT_CONTINUE;
}
}
21 changes: 21 additions & 0 deletions src/main/java/baseball/OutputWriter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package baseball;

public class OutputWriter {
private final ResultOutputFormatter resultOutputFormatter;

public OutputWriter(final ResultOutputFormatter resultOutputFormatter) {
this.resultOutputFormatter = resultOutputFormatter;
}

public void writeWithLine(final GameMessage gameMessage) {
System.out.println(gameMessage);
}

public void write(final GameMessage gameMessage) {
System.out.print(gameMessage);
}

public void writeWithFormat(final Result result) {
System.out.println(resultOutputFormatter.format(result));
}
}
Loading