diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 000000000..52fd25b58 --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,16 @@ +import game.BaseballGame; +import number_generator.NumberGenerator; +import view.InputView; +import view.OutputView; + +import java.io.IOException; + +public class Main { + public static void main(String[] args) throws IOException { + InputView inputView = new InputView(); + OutputView outputView = new OutputView(); + NumberGenerator numberGenerator = new NumberGenerator(); + BaseballGame baseballGame = new BaseballGame(inputView, outputView, numberGenerator); + baseballGame.game(); + } +} diff --git a/src/main/java/checker/BaseballChecker.java b/src/main/java/checker/BaseballChecker.java new file mode 100644 index 000000000..8217413ee --- /dev/null +++ b/src/main/java/checker/BaseballChecker.java @@ -0,0 +1,41 @@ +package checker; + +public class BaseballChecker { + private static final int NUMBER_LENGTH = 3; + private CountVO countVO; + private final String targetNumber; + + public BaseballChecker(String targetNumber) { + this.targetNumber = targetNumber; + } + + public CheckEnum checkGuess(String slicedGenerated, String given) { + if (slicedGenerated.equals(given)) return CheckEnum.STRIKE; + if (this.targetNumber.contains(given)) return CheckEnum.BALL; + return CheckEnum.NOTHING; + } + + public void evaluateGuess(String guess) { + countVO = new CountVO(); + for (int i = 1; i <= NUMBER_LENGTH; i++) { + String generatedDigit = targetNumber.substring(i - 1, i); + String guessedDigit = guess.substring(i - 1, i); + CheckEnum checkEnum = this.checkGuess(generatedDigit, guessedDigit); + updateCount(checkEnum); + } + } + + private void updateCount(CheckEnum checkEnum) { + if (checkEnum == CheckEnum.STRIKE) { + countVO.increaseStrikeCount(); + return; + } + if (checkEnum == CheckEnum.BALL) { + countVO.increaseBallCount(); + } + } + + public CountVO getResult() { + return this.countVO; + } +} diff --git a/src/main/java/checker/CheckEnum.java b/src/main/java/checker/CheckEnum.java new file mode 100644 index 000000000..5fda59239 --- /dev/null +++ b/src/main/java/checker/CheckEnum.java @@ -0,0 +1,9 @@ +package checker; + +public enum CheckEnum { + STRIKE, + BALL, + NOTHING + + +} diff --git a/src/main/java/checker/CountVO.java b/src/main/java/checker/CountVO.java new file mode 100644 index 000000000..bae86b47f --- /dev/null +++ b/src/main/java/checker/CountVO.java @@ -0,0 +1,60 @@ +package checker; + +import java.util.Objects; + +public class CountVO { + private int strikeCount = 0; + private int ballCount = 0; + public int getStrikeCount() { + return strikeCount; + } + + public int getBallCount() { + return ballCount; + } + + public CountVO() { + } + + public CountVO(int strikeCount, int ballCount) { + this.strikeCount = strikeCount; + this.ballCount = ballCount; + } + + public void increaseStrikeCount(){ + this.strikeCount++; + } + + public void increaseBallCount(){ + this.ballCount++; + } + + public boolean isCorrect(){ + return this.strikeCount == 3; + } + + public boolean isNothing(){ + return this.strikeCount == 0 && this.ballCount == 0; + } + + public boolean isNoStrike(){ + return this.strikeCount == 0; + } + + public boolean isNoBall(){ + return this.ballCount == 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CountVO countVO = (CountVO) o; + return strikeCount == countVO.strikeCount && ballCount == countVO.ballCount; + } + + @Override + public int hashCode() { + return Objects.hash(strikeCount, ballCount); + } +} diff --git a/src/main/java/game/BaseballGame.java b/src/main/java/game/BaseballGame.java new file mode 100644 index 000000000..afea37478 --- /dev/null +++ b/src/main/java/game/BaseballGame.java @@ -0,0 +1,46 @@ +package game; + +import checker.BaseballChecker; +import checker.CountVO; +import number_generator.NumberGenerator; +import view.InputView; +import view.OutputView; + +import java.io.IOException; + +public class BaseballGame { + private final InputView inputView; + private final OutputView outputView; + private final NumberGenerator numberGenerator; + + public BaseballGame(InputView inputView, OutputView outputView, NumberGenerator numberGenerator) { + this.inputView = inputView; + this.outputView = outputView; + this.numberGenerator = numberGenerator; + } + + public void game() throws IOException { + outputView.printGetInputNumbers(); + guessUntilCorrect(); + outputView.moreGame(); + if (inputView.getInput()) game(); + } + + private void guessUntilCorrect() throws IOException { + String targetNumber = numberGenerator.generateNumber(); + BaseballChecker checker = new BaseballChecker(targetNumber); + guess(checker); + } + + private void guess(BaseballChecker checker) throws IOException { + CountVO countVO = new CountVO(); + while (!countVO.isCorrect()){ + String input = inputView.getNumericInput(); + if (!outputView.validCheck(input)) continue; + // 여기서 인덴트가 2가 되는데, VO가 상태를 추가로 가지게 되는 것보다는 위와 같은 형식이 더 자연스럽다고 판단함 + checker.evaluateGuess(input); + countVO = checker.getResult(); + outputView.printResult(countVO); + } + } +} diff --git a/src/main/java/number_generator/NumberGenerator.java b/src/main/java/number_generator/NumberGenerator.java new file mode 100644 index 000000000..3c73467ad --- /dev/null +++ b/src/main/java/number_generator/NumberGenerator.java @@ -0,0 +1,21 @@ +package number_generator; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class NumberGenerator { + public String generateNumber() { + List integers = IntStream.range(1, 10) + .boxed() + .collect(Collectors.toList()); + + Collections.shuffle(integers); + + return integers.stream() + .limit(3) + .map(String::valueOf) + .collect(Collectors.joining()); + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 000000000..8aff99cbd --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,42 @@ +package view; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.HashSet; +import java.util.Set; + +public class InputView { + private static final String RETRY_NUMBER = "1"; + public static final String NOT_VALID = "NOT_VALID"; + private final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + public String getNumericInput() throws IOException { + String input = br.readLine(); + if (!validateInput(input)) return NOT_VALID; + return input; + } + + public boolean validateInput(String input){ + if (input.length() != 3) return false; + try { + Integer.parseInt(input); + } catch (NumberFormatException e){ + return false; + } + return isNotDuplicatedNumber(input); + } + + private boolean isNotDuplicatedNumber(String input) { + Set charSet = new HashSet<>(); + char[] charArray = input.toCharArray(); + for (char c : charArray) { + charSet.add(c); + } + return charSet.size() == 3; + } + + public boolean getInput() throws IOException { + String given = br.readLine(); + return given.equals(RETRY_NUMBER); + } +} \ No newline at end of file diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 000000000..6a14a24a5 --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,74 @@ +package view; + +import checker.CountVO; + +public class OutputView { + private static final String PROMPT_INPUT = "숫자를 입력해주세요"; + private static final String PROMPT_VALID_INPUT = "세 개의 서로 다른 숫자를 입력하세요"; + private static final String CORRECT_MESSAGE = "3개의 숫자를 모두 맞히셨습니다! 게임 종료"; + private static final String NOTHING_MESSAGE = "낫싱"; + private static final String STRIKE_MESSAGE = "%d스트라이크"; + private static final String BALL_MESSAGE = "%d볼"; + private static final String STRIKE_AND_BALL_MESSAGE = "%d스트라이크, %d볼"; + private static final String MORE_GAME_PROMPT = "한번 더 하시려면 1, 종료하려면 2를 입력하세요"; + + public void printGetInputNumbers() { + System.out.println(PROMPT_INPUT); + } + + public void printResult(CountVO countVO) { + if (isCorrect(countVO)) { + printCorrectMessage(); + return; + } + if (countVO.isNothing()) { + printNothingMessage(); + return; + } + if (countVO.isNoStrike()) { + printBallMessage(countVO.getBallCount()); + return; + } + if (countVO.isNoBall()) { + printStrikeMessage(countVO.getStrikeCount()); + return; + } + printStrikeAndBallMessage(countVO.getStrikeCount(), countVO.getBallCount()); + } + + public boolean validCheck(String input){ + if (input.equals(InputView.NOT_VALID)){ + System.out.println(PROMPT_VALID_INPUT); + return false; + } + return true; + } + + private boolean isCorrect(CountVO countVO) { + return countVO.isCorrect(); + } + + private void printCorrectMessage() { + System.out.println(CORRECT_MESSAGE); + } + + private void printNothingMessage() { + System.out.println(NOTHING_MESSAGE); + } + + private void printStrikeMessage(int strikeCount) { + System.out.println(String.format(STRIKE_MESSAGE, strikeCount)); + } + + private void printBallMessage(int ballCount) { + System.out.println(String.format(BALL_MESSAGE, ballCount)); + } + + private void printStrikeAndBallMessage(int strikeCount, int ballCount) { + System.out.println(String.format(STRIKE_AND_BALL_MESSAGE, strikeCount, ballCount)); + } + + public void moreGame() { + System.out.println(MORE_GAME_PROMPT); + } +} \ No newline at end of file diff --git a/src/test/java/CheckerTest.java b/src/test/java/CheckerTest.java new file mode 100644 index 000000000..294ce6cc8 --- /dev/null +++ b/src/test/java/CheckerTest.java @@ -0,0 +1,57 @@ +import checker.BaseballChecker; +import checker.CheckEnum; +import checker.CountVO; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class CheckerTest { + BaseballChecker checker; + @BeforeEach + void init(){ + checker = new BaseballChecker("321"); + } + @Test + void strikeTest() { + CheckEnum checkEnum = checker.checkGuess("1", "1"); + assertThat(checkEnum).isEqualTo(CheckEnum.STRIKE); + } + + @Test + void nothingTest(){ + CheckEnum checkEnum = checker.checkGuess("1", "8"); + assertThat(checkEnum).isEqualTo(CheckEnum.NOTHING); + } + + @Test + void ballTest(){ + CheckEnum checkEnum = checker.checkGuess("321", "1"); + assertThat(checkEnum).isEqualTo(CheckEnum.BALL); + } + + @Test + void nothing_ofAll(){ + checker.evaluateGuess( "789"); + assertThat(checker.getResult()).isEqualTo(new CountVO(0, 0)); + + } + + @Test + void one_ball_one_strike() { + checker.evaluateGuess( "316"); + assertThat(checker.getResult()).isEqualTo(new CountVO(1, 1)); + } + + @Test + void three_strike(){ + checker.evaluateGuess( "321"); + assertThat(checker.getResult()).isEqualTo(new CountVO(3, 0)); + } + + @Test + void three_ball(){ + checker.evaluateGuess("132"); + assertThat(checker.getResult()).isEqualTo(new CountVO(0, 3)); + } +} diff --git a/src/test/java/NumberGeneratorTest.java b/src/test/java/NumberGeneratorTest.java new file mode 100644 index 000000000..f9699a4a8 --- /dev/null +++ b/src/test/java/NumberGeneratorTest.java @@ -0,0 +1,31 @@ +import number_generator.NumberGenerator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class NumberGeneratorTest { + NumberGenerator numberGenerator; + @BeforeEach + void init(){ + numberGenerator = new NumberGenerator(); + } + @Test + void generate_length3_string(){ + String number = numberGenerator.generateNumber(); + assertThat(number).hasSize(3); + } + + @Test + void notDuplicatedTest(){ + String generateNumber = numberGenerator.generateNumber(); + Set characterSet = new HashSet<>(); + for (char c : generateNumber.toCharArray()) { + characterSet.add(c); + } + assertThat(characterSet).hasSize(3); + } +} diff --git a/src/test/java/study/StringTest.java b/src/test/java/study/StringTest.java deleted file mode 100644 index 43e47d90b..000000000 --- a/src/test/java/study/StringTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package study; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class StringTest { - @Test - void replace() { - String actual = "abc".replace("b", "d"); - assertThat(actual).isEqualTo("adc"); - } -}