자바스크립트 숫자야구 만들기

버건디·2022년 8월 23일
0
post-thumbnail

- 숫자야구란?

  1. 상대편이 숫자 1~9 중에서 중복되지 않게 네 개의 숫자를 고른다.
  2. 10번의 기회가 주어지고 상대편이 고른 숫자 네 개를 맞히면 된다.
  3. 이때 숫자만 맞히는 것이 아니라 숫자의 순서까지 맞혀야 한다
  4. 만약에 상대방이 3 6 7 9 라는 숫자를 골랐다고 하고 내가 1 7 2 5 라는 숫자를 뽑았다고 가정한다면, 7이라는 숫자를 한개 맞췄지만 순서는 못맞췄으니 1볼이라는 힌트를 얻게 된다.
  5. 다시 6 3 7 8 이라는 숫자를 뽑았다고 가정한다면, 6과 3이라는 숫자는 맞지만 순서는 틀린 숫자 2개와, 7이라는 숫자도 맞고 순서도 맞는 숫자를 뽑은 것이므로, 1스트라이크 2볼이라는 힌트를 준다.
  6. 만약 숫자를 한개도 못맞춘다면 아웃이고 한번에 숫자를 6 3 7 8 이라고 맞춘다면 홈런이 되어서 문제를 맞추는 사람이 승리하게 된다. 10번 이내로 숫자를 못 맞추게 된다면 상대방이 승리하게 된다.

- 숫자 야구 순서도

🔍 숫자 4개 무작위로 뽑기

일단 1부터 10까지의 숫자 4개를 무작위로 뽑아서 빈 배열에 그 값들을 넣어줘야겠다고 생각했다.

document.addEventListener('DOMContentLoaded', () => {
   let answer = [];
   
   for(let i =1; i < 5; i++){
       let num = Math.floor(Math.random() * 9) +1;
       answer.push(num);
   }
})

❗️ 하지만 이렇게 됐을때, 배열에 중복값이 발생하는 문제가 생겼다. 정답에 들어가는 숫자는 중복값이 없어야했다.
1~9까지 숫자가 들어가는 배열을 하나 만들어주고, 거기서 나오는 숫자를 빼주는 식으로 코드를 짰다.

document.addEventListener('DOMContentLoaded', () => {
    
    // 정답 추출

    let answer = [];
    let numbers = [];

    for(let i =1; i <=9; i++){
        numbers.push(i);
    }
    
    
    for(let i = 0; i < 4; i++){
        let index = Math.floor(Math.random() * 9)
        answer.push(numbers[index]);
        numbers.splice(index, 1);
    }

    console.log(answer);
   
})

❗️ 하지만 이렇게 하니까 콘솔창에서 undefiend라는 값이 나왔다 왜일까?

  • 저기서 index 변수는 numbers 배열에 index로 들어가므로 만약에 랜덤으로 뽑은 숫자가 8이 나오고 또 8이 나왔을때, 그에 맞는 index값이 없으므로 undefined 값이 나온다.
// 개선 코드

document.addEventListener('DOMContentLoaded', () => {
    
    // 정답 추출

    let answer = [];
    let numbers = [];

    for(let i =1; i <=9; i++){
        numbers.push(i);
    }
    
    for(let i = 0; i < 4; i++){
        let index = Math.floor(Math.random() * numbers.length);
        answer.push(numbers[index]);
        numbers.splice(index, 1);
    }
})

위 처럼 numbers.length를 해줌으로써 index가 줄어들때 numbers 배열에 맞는 길이에서 랜덤값을 뽑도록 했다.

중복값 없이 답이 잘 나온다.

🔍 숫자 입력했을때 입력을 제대로 하지 않았다면 alert창 띄우기 그게 아니라면 결과값을 띄우기

const input = document.querySelector('#input'); // 값 넣는 인풋창
   const button = document.querySelector('#button'); // 제출 버튼
   const result = document.querySelector('#result'); // 결과창
   


   // 숫자를 제대로 입력 했는가?

   button.addEventListener('click', () => {
       let inputValue = parseInt(input.value);
       let strInputValue = inputValue.toString();
       if(isNaN(inputValue) || inputValue == ''){
           alert('숫자를 입력하세요');
       } else if (strInputValue.length !==4){
           alert('4자리 숫자를 입력하세요');
       } else if(tries.includes(inputValue)){
           alert('이미 시도한 값입니다.')
       } else {
           tries.push(inputValue);
           result.textContent = inputValue;
       }

❗️ 여기서 result창에 input값을 띄웠을때, 값이 입력되고 바로 화면이 초기화 되는 문제가 발생했는데 event.preventDefault() 를 통해서 문제를 해결할 수 있었다❗️

❓ Event.preventDefault() 란 ❓

HTML 에서 a 태그나 submit 태그는 기본적으로 가지고 있는 동작들이 있다. a태그는 클릭시에 그 href 경로로 이동을 한다거나, submit 태그을 눌렀을때 화면이 무조건 새로고침이 되는 현상이 발생하는데
Event.preventDefault() 를 통해서 그 현상들을 막아줄 수 있다.

❗️ 여기서 스트라이크와 볼을 어떻게 검사할거냐가 문제였는데, 처음에는 filter 메서드를 이용하면 되지 않을까 했다. 하지만 filter 메서드는 요소만 반환해줄뿐, index 자리값까지 검사해주지는 못하는 문제가 발생했다 ❗️

const SB = answer.filter((x) => strInputValue.includes(x));
           console.log('SB = ' + SB); // 요소만 반환함

홈런을 구현해볼수는 있지 않을까? 했지만 요소만 반환하는 것이었기 때문에, 정답이 만약 7, 8 ,1 ,2 라고 했을때 인풋값에 8 ,7, 2 ,1 을 입력해도 홈런처리가 됐기때문에 불가능 했다. 어쩔수 없이 filter 메서드 사용보다 다른 메서드를 사용할 수밖에 없었다.

🔍 홈런 구현하기

let count = 0;

       for(let i = 0; i < answer.length; i++){
           if(answer[i] == strInputValue[i]){
               count++;
           }
           if(count == 4){
               result.textContent = ' 홈런 ! ';
           }  
       }

count 변수를 선언해주고, 반복문을 돌려서 값들이 전부 다 같다면 count를 올려준다. 이 값이 4가 된다면 숫자와 인덱스값이 전부 같은 것이므로, 홈런 메세지를 띄어주었다!

🔍 N 스트라이크 N 볼 구현하기

   let strike = 0;
   let ball = 0;

   for (let i = 0; i < answer.length; i++) {
     let index = strInputValue.indexOf(answer[i]);

     if (index > -1) {
       if (index == i) {
         strike++;
       } else {
         ball++;
       }
     }
   }
result.textContent = strike + " 스트라이크 " + ball + " 볼 ";

❗️ 이렇게 하면 결과값이 줄바꿈이 되며 누적되지 않고, tries 배열에 10개의 값들이 채워지더라도 패배했다는 텍스트가 나오지 않았다 ❗️ if 문 안에 조건들을 다 넣어주고 호이스팅이 발생하지 않도록 코드 순서를 바꾸어주니 해결이 되었다.

 for (let i = 0; i < answer.length; i++) {
      let index = strInputValue.indexOf(answer[i]);
      if (answer[i] == strInputValue[i]) {
        count++;
      }

      if (index > -1) {
        if (index == i) {
          strike++;
        } else {
          ball++;
        }
      }
    }


 if (isNaN(inputValue) || inputValue == "") {
      alert("숫자를 입력하세요");
    } else if (strInputValue.length !== 4) {
      alert("4자리 숫자를 입력하세요");
    } else if (tries.includes(inputValue)) {
      alert("이미 시도한 값입니다.");
    } else if (duplicatedInput.size !== 4) {
      alert("숫자를 중복되지 않게 입력해주세요");
    } else if (count == 4) {
      result.textContent = " 홈런 ! ";
    } else {
      result.textContent = strike + " 스트라이크 " + ball + " 볼 ";
      tries.push(inputValue);
      console.log(tries);
    }

🔍 입력값과 N 스트라이크 N 볼이 창에 연속적으로 나타나게 하기

else {
      result.append(inputValue + ' : ' + strike + " 스트라이크 " + ball + " 볼 ", document.createElement('br') );
      tries.push(inputValue);
      console.log(tries);
    }

textContent 가 아닌 append 메서드를 사용함으로써 값이 연속적으로 나타나게 했고 document.createElement('br')를 추가해서 줄바꿈을 해주었다!

값이 잘 출력된다.

📌 완성 코드

<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>숫자야구</title>
    <script src="main.js"></script>
</head>

<body>
    <form id="form">
        <input type="text" id="input">
        <button id="button"> 확인 </button>
    </form>
    <div id="result"></div>
</body>
<script>
    document.addEventListener("DOMContentLoaded", () => {
        // 정답 추출

        let answer = []; // 정답
        let numbers = []; // 1~9까지 숫자
        let tries = []; // 시도한 숫자들 담을 배열

        for (let i = 1; i <= 9; i++) {
            numbers.push(i);
        }

        for (let i = 0; i < 4; i++) {
            let index = Math.floor(Math.random() * numbers.length);
            answer.push(numbers[index]);
            numbers.splice(index, 1);
        }
        console.log(answer);

        // 변수 선언

        const input = document.querySelector("#input"); // 값 넣는 인풋창
        const button = document.querySelector("#button"); // 제출 버튼
        const result = document.querySelector("#result"); // 결과창

        // 숫자를 제대로 입력 했는가?

        button.addEventListener("click", (e) => {
            e.preventDefault(); // 초기화 해결
            let count = 0;
            let strike = 0;
            let ball = 0;
            let inputValue = parseInt(input.value); // 인풋값 숫자로 변경
            let strInputValue = inputValue.toString().split("").map(Number);
            let duplicatedInput = new Set(strInputValue);

            for (let i = 0; i < answer.length; i++) {
                let index = strInputValue.indexOf(answer[i]);
                if (answer[i] == strInputValue[i]) {
                    count++;
                }

                if (index > -1) {
                    if (index == i) {
                        strike++;
                    } else {
                        ball++;
                    }
                }
            }

            //입력을 제대로 못했다면

            if (isNaN(inputValue) || inputValue == "") {
                alert("숫자를 입력하세요");
            } else if (strInputValue.length !== 4) {
                alert("4자리 숫자를 입력하세요");
            } else if (tries.includes(inputValue)) {
                alert("이미 시도한 값입니다.");
            } else if (duplicatedInput.size !== 4) {
                alert("숫자를 중복되지 않게 입력해주세요");
            } else if (count == 4) {
                result.textContent = " 홈런 ! ";
            } else {
                result.append(inputValue + ' : ' + strike + " 스트라이크 " + ball + " 볼 ", document.createElement('br'));
                tries.push(inputValue);
                console.log(tries);
            }

            // 패배 창 띄우기

            if (tries.length >= 9) {
                result.textContent = "패배! 정답은 " + answer.join("") + " 입니다.";
            }

            input.value = ""; // 인풋창 초기화
        });
    });
</script>

</html>
profile
https://brgndy.me/ 로 옮기는 중입니다 :)

0개의 댓글