잿꽃's posting Garden

자바스크립트로 숫자야구 게임 만들기 본문

WEB/JAVASCRIPT

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

잿꽃 2022. 5. 4. 22:38

 

자바스크립트로 숫자야구 게임을 만들어보았다.

 

사담 : 'let/'s get it 자바스크립트'라는 책에 자바스크립트를 연습하기 위한 여러가지 다양한 게임이 있어서 메소드를 복습하면서 새로운 메소드를 익히고 있다. 책을 보고 게임의 구조의 흐름을 대강 파악한 뒤 책을 보지 않고 새로 작성해보는 것이라 책에 나온 코딩과 다를 수 있습니다.

 

 

순서도

1. 랜덤한 4개의 숫자를 얻는다.

2. 사용자가 입력창에 4자리를 입력한다.

3. 사용자가 입력한 숫자가 4자리 인지 검사한다. 4자리가 맞으면 숫자가 중복되지 않는지 검사한다.

4. 숫자가 중복되지 않으면 랜덤하게 얻은 결과 값을 비교해서 얼마나 맞췄는지 알려준다.

 

 

아주 간단하게 입력창과 버튼을 html 만들었다.

<!--/////////////////////////html/////////////////////////-->
<!DOCTYPE html>
<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>number-baseball</title>
    <script defer src="script.js"></script>
</head>
<body>
    <form id="form">
        <input type="text" id="input-num" minlength="4" maxlength="4">
        <!-- 
            pattern : 정규표현식으로 입력값을 검사 > 중복되지 않은 4자리 
            (아직 정규표현식 다 익히지 못했음, 추후에 구현)
        -->
        <button>확인</button>
    </form>
    <div><span id="result"></span></div>
</html>

css는 안 만들었다.... 일단 블로그에 만든 내용을 올리고 git에 올릴 때 css로 꾸며서 올릴 예정이다.

(꾸밀생각하니 벌써부터 걱정....)

 

 

1. 랜덤한 4자리를 얻는다.

랜덤한 4개의 숫자를 결과값으로 지정하기 위한 목적

- 4개의 숫자가 중복되는 값이 되면 안되므로 그냥 랜덤 값을 변수에 넣으면 안되었다.

- 1~9의 숫자가 정해진 상태에서 index의 값을 랜덤으로 해서 중복되지 않는 값을 얻을 수 있다.

//////////////////javascript//////////////////

const formSubmit = document.querySelector('#form');
const inputText = document.querySelector('#input-num');
const resultText = document.querySelector('#result');


let userNum;
// 9자리 배열 만들고 정의하지 않고 채움
// map에 return 한 값을 배열에 채움
const totalNum = Array(9).fill().map((value, index)=> index+1);
// 초기 결과값 없음
let resultNum = [];

- Array()를 통해 9자리의 배열을 만든다. fill()은 만든 배열의 값을 모두 정의되지 않는 undefined로 채운다. 그리고 9자리 배열이 만들어 졌기 때문에 map()을 통해 index가 0~8의 값을 조회할 수 있고 1~9의 값을 얻기 위해 +1한 값을 리턴한다. 리턴한 값이 totalNum의 배열에 차례로 들어가게 된다.

 

//////////////////javascript//////////////////

const formSubmit = document.querySelector('#form');
const inputText = document.querySelector('#input-num');
const resultText = document.querySelector('#result');


let userNum;
// 9자리 배열 만들고 정의하지 않고 채움
// map에 return 한 값을 배열에 채움
const totalNum = Array(9).fill().map((value, index)=> index+1);
// 초기 결과값 없음
let resultNum = [];

// 4자리 숫자를 선정하기 위한 반복문
for(let i = 0; i < 4; i++){
    // 랜덤으로 index에 들어갈 1~9의 값이 필요
    // let indexNum = Math.floor(Math.random()*9);이었으나 totalNum의 값을 
    //splice로 값을 제거하므로 index가 줄어서 indexNum의 값도 같이 감소하여야 함
    let indexNum = Math.floor(Math.random()*totalNum.length);
    // 랜덤 index로 totalNum의 값을 얻어 결과배열에 넣는다.
    resultNum.push(totalNum[indexNum]);
    // 값이 중복되지 않게 뽑은 숫자를 제거한다.
    totalNum.splice(indexNum, 1);
    console.log(resultNum);
}

- resultNum의 초기값은 빈 배열로 두었다.

 

- totalNum의 값은 정해졌고 index의 랜덤한 값을 얻기 위해 변수를 만들었다.

Math.floor(Math.random()*9)를 하면 0~8의 값을 얻을 수 있다. index의 값은 0부터 시작이니 그대로 사용하면 된다.

ex) totalNum[0] = 1, totalNum[8] = 9

totalNum[랜덤인덱스]를 결과 값으로 push한다. 중복된 숫자를 빼기 위해서 해당하는 인덱스 자리 값을 splice()로 제거한다.

>> Math.random() : 무작위의 값을 산출

>> Math.floor() : 내림 값을 얻음

>> splice(시작 위치, 제거 개수, 추가/대체할 요소) : 제거할 개수가 없으면 0

 

- splice()로 제거하면 totalNum배열의 개수가 줄어든다. 1회차에 배열의 길이는 9개에서 8으로 줄게된다. 만약 Math.random()*9라는 랜덤인덱스 값이 8이 나왔다면 totalNum[8]는 undefined가 나오게 된다.

ex) 0번의 값(1)이 제거되었을 경우 totalNum[0] = 2, totalNum[7] = 9,  totalNum[8] = undefined

결론적으로 splice로 제거된 값이 랜덤인덱스 값에 영향을 미쳐야 한다. 마침 totalNum의 배열길이가 줄어들고 있으므로 *9대신에 *totalNum.length을 넣는다면 랜덤한 값의 최대값이 같이 줄어들게 된다.

 

- for문으로 4회 반복하면 4가지의 같지 않은 숫자를 얻을 수 있다.

 

 

2. 사용자가 입력창에 4자리를 입력한다.

사용자가 입력한 값을 변수에 담기 위한 목적

- change이벤트를 통해 현재 타겟의 value의 값을 감지할 수 있다. 사용자가 입력한 input의 값이 userNum의 변수에 들어가게 한다. 또한 값이 들어가게 되면 value의 값을 화면 그대로 남겨두지 않기 위해 빈 값을 넣어준다.

//////////////////javascript//////////////////

const formSubmit = document.querySelector('#form');
const inputText = document.querySelector('#input-num');
const resultText = document.querySelector('#result');


let userNum;
// 9자리 배열 만들고 정의하지 않고 채움
// map에 return 한 값을 배열에 채움
const totalNum = Array(9).fill().map((value, index)=> index+1);
// 초기 결과값 없음
let resultNum = [];


// 4자리 숫자를 선정하기 위한 반복문
for(let i = 0; i < 4; i++){
    // 랜덤으로 index에 들어갈 1~9의 값이 필요
    // let indexNum = Math.floor(Math.random()*9+1);이었으나 totalNum의 값을 
    //splice로 값을 제거하므로 index가 줄어서 indexNum의 값도 같이 감소하여야 함
    let indexNum = Math.floor(Math.random()*totalNum.length);
    // 랜덤 index로 totalNum의 값을 얻어 결과배열에 넣는다.
    resultNum.push(totalNum[indexNum]);
    // 값이 중복되지 않게 뽑은 숫자를 제거한다.
    totalNum.splice(indexNum, 1);
    console.log(resultNum);
}

// input의 value값을 감지하기 위함
inputText.addEventListener('change',(e)=>{
    userNum = e.target.value;
    // 값 초기화
    e.target.value = '';
})

 

3. 사용자가 입력한 숫자가 4자리 인지 검사한다. 4자리가 맞으면 숫자가 중복되지 않는지 검사한다.

값을 비교하기 전에 사용자가 알맞는 값을 넣었는지 확인하기 위한 목적

- form요소에 submit이벤트를 이용하면 input상자에서 enter를 하거나 버튼을 클릭하면 동일하게 이벤트가 발생하게 된다.

값을 입력해서 제출하면 화면이 재생성이 되기 때문에 결과값을 얻을 수 없다. 재생성을 방지하는 메소드를 작성한다.

// form태그 요소가 있을 때 submit으로 enter기능과 버튼 click기능이 동일하게 작동하게 함
formSubmit.addEventListener('submit', (e)=>{
    // 제출 시 화면 재생성 방지
    e.preventDefault();
})

 

- 먼저 유저가 4자리를 알맞게 입력했는지 확인한다. html에 이미 최소, 최대 길이를 4자리로 지정해서 어차피 4자리 밖에 못 받지만 더 정확하게 확인하는 작업이 될 것이다.

(참고로 숫자만 입력하게 하려면 input의 pattern값을 정규표현식으로 제한을 둘 수 있다. 하지만 나는 아직 정확하게 익히지 못하였기 때문에 좀 더 능숙하게 사용할 수 있을 때 작성하는 것이 좋을 것이라 판단했다.)

- 유저가 입력한 4자리에 중복된 숫자를 거르기 위해 내장 객체인 Set()을 이용하였다.

> new Set() :  중복된 값을 제외한 값이 나온다.

> new Set().size : 반환되는 값의 길이를 얻을 수 있다(length가 아님)

size한 값이 4가 나오지 않는다면 중복되는 숫자가 있어서 길이가 3,2,1로 나오게 된다.

//////////////////javascript//////////////////

const formSubmit = document.querySelector('#form');
const inputText = document.querySelector('#input-num');
const resultText = document.querySelector('#result');


let userNum;
// 9자리 배열 만들고 정의하지 않고 채움
// map에 return 한 값을 배열에 채움
const totalNum = Array(9).fill().map((value, index)=> index+1);
// 초기 결과값 없음
let resultNum = [];

// 결과 일치 변수, 스트라이크와 볼의 개수를 나타낼 것임
let strike = 0;
let ball = 0;

// 4자리 숫자를 선정하기 위한 반복문
for(let i = 0; i < 4; i++){
    // 랜덤으로 index에 들어갈 1~9의 값이 필요
    // let indexNum = Math.floor(Math.random()*9+1);이었으나 totalNum의 값을 
    //splice로 값을 제거하므로 index가 줄어서 indexNum의 값도 같이 감소하여야 함
    let indexNum = Math.floor(Math.random()*totalNum.length);
    // 랜덤 index로 totalNum의 값을 얻어 결과배열에 넣는다.
    resultNum.push(totalNum[indexNum]);
    // 값이 중복되지 않게 뽑은 숫자를 제거한다.
    totalNum.splice(indexNum, 1);
    console.log(resultNum);
}

// input의 value값을 감지하기 위함
inputText.addEventListener('change',(e)=>{
    userNum = e.target.value;
    // 값 초기화
    e.target.value = '';
})

// form태그 요소가 있을 때 submit으로 enter기능과 버튼 click기능이 동일하게 작동하게 함
formSubmit.addEventListener('submit', (e)=>{
    // 제출 시 화면 재생성 방지
    e.preventDefault();

    // 유저의 입력길이가 4일 때
    if(userNum.length === 4){
        // Set()은 중복된 데이터를 거른다. 데이터의 길이를 length가 아닌 size로 얻을 수 있음
        // 값이 중복되지 않을 때 (= 데이터 길이가 4일 때)
        if(new Set(userNum).size === 4){
            
            
            }
            resultText.innerHTML += `${userNum} <br> 스트라이크 : ${strike}, 볼 : ${ball} <br>`;
            strike = 0;
            ball = 0;
        }else{
            alert('숫자가 중복됩니다.');
        }
    }else{
        alert('4자리 숫자를 입력하세요.');
    }
})

 

 

4. 숫자가 중복되지 않으면 랜덤하게 얻은 결과 값을 비교해서 얼마나 맞췄는지 알려준다.

유저값과 결과값을 비교하여 결과를 출력하기 위한 목적

- 유저의 값에 결과 값이 포함되어 있는지 includes()를 통해 확인한다. includes()는 포함하면 true, 포함하지 않으면 false의 값을 내보낸다.

- 값이 포함되어있다면 자리까지 비교한다. 유저와 결과의 동일한 인덱스 자리에 동일한 값이 있는지 비교하였다.

- 이 작업을 for문을 통해 4회 반복한다.

//////////////////javascript//////////////////

const formSubmit = document.querySelector('#form');
const inputText = document.querySelector('#input-num');
const resultText = document.querySelector('#result');


let userNum;
// 9자리 배열 만들고 정의하지 않고 채움
// map에 return 한 값을 배열에 채움
const totalNum = Array(9).fill().map((value, index)=> index+1);
// 초기 결과값 없음
let resultNum = [];

// 결과 일치 변수, 스트라이크와 볼의 개수를 나타낼 것임
let strike = 0;
let ball = 0;

// 4자리 숫자를 선정하기 위한 반복문
for(let i = 0; i < 4; i++){
    // 랜덤으로 index에 들어갈 1~9의 값이 필요
    // let indexNum = Math.floor(Math.random()*9+1);이었으나 totalNum의 값을 
    //splice로 값을 제거하므로 index가 줄어서 indexNum의 값도 같이 감소하여야 함
    let indexNum = Math.floor(Math.random()*totalNum.length);
    // 랜덤 index로 totalNum의 값을 얻어 결과배열에 넣는다.
    resultNum.push(totalNum[indexNum]);
    // 값이 중복되지 않게 뽑은 숫자를 제거한다.
    totalNum.splice(indexNum, 1);
    console.log(resultNum);
}

// input의 value값을 감지하기 위함
inputText.addEventListener('change',(e)=>{
    userNum = e.target.value;
    // 값 초기화
    e.target.value = '';
})

// form태그 요소가 있을 때 submit으로 enter기능과 버튼 click기능이 동일하게 작동하게 함
formSubmit.addEventListener('submit', (e)=>{
    // 제출 시 화면 재생성 방지
    e.preventDefault();

    // 유저의 입력길이가 4일 때
    if(userNum.length === 4){
        // Set()은 중복된 데이터를 거른다. 데이터의 길이를 length가 아닌 size로 얻을 수 있음
        // 값이 중복되지 않을 때 (= 데이터 길이가 4일 때)
        if(new Set(userNum).size === 4){
            for(let j = 0; j < resultNum.length; j++){
                // userNum에 resultNum중 일치하는 값이 있으면
                if(userNum.includes(resultNum[j])){
                    // 자리까지 동일하면
                    if(userNum[j] == resultNum[j]){
                        strike++;
                    }else{
                        ball++;
                    }
                }
            }
            resultText.innerHTML += `${userNum} <br> 스트라이크 : ${strike}, 볼 : ${ball} <br>`;
            strike = 0;
            ball = 0;
        }else{
            alert('숫자가 중복됩니다.');
        }
    }else{
        alert('4자리 숫자를 입력하세요.');
    }
})

최종 javascript코드를 완성하였다.

 

 

 

결과화면

아무래도 알맞게 만들어지는지, 일치여부가 적절하게 나오는지 등을 확인하기 위해서 console.log창에 정답이 나오는건 안 비밀이다.....ㅎ

 

 

PS.

나는 직접 값이 포함되는지 확인하고 배열의 위치가 동일한지를 검사하였다. 책에서는 indexof()를 이용하여 indexof()의 값이 0,1,2,3라면 해당 값이 있는 것으로 검사했다. 없으면 false인 -1을 반환하기 때문에 if문에 indexof()을 작성하였다.

 for (let i = 0; i < answer.length; i++) {
    const index = value.indexOf(answer[i]);
    if (index > -1) { // 일치하는 숫자 발견
      if (index === i) { // 자릿수도 같음
        strike += 1;
      } else { // 숫자만 같음
        ball += 1;
      }
    }
  }

아직 이런 방식에는 익숙하지 않아서 내 식대로 작성하였지만 이러한 작성방법도 익혀야 할 필요성이 있을 듯하다. 

 

만들면서 if(userNum.includes(resultNum[j]))이 부분에서 시간을 많이 할애했다. userNum과 resultNum의 위치를 바꿔서 작성했기 때문인데 resultNum이 정해진 결과 값이고 기준이었기 때문에 includes()안에 작성되어야 했다. 기준값이 무엇인지 파악하고 메소드에 알맞게 넣는 연습이 더 필요해보인다.

또한 변수를 만들 때 변수의 값이 다른 값에 의해 영향을 받는다면 알맞는 스코프 내에서 정의되어야 할 필요성을 느꼈다.

 

 

number-baseball (ssoa1111.github.io)

 

number-baseball

 

ssoa1111.github.io

 

728x90
Comments