-
Notifications
You must be signed in to change notification settings - Fork 104
[그리디] 김민욱 자동차 경주 미션 1,2 단계 제출합니다. #174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
hapdaypy
wants to merge
35
commits into
next-step:main
Choose a base branch
from
hapdaypy:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+268
−0
Open
Changes from all commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
9fee122
docs: README.md 기능 요구사항 업데이트
1077534
feat: 자동차(Car) 도메인 모델 구현
5c22eae
feat: 자동차 이름과 시도횟수를 입력받는 기능 추가
4480a21
feat: controller에서 자동차와 시도횟수 입력 기능 추가
b566e31
feat: 자동차들 객체로 배열 만드는 기능 추가
159e617
refactor: racing으로 파일 이름 변경
1c4dfe5
feat: car 객체 이름 추가 기능 생성
7877f6c
fix: application 사용에서 psvm으 변경
11a0d07
feat: 자동차 객체 기능 추가
82e46fb
refactor: 잘못된 호출 수정
059c10a
feat: 자동차 이름을 검사하는 기능 추가
9dd00ba
feat: 경기마다 자동차 거리를 출력하고 최종 우승자를 출력하는 기능 추가
03accdd
feat: 경기를 진행하고 우승자를 뽑는 기능 추가
23abbbe
feat: 경기를 진행시키고 우승자를 출력하는 기능 추가
cb031fe
refactor: for문으로 출력하는 것이 아니라 자바 메서드로 출력하게 바꿈
bfc8e29
refactor: 초기값 0으로 수정
2fa8c64
feat: CarManager를 통하여 자동차 객체 관리
e1ad114
refactor: 오타수정
de9a01b
feat: 중복되는 자동차 이름 삭제 기능 추가
3f90b89
refactor: 출력형식 수정
f8eb16b
refactor: 모든 경우에서 출력하도록 변경
c79e89f
docs: 기능구현목록, 예외 처리 규칙, 프로그램 진행 방식 작성
0783491
refactor: 출력 로진 변경
00c6296
chore: 오타 수정
63eb12b
refactor: 참조자료형으로 변경
6f1f03f
refactor: Domain과 InputView 검증 분리
f0535c2
refactor: Domain과 InputView 검증 분리
480d3f6
feat: ,가 두변연속 나왔을 때 합치는 기능 추가
a3aae79
refactor: domain에서 -로 표시된 길을 outputView에서 출력
7ff14c6
refactor: 비지니스 규칙인 검증 수정
4e5c715
refactor: 사용하지 않는 메서드 삭제
beec64e
refactor: static 사용에서 객체 형태로 변경
2383aca
refactor: 초기값 0 삭제
f84be8a
refactor: 랜덤 발생을 검증하기 위해, static 사용에서 race 객체 사용
16f3a09
chore: 불필요한 공백제거
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| # 🏎️ 자동차 경주 게임 (Car Racing) | ||
|
|
||
| ## 🚀 기능 구현 목록 | ||
|
|
||
| ### 1. 입력 및 데이터 처리 | ||
| - [ ] **자동차 이름 입력**: 쉼표(`,`)를 기준으로 구분하여 경주에 참여할 자동차 이름을 입력받는다. | ||
| - [ ] **데이터 변환**: 입력된 문자열을 분리하여 `Car` 객체 리스트로 생성한다. | ||
| - [ ] **시도 횟수 입력**: 전체 자동차가 이동을 시도할 총 횟수를 입력받는다. | ||
|
|
||
| ### 2. 레이싱 로직 | ||
| - [ ] **전진 조건 확인**: 매 라운드마다 각 자동차별로 무작위 값을 생성하여 전진 여부를 결정한다. | ||
| - [ ] **위치 업데이트**: 전진 조건을 만족하는 경우 자동차의 위치를 $1$ 씩 증가시킨다. | ||
| - [ ] **우승자 판별**: 모든 라운드 종료 후 전진 거리가 가장 긴 자동차를 우승자로 선정한다. (공동 우승 가능) | ||
|
|
||
| ### 3. 출력 | ||
| - [ ] **라운드 결과**: 매 라운드 종료 시점의 자동차별 이름과 전진 상태(`-`)를 출력한다. | ||
| - [ ] **최종 우승자**: 경주 종료 후 최종 우승자의 이름을 출력한다. (공동 우승 시 쉼표로 구분) | ||
|
|
||
| --- | ||
|
|
||
| ## ⚠️ 예외 처리 규칙 (Exception Handling) | ||
| 잘못된 값 입력 시 `IllegalArgumentException`을 발생시키며, 프로그램은 즉시 종료되거나 에러 메시지를 출력해야 한다. | ||
|
|
||
| ### [자동차 이름 관련] | ||
| 1. **형식 오류**: 알파벳과 한글 이외의 문자(특수문자, 숫자 등)가 포함된 경우. | ||
| 2. **공백 포함**: 이름 내부에 공백이 있거나, 입력값이 공백으로만 구성된 경우. | ||
| 3. **입력 부재**: 자동차 이름을 입력하지 않고 진행하려 하는 경우. | ||
| 4. **중복 발생**: 동일한 이름을 가진 자동차가 리스트 내에 중복으로 존재하는 경우. | ||
|
|
||
| ### [시도 횟수 관련] | ||
| 1. **타입 오류**: 숫자 이외의 문자, 특수기호, 공백이 포함된 경우. | ||
| 2. **범위 오류**: 입력값이 $0$ 이하의 정수인 경우 (최소 $1$ 회 이상 필요). | ||
| 3. **미입력**: 시도 횟수를 입력하지 않고 엔터를 입력한 경우. | ||
|
|
||
| --- | ||
|
|
||
| ## 💻 프로그램 진행 방식 | ||
| 1. **[Step 1]** `InputView`를 통한 자동차 이름 및 시도 횟수 입력. | ||
| 2. **[Step 2]** 입력 데이터 검증 및 `List<Car>` 객체 생성. | ||
| 3. **[Step 3]** 설정된 횟수만큼 경주 실행 및 실시간 결과 출력. | ||
| 4. **[Step 4]** 최종 우승자 연산 및 `OutputView`를 통한 결과 발표. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| package racing.controller; | ||
|
|
||
| import racing.domain.Car; | ||
| import racing.view.InputView; | ||
|
|
||
| import racing.domain.Cars; | ||
| import racing.domain.Race; | ||
| import racing.domain.RandomMoveStrategy; | ||
| import racing.view.OutputView; | ||
|
|
||
| public class Controller { | ||
| public static void main(String[] args) { | ||
| // [1] 자동차 이름 및 시도 횟수 입력 받기 | ||
| String carNameInput = InputView.inputCarName(); | ||
| int trialNumber = InputView.inputTrialNumberCount(); | ||
| Race.validateTrialCount(trialNumber); | ||
| // [2] 데이터 변환: 문자열 -> 자동차 객체 리스트 | ||
| Cars cars = new Cars(carNameInput); | ||
| Race race = new Race(cars); | ||
| //[3] 레이씽 경기 시작 | ||
| RandomMoveStrategy moveStrategy = new RandomMoveStrategy(); | ||
| for (int i = 0; i < trialNumber; i++) { | ||
| race.playRound(moveStrategy); | ||
| OutputView.printRoundResult(race.getParticipatingCars()); | ||
| OutputView.println(); | ||
| } | ||
| //[4] 결과 출력 | ||
| OutputView.printWinners(race.getWinners()); | ||
|
|
||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package racing.domain; | ||
|
|
||
| public class Car { | ||
| private final String name; | ||
| private int position; | ||
|
|
||
| public Car(String name) { | ||
| validateName(name); | ||
| this.name = name; | ||
| this.position = 0; | ||
| } | ||
| private void validateName(String name) { | ||
| if (name == null || name.isBlank()) { | ||
| throw new IllegalArgumentException("[ERROR] 자동차 이름은 1자 이상이어야 합니다."); | ||
| } | ||
| if (name.length() > 5) { | ||
| throw new IllegalArgumentException("[ERROR] 자동차 이름은 5자 이하여야 합니다."); | ||
| } | ||
| if (name.contains(" ")) { | ||
| throw new IllegalArgumentException("[ERROR] 자동차 이름에 공백을 포함할 수 없습니다."); | ||
| } | ||
| if (!name.matches("^[a-zA-Z0-9가-힣]*$")) { | ||
| throw new IllegalArgumentException("[ERROR] 자동차 이름에 특수문자를 포함할 수 없습니다."); | ||
| } | ||
| } | ||
| public void move() { | ||
| this.position++; | ||
| } | ||
| public String getName() { | ||
| return name; | ||
| } | ||
|
|
||
| public int getPosition() { | ||
| return position; | ||
| } | ||
|
|
||
|
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package racing.domain; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| public class Cars { | ||
| private final List<Car> cars; | ||
|
|
||
| public Cars(String input) { | ||
| String[] names = input.split(","); | ||
| this.cars = new ArrayList<>(); | ||
| for (String name : names) { | ||
| this.cars.add(new Car(name)); | ||
| } | ||
| validateDuplicate(); | ||
| } | ||
| private void validateDuplicate(){ | ||
| long distinctCount = cars.stream() | ||
| .map(Car::getName) // Car 객체에서 이름을 추출한다고 가정 | ||
| .distinct() | ||
| .count(); | ||
| if (distinctCount != cars.size()) { | ||
| throw new IllegalArgumentException("[ERROR]중복된 자동차 이름이 존재합니다."); | ||
| } | ||
| } | ||
| public void moveAll(MoveStrategy moveStrategy) { | ||
| for (Car car : cars) { | ||
| if (moveStrategy.isMovable()) { | ||
| car.move(); | ||
| } | ||
| } | ||
| } | ||
| public List<Car> getCarList(){ | ||
| return cars; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package racing.domain; | ||
|
|
||
| public interface MoveStrategy { | ||
| boolean isMovable(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package racing.domain; | ||
|
|
||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
| public class Race { | ||
| private final Cars cars; | ||
|
|
||
| public Race(Cars cars){ | ||
| this.cars = cars; | ||
| } | ||
| public void playRound(MoveStrategy moveStrategy) { | ||
| cars.moveAll(moveStrategy); | ||
| } | ||
| public List<Car> getParticipatingCars() { | ||
| return cars.getCarList(); | ||
| } | ||
| public static void validateTrialCount(int tryNumber){ | ||
| if(tryNumber <= 0){ | ||
| throw new IllegalArgumentException("[ERROR] 시도 횟수는 1회 이상이여야합니다."); | ||
| } | ||
| } | ||
| public List<String> getWinners() { | ||
| List<Car> carList = cars.getCarList(); | ||
| int maxPosition = carList.stream() | ||
| .mapToInt(Car::getPosition) | ||
| .max() | ||
| .orElse(0); | ||
|
|
||
| return carList.stream() | ||
| .filter(car -> car.getPosition() == maxPosition) | ||
| .map(Car::getName) | ||
| .collect(Collectors.toList()); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package racing.domain; | ||
|
|
||
| import java.util.Random; | ||
|
|
||
| public class RandomMoveStrategy implements MoveStrategy { | ||
| private static final int RANDOM_RANGE = 10; | ||
| private static final int MOVE_THRESHOLD = 4; | ||
| private static final Random random = new Random(); | ||
| @Override | ||
| public boolean isMovable() { | ||
| return random.nextInt(RANDOM_RANGE) >= MOVE_THRESHOLD; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| package racing.view; | ||
| import java.util.Scanner; | ||
|
|
||
| public class InputView { | ||
hapdaypy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| private static final String INPUT_CAR_NAME_MESSAGE = "경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분."; | ||
| private static final String INPUT_CAR_COUNT_MESSAGE = "시도할 회수는 몇회인가요?"; | ||
| private static final Scanner scanner = new Scanner(System.in); | ||
|
|
||
| public static String inputCarName() { | ||
| System.out.println(INPUT_CAR_NAME_MESSAGE); | ||
| String carName = scanner.nextLine(); | ||
| validateCarNameFormat(carName); | ||
|
|
||
| return carName; | ||
| } | ||
|
|
||
| public static int inputTrialNumberCount() { | ||
| System.out.println(INPUT_CAR_COUNT_MESSAGE); | ||
| String input = scanner.nextLine(); | ||
| return parseTrialCount(input); | ||
| } | ||
|
|
||
| private static void validateCarNameFormat(String input){ | ||
| if(input.isBlank()){ | ||
| throw new IllegalArgumentException("[ERROR] 입력값이 없습니다."); | ||
| } | ||
| String noSpaceInput = input.replace(" ",""); | ||
| if(noSpaceInput.contains(",,")){ | ||
| throw new IllegalArgumentException("[ERROR] 쉼표가 연속으로 입력되었습니다."); | ||
| } | ||
| if(input.startsWith(",")||input.endsWith(",")){ | ||
| throw new IllegalArgumentException("[ERROR] 입력값의 시작이나 끝에 쉼표가 있습니다."); | ||
| } | ||
|
|
||
| } | ||
| private static int parseTrialCount(String input) { | ||
| try { | ||
| return Integer.parseInt(input); | ||
| } catch (NumberFormatException e) { | ||
| throw new IllegalArgumentException("[ERROR] 시도 횟수는 정수 형의 숫자여야 합니다."); | ||
| } | ||
hapdaypy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package racing.view; | ||
| import racing.domain.Car; | ||
| import java.util.List; | ||
|
|
||
| public class OutputView { | ||
| public static void printRoundResult(List<Car> cars) { | ||
| for (Car car : cars) { | ||
| System.out.println(car.getName() + " : " + "-".repeat(car.getPosition())); | ||
| } | ||
| } | ||
|
|
||
| public static void printWinners(List<String> winnerNames) { | ||
| if (winnerNames == null || winnerNames.isEmpty()) { | ||
| return; | ||
| } | ||
| String result = String.join(", ", winnerNames); | ||
| System.out.println(result + "가 최종 우승했습니다."); | ||
| } | ||
|
|
||
|
|
||
| public static void println(){ | ||
| System.out.println(); | ||
| } | ||
|
|
||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Readme에 남겨주신 내용처럼, 검증의 종류를 형식 검증과 나머지 비즈니스 규칙에 따른 검증을 나눠서 고민하신 것 같아요!
예를 들어, 쉼표가 연속해서 두번 나오는 경우(jun,,min)에 대한 검증과 이름길이에 대한 검증(예: sangjun - 5자 초과)는 검증의 종류가 다를텐데 모두 도메인에서 책임을 갖는 것이 맞을까요? 지금도 충분히 좋지만 검증을 어디서 하는지도 고민해보셨으면 좋겠습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
맞습니다! 검증의 종류가 더 많아진다면, 형식과 비즈니스 규칙을 나눈 것처럼 역할과 기능을 분리하는 것이 정말 중요한 행동이라고 생각합니다 ! 😄
제가 검증에 대한 카테고리 분류에 대해서는 깊게 생각하지 못했다고 생각이 됩니다! 감사합니다 !