diff --git a/README.md b/README.md new file mode 100644 index 00000000..aee219d8 --- /dev/null +++ b/README.md @@ -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] 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다. \ No newline at end of file diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 00000000..9bd76eca --- /dev/null +++ b/src/main/java/Application.java @@ -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); + + List names = RacingGameHost.getCarNames(sc); + int round = RacingGameHost.getCount(sc); + + RacingGame game = new RacingGame(names, round); + + RacingGameHost.playGame(game); + + RacingGameHost.projectWinners(game); + } +} diff --git a/src/main/java/Car.java b/src/main/java/Car.java new file mode 100644 index 00000000..103fcfad --- /dev/null +++ b/src/main/java/Car.java @@ -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) { + 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; + } +} diff --git a/src/main/java/Exceptions/InvalidCarNameLengthException.java b/src/main/java/Exceptions/InvalidCarNameLengthException.java new file mode 100644 index 00000000..cb2f17fc --- /dev/null +++ b/src/main/java/Exceptions/InvalidCarNameLengthException.java @@ -0,0 +1,6 @@ +package Exceptions; + +public class InvalidCarNameLengthException extends IllegalArgumentException implements + RacingException { + +} diff --git a/src/main/java/Exceptions/RacingException.java b/src/main/java/Exceptions/RacingException.java new file mode 100644 index 00000000..b9dc94a5 --- /dev/null +++ b/src/main/java/Exceptions/RacingException.java @@ -0,0 +1,5 @@ +package Exceptions; + +public interface RacingException { + +} diff --git a/src/main/java/NumberGenerator.java b/src/main/java/NumberGenerator.java new file mode 100644 index 00000000..618259ae --- /dev/null +++ b/src/main/java/NumberGenerator.java @@ -0,0 +1,4 @@ +public interface NumberGenerator { + + public int getNumber(); +} diff --git a/src/main/java/RacingGame.java b/src/main/java/RacingGame.java new file mode 100644 index 00000000..e25b6092 --- /dev/null +++ b/src/main/java/RacingGame.java @@ -0,0 +1,76 @@ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class RacingGame { + + private final List 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 names, int round) { + this.round = round; + this.cars = new ArrayList<>(); + + for (String name : names) { + this.addCar(Car.makeCar(name)); + } + } + + public List 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 getWinnerNames() { + int maxLocation = 0; + List 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; + } +} diff --git a/src/main/java/RacingGameHost.java b/src/main/java/RacingGameHost.java new file mode 100644 index 00000000..10a59643 --- /dev/null +++ b/src/main/java/RacingGameHost.java @@ -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 getCarNames(final Scanner sc) { + List 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 winnerNames = game.getWinnerNames(); + System.out.println(String.join(",", winnerNames) + "가 최종우승했습니다."); + } + + public static void projectCars(List cars) { + cars.forEach((car) -> { + String progressBar = "-".repeat(car.getCoordinateX()); + System.out.println(car.getName() + " : " + progressBar); + }); + } + + + public static void playGame(final RacingGame game) { + List cars = game.getCars(); + + for (int i = 0; i < game.getRound(); i++) { + game.proceedGame(); + RacingGameHost.projectCars(cars); + System.out.println(); + } + + } +} diff --git a/src/main/java/RandomNumberGenerator.java b/src/main/java/RandomNumberGenerator.java new file mode 100644 index 00000000..73ee6ba1 --- /dev/null +++ b/src/main/java/RandomNumberGenerator.java @@ -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); + } +} diff --git a/src/test/java/CarTest.java b/src/test/java/CarTest.java new file mode 100644 index 00000000..53c16c1e --- /dev/null +++ b/src/test/java/CarTest.java @@ -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); + } +} diff --git a/src/test/java/RacingGameTest.java b/src/test/java/RacingGameTest.java new file mode 100644 index 00000000..6f56fd1f --- /dev/null +++ b/src/test/java/RacingGameTest.java @@ -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 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 winnerNames = game1.getWinnerNames(); + + assertThat(winnerNames).containsExactlyInAnyOrderElementsOf(List.of("자동차3", "자동차2")); + } +} diff --git a/src/test/java/TestNumberGenerator.java b/src/test/java/TestNumberGenerator.java new file mode 100644 index 00000000..77a56fbf --- /dev/null +++ b/src/test/java/TestNumberGenerator.java @@ -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; + } +}