카테고리 없음

[JavaScript] 브라우저에서 동작하는 지뢰찾기 만들기 - 심화편

teamnova 2023. 10. 3. 12:00
728x90

저번 글에 이어서 기능을 추가해보았습니다.

https://stickode.tistory.com/930

 

1. 난이도 설정 추가
2. 타이머 추가
3. 남은 지뢰의 수 표시
4. 게임 관련 ui 정렬

 

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>지뢰찾기</title>
    <!-- 게임 보드 스타일 -->
    <style>
        .board button {
            width: 30px;
            height: 30px;
            font-size: 16px;
        }

        .flagged {
            background-color: yellow;
            font-weight: bold;
            color: red;
        }

        #timer {
            font-size: 20px;
            margin-bottom: 10px;
        }

        .game-container {
            display: flex;
            align-items: start;
        }

        .game-controls {
            margin-left: 20px; /* 보드와 게임 관련 UI 간 간격 */
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            width: 150px; /* 원하는 너비로 조정 가능 */
        }

    </style>
</head>
<body><!-- 게임 보드와 관련 UI 묶기 -->
<div class="game-container">
    <div class="board"></div>
    <div class="game-controls">
        <div>
            남은 지뢰: <span id="remainingMines"></span>
        </div>
        <div>
            <select id="difficulty">
                <option value="beginner">초보</option>
                <option value="intermediate">중급</option>
                <option value="expert">고급</option>
            </select>
        </div>
        <div>
            <button id="startGame">게임 시작</button>
            <button id="pauseGame">일시정지</button><br>
            <span id="timer">0</span>초
        </div>
    </div>
</div>

</body>
<script>
    document.addEventListener('DOMContentLoaded', () => {
        const board = document.querySelector('.board');// 게임 보드 선택
        const timerElement = document.getElementById('timer');
        let timerInterval;
        let startTime;
        let isPaused = false;
        let pausedTime = 0;

        const difficulties = {
            beginner: {rows: 10, cols: 10, mines: 10},
            intermediate: {rows: 16, cols: 16, mines: 40},
            expert: {rows: 24, cols: 24, mines: 99}
        };

        let ROWS = difficulties.beginner.rows;
        let COLS = difficulties.beginner.cols;
        let MINES = difficulties.beginner.mines;
        let minePositions = [];

        // 게임 시작 버튼 이벤트 리스너 추가
        const startButton = document.getElementById('startGame');
        startButton.addEventListener('click', startGame);

        // 게임 일시정지 버튼 이벤트 리스너 추가
        const pauseButton = document.getElementById('pauseGame');
        pauseButton.addEventListener('click', pauseGame);


        let flaggedCount = 0; // 현재까지 깃발 표시한 칸의 수
        const remainingMinesElement = document.getElementById('remainingMines');

        function updateRemainingMines() {
            remainingMinesElement.textContent = MINES - flaggedCount;
        }

        initializeGame();

        function startGame() {
            if (timerInterval) clearInterval(timerInterval); // 기존 타이머 초기화

            if (isPaused) {
                startTime = new Date().getTime() - pausedTime;
                isPaused = false;
            } else {
                initializeGame();
                startTime = new Date().getTime();
            }

            timerElement.textContent = '0';
            startTimer();
        }

        function startTimer() {
            timerInterval = setInterval(() => {
                const currentTime = new Date().getTime();
                const difference = isPaused ? pausedTime : Math.floor((currentTime - startTime) / 1000);
                timerElement.textContent = difference.toString();
            }, 1000);
        }

        function pauseGame() {
            if (!timerInterval) return;

            clearInterval(timerInterval);
            pausedTime = new Date().getTime() - startTime;
            isPaused = true;
        }

        function initializeGame() {
            const selectedDifficulty = document.getElementById('difficulty').value;
            ROWS = difficulties[selectedDifficulty].rows;
            COLS = difficulties[selectedDifficulty].cols;
            MINES = difficulties[selectedDifficulty].mines;

            initializeBoard();
            placeMines();

            flaggedCount = 0; // 게임 초기화시 깃발 카운트도 초기화
            updateRemainingMines(); // 남은 지뢰 수 초기화
        }

        function initializeBoard() {
            board.innerHTML = '';
            for (let i = 0; i < ROWS; i++) {
                let row = document.createElement('div');
                for (let j = 0; j < COLS; j++) {
                    let cell = document.createElement('button');
                    cell.setAttribute('data-row', i);
                    cell.setAttribute('data-col', j);
                    cell.addEventListener('click', onCellClick);
                    cell.addEventListener('contextmenu', onCellRightClick);
                    row.appendChild(cell);
                }
                board.appendChild(row);
            }
        }

        function placeMines() {
            minePositions = [];
            while (minePositions.length < MINES) {
                let position = {
                    row: Math.floor(Math.random() * ROWS),
                    col: Math.floor(Math.random() * COLS)
                };

                if (!minePositions.some(pos => pos.row === position.row && pos.col === position.col)) {
                    minePositions.push(position);
                }
            }
        }

        function onCellClick(event) {
            const row = parseInt(event.target.getAttribute('data-row'));
            const col = parseInt(event.target.getAttribute('data-col'));
            const cell = event.target;

            if (cell.classList.contains('flagged')) return;

            if (isMine(row, col)) {
                revealAllCells();
                clearInterval(timerInterval);
                setTimeout(() => {
                    alert('게임이 끝났습니다.');
                    initializeGame();
                }, 50);
            } else {
                revealCell(row, col);
            }
        }

        function onCellRightClick(event) {
            event.preventDefault();
            const cell = event.target;
            if (cell.disabled) return;

            if (cell.classList.contains('flagged')) {
                cell.classList.remove('flagged');
                cell.textContent = '';
                flaggedCount--;
            } else {
                cell.classList.add('flagged');
                cell.textContent = '🚩';
                flaggedCount++;
            }

            updateRemainingMines(); // 깃발 표시나 제거 후, 남은 지뢰 수 업데이트
        }

        function isMine(row, col) {
            return minePositions.some(pos => pos.row === row && pos.col === col);
        }

        function revealCell(row, col) {
            let minesAround = 0;
            for (let i = -1; i <= 1; i++) {
                for (let j = -1; j <= 1; j++) {
                    if (i !== 0 || j !== 0) {
                        if (isMine(row + i, col + j)) {
                            minesAround++;
                        }
                    }
                }
            }

            const cell = board.children[row].children[col];
            cell.textContent = minesAround > 0 ? minesAround : '';
            cell.disabled = true;
        }

        function revealAllCells() {
            for (let i = 0; i < ROWS; i++) {
                for (let j = 0; j < COLS; j++) {
                    const cell = board.children[i].children[j];
                    if (isMine(i, j)) {
                        cell.textContent = '💣';
                        cell.style.backgroundColor = 'red';
                    } else {
                        const minesAround = countMinesAround(i, j);
                        cell.textContent = minesAround > 0 ? minesAround : '';
                    }
                    cell.disabled = true;
                }
            }
        }

        function countMinesAround(row, col) {
            let minesAround = 0;
            for (let i = -1; i <= 1; i++) {
                for (let j = -1; j <= 1; j++) {
                    if (i !== 0 || j !== 0) {
                        if (isMine(row + i, col + j)) {
                            minesAround++;
                        }
                    }
                }
            }
            return minesAround;
        }
    });

</script>