Vom prezenta treptat, în mod inductiv (vizând implicit şi "elementar") chestiuni circumscrise modelării tablei şi jocului de şah.
În această primă parte (apoi şi în a doua) ne ocupăm de modelarea tablei de şah, urmând în principal (cu permisiunea de plagiere corespunzătoare) modelul CSS introdus în 2008 de chess.dynarch.com.
Mai întâi relevăm câteva aspecte şi direcţii din practica existentă, caracteristice pentru modelarea tablei de şah bazată pe HTML, CSS şi javaScript.
Cel mai obişnuit, tabla de şah este modelată printr-o structură HTML <table>. De exemplu, folosind Firebug putem vedea că pgn4web procedează astfel:
Elementele <td> corespunzătoare câmpurilor tablei au setate proprietăţile CSS width şi height şi sunt atributate cu valign="middle" align="center" - obţinând poziţionarea conţinutului <img> exact în centru (ceea ce este mai dificil fără <table>).
<img>-urile corespund pieselor existente pe câmpurile respective şi au specificată o clasă CSS pentru dimensiuni şi atributul src care indică browserului fişierul-imagine pe care va trebui să-l încarce (pentru fiecare piesă).
În plus, toate acestea au specificate atribute de identificare "id" şi handlere "onclick()" (care gestionează mutarea piesei). Pe figura de mai sus este marcat elementul <td id="tcol7row0" >, având drept conţinut un element <img id="img_tcol7trow0" > - interpretarea fiind: pe câmpul h8 se află un turn negru ("col7" este coloana "h", iar "row0" este linia 8 a tablei).
La urma urmei - văzând cum se identifică fiecare "celulă" şi cum se asociază fiecărei celule câte o "formulă" onclick() - am putea zice că se lucrează ca în Excel… Desigur (ca pentru orice tabel) - putem face un mic experiment: am decupat de sub Firebug textul HTML asociat tablei, l-am salvat cu extensia ".html" şi apoi l-am încărcat în Gnumeric (redăm alături rezultatul). Probabil ar fi doar o "chestiune muncitorească", să transformi (şi… să denaturezi) mai departe toată aplicaţia, în Excel; dar pentru noi a fost mult mai interesant textul HTML asociat tablei, decât ceea ce am putea obţine cu Excel.
Există şi modelări ale tablei de şah care nu folosesc <table>, ci numai elemente <div> cu anumite proprietăţi CSS. Astfel, în DHTML-chess tabla de şah propriu-zisă este (din 2011):
Setarea position: relative (împreună cu fixarea dimensiunilor), permite poziţionarea câmpurilor în raport cu marginile tablei. Această diviziune conţine întâi, 64 de <div> precum:
Având fiecare float: left şi dimensiunea comună de 45px, rezultă că ele se vor poziţiona una după cealaltă, pe 8 linii succesive (8*45px = 360px = lăţimea tablei, fixată anterior).
Imediat după aceste 64 de elemente de "marcare" a câmpurilor, se află maximum 32 de <div> în care se ambalează imaginile pieselor existente pe tablă:
Cu position: absolute, acestea vor fi poziţionate la distanţa indicată de left faţă de marginea stângă a tablei şi la distanţa dată de top faţă de marginea de sus. Este de observat că pentru realizarea operaţiei de "inversare" a tablei va fi necesar să se modifice valorile "top" şi "left" ale diviziunilor corespunzătoare pieselor.
Dar structurile CSS folosite s-ar putea corela mai bine cu unele necesităţi fireşti de operare asupra tablei de şah. Sunt deja câteva idei - furnizate în 2008 de chess.dynarch.com - care simplifică infrastructura HTML-CSS necesară şi măresc gradul de corelare cu dinamica tablei de şah.
Pentru facilitarea operaţiei de inversare a tablei se pot considera clase CSS care să corespundă valorilor "top" şi "left", pentru fiecare dintre cele două orientări posibile ale tablei. Grupând apoi într-o clasă "colectoare" acele dintre aceste clase care corespund unei aceleiaşi orientări, inversarea tablei va reveni la simpla adăugare sau ştergere a acestei clase în <div>-ul de bază al tablei.
În plus, folosind aceste clase (cu "top" şi "left") - apare imediat şi posibilitatea poziţionării directe a câmpurilor, fără a mai folosi "float: left" (şi astfel, cele 64 de <div> incluse mai sus doar pentru alinierea câmpurilor, nu mai sunt necesare).
De asemenea, imaginile celor 12 piese de şah pot fi comasate într-una singură (creând un sprite) - ceea ce este important dacă se vizează folosirea mai multor seturi de piese (browserul va avea de încărcat mai puţine imagini).
Despre toate acestea urmează să discutăm mai departe, într-o anumită logică inductivă, fără a ne feri de probleme şi poate de mici inovaţii (apropo de PGNbrowser-ul propriu).
Să zicem că avem un "container" <div> şi vrem să "aşezăm" înăuntru un "pătrăţel" astfel încât primul colţ al acestuia să fie la un sfert din lăţimea şi respectiv, înălţimea tablei.
Relizăm aceasta setând position: relative pentru container şi position: absolute pentru elementul conţinut:
<!DOCTYPE html> <head> <style> .container { width: 160px; height: 160px; position: relative; border: 1px solid black; } .field { width: 20px; height: 20px; position: absolute; top: 25%; left: 25%; background: black; } </style> </head> <body> <div class="container"> <div class="field"></div> </div> </body>
Textul-sursă HTML redat mai sus, împreună cu imaginea a ceea ce se obţine accesând în Firefox fişierul respectiv - constituie un alt exemplu (şi unul real) de poziţionare:
<div style="position:relative;"> <!-- container pentru <pre> şi <img> --> <img src="rel-abs.png" style="position:absolute; top:35%; left:60%;" /> <pre class="html" style="width:60%;"> <!-- textul-sursă HTML redat mai sus --> <pre> </div>
class="html" serveşte aici pentru marcarea sintactică a textului-HTML conţinut de <pre>.
Dar mai mult, să zicem că avem nu doar unul, ci chiar 8 pătrăţele şi vrem să le poziţionăm unul lângă celălalt. Am putea repeta definiţiile ".field" de 8 ori, modificând doar valoarea pentru left (zero pentru primul, apoi mărind cu câte 160/8=20px pentru următoarele pătrăţele).
Dar avem o soluţie mai elastică: asociem pătrăţelelor o a doua clasă de proprietăţi ".Col-n" - conţinând numai left corespunzător fiecăruia (lăsând în ".field" numai "top", care are aceeaşi valoare pentru toate).
Pentru a putea distinge pătrăţelele, adăugăm un "border" în clasa .field; ca urmare, modificăm lăţimea containerului: 160 px + 8*(1px border-stânga + 1px border-dreapta) = 176px. Măsurăm valorile "left" în pixeli: 20px width + 1px border-stânga + 1 px border-dreapta = 22px, încât valorile sunt 0, 22px, 44px, etc. (în procente calculul devine incomod).
<head> <style> .container { width: 176px; height: 176px; /* 160 + 8*2 (2px border la .field) */ position: relative; border: 1px solid black; } .field { width: 20px; height: 20px; position: absolute; top: 25%; /* left: 25%; */ border:1px solid white; background: black; } .Col-1 { left: 0 } .Col-2 { left: 22px } /* width + border_orizontal */ .Col-3 { left: 44px } .Col-4 { left: 66px } .Col-5 { left: 88px } .Col-6 { left: 110px } .Col-7 { left: 132px } .Col-8 { left: 154px } </style> </head> <body> <div class="container"> <!-- position: relative --> <!-- position: absolute cu `top` de la definiţia .field şi `left` de la definiţia .Col-n --> <div class="field Col-1"></div> <div class="field Col-2"></div> <div class="field Col-3"></div> <div class="field Col-4"></div> <div class="field Col-5"></div> <div class="field Col-6"></div> <div class="field Col-7"></div> <div class="field Col-8"></div> </div> </body>
În sfârşit - să zicem că avem de poziţionat nişte pătrăţele în interiorul containerului nu numai pe orizontală, dar şi pe verticală…
O tablă de şah are 64 de pătrăţele, pe 8 linii şi 8 coloane. Eliminăm "top" din clasa .field şi definim clasele "Row-n", similare cu "Col-n" (şi conţinând "top" pentru fiecare linie). Pătrăţelele tablei vor fi definite acum prin <div class="field Col-n Row-n">, unde .field setează "position: absolute" şi indică browserului dimensiunile pătrăţelului, iar .Col-n şi .Row-n precizează "left" şi "top".
Acum este clar avantajul separării proprietăţilor "top" şi "left": în loc de 64 definiţii .field care ar conţine şi setările pentru "top" şi "left", avem numai 17 definiţii: una comună pentru .field (setează position: absolute şi precizează dimensiunile câmpului), 8 definiţii "Col-n" (precizează "left" pentru fiecare din cele 8 coloane) şi 8 definiţii "Row-n" ("top" pentru fiecare rând al tablei).
Desigur, nu-i de loc de dorit să scrii manual (într-un fişier HTML) 64 de asemenea <div>… Putem obţine "automat" fişierul respectiv, folosind orice limbaj de nivel înalt; dar cel mai bine este să folosim javaScript, având în vedere că HTML este destinat unui browser, iar browserele moderne încorporează şi un interpretor de javaScript.
<!DOCTYPE html> <head> <style> .container { width: 176px; height: 176px; position: relative; border: 1px solid black; } .field { width: 20px; height: 20px; position: absolute; padding: 1px; /* în loc de `border` */ } .WhiteField { background: white; } .BlackField { background: lightgrey; } .Col-1 { left: 0px } .Row-1 { top: 0px } .Col-2 { left: 22px } .Row-2 { top: 22px } .Col-3 { left: 44px } .Row-3 { top: 44px } .Col-4 { left: 66px } .Row-4 { top: 66px } .Col-5 { left: 88px } .Row-5 { top: 88px } .Col-6 { left: 110px } .Row-6 { top: 110px } .Col-7 { left: 132px } .Row-7 { top: 132px } .Col-8 { left: 154px } .Row-8 { top: 154px } </style> </head> <body> <div class="container" id="tabla"></div> <script> function setChessTable() { var html = []; for (var row = 1; row <= 8; row++) { for (var col = 1; col <= 8; col++) { var fieldColor = (row + col) & 1 ? "BlackField" : "WhiteField"; html.push("<div class='field", " Row-", row, " Col-", col, " ", fieldColor, "'></div>") } } return html.join(''); } document.getElementById("tabla") .innerHTML = setChessTable(); </script> </body>
Am adăugat "pătrăţelelor" şi clasa .BlackField, sau .WhiteField - asigurând alternanţa "background" a câmpurilor (câmpul fiind "negru" în cazul în care suma indicilor 1..8 de linie şi coloană este impară). Cu Firebug putem vedea că apelul setChessTable() a constituit în diviziunea indicată 64 de elemente <div>, fiecare având specificate câte patru clase de proprietăţi CSS:
Să observăm acum avantajul de a folosi direct javaScript (în contextul de aici), în loc de un alt limbaj de nivel înalt. Dacă foloseam C++, sau PHP, sau Python, etc. - puteam obţine un fişier static, având deja cele 64 de elemente <div>. În acest caz browserul nu avea nimic altceva de făcut decât să încarce şi să redea "mot-à-mot" conţinutul respectiv (chiar avantajos, dar numai temporar).
În schimb, funcţia javaScript setChessTable() scrisă în fişierul nostru a permis constituirea dinamică a diviziunilor respective: fişierul încărcat de browser conţinea doar <div class="container"> având conţinutul vid; dar imediat după ce l-a încărcat, browserul a trebuit să execute instrucţiunea din <script> document.getElementById("tabla").innerHTML = setChessTable(), ceea ce a determinat execuţia funcţiei setChessTable() şi inserarea în DOM a celor 64 de <div> vizualizate mai sus.
Dacă tabla de şah ar trebui doar afişată ca atare, atunci desigur că generarea statică a fişierului necesar este preferabilă (dacă nu şi mai simplu: folosim Word-ul). Dar pe tabla de şah vor exista piese, iar acestea îşi vor schimba poziţia în timpul jocului - apărând eventual necesitatea regenerării tablei; ca urmare, generarea dinamică (folosind javaScript) este cea avantajoasă.
Desigur - pentru a avea o "tablă de şah" veritabilă, mai sunt câteva lucruri de considerat: trebuie adăugată o diviziune dedesubtul tablei pentru a nota coloanele (cu 'a', 'b', ..., 'h') şi una în stânga tablei pentru a nota liniile (cu 1..8); iar aceste două diviziuni pot fi şi ele poziţionate "absolute" faţă de tabla respectivă (şi pot refolosi definiţiile "Col-n" şi "Row-n").
Dar aspectul cel mai important ţine de dinamica tablei de şah: în orice moment, există nişte piese de şah care ocupă anumite câmpuri. Pe de altă parte, în orice moment (şi cât mai simplu) tabla de şah trebuie să poată fi "inversată" (albul jos - cu notaţia coloanelor "a..h" şi a liniilor "1..8" - sau albul sus, cu notaţia "h..a" şi "8..1"; poziţiile pieselor trebuie şi ele "răsturnate" odată cu notaţia).
vezi Cărţile mele (de programare)