diff --git a/.gitignore b/.gitignore index 5dca701a77..d7f6d9dedb 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,18 @@ out/ ### Mac OS ### .DS_Store +.AppleDouble +.LSOverride + +### Eclipse ### +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders diff --git a/README.md b/README.md index d0286c859f..2b7ff1f120 100644 --- a/README.md +++ b/README.md @@ -1 +1,31 @@ # java-racingcar-precourse + +## ︎⚙️ .gitignore 설정 +### "[7기] 프리코스 1주 차 웹 백엔드 피드백" +* "Git으로 관리할 자원을 고려한다" 내용 중, "IntelliJ IDEA의 .idea 폴더와 Eclipse의 .metadata 폴더" 제외 + +## 🚀 초간단 자동차 경주 게임을 구현한다. +* 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. +* 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다. +* 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다. +* 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. +* 전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다. +* 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다. +* 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다. +* 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 애플리케이션은 종료되어야 한다. + +## ︎📝 구현할 기능 목록 +### - 경주에 참여하는 자동차 이름을 입력 받기 +* 자동차 경주게임에 참여하는 차량의 이름을 입력받는다. 이 때, MVC 구조를 고려한다. +* 사용자에게 전달되는 메시지는 상수를 이용한다. +* 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 애플리케이션을 종료한다. +* 자동차 이름의 입력은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다. +* 차량의 대수를 저장소에 저장하고, 저장소에 저장된 개수를 체크하는 테스트코드 작성한다. +### - 자동차 레이싱 시도 회수 입력 받기 +* 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. +* 자동차 레이싱 시도 회수 입력값 검증 +### - Repository에 여러 자동차 저장 +* 저장소를 만들어 경기에 참여하는 차량들을 보관 +### - 저장소에 있는 자동차들을 이용해서 레이싱 게임 처리 +* 전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다. +* 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다. \ No newline at end of file diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index a17a52e724..f0a9dfaa9d 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,7 +1,10 @@ package racingcar; +import racingcar.controller.RacingcarController; + public class Application { + private static RacingcarController racingcarController = RacingcarController.getInstance(); public static void main(String[] args) { - // TODO: 프로그램 구현 + racingcarController.playGame(); } } diff --git a/src/main/java/racingcar/controller/RacingcarController.java b/src/main/java/racingcar/controller/RacingcarController.java new file mode 100644 index 0000000000..5941a11f4d --- /dev/null +++ b/src/main/java/racingcar/controller/RacingcarController.java @@ -0,0 +1,111 @@ +package racingcar.controller; + +import static camp.nextstep.edu.missionutils.Console.readLine; + +import java.util.ArrayList; +import java.util.List; +import racingcar.domain.enums.InterfaceMsg; +import racingcar.domain.enums.ValidationMsg; +import racingcar.dto.RacingCarInitDto; +import racingcar.service.RacingCarService; +import racingcar.service.StringUtilService; +import racingcar.service.ValidatorService; +import racingcar.view.InputView; +import racingcar.view.OutputView; + +public class RacingcarController { + private final InputView inputView; + private final ValidatorService validatorService = ValidatorService.getInstance(); + private StringUtilService stringUtilService = StringUtilService.getInstance(); + private RacingCarService racingCarService = RacingCarService.getInstance(); + + private List carNameList = new ArrayList<>(); + private Integer inputCarRaceTimes = 0; + + private final OutputView outputView; + + // start: singleton holer + private RacingcarController() { + inputView = new InputView(); + outputView = new OutputView(); + } + + private static class InnerRacingcarController { + private static final RacingcarController INSTANCE = new RacingcarController(); + } + + public static RacingcarController getInstance() { + return InnerRacingcarController.INSTANCE; + } + // end: singleton holer + + public void playGame() { + setCarNameList(validateCarNames(inputView.requestInputCarName())); + setInputCarRaceTimes(validateCarRaceTimes(inputView.requestInputCarRaceTimes())); + initSaveRacingCar(); + System.out.println(InterfaceMsg.PRINT_OUT.getValue()); + outputView.printCarRacing(racingCarService.playCarRacing()); + outputView.printCarRacing(racingCarService.carRacingResult()); + } + + private List validateCarNames(String inputCarName) throws IllegalArgumentException { + ValidationMsg validationMsg = validatorService.validationCarName(inputCarName); + if (validationMsg != ValidationMsg.PROPER_TYPE) { + throw new IllegalArgumentException(validationMsg.getValue()); + } + + return stringUtilService.splitByComma(inputCarName); + } + + public void setCarNameList(List carNameList) { + this.carNameList = carNameList; + } + + public List getCarNameList() { + return carNameList; + } + + public void setInputCarRaceTimes(Integer inputCarRaceTimes) { + this.inputCarRaceTimes = inputCarRaceTimes; + } + + public Integer getInputCarRaceTimes() { + return inputCarRaceTimes; + } + + + private Integer validateCarRaceTimes(String inputCarRaceTimes) { + Integer properCarRaceTimes = 0; + try { + properCarRaceTimes = this.properCarRaceTimes(inputCarRaceTimes); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); inputCarRaceTimes = requestInputCarRaceTimes(); + return validateCarRaceTimes(inputCarRaceTimes); + } + return properCarRaceTimes; + } + + private String requestInputCarRaceTimes() { + System.out.print(InterfaceMsg.REQUEST_INPUT_CAR_RACE_TIMES.getValue()); + String inputCarRaceTimes = readLine(); + System.out.println(inputCarRaceTimes); // Player로부터 자동차 경주의 회수를 입력받음 + + return inputCarRaceTimes; + } + + private Integer properCarRaceTimes(String inputCarRaceTimes) throws IllegalArgumentException { + ValidationMsg validationMsg = validatorService.validationCarRaceTimes(inputCarRaceTimes); + if (validationMsg != ValidationMsg.PROPER_TYPE) { + throw new IllegalArgumentException(validationMsg.getValue()); + } + return Integer.valueOf(inputCarRaceTimes); + } + + private void initSaveRacingCar() { + RacingCarInitDto racingCarInitDto = RacingCarInitDto.builder() + .carNameList(this.getCarNameList()) + .inputCarRaceTimes(this.getInputCarRaceTimes()) + .build(); + racingCarService.initSaveRacingCar(racingCarInitDto); + } +} diff --git a/src/main/java/racingcar/domain/enums/InterfaceMsg.java b/src/main/java/racingcar/domain/enums/InterfaceMsg.java new file mode 100644 index 0000000000..55efa9bd21 --- /dev/null +++ b/src/main/java/racingcar/domain/enums/InterfaceMsg.java @@ -0,0 +1,22 @@ +package racingcar.domain.enums; + +public enum InterfaceMsg { + REQUEST_INPUT_CAR_NAME("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분) "), + REQUEST_INPUT_CAR_RACE_TIMES("시도할 횟수는 몇 회인가요? "), + PRINT_OUT("실행 결과"), + GAME_RESULT("최종 우승자 : ") + ; + + private String value; + InterfaceMsg(String value) { + this.value = value; + } + + public String getKey() { + return name(); + } + + public String getValue() { + return value; + } +} diff --git a/src/main/java/racingcar/domain/enums/NumberTypeGameRule.java b/src/main/java/racingcar/domain/enums/NumberTypeGameRule.java new file mode 100644 index 0000000000..07e32a4bca --- /dev/null +++ b/src/main/java/racingcar/domain/enums/NumberTypeGameRule.java @@ -0,0 +1,23 @@ +package racingcar.domain.enums; + +public enum NumberTypeGameRule { + MAX_CAR_NAME_LENGTH(5), + MOVING_FORWARD(4), + MIN_RAMDOM(0), + MAX_RAMDOM(9) + ; + + private final int value; + + NumberTypeGameRule(int value) { + this.value = value; + } + + public String getKey() { + return name(); + } + + public Integer getValue() { + return value; + } +} diff --git a/src/main/java/racingcar/domain/enums/ValidationMsg.java b/src/main/java/racingcar/domain/enums/ValidationMsg.java new file mode 100644 index 0000000000..cb1d82d6eb --- /dev/null +++ b/src/main/java/racingcar/domain/enums/ValidationMsg.java @@ -0,0 +1,26 @@ +package racingcar.domain.enums; + +public enum ValidationMsg { + NULL_TYPE("[ERROR] 입력하신 값은 NULL이 될 수 없습니다."), + EMPTY_TYPE("[ERROR] 자동차 경주 게임에서 사용될 값이 입력되지 않았습니다."), + NEED_MORE_THEN_TWO("[ERROR] 자동차를 2대이상 입력해주세요"), + CAR_NAME_OVER_FIVE("[ERROR] 자동차의 이름은 5자 이하만 가능합니다."), + NOT_DUPLICATE_NAME("[ERROR] 자동차의 이름들은 중복될 수 없습니다."), + NOT_NUMBER("[ERROR] 숫자만 입력해주세요."), + ZERO_TYPE("[ERROR] 자동차 경주 게임에서 사용될 값은 0이 될 수 없습니다."), + PROPER_TYPE("바르게 입력되었습니다.") + ; + + private String value; + ValidationMsg(String value) { + this.value = value; + } + + public String getKey() { + return name(); + } + + public String getValue() { + return value; + } +} diff --git a/src/main/java/racingcar/domain/racingcar/RacingCar.java b/src/main/java/racingcar/domain/racingcar/RacingCar.java new file mode 100644 index 0000000000..abf531fa1c --- /dev/null +++ b/src/main/java/racingcar/domain/racingcar/RacingCar.java @@ -0,0 +1,61 @@ +package racingcar.domain.racingcar; + +import camp.nextstep.edu.missionutils.Randoms; +import java.util.Objects; +import racingcar.domain.enums.ValidationMsg; + +public class RacingCar { + private static final int MOVING_FORWARD = 4; + private static final int STOP = 3; + + private static final int MIN_RAMDOM = 0; + private static final int MAX_RAMDOM = 9; + + private String carName; + private Integer carPosition; + + public RacingCar(String carName) { + validateCarName(carName); + this.carName = carName; + this.carPosition = 0; + } + + private void validateCarName(String carName) { + if (carName == null || carName.isEmpty() || carName.length() == 0) { + throw new IllegalStateException(ValidationMsg.EMPTY_TYPE.getValue()); + } + } + + public String getCarName() { + return carName; + } + + public Integer getCarPosition() { + return carPosition; + } + + private void movingForwardRacingCar() { + this.carPosition++; + } + + public void movingForward() { + if (Randoms.pickNumberInRange(MIN_RAMDOM, MAX_RAMDOM) >= MOVING_FORWARD) { + this.movingForwardRacingCar(); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + RacingCar racingCar = (RacingCar)o; + return Objects.equals(carName, racingCar.carName); + } + + @Override + public int hashCode() { + return Objects.hash(carName); + } +} diff --git a/src/main/java/racingcar/domain/racingcar/RacingCarRepository.java b/src/main/java/racingcar/domain/racingcar/RacingCarRepository.java new file mode 100644 index 0000000000..0135f2a5cd --- /dev/null +++ b/src/main/java/racingcar/domain/racingcar/RacingCarRepository.java @@ -0,0 +1,61 @@ +package racingcar.domain.racingcar; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import racingcar.dto.RacingCarDto; + +public class RacingCarRepository { + private Map racingCarMap = new LinkedHashMap<>(); + + // start: Singleton Holder + private RacingCarRepository() { + } + + private static class RacingCarRepositoryHoler { + private static final RacingCarRepository RACING_CAR_REPOSITORY = new RacingCarRepository(); + } + + public static RacingCarRepository getInstance() { + return RacingCarRepositoryHoler.RACING_CAR_REPOSITORY; + } + + public void saveRacingCar(String carName) { + racingCarMap.put(carName, new RacingCar(carName)); + } + + public void initSaveRacingCar(RacingCarDto racingCarDto) { + racingCarMap.put(racingCarDto.getCarName(), new RacingCar(racingCarDto.getCarName())); + } + + public void initSaveRacingCar(String carName) { + racingCarMap.put(carName, new RacingCar(carName)); + } + + public RacingCar getRacingCarByName(String carName) { + return racingCarMap.get(carName); + } + + public void movingForwardByName(RacingCarDto racingCarDto) { + racingCarMap.get(racingCarDto.getCarName()).movingForward(); + } + + public Map getRacingCarMap() { + return racingCarMap; + } + + public LinkedHashMap sortRacingCarMapByValueDesc(Map map) { + List> entryList = new LinkedList<>(map.entrySet()); + Collections.sort(entryList, (racingCar, comparedRacingCar) -> comparedRacingCar.getValue().getCarPosition().compareTo(racingCar.getValue().getCarPosition())); + LinkedHashMap racingCarLinkedHashMap = new LinkedHashMap<>(); + for (Map.Entry entity : entryList) { + racingCarLinkedHashMap.put(entity.getKey(), entity.getValue()); + } + + return racingCarLinkedHashMap; + } +} + diff --git a/src/main/java/racingcar/dto/RacingCarDto.java b/src/main/java/racingcar/dto/RacingCarDto.java new file mode 100644 index 0000000000..5e17f59db5 --- /dev/null +++ b/src/main/java/racingcar/dto/RacingCarDto.java @@ -0,0 +1,53 @@ +package racingcar.dto; + +public class RacingCarDto { + private String carName; + private Integer carPosition; + + private RacingCarDto() { + } + + public RacingCarDto(String carName) { + this.carName = carName; + } + + // start: Builder 패턴 + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private RacingCarDto dto = new RacingCarDto(); + + public Builder carName(String carName) { + dto.carName = carName; + return this; + } + + public Builder carPosition(Integer carPosition) { + dto.carPosition = carPosition; + return this; + } + + public RacingCarDto build() { + return dto; + } + } + // end: Builder 패턴 + + public String getCarName() { + return carName; + } + + public void setCarName(String carName) { + this.carName = carName; + } + + public Integer getCarPosition() { + return carPosition; + } + + public void setCarPosition(Integer carPosition) { + this.carPosition = carPosition; + } +} diff --git a/src/main/java/racingcar/dto/RacingCarInitDto.java b/src/main/java/racingcar/dto/RacingCarInitDto.java new file mode 100644 index 0000000000..46706a846d --- /dev/null +++ b/src/main/java/racingcar/dto/RacingCarInitDto.java @@ -0,0 +1,55 @@ +package racingcar.dto; + +import java.util.ArrayList; +import java.util.List; + +/** + * 자동차 경주 게임에 참여하는 자동차 목록과 레이싱 시도 회수를 보관하는 DTO + */ +public class RacingCarInitDto { + private List carNameList = new ArrayList<>(); + private Integer inputCarRaceTimes = 0; + + private RacingCarInitDto() { + } + + // start: Builder 패턴 + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private RacingCarInitDto dto = new RacingCarInitDto(); + + public Builder carNameList(List carNameList) { + dto.carNameList = carNameList; + return this; + } + + public Builder inputCarRaceTimes(Integer inputCarRaceTimes) { + dto.inputCarRaceTimes = inputCarRaceTimes; + return this; + } + + public RacingCarInitDto build() { + return dto; + } + } + // end: Builder 패턴 + + public List getCarNameList() { + return carNameList; + } + + public void setCarNameList(List carNameList) { + this.carNameList = carNameList; + } + + public Integer getInputCarRaceTimes() { + return inputCarRaceTimes; + } + + public void setInputCarRaceTimes(Integer inputCarRaceTimes) { + this.inputCarRaceTimes = inputCarRaceTimes; + } +} diff --git a/src/main/java/racingcar/service/RacingCarService.java b/src/main/java/racingcar/service/RacingCarService.java new file mode 100644 index 0000000000..dee0393c9c --- /dev/null +++ b/src/main/java/racingcar/service/RacingCarService.java @@ -0,0 +1,85 @@ +package racingcar.service; + +import java.util.Map; +import java.util.StringJoiner; +import racingcar.domain.enums.InterfaceMsg; +import racingcar.domain.racingcar.RacingCar; +import racingcar.domain.racingcar.RacingCarRepository; +import racingcar.dto.RacingCarInitDto; + +public class RacingCarService { + private final RacingCarRepository racingCarRepository = RacingCarRepository.getInstance(); + + private Integer inputCarRaceTimes = 0; + + private StringBuffer playStateStringBuffer = new StringBuffer(); + + private RacingCarService() { + } + + private static class RacingCarServiceHolder { + private static final RacingCarService RACING_CAR_SERVICE = new RacingCarService(); + } + + public static RacingCarService getInstance() { + return RacingCarServiceHolder.RACING_CAR_SERVICE; + } + + public void saveRacingCar(RacingCarInitDto racingCarInitDto) { + this.inputCarRaceTimes = racingCarInitDto.getInputCarRaceTimes(); + for (String carName : racingCarInitDto.getCarNameList()) { + racingCarRepository.saveRacingCar(carName); + } + } + + public void initSaveRacingCar(RacingCarInitDto racingCarInitDto) { + this.inputCarRaceTimes = racingCarInitDto.getInputCarRaceTimes(); + for (String carName : racingCarInitDto.getCarNameList()) { + racingCarRepository.initSaveRacingCar(carName); + } + } + + public Map getRacingCarMap() { + return racingCarRepository.getRacingCarMap(); + } + + public String playCarRacing() { + playStateStringBuffer.setLength(0); // 초기화 + + Map racingCarMap = this.getRacingCarMap(); + for (int i = 0; i < this.inputCarRaceTimes; i++) { + racingCarMap.forEach((key, val) -> val.movingForward()); + playRacingStatus(); + } + return playStateStringBuffer.toString(); + } + + private void playRacingStatus() { + Map racingCarMap = this.getRacingCarMap(); + racingCarMap.forEach((key, val) -> this.playMoveForward(key, val.getCarPosition())); + playStateStringBuffer.append(System.lineSeparator()); // OS에 맞는 개행 문자 추가 + } + + private void playMoveForward(String carName, Integer carPosition) { + playStateStringBuffer.append(carName + " : "); + for (int i = 0; i < carPosition; i++) { + playStateStringBuffer.append("-"); + } + playStateStringBuffer.append(System.lineSeparator()); // OS에 맞는 개행 문자 추가 + } + + + public String carRacingResult() { + StringBuffer printCarRacingResult = new StringBuffer(); + Map racingCarMap = racingCarRepository.sortRacingCarMapByValueDesc(this.getRacingCarMap()); + StringJoiner stringJoiner = new StringJoiner(", "); + Integer maxMoveForwardPosition = (racingCarMap.entrySet().iterator().next().getValue()).getCarPosition(); + racingCarMap.forEach((key, val) -> { + if (val.getCarPosition() >= maxMoveForwardPosition) + stringJoiner.add(key); + }); + printCarRacingResult.append(InterfaceMsg.GAME_RESULT.getValue() + stringJoiner); + + return printCarRacingResult.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/racingcar/service/StringUtilService.java b/src/main/java/racingcar/service/StringUtilService.java new file mode 100644 index 0000000000..53368a4ae2 --- /dev/null +++ b/src/main/java/racingcar/service/StringUtilService.java @@ -0,0 +1,30 @@ +package racingcar.service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class StringUtilService { + // start: Singleton Holder + private StringUtilService() { + } + + private static class InnerValidatorService { + private static final StringUtilService instance = new StringUtilService(); + } + + public static StringUtilService getInstance() { + return InnerValidatorService.instance; + } + // end: Singleton Holder + + public List splitByComma(String strInput) { + if (strInput.indexOf(",") == -1) return new ArrayList<>(Arrays.asList(strInput.trim())); + List stringList = new ArrayList<>(); + for (String strEach : strInput.split(",")) { + stringList.add(strEach.trim()); + } + + return stringList; + } +} diff --git a/src/main/java/racingcar/service/ValidatorService.java b/src/main/java/racingcar/service/ValidatorService.java new file mode 100644 index 0000000000..5add66ef3f --- /dev/null +++ b/src/main/java/racingcar/service/ValidatorService.java @@ -0,0 +1,60 @@ +package racingcar.service; + +import java.util.HashSet; +import java.util.Set; +import racingcar.domain.enums.NumberTypeGameRule; +import racingcar.domain.enums.ValidationMsg; + +public class ValidatorService { + private StringUtilService stringUtilService = StringUtilService.getInstance(); + private ValidatorService() { + } + private static class ValidatorServiceHolder { + private static final ValidatorService VALIDATOR_SERVICE = new ValidatorService(); + } + public static ValidatorService getInstance() { + return ValidatorServiceHolder.VALIDATOR_SERVICE; + } + + public ValidationMsg validationCarName(String carName) { + if (carName == null) throw new IllegalArgumentException(String.valueOf(ValidationMsg.NULL_TYPE)); + if (carName.isEmpty()) throw new IllegalArgumentException(String.valueOf(ValidationMsg.EMPTY_TYPE)); + if (!this.isPassCarNameLength(carName)) { + throw new IllegalArgumentException(String.valueOf(ValidationMsg.CAR_NAME_OVER_FIVE)); + } + if (this.isDuplicateUserInput(carName)) { + throw new IllegalArgumentException(String.valueOf(ValidationMsg.NOT_DUPLICATE_NAME)); + } + + return ValidationMsg.PROPER_TYPE; + } + + + private boolean isPassCarNameLength(String carName) { + for (String eachCar : stringUtilService.splitByComma(carName)) { + if (eachCar.length() > NumberTypeGameRule.MAX_CAR_NAME_LENGTH.getValue()) return false; + } + return true; + } + // 중복 입력된 값이 있는지 체크 + public boolean isDuplicateUserInput(String userInput) { + String[] arrUserInput = userInput.split(","); + Set userInputSet = new HashSet<>(); + for (String userInputNum : arrUserInput) { + userInputSet.add(userInputNum.trim()); + } + return (arrUserInput.length != userInputSet.size()); + } + public ValidationMsg validationCarRaceTimes(String RaceTimes) { + if (RaceTimes == null) throw new IllegalArgumentException(String.valueOf(ValidationMsg.NULL_TYPE)); + if (RaceTimes.isEmpty()) throw new IllegalArgumentException(String.valueOf(ValidationMsg.EMPTY_TYPE)); + try { + Integer.valueOf(RaceTimes); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(String.valueOf(ValidationMsg.NOT_NUMBER)); + } + if (RaceTimes.equals("0")) throw new IllegalArgumentException(String.valueOf(ValidationMsg.ZERO_TYPE)); + + return ValidationMsg.PROPER_TYPE; + } +} diff --git a/src/main/java/racingcar/view/InputView.java b/src/main/java/racingcar/view/InputView.java new file mode 100644 index 0000000000..87b9e83ee0 --- /dev/null +++ b/src/main/java/racingcar/view/InputView.java @@ -0,0 +1,25 @@ +package racingcar.view; + +import static camp.nextstep.edu.missionutils.Console.readLine; + +import racingcar.domain.enums.InterfaceMsg; + +public class InputView { + + public String requestInputCarName() { + System.out.print(InterfaceMsg.REQUEST_INPUT_CAR_NAME.getValue()); + String inputCarName = readLine(); + System.out.println(inputCarName); + + return inputCarName; + } + + // Player로부터 자동차 경주의 회수를 입력받음 + public String requestInputCarRaceTimes() { + System.out.print(InterfaceMsg.REQUEST_INPUT_CAR_RACE_TIMES.getValue()); + String inputCarRaceTimes = readLine(); + System.out.println(inputCarRaceTimes); + + return inputCarRaceTimes; + } +} diff --git a/src/main/java/racingcar/view/OutputView.java b/src/main/java/racingcar/view/OutputView.java new file mode 100644 index 0000000000..8e53f90f03 --- /dev/null +++ b/src/main/java/racingcar/view/OutputView.java @@ -0,0 +1,7 @@ +package racingcar.view; + +public class OutputView { + public void printCarRacing(String result) { + System.out.println(result); + } +} diff --git a/src/test/java/racingcar/controller/RacingcarControllerTest.java b/src/test/java/racingcar/controller/RacingcarControllerTest.java new file mode 100644 index 0000000000..ddea003bb7 --- /dev/null +++ b/src/test/java/racingcar/controller/RacingcarControllerTest.java @@ -0,0 +1,169 @@ +package racingcar.controller; + +import static camp.nextstep.edu.missionutils.Console.readLine; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import camp.nextstep.edu.missionutils.test.NsTest; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import racingcar.domain.enums.InterfaceMsg; +import racingcar.domain.enums.ValidationMsg; +import racingcar.domain.racingcar.RacingCar; +import racingcar.dto.RacingCarInitDto; +import racingcar.service.RacingCarService; +import racingcar.service.RacingCarServiceTest; +import racingcar.service.ValidatorServiceTest; + +public class RacingcarControllerTest extends NsTest { + private ValidatorServiceTest validatorServiceTest; + private RacingCarServiceTest racingCarServiceTest; + private List carNameList; + + @BeforeEach + void setUp() { + validatorServiceTest = ValidatorServiceTest.getInstance(); + racingCarServiceTest = RacingCarServiceTest.getInstance(); + carNameList = new ArrayList<>(Arrays.asList("pobi", "woni", "crong")); + } + + @Test + void 자동차_이름_string_null_입력_체크() { + assertEquals(ValidationMsg.NULL_TYPE, validatorServiceTest.getInstance().validationCarName(null)); + } + + @Test + void 자동차_이름_string_empty_입력_체크() { + assertEquals(ValidationMsg.EMPTY_TYPE, validatorServiceTest.getInstance().validationCarName("")); + } + + @Test + void 자동차_2대_이상_검증() { + String carName = "pobi"; final boolean expectFalseMoreThanTwoCar = false; + String carNames = "pobi,javaji"; final boolean expectTrueMoreThanTwoCar = true; + + boolean isFalseMoreThanTwoCar = validatorServiceTest.isMoreThanTwoCar(carName); + boolean isTrueMoreThanTwoCar = validatorServiceTest.isMoreThanTwoCar(carNames); + + assertEquals(expectFalseMoreThanTwoCar, isFalseMoreThanTwoCar); + assertEquals(expectTrueMoreThanTwoCar, isTrueMoreThanTwoCar); + } + + @Test + void 자동차_2대_검증_with_IllegalAccessException() { + assertThrows(IllegalArgumentException.class,() -> { + System.out.print(InterfaceMsg.REQUEST_INPUT_CAR_NAME.getValue()); run("pobi"); + String carName = readLine(); System.out.println(carName); + ValidationMsg validationMsg = validatorServiceTest.getInstance().validationCarName(carName); + if (validationMsg == ValidationMsg.NEED_MORE_THEN_TWO) { + System.out.println(validationMsg.getValue()); throw new IllegalArgumentException(); + } + }); + } + + @Test + void 자동차_이름_길이_유효성_검증() { + assertThrows(IllegalArgumentException.class,() -> { + System.out.print(InterfaceMsg.REQUEST_INPUT_CAR_NAME.getValue()); run("pobi,javaji"); + String carName = readLine(); System.out.println(carName); + ValidationMsg validationMsg = validatorServiceTest.getInstance().validationCarName(carName); + if (validationMsg == ValidationMsg.CAR_NAME_OVER_FIVE) { + System.out.println(validationMsg.getValue()); throw new IllegalArgumentException(); + } + }); + } + + @Test + void 자동차_이름_중복_검증() { + assertThrows(IllegalArgumentException.class,() -> { + System.out.print(InterfaceMsg.REQUEST_INPUT_CAR_NAME.getValue()); run("pobi, pobi"); + String carName = readLine(); System.out.println(carName); + ValidationMsg validationMsg = validatorServiceTest.getInstance().validationCarName(carName); + if (validationMsg == ValidationMsg.NOT_DUPLICATE_NAME) { + System.out.println(validationMsg.getValue()); throw new IllegalArgumentException(); + } + }); + } + + @Test + void 자동차_레이싱_시도_회수_string_null_입력_체크() { + assertEquals(ValidationMsg.NULL_TYPE, validatorServiceTest.getInstance().validationCarRaceTimes(null)); + } + + @Test + void 자동차_레이싱_시도_회수_string_empty_입력_체크() { + assertEquals(ValidationMsg.EMPTY_TYPE, validatorServiceTest.getInstance().validationCarRaceTimes("")); + } + + @DisplayName("자동차_레이싱_시도_회수_입력값_0_검증") + @Test + void 자동차_레이싱_시도_회수_입력값_0_검증() { + assertThrows(IllegalArgumentException.class,() -> { + System.out.print(InterfaceMsg.REQUEST_INPUT_CAR_RACE_TIMES.getValue()); run("0"); + String carName = readLine(); System.out.println(carName); + ValidationMsg validationMsg = validatorServiceTest.getInstance().validationCarRaceTimes(carName); + if (validationMsg == ValidationMsg.ZERO_TYPE) { + System.out.println(validationMsg.getValue()); throw new IllegalArgumentException(); + } + }); + } + + @Test + void 자동차_레이싱_시도_회수_입력값_숫자_이외_값_검증() { + assertThrows(IllegalArgumentException.class,() -> { + System.out.print(InterfaceMsg.REQUEST_INPUT_CAR_RACE_TIMES.getValue()); run("pobi"); + String carName = readLine(); System.out.println(carName); + ValidationMsg validationMsg = validatorServiceTest.getInstance().validationCarRaceTimes(carName); + if (validationMsg == ValidationMsg.NOT_NUMBER) { + System.out.println(validationMsg.getValue()); throw new IllegalArgumentException(); + } + }); + } + + @Test + void RacingCarServiceTest를_통해_RacingCarRepository에_여러_자동차_저장() { + RacingCarInitDto racingCarInitDto = RacingCarInitDto.builder().carNameList(carNameList).inputCarRaceTimes(1).build(); + + racingCarServiceTest.initSaveRacingCar(racingCarInitDto); + + Map racingCarMap = racingCarServiceTest.getRacingCarMap(); + racingCarMap.forEach((key, val) -> assertThat(carNameList).contains(key)); + } + + @Test + void RacingCarService를_통해_RacingCarRepository에_여러_자동차_저장() { + RacingCarInitDto racingCarInitDto = RacingCarInitDto.builder().carNameList(carNameList).inputCarRaceTimes(1).build(); + + RacingCarService.getInstance().initSaveRacingCar(racingCarInitDto); + + RacingCarService.getInstance().getRacingCarMap().forEach((key, val) -> assertThat(carNameList).contains(key)); + RacingCarService.getInstance().getRacingCarMap().forEach((key, val) -> val.movingForward()); + RacingCarService.getInstance().getRacingCarMap().forEach((key, val) -> System.out.println(key + " : " + val.getCarPosition())); + } + + @Test + void RacingCarRepository가_가진_RacingCar들의_이름과_전진한_값을_출력() { + RacingCarInitDto racingCarInitDto = RacingCarInitDto.builder().carNameList(carNameList).inputCarRaceTimes(5).build(); + racingCarServiceTest.initSaveRacingCar(racingCarInitDto); + racingCarServiceTest.playCarRacing(); + } + + @Test + void 최종_우승자_결과출력() { + RacingCarInitDto racingCarInitDto = RacingCarInitDto.builder().carNameList(carNameList).inputCarRaceTimes(5).build(); + racingCarServiceTest.initSaveRacingCar(racingCarInitDto); + racingCarServiceTest.playCarRacing(); + racingCarServiceTest.carRacingResult(); + } + + @Override + protected void runMain() { + + } +} diff --git a/src/test/java/racingcar/service/RacingCarServiceTest.java b/src/test/java/racingcar/service/RacingCarServiceTest.java new file mode 100644 index 0000000000..f29006079c --- /dev/null +++ b/src/test/java/racingcar/service/RacingCarServiceTest.java @@ -0,0 +1,98 @@ +package racingcar.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Map; +import java.util.StringJoiner; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import racingcar.domain.enums.InterfaceMsg; +import racingcar.domain.racingcar.RacingCar; +import racingcar.domain.racingcar.RacingCarRepository; +import racingcar.dto.RacingCarDto; +import racingcar.dto.RacingCarInitDto; + +public class RacingCarServiceTest { + private RacingCarDto racingCarDto; + private RacingCarRepository racingCarRepository = RacingCarRepository.getInstance(); + + private Integer inputCarRaceTimes = 0; + + // start: Singleton Holder + private RacingCarServiceTest() { + } + + private static class InnerRacingCarServiceTest { + private static final RacingCarServiceTest instance = new RacingCarServiceTest(); + } + + public static RacingCarServiceTest getInstance() { + return RacingCarServiceTest.InnerRacingCarServiceTest.instance; + } + // end: Singleton Holder + + @DisplayName("OperatorControllerTest.repository에_RacingCar_저장()") + @Test + void repository에_RacingCar_저장() { + RacingCarDto racingCarDto = new RacingCarDto("pobi2"); + racingCarRepository.initSaveRacingCar(racingCarDto); + assertEquals(racingCarDto.getCarName(), racingCarRepository.getRacingCarByName("pobi2").getCarName()); + } + + @DisplayName("OperatorControllerTest.repository에_RacingCar_저장_후_자동차_전진_또는_정지()") + @Test + void repository에_RacingCar_저장_후_자동차_전진_또는_정지() { + racingCarDto = RacingCarDto.builder().carName("pobi2").build(); + racingCarRepository.initSaveRacingCar(racingCarDto); + System.out.println(racingCarRepository.getRacingCarByName(racingCarDto.getCarName()).getCarPosition()); + racingCarRepository.movingForwardByName(racingCarDto); + System.out.println(racingCarRepository.getRacingCarByName(racingCarDto.getCarName()).getCarPosition()); + racingCarRepository.movingForwardByName(racingCarDto); + System.out.println(racingCarRepository.getRacingCarByName(racingCarDto.getCarName()).getCarPosition()); + racingCarRepository.movingForwardByName(racingCarDto); + System.out.println(racingCarRepository.getRacingCarByName(racingCarDto.getCarName()).getCarPosition()); + } + + public void initSaveRacingCar(RacingCarInitDto racingCarInitDto) { + this.inputCarRaceTimes = racingCarInitDto.getInputCarRaceTimes(); + for (String carName : racingCarInitDto.getCarNameList()) { + racingCarRepository.initSaveRacingCar(carName); + } + } + + public Map getRacingCarMap() { + return racingCarRepository.getRacingCarMap(); + } + + public void playCarRacing() { + Map racingCarMap = this.getRacingCarMap(); + for (int i = 0; i < this.inputCarRaceTimes; i++) { + racingCarMap.forEach((key, val) -> val.movingForward()); + printRacingStatus(); + } + } + private void printRacingStatus() { + Map racingCarMap = this.getRacingCarMap(); + racingCarMap.forEach((key, val) -> this.printMoveForward(key, val.getCarPosition())); + System.out.println(); + } + + private void printMoveForward(String carName, Integer carPosition) { + System.out.print(carName + " : "); + for (int i = 0; i < carPosition; i++) { + System.out.print("-"); + } + System.out.println(); + } + + public void carRacingResult() { + Map racingCarMap = racingCarRepository.sortRacingCarMapByValueDesc(this.getRacingCarMap()); + StringJoiner stringJoiner = new StringJoiner(", "); + Integer maxMoveForwardPosition = (racingCarMap.entrySet().iterator().next().getValue()).getCarPosition(); + racingCarMap.forEach((key, val) -> { + if (val.getCarPosition() >= maxMoveForwardPosition) stringJoiner.add(key); + // this.printMoveForward(key, val.getCarPosition()); + }); + System.out.print(InterfaceMsg.GAME_RESULT.getValue() + stringJoiner); + } +} diff --git a/src/test/java/racingcar/service/ValidatorServiceTest.java b/src/test/java/racingcar/service/ValidatorServiceTest.java new file mode 100644 index 0000000000..f559676a5c --- /dev/null +++ b/src/test/java/racingcar/service/ValidatorServiceTest.java @@ -0,0 +1,66 @@ +package racingcar.service; + +import java.util.HashSet; +import java.util.Set; +import racingcar.domain.enums.ValidationMsg; + +public class ValidatorServiceTest { + private StringUtilService stringUtilService = StringUtilService.getInstance(); + private static final int MAX_CAR_NAME_LENGTH = 5; + + // start: Singleton Holder + private ValidatorServiceTest() { + } + + private static class InnerValidatorService { + private static final ValidatorServiceTest instance = new ValidatorServiceTest(); + } + + public static ValidatorServiceTest getInstance() { + return InnerValidatorService.instance; + } + // end: Singleton Holder + + public boolean isMoreThanTwoCar(String carName) { + return (stringUtilService.splitByComma(carName).size() >= 2); + } + + public ValidationMsg validateCarCount(String carName) { + if (this.isMoreThanTwoCar(carName)) return ValidationMsg.PROPER_TYPE; + return ValidationMsg.NEED_MORE_THEN_TWO; + } + + public ValidationMsg validationCarName(String carName) { + if (carName == null) return ValidationMsg.NULL_TYPE; + if (carName.isEmpty()) return ValidationMsg.EMPTY_TYPE; + if (this.isPassCarNameLength(carName) == false) return ValidationMsg.CAR_NAME_OVER_FIVE; + if (!this.isMoreThanTwoCar(carName)) return ValidationMsg.NEED_MORE_THEN_TWO; + if (this.isDuplicateUserInput(carName)) return ValidationMsg.NOT_DUPLICATE_NAME; + return ValidationMsg.PROPER_TYPE; + } + + private boolean isPassCarNameLength(String carName) { + for (String eachCar : stringUtilService.splitByComma(carName)) { + if (eachCar.length() > MAX_CAR_NAME_LENGTH) return false; + } + return true; + } + + // 중복 입력된 값이 있는지 체크 + public boolean isDuplicateUserInput(String userInput) { + String[] arrUserInput = userInput.split(""); + Set userInputSet = new HashSet<>(); + for (String userInputNum : arrUserInput) { + userInputSet.add(userInputNum); + } + return (arrUserInput.length != userInputSet.size()); + } + + public ValidationMsg validationCarRaceTimes(String RaceTimes) { + if (RaceTimes == null) return ValidationMsg.NULL_TYPE; + if (RaceTimes.isEmpty()) return ValidationMsg.EMPTY_TYPE; + try { Integer.valueOf(RaceTimes); } catch (NumberFormatException e) { return ValidationMsg.NOT_NUMBER; } + if (RaceTimes.equals("0")) return ValidationMsg.ZERO_TYPE; + return ValidationMsg.PROPER_TYPE; + } +}