JavaScript- 2048게임 만들기

Jenna·2022년 12월 28일
1

javascript

목록 보기
15/16

자바스크립트 예제 2048 게임 만들기


📌 순서도 그리기



📌 코드보기


<style>
    #table {
        border-collapse: collapse;
        user-select: none;
        /* 드래그 막기 */
    }

    #table td {
        border: 10px solid #bbada0;
        width: 116px;
        height: 128px;
        font-size: 50px;
        font-weight: bold;
        text-align: center;
    }

    #score {
        user-select: none;
    }

    .color-2 {
        background-color: #eee4da;
        color: #776e65;
    }

    .color-4 {
        background-color: #eee1c9;
        color: #776e65;
    }

    .color-8 {
        background-color: #f3b27a;
        color: white;
    }

    .color-16 {
        background-color: #f69664;
        color: white;
    }

    .color-32 {
        background-color: #f77c5f;
        color: white;
    }

    .color-64 {
        background-color: #f75f3b;
        color: white;
    }

    .color-128 {
        background-color: #edd073;
        color: #776e65;
    }

    .color-256 {
        background-color: #edcc62;
        color: #776e65;
    }

    .color-512 {
        background-color: #edc950;
        color: #776e65;
    }

    .color-1024 {
        background-color: #edc53f;
        color: #776e65;
    }

    .color-2048 {
        background-color: #edc22e;
        color: #776e65;
    }
</style>

<body>
    <table id="table"></table>
    <div id="score">0</div>
    <button id="back">되돌리기</button>
    <script>
        const $table = document.getElementById('table');
        const $score = document.getElementById('score');
        const $back = document.getElementById('back');
        let data = [];
        const history = [];

        $back.addEventListener('click', () => {
            const prevData = history.pop();
            if (!prevData) return; //되돌릴게 없으면 종료
            $score.textContent = prevData.score;
            data = prevData.table;
            draw();
        });

        function startGame() {
            //fragment 메모리에 저장함 성능이 좋아진다.  
            const $fragment = document.createDocumentFragment();
            [1, 2, 3, 4].forEach((function () {
                const rowData = []; //2차원배열 만들어줌 
                data.push(rowData);
                const $tr = document.createElement('tr');
                [1, 2, 3, 4].forEach(() => {
                    rowData.push(0);
                    const $td = document.createElement('td');
                    $tr.appendChild($td);
                });
                $fragment.appendChild($tr);
            }));
            $table.appendChild($fragment);
            put2ToRandomCell();
            draw();
        }

        function put2ToRandomCell() {
            const emptyCells = []; // [i1, j1], [i2,j2], [i3,j3]
            data.forEach(function (rowData, i) {
                rowData.forEach(function (cellData, j) {
                    if (!cellData) {
                        emptyCells.push([i, j]);
                    }
                });
            });
            //randomCell === [i,j]
            const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
            data[randomCell[0]][randomCell[1]] = 2;
        }
        function draw() {
            data.forEach((rowData, i) => {
                rowData.forEach((cellData, j) => {
                    const $target = $table.children[i].children[j];
                    if (cellData > 0) {
                        $target.textContent = cellData;
                        $target.className = 'color-' + cellData;
                    } else {
                        $target.textContent = '';
                        $target.className = '';
                    }
                })
            })
        }
        startGame();

        //dummy data
        // data = [
        //     [32, 2, 4, 2],
        //     [64, 4, 8, 4],
        //     [2, 1024, 1024, 32],
        //     [32, 16, 64, 4],
        // ];
        // draw();

        function moveCells(direction) {
            history.push({
                table: JSON.parse(JSON.stringify(data)), //깊은복사
                score: $score.textContent,
            });
            switch (direction) {
                case 'left': { //case문 안에서 변수 선언했을때 괄호 {} 써주기 
                    const newData = [[], [], [], []];
                    data.forEach((rowData, i) => {
                        rowData.forEach((cellData, j) => {
                            if (cellData) {
                                const currentRow = newData[i]
                                const prevData = currentRow[currentRow.length - 1];
                                if (prevData === cellData) { //이전 값과 지금 값이 같으면 
                                    const score = parseInt($score.textContent);
                                    $score.textContent = score + currentRow[currentRow.length - 1] * 2;
                                    currentRow[currentRow.length - 1] *= -2;
                                } else {
                                    newData[i].push(cellData);
                                }
                            }
                        });
                    });
                    console.log(newData);
                    [1, 2, 3, 4].forEach((rowData, i) => {
                        [1, 2, 3, 4].forEach((cellData, j) => {
                            data[i][j] = Math.abs(newData[i][j]) || 0; //-2 곱해준거 절대값만들기
                        });
                    });
                    break;
                }
                case 'right': {
                    const newData = [[], [], [], []];
                    data.forEach((rowData, i) => {
                        rowData.forEach((cellData, j) => {
                            if (rowData[3 - j]) {
                                const currentRow = newData[i]
                                const prevData = currentRow[currentRow.length - 1];
                                if (prevData === rowData[3 - j]) {
                                    const score = parseInt($score.textContent);
                                    $score.textContent = score + currentRow[currentRow.length - 1] * 2;
                                    currentRow[currentRow.length - 1] *= -2;
                                    currentRow[currentRow.length - 1] *= -2;
                                } else {
                                    newData[i].push(rowData[3 - j]);
                                }
                            }
                        });
                    });
                    console.log(newData);
                    [1, 2, 3, 4].forEach((rowData, i) => {
                        [1, 2, 3, 4].forEach((cellData, j) => {
                            data[i][3 - j] = Math.abs(newData[i][j]) || 0;
                        });
                    });
                    break;
                }
                case 'up': {
                    const newData = [[], [], [], []];
                    data.forEach((rowData, i) => {
                        rowData.forEach((cellData, j) => {
                            if (cellData) {
                                const currentRow = newData[j]
                                const prevData = currentRow[currentRow.length - 1];
                                if (prevData === cellData) {
                                    const score = parseInt($score.textContent);
                                    $score.textContent = score + currentRow[currentRow.length - 1] * 2;
                                    currentRow[currentRow.length - 1] *= -2;
                                    currentRow[currentRow.length - 1] *= -2;
                                } else {
                                    newData[j].push(cellData);
                                }
                            }
                        });
                    });
                    console.log(newData);
                    [1, 2, 3, 4].forEach((cellData, i) => {
                        [1, 2, 3, 4].forEach((rowData, j) => {
                            data[j][i] = Math.abs(newData[i][j]) || 0;
                        });
                    });
                    break;
                }
                case 'down': {
                    const newData = [[], [], [], []];
                    data.forEach((rowData, i) => {
                        rowData.forEach((cellData, j) => {
                            if (data[3 - i][j]) {
                                const currentRow = newData[j]
                                const prevData = currentRow[currentRow.length - 1];
                                if (prevData === data[3 - i][j]) {
                                    const score = parseInt($score.textContent);
                                    $score.textContent = score + currentRow[currentRow.length - 1] * 2;
                                    currentRow[currentRow.length - 1] *= -2;
                                    currentRow[currentRow.length - 1] *= -2;
                                } else {
                                    newData[j].push(data[3 - i][j]);
                                }
                            }
                        });
                    });
                    console.log(newData);
                    [1, 2, 3, 4].forEach((cellData, i) => {
                        [1, 2, 3, 4].forEach((rowData, j) => {
                            data[3 - j][i] = Math.abs(newData[i][j]) || 0;
                        });
                    });
                    break;
                }
            }
            if (data.flat().includes(2048)) { //승리
                draw();
                setTimeout(() => {
                    alert('축하합니다. 2048을 만들었습니다!');
                }, 500);
            } else if (!data.flat().includes(0)) { //빈칸이 없으면 패배
                alert(`패배했습니다...${$score.textContent}`);
            } else {
                put2ToRandomCell();
                draw();
            }
        }
        window.addEventListener('keyup', (event) => {
            if (event.key === 'ArrowUp') {
                moveCells('up');
            } else if (event.key === 'ArrowDown') {
                moveCells('down');
            } else if (event.key === 'ArrowLeft') {
                moveCells('left');
            } else if (event.key === 'ArrowRight') {
                moveCells('right');
            }
        });

        let startCoord;
        window.addEventListener('mousedown', (event) => {
            startCoord = [event.clientX, event.clientY];
        });
        window.addEventListener('mouseup', (event) => {
            const endCoord = [event.clientX, event.clientY];
            const diffX = endCoord[0] - startCoord[0];
            const diffY = endCoord[1] - startCoord[1];
            if (diffX < 0 && Math.abs(diffX) > Math.abs(diffY)) {
                moveCells('left');
            } else if (diffX > 0 && Math.aabs(diffX) > Math.abs(diffY)) {
                moveCells('right');
            } else if (diffY > 0 && Math.abs(diffX) <= Math.abs(diffY)) {
                moveCells('down');
            } else if (diffY < 0 && Math.abs(diffX) <= Math.abs(diffY)) {
                moveCells('up');
            }
            //Math.abs = 절대값 
        });
    </script>
</body>

📌 코드 공부하기


Fragment => 메모리에 저장되어 성능을 좋게 한다.

const $fragment = document.createDocumentFragment();

깊은 복사 - 데이터를 저장할때는 깊은 복사를 사용한다.

 history.push({
                table: JSON.parse(JSON.stringify(data)), //깊은복사
                score: $score.textContent,
            });

case문 안에서 변수를 선언했을 때는 전체를 괄호로 묶어준다.
const는 스코프 문이기 때문에 스코프에 가둬서 제어한다.

case 'left': { //case문 안에서 변수 선언했을때 괄호 {} 써주기 
                    const newData = [[], [], [], []];
                    data.forEach((rowData, i) => {
                        rowData.forEach((cellData, j) => {
                            if (cellData) {
                                const currentRow = newData[i]
                                const prevData = currentRow[currentRow.length - 1];
                                if (prevData === cellData) { //이전 값과 지금 값이 같으면 
                                    const score = parseInt($score.textContent);
                                    $score.textContent = score + currentRow[currentRow.length - 1] * 2;
                                    currentRow[currentRow.length - 1] *= -2;
                                } else {
                                    newData[i].push(cellData);
                                }
                            }
                        });
                    });
                    console.log(newData);
                    [1, 2, 3, 4].forEach((rowData, i) => {
                        [1, 2, 3, 4].forEach((cellData, j) => {
                            data[i][j] = Math.abs(newData[i][j]) || 0; //-2 곱해준거 절대값만들기
                        });
                    });
                    break;
                }
profile
connecting the dots 💫

2개의 댓글

comment-user-thumbnail
2023년 4월 13일

2048 이렇게 하는거 아닌데 조금 불편하네요.

1개의 답글