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

[충남대 BE_김기웅] 미션 제출합니다. #142

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
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
529a49f
README 구현할 기능 목록 작성
lit2020 May 5, 2024
9f84baa
정답 (서로 다른 숫자 3개로 이루어진 3자리수) 생성
lit2020 May 5, 2024
702d1f3
feat : receive user input
lit2020 May 5, 2024
5848cef
feat : evaluate user guess
lit2020 May 5, 2024
26d874b
feat : show evaluation result
lit2020 May 5, 2024
8d90dae
refactor : refactor View
lit2020 May 5, 2024
fdc9545
style : apply google java style guide
lit2020 May 5, 2024
93d79a9
feat : 게임 종료, 재도전 선택 입력 받기
lit2020 May 5, 2024
3a7c789
feat : 게임 종료 기능 추가
lit2020 May 5, 2024
5ba1b99
fix : String 비교 오류 수정
lit2020 May 5, 2024
3b54748
chore : main 예외 출력 수정
lit2020 May 5, 2024
6865d77
fix : 정답이 바뀌지 않는 버그 수정
lit2020 May 5, 2024
fc15b34
refactor : UserInputChecker 싱글톤으로 리팩토링
lit2020 May 5, 2024
1740169
refactor : 게임 한 판과 게임 전체(series of games) 분리
lit2020 May 5, 2024
1fb92b6
fix : 사용자에 입력에 0이 포함된 경우 IllegalArgumentException
lit2020 May 5, 2024
54e0483
fix : 사용자에 입력에 0이 포함된 경우 IllegalArgumentException
lit2020 May 5, 2024
eb52d90
fix : 정답 출력되지 않도록 수정
lit2020 May 5, 2024
ce91637
docs : javadoc 작성
lit2020 May 5, 2024
85eb47c
refactor : nBall과 nStrike를 인스턴스 변수로 변경
lit2020 May 5, 2024
f323a80
chore : 초기화 최적화
lit2020 May 5, 2024
ddb0915
chore : import 최적화
lit2020 May 5, 2024
822cf04
style : formatting
lit2020 May 5, 2024
db0a3a5
test : 테스트 코드
lit2020 May 5, 2024
388c954
style : formatting
lit2020 May 5, 2024
66c4fcf
fix : 정답 출력되지 않도록 수정
lit2020 May 5, 2024
564008d
refactor : evaluator 클래스 분리
lit2020 May 6, 2024
894b3fe
test : evaluator 테스트
lit2020 May 6, 2024
8866ee6
refactor : AnswerGenerator 인터페이스 생성
lit2020 May 6, 2024
ec2fb0a
style : formatting
lit2020 May 6, 2024
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
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,18 @@
# java-baseball-precourse
# java-baseball-precourse

구현할 기능 목록
1. 정답 (서로 다른 숫자 3개로 이루어진 3자리수) 생성
2. 사용자로부터 3자리수 입력받기

다음과 같은 경우 IllegalArgumentException 발생 -> 애플리케이션 종료
1. 정수 외의 입력
2. 3자리 수 이외의 입력
3. 사용자의 입력에 0이 포함된 경우

3. 정답과 사용자입력을 비교하여 결과 계산 (낫싱, 1볼1스트라이크 등)
4. 3의 결과 출력
5. 정답(3스트라이크)인 경우 게임 종료
6. 사용자 선택 (1/ 2) 입력 받기
1. 1을 입력 받은 경우 다음 게임 시작 (반복)
2. 2를 입력받은 경우 애플리케이션 종료

15 changes: 15 additions & 0 deletions src/main/java/app/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package app;

import game.Game;

public class Application {

public static void main(String[] args) {
Game game = new Game();
try {
game.run();
} catch (IllegalArgumentException e) {
System.out.println(e);
}
}
}
7 changes: 7 additions & 0 deletions src/main/java/game/AnswerGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package game;

public interface AnswerGenerator {

String getAnswerAsString();

}
63 changes: 63 additions & 0 deletions src/main/java/game/Evaluator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package game;

public class Evaluator {

private String answer;
private int nBall;
private int nStrike;

public Evaluator() {
nBall = 0;
nStrike = 0;
}

public String getAnswer() {
return answer;
}

/**
* 상대방(컴퓨터)가 예상한 숫자(정답)을 설정한다.
*/
public void setAnswer(AnswerGenerator answerGenerator) {
answer = answerGenerator.getAnswerAsString();
}

public int getNBall() {
return nBall;
}

public int getNStrike() {
return nStrike;
}

/**
* 유저가 정답을 맞혔는지 판단한다
*
* @return 정답을 맞힌 경우 true, 아니면 false
*/
public boolean isCorrect() {
return nStrike == GameParameters.nDigit;
}

/**
* 유저의 응답을 정답과 비교하여 볼과 스트라이크의 수를 세고 nBall과 nStrike를 업데이트한다.
*
* @param userGuess 유저의 응답
*/
public int[] evaluate(String userGuess) {
nBall = 0;
nStrike = 0;
int[] ballAndStrike = new int[2];
for (int i = 0; i < GameParameters.nDigit; i++) {
int idx = answer.indexOf(userGuess.charAt(i));
if (idx == i) {
nStrike++;
} else if (idx != -1) {
nBall++;
}
}
ballAndStrike[0] = nBall;
ballAndStrike[1] = nStrike;
return ballAndStrike;
}
}
59 changes: 59 additions & 0 deletions src/main/java/game/Game.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package game;

import java.util.Objects;
import utils.UserInputChecker;
import view.View;

public class Game {

private static UserInputChecker userInputChecker = null;
private Evaluator evaluator;

public Game() {
userInputChecker = UserInputChecker.getUserInputChecker();
evaluator = new Evaluator();
}


/**
* 다음 판을 진행할 지, 게임을 종료할 지 결정한다
*
* @return 다음 판을 진행하는 경우 true, 종료하는 경우 false
*/
private boolean doNextGame() {
View.printCorrectAndGameOver();
String userChoice = View.getUserChoiceForNextGame();
userInputChecker.isValidChoice(userChoice);

return Objects.equals(userChoice, GameParameters.nextGame);
}

/**
* 한 판의 숫자야구 게임을 실행한다 유저가 정답을 맞출 때까지 응답을 입력받고 결과를 출력한다
*/
private void runSingleGame() {
String userGuess;
while (true) {
userGuess = View.getUserGuess();
userInputChecker.isValidGuess(userGuess);
evaluator.evaluate(userGuess);
View.printResult(evaluator.getNBall(), evaluator.getNStrike());

if (evaluator.isCorrect()) {
break;
}
}
}

/**
* 여러 번의 게임을 반복적으로 실행한다 매 게임이 시작될 때마다 정답을 세팅한다. 매 게임이 끝날 때마다 다음 게임을 시작하거나, 완전히 종료한다
*/
public void run() {
boolean continueGame = true;
while (continueGame) {
evaluator.setAnswer(new RandomAnswerGenerator());
runSingleGame();
continueGame = doNextGame();
}
}
}
11 changes: 11 additions & 0 deletions src/main/java/game/GameParameters.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package game;

public class GameParameters {

public static final int nDigit = 3;
public static final String nextGame = "1";
public static final String quitGame = "2";

private GameParameters() {
}
}
32 changes: 32 additions & 0 deletions src/main/java/game/RandomAnswerGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package game;

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

public class RandomAnswerGenerator implements AnswerGenerator {

private List<Integer> digits;

public RandomAnswerGenerator() {
digits = new ArrayList<>(9);
for (int i = 1; i < 10; i++) {
digits.add(i);
}
}

/**
* 1~9까지 다른 숫자 3개로 이루어진 정답을 생성한다.
*
* @return 랜덤으로 생성된 정답
*/
public String getAnswerAsString() {
String answer = "";
for (int i = 0; i < GameParameters.nDigit; i++) {
Collections.shuffle(digits);
int d = digits.remove(digits.size() - 1);
answer += d;
}
return answer;
}
}
15 changes: 15 additions & 0 deletions src/main/java/game/TestAnswerGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package game;

public class TestAnswerGenerator implements AnswerGenerator {

private final String answer;

public TestAnswerGenerator(String predefinedAnswer) {
answer = predefinedAnswer;
}

@Override
public String getAnswerAsString() {
return answer;
}
}
55 changes: 55 additions & 0 deletions src/main/java/utils/UserInputChecker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package utils;

import game.GameParameters;
import java.util.Objects;

public class UserInputChecker {

private static UserInputChecker userInputChecker = null;

private UserInputChecker() {

}

public static UserInputChecker getUserInputChecker() {
if (userInputChecker == null) {
userInputChecker = new UserInputChecker();
}
return userInputChecker;
}

/**
* 유저가 제출한 숫자의 유효성을 검증한다.
*
* @param userGuess 유저가 제출한 숫자
* @throws IllegalArgumentException 유저의 응답이 0이나 숫자가 아닌 문자를 포함하는 경우 또는 자릿수가 틀린 경우
*/
public void isValidGuess(String userGuess) throws IllegalArgumentException {
if (userGuess.length() != GameParameters.nDigit) {
throw new IllegalArgumentException();
}
char ch;
for (int i = 0; i < GameParameters.nDigit; i++) {
ch = userGuess.charAt(i);
if (!Character.isDigit(ch) || ch == '0') {
throw new IllegalArgumentException();
}
}
}

/**
* 유저가 입력한 선택(새게임 또는 종료)을 검증한다.
*
* @param userChoice 유저가 입력한 선택
* @throws IllegalArgumentException 유저가 옵션에 없는 선택을 한 경우
*/
public void isValidChoice(String userChoice) throws IllegalArgumentException {
if (Objects.equals(userChoice, GameParameters.nextGame)) {
return;
}
if (Objects.equals(userChoice, GameParameters.quitGame)) {
return;
}
throw new IllegalArgumentException();
}
}
61 changes: 61 additions & 0 deletions src/main/java/view/View.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package view;

import java.util.Scanner;

public class View {

private static final Scanner scanner = new Scanner(System.in);

private View() {

}

/**
* 유저에게 응답을 입력받는 프롬프트를 출력한다
*
* @return 유저가 입력한 응답
*/
public static String getUserGuess() {
String strUserInput;
System.out.print("숫자를 입력하세요 : ");
strUserInput = scanner.nextLine();
return strUserInput;
}

/**
* 볼과 스트라이크의 개수를 출력한다
*
* @param nBall 볼의 개수
* @param nStrike 스트라이크의 개수
*/
public static void printResult(int nBall, int nStrike) {
if (nBall == 0 && nStrike == 0) {
System.out.println("낫싱");
return;
}
if (nBall > 0) {
System.out.print(nBall + "볼");
}
if (nStrike > 0) {
System.out.print(nStrike + "스트라이크");
}
System.out.println();
}

/**
* 유저가 정답을 맞혔을 때 출력
*/
public static void printCorrectAndGameOver() {
System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료");
System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.");
}

/**
* 게임 종료 시 유저의 선택을 입력받는다 (새게임 또는 게임종료)
*
* @return 유저의 선택
*/
public static String getUserChoiceForNextGame() {
return scanner.nextLine();
}
}
40 changes: 40 additions & 0 deletions src/test/java/game/EvaluatorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package game;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class EvaluatorTest {

static Evaluator evaluator;

@BeforeAll
static void initAll() {
evaluator = new Evaluator();
AnswerGenerator answerGenerator = new TestAnswerGenerator("123");
evaluator.setAnswer(answerGenerator);
}

@DisplayName("evaluate / isCorrect 테스트")
@Test
public void evaluateTest() {
Assertions.assertArrayEquals(new int[]{0, 0}, evaluator.evaluate("456"));
Assertions.assertArrayEquals(new int[]{0, 1}, evaluator.evaluate("178"));
Assertions.assertArrayEquals(new int[]{0, 2}, evaluator.evaluate("923"));
Assertions.assertArrayEquals(new int[]{0, 3}, evaluator.evaluate("123"));
Assertions.assertArrayEquals(new int[]{1, 0}, evaluator.evaluate("289"));
Assertions.assertArrayEquals(new int[]{1, 1}, evaluator.evaluate("283"));
Assertions.assertArrayEquals(new int[]{2, 1}, evaluator.evaluate("213"));
Assertions.assertArrayEquals(new int[]{3, 0}, evaluator.evaluate("231"));
}

@DisplayName("isCorrect 테스트")
@Test
public void isCorrectTest() {
evaluator.evaluate("123");
Assertions.assertEquals(3, evaluator.getNStrike());
Assertions.assertEquals(0, evaluator.getNBall());
}
}
Loading