Conversia între notaţia obişnuită a câmpurilor şi indecşii corespunzători acestora în .x88Board[] poate fi uşurată, dacă am folosi un tabel TO_x88{} în care (de la bun început) vom fi înregistrat asocierile respective. Va fi util şi un tabel ALL_MOVES{} în care, pentru fiecare piesă şi pentru fiecare câmp [a-h][1-8] - să avem (tot în notaţie obişnuită) lista câmpurilor pe care ar putea muta acea piesă, plecând de pe câmpul indicat.
Să zicem că tocmai am preluat din textul PGN al partidei, mutarea albului Ne2. Ştim câmpul final 'e2' şi piesa care trebuie mutată - un cal alb. Nu ştim câmpul iniţial al acestui cal - în schimb (vezi sintaxa SAN în (XI) şi (XII)) mai ştim că numai unul singur dintre caii albi, poate muta legal pe e2; prin urmare, putem proceda ca în acest pseudocod:
/* Reducerea posibilităţilor FROM-TO la aceea care este legală */ piece = 2; // codul calului alb TO = 'e2'; // câmpul final, indicat de SAN (Ne2) table_cal = ALL_MOVES['N'][TO]; // unde poate muta calul de pe TO = 'e2' for (i = 0; i < table_cal.length; i++) { FROM = table_cal[i]; // de exemplu: FROM = 'g1', sau FROM = 'c3' if (BOARD[TO_x88[FROM]] == piece) { // există Cal pe câmpul FROM? dacă mutarea FROM-TO este legală: // Nc3-e2 lasă regele în şah? actualizează x88Board[], conform mutării BREAK // şi treci la următoarea mutare din textul PGN } }
În loc de a căuta pe toată tabla, care cai albi pot muta pe 'e2' - am căutat numai pe cele maximum 8 câmpuri preînregistrate în ALL_MOVES['N']['e2'] (câmpurile "TO" pe care poate sări calul din 'e2', sau totuna - câmpurile "FROM" de unde ar putea veni pe 'e2'). Şi am folosit TO_x88{} pentru a transforma FROM din notaţia obişnuită, în index de acces pe BOARD[].
Desigur, am profitat de faptul că mutările calului (şi la fel pentru celelalte piese) sunt reversibile (după mutarea de pe FROM pe TO este posibil şi invers: de pe TO pe FROM); pentru mutările pionului (care sunt ireversibile) va trebui să procedăm altfel decât în pseudocodul de mai sus.
TO_x88{} se poate obţine direct (fără widget-ul pe care îl tot dezvoltăm de ceva vreme aici), încărcând în browser fişierul următor:
<!DOCTYPE html> <head> <script type="text/javascript" src="js/jquery-1.6.2.min.js"></script> <script type="text/javascript" src="js/json2.js"></script> <title>Tabel de conversie: notaţia obişnuită -> index BOARD[]</title> </head> <body> <div id="to_x88"></div> <script> function ah18_x88() { var a_h = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; var ah_88 = {}; // câmp [a-h][1-8] ==> index în BOARD[] for(var c = 0; c < 8; c++) for(var r = 1; r <= 8; r++) { field = a_h[c] + r; // notaţia obişnuită [a-h][1-8] // asociază indexul în BOARD[] al câmpului ah_88[field] = (field.charCodeAt(0) - 97) + (field.charCodeAt(1) - 49) * 16; } return ah_88; } $(function() { ah_88 = ah18_x88(); $('#to_x88').html("var TO_x88 = " + JSON.stringify(ah_88)); }); </script> </body>
Vizualizăm sursa generată de browser prin execuţia <script>-ului (în Firefox avem un meniu "View Generated Source") şi copiem conţinutul diviziunii "to_x88" (după o mică reformatare) în fişierul JS al widget-ului nostru (îl numisem mai demult, "brw-sah.js"):
var TO_x88 = { 'a1': 0, 'a2': 16, 'a3': 32, 'a4': 48, 'a5': 64, 'a6': 80, 'a7': 96, 'a8': 112, 'b1': 1, 'b2': 17, 'b3': 33, 'b4': 49, 'b5': 65, 'b6': 81, 'b7': 97, 'b8': 113, 'c1': 2, 'c2': 18, 'c3': 34, 'c4': 50, 'c5': 66, 'c6': 82, 'c7': 98, 'c8': 114, 'd1': 3, 'd2': 19, 'd3': 35, 'd4': 51, 'd5': 67, 'd6': 83, 'd7': 99, 'd8': 115, 'e1': 4, 'e2': 20, 'e3': 36, 'e4': 52, 'e5': 68, 'e6': 84, 'e7': 100, 'e8': 116, 'f1': 5, 'f2': 21, 'f3': 37, 'f4': 53, 'f5': 69, 'f6': 85, 'f7': 101, 'f8': 117, 'g1': 6, 'g2': 22, 'g3': 38, 'g4': 54, 'g5': 70, 'g6': 86, 'g7': 102, 'g8': 118, 'h1': 7, 'h2': 23, 'h3': 39, 'h4': 55, 'h5': 71, 'h6': 87, 'h7': 103, 'h8': 119 };
În fişierul redat mai sus am folosit JSON, pentru a obţine imediat (scutindu-ne de a scrie codul prin care se adaugă apostrofurile, două-puncte, virgulele, etc.) reprezentarea textuală a obiectului ah_88 constituit prin funcţia ah18_x88() - invocând metoda .stringify() a obiectului JSON. De fapt, JSON.stringify() este deja disponibil în unele browsere moderne (şi în acest caz se poate elimina linia din <head> prin care se încarcă "json2.js" în documentul redat mai sus).
Pentru calculul acestui tabel folosim metoda _gen_moves(), făcând astfel şi o primă testare a generatorului de mutări (vezi (XV)).
Vom proceda astfel: adăugăm (temporar!) widget-ului nostru o metodă publică .allMovesTable(), care constituie obiectul JS conţinând toate mutările pieselor şi îl înscrie (după serializarea lui, cu JSON.stringify()) în diviziunea de document al cărei identificator i-a fost transmis la apelare.
În .allMovesTable() prevedem întâi, şablonul var allMoves = {'N': {}, ...} pentru obiectul JS care trebuie completat; de exemplu allMoves['N']['e2'] va trebui să fie tabloul ['c1', 'c3', 'd4', 'f4', 'g3', 'g1'], conţinând câmpurile pe care poate sări calul aflat pe 'e2' (sau, la fel de valabil: al câmpurilor de unde calul poate veni pe 'e2').
Apoi, se şterg de pe x88Board[] toate piesele care există eventual în momentul apelării metodei; pe parcurs, se va aşeza pe tablă o singură piesă şi se vor genera mutările posibile ale ei - după care piesa va fi ştearsă, reluând pentru altă poziţionare a ei, sau pentru altă piesă.
Culoarea piesei nu contează - încât se alege albul, pentru piese şi pentru partea aflată la mutare; deasemenea, rocadele nu au sens în acest context - încât se resetează this.castle.
Să observăm însă că anularea drepturilor de rocadă este chiar obligatorie: dacă în momentul apelării valoarea .castle ar fi de exemplu 2 (adică albul poate face încă, mutările O-O şi O-O-O), atunci în toate listele de mutări ale piesei se vor adăuga şi câmpurile "TO" ale rocadelor 'g1', 'c1' - dat fiind că _gen_moves() generează toate mutările posibile pentru piesele de pe tablă ale părţii aflate la mutare şi pe de altă parte, rocadele sunt generate fără testarea prealabilă a prezenţei regilor pe câmpurile cuvenite (aceasta rezultând implicit din valoarea curentă .castle).
Mai departe, pentru fiecare piesă (dintre cele cinci existente) şi pentru fiecare câmp al tablei - se generează lista mutărilor (în codificarea binară pe care am introdus-o anterior) şi apoi se reţine din fiecare mutare numai câmpul "TO", constituind cu aceste valori (convertite la notaţia obişnuită) tabloul care se adaugă corespunzător în obiectul allMoves:
allMovesTable: function(id_dest) { var allMoves = { 'N': {}, 'K': {}, 'B': {}, 'R': {}, 'Q': {} }; // allMoves['N']['e2'] = ['c1', 'c3', 'd4', 'f4', 'g3', 'g1'] var BOARD = this.x88Board; // vom avea o singură piesă (albă) for (var i = 0; i < 128; i++) BOARD[i] = 0; this.to_move = 0; // fixăm Albul la mutare this.castle = 0; // excludem rocadele (important!) for(var p = 4; p <= 12; p += 2) { // N, K, B, R, Q (piesele albe) var piece = PIECE_CHAR[p]; for(var fld in TO_x88) { // 'a1', 'a2', ..., 'h8' BOARD[TO_x88[fld]] = p; // pune piesa p pe câmpul fld var moves = []; // codurile mutărilor posibile ale piesei this._gen_moves(moves); var to_arr = []; // câmpurile TO din codurile mutărilor for(var i = 0, n = moves.length; i < n; i++) { var TO = (moves[i] >> 8) & 0xFF; // extrage indexul TO to_arr.push(// transformă TO în notaţie obişnuită String.fromCharCode(97 + (TO & 7)) + ((TO >> 4) + 1)); } allMoves[piece][fld] = to_arr; BOARD[TO_x88[fld]] = 0; // şterge piesa curentă de pe fld } } $('#' + id_dest).html( // inserează în DOM, la #id_dest JSON.stringify(allMoves, null, 2)); },
Am adăugat .allMovesTable() în cadrul widget-ului nostru numai în scopul de a obţine tabelul ALL_MOVES{}, de care vom avea nevoie mai încolo (şi eventual, în scopul testării generatorului de mutări); după ce vom fi obţinut acest tabel - putem şterge metoda redată mai sus.
Rămâne să invocăm această metodă dintr-un fişier HTML care instanţiază widget-ul nostru - de exemplu (rezumând la strictul necesar):
<!DOCTYPE html> <head> <script src="js/jquery-1.7.2.min.js"></script> <script src="js/jquery.ui.core.min.js"></script> <script src="js/jquery.ui.widget.min.js"></script> <link href="css/pgnbrw.css" rel="stylesheet" type="text/css" /> <script src="js/pgnbrw.js"></script> <title>Tabelul tuturor mutărilor pieselor</title> </head> <body> <textarea id="txtPGN"></textarea> <!-- pentru instanţierea widget-ului --> <div id="allMoves"></div> <!-- aici vom obţine tabelul tuturor mutărilor --> <script> $(function() { $('#txtPGN').pgnbrw({field_size: 20, show_PGN: false, with_FEN: false}) .pgnbrw('allMovesTable', 'allMoves'); }); </script> </body>
Cum se vede mai sus - am făcut unele modificări de denumiri faţă de (VII) (de când n-am mai redat un HTML pentru instanţierea widget-ului): am renunţat la denumirile "brw-sah.js", "brw-sah.css" (de-acum vor fi "pgnbrw.*") şi am redenumit widget-ul prin .pgnbrw(), în loc de ".fenBrowser()".
Nu derularea unei partide de şah ne interesează, încât am instanţiat widget-ul folosind opţiuni de anulare a unor anumite zone; după instanţiere, .pgnbrw('allMovesTable', 'allMoves') invocă metoda publică definită mai sus, indicându-i (ca parametru) diviziunea "allMoves".
Încărcând în browser, obţinem ceea ce redăm parţial alături. Dedesubtul tablei de şah avem textul tabelului care ne interesează; îl copiem din fereastra browserului într-un editor de text - în gedit. Dacă dorim (mai ales, pentru a verifica uşor mutările generate) putem formata textul respectiv folosind meniul "Search/Replace" (căutăm "}, " şi înlocuim cu "},\n\n", etc.).
În final - înscriem tabelul în "pgnbrw.js" (în zona variabilelor exterioare widget-ului):
var ALL_MOVES = { 'N': { 'a1': ['b3','c2'], 'b1': ['a3','c3','d2'], 'c1': ['a2','b3','d3','e2'], // etc. 'g8': ['f6','e7','h6'], 'h8': ['g6','f7'] }, 'K': { 'a1': ['a2','b2','b1'], 'b1': ['a2','b2','c2','a1','c1'], //etc. 'h8': ['g8', 'g7', 'h7'] }, 'B': { 'a1': ['b2','c3','d4','e5','f6','g7','h8'], 'b1': ['c2','d3','e4','f5','g6','h7','a2'], // etc. 'h8': ['a1','b2','c3','d4','e5','f6','g7'] }, 'R': { 'a1': ['b1','c1','d1','e1','f1','g1','h1','a2','a3','a4','a5','a6','a7','a8'], //etc. }, 'Q': { 'a1': ['b1','c1','d1','e1','f1','g1','h1','a2','a3','a4','a5','a6','a7','a8', 'b2','c3','d4','e5','f6','g7','h8'], //etc. } };
Avem de folosit acest tabel (în maniera pseudocodului formulat la început) în cursul metodei de validare a mutării curente din textul PGN al partidei: mutarea SAN respectivă ne dă câmpul TO, iar tabelul de mai sus ne furnizează câmpurile FROM posibile pentru acest TO; aceste posibilităţi "FROM-TO" sunt apoi reduse folosind _gen_moves() şi _makeMove(), la aceea care este legală.
vezi Cărţile mele (de programare)