diff --git a/README.md b/README.md index 8d7e8aee..96f8ee2b 100644 --- a/README.md +++ b/README.md @@ -1 +1,41 @@ -# java-baseball-precourse \ No newline at end of file +# java-racingcar-precourse + + +## πŸš€ κΈ°λŠ₯ λͺ©λ‘ + +### βœ… car κΈ°λŠ₯ +- [x] μ΄λ™ν•œλ‹€. +- [x] 이름을 λ°˜ν™˜ν•œλ‹€. +- [x] μœ„μΉ˜λ₯Ό λ°˜ν™˜ν•œλ‹€. + +### βœ… car service κΈ°λŠ₯ +- [x] μžλ™μ°¨λ“€μ„ λ§Œλ“ λ‹€. +- [x] μžλ™μ°¨λ“€μ„ random값을 μ΄μš©ν•˜μ—¬ μ΄λ™μ‹œν‚¨λ‹€. +- [x] 우승자λ₯Ό μ°ΎλŠ”λ‹€. +- [x] λΌμš΄λ“œλ§ˆλ‹€ μžλ™μ°¨λ“€μ˜ κ²°κ³Όλ₯Ό μ•Œλ €μ€€λ‹€. + +### βœ… controller κΈ°λŠ₯ +- [x] μžλ™μ°¨ 이름을 μž…λ ₯을 λ°›λŠ”λ‹€. +- [x] μ‹œλ„νšŸμˆ˜λ₯Ό μž…λ ₯λ°›λŠ”λ‹€. +- [x] 우승자λ₯Ό μ°ΎλŠ”λ‹€. +- [x] car game을 μ§„ν–‰ν•œλ‹€. + +### βœ… view κΈ°λŠ₯ +- [x] μž…λ ₯을 λ°›λŠ”λ‹€. +- [x] μž…λ ₯의 κΈ°λ³Έ 검증을 ν•œλ‹€. +- [x] 좜λ ₯을 보여쀀닀. +- [x] μ—λŸ¬λ₯Ό 좜λ ₯ν•œλ‹€. + + +## πŸ˜• μž…λ ₯ μ—λŸ¬ +### βœ… μž…λ ₯ μ—λŸ¬ +- [x] μž…λ ₯ λ¬Έμžμ—΄μ΄ 곡백일 경우 + +### βœ… μ–‘μˆ˜ μž…λ ₯ μ—λŸ¬ +- [x] μ •μˆ˜κ°€ 아닐 경우 +- [x] μ–‘μˆ˜κ°€ 아닐 경우 + +### βœ… car 이름 μž…λ ₯ +- [x] 길이가 5κΈ€μž μ΄ν•˜μ—¬μ•Ό 함. +- [x] μžλ™μ°¨ 이름이 곡백인 경우 +- [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..ceb401a4 --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,8 @@ +import controller.GameController; + +public class Application { + public static void main(String[] args) { + GameController gameController = new GameController(); + gameController.start(); + } +} diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java new file mode 100644 index 00000000..5770eabb --- /dev/null +++ b/src/main/java/controller/GameController.java @@ -0,0 +1,59 @@ +package controller; + +import domain.CarService; +import domain.RandomNumberGenerator; +import java.util.Arrays; +import view.ErrorView; +import view.InputView; +import view.OutputView; + +public class GameController { + private final CarService carService; + private Integer roundNum; + + public GameController() { + this.carService = new CarService(new RandomNumberGenerator()); + } + + public void start() { + setUpCars(); + setUpRoundNum(); + runRound(); + findWinner(); + } + + private void setUpCars() { + boolean success = false; + do { + try { + carService.addCars(Arrays.stream(InputView.inputCarNames().split(",")).toList()); + success = true; + } catch (IllegalArgumentException e) { + ErrorView.printError(e); + } + } while (!success); + } + + private void setUpRoundNum() { + boolean success = false; + do { + try { + this.roundNum = Integer.parseInt(InputView.inputRoundNum()); + success = true; + } catch (IllegalArgumentException e) { + ErrorView.printError(e); + } + } while (!success); + } + + private void runRound() { + OutputView.printRoundResultInit(); + for (int i = 0; i < roundNum; i++) { + OutputView.printRoundResult(carService.run()); + } + } + + private void findWinner() { + OutputView.printWinner(carService.findWinner()); + } +} diff --git a/src/main/java/domain/Car.java b/src/main/java/domain/Car.java new file mode 100644 index 00000000..003cb90e --- /dev/null +++ b/src/main/java/domain/Car.java @@ -0,0 +1,34 @@ +package domain; + +public class Car { + private static final Integer BOUNDARY_VALUE = 4; + + private final String name; + private Integer distance; + + public Car(String name) { + this.name = name; + this.distance = 0; + } + + public void move(Integer number) { + if (number.compareTo(BOUNDARY_VALUE) >= 0) { + advance(); + } + } + + private void advance() { + this.distance++; + } + + /** + * get + */ + public String getName() { + return this.name; + } + + public Integer getDistance() { + return this.distance; + } +} diff --git a/src/main/java/domain/CarService.java b/src/main/java/domain/CarService.java new file mode 100644 index 00000000..fd74dd1c --- /dev/null +++ b/src/main/java/domain/CarService.java @@ -0,0 +1,97 @@ +package domain; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class CarService { + private static final Integer CAR_NAME_LENGTH = 5; + + private final List cars = new ArrayList<>(); + private final NumberGenerator numberGenerator; + + public CarService(NumberGenerator numberGenerator) { + this.numberGenerator = numberGenerator; + } + + // public void addCars(String[] names) { +// for (String name : names) { +// validateCarName(name); +// cars.add(new Car(name)); +// } +// } + public void addCars(List names) { + validateCarNames(names); + for (String name : names) { + cars.add(new Car(name)); + } + } + + public Map run() { + Map roundResult = new HashMap<>(); + for (Car car : cars) { + car.move(numberGenerator.generate()); + roundResult.put(car.getName(), car.getDistance()); + } + return roundResult; + } + + public List findWinner() { + Car winner = cars.stream() + .max(Comparator.comparingInt(Car::getDistance)).get(); + return cars.stream() + .filter(car -> car.getDistance().equals(winner.getDistance())) + .map(Car::getName) + .collect(Collectors.toList()); + } + + /** + * validate + */ + private void validateCarNames(List names) throws IllegalArgumentException { + validateCarNamesEmpty(names); + validateCarNamesLength(names); + validateCarNameDup(names); + } + + private void validateCarNamesLength(List names) throws IllegalArgumentException { + if (names.stream().anyMatch(name -> name.length() > CAR_NAME_LENGTH)) { + throw new IllegalArgumentException("μžλ™μ°¨ 이름이 λ„ˆλ¬΄ κΉλ‹ˆλ‹€."); + } + } + + private void validateCarNamesEmpty(List names) throws IllegalArgumentException { + if (names.stream().anyMatch(String::isEmpty)) { + throw new IllegalArgumentException("μžλ™μ°¨ 이름이 κ³΅λ°±μž…λ‹ˆλ‹€."); + } + } + + private void validateCarNameDup(List names) throws IllegalArgumentException { + if (names.stream().distinct().count() < names.size()) { + throw new IllegalArgumentException("μžλ™μ°¨ 이름이 쀑볡 λ©λ‹ˆλ‹€."); + } + } + + // private void validateCarName(String name) throws IllegalArgumentException { +// validateCarNameLength(name); +// validateCarNameDup(name); +// } +// +// private void validateCarNameLength(String name) throws IllegalArgumentException { +// if (name.length() > CAR_NAME_LENGTH) { +// throw new IllegalArgumentException("μžλ™μ°¨ 이름이 λ„ˆλ¬΄ κΉλ‹ˆλ‹€."); +// } +// if (name.isEmpty()) { +// throw new IllegalArgumentException("μžλ™μ°¨ 이름이 κ³΅λ°±μž…λ‹ˆλ‹€."); +// } +// } +// +// private void validateCarNameDup(String name) throws IllegalArgumentException { +// if (cars.stream().anyMatch(car -> car.getName().equals(name))) { +// throw new IllegalArgumentException("μžλ™μ°¨ 이름이 쀑볡 λ©λ‹ˆλ‹€."); +// } +// } +} diff --git a/src/main/java/domain/NumberGenerator.java b/src/main/java/domain/NumberGenerator.java new file mode 100644 index 00000000..ff72b20b --- /dev/null +++ b/src/main/java/domain/NumberGenerator.java @@ -0,0 +1,5 @@ +package domain; + +public interface NumberGenerator { + public Integer generate(); +} diff --git a/src/main/java/domain/RandomNumberGenerator.java b/src/main/java/domain/RandomNumberGenerator.java new file mode 100644 index 00000000..ae5626d2 --- /dev/null +++ b/src/main/java/domain/RandomNumberGenerator.java @@ -0,0 +1,20 @@ +package domain; + + +import java.util.Random; + +public class RandomNumberGenerator implements NumberGenerator{ + +// @Override +// public Integer generate(){ +// Random random = new Random(); +// random.setSeed(System.currentTimeMillis()); +// return random.nextInt(9); +// } + + @Override + public Integer generate() { + double randomValue = Math.random(); + return (int)(randomValue*10); + } +} diff --git a/src/main/java/view/ErrorView.java b/src/main/java/view/ErrorView.java new file mode 100644 index 00000000..cc7b6eae --- /dev/null +++ b/src/main/java/view/ErrorView.java @@ -0,0 +1,7 @@ +package view; + +public class ErrorView { + public static void printError(RuntimeException error){ + System.out.println("[ERROR] "+error.getMessage()); + } +} diff --git a/src/main/java/view/InputValidator.java b/src/main/java/view/InputValidator.java new file mode 100644 index 00000000..b5c4a2c4 --- /dev/null +++ b/src/main/java/view/InputValidator.java @@ -0,0 +1,33 @@ +package view; + +public class InputValidator { + public static void validateString(String input) { + isNotEmpty(input); + } + + public static void validatePositiveNumber(String input) { + isNotEmpty(input); + isInteger(input); + isPositive(input); + } + + private static void isNotEmpty(String input) { + if (input.isEmpty()) { + throw new IllegalArgumentException("μž…λ ₯ λ¬Έμžμ—΄μ΄ κ³΅λ°±μž…λ‹ˆλ‹€."); + } + } + + private static void isInteger(String input) { + try { + Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("μ •μˆ˜λ₯Ό μž…λ ₯ν•΄ μ£Όμ„Έμš”."); + } + } + + private static void isPositive(String input) { + if (Integer.parseInt(input) < 1) { + throw new IllegalArgumentException("μ–‘μˆ˜λ₯Ό μž…λ ₯ν•΄ μ£Όμ„Έμš”."); + } + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 00000000..9f02ec76 --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,39 @@ +package view; + +import java.util.Scanner; + +public class InputView { + private static final Scanner sc = new Scanner(System.in); + + public static String inputCarNames() { + boolean success = false; + String input; + do { + System.out.println("κ²½μ£Όν•  μžλ™μ°¨ 이름을 μž…λ ₯ν•˜μ„Έμš”.(이름은 μ‰Όν‘œ(,) κΈ°μ€€μœΌλ‘œ ꡬ뢄)"); + input = sc.next(); + try { + InputValidator.validateString(input); + success = true; + } catch (IllegalArgumentException e) { + ErrorView.printError(e); + } + } while (!success); + return input; + } + + public static String inputRoundNum() { + boolean success = false; + String input; + do { + System.out.println("μ‹œλ„ν•  νšŒμˆ˜λŠ” λͺ‡νšŒμΈκ°€μš”?"); + input = sc.next(); + try { + InputValidator.validatePositiveNumber(input); + success = true; + } catch (IllegalArgumentException e) { + ErrorView.printError(e); + } + } while (!success); + return input; + } +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 00000000..6803f622 --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,22 @@ +package view; + +import java.util.List; +import java.util.Map; + +public class OutputView { + public static void printRoundResultInit() { + System.out.println(); + System.out.println("μ‹€ν–‰ κ²°κ³Ό"); + } + + public static void printRoundResult(Map cars) { + for (Map.Entry car : cars.entrySet()) { + System.out.println(car.getKey() + " : " + "-".repeat(car.getValue())); + } + System.out.println(); + } + + public static void printWinner(List winners){ + System.out.print("μ΅œμ’… 우승자 : "+String.join(", ",winners)); + } +} diff --git a/src/test/java/domain/CarServiceTest.java b/src/test/java/domain/CarServiceTest.java new file mode 100644 index 00000000..f648f974 --- /dev/null +++ b/src/test/java/domain/CarServiceTest.java @@ -0,0 +1,90 @@ +package domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CarServiceTest { + static class FakeNumberGenerator implements NumberGenerator { +// private static final Integer[] numbers = {4,2,5,8,8,5,4,5,6,1,2,3,3,3,7}; + private static final Integer[] numbers = {4,5,6,4,5,6,4,5,6,4,5,6,4,5,6}; + private int i = 0; + + @Override + public Integer generate(){ + return numbers[i++]; + } + + public static int getNumbersSize(){ + return numbers.length; + } + } + + CarService carService; + + @BeforeEach + void setCarService(){ + this.carService = new CarService(new FakeNumberGenerator()); + } + + @DisplayName("car 이름 쀑볡 ν…ŒμŠ€νŠΈ") + @Test + void car_names_dup_test() { + //given + List names = List.of("pobi", "woni", "jun", "pobi"); + //when + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> carService.addCars(names)); + //then + // μ˜ˆμ™Έμ˜ νƒ€μž… 확인 + assertThat(exception).isInstanceOf(IllegalArgumentException.class); + // μ˜ˆμ™Έ λ©”μ‹œμ§€ 확인 + assertThat(exception.getMessage()).isEqualTo("μžλ™μ°¨ 이름이 쀑볡 λ©λ‹ˆλ‹€."); + } + + @DisplayName("car 이름 곡백 ν…ŒμŠ€νŠΈ") + @Test + void car_names_empty_test() { + //given + List names = List.of("pobi", "woni", "jun", ""); + //when + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> carService.addCars(names)); + //then + // μ˜ˆμ™Έμ˜ νƒ€μž… 확인 + assertThat(exception).isInstanceOf(IllegalArgumentException.class); + // μ˜ˆμ™Έ λ©”μ‹œμ§€ 확인 + assertThat(exception.getMessage()).isEqualTo("μžλ™μ°¨ 이름이 κ³΅λ°±μž…λ‹ˆλ‹€."); + } + + @DisplayName("car 이름 길이 ν…ŒμŠ€νŠΈ") + @Test + void car_names_length_test() { + //given + List names = List.of("pobi", "woni", "abcdef"); + //when + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> carService.addCars(names)); + //then + // μ˜ˆμ™Έμ˜ νƒ€μž… 확인 + assertThat(exception).isInstanceOf(IllegalArgumentException.class); + // μ˜ˆμ™Έ λ©”μ‹œμ§€ 확인 + assertThat(exception.getMessage()).isEqualTo("μžλ™μ°¨ 이름이 λ„ˆλ¬΄ κΉλ‹ˆλ‹€."); + } + + @DisplayName("κ²½μ£Ό 우승자 μ°ΎκΈ°") + @Test + void car_find_winner_test() { + //given + List names = List.of("pobi", "woni", "jun"); + //when + carService.addCars(names); + for(int i=0;i winners = carService.findWinner(); + //then + assertThat(winners).isEqualTo(List.of("pobi","woni","jun")); + } +} \ No newline at end of file diff --git a/src/test/java/domain/CarTest.java b/src/test/java/domain/CarTest.java new file mode 100644 index 00000000..ee674aa5 --- /dev/null +++ b/src/test/java/domain/CarTest.java @@ -0,0 +1,46 @@ +package domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CarTest { + + @DisplayName("car μ „μ§„ ν…ŒμŠ€νŠΈ") + @Test + void move_test_when_advance() { + //given + Car car = new Car("advance"); + //when + car.move(4); + //then + assertThat(car.getDistance()).isEqualTo(1); + } + + @DisplayName("car 멈좀 ν…ŒμŠ€νŠΈ") + @Test + void move_test_when_stop() { + //given + Car car = new Car("stop"); + //when + car.move(3); + //then + assertThat(car.getDistance()).isEqualTo(0); + } + + @DisplayName("car move ν…ŒμŠ€νŠΈ") + @Test + void move_test() { + //given + Car car = new Car("move"); + //when + car.move(0); + car.move(8); + car.move(9); + car.move(5); + //then + assertThat(car.getDistance()).isEqualTo(3); + } + +} \ No newline at end of file