본문 바로가기

JavaScript

지뢰찾기 | ZeroCho

반응형

💣 JavaScript  |  지뢰찾기 미니 게임 만들기

 

 

1. 지뢰찾기 테이블 만들기

 

실행버튼 클릭시, input의 value를 받아서 사용자가 원하는 값만큼 테이블이 만들어진다.

 

parseInt를 활용하여 받아온 value 문자를 숫자로 바꿔준다.

 

- 2중 반복문을 만들어서 테이블을 만든다.

 

document.querySelector("#exec").addEventListener("click",function () {
    const hor = parseInt(document.querySelector("#hor").value);
    const ver = parseInt(document.querySelector("#ver").value);
    const mine = parseInt(document.querySelector("#mine").value);
    console.log(hor,ver,mine);

    let dataSet = [];
    const tbody = document.querySelector("#table tbody");
    for(i = 0; i < hor; i++){
        let arr = [];
        dataSet.push(arr);
        for(j = 0; j < ver; j++){
            arr.push(1)
        }
    }
    console.log(dataSet);
})

//0: (10) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
1: (10) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
2: (10) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
3: (10) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
4: (10) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
5: (10) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
6: (10) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
7: (10) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
8: (10) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
9: (10) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

 

 

- appendChild를 이용해 tbody에 html을 넣는다.

 

    let dataSet = [];
    const tbody = document.querySelector("#table tbody");
    tbody.innerHTML = '';
    for(i = 0; i < hor; i++){
        let arr = [];
        let tr = document.createElement('tr');
        dataSet.push(arr);
        for(j = 0; j < ver; j++){
            arr.push(1);
            let td = document.createElement('td');
            tr.appendChild(td);
        }
        tbody.appendChild(tr);
    }
    console.log(dataSet);

 

- data와 화면을 따로 생각하되, 둘을 일치시키는 작업(= 복잡하고 번거롭..)이 필요하다. 

(화면구성부분만 신경썼는데 data 일치작업까지 해야된다는 점은 생각을 못했다.)

=> 리액트, 앵귤러, 뷰와 같은 프레임워크를 사용하면 데이터와 화면을 일치시키는 작업이 수월하다.

 

 

2. 지뢰 위치 랜덤 뽑기

 

이전에 로또 추첨기 만들때 랜덤으로 숫자를 생성한 배열을 만들었다.

해당 코드를 이용해서, 지뢰의 위치를 랜덤으로 뽑았다.

 

    let 후보 = Array(hor*ver) //가로*세로 값 만큼의 후보군 뽑기
    .fill()
    .map(function (item, index) {
        return index;
    });
    console.log(후보);

    let 셔플 = [];
    while (후보.length > 80) {
    let 이동값 = 후보.splice(Math.floor(Math.random() * 후보.length), 1)[0];
    셔플.push(이동값);
    }
    console.log(셔플);
    
    //(20) [69, 73, 11, 59, 93, 14, 26, 60, 24, 81, 67, 89, 12, 36, 30, 9, 83, 13, 19, 53]

 

- 이전에 작성해둔 80은 10*10의 지뢰 20개를 제한 80개로 작성해두었다. 지뢰는 20개가 필요한데, 굳이 반복문을 100번 돌릴 필요가 없기 때문이다.

 

 

3. 테이블에 지뢰 심기

 

랜덤으로 뽑은 지뢰의 위치를 각각 세로, 가로의 인덱스를 찾아서 지뢰를 심을 것이다.

 

    for(k=0; k< 셔플.length; k++){
        let 세로 = Math.floor(셔플[k] / 10);
        let 가로 = 셔플[k] % 10;
        console.log(세로,가로);
        tbody.children[세로].children[가로].innerHTML = 'X'
        dataSet[세로][가로].innerHTML = 'X'
    }

 

(20) [73, 45, 11, 59, 93, 14, 26, 60, 24, 81, 67, 89, 12, 36, 30, 9, 83, 13, 19, 53]

위 배열의 요소값을 지뢰의 위치라 생각하고 index를 찾아내야 한다.

 

ex) 73 => 8번째줄, 4번째칸에 지뢰가 심어진다. (세로,가로 값 모두 0부터 시작)

 

 


+) 수정한 코드

 

while (후보.length > hor*ver-mine)

 

- 현재 강의에서는 편의성을 위해서 지뢰가 20개라고 value를 고정하여 코드를 작성했다. 하지만 게임실행시에는 value값을 정해두지 않을테니 원하는 지뢰 갯수대로 테이블에 심어질 수 있도로 코드를 수정하였다.

 

let 세로 = Math.floor(셔플[k] / ver);
let 가로 = 셔플[k] % hor;

 

- 위 수정 이유와 마찬가지로 지뢰갯수가 바뀌기 때문에, 10으로 숫자를 고정한다는 것은 사용자가 가로, 세로 값을 10으로만 작성해야 진행되는 코드이다. 때문에 사용자가 원하는 가로, 세로값 기준으로 지뢰를 심을 수 있도록 코드를 수정하였다.

 


 

 

4. 우클릭으로 깃발 꼽기

 

td.addEventListener("contextmenu",function (e) {
 e.preventDefault();
 //몇번째 줄, 칸인지 확인하기
 let 부모tr = e.currentTarget.parentNode;
 let 부모tbody = e.currentTarget.parentNode.parentNode;
 let 줄 = Array.prototype.indexOf.call(부모tr.children, e.currentTarget);
 let 칸 = Array.prototype.indexOf.call(부모tbody.children, 부모tr);
 console.log(줄, 칸, e.currentTarget);
 e.currentTarget.textContent = "!"
 dataSet[칸][줄] = "!"
})

 

 

- contextmenu = 마우스 오른쪽 클릭시 나오는 메뉴, e.preventDefault( )로 우클릭방지

- parentNode 사용하여 부모요소 찾기

- data와 화면 일치 시키기 

 

 


Array.prototype.indexOf.call(부모대상, td);

indexOf를 사용하여 나의 위치를 찾고 싶은데 못쓰는 대상에게 강제로 사용할 수 있도록 해줌

 


 

+) 클로저, 프로토타입, 스코프 공부할 것

 

 

4. 클릭시 지뢰 터지거나, 주변 지뢰 갯수 알려주기

 

td.addEventListener("click",function(e){
 let 부모tr = e.currentTarget.parentNode;
 let 부모tbody = e.currentTarget.parentNode.parentNode;
 let 줄 = Array.prototype.indexOf.call(부모tr.children, e.currentTarget);
 let 칸 = Array.prototype.indexOf.call(부모tbody.children, 부모tr);
 if(dataSet[칸][줄] === "X"){
   e.currentTarget.textContent = "펑";
 }else{
   let 주변 = [dataSet[칸][줄-1], dataSet[칸][줄+1]];
   if(dataSet[칸-1]){
    주변 = 주변.concat([dataSet[칸-1][줄-1], dataSet[칸-1][줄], dataSet[칸-1][줄+1]]);
   }
   if(dataSet[칸+1]){
     주변 = 주변.concat([dataSet[칸+1][줄-1], dataSet[칸+1][줄], dataSet[칸+1][줄+1]]);
   }
   e.currentTarget.textContent = 주변.filter(value => value === "X").length
 }
})

 

 


concat() 메서드는 인자로 주어진 배열이나 값들을 기존 배열에 합쳐서 새 배열을 반환한다. 

filter() 메서드는 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환한다.

 


 

5. 0 클릭시, 주변 8칸 open

 

재귀함수 = 반복문을 함수로 표현하는 방법 - 계속 무한반복이 되면 안되니까 멈출수 있는 코드도 작성

!! - 배열에서 undifined나 null을 제거한다

 

if(주변지뢰개수 === 0){
    let 주변칸 = [tbody.children[칸].children[줄-1],tbody.children[칸].children[줄+1]];
    if(tbody.children[칸-1]){
        주변칸 = 주변칸.concat([tbody.children[칸-1].children[줄-1],
            tbody.children[칸-1].children[줄],
            tbody.children[칸-1].children[줄+1]])
    }
    if(tbody.children[칸+1]){
        주변칸 = 주변칸.concat([tbody.children[칸+1].children[줄-1],
            tbody.children[칸+1].children[줄],
            tbody.children[칸+1].children[줄+1]])
    }
    주변칸.filter(function(v){
        return !!v;
    }).forEach(function (옆칸) {
        let 부모tr = 옆칸.parentNode;
        let 부모tbody = 옆칸.parentNode.parentNode;
        let 옆칸칸 = Array.prototype.indexOf.call(부모tr.children, 옆칸);
        let 옆칸줄 = Array.prototype.indexOf.call(부모tbody.children, 부모tr);   
        if(dataSet[옆칸줄][옆칸칸] !==1){
            옆칸.click();
        }         
    });
    }

 

 

6. 에러 잡아내기

 

1. 지뢰가 터지면 게임이 끝난다

플래그 = 코드의 흐름을 좌우하는 변수를 가리킨다.

- 플래그를 false로 변수를 정해두고, true가 될시 return이 되어 함수를 바로 종료한다.

 

let 중단플래그 = false;

//클릭시 함수에 넣어줌
if(중단플래그){
    return;
}

//지뢰 클릭시, 중단플래그 true

 

 

2. 지뢰를 제외한 칸이 모두 열렸다면 게임 성공

 

//열은칸 count, 지뢰 제외 전부 open시 승리
if(dataSet[칸][줄] === 1){
    열은칸+=1;
    console.log(열은칸)
    if(열은칸 === hor*ver-mine){
        중단플래그 = true;
        result.textContent = "Victory!"
    }
}