diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md index e69de29b..155c7544 100644 --- a/docs/REQUIREMENTS.md +++ b/docs/REQUIREMENTS.md @@ -0,0 +1,21 @@ +1. 1 이상 50 이하의 랜덤한 숫자를 만든다. + +2. 랜덤 숫자를 저장한다. + +3. 사용자로부터 숫자를 입력 받는다. + +- 검증을 통과한 사용자의 입력 값이 들어온다면 랜덤 숫자와 비교한다. + +4. 검사를 통과하지 못하면 다시 입력하도록 하게 한다. + +- 1에서 50까지의 숫자만 입력 받고, 공백이나 소수도 허용하지 않도록 유효성 검사를 함 + +5. 사용자의 이전 입력 값은 저장한다. + +- 매 시도마다 "이전 추측: 저장된 입력 값들" 형태로 출력 + +6. 시도가 5회 초과되면 초과됐다는 메시지와 함께 게임을 종료하고 다시 시작할지 묻는다. + +7. 결과 중 정답일 경우 게임을 종료하고 게임을 다시 시작할지 여부를 묻는다. + +- yes면 게임 다시 진행, no면 전체 게임을 종료한다. (yes 혹은 no 이외의 대답이 돌아올 경우 yes 혹은 no로만 대답할 수 있다는 메시지와 함께 다시 입력하도록 하게 함) diff --git a/package.json b/package.json index b699f0c5..8ddbdf7a 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,6 @@ "devDependencies": { "esbuild": "^0.21.1", "eslint": "^8.57.0", - "prettier": "^3.2.5" + "prettier": "^3.4.2" } } diff --git a/src/index.js b/src/index.js index e69de29b..780ebf7c 100644 --- a/src/index.js +++ b/src/index.js @@ -0,0 +1,110 @@ +import { readLineAsync } from "./input.js"; + +const getRandomNumber = (startNum, endNum) => { + const randomNumber = Math.floor(Math.random() * endNum) + startNum; + + return randomNumber; +}; + +const gameState = { + randomNumber: getRandomNumber(1, 50), + attempts: 0, + userInput: [], +}; + +const resetGameState = () => { + gameState.randomNumber = getRandomNumber(1, 50); + gameState.attempts = 0; + gameState.userInput = []; +}; + +const validateInputValue = (inputValue) => { + const inputNumber = Number(inputValue); + const isNotNumber = Number.isNaN(inputNumber); + const isNotInteger = !Number.isInteger(inputNumber); + const isOutOfRange = inputNumber < 1 || inputNumber > 50; + + if (isNotNumber || isNotInteger || isOutOfRange) { + throw new Error("1에서 50사이의 정수 값만 입력해주세요"); + } + + return inputNumber; +}; + +const handleGameResult = ({ isCorrect, randomNumber, attempts }) => { + if (isCorrect) { + console.log(`정답! ${attempts}번 만에 숫자를 맞추셨습니다.`); + return true; + } + + if (attempts >= 5) { + console.log(`5회 초과! 숫자를 맞추지 못했습니다. (정답: ${randomNumber})`); + return true; + } + + return false; +}; + +const displayHint = (userNumber, randomNumber, userGuesses) => { + console.log(userNumber > randomNumber ? "다운" : "업"); + console.log(`이전 추측: ${userGuesses.join(", ")}`); +}; + +async function play() { + console.log("컴퓨터가 1~50 사이의 숫자를 선택했습니다. 숫자를 맞춰보세요."); + + while (true) { + try { + const inputValue = await readLineAsync("숫자 입력: "); + const validNumber = validateInputValue(inputValue); + const isCorrect = validNumber === gameState.randomNumber; + + gameState.attempts++; + gameState.userInput.push(validNumber); + + const gameFinished = handleGameResult({ + isCorrect, + randomNumber: gameState.randomNumber, + attempts: gameState.attempts, + }); + + if (gameFinished) break; + + displayHint({ + userNumber: validNumber, + randomNumber: gameState.randomNumber, + userGuesses: gameState.userInput, + }); + } catch (error) { + console.log(error.message); + continue; + } + } +} + +async function askToPlayAgain() { + while (true) { + const answer = await readLineAsync("게임을 다시 시작하시겠습니까? (yes/no): "); + const lowerAnswer = answer.toLowerCase(); + + if (lowerAnswer === "yes") { + return true; + } + if (lowerAnswer === "no") { + return false; + } + + console.log("yes 또는 no만 입력해주세요."); + } +} + +async function upAndDownGame() { + do { + resetGameState(); + await play(); + } while (await askToPlayAgain()); + + console.log("게임을 종료합니다."); +} + +upAndDownGame(); diff --git a/src/input.js b/src/input.js new file mode 100644 index 00000000..e866e4e4 --- /dev/null +++ b/src/input.js @@ -0,0 +1,23 @@ +import readline from "readline"; + +export function readLineAsync(query) { + return new Promise((resolve, reject) => { + if (arguments.length !== 1) { + reject(new Error("arguments must be 1")); + } + + if (typeof query !== "string") { + reject(new Error("query must be string")); + } + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + rl.question(query, (input) => { + rl.close(); + resolve(input); + }); + }); + } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 99d3d6a6..03d13a3f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -697,10 +697,10 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@^3.2.5: - version "3.2.5" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368" - integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== +prettier@^3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.2.tgz#a5ce1fb522a588bf2b78ca44c6e6fe5aa5a2b13f" + integrity sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ== punycode@^2.1.0: version "2.3.1"