From e793483b5ab3fec57f7a5c40e4dbc411b4cf3567 Mon Sep 17 00:00:00 2001 From: yireal Date: Tue, 26 Mar 2024 12:07:11 +0900 Subject: [PATCH 1/2] =?UTF-8?q?2=EC=B0=A8=20=EA=B3=BC=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/RacingGameApplication.java | 38 +++++++++++ src/main/java/RacingMain.java | 7 --- .../java/controller/RacingGameController.java | 55 ++++++++++++++++ src/main/java/domain/Car.java | 35 +++++++++++ src/main/java/domain/Cars.java | 63 +++++++++++++++++++ src/main/java/domain/Count.java | 20 ++++++ src/main/java/domain/Name.java | 30 +++++++++ src/main/java/domain/NumberGenerator.java | 6 ++ src/main/java/domain/Position.java | 44 +++++++++++++ src/main/java/domain/RacingGame.java | 33 ++++++++++ .../java/domain/RandomNumberGenerator.java | 12 ++++ src/main/java/view/InputParser.java | 27 ++++++++ src/main/java/view/InputValidator.java | 54 ++++++++++++++++ src/main/java/view/InputView.java | 38 +++++++++++ src/main/java/view/OutputView.java | 49 +++++++++++++++ 15 files changed, 504 insertions(+), 7 deletions(-) create mode 100644 src/main/java/RacingGameApplication.java delete mode 100644 src/main/java/RacingMain.java create mode 100644 src/main/java/controller/RacingGameController.java create mode 100644 src/main/java/domain/Car.java create mode 100644 src/main/java/domain/Cars.java create mode 100644 src/main/java/domain/Count.java create mode 100644 src/main/java/domain/Name.java create mode 100644 src/main/java/domain/NumberGenerator.java create mode 100644 src/main/java/domain/Position.java create mode 100644 src/main/java/domain/RacingGame.java create mode 100644 src/main/java/domain/RandomNumberGenerator.java create mode 100644 src/main/java/view/InputParser.java create mode 100644 src/main/java/view/InputValidator.java create mode 100644 src/main/java/view/InputView.java create mode 100644 src/main/java/view/OutputView.java diff --git a/src/main/java/RacingGameApplication.java b/src/main/java/RacingGameApplication.java new file mode 100644 index 0000000..cac2a46 --- /dev/null +++ b/src/main/java/RacingGameApplication.java @@ -0,0 +1,38 @@ + +import java.util.Scanner; +import controller.RacingGameController; +import view.InputParser; +import view.InputValidator; +import view.InputView; +import view.OutputView; + +public class RacingGameApplication { + + private static final Scanner scanner = new Scanner(System.in); + + public static void main(String[] args) { + RacingGameController racingGameController = new RacingGameController(inputView(), outputView()); + racingGameController.run(); + clean(); + } + + private static InputView inputView() { + return new InputView(inputValidator(), inputParser(), scanner); + } + + private static InputValidator inputValidator() { + return new InputValidator(); + } + + private static InputParser inputParser() { + return new InputParser(); + } + + private static OutputView outputView() { + return new OutputView(); + } + + private static void clean() { + scanner.close(); + } +} \ No newline at end of file diff --git a/src/main/java/RacingMain.java b/src/main/java/RacingMain.java deleted file mode 100644 index 4394287..0000000 --- a/src/main/java/RacingMain.java +++ /dev/null @@ -1,7 +0,0 @@ -public class RacingMain { - - public static void main(String[] args) { - // TODO: MVC 패턴을 기반으로 자동차 경주 미션 구현해보기 - System.out.println("Hello, World!"); - } -} diff --git a/src/main/java/controller/RacingGameController.java b/src/main/java/controller/RacingGameController.java new file mode 100644 index 0000000..2741cb0 --- /dev/null +++ b/src/main/java/controller/RacingGameController.java @@ -0,0 +1,55 @@ +package controller; + +import java.util.List; +import java.util.function.Supplier; +import domain.Car; +import domain.RacingGame; +import domain.RandomNumberGenerator; +import view.InputView; +import view.OutputView; + +public class RacingGameController { + + private final InputView inputView; + private final OutputView outputView; + + public RacingGameController(final InputView inputView, final OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public T retry(final Supplier supplier) { + try { + return supplier.get(); + } catch (IllegalArgumentException e) { + outputView.printErrorMessage(e.getMessage()); + return retry(supplier); + } + } + + public void run() { + RacingGame racingGame = initialize(); + play(racingGame); + findWinners(racingGame); + } + + private RacingGame initialize() { + List carNames = retry(inputView::readCarNames); + int count = retry(inputView::readCount); + return new RacingGame(new RandomNumberGenerator(), carNames, count); + } + + private void play(final RacingGame racingGame) { + outputView.printResultMessage(); + while (racingGame.isPlayable()) { + racingGame.play(); + List cars = racingGame.findCurrentCarPositions(); + outputView.printCurrentCarPositions(cars); + } + } + + private void findWinners(final RacingGame racingGame) { + List winners = racingGame.findWinners(); + outputView.printWinnersMessage(winners); + } +} \ No newline at end of file diff --git a/src/main/java/domain/Car.java b/src/main/java/domain/Car.java new file mode 100644 index 0000000..2e2e640 --- /dev/null +++ b/src/main/java/domain/Car.java @@ -0,0 +1,35 @@ +package domain; + +public class Car implements Comparable { + + private static final int MOVE_MIN_BOUND = 4; + + private final Name name; + private final Position position; + + + + public Car(final String name) { + this.name = new Name(name); + this.position = new Position(); + } + public void move(final int value){ + if (MOVE_MIN_BOUND <= value){ + position.increase(); + } + } + public boolean isSamePos(final Car car){ + return this.position.equals(car.position); + } + @Override + public int compareTo(final Car opponent){ + return this.position.compareTo(opponent.position); + } + public String getName(){ + return name.getValue(); + } + public int getPosition(){ + return position.getValue(); + } + +} diff --git a/src/main/java/domain/Cars.java b/src/main/java/domain/Cars.java new file mode 100644 index 0000000..b3a31a4 --- /dev/null +++ b/src/main/java/domain/Cars.java @@ -0,0 +1,63 @@ +package domain; + +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toUnmodifiableList; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class Cars { + + private static final String INVALID_WINNER_MESSAGE = "우승자가 존재하지 않습니다."; + private static final String DUPLICATED_NAMES_MESSAGE = "중복된 차 이름이 없어야 합니다."; + + private final List cars; + + public Cars(final List names) { + validate(names); + this.cars = toCars(names); + } + + private void validate(final List names) { + Set nonDuplicateNames = new HashSet<>(names); + if (names.size() != nonDuplicateNames.size()) { + throw new IllegalArgumentException(DUPLICATED_NAMES_MESSAGE); + } + } + + private List toCars(final List names) { + return names.stream() + .map(Car::new) + .collect(toList()); + } + + public void race(final NumberGenerator strategy) { + for (Car car : cars) { + car.move(strategy.generate()); + } + } + + public List findWinners() { + Car winner = findWinner(); + return findWinners(winner); + } + + private Car findWinner() { + return cars.stream() + .max(Car::compareTo) + .orElseThrow(() -> new IllegalArgumentException(INVALID_WINNER_MESSAGE)); + } + + private List findWinners(final Car winner) { + return cars.stream() + .filter(car -> car.isSamePos(winner)) + .map(Car::getName) + .collect(toUnmodifiableList()); + } + + public List getCars() { + return Collections.unmodifiableList(cars); + } +} \ No newline at end of file diff --git a/src/main/java/domain/Count.java b/src/main/java/domain/Count.java new file mode 100644 index 0000000..fbdee97 --- /dev/null +++ b/src/main/java/domain/Count.java @@ -0,0 +1,20 @@ +package domain; + +public class Count { + + private static final int PLAYABLE_MIN_COUNT = 1; + + private int value; + + Count(final int value) { + this.value = value; + } + + public void decrease() { + value--; + } + + public boolean isPlayable() { + return PLAYABLE_MIN_COUNT <= value; + } +} \ No newline at end of file diff --git a/src/main/java/domain/Name.java b/src/main/java/domain/Name.java new file mode 100644 index 0000000..7d7b931 --- /dev/null +++ b/src/main/java/domain/Name.java @@ -0,0 +1,30 @@ +package domain; + +public class Name { + + private static final int NAME_MIN_BOUND = 1; + private static final int NAME_MAX_BOUND = 5; + private static final String INVALID_NAME_LENGTH_MESSAGE = + "차의 이름은 " + NAME_MIN_BOUND + "자 이상, " + NAME_MAX_BOUND + "자 이하여야 합니다. 입력된 차 이름 : "; + + private final String value; + + Name(final String name) { + validate(name); + this.value = name; + } + + private void validate(final String name) { + if (isInvalidNameLength(name)) { + throw new IllegalArgumentException(INVALID_NAME_LENGTH_MESSAGE + name); + } + } + + private boolean isInvalidNameLength(final String name) { + return name.length() < NAME_MIN_BOUND || NAME_MAX_BOUND < name.length(); + } + + public String getValue() { + return value; + } +} \ No newline at end of file diff --git a/src/main/java/domain/NumberGenerator.java b/src/main/java/domain/NumberGenerator.java new file mode 100644 index 0000000..a945b09 --- /dev/null +++ b/src/main/java/domain/NumberGenerator.java @@ -0,0 +1,6 @@ +package domain; + +@FunctionalInterface +public interface NumberGenerator { + int generate(); +} \ No newline at end of file diff --git a/src/main/java/domain/Position.java b/src/main/java/domain/Position.java new file mode 100644 index 0000000..efeaa2b --- /dev/null +++ b/src/main/java/domain/Position.java @@ -0,0 +1,44 @@ +package domain; + +import java.util.Objects; + +public class Position implements Comparable { + + private static final int INITIAL_VALUE = 0; + + private int value; + + Position() { + this.value = INITIAL_VALUE; + } + + public void increase() { + value++; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Position position = (Position) o; + return getValue() == position.getValue(); + } + + @Override + public int hashCode() { + return Objects.hash(getValue()); + } + + @Override + public int compareTo(final Position other) { + return this.value - other.value; + } + + public int getValue() { + return value; + } +} \ No newline at end of file diff --git a/src/main/java/domain/RacingGame.java b/src/main/java/domain/RacingGame.java new file mode 100644 index 0000000..386f945 --- /dev/null +++ b/src/main/java/domain/RacingGame.java @@ -0,0 +1,33 @@ +package domain; + +import java.util.List; + +public class RacingGame { + + private final NumberGenerator numberGenerator; + private final Cars cars; + private final Count count; + + public RacingGame(final NumberGenerator numberGenerator, final List names, final int count) { + this.numberGenerator = numberGenerator; + this.cars = new Cars(names); + this.count = new Count(count); + } + + public boolean isPlayable() { + return count.isPlayable(); + } + + public void play() { + cars.race(numberGenerator); + count.decrease(); + } + + public List findCurrentCarPositions() { + return cars.getCars(); + } + + public List findWinners() { + return cars.findWinners(); + } +} \ No newline at end of file diff --git a/src/main/java/domain/RandomNumberGenerator.java b/src/main/java/domain/RandomNumberGenerator.java new file mode 100644 index 0000000..53ffde2 --- /dev/null +++ b/src/main/java/domain/RandomNumberGenerator.java @@ -0,0 +1,12 @@ +package domain; + +import java.util.Random; + +public class RandomNumberGenerator implements NumberGenerator{ + private static final int NUMBER_MAX_BOUND = 10; + private final Random random = new Random(); + @Override + public int generate() { + return random.nextInt(NUMBER_MAX_BOUND); + } +} diff --git a/src/main/java/view/InputParser.java b/src/main/java/view/InputParser.java new file mode 100644 index 0000000..a1db328 --- /dev/null +++ b/src/main/java/view/InputParser.java @@ -0,0 +1,27 @@ +package view; + +import static java.util.stream.Collectors.toUnmodifiableList; + +import java.util.Arrays; +import java.util.List; + +public class InputParser { + + private static final String PARSE_POINT = ","; + private static final int SPLIT_LIMIT = -1; + private static final String INVALID_COUNT_MESSAGE = "시도할 회수는 정수만 입력할 수 있습니다. 입력한 값 : "; + + public List splitAndParseNames(final String input) { + return Arrays.stream(input.split(PARSE_POINT, SPLIT_LIMIT)) + .map(String::trim) + .collect(toUnmodifiableList()); + } + + public int parseInt(final String input) { + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(INVALID_COUNT_MESSAGE + input); + } + } +} \ No newline at end of file diff --git a/src/main/java/view/InputValidator.java b/src/main/java/view/InputValidator.java new file mode 100644 index 0000000..dd901db --- /dev/null +++ b/src/main/java/view/InputValidator.java @@ -0,0 +1,54 @@ +package view; + +import static java.text.MessageFormat.format; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class InputValidator { + + private static final String INVALID_NAME_MESSAGE = "차의 이름은 {0}자 이상, {1}자 이하여야 합니다."; + private static final int NAME_LENGTH_MIN_BOUND = 1; + private static final int NAME_LENGTH_MAX_BOUND = 5; + private static final String DUPLICATED_NAMES_MESSAGE = "중복된 차 이름이 없어야 합니다."; + private static final String INVALID_COUNT_MESSAGE = "시도할 횟수는 {0}이상, {1}이하의 정수여야 합니다."; + private static final int COUNT_MIN_BOUND = 1; + private static final int COUNT_MAX_BOUND = 100; + + public void validateNames(final List names) { + names.forEach(this::validateNameLength); + validateDuplicateNames(names); + } + + private void validateNameLength(final String name) { + if (isInvalidNameLength(name)) { + throw new IllegalArgumentException( + format(INVALID_NAME_MESSAGE, NAME_LENGTH_MIN_BOUND, NAME_LENGTH_MAX_BOUND) + ); + } + } + + private boolean isInvalidNameLength(final String name) { + return name.length() < NAME_LENGTH_MIN_BOUND || NAME_LENGTH_MAX_BOUND < name.length(); + } + + private void validateDuplicateNames(final List names) { + Set nonDuplicatedNames = new HashSet<>(names); + if (names.size() != nonDuplicatedNames.size()) { + throw new IllegalArgumentException(DUPLICATED_NAMES_MESSAGE); + } + } + + public void validateCount(final int count) { + if (isInvalidCount(count)) { + throw new IllegalArgumentException( + format(INVALID_COUNT_MESSAGE, COUNT_MIN_BOUND, COUNT_MAX_BOUND) + ); + } + } + + private boolean isInvalidCount(final int count) { + return count < COUNT_MIN_BOUND || COUNT_MAX_BOUND < count; + } +} \ No newline at end of file diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 0000000..73b1b9a --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,38 @@ +package view; + +import java.util.List; +import java.util.Scanner; + +public class InputView { + + private static final String READ_NAMES_MESSAGE = "경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."; + private static final String READ_COUNT_MESSAGE = "시도할 회수는 몇회인가요?"; + + private final InputValidator inputValidator; + private final InputParser inputParser; + private final Scanner scanner; + + public InputView(final InputValidator inputValidator, final InputParser inputParser, final Scanner scanner) { + this.inputValidator = inputValidator; + this.inputParser = inputParser; + this.scanner = scanner; + } + + public List readCarNames() { + System.out.println(READ_NAMES_MESSAGE); + List names = inputParser.splitAndParseNames(scanner.nextLine()); + + inputValidator.validateNames(names); + + return names; + } + + public int readCount() { + System.out.println(READ_COUNT_MESSAGE); + int count = inputParser.parseInt(scanner.nextLine()); + + inputValidator.validateCount(count); + + return count; + } +} \ No newline at end of file diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 0000000..db6b19d --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,49 @@ +package view; + +import static java.text.MessageFormat.format; + +import java.util.List; +import java.util.stream.Collectors; +import domain.Car; + +public class OutputView { + + private static final String RESULT_MESSAGE = "\n실행 결과"; + private static final String POSITION_MESSAGE_FORMAT = "{0} : {1}"; + private static final String POSITION_MARK = "-"; + private static final String POSITION_MESSAGE_PARSER = "\n"; + private static final String WINNERS_MESSAGE_FORMAT = "{0}가 최종 우승했습니다."; + private static final String WINNERS_MESSAGE_PARSER = ", "; + private static final String ERROR_MESSAGE = "[ERROR] "; + + public void printResultMessage() { + System.out.println(RESULT_MESSAGE); + } + + public void printCurrentCarPositions(final List cars) { + System.out.println(generatePositionMessages(cars) + POSITION_MESSAGE_PARSER); + } + + private String generatePositionMessages(final List cars) { + return cars.stream() + .map(this::generatePositionMessage) + .collect(Collectors.joining(POSITION_MESSAGE_PARSER)); + } + + private String generatePositionMessage(final Car car) { + return format( + POSITION_MESSAGE_FORMAT, + car.getName(), + POSITION_MARK.repeat(car.getPosition()) + ); + } + + public void printWinnersMessage(final List winners) { + String winnersMessage = String.join(WINNERS_MESSAGE_PARSER, winners); + System.out.println(format(WINNERS_MESSAGE_FORMAT, winnersMessage)); + } + + public void printErrorMessage(final String message) { + System.out.println(ERROR_MESSAGE + message); + } +} \ No newline at end of file From 3938839fd5acf39006d7d56555dba49704abffe8 Mon Sep 17 00:00:00 2001 From: yireal Date: Tue, 26 Mar 2024 16:48:10 +0900 Subject: [PATCH 2/2] readme --- src/main/java/readme.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/main/java/readme.txt diff --git a/src/main/java/readme.txt b/src/main/java/readme.txt new file mode 100644 index 0000000..1002242 --- /dev/null +++ b/src/main/java/readme.txt @@ -0,0 +1,14 @@ + +1. 자동차 + 1. ,를 기준으로 입력 받음 + 2. 이름 + 1. 5자 이하 + 3. 전진 기능 + 1. 무작위로 0~9값을 구한 후 값이 4이상일 경우 전진 + 2. 전진할 때 자동차 위치와 이름을 같이 출력 + +2. 경주 + 1. 경주 횟수 + 1. 정수여야함 + 2. 우승자 출력 + 1. 여러명이면 ,로 구분해서 출력 \ No newline at end of file