diff --git a/docs/README.md b/docs/README.md index e69de29bb2..780dba8013 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,26 @@ +# 숫자야구 객체지향 사고 연습 (Bulls and Cows) + +메시지 : 숫자 3개와 정답을 비교해 결과를 반환해라 +게임 매니저? 심판? 컴퓨터? +-> 컴퓨터가 정답을 가지고 있으니 컴퓨터가 판단하는게 맞을듯 + +그럼 컴퓨터가 정답을 비교해 결과를 반환하기 위해서 해야하는 일은? +일단 정답을 생성해서 가지고 있어야 한다. + +그럼 컴퓨터의 정답을 생성하는 객체가 필요할까? 컴퓨터는 랜덤한 값을 생성할 책임을 가지나? +Nope, 랜덤한 3개의 값을 생성하는 객체를 만들자. + +정답을 만들어서 컴퓨터를 생성했다고 가정해보자. +입력으로 들어오는 숫자와 정답을 어떻게 비교하지? +입력으로 들어오는 정보를 정답과 같은 데이터 타입으로 변환해주는 객체를 만들자 + +비교한 결과는 어떻게 생성하지? +결과를 담을 객체가 필요하다. -> 결과에 따라 다시 입력을 받을지 끝낼지 결정하자 + + +### 필요 객체 + +- 컴퓨터 - Computer +- 정답 생성기 - RandomNumberGenerator +- 입력 변환기 - InputNumberGenerator +- 비교 결과 - ComparisonResult \ No newline at end of file diff --git a/src/main/java/baseball/Application.java b/src/main/java/baseball/Application.java index dd95a34214..750dcd96cd 100644 --- a/src/main/java/baseball/Application.java +++ b/src/main/java/baseball/Application.java @@ -1,7 +1,11 @@ package baseball; +import baseball.controller.BullsAndCowsController; +import baseball.view.InputView; +import baseball.view.OutputView; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + new BullsAndCowsController(new InputView(), new OutputView()).run(); } } diff --git a/src/main/java/baseball/controller/BullsAndCowsController.java b/src/main/java/baseball/controller/BullsAndCowsController.java new file mode 100644 index 0000000000..f9adb855c9 --- /dev/null +++ b/src/main/java/baseball/controller/BullsAndCowsController.java @@ -0,0 +1,50 @@ +package baseball.controller; + +import baseball.domain.ComparisonResult; +import baseball.domain.Computer; +import baseball.domain.InputNumberGenerator; +import baseball.domain.RandomNumberGenerator; +import baseball.view.InputView; +import baseball.view.OutputView; +import java.util.List; + +public class BullsAndCowsController { + + private final InputView inputView; + private final OutputView outputView; + + + public BullsAndCowsController(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void run() { + outputView.printStartMessage(); + int restart; + do { + playGame(); + outputView.printRestartMessage(); + restart = inputView.scanRestartOrEnd(); + } while (restart == 1); + } + + private void playGame() { + Computer computer = initComputer(); + ComparisonResult result; + do { + outputView.printNumberRequestMessage(); + result = computer.compare(scanInputNumber()); + outputView.printResult(result); + } while (!result.isThreeStrike()); + outputView.printEndGameMessage(); + } + + private Computer initComputer() { + return new Computer(RandomNumberGenerator.generate()); + } + + private List scanInputNumber() { + return InputNumberGenerator.generate(inputView.scanThreeDigit()); + } +} diff --git a/src/main/java/baseball/domain/ComparisonResult.java b/src/main/java/baseball/domain/ComparisonResult.java new file mode 100644 index 0000000000..bd4d6c7099 --- /dev/null +++ b/src/main/java/baseball/domain/ComparisonResult.java @@ -0,0 +1,23 @@ +package baseball.domain; + +public record ComparisonResult(int strike, int ball) { + + public Boolean isThreeStrike() { + return strike == 3; + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + if (ball != 0) { + stringBuilder.append(ball + "볼 "); + } + if (strike != 0) { + stringBuilder.append(strike + "스트라이크"); + } + if (ball == 0 && strike == 0) { + stringBuilder.append("낫싱"); + } + return stringBuilder.toString(); + } +} diff --git a/src/main/java/baseball/domain/Computer.java b/src/main/java/baseball/domain/Computer.java new file mode 100644 index 0000000000..d7f2d64739 --- /dev/null +++ b/src/main/java/baseball/domain/Computer.java @@ -0,0 +1,29 @@ +package baseball.domain; + +import java.util.List; +import java.util.stream.IntStream; + +public class Computer { + + private final List answer; + + public Computer(List answer) { + this.answer = answer; + } + + public ComparisonResult compare(List input) { + return new ComparisonResult(findStrike(input), findBall(input)); + } + + private int findStrike(List input) { + return (int) IntStream.range(0, 3) + .filter(i -> answer.get(i).equals(input.get(i))) + .count(); + } + + private int findBall(List input) { + return (int) IntStream.range(0, 3) + .filter(i -> !answer.get(i).equals(input.get(i)) && answer.contains(input.get(i))) + .count(); + } +} diff --git a/src/main/java/baseball/domain/InputNumberGenerator.java b/src/main/java/baseball/domain/InputNumberGenerator.java new file mode 100644 index 0000000000..acb981004a --- /dev/null +++ b/src/main/java/baseball/domain/InputNumberGenerator.java @@ -0,0 +1,14 @@ +package baseball.domain; + +import java.util.List; +import java.util.stream.Collectors; + +public class InputNumberGenerator { + + public static List generate(String input) { + + return input.chars() + .mapToObj(Character::getNumericValue) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/baseball/domain/RandomNumberGenerator.java b/src/main/java/baseball/domain/RandomNumberGenerator.java new file mode 100644 index 0000000000..de7b27fa1b --- /dev/null +++ b/src/main/java/baseball/domain/RandomNumberGenerator.java @@ -0,0 +1,22 @@ +package baseball.domain; + +import camp.nextstep.edu.missionutils.Randoms; +import java.util.ArrayList; +import java.util.List; + +public class RandomNumberGenerator { + + private static final int startRange = 1; + private static final int endRange = 9; + + public static List generate(){ + List threeDigitNumber = new ArrayList<>(); + while (threeDigitNumber.size() < 3) { + int randomNumber = Randoms.pickNumberInRange(startRange, endRange); + if (!threeDigitNumber.contains(randomNumber)) { + threeDigitNumber.add(randomNumber); + } + } + return threeDigitNumber; + } +} diff --git a/src/main/java/baseball/view/InputView.java b/src/main/java/baseball/view/InputView.java new file mode 100644 index 0000000000..3b50ef1b2c --- /dev/null +++ b/src/main/java/baseball/view/InputView.java @@ -0,0 +1,35 @@ +package baseball.view; + +import java.util.Scanner; +import java.util.regex.Pattern; + +public class InputView { + + private Scanner scanner = new Scanner(System.in); + private final String threeDigitPattern = "^\\d{3}$"; + private final String oneOrTwoPattern = "^(1|2)$"; + + public String scanThreeDigit() { + String input = scanner.next(); + isNotThreeDigitNumber(input); + return input; + } + + public int scanRestartOrEnd() { + String input = scanner.next(); + isNotOneOrTwo(input); + return Integer.parseInt(input); + } + + private void isNotThreeDigitNumber(String input) { + if (!Pattern.matches(threeDigitPattern, input)) { + throw new NumberFormatException("3자리 숫자만 입력되어야 합니다."); + } + } + + private void isNotOneOrTwo(String input) { + if (!Pattern.matches(oneOrTwoPattern, input)) { + throw new NumberFormatException("1이나 2만 입력되어야 합니다."); + } + } +} diff --git a/src/main/java/baseball/view/OutputView.java b/src/main/java/baseball/view/OutputView.java new file mode 100644 index 0000000000..149eaee9d9 --- /dev/null +++ b/src/main/java/baseball/view/OutputView.java @@ -0,0 +1,26 @@ +package baseball.view; + +import baseball.domain.ComparisonResult; + +public class OutputView { + + public void printStartMessage() { + System.out.println("숫자 야구 게임을 시작합니다."); + } + + public void printNumberRequestMessage() { + System.out.print("숫자를 입력해주세요 : "); + } + + public void printResult(ComparisonResult result) { + System.out.println(result); + } + + public void printEndGameMessage() { + System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료"); + } + + public void printRestartMessage() { + System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."); + } +} diff --git a/src/test/java/baseball/domain/ComparisonResultTest.java b/src/test/java/baseball/domain/ComparisonResultTest.java new file mode 100644 index 0000000000..53832af72e --- /dev/null +++ b/src/test/java/baseball/domain/ComparisonResultTest.java @@ -0,0 +1,37 @@ +package baseball.domain; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ComparisonResultTest { + @Test + @DisplayName("입력과 정답을 받아 스트라이크 볼의 개수를 가진다.") + void generate() { + int strike = 1; + int ball = 1; + + Assertions.assertDoesNotThrow(() -> + new ComparisonResult(strike, ball)); + + } + + @Test + @DisplayName("3스트라이크일 때 true를 반환한다.") + void ThreeStrikeTrue() { + int strike = 3; + int ball = 0; + ComparisonResult result = new ComparisonResult(strike, ball); + Assertions.assertEquals(result.isThreeStrike(), true); + } + + @Test + @DisplayName("3스트라이크가 아니면 false를 반환한다.") + void ThreeStrikeFalse() { + int strike = 2; + int ball = 1; + ComparisonResult result = new ComparisonResult(strike, ball); + Assertions.assertEquals(result.isThreeStrike(), false); + } + +} \ No newline at end of file diff --git a/src/test/java/baseball/domain/ComputerTest.java b/src/test/java/baseball/domain/ComputerTest.java new file mode 100644 index 0000000000..036ca72ca2 --- /dev/null +++ b/src/test/java/baseball/domain/ComputerTest.java @@ -0,0 +1,45 @@ +package baseball.domain; + +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ComputerTest { + + private Computer computer; + + @BeforeEach + void before() { + computer = new Computer(List.of(1, 2, 3)); + } + + @Test + @DisplayName("입력 리스트를 받아 2볼 1스트라이크를 반환한다.") + void compare2Ball1Strike() throws Exception { + List input = List.of(1, 3, 2); + + ComparisonResult result = computer.compare(input); + Assertions.assertEquals(result.toString(), "2볼 1스트라이크"); + } + + @Test + @DisplayName("입력을 받아 낫싱을 출력한다.") + void compareNothing() throws Exception { + List input = List.of(4, 5, 6); + + ComparisonResult result = computer.compare(input); + Assertions.assertEquals(result.toString(), "낫싱"); + } + + + @Test + @DisplayName("입력을 받아 3스트라이크를 출력한다.") + void compare3Strike() throws Exception { + List input = List.of(1, 2, 3); + + ComparisonResult result = computer.compare(input); + Assertions.assertEquals(result.toString(), "3스트라이크"); + } +} \ No newline at end of file diff --git a/src/test/java/baseball/domain/InputNumberGeneratorTest.java b/src/test/java/baseball/domain/InputNumberGeneratorTest.java new file mode 100644 index 0000000000..c1464da6e9 --- /dev/null +++ b/src/test/java/baseball/domain/InputNumberGeneratorTest.java @@ -0,0 +1,27 @@ +package baseball.domain; + +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class InputNumberGeneratorTest { + + @ParameterizedTest + @DisplayName("입력으로 들어온 문자열을 리스트 형태로 반환한다.") + @ValueSource(strings = {"123", "456", "789"}) + void inputThreeDigit(String input) throws Exception { + List threeDigitNumber = InputNumberGenerator.generate(input); + + Assertions.assertAll( + () -> Assertions.assertEquals( + Character.getNumericValue(input.charAt(0)), threeDigitNumber.get(0)), + () -> Assertions.assertEquals( + Character.getNumericValue(input.charAt(1)), threeDigitNumber.get(1)), + () -> Assertions.assertEquals( + Character.getNumericValue(input.charAt(2)), threeDigitNumber.get(2)) + ); + } + +} \ No newline at end of file diff --git a/src/test/java/baseball/domain/RandomNumberGeneratorTest.java b/src/test/java/baseball/domain/RandomNumberGeneratorTest.java new file mode 100644 index 0000000000..db1f6055a9 --- /dev/null +++ b/src/test/java/baseball/domain/RandomNumberGeneratorTest.java @@ -0,0 +1,20 @@ +package baseball.domain; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class RandomNumberGeneratorTest { + + @Test + @DisplayName("3자리 랜덤한 숫자를 리스트로 반환") + void randomThreeDigit() throws Exception { + List randomThreeDigit = RandomNumberGenerator.generate(); + + Assertions.assertEquals(randomThreeDigit.size(), 3); + } + +} \ No newline at end of file