Node.js
๋ฒ์ 18.17.1
์ด์์ด ํ์ํ๋ค.npm install
npm test
์ด๊ฐ๋จ ์๋์ฐจ ๊ฒฝ์ฃผ ๊ฒ์์ ๊ตฌํํ๋ค.
์์) [ERROR] ์ซ์๊ฐ ์๋ชป๋ ํ์์
๋๋ค.
pobi,woni,jun
5
pobi : --
woni : ----
jun : ---
์ต์ข
์ฐ์น์ : pobi
์ต์ข
์ฐ์น์ : pobi, jun
๊ฒฝ์ฃผํ ์๋์ฐจ ์ด๋ฆ์ ์
๋ ฅํ์ธ์.(์ด๋ฆ์ ์ผํ(,) ๊ธฐ์ค์ผ๋ก ๊ตฌ๋ถ)
pobi,woni,jun
์๋ํ ํ์๋ ๋ช ํ์ธ๊ฐ์?
5
์คํ ๊ฒฐ๊ณผ
pobi : -
woni :
jun : -
pobi : --
woni : -
jun : --
pobi : ---
woni : --
jun : ---
pobi : ----
woni : ---
jun : ----
pobi : -----
woni : ----
jun : -----
์ต์ข
์ฐ์น์ : pobi, jun
App.js
์ play
๋ฉ์๋์ด๋ค. ์๋์ ๊ฐ์ด ํ๋ก๊ทธ๋จ์ ์คํ์ํฌ ์ ์์ด์ผ ํ๋ค.์์
const app = new App();
app.play();
package.json
์ ๋ณ๊ฒฝํ ์ ์๊ณ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(jQuery, Lodash ๋ฑ)๋ฅผ ์ฌ์ฉํ์ง ์๋๋ค. ์์ Vanilla JS๋ก๋ง ๊ตฌํํ๋ค.process.exit()
๋ฅผ ํธ์ถํ์ง ์๋๋ค.ApplicationTest
์ ๋ชจ๋ ํ
์คํธ๊ฐ ์ฑ๊ณตํด์ผ ํ๋ค. ํ
์คํธ๊ฐ ์คํจํ ๊ฒฝ์ฐ 0์ ์ฒ๋ฆฌํ๋ค.__tests__/StringTest.js
๋ฅผ ์ฐธ๊ณ ํ์ฌ ํ์ตํ ํ ํ
์คํธ๋ฅผ ๊ตฌํํ๋ค.@woowacourse/mission-utils
์์ ์ ๊ณตํ๋ Random
๋ฐ Console
API๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌํํด์ผ ํ๋ค.Random.pickNumberInRange()
๋ฅผ ํ์ฉํ๋ค.Console.readLineAsync
, Console.print
๋ฅผ ํ์ฉํ๋ค.MissionUtils.Random.pickNumberInRange(0, 9);
docs/README.md
์ ๊ตฌํํ ๊ธฐ๋ฅ ๋ชฉ๋ก์ ์ ๋ฆฌํด ์ถ๊ฐํ๋ค.docs/README.md
์ ์ ๋ฆฌํ ๊ธฐ๋ฅ ๋ชฉ๋ก ๋จ์๋ก ์ถ๊ฐํ๋ค.import { Console, Random } from "@woowacourse/mission-utils";
class App {
constructor() {
this.carList = [];
this.tryNum = 0;
}
async getInput() {
Console.print(
"๊ฒฝ์ฃผํ ์๋์ฐจ ์ด๋ฆ์ ์
๋ ฅํ์ธ์.(์ด๋ฆ์ ์ผํ(,) ๊ธฐ์ค์ผ๋ก ๊ตฌ๋ถ)"
);
const input = await Console.readLineAsync("");
if (!input.split(",")) {
throw new Error("[ERROR] ์
๋ ฅ๊ฐ์ด ๋น์ด ์์ต๋๋ค.");
} else if (input.split(",").filter((el) => el.length > 5).length > 0) {
throw new Error("[ERROR] ์
๋ ฅ๊ฐ์ ๊ธธ์ด๊ฐ 5๋ณด๋ค ํฝ๋๋ค.");
} else if (input.split(",").filter((el) => !isNaN(+el)).length > 0) {
throw new Error("[ERROR] ์
๋ ฅ๊ฐ์ ํ์์ด ์๋ชป ๋์์ต๋๋ค.");
}
// ์ด๋ฆ ์ค๋ณต๋๋ ๊ฒฝ์ฐ๋ ์์ธ์ฒ๋ฆฌํ๊ธฐ
input.split(",").map((el) => this.carList.push({ name: el, score: 0 }));
await this.getTryNum();
}
async getTryNum() {
Console.print("์๋ํ ํ์๋ ๋ช ํ์ธ๊ฐ์?");
const input = await Console.readLineAsync("");
if (!isNaN(input)) {
Console.print("\n์คํ ๊ฒฐ๊ณผ");
while (this.tryNum < input) {
this.action();
this.tryNum++;
}
this.calcWinner();
} else {
throw new Error("[ERROR] ์
๋ ฅ๊ฐ์ ํ์์ด ์๋ชป ๋์์ต๋๋ค.");
}
}
action() {
this.carList = this.carList.map(({ name, score }) => ({
name: name,
score: score + +this.randomNumGenerator(),
}));
this.carList.map(({ name, score }) =>
Console.print(`${name} : ${"-".repeat(score)}`)
);
Console.print("\n");
}
randomNumGenerator() {
const num = Random.pickNumberInRange(0, 9);
return num >= 4 ? true : false;
}
calcWinner() {
const scoreList = this.carList.map((el) => el.score);
const max = Math.max(...scoreList);
const maxList = this.carList.filter((el) => el.score === max);
Console.print(`์ต์ก ์ฐ์น์ : ${maxList.map((el) => el.name).join(", ")}`);
}
async play() {
await this.getInput();
}
}
export default App;
App.js
import { Console } from "@woowacourse/mission-utils";
import { validateCarNames } from "./ValidateCarNames";
import { addCarNames } from "./AddCarNames";
import { validateTrialNumbers } from "./ValidateTrialNumbers";
import { playGame } from "./PlayGame";
class App {
constructor() {
this.carNames = [];
this.trialNumbers = 0;
}
async getCarNames() {
Console.print(
"๊ฒฝ์ฃผํ ์๋์ฐจ ์ด๋ฆ์ ์
๋ ฅํ์ธ์.(์ด๋ฆ์์ ์ผํ(,) ๊ธฐ์ค์ผ๋ก ๊ตฌ๋ถ)"
);
const INPUT = await Console.readLineAsync("");
const IS_VALID = validateCarNames(INPUT);
if (IS_VALID) {
addCarNames(INPUT, this.carNames);
await this.getTrialNumbers();
}
}
async getTrialNumbers() {
Console.print("์๋ํ ํ์๋ ๋ช ํ์ธ๊ฐ์?");
const INPUT = await Console.readLineAsync("");
const IS_VALID = validateTrialNumbers(INPUT);
if (IS_VALID) {
playGame(this.trialNumbers, INPUT, this.carNames);
this.printWinner();
}
}
printWinner() {
const SCORE_LIST = this.carNames.map((el) => el.score);
const MAX_SCORE = Math.max(...SCORE_LIST);
const MAX_SCORE_LIST = this.carNames.filter((el) => el.score === MAX_SCORE);
Console.print(
`์ต์ก ์ฐ์น์ : ${MAX_SCORE_LIST.map((el) => el.name).join(", ")}`
);
}
async play() {
await this.getCarNames();
}
}
export default App;
AddCarNames
export const addCarNames = (input, carNames) => {
const CAR_NAME = input.split(",");
CAR_NAME.forEach((el) => carNames.push({ name: el, score: 0 }));
};
MoveCar
import { Console } from "@woowacourse/mission-utils";
import { randomNumberGenerator } from "./RandomNumberGenerator";
export const moveCar = (carNames) => {
carNames = carNames.map(({ name, score }) => ({
name: name,
score: score + +randomNumberGenerator(),
}));
carNames.map(({ name, score }) =>
Console.print(`${name} : ${"-".repeat(score)}`)
);
Console.print("\n");
};
playGame
import { Console } from "@woowacourse/mission-utils";
import { moveCar } from "./MoveCar";
export const playGame = (trialNumbers, input, carNames) => {
Console.print("\n์คํ ๊ฒฐ๊ณผ");
while (trialNumbers < input) {
moveCar(carNames);
trialNumbers = trialNumbers + 1;
}
};
RandomNumberGenerator
import { Random } from "@woowacourse/mission-utils";
export const randomNumberGenerator = () => {
const RANDOM_NUMBER = Random.pickNumberInRange(0, 9);
if (RANDOM_NUMBER >= 4) {
return true;
} else {
return false;
}
};
validateCarNames
export const validateCarNames = (input) => {
const CAR_NAME = input.split(",");
if (!CAR_NAME) {
throw new Error("[ERROR] ์
๋ ฅ๊ฐ์ด ๋น์ด ์์ต๋๋ค.");
} else if (CAR_NAME.length < 2) {
throw new Error("[ERROR] ์
๋ ฅ๊ฐ์ด ์ผํ๋ก ๊ตฌ๋ถ๋์ด ์์ง ์์ต๋๋ค.");
} else if (CAR_NAME.filter((el) => el.length > 5).length > 0) {
throw new Error("[ERROR] ์
๋ ฅ๊ฐ์ ๊ธธ์ด๊ฐ 5๋ณด๋ค ํฝ๋๋ค.");
} else if (CAR_NAME.filter((el) => !isNaN(+el)).length > 0) {
throw new Error("[ERROR] ์
๋ ฅ๊ฐ์ ํ์์ด ์๋ชป ๋์์ต๋๋ค.");
} else if (new Set(CAR_NAME).size !== CAR_NAME.length) {
throw new Error("[ERROR] ์
๋ ฅ๊ฐ์ด ์ค๋ณต๋ฉ๋๋ค.");
}
return true;
};
validateTrialNumbers
export const validateTrialNumbers = (input) => {
if (input === 0) {
throw new Error("[ERROR] ์
๋ ฅ๊ฐ์ 0๋ณด๋ค ์ปค์ผ ํฉ๋๋ค.");
} else if (isNaN(input)) {
throw new Error("[ERROR] ์
๋ ฅ๊ฐ์ ํ์์ด ์๋ชป ๋์์ต๋๋ค.");
}
return true;
};
์๋์ฐจ ์ด๋ฆ ์ ํจ์ฑ ๊ฒ์ฌ ํ ์คํธ
import { validateCarNames } from "../src/ValidateCarNames";
test("์
๋ ฅ๊ฐ์ด ๋น์ด์์ ๊ฒฝ์ฐ", () => {
expect(() => validateCarNames("")).toThrow("[ERROR]");
});
test("์
๋ ฅ๊ฐ์ด ์ผํ๋ก ๊ตฌ๋ถ๋์ด ์์ง ์์ ๊ฒฝ์ฐ", () => {
expect(() => validateCarNames("pobiwoni")).toThrow("[ERROR]");
});
test("์
๋ ฅ๊ฐ์ ๊ธธ์ด๊ฐ 5๋ณด๋ค ํฐ ๊ฒฝ์ฐ", () => {
expect(() => validateCarNames("pobi,woni,stella")).toThrow("[ERROR]");
});
test("์
๋ ฅ๊ฐ ํ์์ด ์๋ชป๋ ๊ฒฝ์ฐ", () => {
expect(() => validateCarNames("123,456")).toThrow("[ERROR]");
});
test("์
๋ ฅ๊ฐ์ด ์ค๋ณต๋ ๊ฒฝ์ฐ", () => {
expect(() => validateCarNames("pobi,pobi,woni")).toThrow("[ERROR]");
});
์ํ ํ์ ์ ํจ์ฑ ๊ฒ์ฌ ํ ์คํธ
import { validateTrialNumbers } from "../src/ValidateTrialNumbers";
test("์
๋ ฅ๊ฐ์ด 0์ธ ๊ฒฝ์ฐ", () => {
expect(() => validateTrialNumbers(0)).toThrow("[ERROR]");
});
test("์
๋ ฅ๊ฐ ํ์์ด ์๋ชป๋ ๊ฒฝ์ฐ", () => {
expect(() => validateTrialNumbers("abc")).toThrow("[ERROR]");
});
์ฐ์น์ ์ถ๋ ฅ ํ ์คํธ
import App from "../src/App";
test("์ฐ์น์ ๋ค์์ผ ๋ ํ
์คํธ", () => {
const app = new App();
app.carNames = [
{ name: "Car1", score: 2 },
{ name: "Car2", score: 4 },
{ name: "Car3", score: 4 },
];
const message = app.printWinner();
expect(message).toBe("์ต์ก ์ฐ์น์ : Car2, Car3");
});
test("์ฐ์น์ 1๋ช
์ผ ๋ ํ
์คํธ", () => {
const app = new App();
app.carNames = [
{ name: "Car1", score: 2 },
{ name: "Car2", score: 5 },
{ name: "Car3", score: 4 },
];
const message = app.printWinner();
expect(message).toBe("์ต์ก ์ฐ์น์ : Car2");
});
์ปค๋ฐ ๋๋๋ฆฌ๊ธฐ
https://wooono.tistory.com/667
ํ ์คํธ ์ฝ๋ ์์ฑ๋ฒ
https://codiving.kr/115
https://velog.io/@velopert/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%85%8C%EC%8A%A4%ED%8C%85%EC%9D%98-%EA%B8%B0%EC%B4%88