From d06db9a252464ba8dfc1644f1b759c251dd5e2cc Mon Sep 17 00:00:00 2001 From: Tae Kyoun Kim Date: Tue, 16 Sep 2025 15:29:11 +0900 Subject: [PATCH 01/11] feat/ mvp added --- README.md | 203 +++++++---------------- src/main/java/racingcar/Application.java | 60 ++++++- 2 files changed, 121 insertions(+), 142 deletions(-) diff --git a/README.md b/README.md index a2b6ed1440..111ad95d44 100644 --- a/README.md +++ b/README.md @@ -1,164 +1,87 @@ -# java-racingcar-precourse # 미션 - 자동차 경주 +## 문제 요구사항 분석 -## 🔍 진행 방식 +주제 : 임의의 갯수의 자동차의 랜덤한 움직임 결과를 출력하는 프로그램 -- 미션은 **기능 요구 사항, 프로그래밍 요구 사항, 과제 진행 요구 사항** 세 가지로 구성되어 있다. -- 세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다. -- 기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다. +**자동차** -## 📮 미션 제출 방법 +- 이름 조건 + - 영어 + - 5글자 이하 +- 움직임 종류 + - 전진 + - 멈춤 +- 움직임 조건 + - 0~9까지의 랜덤 숫자 중 4이상 -- 미션 구현을 완료한 후 GitHub을 통해 제출해야 한다. - - GitHub을 활용한 제출 방법은 [프리코스 과제 제출](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) 문서를 참고해 제출한다. -- GitHub에 미션을 제출한 후 [우아한테크코스 지원](https://apply.techcourse.co.kr) 사이트에 접속하여 프리코스 과제를 제출한다. - - 자세한 방법은 [제출 가이드](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse#제출-가이드) 참고 - - **Pull Request만 보내고 지원 플랫폼에서 과제를 제출하지 않으면 최종 제출하지 않은 것으로 처리되니 주의한다.** +**게임** -## 🚨 과제 제출 전 체크 리스트 - 0점 방지 +- 실행 조건 + - n개의 자동차 이름 입력 + - t번의 움직임 횟수 입력 +- 종료 조건 + - t번의 움직임이 모두 이루어짐 +- 우승 조건 + - 가장 많이 움직인 자동차 + - 중복 가능 -- 기능 구현을 모두 정상적으로 했더라도 **요구 사항에 명시된 출력값 형식을 지키지 않을 경우 0점으로 처리**한다. -- 기능 구현을 완료한 뒤 아래 가이드에 따라 테스트를 실행했을 때 모든 테스트가 성공하는지 확인한다. -- **테스트가 실패할 경우 0점으로 처리**되므로, 반드시 확인 후 제출한다. +**입력** -### 테스트 실행 가이드 +- 첫 번째 줄 입력 + - 영어 + - 5글자 이하 + - 쉼표 구분 +- 두 번째 줄 입력 + - 숫자 -- 터미널에서 `java -version`을 실행하여 Java 버전이 17인지 확인한다. - Eclipse 또는 IntelliJ IDEA와 같은 IDE에서 Java 17로 실행되는지 확인한다. -- 터미널에서 Mac 또는 Linux 사용자의 경우 `./gradlew clean test` 명령을 실행하고, - Windows 사용자의 경우 `gradlew.bat clean test` 또는 `./gradlew.bat clean test` 명령을 실행할 때 모든 테스트가 아래와 같이 통과하는지 확인한다. +**출력** -``` -BUILD SUCCESSFUL in 0s -``` +- 실행 시 + - 움직임이 실행 될 때 마다 결과를 출력 +- 종료 시 + - 가장 많이 움직인 자동차의 이름을 출력 + - 중복 시, 쉼표로 구분 ---- +## 서비스 흐름 구조 기획 -## 🚀 기능 요구 사항 +Untitled-diagram-Mermaid-Chart-2025-09-16-052424-1 +## 기능 구현 체크리스트 -초간단 자동차 경주 게임을 구현한다. +### console -- 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. -- 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다. -- 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다. -- 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. -- 전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다. -- 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다. -- 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다. -- 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`을 발생시킨 후 애플리케이션은 종료되어야 한다. +주요 기능 : 입력 값 담당 -### 입출력 요구 사항 +- [ ] 입력 값 받기 +- [ ] 검증기를 통한 입력 값 검증 과정 추가 +- [ ] 입력 값 저장 -#### 입력 +### validator -- 경주 할 자동차 이름(이름은 쉼표(,) 기준으로 구분) +주요 기능 : 입력 값 검증 -``` -pobi,woni,jun -``` +- 이름 검증 + - [ ] 종류 : 영어 + - [ ] 글자 수 : 1~5 + - [ ] 제한 : 구분자 (쉼표) +- 횟수 검증 + - [ ] 종류 : 숫자 -- 시도할 회수 +### moveCar -``` -5 -``` +주요 기능 : 자동차의 이동 거리 계산 및 출력 -#### 출력 +- 이동 거리 계산 + - [ ] 난수 생성 + - [ ] 차량 별 이동 거리 저장 +- 이동 거리 출력 + - [ ] 차량 별 이동 거리 출력 +- 우승자 출력 + - [ ] 전체 차량 이동 거리 비교 -- 각 차수별 실행 결과 +### utils -``` -pobi : -- -woni : ---- -jun : --- -``` +주요 기능 : 상수 저장 -- 단독 우승자 안내 문구 - -``` -최종 우승자 : pobi -``` - -- 공동 우승자 안내 문구 - -``` -최종 우승자 : pobi, jun -``` - -#### 실행 결과 예시 - -``` -경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분) -pobi,woni,jun -시도할 회수는 몇회인가요? -5 - -실행 결과 -pobi : - -woni : -jun : - - -pobi : -- -woni : - -jun : -- - -pobi : --- -woni : -- -jun : --- - -pobi : ---- -woni : --- -jun : ---- - -pobi : ----- -woni : ---- -jun : ----- - -최종 우승자 : pobi, jun -``` - ---- - -## 🎯 프로그래밍 요구 사항 - -- JDK 17 버전에서 실행 가능해야 한다. **JDK 17에서 정상적으로 동작하지 않을 경우 0점 처리한다.** -- 프로그램 실행의 시작점은 `Application`의 `main()`이다. -- `build.gradle` 파일을 변경할 수 없고, 외부 라이브러리를 사용하지 않는다. -- [Java 코드 컨벤션](https://github.com/woowacourse/woowacourse-docs/tree/master/styleguide/java) 가이드를 준수하며 프로그래밍한다. -- 프로그램 종료 시 `System.exit()`를 호출하지 않는다. -- 프로그램 구현이 완료되면 `ApplicationTest`의 모든 테스트가 성공해야 한다. **테스트가 실패할 경우 0점 처리한다.** -- 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 이름을 수정하거나 이동하지 않는다. - -### 추가된 요구 사항 - -- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다. - - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. - - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다. -- 3항 연산자를 쓰지 않는다. -- 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. -- JUnit 5와 AssertJ를 이용하여 본인이 정리한 기능 목록이 정상 동작함을 테스트 코드로 확인한다. - - 테스트 도구 사용법이 익숙하지 않다면 `test/java/study`를 참고하여 학습한 후 테스트를 구현한다. - -### 라이브러리 - -- JDK에서 제공하는 Random 및 Scanner API 대신 `camp.nextstep.edu.missionutils`에서 제공하는 `Randoms` 및 `Console` API를 사용하여 구현해야 한다. - - Random 값 추출은 `camp.nextstep.edu.missionutils.Randoms`의 `pickNumberInRange()`를 활용한다. - - 사용자가 입력하는 값은 `camp.nextstep.edu.missionutils.Console`의 `readLine()`을 활용한다. - -#### 사용 예시 - -- 0에서 9까지의 정수 중 한 개의 정수 반환 - -```java -Randoms.pickNumberInRange(0,9); -``` - ---- - -## ✏️ 과제 진행 요구 사항 - -- 미션은 [java-racingcar-7](https://github.com/woowacourse-precourse/java-racingcar-7) 저장소를 Fork & Clone해 시작한다. -- **기능을 구현하기 전 `docs/README.md`에 구현할 기능 목록을 정리**해 추가한다. -- **Git의 커밋 단위는 앞 단계에서 `docs/README.md`에 정리한 기능 목록 단위**로 추가한다. - - [커밋 메시지 컨벤션](https://gist.github.com/stephenparish/9941e89d80e2bc58a153) 가이드를 참고해 커밋 메시지를 작성한다. -- 과제 진행 및 제출 방법은 [프리코스 과제 제출](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) 문서를 참고한다. +- 입력 값 안내 메시지 +- 입력 값 오류 메시지 +- 정규 표현식 \ No newline at end of file diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index a17a52e724..95571f6dae 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,7 +1,63 @@ package racingcar; +import camp.nextstep.edu.missionutils.Console; +import camp.nextstep.edu.missionutils.Randoms; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + public class Application { - public static void main(String[] args) { - // TODO: 프로그램 구현 + + public static void main(String[] args) { + // TODO: 프로그램 구현 + + // Console 부분 + System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); + String[] cars = Console.readLine().split(","); + for (String car : cars) { + if (!Pattern.matches("^[a-z]{1,5}$", car)) { + throw new IllegalArgumentException("차량 이름 양식 오류"); + } } + System.out.println("시도할 회수는 몇회인가요?"); + //int tryNumber = Integer.parseInt(Console.readLine()); + String tryNumber = Console.readLine(); + if (!Pattern.matches("[0-9]$", tryNumber)) { + throw new IllegalArgumentException("시도 회수 양식 오류"); + } + int tryNumberInt = Integer.parseInt(tryNumber); + + //moveCar 부분 + HashMap carInfo = new HashMap<>(); + for(String car : cars){ + carInfo.put(car,0); + } + while(tryNumberInt > 0) { + for(String car : cars){ + int currentPosition = carInfo.get(car); + int randomNumber = Randoms.pickNumberInRange(0,9); + if(randomNumber >=4) { + currentPosition++; + carInfo.put(car,currentPosition); + } + System.out.println(car + " : " + "-".repeat(currentPosition)); + } + System.out.println(); + tryNumberInt--; + } + // 우승자 출력 + int maxDistance = carInfo.values() + .stream() + .max(Integer::compareTo) + .orElse(0); // 최댓값 구하기 + + List winners = carInfo.entrySet() + .stream() + .filter(entry -> entry.getValue() == maxDistance) // 최댓값과 같은 차량 필터링 + .map(Map.Entry::getKey) // 차량 이름만 추출 + .toList(); + System.out.println("우승자 : " + String.join(",", winners)); + } + } From 322fba779ba39a879a190b083809da3ef2e3c527 Mon Sep 17 00:00:00 2001 From: Tae Kyoun Kim Date: Tue, 16 Sep 2025 15:45:07 +0900 Subject: [PATCH 02/11] refactor / extract string messages from Application to Constants.java --- src/main/java/racingcar/Application.java | 14 +++++++------- src/main/java/racingcar/utils/Constant.java | 10 ++++++++++ 2 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 src/main/java/racingcar/utils/Constant.java diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index 95571f6dae..0c69c2ca84 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import java.util.regex.Pattern; +import racingcar.utils.Constant; public class Application { @@ -13,18 +14,17 @@ public static void main(String[] args) { // TODO: 프로그램 구현 // Console 부분 - System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); + System.out.println(Constant.REQUIRE_CAR_NAMES); String[] cars = Console.readLine().split(","); for (String car : cars) { - if (!Pattern.matches("^[a-z]{1,5}$", car)) { - throw new IllegalArgumentException("차량 이름 양식 오류"); + if (!Pattern.matches(Constant.REGEX_CAR_NAME, car)) { + throw new IllegalArgumentException(Constant.CAR_NAME_ERROR); } } - System.out.println("시도할 회수는 몇회인가요?"); - //int tryNumber = Integer.parseInt(Console.readLine()); + System.out.println(Constant.REQUIRE_TRY_NUMBER); String tryNumber = Console.readLine(); - if (!Pattern.matches("[0-9]$", tryNumber)) { - throw new IllegalArgumentException("시도 회수 양식 오류"); + if (!Pattern.matches(Constant.REGEX_TRY_NUMBER, tryNumber)) { + throw new IllegalArgumentException(Constant.TRY_NUMBER_ERROR); } int tryNumberInt = Integer.parseInt(tryNumber); diff --git a/src/main/java/racingcar/utils/Constant.java b/src/main/java/racingcar/utils/Constant.java new file mode 100644 index 0000000000..0f81ad5dc6 --- /dev/null +++ b/src/main/java/racingcar/utils/Constant.java @@ -0,0 +1,10 @@ +package racingcar.utils; + +public class Constant { + public final static String REQUIRE_CAR_NAMES = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"; + public final static String REQUIRE_TRY_NUMBER = "시도할 회수는 몇회인가요?"; + public final static String CAR_NAME_ERROR = "차량 이름 양식 오류"; + public final static String TRY_NUMBER_ERROR = "시도 회수 양식 오류"; + public final static String REGEX_CAR_NAME = "^[a-z]{1,5}$"; + public final static String REGEX_TRY_NUMBER = "^[0-9]$"; +} From 622b04bc2f9833ab90e8d36a46031ad411dc2fb1 Mon Sep 17 00:00:00 2001 From: Tae Kyoun Kim Date: Tue, 16 Sep 2025 16:03:53 +0900 Subject: [PATCH 03/11] refactor/ extract validate logic from Application to Validator.java --- src/main/java/racingcar/Application.java | 11 +--- src/main/java/racingcar/Application2.java | 63 +++++++++++++++++++ src/main/java/racingcar/utils/Validators.java | 21 +++++++ 3 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 src/main/java/racingcar/Application2.java create mode 100644 src/main/java/racingcar/utils/Validators.java diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index 0c69c2ca84..6025e1c29d 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -7,6 +7,7 @@ import java.util.Map; import java.util.regex.Pattern; import racingcar.utils.Constant; +import racingcar.utils.Validators; public class Application { @@ -16,16 +17,10 @@ public static void main(String[] args) { // Console 부분 System.out.println(Constant.REQUIRE_CAR_NAMES); String[] cars = Console.readLine().split(","); - for (String car : cars) { - if (!Pattern.matches(Constant.REGEX_CAR_NAME, car)) { - throw new IllegalArgumentException(Constant.CAR_NAME_ERROR); - } - } + Validators.validateCarName(cars); System.out.println(Constant.REQUIRE_TRY_NUMBER); String tryNumber = Console.readLine(); - if (!Pattern.matches(Constant.REGEX_TRY_NUMBER, tryNumber)) { - throw new IllegalArgumentException(Constant.TRY_NUMBER_ERROR); - } + Validators.validateTryNumber(tryNumber); int tryNumberInt = Integer.parseInt(tryNumber); //moveCar 부분 diff --git a/src/main/java/racingcar/Application2.java b/src/main/java/racingcar/Application2.java new file mode 100644 index 0000000000..058f4998c0 --- /dev/null +++ b/src/main/java/racingcar/Application2.java @@ -0,0 +1,63 @@ +package racingcar; + +import camp.nextstep.edu.missionutils.Console; +import camp.nextstep.edu.missionutils.Randoms; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import racingcar.utils.Constant; + +public class Application2 { + + public static void main(String[] args) { + // TODO: 프로그램 구현 + + // Console 부분 + System.out.println(Constant.REQUIRE_CAR_NAMES); + String[] cars = Console.readLine().split(","); + for (String car : cars) { + if (!Pattern.matches(Constant.REGEX_CAR_NAME, car)) { + throw new IllegalArgumentException(Constant.CAR_NAME_ERROR); + } + } + System.out.println(Constant.REQUIRE_TRY_NUMBER); + String tryNumber = Console.readLine(); + if (!Pattern.matches(Constant.REGEX_TRY_NUMBER, tryNumber)) { + throw new IllegalArgumentException(Constant.TRY_NUMBER_ERROR); + } + int tryNumberInt = Integer.parseInt(tryNumber); + + //moveCar 부분 + HashMap carInfo = new HashMap<>(); + for(String car : cars){ + carInfo.put(car,0); + } + while(tryNumberInt > 0) { + for(String car : cars){ + int currentPosition = carInfo.get(car); + int randomNumber = Randoms.pickNumberInRange(0,9); + if(randomNumber >=4) { + currentPosition++; + carInfo.put(car,currentPosition); + } + System.out.println(car + " : " + "-".repeat(currentPosition)); + } + System.out.println(); + tryNumberInt--; + } + // 우승자 출력 + int maxDistance = carInfo.values() + .stream() + .max(Integer::compareTo) + .orElse(0); // 최댓값 구하기 + + List winners = carInfo.entrySet() + .stream() + .filter(entry -> entry.getValue() == maxDistance) // 최댓값과 같은 차량 필터링 + .map(Map.Entry::getKey) // 차량 이름만 추출 + .toList(); + System.out.println("우승자 : " + String.join(",", winners)); + } + +} diff --git a/src/main/java/racingcar/utils/Validators.java b/src/main/java/racingcar/utils/Validators.java new file mode 100644 index 0000000000..82f857bc7e --- /dev/null +++ b/src/main/java/racingcar/utils/Validators.java @@ -0,0 +1,21 @@ +package racingcar.utils; + +import java.util.regex.Pattern; + +public class Validators { + private Validators() {} + + public static void validateCarName(String[] cars) { + for (String car : cars) { + if (!Pattern.matches(Constant.REGEX_CAR_NAME, car)) { + throw new IllegalArgumentException(Constant.CAR_NAME_ERROR); + } + } + } + + public static void validateTryNumber(String tryNumber) { + if (!Pattern.matches(Constant.REGEX_TRY_NUMBER, tryNumber)) { + throw new IllegalArgumentException(Constant.TRY_NUMBER_ERROR); + } + } +} From 2012606f37fbf0ff2ac4ef2ed220a8059d539239 Mon Sep 17 00:00:00 2001 From: Tae Kyoun Kim Date: Tue, 16 Sep 2025 16:15:50 +0900 Subject: [PATCH 04/11] refactor/ extract input logic from Application to InputConsole.java --- src/main/java/racingcar/Application.java | 15 ++++--------- .../java/racingcar/console/InputConsole.java | 21 +++++++++++++++++++ .../java/racingcar/console/ResultConsole.java | 5 +++++ 3 files changed, 30 insertions(+), 11 deletions(-) create mode 100644 src/main/java/racingcar/console/InputConsole.java create mode 100644 src/main/java/racingcar/console/ResultConsole.java diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index 6025e1c29d..035a5f06ff 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,13 +1,10 @@ package racingcar; -import camp.nextstep.edu.missionutils.Console; import camp.nextstep.edu.missionutils.Randoms; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.regex.Pattern; -import racingcar.utils.Constant; -import racingcar.utils.Validators; +import racingcar.console.InputConsole; public class Application { @@ -15,13 +12,9 @@ public static void main(String[] args) { // TODO: 프로그램 구현 // Console 부분 - System.out.println(Constant.REQUIRE_CAR_NAMES); - String[] cars = Console.readLine().split(","); - Validators.validateCarName(cars); - System.out.println(Constant.REQUIRE_TRY_NUMBER); - String tryNumber = Console.readLine(); - Validators.validateTryNumber(tryNumber); - int tryNumberInt = Integer.parseInt(tryNumber); + InputConsole input = new InputConsole(); + String[] cars = input.inputCarNames(); + int tryNumberInt = input.inputTryNumber(); //moveCar 부분 HashMap carInfo = new HashMap<>(); diff --git a/src/main/java/racingcar/console/InputConsole.java b/src/main/java/racingcar/console/InputConsole.java new file mode 100644 index 0000000000..98169f7575 --- /dev/null +++ b/src/main/java/racingcar/console/InputConsole.java @@ -0,0 +1,21 @@ +package racingcar.console; + +import camp.nextstep.edu.missionutils.Console; +import racingcar.utils.Constant; +import racingcar.utils.Validators; + +public class InputConsole { + public String[] inputCarNames(){ + System.out.println(Constant.REQUIRE_CAR_NAMES); + String[] cars = Console.readLine().split(","); + Validators.validateCarName(cars); + return cars; + } + + public int inputTryNumber(){ + System.out.println(Constant.REQUIRE_TRY_NUMBER); + String tryNumber = Console.readLine(); + Validators.validateTryNumber(tryNumber); + return Integer.parseInt(tryNumber); + } +} diff --git a/src/main/java/racingcar/console/ResultConsole.java b/src/main/java/racingcar/console/ResultConsole.java new file mode 100644 index 0000000000..3c473a6189 --- /dev/null +++ b/src/main/java/racingcar/console/ResultConsole.java @@ -0,0 +1,5 @@ +package racingcar.console; + +public class ResultConsole { + +} From 9cad1822a4ba3cb448f8796052093d1bd1f5c86d Mon Sep 17 00:00:00 2001 From: Tae Kyoun Kim Date: Tue, 16 Sep 2025 16:32:31 +0900 Subject: [PATCH 05/11] refactor/ extract print result logic from Application to ResultConsole.java --- src/main/java/racingcar/Application.java | 39 +++++-------------- .../java/racingcar/console/ResultConsole.java | 16 ++++++++ src/main/java/racingcar/race/Race.java | 31 +++++++++++++++ 3 files changed, 57 insertions(+), 29 deletions(-) create mode 100644 src/main/java/racingcar/race/Race.java diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index 035a5f06ff..b138299ee8 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -5,6 +5,8 @@ import java.util.List; import java.util.Map; import racingcar.console.InputConsole; +import racingcar.console.ResultConsole; +import racingcar.race.Race; public class Application { @@ -16,36 +18,15 @@ public static void main(String[] args) { String[] cars = input.inputCarNames(); int tryNumberInt = input.inputTryNumber(); - //moveCar 부분 - HashMap carInfo = new HashMap<>(); - for(String car : cars){ - carInfo.put(car,0); - } - while(tryNumberInt > 0) { - for(String car : cars){ - int currentPosition = carInfo.get(car); - int randomNumber = Randoms.pickNumberInRange(0,9); - if(randomNumber >=4) { - currentPosition++; - carInfo.put(car,currentPosition); - } - System.out.println(car + " : " + "-".repeat(currentPosition)); - } - System.out.println(); - tryNumberInt--; - } - // 우승자 출력 - int maxDistance = carInfo.values() - .stream() - .max(Integer::compareTo) - .orElse(0); // 최댓값 구하기 + //Race 부분 + Race race = new Race(); + HashMap carInfo = race.setCarInfo(cars); + race.startRace(tryNumberInt,cars,carInfo); - List winners = carInfo.entrySet() - .stream() - .filter(entry -> entry.getValue() == maxDistance) // 최댓값과 같은 차량 필터링 - .map(Map.Entry::getKey) // 차량 이름만 추출 - .toList(); - System.out.println("우승자 : " + String.join(",", winners)); + // 우승자 출력 + ResultConsole resultConsole = new ResultConsole(); + int maxDistance = resultConsole.getMaxDistance(carInfo); + resultConsole.printResult(carInfo,maxDistance); } } diff --git a/src/main/java/racingcar/console/ResultConsole.java b/src/main/java/racingcar/console/ResultConsole.java index 3c473a6189..0dceb81136 100644 --- a/src/main/java/racingcar/console/ResultConsole.java +++ b/src/main/java/racingcar/console/ResultConsole.java @@ -1,5 +1,21 @@ package racingcar.console; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + public class ResultConsole { + public int getMaxDistance(HashMap carInfo) { + return carInfo.values().stream().max(Integer::compareTo).orElse(0); // 최댓값 구하기 + } + + public void printResult(HashMap carInfo, int maxDistance) { + List winners = carInfo.entrySet() + .stream() + .filter(entry -> entry.getValue() == maxDistance) // 최댓값과 같은 차량 필터링 + .map(Map.Entry::getKey) // 차량 이름만 추출 + .toList(); + System.out.println("최종 우승자 : " + String.join(",", winners)); + } } diff --git a/src/main/java/racingcar/race/Race.java b/src/main/java/racingcar/race/Race.java new file mode 100644 index 0000000000..dc2551c4f9 --- /dev/null +++ b/src/main/java/racingcar/race/Race.java @@ -0,0 +1,31 @@ +package racingcar.race; + +import camp.nextstep.edu.missionutils.Randoms; +import java.util.HashMap; + +public class Race { + + public HashMap setCarInfo(String[] cars) { + HashMap carInfo = new HashMap<>(); + for (String car : cars) { + carInfo.put(car, 0); + } + return carInfo; + } + + public void startRace(int tryNumberInt, String[] cars, HashMap carInfo) { + while(tryNumberInt > 0) { + for(String car : cars){ + int currentPosition = carInfo.get(car); + int randomNumber = Randoms.pickNumberInRange(0,9); + if(randomNumber >=4) { + currentPosition++; + carInfo.put(car,currentPosition); + } + System.out.println(car + " : " + "-".repeat(currentPosition)); + } + System.out.println(); + tryNumberInt--; + } + } +} From 51880e637746c6d459160cc4bd7b09b3e5fc9189 Mon Sep 17 00:00:00 2001 From: Tae Kyoun Kim Date: Tue, 16 Sep 2025 16:36:38 +0900 Subject: [PATCH 06/11] docs/ check complited functions --- README.md | 22 +++++++++++----------- src/main/java/racingcar/Application.java | 7 ++----- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 111ad95d44..275abf01fe 100644 --- a/README.md +++ b/README.md @@ -51,32 +51,32 @@ 주요 기능 : 입력 값 담당 -- [ ] 입력 값 받기 -- [ ] 검증기를 통한 입력 값 검증 과정 추가 -- [ ] 입력 값 저장 +- [X] 입력 값 받기 +- [X] 검증기를 통한 입력 값 검증 과정 추가 +- [X] 입력 값 저장 ### validator 주요 기능 : 입력 값 검증 - 이름 검증 - - [ ] 종류 : 영어 - - [ ] 글자 수 : 1~5 - - [ ] 제한 : 구분자 (쉼표) + - [X] 종류 : 영어 + - [X] 글자 수 : 1~5 + - [X] 제한 : 구분자 (쉼표) - 횟수 검증 - - [ ] 종류 : 숫자 + - [X] 종류 : 숫자 ### moveCar 주요 기능 : 자동차의 이동 거리 계산 및 출력 - 이동 거리 계산 - - [ ] 난수 생성 - - [ ] 차량 별 이동 거리 저장 + - [X] 난수 생성 + - [X] 차량 별 이동 거리 저장 - 이동 거리 출력 - - [ ] 차량 별 이동 거리 출력 + - [X] 차량 별 이동 거리 출력 - 우승자 출력 - - [ ] 전체 차량 이동 거리 비교 + - [X] 전체 차량 이동 거리 비교 ### utils diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index b138299ee8..ffb3f8998a 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,9 +1,6 @@ package racingcar; -import camp.nextstep.edu.missionutils.Randoms; import java.util.HashMap; -import java.util.List; -import java.util.Map; import racingcar.console.InputConsole; import racingcar.console.ResultConsole; import racingcar.race.Race; @@ -13,7 +10,7 @@ public class Application { public static void main(String[] args) { // TODO: 프로그램 구현 - // Console 부분 + // InputConsole 부분 InputConsole input = new InputConsole(); String[] cars = input.inputCarNames(); int tryNumberInt = input.inputTryNumber(); @@ -23,7 +20,7 @@ public static void main(String[] args) { HashMap carInfo = race.setCarInfo(cars); race.startRace(tryNumberInt,cars,carInfo); - // 우승자 출력 + // ResultConsole 부분 ResultConsole resultConsole = new ResultConsole(); int maxDistance = resultConsole.getMaxDistance(carInfo); resultConsole.printResult(carInfo,maxDistance); From 61fc674aa746e330047d2fbfdeff61fcbf90c5bf Mon Sep 17 00:00:00 2001 From: Tae Kyoun Kim Date: Mon, 22 Sep 2025 17:44:48 +0900 Subject: [PATCH 07/11] refactor/ divide constant to Message.java and Regex.java --- src/main/java/racingcar/Application2.java | 126 +++++++++--------- .../java/racingcar/console/InputConsole.java | 8 +- .../racingcar/controller/raceController.java | 21 +++ src/main/java/racingcar/utils/Constant.java | 10 -- src/main/java/racingcar/utils/Validator.java | 25 ++++ src/main/java/racingcar/utils/Validators.java | 21 --- .../racingcar/utils/constant/Message.java | 9 ++ .../java/racingcar/utils/constant/Regex.java | 6 + src/test/java/racingcar/ApplicationTest.java | 8 +- 9 files changed, 135 insertions(+), 99 deletions(-) create mode 100644 src/main/java/racingcar/controller/raceController.java delete mode 100644 src/main/java/racingcar/utils/Constant.java create mode 100644 src/main/java/racingcar/utils/Validator.java delete mode 100644 src/main/java/racingcar/utils/Validators.java create mode 100644 src/main/java/racingcar/utils/constant/Message.java create mode 100644 src/main/java/racingcar/utils/constant/Regex.java diff --git a/src/main/java/racingcar/Application2.java b/src/main/java/racingcar/Application2.java index 058f4998c0..4e35336458 100644 --- a/src/main/java/racingcar/Application2.java +++ b/src/main/java/racingcar/Application2.java @@ -1,63 +1,63 @@ -package racingcar; - -import camp.nextstep.edu.missionutils.Console; -import camp.nextstep.edu.missionutils.Randoms; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; -import racingcar.utils.Constant; - -public class Application2 { - - public static void main(String[] args) { - // TODO: 프로그램 구현 - - // Console 부분 - System.out.println(Constant.REQUIRE_CAR_NAMES); - String[] cars = Console.readLine().split(","); - for (String car : cars) { - if (!Pattern.matches(Constant.REGEX_CAR_NAME, car)) { - throw new IllegalArgumentException(Constant.CAR_NAME_ERROR); - } - } - System.out.println(Constant.REQUIRE_TRY_NUMBER); - String tryNumber = Console.readLine(); - if (!Pattern.matches(Constant.REGEX_TRY_NUMBER, tryNumber)) { - throw new IllegalArgumentException(Constant.TRY_NUMBER_ERROR); - } - int tryNumberInt = Integer.parseInt(tryNumber); - - //moveCar 부분 - HashMap carInfo = new HashMap<>(); - for(String car : cars){ - carInfo.put(car,0); - } - while(tryNumberInt > 0) { - for(String car : cars){ - int currentPosition = carInfo.get(car); - int randomNumber = Randoms.pickNumberInRange(0,9); - if(randomNumber >=4) { - currentPosition++; - carInfo.put(car,currentPosition); - } - System.out.println(car + " : " + "-".repeat(currentPosition)); - } - System.out.println(); - tryNumberInt--; - } - // 우승자 출력 - int maxDistance = carInfo.values() - .stream() - .max(Integer::compareTo) - .orElse(0); // 최댓값 구하기 - - List winners = carInfo.entrySet() - .stream() - .filter(entry -> entry.getValue() == maxDistance) // 최댓값과 같은 차량 필터링 - .map(Map.Entry::getKey) // 차량 이름만 추출 - .toList(); - System.out.println("우승자 : " + String.join(",", winners)); - } - -} +//package racingcar; +// +//import camp.nextstep.edu.missionutils.Console; +//import camp.nextstep.edu.missionutils.Randoms; +//import java.util.HashMap; +//import java.util.List; +//import java.util.Map; +//import java.util.regex.Pattern; +//import racingcar.utils.constant.Constant; +// +//public class Application2 { +// +// public static void main(String[] args) { +// // TODO: 프로그램 구현 +// +// // Console 부분 +// System.out.println(Constant.REQUIRE_CAR_NAMES); +// String[] cars = Console.readLine().split(","); +// for (String car : cars) { +// if (!Pattern.matches(Constant.REGEX_CAR_NAME, car)) { +// throw new IllegalArgumentException(Constant.CAR_NAME_ERROR); +// } +// } +// System.out.println(Constant.REQUIRE_TRY_NUMBER); +// String tryNumber = Console.readLine(); +// if (!Pattern.matches(Constant.REGEX_TRY_NUMBER, tryNumber)) { +// throw new IllegalArgumentException(Constant.TRY_NUMBER_ERROR); +// } +// int tryNumberInt = Integer.parseInt(tryNumber); +// +// //moveCar 부분 +// HashMap carInfo = new HashMap<>(); +// for(String car : cars){ +// carInfo.put(car,0); +// } +// while(tryNumberInt > 0) { +// for(String car : cars){ +// int currentPosition = carInfo.get(car); +// int randomNumber = Randoms.pickNumberInRange(0,9); +// if(randomNumber >=4) { +// currentPosition++; +// carInfo.put(car,currentPosition); +// } +// System.out.println(car + " : " + "-".repeat(currentPosition)); +// } +// System.out.println(); +// tryNumberInt--; +// } +// // 우승자 출력 +// int maxDistance = carInfo.values() +// .stream() +// .max(Integer::compareTo) +// .orElse(0); // 최댓값 구하기 +// +// List winners = carInfo.entrySet() +// .stream() +// .filter(entry -> entry.getValue() == maxDistance) // 최댓값과 같은 차량 필터링 +// .map(Map.Entry::getKey) // 차량 이름만 추출 +// .toList(); +// System.out.println("우승자 : " + String.join(",", winners)); +// } +// +//} diff --git a/src/main/java/racingcar/console/InputConsole.java b/src/main/java/racingcar/console/InputConsole.java index 98169f7575..5c6e935384 100644 --- a/src/main/java/racingcar/console/InputConsole.java +++ b/src/main/java/racingcar/console/InputConsole.java @@ -1,21 +1,21 @@ package racingcar.console; import camp.nextstep.edu.missionutils.Console; -import racingcar.utils.Constant; -import racingcar.utils.Validators; +import racingcar.utils.constant.Constant; +import racingcar.utils.Validator; public class InputConsole { public String[] inputCarNames(){ System.out.println(Constant.REQUIRE_CAR_NAMES); String[] cars = Console.readLine().split(","); - Validators.validateCarName(cars); + Validator.validateCarName(cars); return cars; } public int inputTryNumber(){ System.out.println(Constant.REQUIRE_TRY_NUMBER); String tryNumber = Console.readLine(); - Validators.validateTryNumber(tryNumber); + Validator.validateTryNumber(tryNumber); return Integer.parseInt(tryNumber); } } diff --git a/src/main/java/racingcar/controller/raceController.java b/src/main/java/racingcar/controller/raceController.java new file mode 100644 index 0000000000..8f94db11a9 --- /dev/null +++ b/src/main/java/racingcar/controller/raceController.java @@ -0,0 +1,21 @@ +package racingcar.controller; + +import camp.nextstep.edu.missionutils.Console; +import racingcar.utils.Validator; +import racingcar.utils.constant.Message; + +public class raceController { + public String[] inputCarNames(){ + System.out.println(Message.REQUIRE_CAR_NAMES); + String[] cars = Console.readLine().split(","); + Validator.validateCarName(cars); + return cars; + } + + public int inputTryNumber(){ + System.out.println(Message.REQUIRE_TRY_NUMBER); + String tryNumber = Console.readLine(); + Validator.validateTryNumber(tryNumber); + return Integer.parseInt(tryNumber); + } +} diff --git a/src/main/java/racingcar/utils/Constant.java b/src/main/java/racingcar/utils/Constant.java deleted file mode 100644 index 0f81ad5dc6..0000000000 --- a/src/main/java/racingcar/utils/Constant.java +++ /dev/null @@ -1,10 +0,0 @@ -package racingcar.utils; - -public class Constant { - public final static String REQUIRE_CAR_NAMES = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"; - public final static String REQUIRE_TRY_NUMBER = "시도할 회수는 몇회인가요?"; - public final static String CAR_NAME_ERROR = "차량 이름 양식 오류"; - public final static String TRY_NUMBER_ERROR = "시도 회수 양식 오류"; - public final static String REGEX_CAR_NAME = "^[a-z]{1,5}$"; - public final static String REGEX_TRY_NUMBER = "^[0-9]$"; -} diff --git a/src/main/java/racingcar/utils/Validator.java b/src/main/java/racingcar/utils/Validator.java new file mode 100644 index 0000000000..69193906fa --- /dev/null +++ b/src/main/java/racingcar/utils/Validator.java @@ -0,0 +1,25 @@ +package racingcar.utils; + +import java.util.regex.Pattern; +import racingcar.utils.constant.Constant; +import racingcar.utils.constant.Message; +import racingcar.utils.constant.Regex; + +public class Validator { + private Validator() {} + + public static void validateCarName(String[] cars) { + for (String car : cars) { + if (!Pattern.matches(Regex.CAR_NAME, car)) { + throw new IllegalArgumentException(Message.ERROR_CAR_NAME); + } + } + } + + public static void validateTryNumber(String tryNumber) { + if (!Pattern.matches(Regex.TRY_NUMBER, tryNumber)) { + throw new IllegalArgumentException(Message.ERROR_TRY_NUMBER); + } + } + +} diff --git a/src/main/java/racingcar/utils/Validators.java b/src/main/java/racingcar/utils/Validators.java deleted file mode 100644 index 82f857bc7e..0000000000 --- a/src/main/java/racingcar/utils/Validators.java +++ /dev/null @@ -1,21 +0,0 @@ -package racingcar.utils; - -import java.util.regex.Pattern; - -public class Validators { - private Validators() {} - - public static void validateCarName(String[] cars) { - for (String car : cars) { - if (!Pattern.matches(Constant.REGEX_CAR_NAME, car)) { - throw new IllegalArgumentException(Constant.CAR_NAME_ERROR); - } - } - } - - public static void validateTryNumber(String tryNumber) { - if (!Pattern.matches(Constant.REGEX_TRY_NUMBER, tryNumber)) { - throw new IllegalArgumentException(Constant.TRY_NUMBER_ERROR); - } - } -} diff --git a/src/main/java/racingcar/utils/constant/Message.java b/src/main/java/racingcar/utils/constant/Message.java new file mode 100644 index 0000000000..fcddb80557 --- /dev/null +++ b/src/main/java/racingcar/utils/constant/Message.java @@ -0,0 +1,9 @@ +package racingcar.utils.constant; + +public class Message { + public final static String REQUIRE_CAR_NAMES = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"; + public final static String REQUIRE_TRY_NUMBER = "시도할 회수는 몇회인가요?"; + public final static String ERROR_CAR_NAME = "차량 이름 양식 오류"; + public final static String ERROR_TRY_NUMBER = "시도 회수 양식 오류"; + +} diff --git a/src/main/java/racingcar/utils/constant/Regex.java b/src/main/java/racingcar/utils/constant/Regex.java new file mode 100644 index 0000000000..56a98bf602 --- /dev/null +++ b/src/main/java/racingcar/utils/constant/Regex.java @@ -0,0 +1,6 @@ +package racingcar.utils.constant; + +public class Regex { + public final static String CAR_NAME = "^[a-z]{1,5}$"; + public final static String TRY_NUMBER = "^[0-9]$"; +} diff --git a/src/test/java/racingcar/ApplicationTest.java b/src/test/java/racingcar/ApplicationTest.java index 1d35fc33fe..3b769b7f5b 100644 --- a/src/test/java/racingcar/ApplicationTest.java +++ b/src/test/java/racingcar/ApplicationTest.java @@ -22,7 +22,13 @@ class ApplicationTest extends NsTest { MOVING_FORWARD, STOP ); } - + @Test + void 입력_테스트_입력값_없음(){ + assertSimpleTest(()-> + assertThatThrownBy(() -> run("")) + .isInstanceOf(IllegalArgumentException.class) + ); + } @Test void 예외_테스트() { assertSimpleTest(() -> From cf63008ccd46e3f51a8bd496ca0445b53266c3de Mon Sep 17 00:00:00 2001 From: Tae Kyoun Kim Date: Wed, 24 Sep 2025 18:05:48 +0900 Subject: [PATCH 08/11] refactor/ apply mvc pattern --- src/main/java/racingcar/Application.java | 37 +++++++------- .../java/racingcar/console/InputConsole.java | 21 -------- .../java/racingcar/console/ResultConsole.java | 21 -------- ...aceController.java => RaceController.java} | 8 +-- src/main/java/racingcar/domain/Car.java | 22 ++++++++ src/main/java/racingcar/domain/Cars.java | 18 +++++++ src/main/java/racingcar/race/Race.java | 31 ------------ .../java/racingcar/service/RaceService.java | 50 +++++++++++++++++++ src/main/java/racingcar/utils/Validator.java | 4 +- src/main/java/racingcar/view/RaceView.java | 10 ++++ src/test/java/racingcar/ApplicationTest.java | 2 +- 11 files changed, 127 insertions(+), 97 deletions(-) delete mode 100644 src/main/java/racingcar/console/InputConsole.java delete mode 100644 src/main/java/racingcar/console/ResultConsole.java rename src/main/java/racingcar/controller/{raceController.java => RaceController.java} (83%) create mode 100644 src/main/java/racingcar/domain/Car.java create mode 100644 src/main/java/racingcar/domain/Cars.java delete mode 100644 src/main/java/racingcar/race/Race.java create mode 100644 src/main/java/racingcar/service/RaceService.java create mode 100644 src/main/java/racingcar/view/RaceView.java diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index ffb3f8998a..ae06c90d7d 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,29 +1,28 @@ package racingcar; import java.util.HashMap; -import racingcar.console.InputConsole; -import racingcar.console.ResultConsole; -import racingcar.race.Race; +import java.util.List; +import racingcar.controller.RaceController; +import racingcar.domain.Cars; +import racingcar.service.RaceService; +import racingcar.view.RaceView; public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 - - // InputConsole 부분 - InputConsole input = new InputConsole(); - String[] cars = input.inputCarNames(); - int tryNumberInt = input.inputTryNumber(); - - //Race 부분 - Race race = new Race(); - HashMap carInfo = race.setCarInfo(cars); - race.startRace(tryNumberInt,cars,carInfo); - - // ResultConsole 부분 - ResultConsole resultConsole = new ResultConsole(); - int maxDistance = resultConsole.getMaxDistance(carInfo); - resultConsole.printResult(carInfo,maxDistance); + + RaceController raceController = new RaceController(); + String[] carNames = raceController.inputCarNames(); + int tryNumberInt = raceController.inputTryNumber(); + + RaceService raceService = new RaceService(); + + Cars cars = new Cars(carNames); + raceService.startRace(tryNumberInt, cars); + + List winners = raceService.findWinner(cars); + RaceView raceView = new RaceView(); + raceView.printWinner(winners); } } diff --git a/src/main/java/racingcar/console/InputConsole.java b/src/main/java/racingcar/console/InputConsole.java deleted file mode 100644 index 5c6e935384..0000000000 --- a/src/main/java/racingcar/console/InputConsole.java +++ /dev/null @@ -1,21 +0,0 @@ -package racingcar.console; - -import camp.nextstep.edu.missionutils.Console; -import racingcar.utils.constant.Constant; -import racingcar.utils.Validator; - -public class InputConsole { - public String[] inputCarNames(){ - System.out.println(Constant.REQUIRE_CAR_NAMES); - String[] cars = Console.readLine().split(","); - Validator.validateCarName(cars); - return cars; - } - - public int inputTryNumber(){ - System.out.println(Constant.REQUIRE_TRY_NUMBER); - String tryNumber = Console.readLine(); - Validator.validateTryNumber(tryNumber); - return Integer.parseInt(tryNumber); - } -} diff --git a/src/main/java/racingcar/console/ResultConsole.java b/src/main/java/racingcar/console/ResultConsole.java deleted file mode 100644 index 0dceb81136..0000000000 --- a/src/main/java/racingcar/console/ResultConsole.java +++ /dev/null @@ -1,21 +0,0 @@ -package racingcar.console; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class ResultConsole { - - public int getMaxDistance(HashMap carInfo) { - return carInfo.values().stream().max(Integer::compareTo).orElse(0); // 최댓값 구하기 - } - - public void printResult(HashMap carInfo, int maxDistance) { - List winners = carInfo.entrySet() - .stream() - .filter(entry -> entry.getValue() == maxDistance) // 최댓값과 같은 차량 필터링 - .map(Map.Entry::getKey) // 차량 이름만 추출 - .toList(); - System.out.println("최종 우승자 : " + String.join(",", winners)); - } -} diff --git a/src/main/java/racingcar/controller/raceController.java b/src/main/java/racingcar/controller/RaceController.java similarity index 83% rename from src/main/java/racingcar/controller/raceController.java rename to src/main/java/racingcar/controller/RaceController.java index 8f94db11a9..2d29d8cd6f 100644 --- a/src/main/java/racingcar/controller/raceController.java +++ b/src/main/java/racingcar/controller/RaceController.java @@ -1,18 +1,20 @@ package racingcar.controller; + import camp.nextstep.edu.missionutils.Console; import racingcar.utils.Validator; import racingcar.utils.constant.Message; -public class raceController { - public String[] inputCarNames(){ +public class RaceController { + + public String[] inputCarNames() { System.out.println(Message.REQUIRE_CAR_NAMES); String[] cars = Console.readLine().split(","); Validator.validateCarName(cars); return cars; } - public int inputTryNumber(){ + public int inputTryNumber() { System.out.println(Message.REQUIRE_TRY_NUMBER); String tryNumber = Console.readLine(); Validator.validateTryNumber(tryNumber); diff --git a/src/main/java/racingcar/domain/Car.java b/src/main/java/racingcar/domain/Car.java new file mode 100644 index 0000000000..09d51354f4 --- /dev/null +++ b/src/main/java/racingcar/domain/Car.java @@ -0,0 +1,22 @@ +package racingcar.domain; + +public class Car { + private String name; + private int distance; + + public Car(String name, int distance) { + this.name = name; + this.distance = distance; + } + public String getName(){ + return name; + } + + public int getDistance(){ + return distance; + } + + public void updateDistance(){ + distance += 1; + } +} diff --git a/src/main/java/racingcar/domain/Cars.java b/src/main/java/racingcar/domain/Cars.java new file mode 100644 index 0000000000..a309b7f840 --- /dev/null +++ b/src/main/java/racingcar/domain/Cars.java @@ -0,0 +1,18 @@ +package racingcar.domain; + +public class Cars { + + private Car[] cars; + + public Cars(String[] carNames) { + this.cars = new Car[carNames.length]; + for (int i = 0; i < carNames.length; i++) { + cars[i] = new Car(carNames[i],0); + } + } + + public Car[] getCars(){ + return cars; + } + +} diff --git a/src/main/java/racingcar/race/Race.java b/src/main/java/racingcar/race/Race.java deleted file mode 100644 index dc2551c4f9..0000000000 --- a/src/main/java/racingcar/race/Race.java +++ /dev/null @@ -1,31 +0,0 @@ -package racingcar.race; - -import camp.nextstep.edu.missionutils.Randoms; -import java.util.HashMap; - -public class Race { - - public HashMap setCarInfo(String[] cars) { - HashMap carInfo = new HashMap<>(); - for (String car : cars) { - carInfo.put(car, 0); - } - return carInfo; - } - - public void startRace(int tryNumberInt, String[] cars, HashMap carInfo) { - while(tryNumberInt > 0) { - for(String car : cars){ - int currentPosition = carInfo.get(car); - int randomNumber = Randoms.pickNumberInRange(0,9); - if(randomNumber >=4) { - currentPosition++; - carInfo.put(car,currentPosition); - } - System.out.println(car + " : " + "-".repeat(currentPosition)); - } - System.out.println(); - tryNumberInt--; - } - } -} diff --git a/src/main/java/racingcar/service/RaceService.java b/src/main/java/racingcar/service/RaceService.java new file mode 100644 index 0000000000..665308c547 --- /dev/null +++ b/src/main/java/racingcar/service/RaceService.java @@ -0,0 +1,50 @@ +package racingcar.service; + +import camp.nextstep.edu.missionutils.Randoms; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import racingcar.domain.Car; +import racingcar.domain.Cars; + +public class RaceService { + + public boolean moveCar() { + int randomNumber = Randoms.pickNumberInRange(0, 9); + if (randomNumber >= 4) { + return true; + } else { + return false; + } + } + + public void startRace(int tryNumber, Cars cars) { + Car[] carArray = cars.getCars(); + while (tryNumber > 0) { + for (Car car : carArray) { + if (moveCar()) { + car.updateDistance(); + } + System.out.println(car.getName() + " : " + "-".repeat(car.getDistance())); + } + System.out.println(); + tryNumber--; + } + } + + public List findWinner(Cars cars) { + Car[] carArray = cars.getCars(); + Arrays.sort(carArray, Comparator.comparingInt(Car::getDistance).reversed()); + + List winnerNames = new ArrayList<>(); + int maxDistance = carArray[0].getDistance(); + for (Car car : carArray) { + if (car.getDistance() == maxDistance) { + winnerNames.add(car.getName()); + } + } + return winnerNames; + } + +} diff --git a/src/main/java/racingcar/utils/Validator.java b/src/main/java/racingcar/utils/Validator.java index 69193906fa..486a6fbf9a 100644 --- a/src/main/java/racingcar/utils/Validator.java +++ b/src/main/java/racingcar/utils/Validator.java @@ -1,7 +1,6 @@ package racingcar.utils; import java.util.regex.Pattern; -import racingcar.utils.constant.Constant; import racingcar.utils.constant.Message; import racingcar.utils.constant.Regex; @@ -9,6 +8,9 @@ public class Validator { private Validator() {} public static void validateCarName(String[] cars) { + if(cars == null || cars.length == 0) { + throw new IllegalArgumentException(Message.ERROR_CAR_NAME); + } for (String car : cars) { if (!Pattern.matches(Regex.CAR_NAME, car)) { throw new IllegalArgumentException(Message.ERROR_CAR_NAME); diff --git a/src/main/java/racingcar/view/RaceView.java b/src/main/java/racingcar/view/RaceView.java new file mode 100644 index 0000000000..d11c6c9ef8 --- /dev/null +++ b/src/main/java/racingcar/view/RaceView.java @@ -0,0 +1,10 @@ +package racingcar.view; + +import java.util.List; + +public class RaceView { + + public void printWinner(List winners) { + System.out.println("최종 우승자 : " + String.join(",", winners)); + } +} diff --git a/src/test/java/racingcar/ApplicationTest.java b/src/test/java/racingcar/ApplicationTest.java index 3b769b7f5b..b931cdc909 100644 --- a/src/test/java/racingcar/ApplicationTest.java +++ b/src/test/java/racingcar/ApplicationTest.java @@ -25,7 +25,7 @@ class ApplicationTest extends NsTest { @Test void 입력_테스트_입력값_없음(){ assertSimpleTest(()-> - assertThatThrownBy(() -> run("")) + assertThatThrownBy(() -> run("","")) .isInstanceOf(IllegalArgumentException.class) ); } From 7c6d616f31ec7cb3e8d1197729084f3a413dd2de Mon Sep 17 00:00:00 2001 From: Tae Kyoun Kim Date: Wed, 24 Sep 2025 20:06:42 +0900 Subject: [PATCH 09/11] refactor/ extract race method from RaceService to Race.java --- src/main/java/racingcar/Application.java | 4 +- src/main/java/racingcar/domain/Race.java | 35 +++++++++++++++++ .../java/racingcar/service/RaceService.java | 39 ++----------------- 3 files changed, 40 insertions(+), 38 deletions(-) create mode 100644 src/main/java/racingcar/domain/Race.java diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index ae06c90d7d..ae54a75da4 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,9 +1,9 @@ package racingcar; -import java.util.HashMap; import java.util.List; import racingcar.controller.RaceController; import racingcar.domain.Cars; +import racingcar.domain.Race; import racingcar.service.RaceService; import racingcar.view.RaceView; @@ -20,7 +20,7 @@ public static void main(String[] args) { Cars cars = new Cars(carNames); raceService.startRace(tryNumberInt, cars); - List winners = raceService.findWinner(cars); + List winners = Race.findWinner(cars); RaceView raceView = new RaceView(); raceView.printWinner(winners); } diff --git a/src/main/java/racingcar/domain/Race.java b/src/main/java/racingcar/domain/Race.java new file mode 100644 index 0000000000..c2e4b750cc --- /dev/null +++ b/src/main/java/racingcar/domain/Race.java @@ -0,0 +1,35 @@ +package racingcar.domain; + +import camp.nextstep.edu.missionutils.Randoms; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +public class Race { + + public void moveCar(Car[] carArray) { + for (Car car : carArray) { + int randomNumber = Randoms.pickNumberInRange(0, 9); + if (randomNumber >= 4) { + car.updateDistance(); + } + System.out.println(car.getName() + " : " + "-".repeat(car.getDistance())); + } + System.out.println(); + } + + public static List findWinner(Cars cars) { + Car[] carArray = cars.getCars(); + Arrays.sort(carArray, Comparator.comparingInt(Car::getDistance).reversed()); + + List winnerNames = new ArrayList<>(); + int maxDistance = carArray[0].getDistance(); + for (Car car : carArray) { + if (car.getDistance() == maxDistance) { + winnerNames.add(car.getName()); + } + } + return winnerNames; + } +} diff --git a/src/main/java/racingcar/service/RaceService.java b/src/main/java/racingcar/service/RaceService.java index 665308c547..3ab73c478e 100644 --- a/src/main/java/racingcar/service/RaceService.java +++ b/src/main/java/racingcar/service/RaceService.java @@ -1,50 +1,17 @@ package racingcar.service; -import camp.nextstep.edu.missionutils.Randoms; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; import racingcar.domain.Car; import racingcar.domain.Cars; +import racingcar.domain.Race; public class RaceService { - public boolean moveCar() { - int randomNumber = Randoms.pickNumberInRange(0, 9); - if (randomNumber >= 4) { - return true; - } else { - return false; - } - } - public void startRace(int tryNumber, Cars cars) { + Race race = new Race(); Car[] carArray = cars.getCars(); while (tryNumber > 0) { - for (Car car : carArray) { - if (moveCar()) { - car.updateDistance(); - } - System.out.println(car.getName() + " : " + "-".repeat(car.getDistance())); - } - System.out.println(); + race.moveCar(carArray); tryNumber--; } } - - public List findWinner(Cars cars) { - Car[] carArray = cars.getCars(); - Arrays.sort(carArray, Comparator.comparingInt(Car::getDistance).reversed()); - - List winnerNames = new ArrayList<>(); - int maxDistance = carArray[0].getDistance(); - for (Car car : carArray) { - if (car.getDistance() == maxDistance) { - winnerNames.add(car.getName()); - } - } - return winnerNames; - } - } From 7b916be311ff507784609bfcd36ce7e3369d76c9 Mon Sep 17 00:00:00 2001 From: Tae Kyoun Kim Date: Wed, 24 Sep 2025 20:37:05 +0900 Subject: [PATCH 10/11] refactor/ extract race logic from Application to RaceRunner.java --- src/main/java/racingcar/Application.java | 21 ++------------- .../java/racingcar/runner/RaceRunner.java | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 src/main/java/racingcar/runner/RaceRunner.java diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index ae54a75da4..df2a237a78 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,28 +1,11 @@ package racingcar; -import java.util.List; -import racingcar.controller.RaceController; -import racingcar.domain.Cars; -import racingcar.domain.Race; -import racingcar.service.RaceService; -import racingcar.view.RaceView; +import racingcar.runner.RaceRunner; public class Application { public static void main(String[] args) { - - RaceController raceController = new RaceController(); - String[] carNames = raceController.inputCarNames(); - int tryNumberInt = raceController.inputTryNumber(); - - RaceService raceService = new RaceService(); - - Cars cars = new Cars(carNames); - raceService.startRace(tryNumberInt, cars); - - List winners = Race.findWinner(cars); - RaceView raceView = new RaceView(); - raceView.printWinner(winners); + new RaceRunner().run(); } } diff --git a/src/main/java/racingcar/runner/RaceRunner.java b/src/main/java/racingcar/runner/RaceRunner.java new file mode 100644 index 0000000000..de77aa5917 --- /dev/null +++ b/src/main/java/racingcar/runner/RaceRunner.java @@ -0,0 +1,26 @@ +package racingcar.runner; + +import java.util.List; +import racingcar.controller.RaceController; +import racingcar.domain.Cars; +import racingcar.domain.Race; +import racingcar.service.RaceService; +import racingcar.view.RaceView; + +public class RaceRunner { + + public void run() { + RaceController raceController = new RaceController(); + String[] carNames = raceController.inputCarNames(); + int tryNumber = raceController.inputTryNumber(); + + RaceService raceService = new RaceService(); + + Cars cars = new Cars(carNames); + raceService.startRace(tryNumber, cars); + + List winners = Race.findWinner(cars); + RaceView raceView = new RaceView(); + raceView.printWinner(winners); + } +} From 8f5627ec6978481d7ba407db1de328aba24c4237 Mon Sep 17 00:00:00 2001 From: Tae Kyoun Kim Date: Fri, 26 Sep 2025 21:23:17 +0900 Subject: [PATCH 11/11] refactor/ extract print progress logic from RaceService from RaceVies.java --- .../racingcar/controller/RaceController.java | 6 +++- src/main/java/racingcar/domain/Race.java | 2 -- .../java/racingcar/runner/RaceRunner.java | 4 +-- .../java/racingcar/service/RaceService.java | 2 ++ src/main/java/racingcar/view/RaceView.java | 10 ++++++- src/test/java/racingcar/ApplicationTest.java | 29 ++++++++++++++++++- 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/main/java/racingcar/controller/RaceController.java b/src/main/java/racingcar/controller/RaceController.java index 2d29d8cd6f..cdafbb612a 100644 --- a/src/main/java/racingcar/controller/RaceController.java +++ b/src/main/java/racingcar/controller/RaceController.java @@ -9,7 +9,11 @@ public class RaceController { public String[] inputCarNames() { System.out.println(Message.REQUIRE_CAR_NAMES); - String[] cars = Console.readLine().split(","); + String[] input = Console.readLine().split(","); + String[] cars = new String[input.length]; + for(int i = 0 ; i < input.length ; i++) { + cars[i] = input[i].trim(); + } Validator.validateCarName(cars); return cars; } diff --git a/src/main/java/racingcar/domain/Race.java b/src/main/java/racingcar/domain/Race.java index c2e4b750cc..97aa12d2a7 100644 --- a/src/main/java/racingcar/domain/Race.java +++ b/src/main/java/racingcar/domain/Race.java @@ -14,9 +14,7 @@ public void moveCar(Car[] carArray) { if (randomNumber >= 4) { car.updateDistance(); } - System.out.println(car.getName() + " : " + "-".repeat(car.getDistance())); } - System.out.println(); } public static List findWinner(Cars cars) { diff --git a/src/main/java/racingcar/runner/RaceRunner.java b/src/main/java/racingcar/runner/RaceRunner.java index de77aa5917..1cbf2af3a1 100644 --- a/src/main/java/racingcar/runner/RaceRunner.java +++ b/src/main/java/racingcar/runner/RaceRunner.java @@ -14,9 +14,9 @@ public void run() { String[] carNames = raceController.inputCarNames(); int tryNumber = raceController.inputTryNumber(); - RaceService raceService = new RaceService(); - Cars cars = new Cars(carNames); + + RaceService raceService = new RaceService(); raceService.startRace(tryNumber, cars); List winners = Race.findWinner(cars); diff --git a/src/main/java/racingcar/service/RaceService.java b/src/main/java/racingcar/service/RaceService.java index 3ab73c478e..ac1d48f576 100644 --- a/src/main/java/racingcar/service/RaceService.java +++ b/src/main/java/racingcar/service/RaceService.java @@ -3,6 +3,7 @@ import racingcar.domain.Car; import racingcar.domain.Cars; import racingcar.domain.Race; +import racingcar.view.RaceView; public class RaceService { @@ -11,6 +12,7 @@ public void startRace(int tryNumber, Cars cars) { Car[] carArray = cars.getCars(); while (tryNumber > 0) { race.moveCar(carArray); + RaceView.printProgress(carArray); tryNumber--; } } diff --git a/src/main/java/racingcar/view/RaceView.java b/src/main/java/racingcar/view/RaceView.java index d11c6c9ef8..3d488c1e13 100644 --- a/src/main/java/racingcar/view/RaceView.java +++ b/src/main/java/racingcar/view/RaceView.java @@ -1,10 +1,18 @@ package racingcar.view; import java.util.List; +import racingcar.domain.Car; public class RaceView { + public static void printProgress(Car[] carArray) { + for (Car car : carArray) { + System.out.println(car.getName() + " : " + "-".repeat(car.getDistance())); + } + System.out.println(); + } + public void printWinner(List winners) { - System.out.println("최종 우승자 : " + String.join(",", winners)); + System.out.println("최종 우승자 : " + String.join(", ", winners)); } } diff --git a/src/test/java/racingcar/ApplicationTest.java b/src/test/java/racingcar/ApplicationTest.java index b931cdc909..8e033b488d 100644 --- a/src/test/java/racingcar/ApplicationTest.java +++ b/src/test/java/racingcar/ApplicationTest.java @@ -2,11 +2,14 @@ import camp.nextstep.edu.missionutils.test.NsTest; import org.junit.jupiter.api.Test; +import racingcar.controller.RaceController; +import racingcar.domain.Race; 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.junit.jupiter.api.Assertions.*; class ApplicationTest extends NsTest { private static final int MOVING_FORWARD = 4; @@ -23,12 +26,31 @@ class ApplicationTest extends NsTest { ); } @Test - void 입력_테스트_입력값_없음(){ + void 입력_예외_테스트_입력값_없음(){ assertSimpleTest(()-> assertThatThrownBy(() -> run("","")) .isInstanceOf(IllegalArgumentException.class) ); } + + @Test + void 입력_예외_테스트_차량이름_입력(){ + assertSimpleTest(()-> + assertThatThrownBy(() -> run("포비,워니,준","4")) + .isInstanceOf(IllegalArgumentException.class) + ); + } + + @Test + void 입력_예외_테스트_횟수_입력(){ + assertSimpleTest(()-> + assertThatThrownBy(() -> run("pobi,woni,jun","two")) + .isInstanceOf(IllegalArgumentException.class) + ); + } + + + @Test void 예외_테스트() { assertSimpleTest(() -> @@ -37,6 +59,11 @@ class ApplicationTest extends NsTest { ); } + @Test + void 기능_테스트_컨트롤러_input(){ + RaceController rc = new RaceController(); + } + @Override public void runMain() { Application.main(new String[]{});