From 737b187fa46bf72a9dc8ef3f1b6a30dc12468834 Mon Sep 17 00:00:00 2001 From: JYP Date: Mon, 27 Oct 2025 23:51:55 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EA=B8=B0=EB=8A=A5=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/racingcar/Application.java | 95 ++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index a17a52e724..75e35d8430 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,7 +1,102 @@ package racingcar; +import java.util.ArrayList; + public class Application { public static void main(String[] args) { // TODO: 프로그램 구현 + + // 입력받기 + System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉽표(,) 기준으로 구분)"); + String[] cars = camp.nextstep.edu.missionutils.Console.readLine().split(","); + + System.out.println("시도할 횟수는 몇 회인가요?"); + int maxTryCnt = Integer.parseInt(camp.nextstep.edu.missionutils.Console.readLine()); + int currentTryCnt = 0; + int[] currentRecord = new int[]{0, 0, 0}; + + System.out.println(); + System.out.println("실행결과"); + + testValidInput(cars); + + while (currentTryCnt < maxTryCnt) { + raceOneTurn(cars, currentRecord); + currentTryCnt++; + System.out.println(); + } + + int[] maxIndices = findMaxIndices(currentRecord); + + ArrayList winners = findWinners(cars, currentRecord); + + System.out.println("최종 우승자 : " + String.join(", ", winners)); + } + + public static void testValidInput(String[] cars) { + + for (int i = 0; i < cars.length; i++) { + if (cars[i].length() > 5) { + throw new IllegalArgumentException("입력이올바르지 않습니다"); + } + } + } + + static void raceOneTurn(String[] cars, int[] currentRecord) { + for (int i = 0; i < cars.length; i++) { + moveAndPrintCar(cars[i], currentRecord, i); + } } + + static void moveAndPrintCar(String carName, int[] currentRecord, int index) { + int randNum = camp.nextstep.edu.missionutils.Randoms.pickNumberInRange(0, 9); + if (randNum >= 4) { + currentRecord[index]++; + } + System.out.println(carName + " : " + "-".repeat(currentRecord[index])); + } + + public static int[] findMaxIndices(int[] arr) { + if (arr == null || arr.length == 0) { + return new int[0]; + } + + // 1. 최댓값 찾기 + int maxValue = arr[0]; + for (int num : arr) { + maxValue = Math.max(maxValue, num); + } + + // 2. 최댓값의 개수 세기 + int count = 0; + for (int num : arr) { + if (num == maxValue) { + count++; + } + } + + // 3. 결과 배열 생성 및 최댓값의 인덱스 저장 + int[] maxIndices = new int[count]; + int index = 0; + for (int i = 0; i < arr.length; i++) { + if (arr[i] == maxValue) { + maxIndices[index++] = i; + } + } + + return maxIndices; + } + + private static ArrayList findWinners(String[] cars, int[] currentRecord) { + int[] maxIndices = findMaxIndices(currentRecord); + ArrayList winners = new ArrayList<>(); + + for (int i = 0; i < maxIndices.length; i++) { + winners.add(cars[maxIndices[i]]); + } + + return winners; + } + + } From 6d1588c3bf54fdcdc5a477beebf883383a583061 Mon Sep 17 00:00:00 2001 From: JYP Date: Mon, 27 Oct 2025 23:56:51 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1,=20=EC=84=B8=EB=B6=80?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=88=98=EC=A0=95=20=EC=8B=A4=ED=8C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/racingcar/ApplicationTest.java | 114 +++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/src/test/java/racingcar/ApplicationTest.java b/src/test/java/racingcar/ApplicationTest.java index 1d35fc33fe..e56d2dea39 100644 --- a/src/test/java/racingcar/ApplicationTest.java +++ b/src/test/java/racingcar/ApplicationTest.java @@ -1,12 +1,21 @@ package racingcar; import camp.nextstep.edu.missionutils.test.NsTest; +import java.util.ArrayList; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import static camp.nextstep.edu.missionutils.test.Assertions.assertRandomNumberInRangeTest; import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Fail.fail; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; class ApplicationTest extends NsTest { private static final int MOVING_FORWARD = 4; @@ -31,6 +40,111 @@ class ApplicationTest extends NsTest { ); } + @Nested + @DisplayName("자동차 이름 유효성 검사 (testValidInput)") + class NameValidationTest { + + @Test + @DisplayName("각 자동차 이름이 5자 이하이면 예외가 발생하지 않는다") + void validCarNames_doNotThrow() { + String[] cars = {"pobi", "woni", "jun"}; + assertDoesNotThrow(() -> Application.testValidInput(cars)); + } + + @Test + @DisplayName("자동차 이름이 6자 이상이면 IllegalArgumentException 발생") + void tooLongName_throwsException() { + String[] cars = {"pobi", "superlong"}; // superlong 은 9글자 + IllegalArgumentException ex = assertThrows( + IllegalArgumentException.class, + () -> Application.testValidInput(cars) + ); + assertEquals("입력이올바르지 않습니다", ex.getMessage()); + } + } + + @Nested + @DisplayName("우승자 인덱스 찾기 (findMaxIndices)") + class FindMaxIndicesTest { + + @Test + @DisplayName("여러 값 중 최댓값의 인덱스를 모두 반환한다") + void returnsAllMaxIndices() { + int[] record = {3, 5, 5, 2}; + int[] result = Application.findMaxIndices(record); + + // 기대: 최댓값 5는 index 1과 2 + assertArrayEquals(new int[]{1, 2}, result); + } + + @Test + @DisplayName("배열이 비어있거나 null이면 빈 배열 반환") + void emptyOrNullInput() { + assertArrayEquals(new int[]{}, Application.findMaxIndices(new int[]{})); + assertArrayEquals(new int[]{}, Application.findMaxIndices(null)); + } + + @Test + @DisplayName("모든 값이 동일하면 모든 인덱스를 반환") + void allSameValues() { + int[] record = {4, 4, 4}; + int[] result = Application.findMaxIndices(record); + + assertArrayEquals(new int[]{0, 1, 2}, result); + } + } + + @Nested + @DisplayName("우승자 찾기 (findWinners)") + class FindWinnersTest { + + @Test + @DisplayName("최댓값을 가진 자동차 이름만 반환한다") + void winnersByRecord() { + String[] cars = {"pobi", "woni", "jun"}; + int[] record = {4, 7, 7}; // woni와 jun이 공동 1등 + + ArrayList winners = callFindWinners(cars, record); + + assertEquals(2, winners.size()); + assertTrue(winners.contains("woni")); + assertTrue(winners.contains("jun")); + } + + @Test + @DisplayName("단독 우승자가 있을 경우 한 명만 반환한다") + void singleWinner() { + String[] cars = {"pobi", "woni", "jun"}; + int[] record = {1, 5, 3}; // woni 우승 + + ArrayList winners = callFindWinners(cars, record); + + assertEquals(1, winners.size()); + assertEquals("woni", winners.get(0)); + } + + // findWinners가 private 이라 직접 못 부르므로 + // 동일한 로직을 우회 호출 또는 간접 테스트(메인 로직)를 하기 어렵다. + // 여기서는 테스트 편의를 위해 같은 패키지 내에서 + // 리플렉션으로 private 메서드를 호출하는 헬퍼를 만든다. + private ArrayList callFindWinners(String[] cars, int[] currentRecord) { + try { + var method = Application.class.getDeclaredMethod( + "findWinners", String[].class, int[].class + ); + method.setAccessible(true); + @SuppressWarnings("unchecked") + ArrayList winners = + (ArrayList) method.invoke(null, cars, currentRecord); + return winners; + } catch (Exception e) { + fail("리플렉션 호출 실패: " + e.getMessage()); + return null; // unreachable + } + } + } + + @Override public void runMain() { Application.main(new String[]{});