momente şi schiţe de informatică şi matematică
To attain knowledge, write. To attain wisdom, rewrite.

Modelarea tablei şi jocului de şah (III)

DOM | FEN | jQuery | javaScript | widget
2012 jun

În (II) am început dezvoltarea unui jQuery.widget(), anume "FenBrowser()" - cu intenţia de a asigura crearea unei table de şah vide şi apoi, înscrierea pe tablă a unei poziţii de şah specificate de către utilizator (precizăm totuşi: "dezvoltarea" este aici preponderent didactică!).

Specificarea unei poziţii de şah

Pentru o chestiune de genul "daţi mediile la obiectele şcolare - iată media generală" putem accepta că utilizatorul va introduce un tablou de valori: prima medie, a doua, ş.a.m.d., sau eventual un tablou-asociativ, conţinând perechi "obiect: medie".

Am putea proceda la fel, cerând utilizatorului un tablou POZ[64] în care POZ[k] să reprezinte piesa plasată pe câmpul de index k (sau zero dacă acest câmp este liber), sau un tablou-asociativ POZ = {câmp: piesă} în care ar apărea numai câmpurile care conţin piese.

Pentru exerciţiile din manualele uzuale, transmiterea datelor sub forma unui tablou de valori este ceva aproape indiscutabil, de la sine înţeles… Dar dacă s-ar pune problema arhivării datelor, sau comunicării lor între programe - atunci reprezentarea ca text simplu este clar preferabilă.

Pentru chestiunea mediilor şcolare am putea imagina o reprezentare textuală simplă a datelor: "a95/b10/c775" ar putea reprezenta foarte bine tabloul {'a': 9.5, 'b': 10, 'c': 7.75}. Am codificat fiecare obiect prin câte o literă; în reprezentarea textuală a mediei nu este necesar să apară şi '.' fiindcă mediile au valori între 1 şi 10 (deci 'c775' nu poate fi interpretat decât ca fiind 7.75); am prevăzut '/' pentru a separa obiectele (dar puteam şi renunţa la separator: "a95b10c775").

În mod analog putem imagina o reprezentare textuală pentru poziţiile de şah - dar deja FEN este formatul standard de reprezentare textuală a poziţiei de şah.

Cele 12 piese de şah sunt codificate prin câte o literă, folosind majuscule pentru piesele albe şi litere mici pentru piesele negre: p pentru pion, k pentru rege ("king"), q pentru damă ("queen"), r pentru turn ('rook'), b pentru nebun ('bishop') şi n pentru cal ('knight').

Fiecărei linii a tablei de şah i se asociază un şir de maximum 8 caractere, indicând piesele existente pe linia respectivă începând de la stânga spre dreapta şi folosind câte o cifră 1..8 pentru a indica eventual numărul de câmpuri libere aflate în dreapta. De exemplu, linia ['', 'P', '', '', 'k', 'p', '', ''] este reprezentată prin "1P2kp2"; iar o linie fără piese se reprezintă prin şirul "8".

Reprezentările celor 8 linii sunt separate prin '/' şi sunt concatenate în ordinea "de sus în jos" (în reprezentarea standard a tablei, albul ocupă liniile 1 şi 2 aflate în partea de jos a tablei, iar negrul ocupă liniile 7 şi 8 aflate în partea de sus).

De exemplu, poziţia iniţială are notaţia rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR.

În acest moment ne putem rezuma la această reprezentare a poziţiei. Mai târziu va trebui să implicăm şi celelalte porţiuni din reprezentarea FEN (destinate unor informaţii contextuale: cine este la mutare, ce drepturi de rocadă sunt valabile în poziţia respectivă, şi altele).

Crearea infrastructurii {Load FEN, tabla de şah}

Acum, fişierul HTML trebuie să conţină un element <input> sau <textarea> (pentru ca utilizatorul să introducă FEN-ul) şi cel mai firesc este ca fenBrowser() să fie instanţiat chiar pe acest element:

în final va rezulta:

<body>
    <textarea id="getFEN"></textarea>

    <script>
        $(function() { 
            $('#getFEN').fenBrowser();
        });
    </script>
</body>

Să ne amintim că setChessTable() returna un fragment HTML (şirul celor 64 de elemente <div> pentru câmpurile tablei) care trebuia înscris drept conţinut unui element cu proprietatea "position: relative". Fiindcă am scutit fişierul HTML de prevederea un astfel de element "container" (şi aşa se cuvine), revine metodei _create() să adauge acest element (înainte de a apela _setChessTable()).

Deasemenea, trebuie adăugat (conform uzanţelor) un buton "Load" cu handler-ul corespunzător: click pe "Load" va declanşa metoda _init() (care va "citi" ceea ce a tastat utilizatorul, va verifica dacă este un FEN valid şi dacă da, atunci va înscrie poziţia respectivă pe tablă).

/* js/brw-sah.js */
(function ($) {
    $.widget("brw.fenBrowser", {
        _create: function() {
            var inputFEN = this.element, // <input> sau <textarea>
                self = this;    // salvează referinţa 'this' la instanţa curentă
            
            // adaugă un buton 'Load', cu 'click()' pentru încărcarea poziţiei
            $('<button></button>').text('Load')
                .click(function() { 
                    self._init();       // dar NU "this"._init()     
                    return false;     
                })
                .insertAfter( inputFEN );

            // adaugă un "container" pentru tablă (cu 'position: relative')
            $('<div class="container"></div>')
                .insertAfter( inputFEN.next() )
                // în care acum, inserează cele 64 de câmpuri
                .html( this._setChessTable() );
        },
        
        _init: function() {
            var fen = this.element.val();
            // deocamdată, doar "alertează" conţinutul introdus
            if(fen) alert(fen); 
        },
        
        _setChessTable: function() {
            /* mutăm aici conţinutul funcţiei setChessTable() anterioare */
            // var html = []; ...; return html.join('');
        }
    });
})(jQuery);

Efectuând aceste modificări în fişierele anterioare şi încărcând într-un browser fişierul HTML, va rezulta ceea ce am redat deja în imaginea alăturată mai sus textului HTML. Dacă se va tasta ceva (în caseta corespunzătoare lui <textarea>), atunci click pe "Load" va afişa textul respectiv.

Obs. În interiorul unui obiect javaScript, this referă însuşi acest obiect. Obiectul $('<button/>') este creat în contextul instanţei curente a obiectului fenBrowser() (mai simplu spus: creat înăuntrul widget-ului); în interiorul butonului creat, "this" va referi acest button - încât this._init() ar însemna apelarea unei metode "_init()" proprii obiectului-buton respectiv (ori aici aveam nevoie să invocăm metoda _init() a widget-ului, nu a butonului). Rezolvarea tipică a situaţiei constă în salvarea prealabilă self = this a referinţei la instanţa curentă a widget-ului, urmând ca în interiorul codului butonului să folosim "self._init()" (vezi codul redat mai sus).

vezi Cărţile mele (de programare)

docerpro | Prev | Next