From 02ecbb25a7effce7856387c8f717b96fd54092cb Mon Sep 17 00:00:00 2001 From: haha4825 Date: Mon, 27 Oct 2025 22:34:50 +0900 Subject: [PATCH 01/11] feat(racingcargame): Add car name input feature --- src/main/java/racingcar/RacingCarGame.java | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/main/java/racingcar/RacingCarGame.java diff --git a/src/main/java/racingcar/RacingCarGame.java b/src/main/java/racingcar/RacingCarGame.java new file mode 100644 index 0000000000..49aae3a711 --- /dev/null +++ b/src/main/java/racingcar/RacingCarGame.java @@ -0,0 +1,26 @@ +package racingcar; + +import camp.nextstep.edu.missionutils.Console; +import java.util.HashMap; +import java.util.Map; + +public class RacingCarGame { + Map carMap = new HashMap<>(); + + public Map getCarMap() { + return carMap; + } + + public void play() { + readCarNames(); + } + + public void readCarNames() { + System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)\n"); + String input = Console.readLine(); + String[] carNames = input.split(","); + for (String carName : carNames) { + carMap.put(carName, 0); + } + } +} From 08eb02d4369fe0b3631f49232128e426b2dbf906 Mon Sep 17 00:00:00 2001 From: haha4825 Date: Mon, 27 Oct 2025 22:51:13 +0900 Subject: [PATCH 02/11] feat(racingcargametest): Test car name input feature --- .../java/racingcar/RacingCarGameTest.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/test/java/racingcar/RacingCarGameTest.java diff --git a/src/test/java/racingcar/RacingCarGameTest.java b/src/test/java/racingcar/RacingCarGameTest.java new file mode 100644 index 0000000000..5d2cc54bdc --- /dev/null +++ b/src/test/java/racingcar/RacingCarGameTest.java @@ -0,0 +1,52 @@ +package racingcar; + + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import camp.nextstep.edu.missionutils.Console; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.Map; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class RacingCarGameTest { + + private PrintStream standardOut; + private OutputStream captor; + + RacingCarGame racingCarGame = new RacingCarGame(); + + @BeforeEach + void beforeEach() { + this.standardOut = System.out; + this.captor = new ByteArrayOutputStream(); + System.setOut(new PrintStream(this.captor)); + } + + @Test + void 읽기_테스트() { + setInput("pobi,woni"); + racingCarGame.readCarNames(); + Assertions.assertThat(output()).contains("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); + Map carMap = racingCarGame.getCarMap(); + Assertions.assertThat(carMap).hasSize(2); + Assertions.assertThat(carMap).containsEntry("pobi", 0); + Assertions.assertThat(carMap).containsEntry("woni", 0); + System.setOut(this.standardOut); + } + + + private static void setInput(String ... args) { + byte[] buf = String.join("\n", args).getBytes(); + System.setIn(new ByteArrayInputStream(buf)); + } + + private final String output() { + return this.captor.toString().trim(); + } +} From ec84ef27c350b91688a19fcf0a6d5482d8782704 Mon Sep 17 00:00:00 2001 From: haha4825 Date: Mon, 27 Oct 2025 22:52:34 +0900 Subject: [PATCH 03/11] feat(racingcargame): Validate car name --- src/main/java/racingcar/RacingCarGame.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/racingcar/RacingCarGame.java b/src/main/java/racingcar/RacingCarGame.java index 49aae3a711..0f39779074 100644 --- a/src/main/java/racingcar/RacingCarGame.java +++ b/src/main/java/racingcar/RacingCarGame.java @@ -20,7 +20,14 @@ public void readCarNames() { String input = Console.readLine(); String[] carNames = input.split(","); for (String carName : carNames) { + validateCarName(carName); carMap.put(carName, 0); } } + + private static void validateCarName(String carName) { + if (carName.length() > 5) { + throw new IllegalArgumentException("자동차 이름은 5자 이하만 가능합니다."); + } + } } From f88318020e97af01e193feb7aeaaf1602f5651a5 Mon Sep 17 00:00:00 2001 From: haha4825 Date: Mon, 27 Oct 2025 23:14:47 +0900 Subject: [PATCH 04/11] feat(racingcargametest): Add carname exception test --- src/test/java/racingcar/RacingCarGameTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/racingcar/RacingCarGameTest.java b/src/test/java/racingcar/RacingCarGameTest.java index 5d2cc54bdc..5ae0b3ded3 100644 --- a/src/test/java/racingcar/RacingCarGameTest.java +++ b/src/test/java/racingcar/RacingCarGameTest.java @@ -28,6 +28,13 @@ void beforeEach() { System.setOut(new PrintStream(this.captor)); } + @AfterEach + void afterEach() { + Console.close(); // Console 내부 Scanner 닫기 + System.setOut(this.standardOut); + } + + @Test void 읽기_테스트() { setInput("pobi,woni"); @@ -40,6 +47,13 @@ void beforeEach() { System.setOut(this.standardOut); } + @Test + void 자동차이름_읽기_예외테스트() { + setInput("pobi,sonnii"); + Assertions.assertThatThrownBy(() -> racingCarGame.readCarNames()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("자동차 이름은 5자 이하만 가능합니다."); + } private static void setInput(String ... args) { byte[] buf = String.join("\n", args).getBytes(); From a4bc79ac6bfb2905b95bf23e4a1941ec3c27860a Mon Sep 17 00:00:00 2001 From: haha4825 Date: Mon, 27 Oct 2025 23:18:56 +0900 Subject: [PATCH 05/11] feat(racingcargame): Receive number of games --- src/main/java/racingcar/RacingCarGame.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/racingcar/RacingCarGame.java b/src/main/java/racingcar/RacingCarGame.java index 0f39779074..0fe60a6c76 100644 --- a/src/main/java/racingcar/RacingCarGame.java +++ b/src/main/java/racingcar/RacingCarGame.java @@ -13,8 +13,11 @@ public Map getCarMap() { public void play() { readCarNames(); + int numOfGames = receiceNumOfGames(); } + + public void readCarNames() { System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)\n"); String input = Console.readLine(); @@ -25,9 +28,23 @@ public void readCarNames() { } } - private static void validateCarName(String carName) { + private void validateCarName(String carName) { if (carName.length() > 5) { throw new IllegalArgumentException("자동차 이름은 5자 이하만 가능합니다."); } } + + public int receiceNumOfGames() { + System.out.println("시도할 횟수는 몇 회인가요?"); + String numOfGames = Console.readLine(); + return validateNumOfGames(numOfGames); + } + + private int validateNumOfGames(String numOfGames) { + try { + return Integer.parseInt(numOfGames); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("정수가 아닌 값을 입력했습니다."); + } + } } From a7399a646b346cec99a516b052382a4079489ef7 Mon Sep 17 00:00:00 2001 From: haha4825 Date: Mon, 27 Oct 2025 23:20:06 +0900 Subject: [PATCH 06/11] feat(racingcargametest): Test receiving number of games --- src/test/java/racingcar/RacingCarGameTest.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/test/java/racingcar/RacingCarGameTest.java b/src/test/java/racingcar/RacingCarGameTest.java index 5ae0b3ded3..92e4354268 100644 --- a/src/test/java/racingcar/RacingCarGameTest.java +++ b/src/test/java/racingcar/RacingCarGameTest.java @@ -1,8 +1,5 @@ package racingcar; - -import static org.junit.jupiter.api.Assertions.assertThrows; - import camp.nextstep.edu.missionutils.Console; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -36,7 +33,7 @@ void afterEach() { @Test - void 읽기_테스트() { + void 자동차이름_읽기_테스트() { setInput("pobi,woni"); racingCarGame.readCarNames(); Assertions.assertThat(output()).contains("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); @@ -55,7 +52,13 @@ void afterEach() { .hasMessageContaining("자동차 이름은 5자 이하만 가능합니다."); } - private static void setInput(String ... args) { + @Test + void 게임횟수_읽기_테스트() { + setInput("5"); + Assertions.assertThat(racingCarGame.receiceNumOfGames()).isEqualTo(5); + } + + private static void setInput(String... args) { byte[] buf = String.join("\n", args).getBytes(); System.setIn(new ByteArrayInputStream(buf)); } From 63076d1441eb35a87cbab690817933728f6f2383 Mon Sep 17 00:00:00 2001 From: haha4825 Date: Mon, 27 Oct 2025 23:20:21 +0900 Subject: [PATCH 07/11] feat(racingcargametest): Test receiving number of games exception --- src/test/java/racingcar/RacingCarGameTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/java/racingcar/RacingCarGameTest.java b/src/test/java/racingcar/RacingCarGameTest.java index 92e4354268..a75313cae5 100644 --- a/src/test/java/racingcar/RacingCarGameTest.java +++ b/src/test/java/racingcar/RacingCarGameTest.java @@ -58,6 +58,14 @@ void afterEach() { Assertions.assertThat(racingCarGame.receiceNumOfGames()).isEqualTo(5); } + @Test + void 게임횟수_읽기_예외_테스트() { + setInput("ㅅ"); + Assertions.assertThatThrownBy(() -> racingCarGame.receiceNumOfGames()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("정수가 아닌 값을 입력했습니다."); + } + private static void setInput(String... args) { byte[] buf = String.join("\n", args).getBytes(); System.setIn(new ByteArrayInputStream(buf)); From 32b354ed2eb53913d432e6cba0b70c25eb37ff47 Mon Sep 17 00:00:00 2001 From: haha4825 Date: Mon, 27 Oct 2025 23:35:59 +0900 Subject: [PATCH 08/11] feat(racingcargame): Implement car movement logic for multiple rounds --- src/main/java/racingcar/RacingCarGame.java | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/racingcar/RacingCarGame.java b/src/main/java/racingcar/RacingCarGame.java index 0fe60a6c76..386ecd61b8 100644 --- a/src/main/java/racingcar/RacingCarGame.java +++ b/src/main/java/racingcar/RacingCarGame.java @@ -1,6 +1,7 @@ package racingcar; import camp.nextstep.edu.missionutils.Console; +import camp.nextstep.edu.missionutils.Randoms; import java.util.HashMap; import java.util.Map; @@ -14,12 +15,30 @@ public Map getCarMap() { public void play() { readCarNames(); int numOfGames = receiceNumOfGames(); + System.out.println("\n실행결과"); + for (int i = 0; i < numOfGames; i++) { + playOneRound(); + } + } + + public void playOneRound() { + for (Map.Entry entry : carMap.entrySet()) { + int numOfDice = Randoms.pickNumberInRange(0, 9); + moveForward(entry.getKey(), numOfDice); + System.out.println(entry.getKey() + " : " + "-".repeat(carMap.get(entry.getKey()))); + } + System.out.println("\n"); } + private void moveForward(String carName, int numOfDice) { + if (numOfDice >= 4) { + carMap.put(carName, carMap.get(carName) + 1); + } + } public void readCarNames() { - System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)\n"); + System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); String input = Console.readLine(); String[] carNames = input.split(","); for (String carName : carNames) { From 3d97b02cefbad87cf13d3723c85a534ec3244182 Mon Sep 17 00:00:00 2001 From: haha4825 Date: Mon, 27 Oct 2025 23:51:10 +0900 Subject: [PATCH 09/11] feat(racingcargame): calculate and announce winner --- src/main/java/racingcar/RacingCarGame.java | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/main/java/racingcar/RacingCarGame.java b/src/main/java/racingcar/RacingCarGame.java index 386ecd61b8..0d4c515412 100644 --- a/src/main/java/racingcar/RacingCarGame.java +++ b/src/main/java/racingcar/RacingCarGame.java @@ -2,8 +2,11 @@ import camp.nextstep.edu.missionutils.Console; import camp.nextstep.edu.missionutils.Randoms; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; public class RacingCarGame { Map carMap = new HashMap<>(); @@ -19,6 +22,7 @@ public void play() { for (int i = 0; i < numOfGames; i++) { playOneRound(); } + announceWinners(); } public void playOneRound() { @@ -66,4 +70,24 @@ private int validateNumOfGames(String numOfGames) { throw new IllegalArgumentException("정수가 아닌 값을 입력했습니다."); } } + + private List calculateWinners() { + if (carMap.isEmpty()) return Collections.emptyList(); + int max = carMap.values().stream().mapToInt(Integer::intValue).max().orElse(0); + return carMap.entrySet().stream() + .filter(e -> e.getValue() == max) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + } + + public String getWinnersLine() { + List winners = calculateWinners(); + return "최종 우승자 : " + String.join(", ", winners); + } + + + public void announceWinners() { + System.out.println(); + System.out.println(getWinnersLine()); + } } From e64554a26d9245be573fa5b8e69938c1024a1abf Mon Sep 17 00:00:00 2001 From: haha4825 Date: Mon, 27 Oct 2025 23:55:52 +0900 Subject: [PATCH 10/11] feat(applicaiton): Final application --- src/main/java/racingcar/Application.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index a17a52e724..7968185724 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,7 +1,12 @@ package racingcar; + +import camp.nextstep.edu.missionutils.Console; + public class Application { + private final static RacingCarGame racingCarGame = new RacingCarGame(); + public static void main(String[] args) { - // TODO: 프로그램 구현 + racingCarGame.play(); } } From bb2d394aa5908d80c0ab1a693b845599d0e9591a Mon Sep 17 00:00:00 2001 From: haha4825 Date: Mon, 27 Oct 2025 23:56:12 +0900 Subject: [PATCH 11/11] docs(readme): add readme --- README.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/README.md b/README.md index d0286c859f..85bdac4faa 100644 --- a/README.md +++ b/README.md @@ -1 +1,78 @@ # java-racingcar-precourse + +# 기능 목록 + 1. 자동차 이름을 입력받기 + 2. 자동차이름 예외사항 예외처리하기(5자 제한 필요) + 3. 시도할 횟수를 입력 받기위한 출력문 출력, 입력 받기 (정수가 아니면 잘못된 입력으로 예외처리) + 4. 시도횟수 만큼 각 자동차의 주사위를 던지고, 전진시키기, 전진 횟수를 Map에 반영하기 + 5. Map을 기반으로 우승자 선별하기, 출력하기 + +# NsTest +NsTest에 정의된 init +```java + @BeforeEach //모든 테스트 수행전에 실행 할것 + protected final void init() { + this.standardOut = System.out; //기존 표준 출력을 저장해놓는다 나중에 여기에 한번에 출력하기 위해서 + this.captor = new ByteArrayOutputStream(); // 출력을 가로채서 담을 ByteStream을 정의한다, 출력 내용을 검증하기 위해서 + System.setOut(new PrintStream(this.captor)); //출력을 가로챈다! + } +``` +NsTest에 정의된 run +```java + protected final void run(String... args) { + try { + this.command(args); //입력 stream 설정 (배열로 전달 된 여러 입력 값을 개행 시켜서 실제 입력처럼 만들기) + this.runMain(); //NsTest를 상속한 클래스에서 구현한 메인 Test를 실행해라 + } finally { + Console.close(); //Console 클래스 안의 Scanner 자원 종료 + } + + } +``` +NsTest에 정의된 command +```java + private void command(String... args) { + byte[] buf = String.join("\n", args).getBytes(); + System.setIn(new ByteArrayInputStream(buf)); + } +``` +1. 배열로 전달된 배열을 개행시켜서 하나의 문자열로 만들기 +2. String -> Byte +3. System.in에 설정 실제 사용자가 입력한것 처럼 +Console 라이브러리는 System.in에서 입력을 받아오기 때문에 System.setIn을 하면 실제 사용자가 입력한것과 같은 효과 + +```java +public class Console { + private static Scanner scanner; + + private Console() { + } + + public static String readLine() { + return getInstance().nextLine(); + } + + public static void close() { + if (scanner != null) { + scanner.close(); + scanner = null; + } + + } + + private static Scanner getInstance() { + if (scanner == null) { + scanner = new Scanner(System.in); // => 요기서 + } + + return scanner; + } +} +``` +NsTest에 정의된 output + +```java + protected final String output() { + return this.captor.toString().trim(); //가로챈 출력을 반환 + } +```