В этой статьи вы прочитаете, как сделать игру сапёр на JavaScript, но нужно предупредить, что этот код сделал не я, а был взять с сайта codepen.io у пользователя creme, поэтому если нужен оригинал, заходите к нему.
Также что бы понимать написанный тут код, стоит знать стандарты языка JavaScript ECMAScript 6 и Buble.
Создаём сапёр на JS:
Перейдём к созданию, но для начала нужно создать HTML документ, в котором и будет рендерится игра.
HTML:
1 2 3 4 5 | <div class="board-wrap"> <div class="board"></div> </div> <div class="endscreen"></div> <script src="./scripts/minesweeper.js"></script> |
В HTML нет не чего сложного, тут создаём элемент для рендеринга поля и подключаем скрипт с нашем кодом.
CSS не буду показывать так как, там его слишком много, да и вы всё равно можете скачать внизу эту программу и разобрать весь CSS.
JavaScript:
Как говорилось выше я не буду особо сильно описывать код, так как, тут он очень сложный и если всё объяснять, то получится очень длинный текст.
В этом коде мы объявляем переменный, массивы которые пригодятся во время работы программ, всё написано в комментариях, что для чего нужно.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | // Функция для запуска игры const setup = () => { for (let i = 0; i < Math.pow(size, 2); i++) { const tile = document.createElement('div'); tile.classList.add('tile'); board.appendChild(tile); } tiles = document.querySelectorAll('.tile'); boardSize = Math.sqrt(tiles.length); board.style.width = boardSize * tileSize + 'px'; document.documentElement.style.setProperty('--tileSize', `${tileSize}px`); document.documentElement.style.setProperty('--boardSize', `${boardSize * tileSize}px`); let x = 0; let y = 0; tiles.forEach((tile, i) => { // set tile coordinates tile.setAttribute('data-tile', `${x},${y}`); // Добавление боб let random_boolean = Math.random() < bombFrequency; if (random_boolean) { bombs.push(`${x},${y}`); if (x > 0) numbers.push(`${x-1},${y}`); if (x < boardSize - 1) numbers.push(`${x+1},${y}`); if (y > 0) numbers.push(`${x},${y-1}`); if (y < boardSize - 1) numbers.push(`${x},${y+1}`); if (x > 0 && y > 0) numbers.push(`${x-1},${y-1}`); if (x < boardSize - 1 && y < boardSize - 1) numbers.push(`${x+1},${y+1}`); if (y > 0 && x < boardSize - 1) numbers.push(`${x+1},${y-1}`); if (x > 0 && y < boardSize - 1) numbers.push(`${x-1},${y+1}`); } x++; if (x >= boardSize) { x = 0; y++; } /* Обработка правого клика мыши */ tile.oncontextmenu = function(e) { e.preventDefault(); flag(tile); } /* Обработка левого клика мыши */ tile.addEventListener('click', function(e) { clickTile(tile); }); }); // Цикл для назначения чисел numbers.forEach(num => { let coords = num.split(','); let tile = document.querySelectorAll(`[data-tile="${parseInt(coords[0])},${parseInt(coords[1])}"]`)[0]; let dataNum = parseInt(tile.getAttribute('data-num')); if (!dataNum) dataNum = 0; tile.setAttribute('data-num', dataNum + 1); }); } |
Тут грубо говоря запускаем игры и рендерим поле, блоки.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* Функция для установки флага */ const flag = (tile) => { if (gameOver) return; if (!tile.classList.contains('tile--checked')) { if (!tile.classList.contains('tile--flagged')) { tile.innerHTML = '?'; tile.classList.add('tile--flagged'); } else { tile.innerHTML = ''; tile.classList.remove('tile--flagged'); } } } |
Вот здесь просто создаём функцию, которая нужна для установки флага на определённый блог, функция запускается при правом клики мыши.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /* Проверка есть бомба или нет */ const clickTile = (tile) => { if (gameOver) return; if (tile.classList.contains('tile--checked') || tile.classList.contains('tile--flagged')) return; let coordinate = tile.getAttribute('data-tile'); // Условие, если есть бомба if (bombs.includes(coordinate)) { endGame(tile); } else { // Иначе /* Проверка, если боба поблизости */ let num = tile.getAttribute('data-num'); if (num != null) { tile.classList.add('tile--checked'); tile.innerHTML = num; tile.style.color = numberColors[num-1]; setTimeout(() => { checkVictory(); }, 100); return; } checkTile(tile, coordinate); } tile.classList.add('tile--checked'); } |
В этой части кода, мы уже проверяем, есть ли бомба или нет, если есть, то выводим конец игры, иначе, проверяем, если рядом ещё бомбы.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | /* функция для правильного нажатия */ const checkTile = (tile, coordinate) => { console.log('✔'); let coords = coordinate.split(','); let x = parseInt(coords[0]); let y = parseInt(coords[1]); /* check nearby tiles */ setTimeout(() => { if (x > 0) { let targetW = document.querySelectorAll(`[data-tile="${x-1},${y}"`)[0]; clickTile(targetW, `${x-1},${y}`); } if (x < boardSize - 1) { let targetE = document.querySelectorAll(`[data-tile="${x 1},${y}"`)[0]; clickTile(targetE, `${x 1},${y}`); } if (y > 0) { let targetN = document.querySelectorAll(`[data-tile="${x},${y-1}"]`)[0]; clickTile(targetN, `${x},${y-1}`); } if (y < boardSize - 1) { let targetS = document.querySelectorAll(`[data-tile="${x},${y 1}"]`)[0]; clickTile(targetS, `${x},${y 1}`); } if (x > 0 && y > 0) { let targetNW = document.querySelectorAll(`[data-tile="${x-1},${y-1}"`)[0]; clickTile(targetNW, `${x-1},${y-1}`); } if (x < boardSize - 1 && y < boardSize - 1) { let targetSE = document.querySelectorAll(`[data-tile="${x 1},${y 1}"`)[0]; clickTile(targetSE, `${x 1},${y 1}`); } if (y > 0 && x < boardSize - 1) { let targetNE = document.querySelectorAll(`[data-tile="${x 1},${y-1}"]`)[0]; clickTile(targetNE, `${x 1},${y-1}`); } if (x > 0 && y < boardSize - 1) { let targetSW = document.querySelectorAll(`[data-tile="${x-1},${y 1}"`)[0]; clickTile(targetSW, `${x-1},${y 1}`); } }, 10); } |
Здесь просто идёт обработка, если правый клик, правильный.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /* Обработка не правильного клика */ const endGame = (tile) => { console.log('? Booom! Game over.'); endscreen.innerHTML = endscreenContent.loose; endscreen.classList.add('show'); gameOver = true; tiles.forEach(tile => { let coordinate = tile.getAttribute('data-tile'); if (bombs.includes(coordinate)) { tile.classList.remove('tile--flagged'); tile.classList.add('tile--checked', 'tile--bomb'); tile.innerHTML = '?'; } }); } |
Тут смотрим, если была нажата не верный блок, с бомбой, заканчиваем игру.
1 | setup(); |
Запускаем игру, то есть для одного запуска, надо просто объявить нужную переменную.
Вывод:
В этой статье разобрали сложный вариант создания игры сапёр на JavaScript, это не для новичкам, а скорее для профессионала, кто хочет попробовать сделать что то, сложное, хотя начинающим тоже пригодиться, для понятие логики игры и создания своей версии, более простой.