diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..9f69f474 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,40 @@ +## 구현이 필요한 기능 + +### 컴퓨터가 생각할 숫자 결정하기 +> 1부터 9사이의 세 개의 숫자를 임의로 결정해야한다. +- [MissionUtils 라이브러리](https://github.com/woowacourse-projects/javascript-mission-utils#mission-utils) 의 Random.pickNumberInRange를 사용해 구한다. +```js +Random.pickNumberInRange(1, 10); // 1 +``` + +### 유저가 제시한 숫자를 판정하기 +> 입력한 값을 컴퓨터가 생각한 숫자와 비교하여 힌트 또는 정답을 출력한다. +- 입력된 숫자가 형식을 만족하는지 확인하는 `validateNumber` 메서드 +- **3개의 숫자** 형식을 만족하는 경우 + - 같은 수가 같은 자리에 있으면 `스트라이크` + - 같은 수가 다른 자리에 있으면 `볼` + - 같은 수가 없으면 `낫싱` + - 정확히 일치하는 경우엔 게임을 종료하고 재시작 버튼을 노출시킨다. +- 형식을 만족하지 않는 경우 + - `alert`로 에러 메시지를 보여주고, 다시 입력하도록 한다. + - 잘못된 값이 입력되어 있던 폼은 비워준다. + +### 현재 상태를 결과 영역에 표시하기 +> 현재 상태에 따라 결과 영역에 적절한 값을 출력한다. +- 유저가 **오답**을 입력한 경우 + - 스트라이크, 볼, 낫싱의 결과를 결과 영역에 출력한다. +- 유저가 **정답**을 입력한 경우 + - 축하 메시지와 함께 게임을 재시작 할 수 있는 버튼을 노출한다. +- 유저가 재시작 버튼을 클릭한 경우 + - 결과 영역에 출력된 메시지들을 지운다. + +### 게임 재시작하기 +> 게임을 재시작 할 수 있도록 현재 상태와 정답을 초기화 시킨다. +- **[컴퓨터가 생각할 숫자 결정하기]** 에서 만든 메서드를 통해 정답을 초기화 한다. +- **[현재 상태를 화면에 표시하기]** 에서 만든 메서드를 통해 결과 영역의 메시지들을 지운다. + +### 리팩토링 하기 +> MVC 패턴을 기반으로 프로젝트 구조를 변경한다. +- Model 구성하기 +- View 구성하기 +- Controller 구성하기 diff --git a/index.html b/index.html index dd1bc05c..ef45f423 100644 --- a/index.html +++ b/index.html @@ -13,7 +13,7 @@

⚾ 숫자 야구 게임

올바른 예) 139
틀린 예) 122

-
+
@@ -21,6 +21,6 @@

📄 결과

1볼 1스트라이크
- + diff --git a/src/constants/constants.js b/src/constants/constants.js new file mode 100644 index 00000000..9bdfec29 --- /dev/null +++ b/src/constants/constants.js @@ -0,0 +1,10 @@ +export const Messages = { + nothing: "낫싱", + ball: "볼", + strike: "스트라이크", + correct: "🎉 정답을 맞추셨습니다! 🎉" +}; + +export const HTML = { + result: "

" + Messages.correct + "

" + "게임을 새로 시작하시겠습니까? " +}; diff --git a/src/game/baseballGame.js b/src/game/baseballGame.js new file mode 100644 index 00000000..36cb8927 --- /dev/null +++ b/src/game/baseballGame.js @@ -0,0 +1,45 @@ +import judgeInputNumber from "../utils/judgeInputNumber.js"; + +function uniqueNumberPush(computerNumber, randomNumber) { + if(!computerNumber.includes(randomNumber)) { + computerNumber.push(randomNumber); + } +} + +function generateNumber(computerNumber) { + while(computerNumber.length < 3) { + const randomNumber = MissionUtils.Random.pickNumberInRange(1, 9); + uniqueNumberPush(computerNumber, randomNumber); + } +} + +class BaseballGame { + state = {}; + + constructor() { + this.state.computerNumber = []; + this.state.gameState = true; + } + + setComputerNumber() { + generateNumber(this.state.computerNumber); + } + + getState() { + return this.state; + } + + play(userInputNumber) { + judgeInputNumber(this.state, userInputNumber); + if(this.state.strikes === 3) this.state.gameState = false; + return this.state; + } + + replay() { + this.state.gameState = true; + this.state.computerNumber = []; + this.setComputerNumber(); + } +} + +export default BaseballGame; diff --git a/src/index.js b/src/index.js new file mode 100644 index 00000000..c16d283a --- /dev/null +++ b/src/index.js @@ -0,0 +1,46 @@ +import BaseballGame from "./game/baseballGame.js"; +import validateInputNumber from "./utils/validateInputNumber.js"; +import GameResult from "./view/GameResult.js"; + +const baseballGame = new BaseballGame(); +const gameResult = new GameResult("#result"); +const submitButton = document.getElementById("submit"); +const userInput = document.getElementById("user-input"); + +function playGame() { + if(validateInputNumber(userInput.value)) { + baseballGame.play(userInput.value); + gameResult.render(baseballGame.getState()); + addReplayEvent(baseballGame.getState().gameState); + }else{ + alert("잘못된 값을 입력하였습니다!"); + clearUserInput(); + } +} + +function replayGame() { + const replayButton = document.getElementById("game-restart-button"); + + replayButton.removeEventListener("click", replayGame); + baseballGame.replay(); + gameResult.render("reset"); + clearUserInput(); +} + +function addReplayEvent(state) { + if(!state) { + const replayButton = document.getElementById("game-restart-button"); + replayButton.addEventListener("click", replayGame); + } +} + +function clearUserInput() { + userInput.value = ""; +} + +function init() { + baseballGame.setComputerNumber(); + submitButton.addEventListener("click", playGame); +} + +init(); diff --git a/src/utils/judgeInputNumber.js b/src/utils/judgeInputNumber.js new file mode 100644 index 00000000..ff85487d --- /dev/null +++ b/src/utils/judgeInputNumber.js @@ -0,0 +1,23 @@ +function isStrike(computerInputNumbers, userInputNumbers, index) { + return computerInputNumbers[index] === Number(userInputNumbers.charAt(index)); +} + +function isBall(computerInputNumbers, userInputNumbers, index) { + return computerInputNumbers.includes(Number(userInputNumbers.charAt(index))); +} + +function judgeInputNumber(state, userInputNumbers) { + state.strikes = 0; + state.balls = 0; + console.log(state.computerNumber, userInputNumbers); + + for(let i = 0; i < 3; i++) { + if(isStrike(state.computerNumber, userInputNumbers, i)) { + state.strikes++; + }else if(isBall(state.computerNumber, userInputNumbers, i)) { + state.balls++; + } + } +} + +export default judgeInputNumber; diff --git a/src/utils/validateInputNumber.js b/src/utils/validateInputNumber.js new file mode 100644 index 00000000..95b8aa5e --- /dev/null +++ b/src/utils/validateInputNumber.js @@ -0,0 +1,18 @@ +function isThreeDigits(inputNumber) { + return inputNumber.length === 3; +} + +function isDuplicate(inputNumber) { + const firstDigit = inputNumber.charAt(0); + const secondDigit = inputNumber.charAt(1); + const thirdDigit = inputNumber.charAt(2); + return firstDigit === secondDigit || secondDigit === thirdDigit || thirdDigit === firstDigit; +} + +function validateInputNumber(inputNumbers) { + if(!Number.isInteger(Number(inputNumbers))) return false; + if(!isThreeDigits(inputNumbers)) return false; + return !isDuplicate(inputNumbers); +} + +export default validateInputNumber; diff --git a/src/view/GameResult.js b/src/view/GameResult.js new file mode 100644 index 00000000..89f0c9c3 --- /dev/null +++ b/src/view/GameResult.js @@ -0,0 +1,37 @@ +import { Messages, HTML } from "../constants/constants.js"; + +class gameResult { + + constructor(target) { + this.target = document.querySelector(target); + } + + template(state) { + let msg = ""; + + if(state !== "reset") { + if (state.strikes === 0 && state.balls === 0) { + msg = Messages.nothing; + } else if (state.strikes === 3) { + msg = HTML.result; + } else { + if (state.balls > 0) { + msg += state.balls + Messages.ball + " "; + } + + if (state.strikes > 0) { + msg += state.strikes + Messages.strike; + } + } + } + console.log(msg); + return `

${msg}

`; + } + + render(state) { + console.log(this.target.innerHTML); + this.target.innerHTML = this.template(state); + } +} + +export default gameResult;