관리 메뉴

샐님은 개발중

9강 틱택토 게임 - 이차원 배열, 구조분해할당,이벤트 버블링, 캡처링 본문

자바스크립트/인프런강의-렛츠기릿 자바스크립트

9강 틱택토 게임 - 이차원 배열, 구조분해할당,이벤트 버블링, 캡처링

샐님 2023. 7. 6. 17:18
728x90
반응형

1. 순서도 그리기

 

2. 구조분해 할당

        // document가 객체인데 이 안에 속성으로 들어있는 것을 변수이름과 같게 쓰면 구조분해할당을 사용할 수 있다.
        const body= document.body;
        const {
            body
        } = document;

 

// 배열에 대 구조분해 할당 예
        const arr = [1, 2, 3, 4];
        const one = arr[1];
        const two = arr[2];
        const three = arr[3];
        const four = arr[4];

        const [one, two, three, four] = arr;
        const [one, , , four] = arr;
  // 객체 리터럴 구조분해 할당 예
 
const obj = {
            a: 1,
            b: 2
        };

        const a = obj.a;
        const b = obj.b;

        const {
            a,
            b
        } = obj;
   const obj = {
            a: 'hello',
            b: {
                c: 'bye',
                d: {
                    e: 'I',
                }
            }
        }
 
// 말단에 있는 a,c,e만 변수로 할당가능 
        const {
            a,
            b {
                c,
                d {
                    e
                }
            }
        } = obj;

        const a = obj.a;
        const c = obj.b.c;
        const e = obj.b.d.e;

 

3.삼항연산자

 

  if (turn === "O") {
                turn = "X";
            } else if (turn === "X") {
                turn = "O";
            }
//위의 코드를 줄일 수 있다.

            turn = (turn === 'O' ? 'X' : 'O');

 

4. 이번트 버블링

 

addEventLisnter 를 지우고 싶을 때 
removeEventLisnter 로 지워주는 것이 좋음. 
그러나 만약 여러개를 지워야할때는 그 숫자만큼 removeEventLisnter 를 만들어줘야함
-> 이벤트 버블링 을 쓰면 한방에 해결가능

 

* td가아니라 테이블의 이벤트 리스너를 호출하고 싶을때

event.currenttarget  // 정확하게 이벤트리스너를 추가했던 태그에만 쓸수 있음.
event.target // 하위 태그들중 하나
    // 이벤트버블링 현상 방지
            event.stopPropagation();

      // 태그의 기본 동작 방지
            event.preventDefault();

 

5. 캡처링

  - 부모로 부터 자식태그가 이벤트를 받을 때 

  예) 팝업창 바깥 클릭시 팝업 창닫을 때

 

6. prentNode, cellIndex

 

  // let rowIndex;
            // let cellIndex;
            // console.log("target ", target, turn);
            // //forEach 는 index 를 가지고 있어서 편리하다.
            // rows.forEach((row, ri) => {
            //     row.forEach((cell, ci) => {
            //         if (cell === target) {
            //             // console.log("맞음", ri, ci);
            //             rowIndex = ri;
            //             cellIndex = ci;
            //         }
            //     })
            // })

            // 간단하게 해당 row,cell 인덱스 찾기
            const rowIndex = target.parentNode.rowIndex;
            const cellIndex = target.cellIndex;

 

일차원배열이면 every(),some()을 사용할 수 있다. 

 

flat() : 고차원 함수를 1차원 함수로 변환해준다. 

every() : 배열중 해당 조건에 전체가 만족하는지 검사한다.

some(): 배열중 해당 조건에 1개라도 만족하는 지 검사한다. 

 

 

6. 셀프체크 - 컴퓨터와 게임하기

 

  - 내가 짠 코드

 

  //O가 내차례 X가 컴퓨터 차례
            if (turn === 'X') {
                setTimeout(() => {
                        //rows에서 빈칸에서 무작위로 한개 선택
                        // 그것을 click 이벤트로 강제로 보냄
                        const computerChoice = rows.flat().filter((row) =>
                            row.textContent == ''
                        );
                        const random = Math.floor(Math.random() * computerChoice.length);
                        computerChoice[random].textContent = 'X';
                        turn = (turn === 'O' ? 'X' : 'O');

                    },
                    2000);
            }

- 제로초님

let clickable = true;
        const callback = (event) => {
            if (!clickable) return;
            //칸이 비었는지 확인
            if (event.target.textContent) return;
            event.target.textContent = turn;
            checkWinnerAndDraw(event.target);
            clickable = false;
            if (turn === 'X') {
                setTimeout(() => {
                    const emptyCells = rows.flat().filter((v) => !v.textContent);
                    const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
                    randomCell.textContent = 'X';
                    checkWinnerAndDraw(randomCell);
                }, 2000);
                clickable = true;
            }

 

 

7. 전체코드

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>틱택토</title>
    <style>
        table {
            border-collapse: collapse;
        }
       
        td {
            border: 1px solid black;
            width: 40px;
            height: 40px;
            text-align: center;
        }
    </style>
</head>

<body>

    <script>
        const data = [];
        let turn = 'O';
        const rows = [];

        for (let i = 0; i < 3; i++) {
            data.push([]);
        }
        const $table = document.createElement("table");
        const $result = document.createElement("div");
        const checkWinner = (target) => {
            // 간단하게 해당 row,cell 인덱스 찾기
            console.log(target);
            const rowIndex = target.parentNode.rowIndex;
            const cellIndex = target.cellIndex;


            // 세칸다 채워졌나?
            //검사시 플래그 초기 상태는 false;
            let hasWinner = false;

            // 가로줄 검사
            if (
                rows[rowIndex][0].textContent === turn &&
                rows[rowIndex][1].textContent === turn &&
                rows[rowIndex][2].textContent === turn
            ) {
                hasWinner = true;
            }
            // 세로줄 검사
            if (
                rows[0][cellIndex].textContent === turn &&
                rows[1][cellIndex].textContent === turn &&
                rows[2][cellIndex].textContent === turn
            ) {
                hasWinner = true;
            }
            // 대각선 검사
            if (
                rows[0][0].textContent === turn &&
                rows[1][1].textContent === turn &&
                rows[2][2].textContent === turn
            ) {
                hasWinner = true;
            }
            if (
                rows[0][2].textContent === turn &&
                rows[1][1].textContent === turn &&
                rows[2][0].textContent === turn
            ) {
                hasWinner = true;
            }
            return hasWinner;

        }

        const checkWinnerAndDraw = (target) => {
            if (checkWinner(target)) {
                $result.textContent = `${turn}님이 승리했습니다.`;
                $table.removeEventListener('click', callback);
                return;
            }

            let draw = true;
            rows.forEach((row) => {
                row.forEach((cell) => {
                    if (!cell.textContent) {
                        draw = false;
                    }
                });
            });

            if (draw) {
                $result.textContent = '무승부입니다.';
                $table.removeEventListener('click', callback);
                return;
            }
            turn = (turn === 'O' ? 'X' : 'O');
        }

        let count = 0;
        let clickable = true;
        const callback = (event) => {
            if (!clickable) return;
            //칸이 비었는지 확인
            if (event.target.textContent) return;
            event.target.textContent = turn;
            checkWinnerAndDraw(event.target);
            clickable = false;
            if (turn === 'X') {
                setTimeout(() => {
                    const emptyCells = rows.flat().filter((v) => !v.textContent);
                    const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
                    randomCell.textContent = 'X';
                    checkWinnerAndDraw(randomCell);
                    clickable = true;
                }, 2000);

            }

        }
        for (let i = 0; i < 3; i++) {
            const $tr = document.createElement("tr");
            const cells = [];
            for (let j = 0; j < 3; j++) {
                const $td = document.createElement("td"); // 태그는 재사용이 쉽게 변수로 빼두는 것이 좋음
                // $td.addEventListener('click', callback)
                $tr.append($td);
                console.log("$td", $td);
                cells.push($td);
            }
            $table.append($tr);
            rows.push(cells);

            $table.addEventListener('click', callback);
        }
        console.log(rows);
        document.body.append($table);
        document.body.append($result);
    </script>
</body>

</html>
728x90
반응형