diff --git a/README.md b/README.md index e078fd41f..14293c97e 100644 --- a/README.md +++ b/README.md @@ -1 +1,69 @@ # javascript-racingcar-precourse + +  +## 초간단 자동차 경주 게임을 구현한다. + +  +### ● 경주할 자동차 이름을 쉼표(,)기준으로 구분하여 각각 5자 이하로만 입력받는다. +경주할 자동차 이름을 입력하세요. (이름은 쉼표(,) 기준으로 구분) \n => + + +  +### ● 입력받은 자동차 이름값들을 검증한다. +- 빈값 여부 +- 에러 여부 +- 쉼표 구분여부 +- 영어입력 여부 +- 5자이하 여부 + + +  +### ● 검증 중 입력값에 문제가 있을 경우, 완전히 종료된다. +사용자가 잘못된 값을 입력할 경우 "[ERROR]"로 시작하는 메시지와 함께 Error를 발생시킨 후 애플리케이션은 종료되어야 한다. + + +  +### ● 시도할 횟수를 입력받는다. +시도할 횟수는 몇 회인가요? \n => + +  +### ● 입력받은 시도할 홧수를 검증한다. +- 빈값 여부 +- 숫자가 입력되었는지 여부 +- 과도한 시도 횟수 방지 (임의 20 제한) + + +  +### ● 검증 중 입력값에 문제가 있을 경우, 완전히 종료된다. +사용자가 잘못된 값을 입력할 경우 "[ERROR]"로 시작하는 메시지와 함께 Error를 발생시킨 후 애플리케이션은 종료되어야 한다. + +  +### ● 입력받은 값들에 문제가 없으면 게임이 시작된다. + + +  +### ● 게임 진행 규칙 +주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. +전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다. + + +  +### ● 게임 진행 상황을 출력할 때 자동차 이름을 같이 출력한다. +<출력 예시> +pobi : -- +woni : +jun : - + + +  +### ● 우승자 출력하기 +입력된 시도횟수만큼 게임 진행 후 가장 멀리 간 자동차가 우승이다. +자동차 경주 게임을 완료한 후 누가 우승했는지를 출력하여 알려준다. +<출력 예시> +최종 우승자 : pobi + +전진한 횟수가 동점인 경우 우승자는 한 명 이상일 수 있다. +우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다. +<출력 예시> +최종 우승자 : pobi, jun + diff --git a/package-lock.json b/package-lock.json index 93036a07f..bc33080fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3351,9 +3351,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/App.js b/src/App.js index 091aa0a5d..c9da09c8a 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,13 @@ +import RacingController from "./controller/RacingController.js"; + class App { - async run() {} + constructor () { + this.racingController = new RacingController + } + + async run() { + this.racingController.racing(); + } } export default App; diff --git a/src/constants/errorMessages.js b/src/constants/errorMessages.js new file mode 100644 index 000000000..dfee91baa --- /dev/null +++ b/src/constants/errorMessages.js @@ -0,0 +1,8 @@ +const ERROR_MESSAGES = { + VALIDATE_EMPTY: "[ERROR] 입력값이 없습니다. 다시 입력해주세요.", + VALIDATE_NOT_CORRECT_REGEX: "[ERROR] 형식이 올바르지 않습니다.", + VALIDATE_FIVE_LENGTH: "[ERROR] 입력된 자동차 이름 중에서 5글자가 넘는 이름이 존재합니다.", + VALIDATE_LIMIT_NUMBER: "[ERROR] 너무 과도한 시도 횟수를 입력하셨습니다.", +}; + +export default ERROR_MESSAGES; \ No newline at end of file diff --git a/src/controller/RacingController.js b/src/controller/RacingController.js new file mode 100644 index 000000000..027d615aa --- /dev/null +++ b/src/controller/RacingController.js @@ -0,0 +1,104 @@ +import { MissionUtils } from "@woowacourse/mission-utils"; +import validateCarNames from "../validation/validateCarNames.js"; +import validateTryNumber from "../validation/validateTryNumber.js"; +import makeArrayFromString from "../utils/makeArrayFromString.js"; +import makeObjectFromArray from "../utils/makeObjectFromArray.js"; + +class RacingController { + async racing() { + const carNamesObject = await this.getCarNamesObject(); + const tryNumber = await this.getTryNumber(); + const carRacingResult = this.startCarRacing(carNamesObject, tryNumber); + this.printWinner(carRacingResult); + return; + } + + async getCarNamesObject() { + const carNames = await this.readCarNames(); + const stringType = String(carNames); + const validatedCarNames = this.validationCarNames(stringType); + const carNamesArray = makeArrayFromString(validatedCarNames); + MissionUtils.Console.print(""); + return makeObjectFromArray(carNamesArray); + } + + async getTryNumber() { + const tryNumber = await this.readTryNumbers(); + const validtedTryNumber = this.validationTryNumber(tryNumber); + MissionUtils.Console.print(""); + return Number(validtedTryNumber); + } + + async readCarNames() { + const inputCarNames = await MissionUtils.Console.readLineAsync( + "경주할 자동차 이름을 입력하세요. (이름은 쉼표(,) 기준으로 구분) \n => " + ); + const stringType = String(inputCarNames); + const removeSpace = stringType.replace(/ /g, ""); + return removeSpace; + } + + validationCarNames(validationTarget) { + validateCarNames(validationTarget); + return validationTarget; + } + + async readTryNumbers() { + const inputCarNames = await MissionUtils.Console.readLineAsync( + "시도할 횟수는 몇 회인가요? \n => " + ); + const removeSpace = inputCarNames.replace(/ /g, ""); + return removeSpace; + } + + validationTryNumber(validationTarget) { + validateTryNumber(validationTarget); + return validationTarget; + } + + getRandomNumber() { + return MissionUtils.Random.pickNumberInRange(0, 9); + } + + changeDashUtil(number) { + let dash = ""; + for (let i = 0; i < number; i++) { + dash += "-"; + } + return dash; + } + + printGameProgress(object) { + object.map((array) => { + const carName = array.name; + const dash = this.changeDashUtil(array.forward); + return MissionUtils.Console.print(`${carName} : ${dash}`); + }); + MissionUtils.Console.print(""); + } + + startCarRacing(carNamesObject, tryNumber) { + let racingResult = carNamesObject; + for (let i = 0; i < tryNumber; i++) { + racingResult = racingResult.map((object) => { + if (this.getRandomNumber() > 4) object.forward += 1; + return object; + }); + this.printGameProgress(racingResult); + } + return racingResult; + } + + printWinner(carRacingResult) { + const maxForwardNumber = Math.max( + ...carRacingResult.map((object) => object.forward) + ); + const winnerArray = carRacingResult + .filter((object) => object.forward === maxForwardNumber) + .map((object) => object.name); + const winner = winnerArray.join(", "); + return MissionUtils.Console.print(`최종우승자 : ${winner}`); + } +} + +export default RacingController; diff --git a/src/utils/makeArrayFromString.js b/src/utils/makeArrayFromString.js new file mode 100644 index 000000000..7105dfaab --- /dev/null +++ b/src/utils/makeArrayFromString.js @@ -0,0 +1,5 @@ +const makeArrayFromString = (stringValue) => { + return stringValue.split(','); +} + +export default makeArrayFromString; \ No newline at end of file diff --git a/src/utils/makeObjectFromArray.js b/src/utils/makeObjectFromArray.js new file mode 100644 index 000000000..553f55696 --- /dev/null +++ b/src/utils/makeObjectFromArray.js @@ -0,0 +1,8 @@ +const makeObjectFromArray = (array) => { + const objectedValue = array.map((array) => { + return {name : array, forward : 0}; + }) + return objectedValue; +} + +export default makeObjectFromArray; \ No newline at end of file diff --git a/src/validation/functions/validateEmpty.js b/src/validation/functions/validateEmpty.js new file mode 100644 index 000000000..8d31f2b06 --- /dev/null +++ b/src/validation/functions/validateEmpty.js @@ -0,0 +1,11 @@ +import ERROR_MESSAGES from "../../constants/errorMessages.js"; + +const validateEmpty = (value) => { + if (value == "") { + throw new Error(ERROR_MESSAGES.VALIDATE_EMPTY); + } + + return true; +}; + +export default validateEmpty; \ No newline at end of file diff --git a/src/validation/functions/validateFiveLength.js b/src/validation/functions/validateFiveLength.js new file mode 100644 index 000000000..08d3364d5 --- /dev/null +++ b/src/validation/functions/validateFiveLength.js @@ -0,0 +1,16 @@ +import ERROR_MESSAGES from "../../constants/errorMessages.js"; +import makeArrayFromString from "../../utils/makeArrayFromString.js"; + +const validateFiveLength = (value) => { + const carNamesArray = makeArrayFromString(value); + carNamesArray.forEach((carName) => { + if (carName.length > 5) { + throw new Error(ERROR_MESSAGES.VALIDATE_FIVE_LENGTH); + } + }); + + return true; +}; + +export default validateFiveLength; + diff --git a/src/validation/functions/validateLimitNumber.js b/src/validation/functions/validateLimitNumber.js new file mode 100644 index 000000000..697fe00e6 --- /dev/null +++ b/src/validation/functions/validateLimitNumber.js @@ -0,0 +1,11 @@ +import ERROR_MESSAGES from "../../constants/errorMessages.js"; + +const validateLimitNumber = (value) => { + const numberValue = Number(value); + if(numberValue > 20) + throw new Error(ERROR_MESSAGES.VALIDATE_LIMIT_NUMBER); + + return true; +} + +export default validateLimitNumber; \ No newline at end of file diff --git a/src/validation/functions/validateRegEx.js b/src/validation/functions/validateRegEx.js new file mode 100644 index 000000000..8a9b56eb9 --- /dev/null +++ b/src/validation/functions/validateRegEx.js @@ -0,0 +1,12 @@ +import ERROR_MESSAGES from "../../constants/errorMessages.js"; + +const validateRegEx = (value, regExPattern) => { + const validInclusion = regExPattern.test(value); + + if (!validInclusion) { + throw new Error(ERROR_MESSAGES.VALIDATE_NOT_CORRECT_REGEX); + } + return true; +}; + +export default validateRegEx; diff --git a/src/validation/validateCarNames.js b/src/validation/validateCarNames.js new file mode 100644 index 000000000..961ebc9ae --- /dev/null +++ b/src/validation/validateCarNames.js @@ -0,0 +1,13 @@ +import validateEmpty from "./functions/validateEmpty.js"; +import validateFiveLength from "./functions/validateFiveLength.js"; +import validateRegEx from "./functions/validateRegEx.js"; + +const validateCarNames = (validateTarget) => { + validateEmpty(validateTarget); + const regExPattern = /^(\w+)(,\w+)*$/; + validateRegEx(validateTarget, regExPattern); + validateFiveLength(validateTarget); + return true; +} + +export default validateCarNames; \ No newline at end of file diff --git a/src/validation/validateTryNumber.js b/src/validation/validateTryNumber.js new file mode 100644 index 000000000..a1f6166c3 --- /dev/null +++ b/src/validation/validateTryNumber.js @@ -0,0 +1,13 @@ +import validateEmpty from "./functions/validateEmpty.js"; +import validateLimitNumber from "./functions/validateLimitNumber.js"; +import validateRegEx from "./functions/validateRegEx.js"; + +const validateTryNumber = (validationTarget) => { + validateEmpty(validationTarget); + const regExPattern = /^\d{1,2}$/; + validateRegEx(validationTarget, regExPattern); + validateLimitNumber(validationTarget); + return true; +}; + +export default validateTryNumber;