Skip to content

Commit

Permalink
[1단계 - 자동차 경주 구현] 가브리엘(윤주현) 미션 제출합니다. (#153)
Browse files Browse the repository at this point in the history
* chore: npm 모듈 설치

- @woowacourse/mission-utils 설치

* docs: 요구사항 문서 작성

* docs: 프리코스 피드백 및 페어프로그래밍 규칙 작성

* chore: 불필요한 npm 모듈 삭제

* feat: 입출력 담당 모듈 구현

* feat: n대의 자동차를 입력 받는 기능 구현

Co-authored-by: Gabriel Ju Hyun, Yoon <gabrielyoon7@gmail.com>

* feat: 각 자동차 이름이 1자 이상 5자 이하의 글자인지 판별하는 기능 구현

* test: 각 자동차 이름이 1자 이상 5자 이하의 글자인지 판별하는 기능 테스트

* feat: 자동차 생성 기능 구현

* feat: 시도 횟수를 입력 받는 기능 구현

* feat: 시도 횟수를 입력이 자연수인지 판별하는 기능 구현

* test: 시도 횟수를 입력이 자연수인지 판별하는 기능 테스트

* feat: 입력받은 시도 횟수 만큼 모든 자동차의 이동을 시도하는 기능 구현

* feat: 0부터 9까지 랜덤으로 숫자를 발생시키는 기능 구현

* feat: 차가 전진하는 기능 구현

* feat: 자동차의 현재 이동 상태를 출력하는 기능 구현

* feat: 최종 우승자를 판별하고 출력하는 기능 구현

* docs: 기능목록 최신화

* feat: 잘못된 이름 입력이 들어왔을 경우 에러 처리 구현

Co-authored-by: Gabriel Ju Hyun, Yoon <gabrielyoon7@gmail.com>

* feat: 잘못된 횟수 입력이 들어왔을 경우 에러 처리 구현

* feat: 게임 종료 후 cleanup 기능 구현

* test: 자동차 이름 유효성 검사 테스트 케이스 추가

* test:  시도 횟수 검사 테스트 케이스 추가

* refactor: 콜백 패턴의 입력 로직을 async/await로 변경

Co-authored-by: Gabriel Ju Hyun, Yoon <gabrielyoon7@gmail.com>

* refactor: 형 변환 책임 분리

* refactor: 게임 동작/제어에 관한 책임 분리

- 원래 콜백패턴에서 사용해 의존성이 입력 로직에 얽혀있었는데, play 메서드로 이동했다.

* refactor: UI 출력을 담당하는 로직을 분리

* refactor: 콘솔을 담당하는 책임 분리

* style: 비구조화 할당 문법으로 변경

* refactor: 문자열 리터럴 상수화

* refactor: 불필요한 필드 삭제

- 일회성으로 사용한 tryCount를 제거

* refactor: 사용하지 않는 import 문 삭제

* refactor: 자동차 생성 로직 분리

* refactor: 메서드에서 필드로 사용하던 변수를 매개변수로 변경

* refactor: 불필요한 import 문 제거

* test: Car 클래스 테스트 작성

* test: 테스트를 위한 형변환 로직 추가

* chore: eslint 설정 변경

* test: 랜덤 기능 mock 함수 생성

* test: 전진할지 안할지 판단하는 기능 테스트

* fix: mocking 이 안되는 이슈 해결

* test: 승자들을 판단하는 기능 테스트

* chore: lint 설정 변경

* docs: 요구사항 문서 작성

* chore: yarn 삭제

* docs: 기능 요구사항 개선

* refactor: 랜덤 생성한 숫자가 4 이상인 경우 이동하는 로직 개선

- 자동차가 아닌, 게임 매니저가 판단하도록 개선

* refactor: 함수명 변경

- tryMoveCars => raceCars

* refactor: for i 를 forEach로 변경

* refactor: this.#cars에 대한 정보를 클래스에서 직접 참조하도록 변경

* refactor: 자동차 출력 메소드 로직 개선

* refactor: pickRandomNumber로직 개선

* refactor: 메시지를 상수로 관리하는 파일 위치 및 파일명 변경

* refactor: 메세지 값 상수화 추가

* refactor: 차를 설정해주는 메소드가 차량 객체를 저장하도록 개선

* fix: 테스트 케이스 오류 수정

- description을 이해할 수 있는 표현으로 수정
- 로직 수정으로 인한 테스트케이스 fail을 해결

---------

Co-authored-by: 2yunseong <dbsdltjd3701@naver.com>
  • Loading branch information
gabrielyoon7 and 2yunseong authored Feb 12, 2023
1 parent 51fbb02 commit 8208397
Show file tree
Hide file tree
Showing 17 changed files with 8,953 additions and 3,987 deletions.
8 changes: 5 additions & 3 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"rules": {},
"rules": {
"max-lines-per-function": ["error", 10],
"max-depth": ["error", 2]
},
"env": {
"es6": true,
"node": true
Expand All @@ -9,7 +12,6 @@
"sourceType": "module"
},
"extends": [
"eslint:recommended",
"plugin:prettier/recommended"
"eslint:recommended"
]
}
16 changes: 16 additions & 0 deletions __tests__/Car.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* eslint-disable max-lines-per-function */
/* eslint-disable no-undef */
const Car = require('../src/Car');

describe('Car Test', () => {
test.each([
['yun', 12, 12, true],
['ga', 10, 11, false],
])('자동차 전진 검사', (name, go, position, expected) => {
const car = new Car(name);
for (let i = 0; i < go; i++) {
car.move(true);
}
expect(car.getPosition() === position).toBe(expected);
});
});
56 changes: 56 additions & 0 deletions __tests__/GameManager.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* eslint-disable max-lines-per-function */
/* eslint-disable no-undef */
// const Car = require('../src/Car');
const GameManager = require('../src/GameManager');
const RandomGenerator = require('../src/utils/RandomGenerator');

const mockRandom = (number) => {
RandomGenerator.pickRandomNumber = jest.fn();
RandomGenerator.pickRandomNumber.mockReturnValueOnce(number);
};

const mockRandoms = (numbers) => {
RandomGenerator.pickRandomNumber = jest.fn();
numbers.reduce((acc, number) => {
return acc.mockReturnValueOnce(number);
}, RandomGenerator.pickRandomNumber);
};

describe('GameManager Test', () => {
test('Random mock Test', () => {
mockRandom(10);
expect(RandomGenerator.pickRandomNumber()).toBe(10);
});

test.each([
[9, true],
[4, true],
[3, false],
[0, false],
])('특정 숫자가 전진이 가능한 값인지를 판별하는 함수 : %i', (number, expected) => {
mockRandom(number);
const gameManager = new GameManager();
expect(gameManager.isForward()).toEqual(expected);
});

test.each([
[['yun', 'park', 'kim'], [9, 0, 1, 8, 1, 2, 7, 5, 6], ['yun']],
[
['choi', 'ann', 'lee', 'gabi'],
[0, 0, 5, 5, 0, 0, 9, 9],
['lee', 'gabi'],
],
[
['aa', 'bb'],
[0, 0, 5, 5, 0, 0, 9, 9],
['aa', 'bb'],
],
])('승자를 판별하는 테스트 (%#)', (carNames, moves, winners) => {
const gameManager = new GameManager();
const cars = gameManager.generateCars(carNames);
mockRandoms(moves);
gameManager.setCars(cars);
gameManager.raceCars(moves.length/cars.length);
expect(gameManager.judgeWinners()).toEqual(winners);
});
});
28 changes: 28 additions & 0 deletions __tests__/Validation.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-disable max-lines-per-function */
/* eslint-disable no-undef */
const { isValidCarNames, isValidTryCount } = require('../src/utils/Validation');

describe('Validation Test', () => {
test.each([
[['yunseong', 'gabriel'], false],
[['aa', 'bb', 'cc'], true],
[['aa', 'bb', ''], false],
// [['aa', 'bb', '윤생이😁😁'], true], => 고민할 부분(서로게이트 쌍)
])('이름 유효성 검사(%s: %s)', (names, expected) => {
expect(isValidCarNames(names)).toBe(expected);
});

test.each([
['-1', false],
['0', false],
['5', true],
['34.55', false],
['10,000', false],
['string134', false],
['aa', false],
['13n', false],
['', false],
])('시도 횟수가 자연수 인가?(%s : %s)', (tryCount, expected) => {
expect(isValidTryCount(+tryCount)).toBe(expected);
});
});
52 changes: 52 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# 🚗 자동차 경주

## 🔎 프리코스 피드백(참고용)

### 1주차
- 요구사항을 정확히 준수한다
- 커밋 메시지를 의미 있게 작성한다
- git을 통해 관리할 자원에 대해서도 고려한다
- Pull Request를 보내기 전 브랜치를 확인한다
- PR을 한 번 작성했다면 닫지 말고 추가 커밋을 한다
- 이름을 통해 의도를 드러낸다
- 축약하지 않는다
- 공백도 코딩 컨벤션이다
- 공백 라인을 의미 있게 사용한다
- space와 tab을 혼용하지 않는다
- 의미 없는 주석을 달지 않는다
- linter와 Code Formatter의 기능을 활용한다
- EOL(End Of Line)
- 불필요한 console.log를 남기지 않는다
- JavaScript에서 제공하는 API를 적극 활용한다


### 2주차
- README.md를 상세히 작성한다
- 기능 목록을 재검토한다
- 기능 목록을 업데이트한다
- 값을 하드 코딩하지 않는다
- 구현 순서도 코딩 컨벤션이다
- 한 함수가 한 가지 기능만 담당하게 한다
- 함수가 한 가지 기능을 하는지 확인하는 기준을 세운다
- JavaScript에서 객체를 만드는 다양한 방법을 이해하고 사용한다.
- 테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리해본다
- 처음부터 큰 단위의 테스트를 만들지 않는다


### 3주차
- 함수(메서드) 라인에 대한 기준
- 발생할 수 있는 예외 상황에 대해 고민한다
- 비즈니스 로직과 UI 로직을 분리한다
- 객체의 상태 접근을 제한한다
- 객체는 객체스럽게 사용한다
- 필드의 수를 줄이기 위해 노력한다
- 성공하는 케이스 뿐만 아니라 예외에 대한 케이스도 테스트한다
- 테스트 코드도 코드다
- 테스트를 위한 코드는 구현 코드에서 분리되어야 한다
- 단위 테스트하기 어려운 코드를 단위 테스트하기

## 💛 페어 프로그래밍 규칙
- 네비게이터와 드라이버는 요구사항 1개 완료시 변경한다.
- 모든 작업은 정규 시간 안에 끝낸다. 미련이 남아도 생각만 해온다.
- 쉬고 싶으면 쉬고싶다고 편하게 말한다.
- 의견을 자유롭게 내는데 주저하지 말자.
51 changes: 51 additions & 0 deletions docs/REQUIREMENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 자동차 경주 요구사항

## 기능 요구사항

- [x] 주어진 횟수 동안 n 대의 자동차는 전진 또는 멈출 수 있다.
- [x] 자동차에 이름을 부여할 수 있다.
- [x] 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
- [x] 자동차 이름은 쉼표(,)를 기준으로 구분한다.
- [x] 자동차 이름은 5자 이하만 가능하다.
- [x] 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
- [x] 전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다.
- [x] 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다.
- [x] 우승자는 한 명 이상일 수 있다.
- [x] 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다.
- [x] 사용자가 잘못된 입력 값을 작성한 경우 에러 메시지를 보여주고, 다시 입력할 수 있게 한다.
- [x] n대의 자동차를 입력 받을 수 있다.
- [x] 사용자의 이동 횟수 입력을 자연수인지 판별한다.
- [x] 각 자동차 이름이 1자 이상 5자 이하의 글자인지 판별한다.
- [x] 최종 우승자의 이름을 출력한다.
- [x] 최종 우승자를 판별한다.
- [x] 입력받은 시도 횟수 만큼 모든 자동차의 이동을 시도한다.
- [x] 자동차가 이동할지 시도한다.
- [x] 모든 자동차의 현재 이동 상태를 출력한다.
- [x] 자동차의 현재 이동 상태를 출력한다.
- [x] 입력받은 이름을 기반으로 자동차들을 생성한다.
- [x] 입출력을 받게 하는 모듈을 구현한다.
- [x] 자동차 객체를 자동차 이름과 위치 상태를 갖도록 구현한다.
- [x] 0부터 9까지 랜덤으로 숫자를 발생시킨다.

## 프로그래밍 요구사항
- [x] 코드 스타일 가이드에 따라 컨벤션을 준수하며 개발한다.
- [x] 변수 선언시 var를 사용하지 않는다. let, const를 사용한다.
- [x] 전역 변수를 만들지 않는다.
- [x] 축약하지 않는다.
- [x] 하드 코딩된 값 대신에 의미 있는 상수를 활용한다.
- [x] 동등 연산자는 `===`로만 사용한다.
- [x] 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다.
- [x] 함수(또는 메서드)가 한 가지 일만 하도록 만든다.
- [x] 함수(또는 메서드)의 들여쓰기 depth는 2단계까지만 허용한다.
- 예를 들어 while문 안에 if문이 있으면 depth는 2단계 이다.
- 힌트) 함수(또는 메서드) 분리는 들여쓰기 depth를 줄이는 좋은 방법이다.
- [x] else 예약어를 쓰지 않는다.
- [x] 도메인 로직과 UI 로직을 분리한다.
- [x] 모든 도메인 로직에 단위 테스트를 구현한다. (UI 로직은 제외)

## 과제 진행 요구사항

### 기능 목록 및 commit 로그 요구사항

- [x] 기능을 구현하기 전에 `docs/REQUIREMENTS.md` 파일에 구현할 기능 목록을 정리해 추가한다.
- [x] git의 commit 단위는 앞 단계에서 `REQUIREMENTS.md` 파일에 정리한 기능 목록 단위로 추가한다.
Loading

0 comments on commit 8208397

Please sign in to comment.