Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
95 changes: 95 additions & 0 deletions src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
@@ -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<String> 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<String> findWinners(String[] cars, int[] currentRecord) {
int[] maxIndices = findMaxIndices(currentRecord);
ArrayList<String> winners = new ArrayList<>();

for (int i = 0; i < maxIndices.length; i++) {
winners.add(cars[maxIndices[i]]);
}

return winners;
}


}
114 changes: 114 additions & 0 deletions src/test/java/racingcar/ApplicationTest.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<String> 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<String> winners = callFindWinners(cars, record);

assertEquals(1, winners.size());
assertEquals("woni", winners.get(0));
}

// findWinners가 private 이라 직접 못 부르므로
// 동일한 로직을 우회 호출 또는 간접 테스트(메인 로직)를 하기 어렵다.
// 여기서는 테스트 편의를 위해 같은 패키지 내에서
// 리플렉션으로 private 메서드를 호출하는 헬퍼를 만든다.
private ArrayList<String> callFindWinners(String[] cars, int[] currentRecord) {
try {
var method = Application.class.getDeclaredMethod(
"findWinners", String[].class, int[].class
);
method.setAccessible(true);
@SuppressWarnings("unchecked")
ArrayList<String> winners =
(ArrayList<String>) method.invoke(null, cars, currentRecord);
return winners;
} catch (Exception e) {
fail("리플렉션 호출 실패: " + e.getMessage());
return null; // unreachable
}
}
}


@Override
public void runMain() {
Application.main(new String[]{});
Expand Down