diff --git a/README.md b/README.md deleted file mode 100644 index ac237bb90..000000000 --- a/README.md +++ /dev/null @@ -1,187 +0,0 @@ -# 미션 - 자동차 경주 게임 - -## 🔍 진행방식 - -- 미션은 **기능 요구사항, 프로그래밍 요구사항, 과제 진행 요구사항** 세 가지로 구성되어 있다. -- 세 개의 요구사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다. - -## ✉️ 미션 제출 방법 - -- 미션 구현을 완료한 후 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점 방지 - -- 터미널에서 `java -version`을 실행해 자바 8인지 확인한다. 또는 Eclipse, IntelliJ IDEA와 같은 IDE의 자바 8로 실행하는지 확인한다. -- 터미널에서 맥 또는 리눅스 사용자의 경우 `./gradlew clean test`, 윈도우 사용자의 경우 `gradlew.bat clean test` 명령을 실행했을 때 모든 테스트가 아래와 같이 통과하는지 확인한다. - -``` -BUILD SUCCESSFUL in 0s -``` - ---- - -## 🚀 기능 요구사항 - -초간단 자동차 경주 게임을 구현한다. - -- 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. -- 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다. -- 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다. -- 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. -- 전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다. -- 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다. -- 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다. -- 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다. -- 아래의 프로그래밍 실행 결과 예시와 동일하게 입력과 출력이 이루어져야 한다. - -
- -## ✍🏻 입출력 요구사항 - -### ⌨️ 입력 - -- 경주 할 자동차 이름(이름은 쉼표(,) 기준으로 구분) - -``` -pobi,woni,jun -``` - -- 시도할 회수 - -``` -5 -``` - -### 🖥 출력 - -- 각 차수별 실행 결과 - -``` -pobi : -- -woni : ---- -jun : --- -``` - -- 단독 우승자 안내 문구 - -``` -최종 우승자 : pobi -``` - -- 공동 우승자 안내 문구 - -``` -최종 우승자 : pobi, jun -``` - -- 예외 상황 시 에러 문구를 출력해야 한다. 단, 에러 문구는 [ERROR] 로 시작해야 한다. - -``` -[ERROR] 시도 횟수는 숫자여야 한다. -``` - -### 💻 프로그래밍 실행 결과 예시 - -``` -경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분) -pobi,woni,jun -시도할 회수는 몇회인가요? -5 - -실행 결과 -pobi : - -woni : -jun : - - -pobi : -- -woni : - -jun : -- - -pobi : --- -woni : -- -jun : --- - -pobi : ---- -woni : --- -jun : ---- - -pobi : ----- -woni : ---- -jun : ----- - -최종 우승자 : pobi, jun -``` - -
- -## 🎱 프로그래밍 요구사항 - -- 프로그램을 실행하는 시작점은 `Application`의 `main()`이다. -- JDK 8 버전에서 실행 가능해야 한다. **JDK 8에서 정상 동작하지 않을 경우 0점 처리**한다. -- 자바 코드 컨벤션을 지키면서 프로그래밍한다. - - https://naver.github.io/hackday-conventions-java -- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다. - - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. - - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다. -- 3항 연산자를 쓰지 않는다. -- 함수(또는 메소드)가 한 가지 일만 하도록 최대한 작게 만들어라. -- 프로그래밍 요구사항에서 별도로 변경 불가 안내가 없는 경우 파일 수정과 패키지 이동을 자유롭게 할 수 있다. - -### 추가된 요구사항 - -- 함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다. - - 함수(또는 메소드)가 한 가지 일만 잘 하도록 구현한다. -- else 예약어를 쓰지 않는다. - - 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다. - - else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다. - -### 프로그래밍 요구사항 - Car 객체 - -- 다음 Car 객체를 활용해 구현해야 한다. -- Car 기본 생성자를 추가할 수 없다. -- name, position 변수의 접근 제어자인 private을 변경할 수 없다. -- 가능하면 setPosition(int position) 메소드를 추가하지 않고 구현한다. - -```java -public class Car { - private final String name; - private int position = 0; - - public Car(String name) { - this.name = name; - } - - // 추가 기능 구현 -} -``` - -### 프로그래밍 요구사항 - Randoms, Console - -- JDK에서 기본 제공하는 Random, Scanner API 대신 `camp.nextstep.edu.missionutils`에서 제공하는 `Randoms`, `Console` API를 활용해 구현해야 한다. - - Random 값 추출은 `camp.nextstep.edu.missionutils.Randoms`의 `pickNumberInRange()`를 활용한다. - - 사용자가 입력하는 값은 `camp.nextstep.edu.missionutils.Console`의 `readLine()`을 활용한다. -- 프로그램 구현을 완료했을 때 `src/test/java` 디렉터리의 `ApplicationTest`에 있는 모든 테스트 케이스가 성공해야 한다. **테스트가 실패할 경우 0점 처리한다.** - -
- ---- - -## 📈 과제 진행 요구사항 - -- 미션은 [java-racingcar-precourse](https://github.com/woowacourse/java-racingcar-precourse) 저장소를 Fork/Clone해 시작한다. -- **기능을 구현하기 전에 java-racingcar-precourse/docs/README.md 파일에 구현할 기능 목록을 정리**해 추가한다. -- **Git의 커밋 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위**로 추가한다. - - [AngularJS Commit Message Conventions](https://gist.github.com/stephenparish/9941e89d80e2bc58a153) 참고해 commit log를 남긴다. -- 과제 진행 및 제출 방법은 [프리코스 과제 제출 문서](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) 를 참고한다. - -
- ---- - -## 📝 License - -This project is [MIT](https://github.com/woowacourse/java-racingcar-precourse/blob/master/LICENSE) licensed. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..bc41d2df1 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,59 @@ +# 새로 알게된 것 & 부족한 것 +[어려웠던 점]
+문제 요구사항을 읽고 난 뒤에 기능을 구현하는 것 자체가 어렵지는 않았다. +하지만 기능 목록 정리를 위해 전체 프로그램을 기능 단위로 분리하는 것과 전체 프로그램을 잘 설계하는 것이 너무 복잡하다고 느꼈다. +반드시 필요한 함수들로만 구성하고 함수들을 가장 작은 단위로 쪼개는 것을 목표로 했는데 이 두 가지를 고려하면서 프로그램을 짜는 것이 어려웠던 것 같다. +그리고 '함수는 한 가지 일만 잘 하도록 구현한다' 조건에 맞게 각 함수가 한 가지 일만 하도록 구성하는 것도 쉽지 않았다. +예를 들어 startGame()이라는 메서드를 추가하는 과정에서 '게임 시작을 위한 기능이 아니면 모두 분리해야하나?'하는 생각이 들었다. +모든 기능이 유기적으로 연결되어 있어서 주관적인 생각으로 기능을 분리하면 되는 것인가 하는 생각이 들었다. +아직 코드에 대해 조금 더 고민해봐야 할 것 같다. 3기 크루였던 분의 블로그에서 MVC패턴에 맞게 설계한다는 내용을 봤는데, 한번 찾아봐야할 것 같다. + +[문법 관련]
+- 이터레이터 사용법 +- ArrayList 사용법 +- join, split 하는 방법 +- Integer.parseInt : primitive int형으로 반환, Integer.valueOf() : Integer 객체로 반환 +- Vector : 동적 추가 + +[기타] +- 프로젝트 시작 전 gradle, File Structure 버전8로 세팅 확인하기 +- git branch push 할 때 ```git push origin swonny``` (main과 혼동하지 말기) +- unindent(들여쓰기 취소) : ⇧(shift) + ⇥(tab) +- MVC패턴 + +# 기능 목록 + +## 1. 자동차 ✔️ +### [속성] ✔️ +- 자동차 이름
+- 위치
+ +### [기능] +- 기능 1 [자동차 이름 부여] ✔️
+ - 자동차 이름 입력 받기
+ - 자동차 이름 유효성 검사 (5글자 이하)
+ - 자동차 이름 부여 및 생성
+
+- 기능 2 [자동차 전진] ✔️
+ - 0 ~ 9 사이의 무작위 값이 4 이상이라면 전진한다. 전진한 값을 입력받는다.
+ - 전진하는 자동차를 출력한다. 전진하는 자동차 이름을 출력하고 전진 상황은 하이픈(-)으로 나타낸다.
+ +## 2. 자동차 경주 게임 ✔️ +### [기능] +- 기능 1 [게임 시작] ✔️
+ - 사용자에게 반복 횟수 입력받는다.
+ - 잘못된 값을 입력하면 exceptionError 발생시킨다.
+
+- 기능 2 [게임 종료] ✔️
+ - 경주 완료 후 누가 우승했는지 알려준다.
+ - (우승자는 한 명 이상일 수 있다.)
+ - (우승자가 여러명일 경우 쉼표로 구분한다.)
+ +## 3. 사용자 입력 +### [기능] +- 기능 1 [사용자 입력값 받기] ✔️
+
+- 기능 2 [예외 처리] ✔️
+ - '[ERROR]'를 포함한 문자열
+ - 자동차 이름 입력 오류 처리
+ - 반복 횟수 입력 오류 처리 ()
\ No newline at end of file diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index b9ed0456a..ef5ac2aa7 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -3,5 +3,7 @@ public class Application { public static void main(String[] args) { // TODO 구현 진행 + Game game = new Game(); + game.playGame(); } -} +} \ No newline at end of file diff --git a/src/main/java/racingcar/Car.java b/src/main/java/racingcar/Car.java index ab3df9492..10e6b4e61 100644 --- a/src/main/java/racingcar/Car.java +++ b/src/main/java/racingcar/Car.java @@ -9,4 +9,15 @@ public Car(String name) { } // 추가 기능 구현 + public void moveForward () { + position += 1; + } + + public int getPosition() { + return position; + } + + public String getName() { + return name; + } } diff --git a/src/main/java/racingcar/Game.java b/src/main/java/racingcar/Game.java new file mode 100644 index 000000000..2522c4a2a --- /dev/null +++ b/src/main/java/racingcar/Game.java @@ -0,0 +1,90 @@ +package racingcar; + +import camp.nextstep.edu.missionutils.Randoms; + +import java.util.Vector; + +public class Game { + Player player; + Car[] cars; + int rounds; + int firstPosition; + Vector winners; + + Game() { // Game의 생성자, 게임 시작을 위한 초기 세팅 + winners = new Vector(); + firstPosition = 0; + player = new Player(); + rounds = player.rounds; + cars = new Car[player.cars.length]; // 자동차 n개의 배열을 위한 메모리 할당 + for (int i = 0; i < player.cars.length; i++) { // n개의 car 인스턴스 생성 + cars[i] = new Car(player.cars[i]); + } + } + + public void playGame() { // 입력 횟수만큼 반복 + for (int i = 0; i < rounds; i++) { + startRound(); + printProgress(); + } + getWinners(); + finishGame(); + } + + public void startRound() { + for (int i = 0; i < cars.length; i++) { + if (moveForward(Randoms.pickNumberInRange(0, 9))) { // 난수 생성 및 4보다 큰지 판단 + cars[i].moveForward(); + } + } + } + + public boolean moveForward(int randomNumber) { + if (randomNumber >= 4) { + return true; + } + + return false; + } + + public void printProgress() { + for (int i = 0; i < cars.length; i++) { + String name = cars[i].getName(); + int position = cars[i].getPosition(); + System.out.print(name + " : "); + for (int j = 0; j < position; j++) { + System.out.print("-"); + } + System.out.println(); + } + System.out.println(); + } + + public int getFirstPosition() { + int firstPosition = 0; + for (int i = 0; i < cars.length; i++) { + int tmp = cars[i].getPosition(); + if (tmp < firstPosition) { + continue; + } + firstPosition = tmp; + } + + return firstPosition; + } + + public void getWinners() { + int firstPosition = getFirstPosition(); + for (int i = 0; i < cars.length; i++) { + int tmp = cars[i].getPosition(); + if (tmp == firstPosition) { + winners.add(cars[i].getName()); + } + } + } + + public void finishGame() { + String result = String.join(", ", winners); + System.out.print("최종 우승자 : " + result); + } +} diff --git a/src/main/java/racingcar/Player.java b/src/main/java/racingcar/Player.java new file mode 100644 index 000000000..1930305c4 --- /dev/null +++ b/src/main/java/racingcar/Player.java @@ -0,0 +1,59 @@ +package racingcar; + +import camp.nextstep.edu.missionutils.Console; + +public class Player { + private final String ASK_CAR_NAME = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"; + private final String GET_ROUNDS = "시도할 회수는 몇회인가요?"; + int rounds; + String[] cars; + + Player() { + getCarNames(); + getRounds(); + } + + public void getCarNames() { + System.out.println(ASK_CAR_NAME); + String names = Console.readLine(); + try { + cars = splitString(names); + for (int i = 0; i < cars.length; i++) { + checkNames(cars[i]); + } + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + getCarNames(); + } + + } + + public String[] splitString(String str) { + return str.split(","); + } + + public void getRounds() { + System.out.println(GET_ROUNDS); + String tmp = Console.readLine(); + try { + checkRounds(tmp); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + getRounds(); + } + } + + public void checkNames(String name) throws IllegalArgumentException { + if (name.length() > 5) { + throw new IllegalArgumentException("[ERROR] 이름은 다섯 글자를 넘길 수 없다."); + } + } + + public void checkRounds(String rounds) throws IllegalArgumentException { + try { + this.rounds = Integer.parseInt(rounds); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("[ERROR] 시도 횟수는 숫자여야 한다."); + } + } +}