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,
+ })
+ );