-
Notifications
You must be signed in to change notification settings - Fork 476
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[자동차 경주] 윤선례 미션 제출합니다 #451
base: main
Are you sure you want to change the base?
Changes from all commits
b27dfdb
64a9e62
df314fa
ccdfd0f
7fc0d70
5ed4287
37415f8
0869e04
ea651fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,36 @@ | ||
# javascript-racingcar-precourse | ||
### 자동차 경주 게임 | ||
|
||
초간단 자동차 경주 게임을 구현한다. | ||
|
||
### 프로그램 실행 시 | ||
|
||
- 프로그램 실행 시 ‘경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)’ 가 출력된다 | ||
- 자동차 이름을 쉼표 기준으로 구분하여 입력한다 | ||
- 각각의 자동차 이름은 5글자 이하로 입력한다 | ||
- 올바르게 입력되었을 경우 시도할 횟수를 입력할 수 있다 | ||
- 시도한 횟수만큼 전진 로직을 실행한다 | ||
|
||
### 전진 로직 | ||
|
||
- `Random.pickNumberInRange()`을 활용해 랜덤 값을 추출한다 | ||
- 추출한 랜덤 값이 4 이상일 경우 전진한다 | ||
- 전진할 경우 이전 회차에서 전진한 문자에 - 기호를 추가로 붙여준다 | ||
- 매 횟수마다 전진 결과를 콘솔로 출력한다 | ||
|
||
### 우승자 발표 | ||
|
||
- 가장 전진거리가 긴 우승자를 출력한다 | ||
- 같은 전진거리인 우승자가 여러명일 경우 쉼표로 구분하여 출력한다 (우승자는 한 명 이상일 수 있다) | ||
|
||
### 에러 처리 | ||
|
||
- 사용자가 잘못된 값을 입력할 경우 "[ERROR]"로 시작하는 메시지와 함께 Error를 발생시킨 후 애플리케이션은 종료되어야 한다. | ||
|
||
- 입력 값이 비어있는 경우 "[ERROR] 입력 값이 비어 있습니다"를 출력한다. | ||
- 구분자가 입력되지 않은 경우 "[ERROR] 구분자가 입력되지 않았습니다"를 출력한다. | ||
- 음수 값이 입력된 경우 "[ERROR] 음수는 입력하실 수 없습니다"를 출력한다. | ||
- 시도 횟수가 입력되지 않은 경우 "[ERROR] 시도 횟수를 입력해 주세요"를 출력한다. | ||
- 시도 횟수 입력이 숫자가 아닌 경우 "[ERROR] 시도 횟수는 숫자만 입력 가능합니다"를 출력한다. | ||
- 자동차 이름을 쉼표로 구분하지 않은 경우 "[ERROR] 자동차 이름을 쉼표로 구분하여 입력해 주세요"를 출력한다. | ||
- 자동차 이름이 5자를 초과한 경우 "[ERROR] 자동차 이름은 5자 이하로 입력해 주세요"를 출력한다. | ||
- 자동차 이름에 공백이 포함된 경우 "[ERROR] 이름에 공백 문자는 사용하실 수 없습니다"를 출력한다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,13 @@ | ||
import Controller from './controller/controller.js'; | ||
|
||
class App { | ||
async run() {} | ||
constructor() { | ||
this.controller = new Controller(); | ||
} | ||
|
||
async run() { | ||
await this.controller.execute(); | ||
} | ||
} | ||
|
||
export default App; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const MOVE_REQUIREMENT = 3; | ||
export const CAR_NAME_LENGTH_LIMIT = 5; | ||
export const DELIMITER = ','; | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
export const PROMPT_MESSAGES = Object.freeze({ | ||
INPUT_ADDITION_CAR: | ||
'경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)\n', | ||
INPUT_ATTEMPTS: '시도할 횟수는 몇 회인가요?\n', | ||
Comment on lines
+2
to
+4
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분은 이름이 조금 더 명확하게 변경되면 좋을 것 같아요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아! 추천해주신 이름이 더 직관적인 것 같아요! |
||
}); | ||
|
||
export const ERROR_MESSAGES = Object.freeze({ | ||
EMPTY_INPUT: '[ERROR] 입력 값이 비어 있습니다.', | ||
MISSING_DELIMITER: '[ERROR] 구분자가 입력되지 않았습니다.', | ||
NEGATIVE_NUMBER: '[ERROR] 음수는 입력하실 수 없습니다.', | ||
EMPTY_ATTEMPTS: '[ERROR] 시도 횟수를 입력해 주세요', | ||
INVALID_ATTEMPTS: '[ERROR] 시도 횟수는 숫자만 입력 가능합니다', | ||
CAR_NAME_DELIMITER_REQUIRED: | ||
'[ERROR] 자동차 이름을 쉼표로 구분하여 입력해 주세요.', | ||
CAR_NAME_LENGTH_EXCEEDED: '[ERROR] 자동차 이름은 5자 이하로 입력해 주세요', | ||
CAR_NAME_CONTAINS_WHITESPACE: | ||
'[ERROR] 이름에 공백 문자는 사용하실 수 없습니다', | ||
}); |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,55 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import Car from '../model/model.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { DELIMITER } from '../constants/constant.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { Random } from '@woowacourse/mission-utils'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
getAttempts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
getCarNames, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
announceWinners, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
displayRaceProgress, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} from '../view/view.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { validateAttempts, validCarName } from '../utils/validator.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
class Controller { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
constructor() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.cars = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
async execute() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const enteredCarNames = await getCarNames(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const splittedNames = enteredCarNames.split(DELIMITER); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
validCarName(splittedNames); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.cars = this.createCars(splittedNames); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const attempts = await getAttempts(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
validateAttempts(attempts); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for (let attemptIndex = 0; attemptIndex < attempts; attemptIndex++) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이게 말씀해주신 반복 부분이군요! 제 개인적인 생각으론 그대로 추가로 이 부분을 레이싱을 진행하는 기능 하나로 봐서 하나의 함수로 분리하는 것도 좋아보입니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 다시 보니 그렇네요! 반복 부분을 함수로 감싸야 문맥에 더 자연스러울 것 같아요 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.startCarRace(this.cars); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. constructor에 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아 그렇네요! 클래스 문법이 아직 익숙하지 않아서 자꾸 놓치는 것 같아요 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
displayRaceProgress(this.cars); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const result = this.getWinners(this.cars); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
announceWinners(result.join(',')); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+17
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 지금도 책임 소재에 맞게 메서드를 잘 분리해 주셨다고 생각해요! 여기서 조금더 수정해본다면 execute를 약간 더 다듬어 볼 수 있지 않을까요? 이렇게 다듬어보는건 어떨까 제안드려봅니다
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제안해주신 코드가 더 깔끔하고 보기 좋은 것 같아요! |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
createCars(splittedNames) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return splittedNames.map((name) => new Car(name)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제가 생각하기에 클래스 문법의 이점은 클래스 내 다른 함수에서 조작한 마침 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아하 말씀해주신 것처럼 createCars 함수를 수정하면 더 좋을 것 같아요! |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
startCarRace(cars) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cars.forEach((car) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const number = Random.pickNumberInRange(0, 9); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
car.race(number); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return cars; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
getWinners(cars) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const maxScore = Math.max(...cars.map((car) => car.distance.length)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return cars | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.filter((car) => car.distance.length === maxScore) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.map((car) => car.name); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
export default Controller; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { MOVE_REQUIREMENT } from '../constants/constant.js'; | ||
|
||
class Car { | ||
constructor(name) { | ||
this.name = name; | ||
this.distance = ''; | ||
} | ||
|
||
race(pickNumber) { | ||
if (pickNumber > MOVE_REQUIREMENT) { | ||
this.distance = this.distance + '-'; | ||
} | ||
} | ||
} | ||
|
||
export default Car; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { CAR_NAME_LENGTH_LIMIT } from '../constants/constant.js'; | ||
|
||
export const utils = { | ||
hasValidCharacter(name) { | ||
const regex = /^[\p{L},\s]*$/u; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 정규표현식을 이용해 세부적인 오류를 검출하셨네요!👍 |
||
return regex.test(name); | ||
}, | ||
isNameLengthLimit(name) { | ||
return name.length <= CAR_NAME_LENGTH_LIMIT; | ||
}, | ||
hasWhitespace(name) { | ||
const regex = /\s/; | ||
return regex.test(name); | ||
}, | ||
isNumberType(number) { | ||
return !isNaN(number); | ||
}, | ||
isNegativeNumber(number) { | ||
return number < 0; | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { utils } from './utils.js'; | ||
import { ERROR_MESSAGES } from '../constants/messages.js'; | ||
|
||
export const validCarName = (splittedNames) => { | ||
splittedNames.forEach((car) => { | ||
if (!utils.hasValidCharacter(car)) { | ||
throw new Error(ERROR_MESSAGES.CAR_NAME_DELIMITER_REQUIRED); | ||
} | ||
if (!utils.isNameLengthLimit(car)) { | ||
throw new Error(ERROR_MESSAGES.CAR_NAME_LENGTH_EXCEEDED); | ||
} | ||
if (utils.hasWhitespace(car)) { | ||
throw new Error(ERROR_MESSAGES.CAR_NAME_CONTAINS_WHITESPACE); | ||
} | ||
}); | ||
}; | ||
|
||
export const validateAttempts = (attempts) => { | ||
if (!attempts) { | ||
throw new Error(ERROR_MESSAGES.EMPTY_ATTEMPTS); | ||
} | ||
if (!utils.isNumberType(attempts)) { | ||
throw new Error(ERROR_MESSAGES.INVALID_ATTEMPTS); | ||
} | ||
if (utils.isNegativeNumber(attempts)) { | ||
throw new Error(ERROR_MESSAGES.NEGATIVE_NUMBER); | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { Console } from '@woowacourse/mission-utils'; | ||
import { PROMPT_MESSAGES } from '../constants/messages.js'; | ||
|
||
export const getCarNames = async () => { | ||
const inputs = await Console.readLineAsync( | ||
PROMPT_MESSAGES.INPUT_ADDITION_CAR | ||
); | ||
return inputs; | ||
}; | ||
|
||
export const getAttempts = async () => { | ||
const attempts = await Console.readLineAsync(PROMPT_MESSAGES.INPUT_ATTEMPTS); | ||
return attempts; | ||
}; | ||
|
||
export const announceWinners = (winner) => { | ||
Console.print(`최종 우승자 : ${winner}`); | ||
}; | ||
|
||
export const displayRaceProgress = (cars) => { | ||
cars.forEach((car) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Destructuring 문법을 사용하면 좋을 것 같습니다🙂 cars.forEach(({name, distance}) => {
Console.print(`${name} : ${distance}`);
}); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 앗 다시 보니 그렇네요! 놓쳤던 부분인 것 같습니다 ;ㅅ; |
||
Console.print(`${car.name} : ${car.distance}`); | ||
}); | ||
Console.print(`\n`); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
자주 쓰이는 숫자나 변수들을 상수로 관리하는 건 좋은 방법이라고 생각해요! 지금 작성해주신 상수에서 조금 더 나아가서 필요한 게 어떤게 있을까 고민해보면 더 좋을 것 같아요 :>
예를들어 지금은
NAME_LENGTH_LIMIT
으로 5라는 상한선만 가지고 있는데 하한선도 포함해 보는건 어떨까요?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋은 의견 감사합니다! 하한선을 두는 것도 좋은 것 같아요! :)
상수명 부분에서도 확장성을 좀 더 고려해볼게요!