Веб дизајн/К2 2022

Извор: SI Wiki
Пређи на навигацију Пређи на претрагу

Drugi kolokvijum 2022. godine održan je 5. maja. Postavka je dostupna sa stranice predmeta.. jQuery biblioteka se uvozila preko minifikovanog fajla koji je bio dat u materijalima.

Решење

zadatak.html

<!DOCTYPE html>
<html>
    <head>
        <title>K2 2022</title>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
        <link href="zadatak.css" rel="stylesheet" />
        <script src="zadatak.js"></script>
    </head>
    <body>
        <main>
            <table>
                <tr>
                    <td id="field-0"></td>
                    <td id="field-1"></td>
                    <td id="field-2"></td>
                </tr>
                <tr>
                    <td id="field-3"></td>
                    <td id="field-4"></td>
                    <td id="field-5"></td>
                </tr>
                <tr>
                    <td id="field-6"></td>
                    <td id="field-7"></td>
                    <td id="field-8"></td>
                </tr>
            </table>
            <form>
                <input type="range" id="n" min="1" max="9">
                <input type="submit" id="start" value="Почни">
            </form>
            <p class="hidden">Игра је завршена!</p>
        </main>
    </body>
</html>

zadatak.css

body {
    font-family: Arial, Helvetica, sans-serif;
}

main {
    padding: 20px;
    text-align: center;
}

table {
    border-collapse: collapse;
    margin: 0 auto;
}

td {
    border: 1px solid black;
    box-sizing: border-box;
    height: 100px;
    pointer-events: none;
    width: 100px;
}

form {
    margin-top: 20px;
}

.hidden {
    display: none;
}

.preview .selected:not(.previous) {
    background-color: blue;
}

.playing td {
    pointer-events: all;
}

.playing .selected, .finished .selected {
    background-color: green;
}

.error .selected {
    background-color: red;
}

zadatak.js

$(function() {
    'use strict';
    // Niz tačnih polja u trenutnoj partiji
    let correctFields = null;
    // Identifikator intervala koji u 'preview' stanju boji ćelije
    let interval = 0;
    // DOM elementi
    const $startButton = $('#start');
    const $n = $('#n');
    const $message = $('p');
    const $table = $('table');
    const $cells = $('td');
    /**
     * Generiše indekse polja na tabli sa tačnim izborima, za dat broj polja
     * koji se generiše i ukupan broj polja.
     * @param {number} n Broj polja koji se generiše
     * @param {number} max Broj za jedan većeg od najvećeg broja koji se može
     *                     generisati
     * @returns {number[]} Niz brojeva koji predstavlja indekse polja na tabli
     *                     koji su tačni izbori
     */
    function selectFields(n, max) {
        const selected = new Set();
        while (selected.size < n) {
            selected.add(Math.floor(Math.random() * max));
        }
        return [...selected];
    }
    /**
     * Dohvata indeks zadate ćelije. Ćelije su numerisane brojevima počev od 0
     * i indeks ćelije se čuva u njenom identifikatoru.
     * @param {Node} cell DOM ćelija ili jQuery objekat ćelije čiji se indeks
     *                    dohvata
     * @returns Indeks zadate ćelije
     */
    function getCellIndex(cell) {
        return Number($(cell).attr('id').substring(6));
    }
    /**
     * Vraća indekse ćelija koje nisu izabrane.
     * @returns {number[]} Niz indeksa svih ćelija koje nisu izabrane
     */
    function getUnselectedFields() {
        const selectedFields = $('.selected')
            .map((_, cell) => getCellIndex(cell))
            .toArray();
        return correctFields.filter(field => !selectedFields.includes(field));
    }
    /**
     * Označava ćeliju sa zadatim indeksom kao izabranu.
     * @param {number} number Indeks ćelije za biranje
     */
    function selectField(number) {
        $('#field-' + number).addClass('selected');
    }
    /**
     * Uklanja oznaku da su izabrane sa svih ćelija.
     */
    function deselectAll() {
        $('.selected').removeClass('selected').removeClass('previous');
    }
    /**
     * Postavlja trenutno stanje igre kao klasu table za igru.
     * 
     * Moguća stanja su:
     * - preview: Kada tačne ćelije postaju plave jedna po jedna
     * - playing: Kada korisnik može da bira ćelije i one postaju zelene
     * - error: Kada korisnik izabere pogrešnu ćeliju i ona postane crvena
     * - finished: Kada korisnik završi igru
     * @param {string} state Trenutno stanje igre
     */
    function setGameState(state) {
        $table.attr('class', state);
    }
    /**
     * Poziva se svake sekunde dok je igra u 'preview' stanju i boji jedno
     * polje u plavo ukoliko nije već obojeno, ili prelazi u 'playing' stanje
     * ako su sva već obojena.
     */
    function previewField() {
        const fieldsToPreview = getUnselectedFields();
        if (fieldsToPreview.length === 0) {
            deselectAll();
            setGameState('playing');
            clearInterval(interval);
        } else {
            $('.selected').addClass('previous');
            selectField(fieldsToPreview[0]);
        }
    }
    /**
     * Vraća igru u 'playing' stanje nakon što je korisnik pogrešio.
     */
    function restoreStart() {
        setGameState('playing');
        deselectAll();
    }
    /**
     * Rukovalac pritiska na dugme za početak.
     * @param {ClickEvent} event Podaci događaja o pritisku na dugme
     */
    $startButton.click(event => {
        event.preventDefault();
        deselectAll();
        $startButton.attr('disabled', '');
        $message.addClass('hidden');
        setGameState('preview');
        correctFields = selectFields($n.val(), $cells.length);
        interval = setInterval(previewField, 1000);
    });
    /**
     * Rukovalac pritiska na ćeliju na tabli.
     * @param {ClickEvent} event Podaci događaja o pritisku na ćeliju
     */
    $cells.click(event => {
        const $cell = $(event.currentTarget);
        $cell.addClass('selected');
        if (correctFields.includes(getCellIndex($cell))) {
            if (getUnselectedFields().length === 0) {
                $message.removeClass('hidden');
                $startButton.removeAttr('disabled');
                setGameState('finished');
            }
        } else {
            setGameState('error');
            setTimeout(restoreStart, 1000);
        }
    });
});