diff --git a/src/main/java/baseball/App.java b/src/main/java/baseball/App.java new file mode 100644 index 0000000..c22eebd --- /dev/null +++ b/src/main/java/baseball/App.java @@ -0,0 +1,20 @@ +package baseball; + +import java.util.Scanner; + +public class App { + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + Game game = new Game(); + + while (true) { + game.start(); + System.out.println("게임을 새로 시작은 1, 종료 2를 입력하세요."); + String choice = scanner.nextLine().trim(); + if (!choice.equals("1")) { + System.out.println("게임 종료"); + break; + } + } + } +} diff --git a/src/main/java/baseball/Game.java b/src/main/java/baseball/Game.java new file mode 100644 index 0000000..93225bf --- /dev/null +++ b/src/main/java/baseball/Game.java @@ -0,0 +1,76 @@ +package baseball; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class Game { + private List answer; + + public Game() { + this.answer = generateRandomNumbers(); + } + + public Game(List answer) { + this.answer = answer; + } + + public void start() { + boolean isCorrect = false; + while (!isCorrect) { + try { + List guess = InputView.getUserInput(); + Result result = checkGuess(guess); + ResultView.printResult(result); + isCorrect = result.isCorrect(); + } catch (IllegalArgumentException e) { + System.out.println("입력 오류: " + e.getMessage()); + } + } + System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료"); + } + + private List generateRandomNumbers() { + List numbers = new ArrayList<>(); + Random random = new Random(); + while (numbers.size() < 3) { + int num = random.nextInt(9) + 1; + if (!numbers.contains(num)) { + numbers.add(num); + } + } + return numbers; + } + + public Result checkGuess(List guess) { + validateInput(guess); + + int strike = 0; + int ball = 0; + for (int i = 0; i < 3; i++) { + if (guess.get(i).equals(answer.get(i))) { + strike++; + } + if (!guess.get(i).equals(answer.get(i)) && answer.contains(guess.get(i))) { + ball++; + } + } + return new Result(strike, ball); + } + + private void validateInput(List guess) { + if (guess.size() != 3) { + throw new IllegalArgumentException("숫자 3개를 정확히 입력해야 합니다."); + } + if (guess.stream().distinct().count() != 3) { + throw new IllegalArgumentException("각 숫자는 중복되지 않아야 합니다."); + } + if (!guess.stream().allMatch(n -> n >= 1 && n <= 9)) { + throw new IllegalArgumentException("모든 숫자는 1에서 9 사이여야 합니다."); + } + } + + public List getAnswer() { + return new ArrayList<>(answer); + } +} diff --git a/src/main/java/baseball/InputView.java b/src/main/java/baseball/InputView.java new file mode 100644 index 0000000..6c15d92 --- /dev/null +++ b/src/main/java/baseball/InputView.java @@ -0,0 +1,67 @@ +package baseball; + +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +public class InputView { + + private static final Scanner scanner = new Scanner(System.in); + + public static List getUserInput() { + while (true) { + try { + String input = getInputFromUser(); + validateRawInput(input); + List numbers = extractNumbers(input); + validateNumbers(numbers); + return numbers; + } catch (IllegalArgumentException e) { + System.out.println("ERROR : " + e.getMessage()); + } + } + } + + private static String getInputFromUser() { + System.out.print("숫자를 입력해 주세요(1에서 9 사이의 숫자 3개): "); + return scanner.nextLine().trim(); + } + + static void validateRawInput(String input) { + if (input.isEmpty()) { + throw new IllegalArgumentException("입력값이 비어있습니다. 숫자 3개를 입력하세요."); + } + if (input.length() != 3) { + throw new IllegalArgumentException("입력값은 정확히 3자리여야 합니다."); + } + if (!input.matches("[1-9]+")) { // 숫자가 아닌 문자나 특수기호를 처리 + throw new IllegalArgumentException("입력값에는 1에서 9 사이의 숫자만 포함되어야 합니다."); + } + } + + static List extractNumbers(String input) { + List numbers = new ArrayList<>(); + + // 입력에서 숫자만 추출하고 공백은 무시 + for (char c : input.toCharArray()) { + if (Character.isDigit(c)) { + int num = Character.getNumericValue(c); + numbers.add(num); + } + } + + return numbers; + } + + static void validateNumbers(List numbers) { + if (numbers.size() != 3) { + throw new IllegalArgumentException("숫자 3개를 정확히 입력해야 합니다."); + } + if (numbers.stream().distinct().count() != 3) { + throw new IllegalArgumentException("각 숫자는 중복되지 않아야 합니다."); + } + if (!numbers.stream().allMatch(n -> n >= 1 && n <= 9)) { + throw new IllegalArgumentException("모든 숫자는 1에서 9 사이여야 합니다."); + } + } +} diff --git a/src/main/java/baseball/Result.java b/src/main/java/baseball/Result.java new file mode 100644 index 0000000..f2809f5 --- /dev/null +++ b/src/main/java/baseball/Result.java @@ -0,0 +1,23 @@ +package baseball; + +public class Result { + private final int strike; + private final int ball; + + public Result(int strike, int ball) { + this.strike = strike; + this.ball = ball; + } + + public int getStrike() { + return strike; + } + + public int getBall() { + return ball; + } + + public boolean isCorrect() { + return strike == 3; + } +} diff --git a/src/main/java/baseball/ResultView.java b/src/main/java/baseball/ResultView.java new file mode 100644 index 0000000..6a0fcf1 --- /dev/null +++ b/src/main/java/baseball/ResultView.java @@ -0,0 +1,11 @@ +package baseball; + +public class ResultView { + public static void printResult(Result result) { + if (result.getStrike() == 0 && result.getBall() == 0) { + System.out.println("낫싱"); + } else { + System.out.println(result.getBall() + "볼 " + result.getStrike() + "스트라이크"); + } + } +} diff --git a/src/test/java/baseball/GameTest.java b/src/test/java/baseball/GameTest.java new file mode 100644 index 0000000..0fd307e --- /dev/null +++ b/src/test/java/baseball/GameTest.java @@ -0,0 +1,37 @@ +package baseball; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class GameTest { + + @Test + @DisplayName("정상적인 랜덤 숫자 생성 테스트") + public void testGenerateRandomNumbers() { + Game game = new Game(); + List numbers = game.getAnswer(); + + assertEquals(3, numbers.size(), "숫자 개수가 3개여야 됩니다."); + assertTrue(numbers.stream().allMatch(n -> n >= 1 && n <= 9), + "모든 숫자는 1에서 9 사이"); + assertEquals(3, numbers.stream().distinct().count(), + "숫자는 중복되지 않됨."); + } + + @Test + @DisplayName("정답을 맞췄을 때 3 스트라이크 확인") + public void testCheckGuessCorrect() { + Game game = new Game(List.of(4, 2, 5)); + Result result = game.checkGuess(List.of(4, 2, 5)); + + assertEquals(3, result.getStrike(), "3 스트라이크임."); + assertEquals(0, result.getBall(), "볼이 없음."); + assertTrue(result.isCorrect(), "정답을 맞췄으므로 게임이 종료."); + } + + +} diff --git a/src/test/java/baseball/InputViewTest.java b/src/test/java/baseball/InputViewTest.java new file mode 100644 index 0000000..951074a --- /dev/null +++ b/src/test/java/baseball/InputViewTest.java @@ -0,0 +1,89 @@ +package baseball; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class InputViewTest { + + @Test + @DisplayName("정상적인 입력 테스트") + public void testValidInput() { + // 사용자 입력을 시뮬레이션 (Scanner 대신 직접 입력 전달) + String input = "123"; + List numbers = InputView.extractNumbers(input); + + assertEquals(3, numbers.size(), "숫자 개수가 3개여야 합니다."); + assertTrue(numbers.containsAll(List.of(1, 2, 3)), "입력된 숫자는 1, 2, 3이어야 합니다."); + } + + @Test + @DisplayName("빈 입력값 테스트") + public void testEmptyInputThrowsException() { + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> InputView.validateRawInput("") + ); + assertEquals("입력값이 비어있습니다. 숫자 3개를 입력하세요.", exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {"12", "1234", "12a", "1,2,3", "1 2 3"}) + @DisplayName("잘못된 입력값에 대한 테스트") + public void testInvalidInputThrowsException(String input) { + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> InputView.validateRawInput(input) + ); + assertTrue(exception.getMessage().startsWith("입력값은 정확히") || + exception.getMessage().startsWith("입력값에는"), + "예외 메시지가 정확히"); + } + + @Test + @DisplayName("중복된 숫자 입력 테스트") + public void testDuplicateNumbersThrowsException() { + List numbers = List.of(1, 1, 2); + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> InputView.validateNumbers(numbers) + ); + assertEquals("각 숫자는 중복되지 않아야 합니다.", exception.getMessage()); + } + + @Test + @DisplayName("범위 밖의 숫자 입력 테스트") + public void testOutOfRangeNumbersThrowsException() { + List numbers = List.of(1, 10, 2); + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> InputView.validateNumbers(numbers) + ); + assertEquals("엥간하면 0이하 10 이상은 하지 말자", exception.getMessage()); + } + + @Test + @DisplayName("공백이 포함된 입력 처리 테스트") + public void testInputWithSpaces() { + String input = " 1 2 3 "; + List numbers = InputView.extractNumbers(input); + + assertEquals(3, numbers.size(), "숫자 개수가 3개여야 합니다."); + assertTrue(numbers.containsAll(List.of(1, 2, 3)), "공백 처리 쏘 괜츈"); + } + + @Test + @DisplayName("특수문자 포함된 입력 처리 테스트") + public void testInputWithSpecialCharacters() { + String input = "1,2,3"; + List numbers = InputView.extractNumbers(input); + + assertEquals(3, numbers.size(), "숫자 개수가 3개여야 합니다."); + assertTrue(numbers.containsAll(List.of(1, 2, 3)), "특수 문자 X , 숫자만 가능 합니다."); + } +} diff --git a/src/test/java/study/StringTest.java b/src/test/java/study/StringTest.java deleted file mode 100644 index 43e47d9..0000000 --- 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"); - } -}