From 581af1820c5a111ddabc35b3b7691e1c9783fdb3 Mon Sep 17 00:00:00 2001 From: Sejin Cha Date: Sun, 30 Jun 2024 18:43:22 +0900 Subject: [PATCH 01/14] =?UTF-8?q?feat:=20rule=20=EC=9C=84=EC=A3=BC?= =?UTF-8?q?=EC=9D=98=20=EA=B5=AC=EC=A1=B0=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/Car.js | 4 +-- src/domains/NewGame.js | 0 src/domains/NewRacingGame.js | 70 ++++++++++++++++++++++++++++++++++++ src/main.js | 33 ++++++++++++----- 4 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 src/domains/NewGame.js create mode 100644 src/domains/NewRacingGame.js diff --git a/src/domains/Car.js b/src/domains/Car.js index 2a4e736..252a9d6 100644 --- a/src/domains/Car.js +++ b/src/domains/Car.js @@ -14,8 +14,8 @@ export default class Car { return this.#position } - move() { - this.#position += 1 + move(diff) { + this.#position += diff } validate() { diff --git a/src/domains/NewGame.js b/src/domains/NewGame.js new file mode 100644 index 0000000..e69de29 diff --git a/src/domains/NewRacingGame.js b/src/domains/NewRacingGame.js new file mode 100644 index 0000000..29b3d14 --- /dev/null +++ b/src/domains/NewRacingGame.js @@ -0,0 +1,70 @@ +import Car from './Car.js' +import { getRandomNumber } from '../utils/getRandomNumber.js' + +export default class NewRacingGame { + #results + + constructor({ carNames, config: { maxRound, rules, ...config } }) { + this.config = config + this.maxRound = maxRound + this.rules = rules + + this.cars = this.createCars(carNames) + this.#results = [] + this.currentRound = 0 + } + + get results() { + return this.#results + } + + get winner() { + const lastResult = this.results.at(-1) + const maxPosition = Math.max(...Object.values(lastResult)) + return Object.keys(lastResult).filter(name => lastResult[name] === maxPosition) + } + + get randomRule() { + const ruleNames = Object.keys(this.rules) + return ruleNames[getRandomNumber(0, ruleNames.length - 1)] + } + + createCars(carNames) { + return carNames.map(name => new Car(name)) + } + + doRacing() { + const ruleName = this.randomRule + this.cars.forEach(car => { + this.race(ruleName, car) + }) + this.updateResult(ruleName) + } + + race(ruleName, car) { + const ruleResult = this.rules[ruleName]() + if (typeof ruleResult === 'number') { + car.move(ruleResult) + } else if (ruleResult) { + car.move(1) + } + } + + updateResult(ruleName) { + const captureCars = this.cars.reduce((acc, car) => ({ + ...acc, + [car.name]: car.position, + }), {}) + this.#results.push({ + ruleName, cars: captureCars, + }) + } + + play() { + while (this.currentRound < this.maxRound) { + this.doRacing() + this.currentRound += 1 + } + } + +} \ No newline at end of file diff --git a/src/main.js b/src/main.js index 39a300e..60c3685 100644 --- a/src/main.js +++ b/src/main.js @@ -1,19 +1,36 @@ -import RacingGame from './domains/RacingGame.js' import Printer from './service/Printer.js' +import NewRacingGame from './domains/NewRacingGame.js' +import { getRandomNumber } from './utils/getRandomNumber.js' async function main() { const printer = new Printer() - const racingGame = new RacingGame({ - display: printer, - maxRound: 5, + // const racingGame = new RacingGame({ + // display: printer, + // maxRound: 5, + // config: { + // min: 0, + // max: 9, + // threshold: 4, + // }, + // }) + // await racingGame.play() + + const newRacingGame = new NewRacingGame({ + carNames: ['cha', 'cszzi', 'hello'], config: { - min: 0, - max: 9, - threshold: 4, + maxRound: 3, + rules: { + randomNumberMove: () => getRandomNumber(0, 5), + randomNumberOver40: () => getRandomNumber(0, 100) > 40, + randomNumberOver3: () => getRandomNumber(1, 4) > 3, + }, }, }) - await racingGame.play() + + newRacingGame.play() + console.log(newRacingGame.results) + console.log(newRacingGame.winner) } main() From 3e50b3a80ff860aec2086f1ee7d6d0bdcb764d55 Mon Sep 17 00:00:00 2001 From: Sejin Cha Date: Sun, 30 Jun 2024 19:12:15 +0900 Subject: [PATCH 02/14] =?UTF-8?q?feat:=20random=20rule=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=EC=9C=BC=EB=A1=9C=20Game,=20RacingGame=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/Game.js | 59 +++++++++++------------ src/domains/NewGame.js | 0 src/domains/NewRacingGame.js | 70 ---------------------------- src/domains/RacingGame.js | 90 +++++++++++++----------------------- src/main.js | 20 ++------ 5 files changed, 60 insertions(+), 179 deletions(-) delete mode 100644 src/domains/NewGame.js delete mode 100644 src/domains/NewRacingGame.js diff --git a/src/domains/Game.js b/src/domains/Game.js index f97b18d..20f8889 100644 --- a/src/domains/Game.js +++ b/src/domains/Game.js @@ -1,46 +1,39 @@ +import { getRandomNumber } from '../utils/getRandomNumber.js' + export default class Game { - constructor({ display, maxRound, config }) { - this.display = display - this.currentRound = 0 + #results + + constructor({ maxRound, rules }) { this.maxRound = maxRound - this.config = config - this.rounds = [] - this.bindRounds() + this.rules = rules + this.#results = [] + this.currentRound = 0 } - async setup() {} + get results() { + return this.#results + } - eachRound() {} + get lastResult() { + return this.#results.at(-1) + } - finish() {} + get randomRule() { + const ruleNames = Object.keys(this.rules) + return ruleNames[getRandomNumber(0, ruleNames.length - 1)] + } - bindRounds() { - if (Object.getPrototypeOf(this).hasOwnProperty('eachRound')) { - this.rounds = Array.from({ length: this.maxRound }, _ => - this.eachRound.bind(this) - ) - } else { - const roundMethodNames = Object.getOwnPropertyNames( - Object.getPrototypeOf(this) - ).filter(prop => prop.startsWith('round') && prop !== 'rounds') - this.rounds = roundMethodNames.map(methodName => - this[methodName].bind(this) - ) - } + addResult(resultAfterRound) { + this.#results.push(resultAfterRound) } - async play() { - try { - await this.setup() - } catch (error) { - this.display.printError(error) - return this.play() - } + doRound() { + } + play() { while (this.currentRound < this.maxRound) { - this.rounds[this.currentRound++]() + this.doRound() + this.currentRound += 1 } - - this.finish() } -} +} \ No newline at end of file diff --git a/src/domains/NewGame.js b/src/domains/NewGame.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/domains/NewRacingGame.js b/src/domains/NewRacingGame.js deleted file mode 100644 index 29b3d14..0000000 --- a/src/domains/NewRacingGame.js +++ /dev/null @@ -1,70 +0,0 @@ -import Car from './Car.js' -import { getRandomNumber } from '../utils/getRandomNumber.js' - -export default class NewRacingGame { - #results - - constructor({ carNames, config: { maxRound, rules, ...config } }) { - this.config = config - this.maxRound = maxRound - this.rules = rules - - this.cars = this.createCars(carNames) - this.#results = [] - this.currentRound = 0 - } - - get results() { - return this.#results - } - - get winner() { - const lastResult = this.results.at(-1) - const maxPosition = Math.max(...Object.values(lastResult)) - return Object.keys(lastResult).filter(name => lastResult[name] === maxPosition) - } - - get randomRule() { - const ruleNames = Object.keys(this.rules) - return ruleNames[getRandomNumber(0, ruleNames.length - 1)] - } - - createCars(carNames) { - return carNames.map(name => new Car(name)) - } - - doRacing() { - const ruleName = this.randomRule - this.cars.forEach(car => { - this.race(ruleName, car) - }) - this.updateResult(ruleName) - } - - race(ruleName, car) { - const ruleResult = this.rules[ruleName]() - if (typeof ruleResult === 'number') { - car.move(ruleResult) - } else if (ruleResult) { - car.move(1) - } - } - - updateResult(ruleName) { - const captureCars = this.cars.reduce((acc, car) => ({ - ...acc, - [car.name]: car.position, - }), {}) - this.#results.push({ - ruleName, cars: captureCars, - }) - } - - play() { - while (this.currentRound < this.maxRound) { - this.doRacing() - this.currentRound += 1 - } - } - -} \ No newline at end of file diff --git a/src/domains/RacingGame.js b/src/domains/RacingGame.js index da6cefc..598b00d 100644 --- a/src/domains/RacingGame.js +++ b/src/domains/RacingGame.js @@ -1,75 +1,47 @@ -import Game from './Game.js' import Car from './Car.js' -import { getRandomNumber } from '../utils/getRandomNumber.js' -import { racingValidations } from '../validations/racing.js' +import Game from './Game.js' export default class RacingGame extends Game { - cars = {} - carNames = [] - - get winners() { - const maxPosition = Math.max( - ...this.carNames.map(name => this.cars[name].position) - ) - return this.carNames.filter( - name => this.cars[name].position === maxPosition - ) - } - - async setup() { - this.initializeCars() - const carNames = await this.readCarNames() - carNames.forEach(name => this.registerCar(name)) - this.validate() - } - - eachRound() { - this.display.print('') - this.carNames.forEach(name => this.moveCarByRandomNumber(name)) - this.carNames.forEach(name => this.showCarPosition(name)) + constructor({ carNames, config }) { + super(config) + this.cars = this.createCars(carNames) } - finish() { - this.showWinners() + get winner() { + const lastCapturedCars = this.lastResult.cars + const maxPosition = Math.max(...Object.values(lastCapturedCars)) + return Object.keys(lastCapturedCars).filter(name => lastCapturedCars[name] === maxPosition) } - initializeCars() { - this.cars = {} - this.carNames = [] + createCars(carNames) { + return carNames.map(name => new Car(name)) } - async readCarNames() { - return ( - await this.display.read( - '경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).' - ) - ).split(',') - } - - registerCar(name) { - const registeredCar = new Car(name) - this.cars[registeredCar.name] = registeredCar - this.carNames.push(registeredCar.name) - } - - validate() { - Object.values(racingValidations).forEach(({ check, errorMessage }) => { - if (!check(this.carNames)) { - throw new Error(errorMessage) - } + doRound() { + const ruleName = this.randomRule + this.cars.forEach(car => { + this.race(ruleName, car) }) + this.updateResult(ruleName) } - moveCarByRandomNumber(name) { - const { min, max, threshold } = this.config - if (getRandomNumber(min, max) > threshold) this.cars[name].move() + race(ruleName, car) { + const ruleResult = this.rules[ruleName]() + if (typeof ruleResult === 'number') { + car.move(ruleResult) + } else if (ruleResult) { + car.move(1) + } } - showCarPosition(name) { - this.display.print(`${name} : ${'_'.repeat(this.cars[name].position)}`) + updateResult(ruleName) { + const captureCars = this.cars.reduce((acc, car) => ({ + ...acc, + [car.name]: car.position, + }), {}) + this.addResult({ + ruleName, cars: captureCars, + }) } - showWinners() { - this.display.print(`\n🎉 우승자 : ${this.winners.join(', ')} 🎉\n`) - } -} +} \ No newline at end of file diff --git a/src/main.js b/src/main.js index 60c3685..96fd3fc 100644 --- a/src/main.js +++ b/src/main.js @@ -1,25 +1,11 @@ -import Printer from './service/Printer.js' -import NewRacingGame from './domains/NewRacingGame.js' +import RacingGame from './domains/RacingGame.js' import { getRandomNumber } from './utils/getRandomNumber.js' async function main() { - const printer = new Printer() - - // const racingGame = new RacingGame({ - // display: printer, - // maxRound: 5, - // config: { - // min: 0, - // max: 9, - // threshold: 4, - // }, - // }) - // await racingGame.play() - - const newRacingGame = new NewRacingGame({ + const newRacingGame = new RacingGame({ carNames: ['cha', 'cszzi', 'hello'], config: { - maxRound: 3, + maxRound: 5, rules: { randomNumberMove: () => getRandomNumber(0, 5), randomNumberOver40: () => getRandomNumber(0, 100) > 40, From f781060b57921fcc8e0e0ccdb229589a98059caf Mon Sep 17 00:00:00 2001 From: Sejin Cha Date: Sun, 30 Jun 2024 22:15:49 +0900 Subject: [PATCH 03/14] =?UTF-8?q?refactor:=20=EB=B3=80=EC=88=98=EB=AA=85?= =?UTF-8?q?=20=EB=B0=8F=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/Game.js | 19 +++++++++++----- src/domains/RacingGame.js | 46 +++++++++++++++++++-------------------- src/main.js | 6 ++--- 3 files changed, 40 insertions(+), 31 deletions(-) diff --git a/src/domains/Game.js b/src/domains/Game.js index 20f8889..4c99ffc 100644 --- a/src/domains/Game.js +++ b/src/domains/Game.js @@ -2,27 +2,36 @@ import { getRandomNumber } from '../utils/getRandomNumber.js' export default class Game { #results + #rules - constructor({ maxRound, rules }) { + constructor({ startRound = 1, maxRound, rules }) { this.maxRound = maxRound - this.rules = rules + this.currentRound = startRound - 1 + + this.#rules = rules + this.currentRule = '' + this.#results = [] - this.currentRound = 0 } get results() { return this.#results } + get rules() { + return this.#rules + } + get lastResult() { return this.#results.at(-1) } - get randomRule() { + selectRandomRule() { const ruleNames = Object.keys(this.rules) - return ruleNames[getRandomNumber(0, ruleNames.length - 1)] + this.currentRule = ruleNames[getRandomNumber(0, ruleNames.length - 1)] } + addResult(resultAfterRound) { this.#results.push(resultAfterRound) } diff --git a/src/domains/RacingGame.js b/src/domains/RacingGame.js index 598b00d..00c0443 100644 --- a/src/domains/RacingGame.js +++ b/src/domains/RacingGame.js @@ -8,40 +8,40 @@ export default class RacingGame extends Game { } get winner() { - const lastCapturedCars = this.lastResult.cars - const maxPosition = Math.max(...Object.values(lastCapturedCars)) - return Object.keys(lastCapturedCars).filter(name => lastCapturedCars[name] === maxPosition) + const { cars } = this.lastResult + const maxPosition = Math.max(...Object.values(cars)) + return Object.keys(cars).filter(name => cars[name] === maxPosition) } + createCars(carNames) { return carNames.map(name => new Car(name)) } + createResult() { + return { + ruleName: this.currentRule, + cars: this.cars.reduce((acc, car) => ({ + ...acc, + [car.name]: car.position, + }), {}), + } + } + doRound() { - const ruleName = this.randomRule + this.selectRandomRule() this.cars.forEach(car => { - this.race(ruleName, car) + this.race(car) }) - this.updateResult(ruleName) + this.addResult(this.createResult()) } - race(ruleName, car) { - const ruleResult = this.rules[ruleName]() - if (typeof ruleResult === 'number') { - car.move(ruleResult) - } else if (ruleResult) { + race(car) { + const moveCommand = this.rules[this.currentRule]() + if (typeof moveCommand === 'number') { + car.move(moveCommand) + } else if (moveCommand) { car.move(1) } } - - updateResult(ruleName) { - const captureCars = this.cars.reduce((acc, car) => ({ - ...acc, - [car.name]: car.position, - }), {}) - this.addResult({ - ruleName, cars: captureCars, - }) - } - -} \ No newline at end of file +} diff --git a/src/main.js b/src/main.js index 96fd3fc..2d58ff2 100644 --- a/src/main.js +++ b/src/main.js @@ -7,9 +7,9 @@ async function main() { config: { maxRound: 5, rules: { - randomNumberMove: () => getRandomNumber(0, 5), - randomNumberOver40: () => getRandomNumber(0, 100) > 40, - randomNumberOver3: () => getRandomNumber(1, 4) > 3, + ruleName1: () => getRandomNumber(0, 5), + ruleName2: () => getRandomNumber(0, 100) > 40, + ruleName3: () => getRandomNumber(1, 4) > 3, }, }, }) From eb7a7b03c17125a7f8066da4a393a360e09d711a Mon Sep 17 00:00:00 2001 From: Sejin Cha Date: Sun, 30 Jun 2024 22:22:27 +0900 Subject: [PATCH 04/14] =?UTF-8?q?feat:=20=EC=A4=91=EA=B0=84=20=EB=9D=BC?= =?UTF-8?q?=EC=9A=B4=EB=93=9C=EB=B6=80=ED=84=B0=20=ED=94=8C=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/Game.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/domains/Game.js b/src/domains/Game.js index 4c99ffc..8b252bc 100644 --- a/src/domains/Game.js +++ b/src/domains/Game.js @@ -4,9 +4,9 @@ export default class Game { #results #rules - constructor({ startRound = 1, maxRound, rules }) { + constructor({ maxRound, rules }) { this.maxRound = maxRound - this.currentRound = startRound - 1 + this.currentRound = 1 this.#rules = rules this.currentRule = '' @@ -39,8 +39,9 @@ export default class Game { doRound() { } - play() { - while (this.currentRound < this.maxRound) { + play(startRound = this.currentRound, endRound = this.maxRound) { + this.currentRound = startRound + while (this.currentRound <= endRound) { this.doRound() this.currentRound += 1 } From b558de446d09bc82f50d215018921ab1b79d53ef Mon Sep 17 00:00:00 2001 From: Sejin Cha Date: Mon, 1 Jul 2024 20:32:44 +0900 Subject: [PATCH 05/14] =?UTF-8?q?feat:=20=EA=B0=80=EC=9C=84=EB=B0=94?= =?UTF-8?q?=EC=9C=84=EB=B3=B4=20=EB=AF=B8=EB=8B=88=EA=B2=8C=EC=9E=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20readInput=20=EC=9C=A0=ED=8B=B8?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/Game.js | 22 ++++---- src/domains/RacingGame.js | 43 +++++++++++----- src/domains/miniGames/MiniGame.js | 14 +++++ src/domains/miniGames/RockPaperScissors.js | 60 ++++++++++++++++++++++ src/main.js | 17 +++--- src/service/Printer.js | 2 +- src/utils/readInput.js | 32 ++++++++++++ src/validations/miniGames.js | 6 +++ 8 files changed, 164 insertions(+), 32 deletions(-) create mode 100644 src/domains/miniGames/MiniGame.js create mode 100644 src/domains/miniGames/RockPaperScissors.js create mode 100644 src/utils/readInput.js create mode 100644 src/validations/miniGames.js diff --git a/src/domains/Game.js b/src/domains/Game.js index 8b252bc..9fb1110 100644 --- a/src/domains/Game.js +++ b/src/domains/Game.js @@ -2,14 +2,14 @@ import { getRandomNumber } from '../utils/getRandomNumber.js' export default class Game { #results - #rules + #miniGames - constructor({ maxRound, rules }) { + constructor({ maxRound, miniGames }) { this.maxRound = maxRound this.currentRound = 1 - this.#rules = rules - this.currentRule = '' + this.#miniGames = miniGames + this.currentMiniGame = '' this.#results = [] } @@ -18,17 +18,17 @@ export default class Game { return this.#results } - get rules() { - return this.#rules + get miniGames() { + return this.#miniGames } get lastResult() { return this.#results.at(-1) } - selectRandomRule() { - const ruleNames = Object.keys(this.rules) - this.currentRule = ruleNames[getRandomNumber(0, ruleNames.length - 1)] + selectRandomMiniGame() { + const miniGameNames = Object.keys(this.miniGames) + this.currentMiniGame = miniGameNames[getRandomNumber(0, miniGameNames.length - 1)] } @@ -39,10 +39,10 @@ export default class Game { doRound() { } - play(startRound = this.currentRound, endRound = this.maxRound) { + async play(startRound = this.currentRound, endRound = this.maxRound) { this.currentRound = startRound while (this.currentRound <= endRound) { - this.doRound() + await this.doRound() this.currentRound += 1 } } diff --git a/src/domains/RacingGame.js b/src/domains/RacingGame.js index 00c0443..7f26700 100644 --- a/src/domains/RacingGame.js +++ b/src/domains/RacingGame.js @@ -2,9 +2,11 @@ import Car from './Car.js' import Game from './Game.js' export default class RacingGame extends Game { - constructor({ carNames, config }) { + constructor({ carNames, playerName, config }) { super(config) + this.player = playerName this.cars = this.createCars(carNames) + this.gameLogs = [] } get winner() { @@ -20,28 +22,43 @@ export default class RacingGame extends Game { createResult() { return { - ruleName: this.currentRule, + ruleName: this.currentMiniGame, cars: this.cars.reduce((acc, car) => ({ ...acc, [car.name]: car.position, }), {}), + gameLogs: this.gameLogs, } } - doRound() { - this.selectRandomRule() - this.cars.forEach(car => { - this.race(car) - }) + + async doRound() { + this.selectRandomMiniGame() + + for (const car of this.cars) { + await this.race(car) + } this.addResult(this.createResult()) + this.gameLogs = [] + } + + async doMiniGame(car) { + const miniGame = this.miniGames[this.currentMiniGame] + if (car.name === this.player) { + return await miniGame.PvC(car.name) + } + return miniGame.CvC(car.name) } - race(car) { - const moveCommand = this.rules[this.currentRule]() - if (typeof moveCommand === 'number') { - car.move(moveCommand) - } else if (moveCommand) { - car.move(1) + async race(car) { + const result = await this.doMiniGame(car) + + if (result.hasOwnProperty('score')) { + car.move(result.score) + } + if (result.hasOwnProperty('win')) { + car.move(result.win ? 1 : 0) } + this.gameLogs.push(result.log) } } diff --git a/src/domains/miniGames/MiniGame.js b/src/domains/miniGames/MiniGame.js new file mode 100644 index 0000000..060651a --- /dev/null +++ b/src/domains/miniGames/MiniGame.js @@ -0,0 +1,14 @@ +import Printer from '../../service/Printer.js' + +export default class MiniGame { + + constructor() { + this.printer = new Printer() + } + + CvC(player) { + } + + async PvC(player) { + } +} diff --git a/src/domains/miniGames/RockPaperScissors.js b/src/domains/miniGames/RockPaperScissors.js new file mode 100644 index 0000000..ed3f990 --- /dev/null +++ b/src/domains/miniGames/RockPaperScissors.js @@ -0,0 +1,60 @@ +import MiniGame from './MiniGame.js' +import { getRandomNumber } from '../../utils/getRandomNumber.js' +import readInput from '../../utils/readInput.js' +import { rockPaperScissorsValidations } from '../../validations/miniGames.js' + +const rockScissorPaperMap = { + rock: { + win: 'scissor', + icon: '👊', + }, + scissor: { + win: 'paper', + icon: '✌️', + }, + paper: { + win: 'rock', + icon: '✋', + }, +} +export default class RockPaperScissors extends MiniGame { + + static get answerList() { + return Object.keys(rockScissorPaperMap) + } + + static get randomAnswer() { + return this.answerList[getRandomNumber(0, 2)].padEnd(2, ' ') + } + + + static getIcon(answer) { + return rockScissorPaperMap[answer].icon.padEnd(3, ' ') + } + + static getPlayerResult(playerName, playerAnswer, opponentAnswer) { + let score = 0 + let status = 'draw' + if (rockScissorPaperMap[playerAnswer].win === opponentAnswer) { + score = 1 + status = 'win' + } else if (rockScissorPaperMap[opponentAnswer].win === playerAnswer) { + score = -1 + status = 'lose' + } + return { + score, + log: `${playerName}:${this.getIcon(playerAnswer)}VS computer:${this.getIcon(opponentAnswer)} ➡️ ${status}`, + } + } + + static CvC(playerName) { + return this.getPlayerResult(playerName, this.randomAnswer, this.randomAnswer) + } + + static async PvC(playerName) { + const answerIndex = await readInput('가위바위보 대결 : 1.바위 2.가위 3.보', rockPaperScissorsValidations) + const playerAnswer = this.answerList[Number(answerIndex) - 1] + return this.getPlayerResult(`*${playerName}`, playerAnswer, this.randomAnswer) + } +} diff --git a/src/main.js b/src/main.js index 2d58ff2..288276a 100644 --- a/src/main.js +++ b/src/main.js @@ -1,21 +1,24 @@ import RacingGame from './domains/RacingGame.js' -import { getRandomNumber } from './utils/getRandomNumber.js' +import RockPaperScissors from './domains/miniGames/RockPaperScissors.js' async function main() { const newRacingGame = new RacingGame({ carNames: ['cha', 'cszzi', 'hello'], + playerName: 'cszzi', config: { maxRound: 5, - rules: { - ruleName1: () => getRandomNumber(0, 5), - ruleName2: () => getRandomNumber(0, 100) > 40, - ruleName3: () => getRandomNumber(1, 4) > 3, + miniGames: { + RockPaperScissors, }, }, }) - newRacingGame.play() - console.log(newRacingGame.results) + await newRacingGame.play(1, 1) + console.log(newRacingGame.lastResult) + + await newRacingGame.play(2, 2) + console.log(newRacingGame.lastResult) + console.log(newRacingGame.winner) } diff --git a/src/service/Printer.js b/src/service/Printer.js index f400fc7..c0aa6e8 100644 --- a/src/service/Printer.js +++ b/src/service/Printer.js @@ -18,7 +18,7 @@ export default class Printer { this.rl = createReadline() } return new Promise(resolve => { - this.rl.question(`⬇️ ${message}\n\n`, input => { + this.rl.question(`${message}\n`, input => { resolve(input) this.rl.close() }) diff --git a/src/utils/readInput.js b/src/utils/readInput.js new file mode 100644 index 0000000..4b14e47 --- /dev/null +++ b/src/utils/readInput.js @@ -0,0 +1,32 @@ +import createReadline from './createReadline.js' + +const askQuestion = (rl, message) => { + return new Promise((resolve) => { + rl.question(`${message}\n`, input => { + resolve(input) + }) + }) +} + +const findInputError = (input, validations) => { + const { errorMessage } = validations.find(({ check }) => !check(input)) ?? {} + return errorMessage +} + +const readInput = (message, validations = [], option = 'repeat') => { + const rl = createReadline() + const processInput = async () => { + const input = await askQuestion(rl, message) + const errorMessage = findInputError(input, validations) + + if (option === 'repeat' && errorMessage) { + console.error(errorMessage) + return processInput() + } + rl.close() + return input + } + return processInput() +} + +export default readInput \ No newline at end of file diff --git a/src/validations/miniGames.js b/src/validations/miniGames.js new file mode 100644 index 0000000..e750e0a --- /dev/null +++ b/src/validations/miniGames.js @@ -0,0 +1,6 @@ +export const rockPaperScissorsValidations = [ + { + check: (input) => [1, 2, 3].includes(Number(input)), + errorMessage: '가위바위보 입력값은 1,2,3 중 하나여야 합니다.', + }, +] \ No newline at end of file From 08e0ea18b91f0999298097826cd9c6435f5cd375 Mon Sep 17 00:00:00 2001 From: Sejin Cha Date: Mon, 1 Jul 2024 22:34:45 +0900 Subject: [PATCH 06/14] =?UTF-8?q?feat:=20=EC=A3=BC=EC=82=AC=EC=9C=84=20?= =?UTF-8?q?=EB=AF=B8=EB=8B=88=EA=B2=8C=EC=9E=84=20=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?=EB=8B=A4=EC=A4=91=20Player=20=EA=B0=80=EB=8A=A5=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/RacingGame.js | 8 ++--- src/domains/miniGames/DiceDiffGame.js | 27 ++++++++++++++++ src/domains/miniGames/GuessRandomNumber.js | 26 +++++++++++++++ src/domains/miniGames/GuessTimeOut.js | 37 ++++++++++++++++++++++ src/domains/miniGames/MiniGame.js | 14 -------- src/domains/miniGames/RockPaperScissors.js | 8 ++--- src/main.js | 20 ++++++------ src/utils/readInput.js | 2 +- src/validations/miniGames.js | 7 ++++ 9 files changed, 116 insertions(+), 33 deletions(-) create mode 100644 src/domains/miniGames/DiceDiffGame.js create mode 100644 src/domains/miniGames/GuessRandomNumber.js create mode 100644 src/domains/miniGames/GuessTimeOut.js delete mode 100644 src/domains/miniGames/MiniGame.js diff --git a/src/domains/RacingGame.js b/src/domains/RacingGame.js index 7f26700..a896455 100644 --- a/src/domains/RacingGame.js +++ b/src/domains/RacingGame.js @@ -2,9 +2,9 @@ import Car from './Car.js' import Game from './Game.js' export default class RacingGame extends Game { - constructor({ carNames, playerName, config }) { + constructor({ carNames, playerNames, config }) { super(config) - this.player = playerName + this.players = playerNames this.cars = this.createCars(carNames) this.gameLogs = [] } @@ -44,7 +44,8 @@ export default class RacingGame extends Game { async doMiniGame(car) { const miniGame = this.miniGames[this.currentMiniGame] - if (car.name === this.player) { + if (this.players.includes(car.name)) { + console.log(`${car.name} TURN`) return await miniGame.PvC(car.name) } return miniGame.CvC(car.name) @@ -52,7 +53,6 @@ export default class RacingGame extends Game { async race(car) { const result = await this.doMiniGame(car) - if (result.hasOwnProperty('score')) { car.move(result.score) } diff --git a/src/domains/miniGames/DiceDiffGame.js b/src/domains/miniGames/DiceDiffGame.js new file mode 100644 index 0000000..bddc4e5 --- /dev/null +++ b/src/domains/miniGames/DiceDiffGame.js @@ -0,0 +1,27 @@ +import { getRandomNumber } from '../../utils/getRandomNumber.js' +import readInput from '../../utils/readInput.js' + +export default class DiceDiffGame { + static get diceNumber() { + return getRandomNumber(1, 6) + } + + static getPlayerResult(playerName, playerAnswer, opponentAnswer) { + const diff = opponentAnswer - playerAnswer + return { + score: diff, + log: `${playerName}:${playerAnswer} VS computer:${opponentAnswer} ➡➡ ${diff}`, + } + } + + static CvC(playerName) { + return this.getPlayerResult(playerName, this.diceNumber, this.diceNumber) + } + + static async PvC(playerName) { + await readInput('주사위 던지기 게임 : Enter 입력 시 주사위를 던지고, 그 차이만큼 이동합니다.') + const answer = this.diceNumber + console.log(`${answer}이 나왔습니다.`) + return this.getPlayerResult(`*${playerName}`, answer, this.diceNumber) + } +} \ No newline at end of file diff --git a/src/domains/miniGames/GuessRandomNumber.js b/src/domains/miniGames/GuessRandomNumber.js new file mode 100644 index 0000000..c9b6ccc --- /dev/null +++ b/src/domains/miniGames/GuessRandomNumber.js @@ -0,0 +1,26 @@ +import { getRandomNumber } from '../../utils/getRandomNumber.js' +import readInput from '../../utils/readInput.js' +import { guessRandomNumberValidations } from '../../validations/miniGames.js' + +export default class GuessRandomNumber { + static get randomNumber() { + return getRandomNumber(1, 10) + } + + static getPlayerResult(playerName, playerAnswer, opponentAnswer) { + const win = playerAnswer === opponentAnswer + return { + win, + log: `${playerName}:${playerAnswer} VS computer:${opponentAnswer} ➡➡ ${win ? 'win' : 'lose'}`, + } + } + + static CvC(playerName) { + return this.getPlayerResult(playerName, this.randomNumber, this.randomNumber) + } + + static async PvC(playerName) { + const answer = await readInput('숫자 맞추기 대결 : 1 부터 10 까지의 숫자 중 하나를 입력\n', guessRandomNumberValidations) + return this.getPlayerResult(`*${playerName}`, Number(answer), this.randomNumber) + } +} \ No newline at end of file diff --git a/src/domains/miniGames/GuessTimeOut.js b/src/domains/miniGames/GuessTimeOut.js new file mode 100644 index 0000000..5fa412f --- /dev/null +++ b/src/domains/miniGames/GuessTimeOut.js @@ -0,0 +1,37 @@ +import { getRandomNumber } from '../../utils/getRandomNumber.js' +import readInput from '../../utils/readInput.js' + +export default class GuessTimeOut { + static get randomSeconds() { + return getRandomNumber(5, 10) // 5초에서 10초 사이의 랜덤 시간 생성 + } + + + static async getPlayerTime(opponentTime) { + const startTime = Date.now() + await readInput(`타이머 맞추기: ${opponentTime} 뒤에 Enter 누르기`) + const endTime = Date.now() + return (endTime - startTime) / 1000 // 밀리초를 초로 변환 + } + + static getPlayerResult(playerName, playerTime, opponentTime) { + const difference = Math.abs(playerTime - opponentTime) + const win = difference < 0.5 + return { + win, + log: `${playerName}:${playerTime.toFixed(2)}s VS computer:${opponentTime.toFixed(2)}s ➡➡ ${win ? 'win' : 'lose'}`, + } + } + + static CvC(playerName) { + const opponentTime = this.randomSeconds + const playerTime = opponentTime + (Math.random() * 2 - 1) + return this.getPlayerResult(playerName, playerTime, opponentTime) + } + + static async PvC(playerName) { + const opponentTime = this.randomSeconds + const playerTime = await this.getPlayerTime(opponentTime) + return this.getPlayerResult(`*${playerName}`, playerTime, opponentTime) + } +} diff --git a/src/domains/miniGames/MiniGame.js b/src/domains/miniGames/MiniGame.js deleted file mode 100644 index 060651a..0000000 --- a/src/domains/miniGames/MiniGame.js +++ /dev/null @@ -1,14 +0,0 @@ -import Printer from '../../service/Printer.js' - -export default class MiniGame { - - constructor() { - this.printer = new Printer() - } - - CvC(player) { - } - - async PvC(player) { - } -} diff --git a/src/domains/miniGames/RockPaperScissors.js b/src/domains/miniGames/RockPaperScissors.js index ed3f990..dd30c40 100644 --- a/src/domains/miniGames/RockPaperScissors.js +++ b/src/domains/miniGames/RockPaperScissors.js @@ -1,4 +1,3 @@ -import MiniGame from './MiniGame.js' import { getRandomNumber } from '../../utils/getRandomNumber.js' import readInput from '../../utils/readInput.js' import { rockPaperScissorsValidations } from '../../validations/miniGames.js' @@ -17,7 +16,7 @@ const rockScissorPaperMap = { icon: '✋', }, } -export default class RockPaperScissors extends MiniGame { +export default class RockPaperScissors { static get answerList() { return Object.keys(rockScissorPaperMap) @@ -27,7 +26,6 @@ export default class RockPaperScissors extends MiniGame { return this.answerList[getRandomNumber(0, 2)].padEnd(2, ' ') } - static getIcon(answer) { return rockScissorPaperMap[answer].icon.padEnd(3, ' ') } @@ -44,7 +42,7 @@ export default class RockPaperScissors extends MiniGame { } return { score, - log: `${playerName}:${this.getIcon(playerAnswer)}VS computer:${this.getIcon(opponentAnswer)} ➡️ ${status}`, + log: `${playerName}:${this.getIcon(playerAnswer)}VS computer:${this.getIcon(opponentAnswer)} ➡➡ ${status}`, } } @@ -53,7 +51,7 @@ export default class RockPaperScissors extends MiniGame { } static async PvC(playerName) { - const answerIndex = await readInput('가위바위보 대결 : 1.바위 2.가위 3.보', rockPaperScissorsValidations) + const answerIndex = await readInput('가위바위보 대결 : 1.바위 2.가위 3.보\n', rockPaperScissorsValidations) const playerAnswer = this.answerList[Number(answerIndex) - 1] return this.getPlayerResult(`*${playerName}`, playerAnswer, this.randomAnswer) } diff --git a/src/main.js b/src/main.js index 288276a..329deea 100644 --- a/src/main.js +++ b/src/main.js @@ -1,24 +1,26 @@ import RacingGame from './domains/RacingGame.js' +import GuessTimeOut from './domains/miniGames/GuessTimeOut.js' +import GuessRandomNumber from './domains/miniGames/GuessRandomNumber.js' import RockPaperScissors from './domains/miniGames/RockPaperScissors.js' +import DiceDiffGame from './domains/miniGames/DiceDiffGame.js' async function main() { const newRacingGame = new RacingGame({ - carNames: ['cha', 'cszzi', 'hello'], - playerName: 'cszzi', + carNames: ['miko', 'cszzi', 'hello'], + playerNames: ['cszzi', 'miko'], config: { - maxRound: 5, + maxRound: 3, miniGames: { RockPaperScissors, + GuessRandomNumber, + GuessTimeOut, + DiceDiffGame, }, }, }) - await newRacingGame.play(1, 1) - console.log(newRacingGame.lastResult) - - await newRacingGame.play(2, 2) - console.log(newRacingGame.lastResult) - + await newRacingGame.play() + console.log(newRacingGame.results) console.log(newRacingGame.winner) } diff --git a/src/utils/readInput.js b/src/utils/readInput.js index 4b14e47..8d479cb 100644 --- a/src/utils/readInput.js +++ b/src/utils/readInput.js @@ -2,7 +2,7 @@ import createReadline from './createReadline.js' const askQuestion = (rl, message) => { return new Promise((resolve) => { - rl.question(`${message}\n`, input => { + rl.question(message, input => { resolve(input) }) }) diff --git a/src/validations/miniGames.js b/src/validations/miniGames.js index e750e0a..de4050e 100644 --- a/src/validations/miniGames.js +++ b/src/validations/miniGames.js @@ -3,4 +3,11 @@ export const rockPaperScissorsValidations = [ check: (input) => [1, 2, 3].includes(Number(input)), errorMessage: '가위바위보 입력값은 1,2,3 중 하나여야 합니다.', }, +] + +export const guessRandomNumberValidations = [ + { + check: (input) => Number(input) >= 1 && Number(input) <= 10 && Number.isInteger(Number(input)), + errorMessage: '1 부터 10 까지의 자연수만 가능합니다.', + }, ] \ No newline at end of file From 35c5da20fc82908eba103cf98decb313bf833877 Mon Sep 17 00:00:00 2001 From: Sejin Cha Date: Thu, 4 Jul 2024 22:38:18 +0900 Subject: [PATCH 07/14] =?UTF-8?q?feat:=20view,=20controller=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20=EB=B0=8F=20=EC=9E=85=EC=B6=9C=EB=A0=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/Car.js | 6 +- src/domains/Game.js | 11 ++- src/domains/RacingGame.js | 63 ++++++++++------ src/domains/RacingGameController.js | 44 +++++++++++ src/domains/miniGames/DiceDiffGame.js | 6 +- src/domains/miniGames/GuessRandomNumber.js | 6 +- src/domains/miniGames/GuessTimeOut.js | 6 +- src/domains/miniGames/RockPaperScissors.js | 12 ++- src/domains/view/RacingGameViewer.js | 85 ++++++++++++++++++++++ src/main.js | 26 +++---- src/service/Printer.js | 40 +++++----- 11 files changed, 242 insertions(+), 63 deletions(-) create mode 100644 src/domains/RacingGameController.js create mode 100644 src/domains/view/RacingGameViewer.js diff --git a/src/domains/Car.js b/src/domains/Car.js index 252a9d6..735c063 100644 --- a/src/domains/Car.js +++ b/src/domains/Car.js @@ -15,7 +15,11 @@ export default class Car { } move(diff) { - this.#position += diff + if (this.#position + diff < 0) { + this.#position = 0 + } else { + this.#position += diff + } } validate() { diff --git a/src/domains/Game.js b/src/domains/Game.js index 9fb1110..28f69be 100644 --- a/src/domains/Game.js +++ b/src/domains/Game.js @@ -1,17 +1,19 @@ import { getRandomNumber } from '../utils/getRandomNumber.js' +import { EventEmitter } from 'events' export default class Game { #results #miniGames - constructor({ maxRound, miniGames }) { - this.maxRound = maxRound + constructor({ miniGames }) { + this.maxRound = 1 this.currentRound = 1 this.#miniGames = miniGames this.currentMiniGame = '' this.#results = [] + this.eventEmitter = new EventEmitter() } get results() { @@ -31,6 +33,10 @@ export default class Game { this.currentMiniGame = miniGameNames[getRandomNumber(0, miniGameNames.length - 1)] } + emitEvent(eventName, ...args) { + this.eventEmitter.emit(eventName, ...args) + } + addResult(resultAfterRound) { this.#results.push(resultAfterRound) @@ -39,6 +45,7 @@ export default class Game { doRound() { } + async play(startRound = this.currentRound, endRound = this.maxRound) { this.currentRound = startRound while (this.currentRound <= endRound) { diff --git a/src/domains/RacingGame.js b/src/domains/RacingGame.js index a896455..0e141b4 100644 --- a/src/domains/RacingGame.js +++ b/src/domains/RacingGame.js @@ -2,63 +2,84 @@ import Car from './Car.js' import Game from './Game.js' export default class RacingGame extends Game { - constructor({ carNames, playerNames, config }) { - super(config) - this.players = playerNames - this.cars = this.createCars(carNames) - this.gameLogs = [] + constructor({ miniGames }) { + super({ miniGames }) + this.cars = [] + this.players = [] } - get winner() { - const { cars } = this.lastResult - const maxPosition = Math.max(...Object.values(cars)) - return Object.keys(cars).filter(name => cars[name] === maxPosition) + get maxPosition() { + return Math.max(...Object.values(this.lastResult.positions)) } + get winners() { + const { positions } = this.lastResult + return Object.keys(positions).filter(name => positions[name] === this.maxPosition) + } + + setMaxRound(maxRound) { + this.maxRound = maxRound + } - createCars(carNames) { - return carNames.map(name => new Car(name)) + + addPlayer(name) { + const newPlayerCar = new Car(name) + + this.players.push(newPlayerCar.name) + this.cars.push(newPlayerCar) + } + + addCar(name) { + const newCar = new Car(name) + this.cars.push(newCar) } createResult() { - return { + const result = { ruleName: this.currentMiniGame, - cars: this.cars.reduce((acc, car) => ({ + positions: this.cars.reduce((acc, car) => ({ ...acc, [car.name]: car.position, }), {}), gameLogs: this.gameLogs, } + this.gameLogs = {} + return result } async doRound() { this.selectRandomMiniGame() + this.emitEvent('roundStart') for (const car of this.cars) { await this.race(car) } + this.addResult(this.createResult()) - this.gameLogs = [] + this.emitEvent('roundEnd') } + async doMiniGame(car) { const miniGame = this.miniGames[this.currentMiniGame] + if (this.players.includes(car.name)) { - console.log(`${car.name} TURN`) + this.emitEvent('miniGameStart', car.name) return await miniGame.PvC(car.name) } + return miniGame.CvC(car.name) } async race(car) { - const result = await this.doMiniGame(car) - if (result.hasOwnProperty('score')) { - car.move(result.score) + const miniGameResult = await this.doMiniGame(car) + if (miniGameResult.hasOwnProperty('score')) { + car.move(miniGameResult.score) } - if (result.hasOwnProperty('win')) { - car.move(result.win ? 1 : 0) + if (miniGameResult.hasOwnProperty('win')) { + car.move(miniGameResult.win ? 1 : 0) } - this.gameLogs.push(result.log) + this.gameLogs = { ...this.gameLogs, [car.name]: miniGameResult.log } } } diff --git a/src/domains/RacingGameController.js b/src/domains/RacingGameController.js new file mode 100644 index 0000000..6aa3b74 --- /dev/null +++ b/src/domains/RacingGameController.js @@ -0,0 +1,44 @@ +export default class RacingGameController { + constructor(racingGame, viewer) { + this.racingGame = racingGame + this.viewer = viewer + + this.racingGame.eventEmitter.on('roundStart', () => this.onRoundStart()) + this.racingGame.eventEmitter.on('roundEnd', () => this.onRoundEnd()) + this.racingGame.eventEmitter.on('miniGameStart', (name) => this.onMiniGameStart(name)) + } + + onRoundStart() { + this.viewer.displayStartRound(this.racingGame) + } + + onRoundEnd() { + this.viewer.displayGameLogs(this.racingGame) + this.viewer.displayRoundResult(this.racingGame) + } + + onMiniGameStart(name) { + this.viewer.displayMiniGameStart(name) + } + + async setupGame() { + const playerNames = await this.viewer.readPlayerCarNames() + playerNames.split(',').forEach(name => this.racingGame.addPlayer(name)) + + const carNames = await this.viewer.readCarNames() + carNames.split(',').forEach(name => this.racingGame.addCar(name)) + + const maxRound = await this.viewer.readRoundCount() + this.racingGame.setMaxRound(Number(maxRound)) + } + + async startGame() { + this.viewer.displayGameStart() + await this.setupGame() + await this.racingGame.play() + + this.viewer.displayWinners(this.racingGame) + } +} + + diff --git a/src/domains/miniGames/DiceDiffGame.js b/src/domains/miniGames/DiceDiffGame.js index bddc4e5..d356e51 100644 --- a/src/domains/miniGames/DiceDiffGame.js +++ b/src/domains/miniGames/DiceDiffGame.js @@ -10,7 +10,11 @@ export default class DiceDiffGame { const diff = opponentAnswer - playerAnswer return { score: diff, - log: `${playerName}:${playerAnswer} VS computer:${opponentAnswer} ➡➡ ${diff}`, + log: { + player: playerAnswer, + computer: opponentAnswer, + result: diff, + }, } } diff --git a/src/domains/miniGames/GuessRandomNumber.js b/src/domains/miniGames/GuessRandomNumber.js index c9b6ccc..76aa201 100644 --- a/src/domains/miniGames/GuessRandomNumber.js +++ b/src/domains/miniGames/GuessRandomNumber.js @@ -11,7 +11,11 @@ export default class GuessRandomNumber { const win = playerAnswer === opponentAnswer return { win, - log: `${playerName}:${playerAnswer} VS computer:${opponentAnswer} ➡➡ ${win ? 'win' : 'lose'}`, + log: { + player: playerAnswer, + computer: opponentAnswer, + result: win ? 'win' : 'lose', + }, } } diff --git a/src/domains/miniGames/GuessTimeOut.js b/src/domains/miniGames/GuessTimeOut.js index 5fa412f..391cde4 100644 --- a/src/domains/miniGames/GuessTimeOut.js +++ b/src/domains/miniGames/GuessTimeOut.js @@ -19,7 +19,11 @@ export default class GuessTimeOut { const win = difference < 0.5 return { win, - log: `${playerName}:${playerTime.toFixed(2)}s VS computer:${opponentTime.toFixed(2)}s ➡➡ ${win ? 'win' : 'lose'}`, + log: { + player: playerTime.toFixed(2), + computer: opponentTime.toFixed(2), + result: win ? 'win' : 'lose', + }, } } diff --git a/src/domains/miniGames/RockPaperScissors.js b/src/domains/miniGames/RockPaperScissors.js index dd30c40..a588c5b 100644 --- a/src/domains/miniGames/RockPaperScissors.js +++ b/src/domains/miniGames/RockPaperScissors.js @@ -32,17 +32,21 @@ export default class RockPaperScissors { static getPlayerResult(playerName, playerAnswer, opponentAnswer) { let score = 0 - let status = 'draw' + let result = 'draw' if (rockScissorPaperMap[playerAnswer].win === opponentAnswer) { score = 1 - status = 'win' + result = 'win' } else if (rockScissorPaperMap[opponentAnswer].win === playerAnswer) { score = -1 - status = 'lose' + result = 'lose' } return { score, - log: `${playerName}:${this.getIcon(playerAnswer)}VS computer:${this.getIcon(opponentAnswer)} ➡➡ ${status}`, + log: { + player: this.getIcon(playerAnswer), + computer: this.getIcon(opponentAnswer), + result, + }, } } diff --git a/src/domains/view/RacingGameViewer.js b/src/domains/view/RacingGameViewer.js new file mode 100644 index 0000000..314a348 --- /dev/null +++ b/src/domains/view/RacingGameViewer.js @@ -0,0 +1,85 @@ +import ConsolePrinter from '../../service/Printer.js' +import readInput from '../../utils/readInput.js' + +export default class RacingGameViewer { + constructor() { + this.printer = new ConsolePrinter({ + roundStart: '------------------------⭐ Round%{1}⭐️️------------------------', + carPosition: '%{1} : %{2} (%{3}%{4} ➡ %{5})', + gameLog: '%{1} : %{2} VS computer : %{3} ➡➡ %{4}', + miniGameStart: '>> player %{1} Turn!', + winner: '최종 우승자는 👑 %{1} 입니다. 축하합니다!', + }) + } + + displayStartRound({ currentRound }) { + this.printer.printWithTemplate('roundStart', [currentRound]) + } + + displayRoundResult({ results, maxPosition, currentRound }) { + const { positions } = results[currentRound - 1] + const prevPositions = currentRound > 1 ? results[currentRound - 2].positions : {} + + Object.entries(positions).forEach(([name, position]) => { + const positionString = this.formatPositionString(position, maxPosition) + const positionDiffArgs = this.getPositionDiffArgs(position, prevPositions[name] || 0) + + this.printer.printWithTemplate( + 'carPosition', + [name.padEnd(6, ' '), positionString, ...positionDiffArgs], + ) + }) + this.printer.lineBreak() + } + + formatPositionString(position, maxPosition) { + return '⛳️'.padStart((position + 1) * 2, '__').padEnd((maxPosition + 1) * 2, '__') + } + + getPositionDiffArgs(position, prevPosition) { + const positionDiff = position - prevPosition + return [positionDiff < 0 ? '' : '+', positionDiff, position] + } + + displayGameLogs({ results }) { + const { gameLogs } = results.at(-1) + this.printer.lineBreak() + + Object.entries(gameLogs).forEach(([name, log]) => { + this.printer.printWithTemplate('gameLog', [ + name.padEnd(6, ' '), + ...Object.values(log), + ]) + }) + + this.printer.lineBreak() + } + + displayGameStart() { + this.printer.print('🚕 레이싱 게임을 시작합니다 🚗') + this.printer.print('각 라운드마다 랜덤 미니 게임을 진행하여 이동할 수 있습니다.') + this.printer.lineBreak() + } + + displayMiniGameStart(playerName) { + this.printer.lineBreak() + this.printer.printWithTemplate('miniGameStart', [playerName]) + } + + async readPlayerCarNames() { + return await readInput('직접 레이싱에 참여할 자동차들의 이름을 입력해주세요. (쉼표로 구분)\n') + } + + async readCarNames() { + return await readInput('봇으로 참여할 자동차들의 이름을 입력해주세요. (쉼표로 구분)\n') + } + + async readRoundCount() { + return await readInput('몇 라운드 플레이할 지 알려주세요.\n') + } + + displayWinners({ winners }) { + this.printer.printWithTemplate('winner', [winners.join(', ')]) + this.printer.lineBreak() + } +} diff --git a/src/main.js b/src/main.js index 329deea..624807c 100644 --- a/src/main.js +++ b/src/main.js @@ -3,25 +3,23 @@ import GuessTimeOut from './domains/miniGames/GuessTimeOut.js' import GuessRandomNumber from './domains/miniGames/GuessRandomNumber.js' import RockPaperScissors from './domains/miniGames/RockPaperScissors.js' import DiceDiffGame from './domains/miniGames/DiceDiffGame.js' +import RacingGameController from './domains/RacingGameController.js' +import RacingGameViewer from './domains/view/RacingGameViewer.js' async function main() { - const newRacingGame = new RacingGame({ - carNames: ['miko', 'cszzi', 'hello'], - playerNames: ['cszzi', 'miko'], - config: { - maxRound: 3, - miniGames: { - RockPaperScissors, - GuessRandomNumber, - GuessTimeOut, - DiceDiffGame, - }, + const racingGame = new RacingGame({ + miniGames: { + RockPaperScissors, + GuessRandomNumber, + GuessTimeOut, + DiceDiffGame, }, }) - await newRacingGame.play() - console.log(newRacingGame.results) - console.log(newRacingGame.winner) + const view = new RacingGameViewer() + const controller = new RacingGameController(racingGame, view) + + await controller.startGame() } main() diff --git a/src/service/Printer.js b/src/service/Printer.js index c0aa6e8..7ac1ba0 100644 --- a/src/service/Printer.js +++ b/src/service/Printer.js @@ -1,27 +1,31 @@ -import createReadline from '../utils/createReadline.js' - -export default class Printer { - constructor() { - this.rl = createReadline() +export default class ConsolePrinter { + constructor(template) { + this.template = template } - print(message) { - console.log(message) + format(templateKey, messages) { + let result = this.template[templateKey] + messages.forEach((message, index) => { + result = result.replaceAll(`%{${index + 1}}`, message) + }) + return result } - printError(error) { - console.log(`\n⚠️ ${error.message}\n`) + print(...messages) { + console.log(...messages) } - async read(message) { - if (!this.rl.isOpened) { - this.rl = createReadline() + printWithTemplate(templateKey, messages) { + if (this.template.hasOwnProperty(templateKey)) { + console.log(this.format(templateKey, messages)) + } else { + console.log(...messages) } - return new Promise(resolve => { - this.rl.question(`${message}\n`, input => { - resolve(input) - this.rl.close() - }) - }) } + + lineBreak() { + console.log('') + } + + } From b3c7a5742e76a0dc4e841bfafd16eba17e1aee44 Mon Sep 17 00:00:00 2001 From: Sejin Cha Date: Thu, 4 Jul 2024 22:45:09 +0900 Subject: [PATCH 08/14] =?UTF-8?q?refactor:=20=EB=A9=94=EC=86=8C=EB=93=9C?= =?UTF-8?q?=20=EC=88=9C=EC=84=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/RacingGame.js | 44 +++++++++----------- src/domains/RacingGameController.js | 21 ++++------ src/domains/view/RacingGameViewer.js | 62 ++++++++++++---------------- 3 files changed, 55 insertions(+), 72 deletions(-) diff --git a/src/domains/RacingGame.js b/src/domains/RacingGame.js index 0e141b4..f11cf8f 100644 --- a/src/domains/RacingGame.js +++ b/src/domains/RacingGame.js @@ -21,10 +21,8 @@ export default class RacingGame extends Game { this.maxRound = maxRound } - addPlayer(name) { const newPlayerCar = new Car(name) - this.players.push(newPlayerCar.name) this.cars.push(newPlayerCar) } @@ -34,6 +32,18 @@ export default class RacingGame extends Game { this.cars.push(newCar) } + async doRound() { + this.selectRandomMiniGame() + this.emitEvent('roundStart') + + for (const car of this.cars) { + await this.race(car) + } + + this.addResult(this.createResult()) + this.emitEvent('roundEnd') + } + createResult() { const result = { ruleName: this.currentMiniGame, @@ -47,20 +57,17 @@ export default class RacingGame extends Game { return result } - - async doRound() { - this.selectRandomMiniGame() - this.emitEvent('roundStart') - - for (const car of this.cars) { - await this.race(car) + async race(car) { + const miniGameResult = await this.doMiniGame(car) + if (miniGameResult.hasOwnProperty('score')) { + car.move(miniGameResult.score) } - - this.addResult(this.createResult()) - this.emitEvent('roundEnd') + if (miniGameResult.hasOwnProperty('win')) { + car.move(miniGameResult.win ? 1 : 0) + } + this.gameLogs = { ...this.gameLogs, [car.name]: miniGameResult.log } } - async doMiniGame(car) { const miniGame = this.miniGames[this.currentMiniGame] @@ -71,15 +78,4 @@ export default class RacingGame extends Game { return miniGame.CvC(car.name) } - - async race(car) { - const miniGameResult = await this.doMiniGame(car) - if (miniGameResult.hasOwnProperty('score')) { - car.move(miniGameResult.score) - } - if (miniGameResult.hasOwnProperty('win')) { - car.move(miniGameResult.win ? 1 : 0) - } - this.gameLogs = { ...this.gameLogs, [car.name]: miniGameResult.log } - } } diff --git a/src/domains/RacingGameController.js b/src/domains/RacingGameController.js index 6aa3b74..6718409 100644 --- a/src/domains/RacingGameController.js +++ b/src/domains/RacingGameController.js @@ -12,13 +12,20 @@ export default class RacingGameController { this.viewer.displayStartRound(this.racingGame) } + onMiniGameStart(name) { + this.viewer.displayMiniGameStart(name) + } + onRoundEnd() { this.viewer.displayGameLogs(this.racingGame) this.viewer.displayRoundResult(this.racingGame) } - onMiniGameStart(name) { - this.viewer.displayMiniGameStart(name) + async startGame() { + this.viewer.displayGameStart() + await this.setupGame() + await this.racingGame.play() + this.viewer.displayWinners(this.racingGame) } async setupGame() { @@ -31,14 +38,4 @@ export default class RacingGameController { const maxRound = await this.viewer.readRoundCount() this.racingGame.setMaxRound(Number(maxRound)) } - - async startGame() { - this.viewer.displayGameStart() - await this.setupGame() - await this.racingGame.play() - - this.viewer.displayWinners(this.racingGame) - } } - - diff --git a/src/domains/view/RacingGameViewer.js b/src/domains/view/RacingGameViewer.js index 314a348..86167ca 100644 --- a/src/domains/view/RacingGameViewer.js +++ b/src/domains/view/RacingGameViewer.js @@ -12,58 +12,44 @@ export default class RacingGameViewer { }) } - displayStartRound({ currentRound }) { - this.printer.printWithTemplate('roundStart', [currentRound]) - } - - displayRoundResult({ results, maxPosition, currentRound }) { - const { positions } = results[currentRound - 1] - const prevPositions = currentRound > 1 ? results[currentRound - 2].positions : {} - - Object.entries(positions).forEach(([name, position]) => { - const positionString = this.formatPositionString(position, maxPosition) - const positionDiffArgs = this.getPositionDiffArgs(position, prevPositions[name] || 0) - - this.printer.printWithTemplate( - 'carPosition', - [name.padEnd(6, ' '), positionString, ...positionDiffArgs], - ) - }) + displayGameStart() { + this.printer.print('🚕 레이싱 게임을 시작합니다 🚗') + this.printer.print('각 라운드마다 랜덤 미니 게임을 진행하여 이동할 수 있습니다.') this.printer.lineBreak() } - formatPositionString(position, maxPosition) { - return '⛳️'.padStart((position + 1) * 2, '__').padEnd((maxPosition + 1) * 2, '__') + displayStartRound({ currentRound }) { + this.printer.printWithTemplate('roundStart', [currentRound]) } - getPositionDiffArgs(position, prevPosition) { - const positionDiff = position - prevPosition - return [positionDiff < 0 ? '' : '+', positionDiff, position] + displayMiniGameStart(playerName) { + this.printer.lineBreak() + this.printer.printWithTemplate('miniGameStart', [playerName]) } displayGameLogs({ results }) { const { gameLogs } = results.at(-1) this.printer.lineBreak() - Object.entries(gameLogs).forEach(([name, log]) => { - this.printer.printWithTemplate('gameLog', [ - name.padEnd(6, ' '), - ...Object.values(log), - ]) + this.printer.printWithTemplate('gameLog', [name.padEnd(5, ' '), ...Object.values(log)]) }) - this.printer.lineBreak() } - displayGameStart() { - this.printer.print('🚕 레이싱 게임을 시작합니다 🚗') - this.printer.print('각 라운드마다 랜덤 미니 게임을 진행하여 이동할 수 있습니다.') + displayRoundResult({ results, maxPosition, currentRound }) { + const { positions } = results[currentRound - 1] + const prevPositions = currentRound > 1 ? results[currentRound - 2].positions : {} + Object.entries(positions).forEach(([name, position]) => { + const positionString = this.formatPositionString(position, maxPosition) + const positionDiffArgs = this.getPositionDiffArgs(position, prevPositions[name] || 0) + this.printer.printWithTemplate('carPosition', [name.padEnd(5, ' '), positionString, ...positionDiffArgs]) + }) this.printer.lineBreak() } - displayMiniGameStart(playerName) { + displayWinners({ winners }) { + this.printer.printWithTemplate('winner', [winners.join(', ')]) this.printer.lineBreak() - this.printer.printWithTemplate('miniGameStart', [playerName]) } async readPlayerCarNames() { @@ -78,8 +64,12 @@ export default class RacingGameViewer { return await readInput('몇 라운드 플레이할 지 알려주세요.\n') } - displayWinners({ winners }) { - this.printer.printWithTemplate('winner', [winners.join(', ')]) - this.printer.lineBreak() + formatPositionString(position, maxPosition) { + return '⛳️'.padStart((position + 1) * 2, '__').padEnd((maxPosition + 1) * 2, '__') + } + + getPositionDiffArgs(position, prevPosition) { + const positionDiff = position - prevPosition + return [positionDiff < 0 ? '' : '+', positionDiff, position] } } From 0bbf8c4320eab34e9d8599a684a7926a642fba5e Mon Sep 17 00:00:00 2001 From: chacha Date: Fri, 5 Jul 2024 09:54:56 +0900 Subject: [PATCH 09/14] =?UTF-8?q?feat:=20validation=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EB=B3=B4=EC=99=84=20=EB=B0=8F=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/RacingGame.js | 28 +++++++++++++---------- src/domains/RacingGameController.js | 32 +++++++++++++++++++-------- src/domains/miniGames/DiceDiffGame.js | 5 ++--- src/domains/view/RacingGameViewer.js | 13 +++++++++-- src/service/Printer.js | 8 ++++--- src/validations/createValidator.js | 14 ++++++++++++ src/validations/miniGames.js | 2 +- src/validations/racing.js | 23 +++++++++++++++++-- 8 files changed, 93 insertions(+), 32 deletions(-) create mode 100644 src/validations/createValidator.js diff --git a/src/domains/RacingGame.js b/src/domains/RacingGame.js index f11cf8f..9bb4e43 100644 --- a/src/domains/RacingGame.js +++ b/src/domains/RacingGame.js @@ -1,11 +1,13 @@ import Car from './Car.js' import Game from './Game.js' +import createValidator from "../validations/createValidator.js"; +import {racingValidations} from "../validations/racing.js"; export default class RacingGame extends Game { constructor({ miniGames }) { super({ miniGames }) - this.cars = [] - this.players = [] + this.validate = createValidator(racingValidations) + this.validate(this.miniGames, ['miniGameInterface', 'miniGameSize']) } get maxPosition() { @@ -19,17 +21,20 @@ export default class RacingGame extends Game { setMaxRound(maxRound) { this.maxRound = maxRound + this.validate(this.maxRound, ['maxRoundNumber', 'maxRoundRange']) } - addPlayer(name) { - const newPlayerCar = new Car(name) - this.players.push(newPlayerCar.name) - this.cars.push(newPlayerCar) - } - - addCar(name) { - const newCar = new Car(name) - this.cars.push(newCar) + setCars(playerNames, botNames) { + this.cars = [] + this.players = []; + [...playerNames, ...botNames].forEach(name => { + const newCar = new Car(name); + this.cars.push(newCar); + if (playerNames.includes(name)) { + this.players.push(newCar.name); + } + }); + this.validate(this.cars,['leastCarCount', 'uniqueCarName']) } async doRound() { @@ -75,7 +80,6 @@ export default class RacingGame extends Game { this.emitEvent('miniGameStart', car.name) return await miniGame.PvC(car.name) } - return miniGame.CvC(car.name) } } diff --git a/src/domains/RacingGameController.js b/src/domains/RacingGameController.js index 6718409..60be217 100644 --- a/src/domains/RacingGameController.js +++ b/src/domains/RacingGameController.js @@ -23,19 +23,33 @@ export default class RacingGameController { async startGame() { this.viewer.displayGameStart() - await this.setupGame() + await this.setupNames() + await this.setupMaxRound() await this.racingGame.play() this.viewer.displayWinners(this.racingGame) } - async setupGame() { - const playerNames = await this.viewer.readPlayerCarNames() - playerNames.split(',').forEach(name => this.racingGame.addPlayer(name)) - - const carNames = await this.viewer.readCarNames() - carNames.split(',').forEach(name => this.racingGame.addCar(name)) + async setupNames() { + try { + const playerNames = await this.viewer.readPlayerCarNames() + const botNames = await this.viewer.readBotCarNames() + this.racingGame.setCars( + playerNames.split(',').filter(Boolean), + botNames.split(',').filter(Boolean), + ) + } catch (error) { + this.viewer.displayError(error) + await this.setupNames() + } + } - const maxRound = await this.viewer.readRoundCount() - this.racingGame.setMaxRound(Number(maxRound)) + async setupMaxRound() { + try { + const maxRound = await this.viewer.readRoundCount() + this.racingGame.setMaxRound(Number(maxRound)) + }catch (error) { + this.viewer.displayError(error) + await this.setupMaxRound() + } } } diff --git a/src/domains/miniGames/DiceDiffGame.js b/src/domains/miniGames/DiceDiffGame.js index d356e51..0517f7d 100644 --- a/src/domains/miniGames/DiceDiffGame.js +++ b/src/domains/miniGames/DiceDiffGame.js @@ -7,7 +7,7 @@ export default class DiceDiffGame { } static getPlayerResult(playerName, playerAnswer, opponentAnswer) { - const diff = opponentAnswer - playerAnswer + const diff = playerAnswer - opponentAnswer return { score: diff, log: { @@ -25,7 +25,6 @@ export default class DiceDiffGame { static async PvC(playerName) { await readInput('주사위 던지기 게임 : Enter 입력 시 주사위를 던지고, 그 차이만큼 이동합니다.') const answer = this.diceNumber - console.log(`${answer}이 나왔습니다.`) return this.getPlayerResult(`*${playerName}`, answer, this.diceNumber) } -} \ No newline at end of file +} diff --git a/src/domains/view/RacingGameViewer.js b/src/domains/view/RacingGameViewer.js index 86167ca..3d4ef04 100644 --- a/src/domains/view/RacingGameViewer.js +++ b/src/domains/view/RacingGameViewer.js @@ -4,11 +4,13 @@ import readInput from '../../utils/readInput.js' export default class RacingGameViewer { constructor() { this.printer = new ConsolePrinter({ - roundStart: '------------------------⭐ Round%{1}⭐️️------------------------', + roundStart: '--------------------------⭐ Round%{1}⭐️️--------------------------', carPosition: '%{1} : %{2} (%{3}%{4} ➡ %{5})', gameLog: '%{1} : %{2} VS computer : %{3} ➡➡ %{4}', miniGameStart: '>> player %{1} Turn!', winner: '최종 우승자는 👑 %{1} 입니다. 축하합니다!', + error: '⚠️ %{1}', + divider: '---------------------------------------------------------------' }) } @@ -52,11 +54,18 @@ export default class RacingGameViewer { this.printer.lineBreak() } + displayError(errorMessage) { + this.printer.printWithTemplate('divider') + this.printer.printWithTemplate('error', [errorMessage]) + this.printer.printWithTemplate('divider') + this.printer.lineBreak() + } + async readPlayerCarNames() { return await readInput('직접 레이싱에 참여할 자동차들의 이름을 입력해주세요. (쉼표로 구분)\n') } - async readCarNames() { + async readBotCarNames() { return await readInput('봇으로 참여할 자동차들의 이름을 입력해주세요. (쉼표로 구분)\n') } diff --git a/src/service/Printer.js b/src/service/Printer.js index 7ac1ba0..0e00b24 100644 --- a/src/service/Printer.js +++ b/src/service/Printer.js @@ -5,9 +5,11 @@ export default class ConsolePrinter { format(templateKey, messages) { let result = this.template[templateKey] - messages.forEach((message, index) => { - result = result.replaceAll(`%{${index + 1}}`, message) - }) + if (messages && messages.length > 0) { + messages.forEach((message, index) => { + result = result.replaceAll(`%{${index + 1}}`, message) + }) + } return result } diff --git a/src/validations/createValidator.js b/src/validations/createValidator.js new file mode 100644 index 0000000..cf525d9 --- /dev/null +++ b/src/validations/createValidator.js @@ -0,0 +1,14 @@ +const createValidator = (validations) => { + return (target, validationKeys) => { + validationKeys.forEach((key) => { + if (!validations.hasOwnProperty(key)) { + throw new Error('올바른 검사 키가 아닙니다.') + } + if (!validations[key].check(target)) { + throw new Error(validations[key].errorMessage) + } + }) + } +} + +export default createValidator diff --git a/src/validations/miniGames.js b/src/validations/miniGames.js index de4050e..c4ade31 100644 --- a/src/validations/miniGames.js +++ b/src/validations/miniGames.js @@ -10,4 +10,4 @@ export const guessRandomNumberValidations = [ check: (input) => Number(input) >= 1 && Number(input) <= 10 && Number.isInteger(Number(input)), errorMessage: '1 부터 10 까지의 자연수만 가능합니다.', }, -] \ No newline at end of file +] diff --git a/src/validations/racing.js b/src/validations/racing.js index 2185e6a..8ad0f89 100644 --- a/src/validations/racing.js +++ b/src/validations/racing.js @@ -1,10 +1,29 @@ export const racingValidations = { uniqueCarName: { - check: (carNames) => new Set(carNames).size === carNames.length, + check: (cars) =>{ + const carNames = cars.map(({ name }) => name) + return new Set(carNames).size === carNames.length + }, errorMessage: '경주할 자동차 이름은 각각 달라야 합니다.' }, leastCarCount: { - check: (carNames) => carNames.length > 1, + check: (cars) => cars.length > 1, errorMessage: '최소 2대의 자동차가 참가해야 합니다.' + }, + maxRoundNumber: { + check: (maxRound) => Number.isInteger(maxRound), + errorMessage: '최대 라운드 값은 숫자여야 합니다.' + }, + maxRoundRange: { + check: (maxRound) => 1 <= maxRound && maxRound <= 5, + errorMessage: '최소 1라운드부터 최대 5라운드 동안 플레이할 수 있습니다.' + }, + miniGameInterface: { + check: (miniGames) => Object.values(miniGames).every(game => game.hasOwnProperty('CvC') && game.hasOwnProperty('PvC')), + errorMessage: '올바른 미니게임이 아닙니다. 플레이 기능이 존재하지 않습니다.' + }, + miniGameSize: { + check: (miniGames) => Object.values(miniGames).length > 0, + errorMessage: '최대 1개 이상의 미니게임이 있어야 합니다.' } } From ef8b6d368d176d11073f1419ad22b0a62a18fd5c Mon Sep 17 00:00:00 2001 From: chacha Date: Fri, 5 Jul 2024 15:26:33 +0900 Subject: [PATCH 10/14] =?UTF-8?q?feat:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/car.test.js | 48 +++--- src/__tests__/game.test.js | 123 -------------- src/__tests__/racingGame.test.js | 154 ++++++++---------- src/__tests__/racingGameController.test.js | 39 +++++ src/domains/Game.js | 2 +- src/domains/miniGames/index.js | 4 + src/domains/{ => racingGame}/RacingGame.js | 9 +- .../{ => racingGame}/RacingGameController.js | 2 +- .../{view => racingGame}/RacingGameViewer.js | 4 +- src/domains/racingGame/index.js | 3 + src/main.js | 33 ++-- src/validations/index.js | 5 + src/validations/racing.js | 12 ++ 13 files changed, 171 insertions(+), 267 deletions(-) delete mode 100644 src/__tests__/game.test.js create mode 100644 src/__tests__/racingGameController.test.js create mode 100644 src/domains/miniGames/index.js rename src/domains/{ => racingGame}/RacingGame.js (91%) rename src/domains/{ => racingGame}/RacingGameController.js (97%) rename src/domains/{view => racingGame}/RacingGameViewer.js (97%) create mode 100644 src/domains/racingGame/index.js create mode 100644 src/validations/index.js diff --git a/src/__tests__/car.test.js b/src/__tests__/car.test.js index f7f9b94..b9dba97 100644 --- a/src/__tests__/car.test.js +++ b/src/__tests__/car.test.js @@ -2,35 +2,25 @@ import { describe, test, expect } from 'vitest' import Car from '../domains/Car.js' import { carValidations } from '../validations/car.js' -const spacedCarNameCases = [' ab', 'a b', ' ab ', 'a b '] -const invalidCarNameCases = ['', 'abcdef', ' '] +describe('Car', () => { + test('올바른 이름과 초기 위치(0)으로 자동차를 초기화한다.', () => { + const car = new Car('테스트') + expect(car.name).toBe('테스트') + expect(car.position).toBe(0) + }) -describe('Car Test', () => { - test.each(invalidCarNameCases)( - '자동차 이름은 공백 제외 1자 이상 5자 이하여야 한다.', - name => { - expect(() => new Car(name)).toThrow( - carValidations.carNameLength.errorMessage - ) - } - ) + test('자동차를 올바르게 이동시킨다.', () => { + const car = new Car('테스트') + car.move(3) + expect(car.position).toBe(3) + car.move(-1) + expect(car.position).toBe(2) + car.move(-3) + expect(car.position).toBe(0) + }) - test.each(spacedCarNameCases)( - '생성된 자동차 이름에는 공백이 없어야 한다.', - name => { - const car = new Car(name) - expect(car.name).toBe('ab') - } - ) - - test('생성된 자동차의 위치는 0으로 초기화되어야 한다.', () => { - const car = new Car('car') - expect(car.position).toBe(0) - }) - - test('move 시 자동차의 위치가 1 증가해야 한다.', () => { - const car = new Car('car') - car.move() - expect(car.position).toBe(1) - }) + test('유효하지 않은 이름에 대해 오류를 발생시킨다.', () => { + expect(() => new Car(' ')).toThrow(carValidations.carNameLength.errorMessage) + expect(() => new Car('기이이인이름')).toThrow(carValidations.carNameLength.errorMessage) + }) }) diff --git a/src/__tests__/game.test.js b/src/__tests__/game.test.js deleted file mode 100644 index 2e63fc6..0000000 --- a/src/__tests__/game.test.js +++ /dev/null @@ -1,123 +0,0 @@ -import { describe, test, expect, beforeEach, vi } from 'vitest' -import Game from '../domains/Game.js' - -const testMaxRound = 4 - -describe('Game Test', () => { - let display - let game - - beforeEach(() => { - display = { - print: vi.fn(), - printError: vi.fn(), - } - - game = new Game({ - display, - maxRound: testMaxRound, - config: {}, - }) - }) - - test('eachRound 메소드가 rounds 에 올바르게 바인딩되어야 한다.', () => { - // eachRound 모킹 - game.eachRound = vi.fn() - - // rounds 배열에 바인딩 - game.bindRounds() - - // 설정한 라운드 수 만큼 배열에 들어갔는지 + 실행되었는지 확인 - expect(game.rounds.length).toBe(testMaxRound) - game.rounds.forEach(round => { - round() - }) - expect(game.eachRound).toHaveBeenCalledTimes(testMaxRound) - }) - - test('round(..) 메소드가 rounds 에 올바르게 바인딩되어야 한다.', () => { - // mock 함수 생성 - const play1 = vi.fn() - const play2 = vi.fn() - const notPlay = vi.fn() - - // round 함수 방식의 게임 생성 - class ExtendedGame extends Game { - round1() { - play1() - } - round2() { - play2() - } - anyMethod() { - notPlay() - } - } - const extendedGame = new ExtendedGame({ - display, - maxRound: 2, - config: {}, - }) - - // rounds 배열 에 바인딩 - extendedGame.bindRounds() - - // rounds 배열에 [round1(), round2()] 있는지 비교 - expect(extendedGame.rounds.length).toBe(2) - - extendedGame.rounds.forEach(round => { - round() - }) - - // round1,2 함수 안에서 실행했던 mock 함수의 호출 확인 (round1,2 함수가 제대로 실행되었는가) - expect(play1).toHaveBeenCalled() - expect(play2).toHaveBeenCalled() - expect(notPlay).not.toHaveBeenCalled() - }) - - test('play 시 setup, eachRound, finish 가 순서에 맞게 동작해야 한다.', async () => { - // setup, eachRound, finish 모킹 - game.setup = vi.fn() - game.eachRound = vi.fn() - game.finish = vi.fn() - - game.bindRounds() - await game.play() - - // setup, eachRound, finish 호출 확인 - expect(game.setup).toHaveBeenCalled() - expect(game.eachRound).toHaveBeenCalledTimes(testMaxRound) - expect(game.finish).toHaveBeenCalled() - - // currentRound 변경 확인 - expect(game.currentRound).toBe(testMaxRound) - - // setup - eachRound - finish 순서로 호출되었는지 확인 - expect(game.setup.mock.invocationCallOrder[0]).toBeLessThan( - game.eachRound.mock.invocationCallOrder[0] - ) - expect(game.finish.mock.invocationCallOrder[0]).toBeGreaterThan( - game.eachRound.mock.invocationCallOrder[0] - ) - }) - - test('setup 도중 에러가 발생하면 다시 play 한다.', async () => { - const setUpError = new Error('setup error') - - // setup 모킹 / 첫번째 호출 시에는 에러, 두번째 호출 시에는 성공 - game.setup = vi - .fn() - .mockImplementationOnce(() => { - throw setUpError - }) - .mockImplementationOnce(() => Promise.resolve()) - - game.finish = vi.fn() - await game.play() - - // 에러 출력 여부 확인 / 다시 play 되었는지 확인 - expect(display.printError).toHaveBeenCalledWith(setUpError) - expect(game.setup).toHaveBeenCalledTimes(2) - expect(game.finish).toHaveBeenCalledTimes(1) - }) -}) diff --git a/src/__tests__/racingGame.test.js b/src/__tests__/racingGame.test.js index 0d6f56b..ce5bc31 100644 --- a/src/__tests__/racingGame.test.js +++ b/src/__tests__/racingGame.test.js @@ -1,116 +1,96 @@ import { describe, test, expect, beforeEach, vi } from 'vitest' -import RacingGame from '../domains/RacingGame.js' -import Car from '../domains/Car.js' -import * as randomNumberModule from '../utils/getRandomNumber.js' +import RacingGame from '../domains/racingGame/RacingGame.js' import { racingValidations } from '../validations/racing.js' -const testRacingConfig = { - min: 0, - max: 100, - threshold: 50, +const mockMiniGame = { + PvC: vi.fn().mockResolvedValue({ score: 1, log: { player: 1, computer: 0, result: 'win' } }), + CvC: vi.fn().mockResolvedValue({ score: 2, log: { player: 1, computer: 0, result: 'win' } }), } -describe('RacingGame Test', () => { - let display - let racingGame + +describe('RacingGame', () => { + let game; beforeEach(() => { - display = { - print: vi.fn(), - printError: vi.fn(), - read: vi.fn(), - } + game = new RacingGame({ miniGames: { MockGame: mockMiniGame } }) + }) - racingGame = new RacingGame({ - display: display, - maxRound: 3, - config: testRacingConfig, - }) + + test('최대 라운드를 설정할 수 있다.', () => { + game.setMaxRound(3) + expect(game.maxRound).toBe(3) }) - // 출력된 문자열을 확인하기 위해 display 호출의 매개변수를 가져오는 함수 - const getDisplayText = type => { - return display[type === 'error' ? 'printError' : 'print'].mock.calls.map( - call => call[0] - )[0] - } - test('게임에 참여하는 자동차 이름은 각각 달라야 한다.', async () => { - // display.read 에서 겹치는 자동차 입력값을 읽어온 것으로 모킹 - display.read.mockResolvedValue('car1,car2,car1') + test('자동차를 생성할 수 있다.', () => { + game.setCars(['자동차1'], ['자동차2']) + expect(game.cars).toHaveLength(2) + expect(game.cars[0].name).toBe('자동차1') + expect(game.cars[1].name).toBe('자동차2') + }) - // matcher 가 1번은 실행되야 함 (toBe 메소드가 실행되어야 함 = 에러 발생해야 함) - expect.assertions(1) - try { - await racingGame.setup() - } catch (error) { - // 에러 메시지 확인 - expect(error.message).toBe(racingValidations.uniqueCarName.errorMessage) + + test('유효하지 않은 자동차에 대해 오류를 발생시킨다.', () => { + expect(() => game.setCars(['자동차1', '자동차1'], [])).toThrow(racingValidations.uniqueCarName.errorMessage) + expect(() => game.setCars([], [])).toThrow(racingValidations.leastCarCount.errorMessage) + }) + + + test('유효하지 않은 최대 라운드 값에 대해 오류를 발생시킨다.', () => { + expect(() => game.setMaxRound('문자열')).toThrow(racingValidations.maxRoundNumber.errorMessage) + expect(() => game.setMaxRound(6)).toThrow(racingValidations.maxRoundRange.errorMessage) + }) + + test('유효하지 않은 미니게임 함수에 대해 오류를 발생시킨다.', () => { + const invalidMiniGame = { + CvC: vi.fn().mockResolvedValue({ score: 1, log: { player: 1, computer: 0 } }), } + expect(() => new RacingGame({ miniGames: { MockGame: invalidMiniGame } })).toThrow(racingValidations.miniGameInterface.errorMessage) }) - test('게임에는 최소 2대의 자동차가 참여해야 한다.', async () => { - // display.read 에서 1대 자동차를 읽어온 것으로 모킹 - display.read.mockResolvedValue('car1') + test('유효하지 않은 미니게임 결과에 대해 오류를 발생시킨다.', async () => { + const invalidMiniGame = { + PvC: vi.fn().mockResolvedValue({score: 1, log: {hello: 'hello'}}), + CvC: vi.fn().mockReturnValue({score: 1, win: true}), + } + const racingGame = new RacingGame({ + miniGames: {MockGame: invalidMiniGame}, + }) + racingGame.setCars([], ['자동차1', '자동차2']) - expect.assertions(1) try { - await racingGame.setup() - } catch (error) { - expect(error.message).toBe(racingValidations.leastCarCount.errorMessage) + await racingGame.doRound() + }catch(error){ + expect(error.message).toBe(racingValidations.miniGameResult.errorMessage) } }) - const testCarMovement = move => { - racingGame.carNames = ['car1'] - racingGame.cars = { car1: new Car('car1') } - - // getRandomNumber 에서 설정한 가짜 값을 반환하도록 spyOn 호출 - const mockGetRandomNumber = vi.spyOn(randomNumberModule, 'getRandomNumber') - - // 기준치 초과, 또는 기준치 이하를 반환하도록 하여 테스트 - mockGetRandomNumber.mockReturnValue( - move ? testRacingConfig.threshold + 1 : testRacingConfig.threshold - ) - // move 함수 모킹 - racingGame.cars['car1'].move = vi.fn() - racingGame.eachRound() - - if (move) expect(racingGame.cars['car1'].move).toBeCalled() // '전진한다' 테스트 - else expect(racingGame.cars['car1'].move).not.toBeCalled() // '움직이지 않는다' 테스트 - mockGetRandomNumber.mockRestore() - } - - test('자동차는 라운드에서 랜덤 숫자가 설정값보다 크면 전진한다.', () => { - // getRandomNumber 가 기준치 초과해서 반환했을 때의 테스트 작동 - testCarMovement(true) - }) + test('우승자를 올바르게 계산할 수 있다.', async () => { + game.setCars(['자동차1'], ['자동차2']) + await game.doRound() - test('자동차는 라운드에서 랜덤 숫자가 설정값보다 작거나 같으면 움직이지 않는다', () => { - // getRandomNumber 가 기준치 이하을 반환했을 때의 테스트 작동 - testCarMovement(false) + expect(game.winners).toEqual(['자동차1', '자동차2']) }) - test('가장 많이 움직인 자동차들이 게임에서 승리한다', () => { - racingGame.carNames = ['car1', 'car2', 'car3'] - racingGame.cars = { - car1: new Car('car1'), - car2: new Car('car2'), - car3: new Car('car3'), - } + test('점수를 올바르게 저장하고 계산할 수 있다.', async () => { + game.setCars(['자동차1'], ['자동차2']) + + await game.doRound() + await game.doRound() - // 1,3 번 자동차만 이동 - racingGame.cars['car1'].move() - racingGame.cars['car3'].move() + expect(game.lastResult.positions).toEqual({ + '자동차1': 2, + '자동차2': 4 + }) + expect(game.maxPosition).toBe(4) + }) - // winner 확인 - expect(racingGame.winners).toEqual(['car1', 'car3']) + test('라운드 종료 후 이벤트가 발생한다..', async () => { + const roundEndSpy = vi.spyOn(game, 'emitEvent') - racingGame.finish() - const winnerMessage = getDisplayText() + game.setCars(['자동차1'], ['자동차2']) + await game.doRound() - // 우승자 출력 확인 - expect(winnerMessage).toContain('car1') - expect(winnerMessage).toContain('car3') + expect(roundEndSpy).toHaveBeenCalledWith('roundEnd') }) }) diff --git a/src/__tests__/racingGameController.test.js b/src/__tests__/racingGameController.test.js new file mode 100644 index 0000000..90f764f --- /dev/null +++ b/src/__tests__/racingGameController.test.js @@ -0,0 +1,39 @@ +import { describe, test, expect, vi, beforeEach } from 'vitest' + +import { RacingGame, RacingGameController } from "../domains/racingGame/index.js"; + +const mockMiniGame = { + PvC: null, + CvC: null +} + +const mockViewer = { + readPlayerCarNames: vi.fn().mockResolvedValue('자동차1,자동차2'), + readBotCarNames: vi.fn().mockResolvedValue('봇1,봇2,봇3'), + readRoundCount: vi.fn().mockResolvedValue('3') +} + +describe('RacingGameController', () => { + let racingGame, viewer, controller + + beforeEach(() => { + racingGame = new RacingGame({ miniGames: { MockGame: mockMiniGame } }) + viewer = mockViewer + controller = new RacingGameController({ racingGame, viewer }) + }) + + + test('플레이어와 봇의 이름을 설정한다.', async () => { + await controller.setupNames() + + expect(racingGame.players).toEqual(['자동차1', '자동차2']) + expect(racingGame.cars.length).toBe(5) + }) + + test('최대 라운드를 설정한다.', async () => { + vi.spyOn(racingGame, 'setMaxRound') + await controller.setupMaxRound() + + expect(racingGame.maxRound).toBe(3) + }) +}) diff --git a/src/domains/Game.js b/src/domains/Game.js index 28f69be..2eb4233 100644 --- a/src/domains/Game.js +++ b/src/domains/Game.js @@ -53,4 +53,4 @@ export default class Game { this.currentRound += 1 } } -} \ No newline at end of file +} diff --git a/src/domains/miniGames/index.js b/src/domains/miniGames/index.js new file mode 100644 index 0000000..91b3c0a --- /dev/null +++ b/src/domains/miniGames/index.js @@ -0,0 +1,4 @@ +export { default as DiceDiffGame } from './DiceDiffGame.js' +export { default as GuessRandomNumber } from './GuessRandomNumber.js' +export { default as GuessTimeOut } from './GuessTimeOut.js' +export { default as RockPaperScissors } from './RockPaperScissors.js' diff --git a/src/domains/RacingGame.js b/src/domains/racingGame/RacingGame.js similarity index 91% rename from src/domains/RacingGame.js rename to src/domains/racingGame/RacingGame.js index 9bb4e43..901431e 100644 --- a/src/domains/RacingGame.js +++ b/src/domains/racingGame/RacingGame.js @@ -1,7 +1,6 @@ -import Car from './Car.js' -import Game from './Game.js' -import createValidator from "../validations/createValidator.js"; -import {racingValidations} from "../validations/racing.js"; +import Car from '../Car.js' +import Game from '../Game.js' +import { racingValidations, createValidator } from "../../validations"; export default class RacingGame extends Game { constructor({ miniGames }) { @@ -64,6 +63,8 @@ export default class RacingGame extends Game { async race(car) { const miniGameResult = await this.doMiniGame(car) + this.validate(miniGameResult, ['miniGameResult']) + if (miniGameResult.hasOwnProperty('score')) { car.move(miniGameResult.score) } diff --git a/src/domains/RacingGameController.js b/src/domains/racingGame/RacingGameController.js similarity index 97% rename from src/domains/RacingGameController.js rename to src/domains/racingGame/RacingGameController.js index 60be217..e10cdf0 100644 --- a/src/domains/RacingGameController.js +++ b/src/domains/racingGame/RacingGameController.js @@ -1,5 +1,5 @@ export default class RacingGameController { - constructor(racingGame, viewer) { + constructor({ racingGame, viewer }) { this.racingGame = racingGame this.viewer = viewer diff --git a/src/domains/view/RacingGameViewer.js b/src/domains/racingGame/RacingGameViewer.js similarity index 97% rename from src/domains/view/RacingGameViewer.js rename to src/domains/racingGame/RacingGameViewer.js index 3d4ef04..384944e 100644 --- a/src/domains/view/RacingGameViewer.js +++ b/src/domains/racingGame/RacingGameViewer.js @@ -29,8 +29,8 @@ export default class RacingGameViewer { this.printer.printWithTemplate('miniGameStart', [playerName]) } - displayGameLogs({ results }) { - const { gameLogs } = results.at(-1) + displayGameLogs({ lastResult }) { + const { gameLogs } = lastResult this.printer.lineBreak() Object.entries(gameLogs).forEach(([name, log]) => { this.printer.printWithTemplate('gameLog', [name.padEnd(5, ' '), ...Object.values(log)]) diff --git a/src/domains/racingGame/index.js b/src/domains/racingGame/index.js new file mode 100644 index 0000000..5eebeff --- /dev/null +++ b/src/domains/racingGame/index.js @@ -0,0 +1,3 @@ +export { default as RacingGame } from './RacingGame' +export { default as RacingGameController } from './RacingGameController' +export { default as RacingGameViewer } from './RacingGameViewer' diff --git a/src/main.js b/src/main.js index 624807c..014b706 100644 --- a/src/main.js +++ b/src/main.js @@ -1,25 +1,18 @@ -import RacingGame from './domains/RacingGame.js' -import GuessTimeOut from './domains/miniGames/GuessTimeOut.js' -import GuessRandomNumber from './domains/miniGames/GuessRandomNumber.js' -import RockPaperScissors from './domains/miniGames/RockPaperScissors.js' -import DiceDiffGame from './domains/miniGames/DiceDiffGame.js' -import RacingGameController from './domains/RacingGameController.js' -import RacingGameViewer from './domains/view/RacingGameViewer.js' +import { RacingGame, RacingGameViewer, RacingGameController } from './domains/racingGame' +import { GuessTimeOut, GuessRandomNumber, RockPaperScissors, DiceDiffGame } from './domains/miniGames' async function main() { - const racingGame = new RacingGame({ - miniGames: { - RockPaperScissors, - GuessRandomNumber, - GuessTimeOut, - DiceDiffGame, - }, - }) - - const view = new RacingGameViewer() - const controller = new RacingGameController(racingGame, view) - - await controller.startGame() + await new RacingGameController({ + racingGame: new RacingGame({ + miniGames: { + RockPaperScissors, + GuessRandomNumber, + GuessTimeOut, + DiceDiffGame, + }, + }), + viewer: new RacingGameViewer() + }).startGame() } main() diff --git a/src/validations/index.js b/src/validations/index.js new file mode 100644 index 0000000..cc9fab5 --- /dev/null +++ b/src/validations/index.js @@ -0,0 +1,5 @@ +export * from './car' +export * from './miniGames.js' +export * from './racing.js' + +export { default as createValidator } from './createValidator' diff --git a/src/validations/racing.js b/src/validations/racing.js index 8ad0f89..a562d7b 100644 --- a/src/validations/racing.js +++ b/src/validations/racing.js @@ -25,5 +25,17 @@ export const racingValidations = { miniGameSize: { check: (miniGames) => Object.values(miniGames).length > 0, errorMessage: '최대 1개 이상의 미니게임이 있어야 합니다.' + }, + miniGameResult: { + check: (miniGameResult) => { + const isResultCorrect = ( + miniGameResult.hasOwnProperty('score') || + miniGameResult.hasOwnProperty('win') + ) && miniGameResult.hasOwnProperty('log') + + const { log } = miniGameResult + return isResultCorrect && log.hasOwnProperty('player') && log.hasOwnProperty('computer') && log.hasOwnProperty('result') + }, + errorMessage: '올바른 미니게임 결과가 아닙니다.' } } From 3db245ea3144b1fc6a29094ceb71a500e358161c Mon Sep 17 00:00:00 2001 From: chacha Date: Fri, 5 Jul 2024 15:44:26 +0900 Subject: [PATCH 11/14] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EA=B3=B5=EB=B0=B1=20=EB=B0=8F=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/car.test.js | 48 ++++---- src/__tests__/racingGame.test.js | 109 +++++++++--------- src/__tests__/racingGameController.test.js | 56 ++++----- src/domains/Car.js | 20 ++-- src/domains/Game.js | 46 ++++---- src/domains/miniGames/DiceDiffGame.js | 20 ++-- src/domains/miniGames/GuessRandomNumber.js | 31 +++-- src/domains/miniGames/GuessTimeOut.js | 33 +++--- src/domains/miniGames/RockPaperScissors.js | 48 +++++--- src/domains/miniGames/index.js | 8 +- src/domains/racingGame/RacingGame.js | 73 ++++++------ .../racingGame/RacingGameController.js | 54 ++++----- src/domains/racingGame/RacingGameViewer.js | 91 +++++++++------ src/domains/racingGame/index.js | 6 +- src/main.js | 19 ++- src/service/Printer.js | 20 ++-- src/utils/createReadline.js | 16 +-- src/utils/getRandomNumber.js | 4 +- src/utils/readInput.js | 40 +++---- src/validations/car.js | 10 +- src/validations/createValidator.js | 26 ++--- src/validations/index.js | 8 +- src/validations/miniGames.js | 11 +- src/validations/racing.js | 85 ++++++++------ 24 files changed, 476 insertions(+), 406 deletions(-) diff --git a/src/__tests__/car.test.js b/src/__tests__/car.test.js index b9dba97..dc8efd4 100644 --- a/src/__tests__/car.test.js +++ b/src/__tests__/car.test.js @@ -1,26 +1,30 @@ -import { describe, test, expect } from 'vitest' -import Car from '../domains/Car.js' -import { carValidations } from '../validations/car.js' +import { describe, test, expect } from 'vitest'; +import Car from '../domains/Car.js'; +import { carValidations } from '../validations/car.js'; describe('Car', () => { - test('올바른 이름과 초기 위치(0)으로 자동차를 초기화한다.', () => { - const car = new Car('테스트') - expect(car.name).toBe('테스트') - expect(car.position).toBe(0) - }) + test('올바른 이름과 초기 위치(0)으로 자동차를 초기화한다.', () => { + const car = new Car('테스트'); + expect(car.name).toBe('테스트'); + expect(car.position).toBe(0); + }); - test('자동차를 올바르게 이동시킨다.', () => { - const car = new Car('테스트') - car.move(3) - expect(car.position).toBe(3) - car.move(-1) - expect(car.position).toBe(2) - car.move(-3) - expect(car.position).toBe(0) - }) + test('자동차를 올바르게 이동시킨다.', () => { + const car = new Car('테스트'); + car.move(3); + expect(car.position).toBe(3); + car.move(-1); + expect(car.position).toBe(2); + car.move(-3); + expect(car.position).toBe(0); + }); - test('유효하지 않은 이름에 대해 오류를 발생시킨다.', () => { - expect(() => new Car(' ')).toThrow(carValidations.carNameLength.errorMessage) - expect(() => new Car('기이이인이름')).toThrow(carValidations.carNameLength.errorMessage) - }) -}) + test('유효하지 않은 이름에 대해 오류를 발생시킨다.', () => { + expect(() => new Car(' ')).toThrow( + carValidations.carNameLength.errorMessage, + ); + expect(() => new Car('기이이인이름')).toThrow( + carValidations.carNameLength.errorMessage, + ); + }); +}); diff --git a/src/__tests__/racingGame.test.js b/src/__tests__/racingGame.test.js index ce5bc31..fc74442 100644 --- a/src/__tests__/racingGame.test.js +++ b/src/__tests__/racingGame.test.js @@ -1,96 +1,93 @@ -import { describe, test, expect, beforeEach, vi } from 'vitest' -import RacingGame from '../domains/racingGame/RacingGame.js' -import { racingValidations } from '../validations/racing.js' +import { describe, test, expect, beforeEach, vi } from 'vitest'; +import RacingGame from '../domains/racingGame/RacingGame.js'; +import { racingValidations } from '../validations/racing.js'; const mockMiniGame = { PvC: vi.fn().mockResolvedValue({ score: 1, log: { player: 1, computer: 0, result: 'win' } }), CvC: vi.fn().mockResolvedValue({ score: 2, log: { player: 1, computer: 0, result: 'win' } }), -} - +}; describe('RacingGame', () => { let game; beforeEach(() => { - game = new RacingGame({ miniGames: { MockGame: mockMiniGame } }) - }) - + game = new RacingGame({ miniGames: { MockGame: mockMiniGame } }); + }); test('최대 라운드를 설정할 수 있다.', () => { - game.setMaxRound(3) - expect(game.maxRound).toBe(3) - }) - + game.setMaxRound(3); + expect(game.maxRound).toBe(3); + }); test('자동차를 생성할 수 있다.', () => { - game.setCars(['자동차1'], ['자동차2']) - expect(game.cars).toHaveLength(2) - expect(game.cars[0].name).toBe('자동차1') - expect(game.cars[1].name).toBe('자동차2') - }) - + game.setCars(['자동차1'], ['자동차2']); + expect(game.cars).toHaveLength(2); + expect(game.cars[0].name).toBe('자동차1'); + expect(game.cars[1].name).toBe('자동차2'); + }); test('유효하지 않은 자동차에 대해 오류를 발생시킨다.', () => { - expect(() => game.setCars(['자동차1', '자동차1'], [])).toThrow(racingValidations.uniqueCarName.errorMessage) - expect(() => game.setCars([], [])).toThrow(racingValidations.leastCarCount.errorMessage) - }) - + expect(() => game.setCars(['자동차1', '자동차1'], [])).toThrow(racingValidations.uniqueCarName.errorMessage); + expect(() => game.setCars([], [])).toThrow(racingValidations.leastCarCount.errorMessage); + }); test('유효하지 않은 최대 라운드 값에 대해 오류를 발생시킨다.', () => { - expect(() => game.setMaxRound('문자열')).toThrow(racingValidations.maxRoundNumber.errorMessage) - expect(() => game.setMaxRound(6)).toThrow(racingValidations.maxRoundRange.errorMessage) - }) + expect(() => game.setMaxRound('문자열')).toThrow(racingValidations.maxRoundNumber.errorMessage); + expect(() => game.setMaxRound(6)).toThrow(racingValidations.maxRoundRange.errorMessage); + }); test('유효하지 않은 미니게임 함수에 대해 오류를 발생시킨다.', () => { const invalidMiniGame = { CvC: vi.fn().mockResolvedValue({ score: 1, log: { player: 1, computer: 0 } }), - } - expect(() => new RacingGame({ miniGames: { MockGame: invalidMiniGame } })).toThrow(racingValidations.miniGameInterface.errorMessage) - }) + }; + expect(() => new RacingGame({ miniGames: { MockGame: invalidMiniGame } })).toThrow( + racingValidations.miniGameInterface.errorMessage, + ); + }); test('유효하지 않은 미니게임 결과에 대해 오류를 발생시킨다.', async () => { const invalidMiniGame = { - PvC: vi.fn().mockResolvedValue({score: 1, log: {hello: 'hello'}}), - CvC: vi.fn().mockReturnValue({score: 1, win: true}), - } + PvC: vi.fn().mockResolvedValue({ score: 1, log: { hello: 'hello' } }), + CvC: vi.fn().mockReturnValue({ score: 1, win: true }), + }; const racingGame = new RacingGame({ - miniGames: {MockGame: invalidMiniGame}, - }) - racingGame.setCars([], ['자동차1', '자동차2']) + miniGames: { MockGame: invalidMiniGame }, + }); + racingGame.setCars([], ['자동차1', '자동차2']); try { - await racingGame.doRound() - }catch(error){ - expect(error.message).toBe(racingValidations.miniGameResult.errorMessage) + await racingGame.doRound(); + } catch (error) { + expect(error.message).toBe(racingValidations.miniGameResult.errorMessage); } - }) + }); test('우승자를 올바르게 계산할 수 있다.', async () => { - game.setCars(['자동차1'], ['자동차2']) - await game.doRound() + game.setCars(['자동차1'], ['자동차2']); + await game.doRound(); - expect(game.winners).toEqual(['자동차1', '자동차2']) - }) + expect(game.winners).toEqual(['자동차1', '자동차2']); + }); test('점수를 올바르게 저장하고 계산할 수 있다.', async () => { - game.setCars(['자동차1'], ['자동차2']) + game.setCars(['자동차1'], ['자동차2']); - await game.doRound() - await game.doRound() + await game.doRound(); + await game.doRound(); expect(game.lastResult.positions).toEqual({ - '자동차1': 2, - '자동차2': 4 - }) - expect(game.maxPosition).toBe(4) - }) + 자동차1: 2, + 자동차2: 4, + }); + expect(game.maxPosition).toBe(4); + }); test('라운드 종료 후 이벤트가 발생한다..', async () => { - const roundEndSpy = vi.spyOn(game, 'emitEvent') + const roundEndSpy = vi.spyOn(game, 'emitEvent'); - game.setCars(['자동차1'], ['자동차2']) - await game.doRound() + game.setCars(['자동차1'], ['자동차2']); + await game.doRound(); - expect(roundEndSpy).toHaveBeenCalledWith('roundEnd') - }) -}) + expect(roundEndSpy).toHaveBeenCalledWith('roundEnd'); + }); +}); diff --git a/src/__tests__/racingGameController.test.js b/src/__tests__/racingGameController.test.js index 90f764f..0d60215 100644 --- a/src/__tests__/racingGameController.test.js +++ b/src/__tests__/racingGameController.test.js @@ -1,39 +1,41 @@ -import { describe, test, expect, vi, beforeEach } from 'vitest' +import { describe, test, expect, vi, beforeEach } from 'vitest'; -import { RacingGame, RacingGameController } from "../domains/racingGame/index.js"; +import { + RacingGame, + RacingGameController, +} from '../domains/racingGame/index.js'; const mockMiniGame = { - PvC: null, - CvC: null -} + PvC: null, + CvC: null, +}; const mockViewer = { - readPlayerCarNames: vi.fn().mockResolvedValue('자동차1,자동차2'), - readBotCarNames: vi.fn().mockResolvedValue('봇1,봇2,봇3'), - readRoundCount: vi.fn().mockResolvedValue('3') -} + readPlayerCarNames: vi.fn().mockResolvedValue('자동차1,자동차2'), + readBotCarNames: vi.fn().mockResolvedValue('봇1,봇2,봇3'), + readRoundCount: vi.fn().mockResolvedValue('3'), +}; describe('RacingGameController', () => { - let racingGame, viewer, controller + let racingGame, viewer, controller; - beforeEach(() => { - racingGame = new RacingGame({ miniGames: { MockGame: mockMiniGame } }) - viewer = mockViewer - controller = new RacingGameController({ racingGame, viewer }) - }) + beforeEach(() => { + racingGame = new RacingGame({ miniGames: { MockGame: mockMiniGame } }); + viewer = mockViewer; + controller = new RacingGameController({ racingGame, viewer }); + }); + test('플레이어와 봇의 이름을 설정한다.', async () => { + await controller.setupNames(); - test('플레이어와 봇의 이름을 설정한다.', async () => { - await controller.setupNames() + expect(racingGame.players).toEqual(['자동차1', '자동차2']); + expect(racingGame.cars.length).toBe(5); + }); - expect(racingGame.players).toEqual(['자동차1', '자동차2']) - expect(racingGame.cars.length).toBe(5) - }) + test('최대 라운드를 설정한다.', async () => { + vi.spyOn(racingGame, 'setMaxRound'); + await controller.setupMaxRound(); - test('최대 라운드를 설정한다.', async () => { - vi.spyOn(racingGame, 'setMaxRound') - await controller.setupMaxRound() - - expect(racingGame.maxRound).toBe(3) - }) -}) + expect(racingGame.maxRound).toBe(3); + }); +}); diff --git a/src/domains/Car.js b/src/domains/Car.js index 735c063..26ab830 100644 --- a/src/domains/Car.js +++ b/src/domains/Car.js @@ -1,32 +1,32 @@ -import { carValidations } from '../validations/car.js' +import { carValidations } from '../validations/car.js'; export default class Car { - #position + #position; constructor(name) { - this.name = name.replaceAll(' ', '') + this.name = name.replaceAll(' ', ''); - this.validate() - this.#position = 0 + this.validate(); + this.#position = 0; } get position() { - return this.#position + return this.#position; } move(diff) { if (this.#position + diff < 0) { - this.#position = 0 + this.#position = 0; } else { - this.#position += diff + this.#position += diff; } } validate() { Object.values(carValidations).forEach(({ check, errorMessage }) => { if (!check(this.name)) { - throw new Error(errorMessage) + throw new Error(errorMessage); } - }) + }); } } diff --git a/src/domains/Game.js b/src/domains/Game.js index 2eb4233..404cc42 100644 --- a/src/domains/Game.js +++ b/src/domains/Game.js @@ -1,56 +1,54 @@ -import { getRandomNumber } from '../utils/getRandomNumber.js' -import { EventEmitter } from 'events' +import { getRandomNumber } from '../utils/getRandomNumber.js'; +import { EventEmitter } from 'events'; export default class Game { - #results - #miniGames + #results; + #miniGames; constructor({ miniGames }) { - this.maxRound = 1 - this.currentRound = 1 + this.maxRound = 1; + this.currentRound = 1; - this.#miniGames = miniGames - this.currentMiniGame = '' + this.#miniGames = miniGames; + this.currentMiniGame = ''; - this.#results = [] - this.eventEmitter = new EventEmitter() + this.#results = []; + this.eventEmitter = new EventEmitter(); } get results() { - return this.#results + return this.#results; } get miniGames() { - return this.#miniGames + return this.#miniGames; } get lastResult() { - return this.#results.at(-1) + return this.#results.at(-1); } selectRandomMiniGame() { - const miniGameNames = Object.keys(this.miniGames) - this.currentMiniGame = miniGameNames[getRandomNumber(0, miniGameNames.length - 1)] + const miniGameNames = Object.keys(this.miniGames); + this.currentMiniGame = + miniGameNames[getRandomNumber(0, miniGameNames.length - 1)]; } emitEvent(eventName, ...args) { - this.eventEmitter.emit(eventName, ...args) + this.eventEmitter.emit(eventName, ...args); } - addResult(resultAfterRound) { - this.#results.push(resultAfterRound) - } - - doRound() { + this.#results.push(resultAfterRound); } + doRound() {} async play(startRound = this.currentRound, endRound = this.maxRound) { - this.currentRound = startRound + this.currentRound = startRound; while (this.currentRound <= endRound) { - await this.doRound() - this.currentRound += 1 + await this.doRound(); + this.currentRound += 1; } } } diff --git a/src/domains/miniGames/DiceDiffGame.js b/src/domains/miniGames/DiceDiffGame.js index 0517f7d..78ea453 100644 --- a/src/domains/miniGames/DiceDiffGame.js +++ b/src/domains/miniGames/DiceDiffGame.js @@ -1,13 +1,13 @@ -import { getRandomNumber } from '../../utils/getRandomNumber.js' -import readInput from '../../utils/readInput.js' +import { getRandomNumber } from '../../utils/getRandomNumber.js'; +import readInput from '../../utils/readInput.js'; export default class DiceDiffGame { static get diceNumber() { - return getRandomNumber(1, 6) + return getRandomNumber(1, 6); } static getPlayerResult(playerName, playerAnswer, opponentAnswer) { - const diff = playerAnswer - opponentAnswer + const diff = playerAnswer - opponentAnswer; return { score: diff, log: { @@ -15,16 +15,18 @@ export default class DiceDiffGame { computer: opponentAnswer, result: diff, }, - } + }; } static CvC(playerName) { - return this.getPlayerResult(playerName, this.diceNumber, this.diceNumber) + return this.getPlayerResult(playerName, this.diceNumber, this.diceNumber); } static async PvC(playerName) { - await readInput('주사위 던지기 게임 : Enter 입력 시 주사위를 던지고, 그 차이만큼 이동합니다.') - const answer = this.diceNumber - return this.getPlayerResult(`*${playerName}`, answer, this.diceNumber) + await readInput( + '주사위 던지기 게임 : Enter 입력 시 주사위를 던지고, 그 차이만큼 이동합니다.', + ); + const answer = this.diceNumber; + return this.getPlayerResult(`*${playerName}`, answer, this.diceNumber); } } diff --git a/src/domains/miniGames/GuessRandomNumber.js b/src/domains/miniGames/GuessRandomNumber.js index 76aa201..af4f127 100644 --- a/src/domains/miniGames/GuessRandomNumber.js +++ b/src/domains/miniGames/GuessRandomNumber.js @@ -1,14 +1,14 @@ -import { getRandomNumber } from '../../utils/getRandomNumber.js' -import readInput from '../../utils/readInput.js' -import { guessRandomNumberValidations } from '../../validations/miniGames.js' +import { getRandomNumber } from '../../utils/getRandomNumber.js'; +import readInput from '../../utils/readInput.js'; +import { guessRandomNumberValidations } from '../../validations'; export default class GuessRandomNumber { static get randomNumber() { - return getRandomNumber(1, 10) + return getRandomNumber(1, 10); } static getPlayerResult(playerName, playerAnswer, opponentAnswer) { - const win = playerAnswer === opponentAnswer + const win = playerAnswer === opponentAnswer; return { win, log: { @@ -16,15 +16,26 @@ export default class GuessRandomNumber { computer: opponentAnswer, result: win ? 'win' : 'lose', }, - } + }; } static CvC(playerName) { - return this.getPlayerResult(playerName, this.randomNumber, this.randomNumber) + return this.getPlayerResult( + playerName, + this.randomNumber, + this.randomNumber, + ); } static async PvC(playerName) { - const answer = await readInput('숫자 맞추기 대결 : 1 부터 10 까지의 숫자 중 하나를 입력\n', guessRandomNumberValidations) - return this.getPlayerResult(`*${playerName}`, Number(answer), this.randomNumber) + const answer = await readInput( + '숫자 맞추기 대결 : 1 부터 10 까지의 숫자 중 하나를 입력\n', + guessRandomNumberValidations, + ); + return this.getPlayerResult( + `*${playerName}`, + Number(answer), + this.randomNumber, + ); } -} \ No newline at end of file +} diff --git a/src/domains/miniGames/GuessTimeOut.js b/src/domains/miniGames/GuessTimeOut.js index 391cde4..c5a01d2 100644 --- a/src/domains/miniGames/GuessTimeOut.js +++ b/src/domains/miniGames/GuessTimeOut.js @@ -1,22 +1,21 @@ -import { getRandomNumber } from '../../utils/getRandomNumber.js' -import readInput from '../../utils/readInput.js' +import { getRandomNumber } from '../../utils/getRandomNumber.js'; +import readInput from '../../utils/readInput.js'; export default class GuessTimeOut { static get randomSeconds() { - return getRandomNumber(5, 10) // 5초에서 10초 사이의 랜덤 시간 생성 + return getRandomNumber(5, 10); // 5초에서 10초 사이의 랜덤 시간 생성 } - static async getPlayerTime(opponentTime) { - const startTime = Date.now() - await readInput(`타이머 맞추기: ${opponentTime} 뒤에 Enter 누르기`) - const endTime = Date.now() - return (endTime - startTime) / 1000 // 밀리초를 초로 변환 + const startTime = Date.now(); + await readInput(`타이머 맞추기: ${opponentTime} 뒤에 Enter 누르기`); + const endTime = Date.now(); + return (endTime - startTime) / 1000; // 밀리초를 초로 변환 } static getPlayerResult(playerName, playerTime, opponentTime) { - const difference = Math.abs(playerTime - opponentTime) - const win = difference < 0.5 + const difference = Math.abs(playerTime - opponentTime); + const win = difference < 0.5; return { win, log: { @@ -24,18 +23,18 @@ export default class GuessTimeOut { computer: opponentTime.toFixed(2), result: win ? 'win' : 'lose', }, - } + }; } static CvC(playerName) { - const opponentTime = this.randomSeconds - const playerTime = opponentTime + (Math.random() * 2 - 1) - return this.getPlayerResult(playerName, playerTime, opponentTime) + const opponentTime = this.randomSeconds; + const playerTime = opponentTime + (Math.random() * 2 - 1); + return this.getPlayerResult(playerName, playerTime, opponentTime); } static async PvC(playerName) { - const opponentTime = this.randomSeconds - const playerTime = await this.getPlayerTime(opponentTime) - return this.getPlayerResult(`*${playerName}`, playerTime, opponentTime) + const opponentTime = this.randomSeconds; + const playerTime = await this.getPlayerTime(opponentTime); + return this.getPlayerResult(`*${playerName}`, playerTime, opponentTime); } } diff --git a/src/domains/miniGames/RockPaperScissors.js b/src/domains/miniGames/RockPaperScissors.js index a588c5b..feb6b08 100644 --- a/src/domains/miniGames/RockPaperScissors.js +++ b/src/domains/miniGames/RockPaperScissors.js @@ -1,6 +1,6 @@ -import { getRandomNumber } from '../../utils/getRandomNumber.js' -import readInput from '../../utils/readInput.js' -import { rockPaperScissorsValidations } from '../../validations/miniGames.js' +import { getRandomNumber } from '../../utils/getRandomNumber.js'; +import readInput from '../../utils/readInput.js'; +import { rockPaperScissorsValidations } from '../../validations'; const rockScissorPaperMap = { rock: { @@ -15,30 +15,29 @@ const rockScissorPaperMap = { win: 'rock', icon: '✋', }, -} +}; export default class RockPaperScissors { - static get answerList() { - return Object.keys(rockScissorPaperMap) + return Object.keys(rockScissorPaperMap); } static get randomAnswer() { - return this.answerList[getRandomNumber(0, 2)].padEnd(2, ' ') + return this.answerList[getRandomNumber(0, 2)].padEnd(2, ' '); } static getIcon(answer) { - return rockScissorPaperMap[answer].icon.padEnd(3, ' ') + return rockScissorPaperMap[answer].icon.padEnd(3, ' '); } static getPlayerResult(playerName, playerAnswer, opponentAnswer) { - let score = 0 - let result = 'draw' + let score = 0; + let result = 'draw'; if (rockScissorPaperMap[playerAnswer].win === opponentAnswer) { - score = 1 - result = 'win' + score = 1; + result = 'win'; } else if (rockScissorPaperMap[opponentAnswer].win === playerAnswer) { - score = -1 - result = 'lose' + score = -1; + result = 'lose'; } return { score, @@ -47,16 +46,27 @@ export default class RockPaperScissors { computer: this.getIcon(opponentAnswer), result, }, - } + }; } static CvC(playerName) { - return this.getPlayerResult(playerName, this.randomAnswer, this.randomAnswer) + return this.getPlayerResult( + playerName, + this.randomAnswer, + this.randomAnswer, + ); } static async PvC(playerName) { - const answerIndex = await readInput('가위바위보 대결 : 1.바위 2.가위 3.보\n', rockPaperScissorsValidations) - const playerAnswer = this.answerList[Number(answerIndex) - 1] - return this.getPlayerResult(`*${playerName}`, playerAnswer, this.randomAnswer) + const answerIndex = await readInput( + '가위바위보 대결 : 1.바위 2.가위 3.보\n', + rockPaperScissorsValidations, + ); + const playerAnswer = this.answerList[Number(answerIndex) - 1]; + return this.getPlayerResult( + `*${playerName}`, + playerAnswer, + this.randomAnswer, + ); } } diff --git a/src/domains/miniGames/index.js b/src/domains/miniGames/index.js index 91b3c0a..aa863a6 100644 --- a/src/domains/miniGames/index.js +++ b/src/domains/miniGames/index.js @@ -1,4 +1,4 @@ -export { default as DiceDiffGame } from './DiceDiffGame.js' -export { default as GuessRandomNumber } from './GuessRandomNumber.js' -export { default as GuessTimeOut } from './GuessTimeOut.js' -export { default as RockPaperScissors } from './RockPaperScissors.js' +export { default as DiceDiffGame } from './DiceDiffGame.js'; +export { default as GuessRandomNumber } from './GuessRandomNumber.js'; +export { default as GuessTimeOut } from './GuessTimeOut.js'; +export { default as RockPaperScissors } from './RockPaperScissors.js'; diff --git a/src/domains/racingGame/RacingGame.js b/src/domains/racingGame/RacingGame.js index 901431e..d0ee617 100644 --- a/src/domains/racingGame/RacingGame.js +++ b/src/domains/racingGame/RacingGame.js @@ -1,30 +1,32 @@ -import Car from '../Car.js' -import Game from '../Game.js' -import { racingValidations, createValidator } from "../../validations"; +import Car from '../Car.js'; +import Game from '../Game.js'; +import { racingValidations, createValidator } from '../../validations'; export default class RacingGame extends Game { constructor({ miniGames }) { - super({ miniGames }) - this.validate = createValidator(racingValidations) - this.validate(this.miniGames, ['miniGameInterface', 'miniGameSize']) + super({ miniGames }); + this.validate = createValidator(racingValidations); + this.validate(this.miniGames, ['miniGameInterface', 'miniGameSize']); } get maxPosition() { - return Math.max(...Object.values(this.lastResult.positions)) + return Math.max(...Object.values(this.lastResult.positions)); } get winners() { - const { positions } = this.lastResult - return Object.keys(positions).filter(name => positions[name] === this.maxPosition) + const { positions } = this.lastResult; + return Object.keys(positions).filter( + name => positions[name] === this.maxPosition, + ); } setMaxRound(maxRound) { - this.maxRound = maxRound - this.validate(this.maxRound, ['maxRoundNumber', 'maxRoundRange']) + this.maxRound = maxRound; + this.validate(this.maxRound, ['maxRoundNumber', 'maxRoundRange']); } setCars(playerNames, botNames) { - this.cars = [] + this.cars = []; this.players = []; [...playerNames, ...botNames].forEach(name => { const newCar = new Car(name); @@ -33,54 +35,57 @@ export default class RacingGame extends Game { this.players.push(newCar.name); } }); - this.validate(this.cars,['leastCarCount', 'uniqueCarName']) + this.validate(this.cars, ['leastCarCount', 'uniqueCarName']); } async doRound() { - this.selectRandomMiniGame() - this.emitEvent('roundStart') + this.selectRandomMiniGame(); + this.emitEvent('roundStart'); for (const car of this.cars) { - await this.race(car) + await this.race(car); } - this.addResult(this.createResult()) - this.emitEvent('roundEnd') + this.addResult(this.createResult()); + this.emitEvent('roundEnd'); } createResult() { const result = { ruleName: this.currentMiniGame, - positions: this.cars.reduce((acc, car) => ({ - ...acc, - [car.name]: car.position, - }), {}), + positions: this.cars.reduce( + (acc, car) => ({ + ...acc, + [car.name]: car.position, + }), + {}, + ), gameLogs: this.gameLogs, - } - this.gameLogs = {} - return result + }; + this.gameLogs = {}; + return result; } async race(car) { - const miniGameResult = await this.doMiniGame(car) - this.validate(miniGameResult, ['miniGameResult']) + const miniGameResult = await this.doMiniGame(car); + this.validate(miniGameResult, ['miniGameResult']); if (miniGameResult.hasOwnProperty('score')) { - car.move(miniGameResult.score) + car.move(miniGameResult.score); } if (miniGameResult.hasOwnProperty('win')) { - car.move(miniGameResult.win ? 1 : 0) + car.move(miniGameResult.win ? 1 : 0); } - this.gameLogs = { ...this.gameLogs, [car.name]: miniGameResult.log } + this.gameLogs = { ...this.gameLogs, [car.name]: miniGameResult.log }; } async doMiniGame(car) { - const miniGame = this.miniGames[this.currentMiniGame] + const miniGame = this.miniGames[this.currentMiniGame]; if (this.players.includes(car.name)) { - this.emitEvent('miniGameStart', car.name) - return await miniGame.PvC(car.name) + this.emitEvent('miniGameStart', car.name); + return await miniGame.PvC(car.name); } - return miniGame.CvC(car.name) + return miniGame.CvC(car.name); } } diff --git a/src/domains/racingGame/RacingGameController.js b/src/domains/racingGame/RacingGameController.js index e10cdf0..6cffdf5 100644 --- a/src/domains/racingGame/RacingGameController.js +++ b/src/domains/racingGame/RacingGameController.js @@ -1,55 +1,57 @@ export default class RacingGameController { constructor({ racingGame, viewer }) { - this.racingGame = racingGame - this.viewer = viewer + this.racingGame = racingGame; + this.viewer = viewer; - this.racingGame.eventEmitter.on('roundStart', () => this.onRoundStart()) - this.racingGame.eventEmitter.on('roundEnd', () => this.onRoundEnd()) - this.racingGame.eventEmitter.on('miniGameStart', (name) => this.onMiniGameStart(name)) + this.racingGame.eventEmitter.on('roundStart', () => this.onRoundStart()); + this.racingGame.eventEmitter.on('roundEnd', () => this.onRoundEnd()); + this.racingGame.eventEmitter.on('miniGameStart', name => + this.onMiniGameStart(name), + ); } onRoundStart() { - this.viewer.displayStartRound(this.racingGame) + this.viewer.displayStartRound(this.racingGame); } onMiniGameStart(name) { - this.viewer.displayMiniGameStart(name) + this.viewer.displayMiniGameStart(name); } onRoundEnd() { - this.viewer.displayGameLogs(this.racingGame) - this.viewer.displayRoundResult(this.racingGame) + this.viewer.displayGameLogs(this.racingGame); + this.viewer.displayRoundResult(this.racingGame); } async startGame() { - this.viewer.displayGameStart() - await this.setupNames() - await this.setupMaxRound() - await this.racingGame.play() - this.viewer.displayWinners(this.racingGame) + this.viewer.displayGameStart(); + await this.setupNames(); + await this.setupMaxRound(); + await this.racingGame.play(); + this.viewer.displayWinners(this.racingGame); } async setupNames() { try { - const playerNames = await this.viewer.readPlayerCarNames() - const botNames = await this.viewer.readBotCarNames() + const playerNames = await this.viewer.readPlayerCarNames(); + const botNames = await this.viewer.readBotCarNames(); this.racingGame.setCars( - playerNames.split(',').filter(Boolean), - botNames.split(',').filter(Boolean), - ) + playerNames.split(',').filter(Boolean), + botNames.split(',').filter(Boolean), + ); } catch (error) { - this.viewer.displayError(error) - await this.setupNames() + this.viewer.displayError(error); + await this.setupNames(); } } async setupMaxRound() { try { - const maxRound = await this.viewer.readRoundCount() - this.racingGame.setMaxRound(Number(maxRound)) - }catch (error) { - this.viewer.displayError(error) - await this.setupMaxRound() + const maxRound = await this.viewer.readRoundCount(); + this.racingGame.setMaxRound(Number(maxRound)); + } catch (error) { + this.viewer.displayError(error); + await this.setupMaxRound(); } } } diff --git a/src/domains/racingGame/RacingGameViewer.js b/src/domains/racingGame/RacingGameViewer.js index 384944e..eb6ee9f 100644 --- a/src/domains/racingGame/RacingGameViewer.js +++ b/src/domains/racingGame/RacingGameViewer.js @@ -1,84 +1,105 @@ -import ConsolePrinter from '../../service/Printer.js' -import readInput from '../../utils/readInput.js' +import ConsolePrinter from '../../service/Printer.js'; +import readInput from '../../utils/readInput.js'; export default class RacingGameViewer { constructor() { this.printer = new ConsolePrinter({ - roundStart: '--------------------------⭐ Round%{1}⭐️️--------------------------', + roundStart: + '--------------------------⭐ Round%{1}⭐️️--------------------------', carPosition: '%{1} : %{2} (%{3}%{4} ➡ %{5})', gameLog: '%{1} : %{2} VS computer : %{3} ➡➡ %{4}', miniGameStart: '>> player %{1} Turn!', winner: '최종 우승자는 👑 %{1} 입니다. 축하합니다!', error: '⚠️ %{1}', - divider: '---------------------------------------------------------------' - }) + divider: + '---------------------------------------------------------------', + }); } displayGameStart() { - this.printer.print('🚕 레이싱 게임을 시작합니다 🚗') - this.printer.print('각 라운드마다 랜덤 미니 게임을 진행하여 이동할 수 있습니다.') - this.printer.lineBreak() + this.printer.print('🚕 레이싱 게임을 시작합니다 🚗'); + this.printer.print( + '각 라운드마다 랜덤 미니 게임을 진행하여 이동할 수 있습니다.', + ); + this.printer.lineBreak(); } displayStartRound({ currentRound }) { - this.printer.printWithTemplate('roundStart', [currentRound]) + this.printer.printWithTemplate('roundStart', [currentRound]); } displayMiniGameStart(playerName) { - this.printer.lineBreak() - this.printer.printWithTemplate('miniGameStart', [playerName]) + this.printer.lineBreak(); + this.printer.printWithTemplate('miniGameStart', [playerName]); } displayGameLogs({ lastResult }) { - const { gameLogs } = lastResult - this.printer.lineBreak() + const { gameLogs } = lastResult; + this.printer.lineBreak(); Object.entries(gameLogs).forEach(([name, log]) => { - this.printer.printWithTemplate('gameLog', [name.padEnd(5, ' '), ...Object.values(log)]) - }) - this.printer.lineBreak() + this.printer.printWithTemplate('gameLog', [ + name.padEnd(5, ' '), + ...Object.values(log), + ]); + }); + this.printer.lineBreak(); } displayRoundResult({ results, maxPosition, currentRound }) { - const { positions } = results[currentRound - 1] - const prevPositions = currentRound > 1 ? results[currentRound - 2].positions : {} + const { positions } = results[currentRound - 1]; + const prevPositions = + currentRound > 1 ? results[currentRound - 2].positions : {}; Object.entries(positions).forEach(([name, position]) => { - const positionString = this.formatPositionString(position, maxPosition) - const positionDiffArgs = this.getPositionDiffArgs(position, prevPositions[name] || 0) - this.printer.printWithTemplate('carPosition', [name.padEnd(5, ' '), positionString, ...positionDiffArgs]) - }) - this.printer.lineBreak() + const positionString = this.formatPositionString(position, maxPosition); + const positionDiffArgs = this.getPositionDiffArgs( + position, + prevPositions[name] || 0, + ); + this.printer.printWithTemplate('carPosition', [ + name.padEnd(5, ' '), + positionString, + ...positionDiffArgs, + ]); + }); + this.printer.lineBreak(); } displayWinners({ winners }) { - this.printer.printWithTemplate('winner', [winners.join(', ')]) - this.printer.lineBreak() + this.printer.printWithTemplate('winner', [winners.join(', ')]); + this.printer.lineBreak(); } displayError(errorMessage) { - this.printer.printWithTemplate('divider') - this.printer.printWithTemplate('error', [errorMessage]) - this.printer.printWithTemplate('divider') - this.printer.lineBreak() + this.printer.printWithTemplate('divider'); + this.printer.printWithTemplate('error', [errorMessage]); + this.printer.printWithTemplate('divider'); + this.printer.lineBreak(); } async readPlayerCarNames() { - return await readInput('직접 레이싱에 참여할 자동차들의 이름을 입력해주세요. (쉼표로 구분)\n') + return await readInput( + '직접 레이싱에 참여할 자동차들의 이름을 입력해주세요. (쉼표로 구분)\n', + ); } async readBotCarNames() { - return await readInput('봇으로 참여할 자동차들의 이름을 입력해주세요. (쉼표로 구분)\n') + return await readInput( + '봇으로 참여할 자동차들의 이름을 입력해주세요. (쉼표로 구분)\n', + ); } async readRoundCount() { - return await readInput('몇 라운드 플레이할 지 알려주세요.\n') + return await readInput('몇 라운드 플레이할 지 알려주세요.\n'); } formatPositionString(position, maxPosition) { - return '⛳️'.padStart((position + 1) * 2, '__').padEnd((maxPosition + 1) * 2, '__') + return '⛳️' + .padStart((position + 1) * 2, '__') + .padEnd((maxPosition + 1) * 2, '__'); } getPositionDiffArgs(position, prevPosition) { - const positionDiff = position - prevPosition - return [positionDiff < 0 ? '' : '+', positionDiff, position] + const positionDiff = position - prevPosition; + return [positionDiff < 0 ? '' : '+', positionDiff, position]; } } diff --git a/src/domains/racingGame/index.js b/src/domains/racingGame/index.js index 5eebeff..b974b75 100644 --- a/src/domains/racingGame/index.js +++ b/src/domains/racingGame/index.js @@ -1,3 +1,3 @@ -export { default as RacingGame } from './RacingGame' -export { default as RacingGameController } from './RacingGameController' -export { default as RacingGameViewer } from './RacingGameViewer' +export { default as RacingGame } from './RacingGame'; +export { default as RacingGameController } from './RacingGameController'; +export { default as RacingGameViewer } from './RacingGameViewer'; diff --git a/src/main.js b/src/main.js index 014b706..5628027 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,14 @@ -import { RacingGame, RacingGameViewer, RacingGameController } from './domains/racingGame' -import { GuessTimeOut, GuessRandomNumber, RockPaperScissors, DiceDiffGame } from './domains/miniGames' +import { + RacingGame, + RacingGameViewer, + RacingGameController, +} from './domains/racingGame'; +import { + GuessTimeOut, + GuessRandomNumber, + RockPaperScissors, + DiceDiffGame, +} from './domains/miniGames'; async function main() { await new RacingGameController({ @@ -11,8 +20,8 @@ async function main() { DiceDiffGame, }, }), - viewer: new RacingGameViewer() - }).startGame() + viewer: new RacingGameViewer(), + }).startGame(); } -main() +main(); diff --git a/src/service/Printer.js b/src/service/Printer.js index 0e00b24..41d1ada 100644 --- a/src/service/Printer.js +++ b/src/service/Printer.js @@ -1,33 +1,31 @@ export default class ConsolePrinter { constructor(template) { - this.template = template + this.template = template; } format(templateKey, messages) { - let result = this.template[templateKey] + let result = this.template[templateKey]; if (messages && messages.length > 0) { messages.forEach((message, index) => { - result = result.replaceAll(`%{${index + 1}}`, message) - }) + result = result.replaceAll(`%{${index + 1}}`, message); + }); } - return result + return result; } print(...messages) { - console.log(...messages) + console.log(...messages); } printWithTemplate(templateKey, messages) { if (this.template.hasOwnProperty(templateKey)) { - console.log(this.format(templateKey, messages)) + console.log(this.format(templateKey, messages)); } else { - console.log(...messages) + console.log(...messages); } } lineBreak() { - console.log('') + console.log(''); } - - } diff --git a/src/utils/createReadline.js b/src/utils/createReadline.js index 07e50ae..3131ce1 100644 --- a/src/utils/createReadline.js +++ b/src/utils/createReadline.js @@ -1,15 +1,15 @@ -import readline from 'readline' +import readline from 'readline'; const createReadline = () => { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, - }) - rl.isOpened = true + }); + rl.isOpened = true; rl.on('close', () => { - rl.isOpened = false - }) - return rl -} + rl.isOpened = false; + }); + return rl; +}; -export default createReadline +export default createReadline; diff --git a/src/utils/getRandomNumber.js b/src/utils/getRandomNumber.js index 265bae4..c624e58 100644 --- a/src/utils/getRandomNumber.js +++ b/src/utils/getRandomNumber.js @@ -1,3 +1,3 @@ export const getRandomNumber = (min, max) => { - return Math.floor(Math.random() * (max - min + 1)) + min -} + return Math.floor(Math.random() * (max - min + 1)) + min; +}; diff --git a/src/utils/readInput.js b/src/utils/readInput.js index 8d479cb..57a3a69 100644 --- a/src/utils/readInput.js +++ b/src/utils/readInput.js @@ -1,32 +1,32 @@ -import createReadline from './createReadline.js' +import createReadline from './createReadline.js'; const askQuestion = (rl, message) => { - return new Promise((resolve) => { + return new Promise(resolve => { rl.question(message, input => { - resolve(input) - }) - }) -} + resolve(input); + }); + }); +}; const findInputError = (input, validations) => { - const { errorMessage } = validations.find(({ check }) => !check(input)) ?? {} - return errorMessage -} + const { errorMessage } = validations.find(({ check }) => !check(input)) ?? {}; + return errorMessage; +}; const readInput = (message, validations = [], option = 'repeat') => { - const rl = createReadline() + const rl = createReadline(); const processInput = async () => { - const input = await askQuestion(rl, message) - const errorMessage = findInputError(input, validations) + const input = await askQuestion(rl, message); + const errorMessage = findInputError(input, validations); if (option === 'repeat' && errorMessage) { - console.error(errorMessage) - return processInput() + console.error(errorMessage); + return processInput(); } - rl.close() - return input - } - return processInput() -} + rl.close(); + return input; + }; + return processInput(); +}; -export default readInput \ No newline at end of file +export default readInput; diff --git a/src/validations/car.js b/src/validations/car.js index 73a3ee2..8d4eaa7 100644 --- a/src/validations/car.js +++ b/src/validations/car.js @@ -1,6 +1,6 @@ export const carValidations = { - carNameLength: { - check: name => name.length >= 1 && name.length <= 5, - errorMessage: '자동차 이름은 1자 이상 5자 이하여야 합니다.' - } -} + carNameLength: { + check: name => name.length >= 1 && name.length <= 5, + errorMessage: '자동차 이름은 1자 이상 5자 이하여야 합니다.', + }, +}; diff --git a/src/validations/createValidator.js b/src/validations/createValidator.js index cf525d9..08dfce8 100644 --- a/src/validations/createValidator.js +++ b/src/validations/createValidator.js @@ -1,14 +1,14 @@ -const createValidator = (validations) => { - return (target, validationKeys) => { - validationKeys.forEach((key) => { - if (!validations.hasOwnProperty(key)) { - throw new Error('올바른 검사 키가 아닙니다.') - } - if (!validations[key].check(target)) { - throw new Error(validations[key].errorMessage) - } - }) - } -} +const createValidator = validations => { + return (target, validationKeys) => { + validationKeys.forEach(key => { + if (!validations.hasOwnProperty(key)) { + throw new Error('올바른 검사 키가 아닙니다.'); + } + if (!validations[key].check(target)) { + throw new Error(validations[key].errorMessage); + } + }); + }; +}; -export default createValidator +export default createValidator; diff --git a/src/validations/index.js b/src/validations/index.js index cc9fab5..ca5646a 100644 --- a/src/validations/index.js +++ b/src/validations/index.js @@ -1,5 +1,5 @@ -export * from './car' -export * from './miniGames.js' -export * from './racing.js' +export * from './car'; +export * from './miniGames.js'; +export * from './racing.js'; -export { default as createValidator } from './createValidator' +export { default as createValidator } from './createValidator'; diff --git a/src/validations/miniGames.js b/src/validations/miniGames.js index c4ade31..6ea73c2 100644 --- a/src/validations/miniGames.js +++ b/src/validations/miniGames.js @@ -1,13 +1,16 @@ export const rockPaperScissorsValidations = [ { - check: (input) => [1, 2, 3].includes(Number(input)), + check: input => [1, 2, 3].includes(Number(input)), errorMessage: '가위바위보 입력값은 1,2,3 중 하나여야 합니다.', }, -] +]; export const guessRandomNumberValidations = [ { - check: (input) => Number(input) >= 1 && Number(input) <= 10 && Number.isInteger(Number(input)), + check: input => + Number(input) >= 1 && + Number(input) <= 10 && + Number.isInteger(Number(input)), errorMessage: '1 부터 10 까지의 자연수만 가능합니다.', }, -] +]; diff --git a/src/validations/racing.js b/src/validations/racing.js index a562d7b..c6eab86 100644 --- a/src/validations/racing.js +++ b/src/validations/racing.js @@ -1,41 +1,50 @@ export const racingValidations = { - uniqueCarName: { - check: (cars) =>{ - const carNames = cars.map(({ name }) => name) - return new Set(carNames).size === carNames.length - }, - errorMessage: '경주할 자동차 이름은 각각 달라야 합니다.' + uniqueCarName: { + check: cars => { + const carNames = cars.map(({ name }) => name); + return new Set(carNames).size === carNames.length; }, - leastCarCount: { - check: (cars) => cars.length > 1, - errorMessage: '최소 2대의 자동차가 참가해야 합니다.' - }, - maxRoundNumber: { - check: (maxRound) => Number.isInteger(maxRound), - errorMessage: '최대 라운드 값은 숫자여야 합니다.' - }, - maxRoundRange: { - check: (maxRound) => 1 <= maxRound && maxRound <= 5, - errorMessage: '최소 1라운드부터 최대 5라운드 동안 플레이할 수 있습니다.' - }, - miniGameInterface: { - check: (miniGames) => Object.values(miniGames).every(game => game.hasOwnProperty('CvC') && game.hasOwnProperty('PvC')), - errorMessage: '올바른 미니게임이 아닙니다. 플레이 기능이 존재하지 않습니다.' - }, - miniGameSize: { - check: (miniGames) => Object.values(miniGames).length > 0, - errorMessage: '최대 1개 이상의 미니게임이 있어야 합니다.' - }, - miniGameResult: { - check: (miniGameResult) => { - const isResultCorrect = ( - miniGameResult.hasOwnProperty('score') || - miniGameResult.hasOwnProperty('win') - ) && miniGameResult.hasOwnProperty('log') + errorMessage: '경주할 자동차 이름은 각각 달라야 합니다.', + }, + leastCarCount: { + check: cars => cars.length > 1, + errorMessage: '최소 2대의 자동차가 참가해야 합니다.', + }, + maxRoundNumber: { + check: maxRound => Number.isInteger(maxRound), + errorMessage: '최대 라운드 값은 숫자여야 합니다.', + }, + maxRoundRange: { + check: maxRound => 1 <= maxRound && maxRound <= 5, + errorMessage: '최소 1라운드부터 최대 5라운드 동안 플레이할 수 있습니다.', + }, + miniGameInterface: { + check: miniGames => + Object.values(miniGames).every( + game => game.hasOwnProperty('CvC') && game.hasOwnProperty('PvC'), + ), + errorMessage: + '올바른 미니게임이 아닙니다. 플레이 기능이 존재하지 않습니다.', + }, + miniGameSize: { + check: miniGames => Object.values(miniGames).length > 0, + errorMessage: '최대 1개 이상의 미니게임이 있어야 합니다.', + }, + miniGameResult: { + check: miniGameResult => { + const isResultCorrect = + (miniGameResult.hasOwnProperty('score') || + miniGameResult.hasOwnProperty('win')) && + miniGameResult.hasOwnProperty('log'); - const { log } = miniGameResult - return isResultCorrect && log.hasOwnProperty('player') && log.hasOwnProperty('computer') && log.hasOwnProperty('result') - }, - errorMessage: '올바른 미니게임 결과가 아닙니다.' - } -} + const { log } = miniGameResult; + return ( + isResultCorrect && + log.hasOwnProperty('player') && + log.hasOwnProperty('computer') && + log.hasOwnProperty('result') + ); + }, + errorMessage: '올바른 미니게임 결과가 아닙니다.', + }, +}; From b1bb055b87eb77a6db4120d27d10c22dc6a5569b Mon Sep 17 00:00:00 2001 From: chacha Date: Fri, 5 Jul 2024 16:35:28 +0900 Subject: [PATCH 12/14] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/racingGame.test.js | 38 +++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/__tests__/racingGame.test.js b/src/__tests__/racingGame.test.js index fc74442..92f81c6 100644 --- a/src/__tests__/racingGame.test.js +++ b/src/__tests__/racingGame.test.js @@ -3,8 +3,14 @@ import RacingGame from '../domains/racingGame/RacingGame.js'; import { racingValidations } from '../validations/racing.js'; const mockMiniGame = { - PvC: vi.fn().mockResolvedValue({ score: 1, log: { player: 1, computer: 0, result: 'win' } }), - CvC: vi.fn().mockResolvedValue({ score: 2, log: { player: 1, computer: 0, result: 'win' } }), + PvC: vi.fn().mockResolvedValue({ + score: 1, + log: { player: 1, computer: 0, result: 'win' }, + }), + CvC: vi.fn().mockResolvedValue({ + score: 2, + log: { player: 1, computer: 0, result: 'win' }, + }), }; describe('RacingGame', () => { @@ -27,22 +33,32 @@ describe('RacingGame', () => { }); test('유효하지 않은 자동차에 대해 오류를 발생시킨다.', () => { - expect(() => game.setCars(['자동차1', '자동차1'], [])).toThrow(racingValidations.uniqueCarName.errorMessage); - expect(() => game.setCars([], [])).toThrow(racingValidations.leastCarCount.errorMessage); + expect(() => game.setCars(['자동차1', '자동차1'], [])).toThrow( + racingValidations.uniqueCarName.errorMessage, + ); + expect(() => game.setCars([], [])).toThrow( + racingValidations.leastCarCount.errorMessage, + ); }); test('유효하지 않은 최대 라운드 값에 대해 오류를 발생시킨다.', () => { - expect(() => game.setMaxRound('문자열')).toThrow(racingValidations.maxRoundNumber.errorMessage); - expect(() => game.setMaxRound(6)).toThrow(racingValidations.maxRoundRange.errorMessage); + expect(() => game.setMaxRound('문자열')).toThrow( + racingValidations.maxRoundNumber.errorMessage, + ); + expect(() => game.setMaxRound(6)).toThrow( + racingValidations.maxRoundRange.errorMessage, + ); }); test('유효하지 않은 미니게임 함수에 대해 오류를 발생시킨다.', () => { const invalidMiniGame = { - CvC: vi.fn().mockResolvedValue({ score: 1, log: { player: 1, computer: 0 } }), + CvC: vi + .fn() + .mockResolvedValue({ score: 1, log: { player: 1, computer: 0 } }), }; - expect(() => new RacingGame({ miniGames: { MockGame: invalidMiniGame } })).toThrow( - racingValidations.miniGameInterface.errorMessage, - ); + expect( + () => new RacingGame({ miniGames: { MockGame: invalidMiniGame } }), + ).toThrow(racingValidations.miniGameInterface.errorMessage); }); test('유효하지 않은 미니게임 결과에 대해 오류를 발생시킨다.', async () => { @@ -66,7 +82,7 @@ describe('RacingGame', () => { game.setCars(['자동차1'], ['자동차2']); await game.doRound(); - expect(game.winners).toEqual(['자동차1', '자동차2']); + expect(game.winners).toEqual(['자동차2']); }); test('점수를 올바르게 저장하고 계산할 수 있다.', async () => { From 7bae4154658f605247e0c122ba7351659bf6ad13 Mon Sep 17 00:00:00 2001 From: chacha Date: Fri, 5 Jul 2024 16:49:58 +0900 Subject: [PATCH 13/14] =?UTF-8?q?fix:=20index.js=20=EC=9E=84=ED=8F=AC?= =?UTF-8?q?=ED=8A=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/miniGames/GuessRandomNumber.js | 2 +- src/domains/miniGames/RockPaperScissors.js | 2 +- src/domains/miniGames/index.js | 4 ---- src/domains/racingGame/RacingGame.js | 3 ++- src/domains/racingGame/index.js | 3 --- src/main.js | 18 +++++++----------- src/validations/index.js | 5 ----- 7 files changed, 11 insertions(+), 26 deletions(-) delete mode 100644 src/domains/miniGames/index.js delete mode 100644 src/domains/racingGame/index.js delete mode 100644 src/validations/index.js diff --git a/src/domains/miniGames/GuessRandomNumber.js b/src/domains/miniGames/GuessRandomNumber.js index af4f127..1e541a8 100644 --- a/src/domains/miniGames/GuessRandomNumber.js +++ b/src/domains/miniGames/GuessRandomNumber.js @@ -1,6 +1,6 @@ import { getRandomNumber } from '../../utils/getRandomNumber.js'; import readInput from '../../utils/readInput.js'; -import { guessRandomNumberValidations } from '../../validations'; +import { guessRandomNumberValidations } from '../../validations/miniGames.js'; export default class GuessRandomNumber { static get randomNumber() { diff --git a/src/domains/miniGames/RockPaperScissors.js b/src/domains/miniGames/RockPaperScissors.js index feb6b08..f16f382 100644 --- a/src/domains/miniGames/RockPaperScissors.js +++ b/src/domains/miniGames/RockPaperScissors.js @@ -1,6 +1,6 @@ import { getRandomNumber } from '../../utils/getRandomNumber.js'; import readInput from '../../utils/readInput.js'; -import { rockPaperScissorsValidations } from '../../validations'; +import { rockPaperScissorsValidations } from '../../validations/miniGames.js'; const rockScissorPaperMap = { rock: { diff --git a/src/domains/miniGames/index.js b/src/domains/miniGames/index.js deleted file mode 100644 index aa863a6..0000000 --- a/src/domains/miniGames/index.js +++ /dev/null @@ -1,4 +0,0 @@ -export { default as DiceDiffGame } from './DiceDiffGame.js'; -export { default as GuessRandomNumber } from './GuessRandomNumber.js'; -export { default as GuessTimeOut } from './GuessTimeOut.js'; -export { default as RockPaperScissors } from './RockPaperScissors.js'; diff --git a/src/domains/racingGame/RacingGame.js b/src/domains/racingGame/RacingGame.js index d0ee617..a34ea70 100644 --- a/src/domains/racingGame/RacingGame.js +++ b/src/domains/racingGame/RacingGame.js @@ -1,6 +1,7 @@ import Car from '../Car.js'; import Game from '../Game.js'; -import { racingValidations, createValidator } from '../../validations'; +import { racingValidations } from '../../validations/racing.js'; +import createValidator from '../../validations/createValidator.js'; export default class RacingGame extends Game { constructor({ miniGames }) { diff --git a/src/domains/racingGame/index.js b/src/domains/racingGame/index.js deleted file mode 100644 index b974b75..0000000 --- a/src/domains/racingGame/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { default as RacingGame } from './RacingGame'; -export { default as RacingGameController } from './RacingGameController'; -export { default as RacingGameViewer } from './RacingGameViewer'; diff --git a/src/main.js b/src/main.js index 5628027..522e419 100644 --- a/src/main.js +++ b/src/main.js @@ -1,14 +1,10 @@ -import { - RacingGame, - RacingGameViewer, - RacingGameController, -} from './domains/racingGame'; -import { - GuessTimeOut, - GuessRandomNumber, - RockPaperScissors, - DiceDiffGame, -} from './domains/miniGames'; +import RacingGame from './domains/racingGame/RacingGame.js'; +import RacingGameController from './domains/racingGame/RacingGameController.js'; +import RacingGameViewer from './domains/racingGame/RacingGameViewer.js'; +import RockPaperScissors from './domains/miniGames/RockPaperScissors.js'; +import GuessRandomNumber from './domains/miniGames/GuessRandomNumber.js'; +import GuessTimeOut from './domains/miniGames/GuessTimeOut.js'; +import DiceDiffGame from './domains/miniGames/DiceDiffGame.js'; async function main() { await new RacingGameController({ diff --git a/src/validations/index.js b/src/validations/index.js deleted file mode 100644 index ca5646a..0000000 --- a/src/validations/index.js +++ /dev/null @@ -1,5 +0,0 @@ -export * from './car'; -export * from './miniGames.js'; -export * from './racing.js'; - -export { default as createValidator } from './createValidator'; From b9180db13258c0170be5264b541195d8d4605214 Mon Sep 17 00:00:00 2001 From: chacha Date: Fri, 5 Jul 2024 16:51:28 +0900 Subject: [PATCH 14/14] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20import=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/racingGameController.test.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/__tests__/racingGameController.test.js b/src/__tests__/racingGameController.test.js index 0d60215..971b72b 100644 --- a/src/__tests__/racingGameController.test.js +++ b/src/__tests__/racingGameController.test.js @@ -1,9 +1,7 @@ import { describe, test, expect, vi, beforeEach } from 'vitest'; -import { - RacingGame, - RacingGameController, -} from '../domains/racingGame/index.js'; +import RacingGame from '../domains/racingGame/RacingGame.js'; +import RacingGameController from '../domains/racingGame/RacingGameController.js'; const mockMiniGame = { PvC: null,