Skip to content
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

[자동차 경주] 최현진 미션 제출합니다. #442

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: 'airbnb-base',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
rules: {
eqeqeq: ['error', 'always'], // == 대신 === 사용 강제
'no-console': 'warn', // console.log는 경고로 표시
'no-var': 'error', // var 사용 금지, let이나 const 사용
'prefer-const': 'error', // const 사용 강제
'arrow-parens': ['error', 'as-needed'], // 화살표 함수 매개변수 괄호 생략 허용
'prefer-template': 'error', // 문자열 연결 대신 템플릿 리터럴 사용
'object-curly-newline': ['error', { consistent: true }], // 중괄호 안에서 줄바꿈 규칙
'comma-dangle': ['error', 'always-multiline'], // 여러 줄일 때 마지막에 쉼표 허용
'no-alert': 'error', // alert, confirm, prompt 사용 금지
'no-unused-vars': 'warn', // 사용되지 않는 변수 경고
'consistent-return': 'error', // 일관된 반환 값 강제
'no-redeclare': 'error', // 동일한 변수 재선언 금지
'default-case': 'error', // switch문에서 default case 강제
'no-else-return': 'error', // else 사용 금지, if문 안에 return 있을 경우
'no-implicit-coercion': ['error', { allow: ['!!'] }], // !! 외의 암시적 형 변환 금지
'no-magic-numbers': ['warn', { ignore: [0, 1] }], // 0, 1 외의 상수 숫자 경고
'max-depth': ['error', 2], // 들여쓰기 깊이 2로 제한
'no-ternary': 'error', // 삼항 연산자 사용 금지
camelcase: 'off', // camelCase 규칙 비활성화
},
};
151 changes: 150 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,150 @@
# javascript-racingcar-precourse
# Week2 - 자동차 경주 게임


## 기능 요구 사항

- 주어진 횟수 동안 여러 대의 자동차가 전진 또는 멈출 수 있는 간단한 자동차 경주 게임을 구현한다.

- 각 자동차는 이름을 부여할 수 있으며, 경주 중 전진할 때마다 해당 이름을 함께 출력한다.

- 자동차 이름은 쉼표(,)로 구분되며, 이름은 최대 5자 이하만 가능하다.

- 사용자는 경주할 횟수를 입력할 수 있다.

- 자동차가 전진하는 조건은 0에서 9 사이의 무작위 값을 생성하여, 이 값이 4 이상일 경우이다.

- 경주가 끝난 후 누가 우승했는지 결과를 보여준다. 우승자는 한 명 이상일 수 있다.

- 우승자가 여러 명일 경우, 쉼표(,)로 구분하여 표시한다.

- 사용자가 잘못된 값을 입력할 경우 "[ERROR]"로 시작하는 메시지를 출력하고 프로그램이 종료된다.

 


## 입출력 요구 사항

- 입력:
- 경주할 자동차 이름 (쉼표로 구분)
- 예시: `pobi,woni,jun`
- 시도할 횟수
- 예시: `5`

- 출력:
- 라운드별 실행 결과 (자동차의 진행 상태를 '-'로 표시)
- 예시:
```
pobi : -
woni :
jun : -

pobi : --
woni : -
jun : --

pobi : ---
woni : --
jun : ---

pobi : ----
woni : ---
jun : ----

pobi : -----
woni : ----
jun : -----
```

- 최종 우승자 출력
- 단독 우승자: `최종 우승자 : pobi`
- 공동 우승자: `최종 우승자 : pobi, jun`

 


## 기능 구현 목록 체크 리스트

- [x] **자동차 이름 입력 기능**
- 사용자가 경주할 자동차의 이름을 쉼표로 구분하여 입력받는다.
- 입력받은 각 이름이 5자 이하인지 확인하며, 유효하지 않은 경우 "[ERROR]"로 시작하는 메시지를 출력하고 종료한다.

- [x] **이동 횟수 입력 기능**
- 사용자가 시도할 횟수를 입력받고, 해당 값이 양의 정수인지 검사한다.
- 유효하지 않은 경우 "[ERROR]"로 시작하는 메시지를 출력하고 종료한다.

- [x] **자동차 전진 기능**
- 각 자동차에 대해 매 시도마다 0에서 9 사이의 무작위 값을 생성하여 4 이상일 경우 한 칸 전진한다.
- `@woowacourse/mission-utils`의 `Random.pickNumberInRange()` 메서드를 활용한다.

- [x] **라운드별 진행 상황 출력 기능**
- 각 라운드가 끝날 때마다 각 자동차의 이름과 해당 라운드까지의 진행 상태를 출력한다.
- 각 전진 상황은 `-`로 표시한다.

- [x] **우승자 결정 기능**
- 모든 라운드가 종료된 후 가장 멀리 이동한 자동차(들)를 우승자로 결정하여 출력한다.
- 우승자가 여러 명일 경우, 쉼표로 구분하여 출력한다.

- [x] **에러 처리 기능**
- 사용자가 잘못된 입력을 할 경우 "[ERROR]"로 시작하는 메시지를 출력하고 프로그램을 종료한다.

 


## 과제 진행 요구 사항 체크 리스트

- [x] 자동차 경주 게임을 포크하고 클론하기.

- [x] 기능 구현하기 전, 구현할 기능 목록을 `README.md`에 정리하기.

- [x] Git의 커밋 단위는 앞 단계에서 README.md에 정리한 기능 목록 단위로 추가한다.
- [AngularJS Git Commit Message Conventions](https://gist.github.com/stephenparish/9941e89d80e2bc58a153)을 참고해 커밋 메시지를 작성한다.

 


## 프로그래밍 요구 사항

- [x] `Node.js 20.17.0` 버전에서 실행 가능해야 한다.

- [x] package.json 파일은 변경할 수 없다.
- 제공된 라이브러리와 스타일 라이브러리 이외의 외부 라이브러리 사용은 불가하다.

- [x] 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다.

- [x] 자바스크립트 코드 컨벤션을 지키면서 프로그래밍한다.
- 기본적으로 [JavaScript Style Guide](https://github.com/airbnb/javascript)를 원칙으로 한다.

- [x] indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.

- [x] 3항 연산자를 쓰지 않는다.

- [x] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.

- [x] Jest를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다.

 


### 프로그래밍 환경 설정

- [x] `ESLint` 세팅하기.
- [x] `프로그래밍 요구 사항2` 의 규칙을 `ESLint rule` 에 추가하기.

- [x] **Node.js 버전** `20.17.0` 이상인지 확인하기.

- [x] **npm 버전** `10.8.2` 이상인지 확인하기.


 



## 실행 요구 사항 및 제출 체크 리스트

- [x] 프로그램 실행의 시작점은 App.js의 run().

- [x] 프로그램 종료 시 process.exit()는 호출하지 않는다.

- [x] 요구 사항에 명시된 출력 형식을 따르지 않으면 `0점`

- [x] 테스트가 실패하면 점수가 `0점`이 되므로 제출하기 전에 반드시 확인한다.
43 changes: 43 additions & 0 deletions __tests__/Input.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { getCarNames, getMoveAttempts } from '../src/utils/getInput';
import { CONSTANTS } from '../src/constant/Constant';
import { Console } from '@woowacourse/mission-utils';

jest.mock('@woowacourse/mission-utils', () => ({
Console: {
readLineAsync: jest.fn(),
},
}));

describe('자동차 이름 입력 테스트', () => {
beforeEach(() => {
jest.clearAllMocks();
});

test('이름이 5자 이하인 경우 배열로 반환.', async () => {
Console.readLineAsync.mockResolvedValueOnce('pobi,crong,honux');
const carNames = await getCarNames();
expect(carNames).toEqual(['pobi', 'crong', 'honux']);
});

test('빈 입력이나 공백 입력 시 에러가 발생해야 합니다.', async () => {
Console.readLineAsync.mockResolvedValueOnce('');
await expect(getCarNames()).rejects.toThrow(CONSTANTS.ERROR_MSG0);
});
});

describe('시도 횟수 입력 테스트', () => {
beforeEach(() => {
jest.clearAllMocks();
});

test('양의 정수 입력 시 정상적으로 반환.', async () => {
Console.readLineAsync.mockResolvedValueOnce('5');
const attempts = await getMoveAttempts();
expect(attempts).toBe(5);
});

test('0 이하의 값 입력 시 에러가 발생.', async () => {
Console.readLineAsync.mockResolvedValueOnce('0');
await expect(getMoveAttempts()).rejects.toThrow(CONSTANTS.ERROR_MSG2);
});
});
23 changes: 23 additions & 0 deletions __tests__/Print.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { printWinners } from '../src/utils/Print';

describe('우승자 결정 테스트', () => {
test('가장 높은 위치에 있는 자동차가 단독 우승.', () => {
const raceResults = [
{ name: 'pobi', position: 3 },
{ name: 'crong', position: 2 },
{ name: 'honux', position: 1 },
];
const winners = printWinners(raceResults);
expect(winners).toBe('최종 우승자 : pobi');
});

test('여러 우승자가 발생할 경우, 쉼표로 구분하여 출력.', () => {
const raceResults = [
{ name: 'pobi', position: 5 },
{ name: 'crong', position: 5 },
{ name: 'honux', position: 3 },
];
const winners = printWinners(raceResults);
expect(winners).toBe('최종 우승자 : pobi, crong');
});
});
Loading