diff --git a/index.html b/index.html index 041e0300..09b05d51 100644 --- a/index.html +++ b/index.html @@ -13,8 +13,10 @@

⚾ 숫자 야구 게임

올바른 예) 139
틀린 예) 122

- - +
+ + +

📄 결과

diff --git a/src/BaseballGame.js b/src/BaseballGame.js new file mode 100644 index 00000000..6e386b1c --- /dev/null +++ b/src/BaseballGame.js @@ -0,0 +1,78 @@ +// Controller +export default class BaseballGame { + #DIGIT_LENGTH = 3; + #referee; + + constructor(referee) { + this.#referee = referee; + } + + play(computerInputNumbers, userInputNumbers) { + const isValidInput = + this.#validateInputNumbers(computerInputNumbers) && + this.#validateInputNumbers(userInputNumbers); + if (!isValidInput) return ""; + + const judgement = this.#judge(computerInputNumbers, userInputNumbers); + const result = this.#formatResult(judgement); + + return result; + } + + // TODO: 널 오브젝트 패턴?? Judgeable을 validate하는 서비스로 추출 + #validateInputNumbers(inputNumbers) { + const inputStr = String(inputNumbers); + if (!this.#validateCorrectLength(inputStr)) return false; + if (!this.#validateAllDigit(inputStr)) return false; + if (!this.#validateDuplication(inputStr)) return false; + + return true; + } + + #validateCorrectLength(input) { + return input.length === this.#DIGIT_LENGTH; + } + + #validateAllDigit(input) { + return /^\d+$/.test(input); + } + + #validateDuplication(input) { + return new Set(input).size === inputStr.length; + } + + // TODO: Judgeable 객체를 두 개를 비즈니스 로직에 따라 처리하는 Referee 서비스로 추출 + #judge(computerInputNumbers, userInputNumbers) { + const set = new Set(); + let strike = 0; + let ball = 0; + const computerInputDigits = Array.from( + String(computerInputNumbers), + Number + ); + const userInputDigits = Array.from(String(userInputNumbers), Number); + + for (let i = 0; i < computerInputDigits.length; i++) { + if (computerInputDigits[i] === userInputDigits[i]) { + strike += 1; + continue; + } + set.add(computerInputDigits[i]); + } + + for (const userInputDigit of userInputDigits) { + if (set.has(userInputDigit)) ball += 1; + } + + return { strike, ball }; + } + + // TODO: VO 객체(Judge) 내의 로직으로 분리 + #formatResult({ strike, ball }) { + if (strike === 0 && ball === 0) return "낫싱"; + if (strike === this.#DIGIT_LENGTH) return "정답을 맞추셨습니다!"; + if (strike === 0) return `${ball}볼`; + if (ball === 0) return `${strike}스트라이크`; + return `${ball}볼 ${strike}스트라이크`; + } +} diff --git a/src/index.js b/src/index.js index 1553d753..a546b0f0 100644 --- a/src/index.js +++ b/src/index.js @@ -1,13 +1,76 @@ -export default function BaseballGame() { - this.play = function (computerInputNumbers, userInputNumbers) { - return "결과 값 String"; - }; -} - -// export default class BaseballGame { -// play(computerInputNumbers, userInputNumbers) { -// return "결과 값 String"; -// } -// } - -new BaseballGame(); +import BaseballGame from "/src/BaseballGame.js"; +import { + convertDigitArrayToInt, + generateNonDuplicateRandomIntArray, +} from "/src/utils.js"; + +const generateComputerInputNumbers = () => + convertDigitArrayToInt( + generateNonDuplicateRandomIntArray({ + arraylength: 3, + digitCount: 1, + }) + ); + +const userInputEl = document.querySelector("#user-input"); +const submitBtnEl = document.querySelector("#submit"); +const resultEl = document.querySelector("#result"); + +const baseballGame = new BaseballGame(); +let computerInputNumbers = generateComputerInputNumbers(); + +const resetUserInput = () => { + userInputEl.disabled = false; + userInputEl.value = null; + userInputEl.focus(); +}; + +const resetResultEl = () => { + resultEl.innerHTML = ""; +}; + +const showWinningResult = () => { + const strongEl = document.createElement("strong"); + strongEl.textContent = result; + resultEl.textContent = ""; + resultEl.appendChild(strongEl); + + const restartMention = document.createElement("span"); + restartMention.textContent = "게임을 새로 시작하시겠습니까?"; + + const restartButtonEl = document.createElement("button"); + restartButtonEl.textContent = "게임 재시작"; + restartButtonEl.addEventListener("click", handleRestart); + + const restartWrapper = document.createElement("div"); + restartWrapper.appendChild(restartMention); + restartWrapper.appendChild(restartButtonEl); + + resultEl.appendChild(restartWrapper); +}; + +const handleRestart = (e) => { + resetUserInput(); + resetResultEl(); + computerInputNumbers = generateComputerInputNumbers(); +}; + +const handleSubmit = (e) => { + const userInputNumbers = Number(userInputEl?.value); + const result = baseballGame.play(computerInputNumbers, userInputNumbers); + + if (!result) { + alert("유효하지 않은 입력입니다."); + userInputEl.focus(); + return; + } + if (result === "정답을 맞추셨습니다!") { + userInputEl.disabled = true; + showWinningResult(); + return; + } + + resultEl.textContent = result; +}; + +submitBtnEl.addEventListener("click", handleSubmit); diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 00000000..24140575 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,27 @@ +export const convertDigitArrayToInt = (digitArr) => parseInt(digitArr.join("")); + +export const generateRandomInt = (length = 1) => { + const min = 10 ** (length - 1); + const max = 10 ** length; + + return Math.floor(Math.random() * (max - min)) + min; +}; + +export const generateFixedSizeSet = (generateValue, { size = 1 }) => { + const set = new Set(); + while (set.size < size) { + const value = generateValue(); + set.add(value); + } + return set; +}; + +export const generateNonDuplicateRandomIntArray = ({ + arraylength, + digitCount = 1, +}) => + Array.from( + generateFixedSizeSet(() => generateRandomInt(digitCount), { + size: arraylength, + }) + );