diff --git a/docs/README.md b/docs/README.md index e69de29..a2f2a6b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,42 @@ +1. 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. +2. 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다. +3. 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다. +4. 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. +5. 전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다. +6. 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다. +7. 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다. +8. 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 애플리케이션은 종료되어야 한다. + + +Car class / Validator class / main(사용자 입출력/게임 실행) +1. Car +- 이름/위치 정보 +- move, getter 메서드 + +2. Validator +- 자동차 이름 입력값 검사 +- 횟수 입력값 검사 + +3. Application(main) +- 사용자로부터 입력받음 +- Validator로 검사 +- Car 객체 생성-> 레이스 동작 +- 결과 출력 + + +2주차_리팩토링(주요 변경사항) +1. Application +main() 안에 로직이 많음 ->>> 메서드 분리 +- getCarNames(): 이름 입력,검사 +- getTryCount(): 횟수 입력,검사 +- setupCars(String names): 입력받은 이름으로 car 리스트 만듦 +- runRace(List racingCars, int tryCount): 경주 진행(자동차 움직임, 핵심) +- void printCarStatus(Car car): 차 위치(position)에 따른 - 출력 +- void printWinners(List racingCars): 최종 우승자 출력 +- main(): 위의 메서드 사용 -> 실행! + +2. Validator +- int name_max 상수 선언했지만, 사용하지 않았음 -> 사용 +- 이름 검사: 2.2 이름 빈값/공백 감지 시, 메세지 출력하도록 수정 + + diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index a17a52e..244f8be 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,7 +1,73 @@ package racingcar; +import java.util.ArrayList; +import java.util.List; +import camp.nextstep.edu.missionutils.Console; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + String names = getCarNames(); // 입력 및 검증 + int tryCount = getTryCount(); + List racingCars = setupCarsList(names); // 자동차(객체) 생성 + runRace(racingCars, tryCount); // 경주 진행/출력 + printWinners(racingCars); // 최종 우승자 출력 + } + + //메서드 분리 + private static String getCarNames() { + System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); + String names = Console.readLine(); //내부적으로 Scanner.nextLine()과 동일하게 작동 + return Validator.validateCarName(names); //반환값 단순화 + } + + private static int getTryCount() { + System.out.println("시도할 회수는 몇회인가요?"); + String times = Console.readLine(); + return Validator.validateTry(times); + } + + private static List setupCarsList(String names) { + String[] nameList = names.split(",", -1); //문자열을 , 을 구분으로 슬라이싱 + List racingCars = new ArrayList<>(); + for (String name : nameList) { //이름으로 자동차 리스트 + racingCars.add(new Car(name)); + } + return racingCars; + } + + private static void runRace(List racingCars, int tryCount) { + System.out.println("\n실행 결과"); + for (int i = 0; i < tryCount; i++) { + for (Car car : racingCars) { + car.move(); + printCarStatus(car); //- 출력 + } + System.out.println(); + } + } + private static void printCarStatus(Car car) { + System.out.print(car.getName() + " : "); + for (int j = 0; j < car.getPosition(); j++) { + System.out.print("-"); + } + System.out.println(); + } + + private static void printWinners(List racingCars) { + int maxPosition = 0; + for (Car car : racingCars) { + if (car.getPosition() > maxPosition) { + maxPosition = car.getPosition(); + } + } + List winners = new ArrayList<>(); + for (Car car : racingCars) { + if (car.getPosition() == maxPosition) { + winners.add(car.getName()); + } + } + System.out.println("최종 우승자 : " + String.join(", ", winners)); } } + + diff --git a/src/main/java/racingcar/Car.java b/src/main/java/racingcar/Car.java new file mode 100644 index 0000000..bd645ac --- /dev/null +++ b/src/main/java/racingcar/Car.java @@ -0,0 +1,34 @@ +package racingcar; + +import camp.nextstep.edu.missionutils.Randoms; + +/*Car +- 이름/위치 정보 +- move, getter 메서드*/ + +public class Car { + private final String name; + private int position; + + //정보를 넘겨받아 필드에 저장(생성자) + public Car(String name){ + this.name = name; + this.position = 0; + } + + //동작: move 메서드 + public int move(){ + if (Randoms.pickNumberInRange(0,9) >=4){ + position++; + } + return position; + } + + //현재 이름/위치 반환: getter 메서드 넘겨주기 위해 + public String getName(){ + return name; + } + public int getPosition(){ + return position; + } +} diff --git a/src/main/java/racingcar/Validator.java b/src/main/java/racingcar/Validator.java new file mode 100644 index 0000000..46b4f7a --- /dev/null +++ b/src/main/java/racingcar/Validator.java @@ -0,0 +1,49 @@ +package racingcar; +import java.util.HashSet; +import java.util.Set; + +/*2. Validator +- 자동차 이름 입력값 검사 +- 횟수 입력값 검사*/ + + +public class Validator { + //이름 입력 검사 + public static String validateCarName(String names){ + if(names.isBlank()){ //1. 전체 검사 + throw new IllegalArgumentException("이름 입력값이 공백이거나 존재하지 않습니다."); + } + //입력을 ,를 기준으로 자른다. 배열로 저장. + String [] nameList = names.split(",",-1); + int name_max = 5; + Set equalNames = new HashSet<>(); //자바에서 문자열 중복 검사는 HashSet을 사용하면 편리하다. + //2. 개별 입력에 대한 검사 + for(String name:nameList){ + if(name.length() > name_max) { //2.1 이름 길이 + throw new IllegalArgumentException("자동차 이름은 5자 이하로 입력해주세요."); + } + if(name.isBlank() || name.contains(" ")){ //2.2 이름 빈 값/공백 + throw new IllegalArgumentException("자동차 이름을 입력하지 않았습니다."); + }if(!equalNames.add(name)){ //2.3 이름 중복 검사 + throw new IllegalArgumentException("자동차 이름은 중복될 수 없습니다."); + } + } + return names; + + } + + //시도 횟수 검사 + public static int validateTry(String times){ + int count; + try{ + count = Integer.parseInt(times); //Integer.parseInt(문자)일 경우, NumberException 에러 발생 ->try/catch + }catch(NumberFormatException e){ + throw new IllegalArgumentException("시도 횟수를 숫자로 입력해주세요."); + } + if (count < 1) { + throw new IllegalArgumentException("시도 횟수는 1 이상이어야 합니다."); + } + return count; + } + +} diff --git a/src/test/java/racingcar/ApplicationTest.java b/src/test/java/racingcar/ApplicationTest.java index 03aa74b..d89d605 100644 --- a/src/test/java/racingcar/ApplicationTest.java +++ b/src/test/java/racingcar/ApplicationTest.java @@ -19,7 +19,7 @@ class ApplicationTest extends NsTest { run("pobi,woni", "1"); assertThat(output()).contains("pobi : -", "woni : ", "최종 우승자 : pobi"); }, - MOVING_FORWARD, STOP + MOVING_FORWARD, STOP //난수 반환 설정 ); }