Skip to content

[중앙대 동네] 한성재 레이싱카 미션 #66

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
wants to merge 19 commits into
base: bingle625
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
817a2e5
docs: README.md에 1단계 TODO 작성
bingle625 Oct 4, 2024
91ee068
docs: 1단계 README 작성
bingle625 Oct 4, 2024
950db53
feat: 자동차 속성, 움직임 기능 구현
bingle625 Oct 4, 2024
46df3ff
refactor: 자동차 name, 좌표 속성 private 하게 전환 및 getter 추가
bingle625 Oct 4, 2024
bda9c1c
test: 자동차 이름 속성 테스트
bingle625 Oct 4, 2024
d98d93a
docs: README 를 통해 자동차 관련 구현사항 확인
bingle625 Oct 4, 2024
04759fe
feature: 레이싱 게임 클래스 추가 및 참여 기능 구현
bingle625 Oct 6, 2024
91488de
feat: 게임 진행 기능 및 승자 확인 기능 구현
bingle625 Oct 6, 2024
4d88902
feat: randomNumber를 만드는 NumberGenrator 인터페이스 분리
bingle625 Oct 12, 2024
c3647b1
test: 주석 정리 및 복수 우승자 테스트 추가
bingle625 Oct 12, 2024
fe72b38
fix: 우승자 구할 시에 로직 상 오류 수정
bingle625 Oct 12, 2024
43c5f4c
feat: 무작위 numberGenerator 클래스 구현
bingle625 Oct 12, 2024
c74a2cd
refactor: 메서드명 변경
bingle625 Oct 12, 2024
ccf524e
feat: 자동차 경주 게임 메인메서드 구현
bingle625 Oct 12, 2024
4d5d8f4
refactor: RacingGameHost 클래스 와 Application 클래스 분리
bingle625 Oct 12, 2024
44f8134
refactor: Application에서 view 코드 분리
bingle625 Oct 12, 2024
0d474fd
refactor: 자동차 names array로 입력받는 로직 RacingGameHost로 이동 및 exception 추가
bingle625 Oct 13, 2024
95e3a35
refactor: 프로덕션에서 Car 생성 메서드 추가
bingle625 Oct 13, 2024
e7257ab
refactor: 필요 없는 메서드 삭제
bingle625 Oct 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

# 자동차 경주

## 1. 움직이는 자동차 구하기
- [x] 자동차 구현
- [x] 이름 property 를 가짐
- [x] 좌표 property 를 가짐
- [x] 움직이는 기능
- [x] 0~9 무작위 숫자 4 이상 => 전진, 3 이하 => 전진
- [x] 코드 컨벤션
- [x] 인덴트 2를 넘지 않도록 구현
- [x] 3항 연산자 사용 X
- [x] `else` 사용 X
- [x] `switch/case` 사용 X
- [x] 메서드의 길이 15라인을 넘지 않도록
- [x] 메서드는 하나의 일만 수행하도록
- [x] 메인 메서드는 생성 X
- [x] 테스트 코드
- [x] 자동차 이름 확인
- [x] 자동차 움직임 기능 확인

## 2. 우승 자동차 구하기
- [x] 경주 게임 구현
- [x] n대의 자동차가 참여 가능
- [x] 횟수 x 번 동안 각 자동차 전진 혹은 정지
- [x] 경주 게임 완료 후 우승 자동차(중복 가능) 확인 가능
- [x] 코드 컨벤션
- [ ] 인덴트 2를 넘지 않도록 구현
- [x] 3항 연산자 사용 X
- [x] `else` 사용 X
- [x] `switch/case` 사용 X
- [x] 메서드의 길이 15라인을 넘지 않도록
- [x] 메서드는 하나의 일만 수행하도록
- [x] 메인 메서드는 생성 X
- [x] 테스트 코드
- [x] 자동차 움직임 기능 확인

## 3. 게임 실행
- [X] 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
- [X] 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
- [X] 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다.
- [X] 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
- [X] 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4 이상일 경우 전진하고, 3 이하의 값이면 멈춘다.
- [X] 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.
20 changes: 20 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Exceptions.InvalidCarNameLengthException;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class Application {

public static void main(String[] args) {
Scanner sc = new Scanner(System.in);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

깔끔하게 분리가 되었네요. 잘하셨습니다!


List<String> names = RacingGameHost.getCarNames(sc);
int round = RacingGameHost.getCount(sc);

RacingGame game = new RacingGame(names, round);

RacingGameHost.playGame(game);

RacingGameHost.projectWinners(game);
}
}
32 changes: 32 additions & 0 deletions src/main/java/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
public class Car {

private int coordinateX;
private final String name;
private final NumberGenerator numberGenerator;

public static Car makeCar(String name) {
return new Car(new RandomNumberGenerator(), 0, name);
}

public Car(NumberGenerator numberGenerator, int coordinateX, String name) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

production에선 NumberGenerator나 coordinateX의 경우에는 현재 요구사항에선 고정되어있어요.

numberGenerator -> RandomNumberGenerator
coordinateX -> 0

name만 받는 생성자를 만들어보면 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋은 방법인 것 같습니다!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name 만 인자로 받는 static 메서드를 Car 클래스 내부에 추가했습니다.
해당 방식은 어떨지 피드백 부탁드립니다.
95e3a35

this.numberGenerator = numberGenerator;
this.coordinateX = coordinateX;
this.name = name;
}

public void move() {
int randomNumber = this.numberGenerator.getNumber();

if (randomNumber >= 4) {
this.coordinateX += 1;
}
}

public String getName() {
return name;
}

public int getCoordinateX() {
return coordinateX;
}
}
6 changes: 6 additions & 0 deletions src/main/java/Exceptions/InvalidCarNameLengthException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package Exceptions;

public class InvalidCarNameLengthException extends IllegalArgumentException implements
RacingException {

}
5 changes: 5 additions & 0 deletions src/main/java/Exceptions/RacingException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package Exceptions;

public interface RacingException {

}
4 changes: 4 additions & 0 deletions src/main/java/NumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
public interface NumberGenerator {

public int getNumber();
}
76 changes: 76 additions & 0 deletions src/main/java/RacingGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class RacingGame {

private final List<Car> cars;
private final int round;

/**
* Constructor `self`
*/
public RacingGame() {
this.cars = new ArrayList<>();
this.round = 0;
}

/**
* Constructor `self`
*
* @param names 입력받은 차량 이름들
* @param round 입력받은 게임 라운드 수
*/
public RacingGame(List<String> names, int round) {
this.round = round;
this.cars = new ArrayList<>();

for (String name : names) {
this.addCar(Car.makeCar(name));
}
}

public List<Car> getCars() {
return this.cars;
}


public int getRound() {
return this.round;
}

/**
* @param car 게임 참여 차량
*/
public void addCar(Car car) {
this.cars.add(car);
}

/**
* 게임 진행
*/
public void proceedGame() {
this.cars.forEach(Car::move);
}

/**
* @return winners 현재 우승자 이름 배열
*/
public List<String> getWinnerNames() {
int maxLocation = 0;
List<String> winners = new ArrayList<>();

for (Car car : this.cars) {
int carLocation = car.getCoordinateX();
if (maxLocation < carLocation) {
maxLocation = carLocation;
winners.clear();
winners.add(car.getName());
} else if (maxLocation == carLocation) {
winners.add(car.getName());
}
}

return winners;
}
}
62 changes: 62 additions & 0 deletions src/main/java/RacingGameHost.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import Exceptions.InvalidCarNameLengthException;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class RacingGameHost {

public static List<String> getCarNames(final Scanner sc) {
List<String> result = null;
while (result == null) {
System.out.println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).");
String namesText = sc.nextLine();

try {
result = Arrays.stream(namesText.split(","))
.map(String::strip)
.peek(RacingGameHost::checkNameLength)
.toList();
} catch (InvalidCarNameLengthException exception) {
System.out.println("이름은 5자 이하만 입력 가능합니다.");
}

}

return result;
}

private static void checkNameLength(final String name) {
if (name.length() > 5) {
throw new InvalidCarNameLengthException();
}
}

public static int getCount(final Scanner sc) {
System.out.println("시도할 회수는 몇회인가요?");
return sc.nextInt();
}

public static void projectWinners(final RacingGame game) {
List<String> winnerNames = game.getWinnerNames();
System.out.println(String.join(",", winnerNames) + "가 최종우승했습니다.");
}

public static void projectCars(List<Car> cars) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어떤건 final을 붙였고, 어떤건 안붙였군요 성재님만의 기준이 있으신가요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final에 대한 기준이 아직 모호해서 발생하는 것 같습니다.
아직은 final을 명확히 붙여야겠다는 생각이 드는 파라미터나 변수에서 사용하게 되는거 같습니다.

따라서 기준이 있다기보다는 익숙치 않은 사용으로 봐주시면 될거 같습니다.ㅎㅎ

cars.forEach((car) -> {
String progressBar = "-".repeat(car.getCoordinateX());
System.out.println(car.getName() + " : " + progressBar);
});
}


public static void playGame(final RacingGame game) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RacingGameHost에서 입출력 뿐 아니라 게임도 진행시키는군요.

이렇게 설정하신 이유가 있을까요??

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 부분에 대해서 좀 고민을 했는데, progressBar가 시스템 상에서 출력이 되는 부분이 RacingGameHost 에 있는 편이 더 자연스럽다고 생각한것 같습니다.
이 부분에 대해서는 수업시간에 좀더 이야기해보고 싶습니다.

List<Car> cars = game.getCars();

for (int i = 0; i < game.getRound(); i++) {
game.proceedGame();
RacingGameHost.projectCars(cars);
System.out.println();
}

}
}
16 changes: 16 additions & 0 deletions src/main/java/RandomNumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import java.security.SecureRandom;
import java.util.Random;

public class RandomNumberGenerator implements NumberGenerator{

private final SecureRandom secureRandom;

public RandomNumberGenerator() {
this.secureRandom = new SecureRandom();
}

@Override
public int getNumber() {
return this.secureRandom.nextInt(9);
}
}
39 changes: 39 additions & 0 deletions src/test/java/CarTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("자동차 테스트")
public class CarTest {

@Test
@DisplayName("이름 속성 확인")
void testName() {
Car car = new Car(new TestNumberGenerator(3), 0, "자동차1");

String name = car.getName();

assertThat(name).isEqualTo("자동차1");
}

@Test
@DisplayName("숫자가 4이상일 때 자동차가 움직이는 지 테스트")
void testMove() {
Car car = new Car(new TestNumberGenerator(4),0, "자동차1");

car.move();

assertThat(car.getCoordinateX()).isEqualTo(1);
}

@Test
@DisplayName("숫자가 3 이하일 때 자동차가 움직이지 않는 지 테스트")
void testNotMove() {
Car car = new Car(new TestNumberGenerator(3), 0, "자동차1");

car.move();

assertThat(car.getCoordinateX()).isEqualTo(0);
}
}
62 changes: 62 additions & 0 deletions src/test/java/RacingGameTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("레이싱 게임 테스트")
public class RacingGameTest {

@Test
@DisplayName("자동차 참여 테스트")
void testCarJoin() {
Car car1 = new Car(new TestNumberGenerator(3), 0, "자동차1");
Car car2 = new Car(new TestNumberGenerator(4), 0, "자동차2");

RacingGame game1 = new RacingGame();

game1.addCar(car1);
game1.addCar(car2);

assertThat(game1.getCars().size()).isEqualTo(2);
}

@Test
@DisplayName("우승자 확인 테스트")
void testRacingWinner() {
Car car1 = new Car(new TestNumberGenerator(3), 0, "자동차1");
Car car2 = new Car(new TestNumberGenerator(4), 0, "자동차2");

RacingGame game1 = new RacingGame();
game1.addCar(car1);
game1.addCar(car2);

game1.proceedGame();

List<String> winnerNames = game1.getWinnerNames();

assertThat(winnerNames).containsExactlyInAnyOrderElementsOf(List.of("자동차2"));
}

@Test
@DisplayName("복수 우승자 확인 테스트")
void testMultipleWinner() {
Car car0 = new Car(new TestNumberGenerator(3), 0, "자동차0");
Car car1 = new Car(new TestNumberGenerator(3), 0, "자동차1");
Car car2 = new Car(new TestNumberGenerator(4), 0, "자동차2");
Car car3 = new Car(new TestNumberGenerator(4), 0, "자동차3");

RacingGame game1 = new RacingGame();
game1.addCar(car0);
game1.addCar(car1);
game1.addCar(car2);
game1.addCar(car3);

game1.proceedGame();

List<String> winnerNames = game1.getWinnerNames();

assertThat(winnerNames).containsExactlyInAnyOrderElementsOf(List.of("자동차3", "자동차2"));
}
}
13 changes: 13 additions & 0 deletions src/test/java/TestNumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
public class TestNumberGenerator implements NumberGenerator{

private final int setNumber;

public TestNumberGenerator(int number) {
this.setNumber = number;
}

@Override
public int getNumber() {
return this.setNumber;
}
}