Veb dizajn/K2 2022

Izvor: SI Wiki
< Веб дизајн
Datum izmene: 16. jul 2022. u 21:19; autor: KockaAdmiralac (razgovor | doprinosi) (Napomena o jQuery)
Pređi na navigaciju Pređi na pretragu

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

Postavka

Potrebno je napraviti igru memorije. Treba napraviti kvadrat 3x3 i slajder za izbor broja slučajno generisanih polja. Igra počinje tako što se generiše izabrani broj slučajnih polja i polja se oboje u plavo (polja se boje jedno po jedno, i drže se obojena 1 s nakon čega se vraćaju u belu boju). Nakon toga treba da se izaberu ta polja pritiskom miša. Sve dok se biraju dobra polja ona se boje u zeleno, kada se prvi put izabere polje koje nije generisano sva izabrana polja se boje u crveno i drže se tako 1 s nakon čega se vraćaju u belu boju. Treba omogućiti da se nakon toga ponovo mogu pogađati polja. Ako se pogode sva polja treba ispisati poruku da je igra završena i omogućiti da se ponovo mogu generisati polja.

Rešenje

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);
        }
    });
});