JS30. Задание 30 Whack-A-Mole



Демо: https://js3030.github.io/
Код: https://github.com/js3030/js3030.github.io

Это последнее задание курса и в нём мы поиграем в классическую игру Whack-A-Mole wiki.
Но вначале её напишем, потому что играть в свою собственную игру в разы забавнее.

Разметка

<span class="score">0</span></h1>
<button onClick="startGame()">Start!</button>

<div class="hole hole1">
    <div class="mole"></div>
</div>

Элемент span, в котором будут показаны набранные баллы, кнопка начала игры и шесть div с классом hole, внутри каждого из них div с классом mole

Стили

В CSS есть класс «.up», который изменяет верхнюю позицию мола от 100% до 0%.

.hole.up .mole {
    top: 0;
}

Добавление этого класса приводит к тому, что цель становится видимой


Код

Находим элементы разметки

var holes = document.querySelectorAll(".hole");
var scoreBoard = document.querySelector(".score");
var moles = document.querySelectorAll(".mole");

Создаём три переменные, которые позже будут переназначены:

var lastHole;
var timeUp = false;
var score = 0;

lastHole, чтобы предотвратить то же самое отверстие в два раза.
timeUp, переменная-флаг, чтобы начать и закончить игру.
score, чтобы показать счет пользователя.

Первая функция randomTime принимает минимальное и максимальное значение времени между появлением цели и возвращает случайное значение между этим интервалом.
Код  функции:

function randomTime(min, max) {
    return Math.round(Math.random() * (max - min) + min);
}


Вторая функция randomHole выбирает рандомное отверстие hole, из которого появляется цель. 
Для этого находим случайный индекс idx умножая Math.random() на длинну объекта NodeList, показывающего количество отверстий, округляем полученное значение до ближайшего целого в меньшую сторону, и выбираем отверстие с данным индексом:

    var idx = Math.floor(Math.random() * holes.length);
    var hole = holes[idx];


В функции используется переменная lastHole, чтобы убедиться, что одно и то же отверстие не выбрано два раза подряд. Если lastHole равен текущему отверстию, снова вызываем randomHole.

function randomHole(holes) {
    var idx = Math.floor(Math.random() * holes.length);
    var hole = holes[idx];
    if (hole === lastHole) {
        return randomHole(holes);
    }
    lastHole = hole;
    return hole;
}

Создаём функцию peep - основную функцию данной игры.
У нас есть рандомное время, у нас есть рандомное отверстие, присвоим их переменным time и hole

    var time = randomTime(200, 1000);
    var hole = randomHole(holes);


Выбранному отверстию добавим класс up, так что цель станет видна:

hole.classList.add("up");

Через промежуток времени, равный  time, уберём класс up - цель скроется

    setTimeout(() => {
        hole.classList.remove("up");       
    }, time);


И, если время игры не закончилось, запустим функцию снова.

    if (!timeUp) peep();

Код функции:

function peep() {
    var time = randomTime(200, 1000);
    var hole = randomHole(holes);
    hole.classList.add("up");
    setTimeout(() => {
        hole.classList.remove("up");
        if (!timeUp) peep();
    }, time);
}


Следующая функция - это функция startGame, которая сбрасывает табло до нуля, переменную флага timeUp устанавливает равной false, а счетчик игры равным нулю. Кроме того, эта функция вызывает функцию peep, чтобы начать игру. Последнее, что выполняет функция, - это определение продолжительности игры с использованием функции setTimeout и переменной timeup, которая получает значение true через 10000 миллисекунд.

function startGame() {
    scoreBoard.textContent = 0;
    timeUp = false;
    score = 0;
    peep();
    setTimeout(() => timeUp = true, 10000)
}


Последняя функция bonk.

function bonk(e) {
    if(!e.isTrusted) return; // cheater!
    score++;
    this.classList.remove("up");
    scoreBoard.textContent = score;
}


bonk умеет увеличивать счёт

    score++;

выводить его в окно счёта

    scoreBoard.textContent = score;

удалять цель по которой произошёл клик

    this.classList.remove("up");

и даже выявлять читеров!

     if(!e.isTrusted) return; // cheater!

Цитата с mdn

isTrusted - свойство объекта Event, доступное только на чтение. Принимает значение true, если событие было инициировано действиями пользователя, и false, если событие было создано или изменено скриптом, либо с помощью dispatchEvent

Вызовем функцию bonk при клике по каждой мишени, и на этом код игры закончен

moles.forEach(mole => mole.addEventListener("click", bonk)); 
 

Код полностью:

var holes = document.querySelectorAll(".hole");
var scoreBoard = document.querySelector(".score");
var moles = document.querySelectorAll(".mole");
var lastHole;
var timeUp = false;
var score = 0;

function randomTime(min, max) {
    return Math.round(Math.random() * (max - min) + min);
}

function randomHole(holes) {
    var idx = Math.floor(Math.random() * holes.length);
    var hole = holes[idx];
    if (hole === lastHole) {
        console.log("Ah nah thats the same one bud");
        return randomHole(holes);
    }
    lastHole = hole;
    return hole;
}

function peep() {
    var time = randomTime(200, 1000);
    var hole = randomHole(holes);
    hole.classList.add("up");
    setTimeout(() => {
        hole.classList.remove("up");
        if (!timeUp) peep();
    }, time);
}

function startGame() {
    scoreBoard.textContent = 0;
    timeUp = false;
    score = 0;
    peep();
    setTimeout(() => timeUp = true, 10000)
}

function bonk(e) {
    if(!e.isTrusted) return; // cheater!
    score++;
    this.classList.remove("up");
    scoreBoard.textContent = score;
}

moles.forEach(mole => mole.addEventListener("click", bonk));