-
Notifications
You must be signed in to change notification settings - Fork 408
[자동차 경주] 정석찬 미션 제출합니다. #433
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
base: main
Are you sure you want to change the base?
Changes from all commits
8a2b23e
de7b83c
51f1d7a
0c75478
c9edb4c
af859f2
8e64b7e
cd443de
eef518f
7a9191b
2b64eb7
0296e94
ac177d0
414d5a3
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,23 @@ | ||
| # javascript-racingcar-precourse | ||
| # 자동차 경주 | ||
|
|
||
| ## 과제 진행 요구 사항 | ||
| 미션은 자동차 경주 저장소를 포크하고 클론하는 것으로 시작한다. | ||
| 기능을 구현하기 전 README.md에 구현할 기능 목록을 정리해 추가한다. | ||
| Git의 커밋 단위는 앞 단계에서 README.md에 정리한 기능 목록 단위로 추가한다. | ||
| AngularJS Git Commit Message Conventions을 참고해 커밋 메시지를 작성한다. | ||
| 자세한 과제 진행 방법은 프리코스 진행 가이드 문서를 참고한다. | ||
|
|
||
| # 기능 요구 사항 | ||
|
|
||
| - [x] 자동차 이름, 횟수 입력 받기 | ||
| - [x] ',' 기준으로 이름 입력 (5자 이하) | ||
| - [x] 자연수 숫자 입력받기 | ||
| - [x] 잘못된 값 입력시 에러처리 | ||
| - [x] 유효한 숫자 아닌 경우 에러처리 | ||
| - [x] 자동차 경주 | ||
| - [x] 전진 기능 구현 | ||
| - [x] 결과 출력 기능 구현 | ||
| - [x] 최종 우승자 출력 | ||
| - [x] 공동 우승 처리 | ||
| - [x] ERROR 출력 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| import App from "../src/App.js"; | ||
| import { MissionUtils } from "@woowacourse/mission-utils"; | ||
|
|
||
| const mockQuestions = (inputs) => { | ||
| MissionUtils.Console.readLineAsync = jest.fn(); | ||
|
|
||
| MissionUtils.Console.readLineAsync.mockImplementation(() => { | ||
| const input = inputs.shift(); | ||
| return Promise.resolve(input); | ||
| }); | ||
| }; | ||
|
|
||
| const mockRandoms = (numbers) => { | ||
| MissionUtils.Random.pickNumberInRange = jest.fn(); | ||
|
|
||
| numbers.reduce((acc, number) => { | ||
| return acc.mockReturnValueOnce(number); | ||
| }, MissionUtils.Random.pickNumberInRange); | ||
| }; | ||
|
|
||
| const getLogSpy = () => { | ||
| const logSpy = jest.spyOn(MissionUtils.Console, "print"); | ||
| logSpy.mockClear(); | ||
| return logSpy; | ||
| }; | ||
|
|
||
| describe("자동차 경주", () => { | ||
| test("기능 테스트", async () => { | ||
| // given | ||
| const MOVING_FORWARD = 4; | ||
| const STOP = 3; | ||
| const inputs = ["dao,bazzi", "1"]; | ||
| const logs = ["dao : -", "bazzi : ", "최종 우승자 : dao"]; | ||
| const logSpy = getLogSpy(); | ||
|
|
||
| mockQuestions(inputs); | ||
| mockRandoms([MOVING_FORWARD, STOP]); | ||
|
|
||
| // when | ||
| const app = new App(); | ||
| await app.run(); | ||
|
|
||
| // then | ||
| logs.forEach((log) => { | ||
| expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(log)); | ||
| }); | ||
| }); | ||
|
|
||
| test("예외 테스트", async () => { | ||
| // given | ||
| const inputs = ["bazzi,dao,,json"]; | ||
| mockQuestions(inputs); | ||
|
|
||
| // when | ||
| const app = new App(); | ||
|
|
||
| // then | ||
| await expect(app.run()).rejects.toThrow("[ERROR]"); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,36 @@ | ||
| import View from "./View.js"; | ||
| import Car from "./Car.js"; | ||
| import Race from "./Race.js"; | ||
| import OutView from "./OutView.js"; | ||
|
|
||
| class App { | ||
| async run() {} | ||
| #input = new View(); | ||
| #race = new Race(); | ||
| #output = new OutView(); | ||
|
|
||
| async run() { | ||
| const carNames = await this.#input.readInputCar(); | ||
| const raceCount = await this.#input.readInputRaceCount(); | ||
|
|
||
| this.#race.setRaceCount(raceCount); | ||
|
|
||
| // Race 객체에 Car객체 추가 | ||
| carNames.forEach(carName => { | ||
| this.#race.addRacingCar(new Car(carName)); | ||
| }); | ||
|
|
||
| for (let count = 0; count < this.#race.getRaceCount(); count++) { | ||
| this.#race.racing(); | ||
| this.#race.showRacingResult(); | ||
|
|
||
| } | ||
|
|
||
| this.#race.determineWinner(); | ||
| this.#race.showWinner(); | ||
| const winners = this.#race.getWinner(); | ||
|
|
||
| this.#output.showWinner(winners); | ||
| } | ||
| } | ||
|
|
||
| export default App; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| class Car { | ||
| #forwardCount=0; | ||
| #name; | ||
|
|
||
| constructor(name) { | ||
| this.#name = name; | ||
| } | ||
|
|
||
| forward(random) { | ||
| if (random >= 4) { | ||
| this.#forwardCount += 1; | ||
| } | ||
| } | ||
|
|
||
| getName() { | ||
| return this.#name; | ||
| } | ||
|
|
||
| getForwardCount() { | ||
| return this.#forwardCount; | ||
| } | ||
| } | ||
|
|
||
| export default Car; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import { Console } from "@woowacourse/mission-utils"; | ||
|
|
||
| class OutView { | ||
|
|
||
| showWinner(winners) { | ||
| Console.print(`최종 우승자 : ${winners.join(',')}`); | ||
| } | ||
|
|
||
|
|
||
| } | ||
|
|
||
| export default OutView |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| import { Random, Console } from "@woowacourse/mission-utils"; | ||
|
|
||
| class Race { | ||
| #cars = []; | ||
| #winners = []; | ||
| #raceCount; | ||
|
|
||
|
|
||
| addRacingCar(car) { | ||
| this.#cars.push(car); | ||
| } | ||
|
|
||
| setRaceCount(raceCount) { | ||
| this.#raceCount = raceCount; | ||
| } | ||
|
|
||
| getRaceCount() { | ||
| return this.#raceCount; | ||
| } | ||
|
|
||
| racing() { | ||
| this.#cars.forEach(car => { | ||
| const random = Random.pickNumberInRange(0, 9); | ||
| // 4 이상일 경우 전진 | ||
| car.forward(random); | ||
| }) | ||
| } | ||
|
|
||
| showRacingResult() { | ||
| // name : --- 형식으로 출력 | ||
| this.#cars.forEach( car => { | ||
| Console.print(car.getName() + ' : ' + '-'.repeat(car.getForwardCount())); | ||
|
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. Airbnb JS 스타일 가이드에서는 문자열을 연결할 때 template string 사용을 권장하는 것 같습니다! https://github.com/airbnb/javascript?tab=readme-ov-file#es6-template-literals 추가적으로 사용자에게 입력을 받는 역할을 View 클래스에 위임하신 것처럼 결과를 출력하는 역할도 View 클래스에 위임하면 좋을 것 같아요! 현재는 결과 문자열을 만들어서 출력해주다보니 UI와 결합성이 강해 추후 UI가 변경되면 |
||
| }) | ||
| Console.print(''); | ||
| } | ||
|
|
||
| determineWinner() { | ||
| const max = Math.max(...this.#cars.map(car => car.getForwardCount())); | ||
|
|
||
| this.#cars.forEach(car => { | ||
| if (car.getForwardCount() === max){ | ||
| this.#winners.push(car.getName()); | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| getWinner() { | ||
| return this.#winners; | ||
| } | ||
|
|
||
| } | ||
|
|
||
| export default Race; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { Console } from "@woowacourse/mission-utils"; | ||
|
|
||
| class View { | ||
|
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. View 클래스로 UI 로직을 분리한게 좋네요👍 |
||
| #inputCarNameRegex = /^([a-zA-z]{1,5})(,[a-zA-Z]{1,5})*$/ | ||
|
|
||
| #carInputMessage = "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)\n"; | ||
| #raceCount = "시도할 횟수는 몇 회인가요?\n"; | ||
|
|
||
| async readInputCar() { | ||
| const carNamesStr = await Console.readLineAsync(this.#carInputMessage); | ||
|
|
||
| if (!this.#inputCarNameRegex.test(carNamesStr)){ | ||
| throw new Error("[ERROR] : 잘못된 이름 입력\n"); | ||
| } | ||
|
|
||
| const carNames = carNamesStr.split(','); | ||
| return carNames; | ||
| } | ||
|
|
||
| async readInputRaceCount() { | ||
| const raceCountStr = await Console.readLineAsync(this.#raceCount); | ||
| const raceCount = Number(raceCountStr); | ||
| if (raceCount.isNaN() || raceCount <= 0){ | ||
| throw new Error("[ERROR] : 잘못된 숫자 입력\n"); | ||
| } | ||
|
|
||
| return raceCount; | ||
| } | ||
| } | ||
|
|
||
| export default View; | ||
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.
메서드나 함수의 이름은 동사로 시작하도록 짓는 것이 가독성이 좋을 것 같습니다!
https://velog.io/@pjc0247/%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%98%81%EC%96%B4