Copyright 2005 Editura EduSoft Toate drepturile asupra prezentei ediii sunt rezervate Editurii EduSoft. Reproducerea parial sau integral a coninutului, prin orice mijloc, fr acordul scris al Editurii EduSoft este interzis i se va pedepsi conform legislaiei n vigoare. Editura EduSoft 600065 Bacu, str. 9 Mai, nr. 82, sc. C, ap. 13 E-mail: contact@edusoft.ro, Web: www.edusoft.ro
ISBN 973-87655-3-6
CUPRINS
INTRODUCERE PARTEA I. APLICAII N DELPHI 5 1. Ciupercile - un joc de ndemnare i perspicacitate 7 2. Harta - un program despre Romnia turistic 26 3. Mrfuri - un joc de logic 38 4. Puzzle - un joc de perspicacitate 49 5. Supraf - reprezentarea grafic a suprafeelor 54 6. Parser - analiza sintactic i lexical a unei fraze 61 7. AutoWeb - Creator de pagini web 71 8. Zodiac - ce ne este scris n stele? 84 9. Gastro - program pentru reete culinare 102 PARTEA II. APLICAII N VISUAL BASIC 113 10. Calculator de buzunar 115 11. Prinde mutele 122 12. Vntoarea de berze 128 13. Tetris 135 14. Puzzle cu numere 148 15. Puzzle cu imagini 151 16. Bila 158 17. Test gril 163 18. Test de circulaie rutier 171 19. Bioritm 182 20. Editor de hri 196 BIBLIOGRAFIE 211
Introducere
Prin aceast carte dorim s venim n sprijinul tuturor celor care programeaz sau doresc s programeze n mediul Windows i care vor s realizeze aplicaii Windows uor i repede. Aceast carte este o colecie de aplicaii dezvoltate n mediile de programare vizual Delphi i Visual Basic, realizate i comercializate de firmele Inprise (Borland), respectiv Microsoft. Mediul de programare Delphi permite realizarea de programe care s se execute n Windows, cu interfee de tip Windows, pe baza unui limbaj de programare de tip Pascal, iar mediul de programare Visual Basic se bazeaz pe limbajul foarte simplu de nvat Basic. Interfeele aplicaiilor vizuale se implementeaz cu uurin, dar proiectarea i dezvoltarea unor aplicaii inteligente i puternice nu se poate realiza fr un efort de gndire din partea programatorului. Aceasta presupune proiectarea i implementarea n limbajul Object Pascal, respectiv Visual Basic a unor algoritmi eficieni de rezolvare a problemelor n cauz. Aadar, cartea se adreseaz programatorilor serioi, care au cunotinele suficiente de programare obiectual i vizual n mediile de programare menionate. Dar chiar i programatorii obinuii cu medii de programare mai simple pot nva uor programarea vizual n Delphi sau Visual Basic, pe baza unor exemple ca cele din aceast lucrare. Exemplele de aplicaii prezentate n aceast lucrare sunt diferite prin temele pe care le trateaz. Am ncercat s acoperim un numr suficient de situaii pe care orice programator le-ar ntlni atunci cnd ar dori s elaboreze un program mai complex n Delphi sau Visual Basic. Cititorii care doresc s intre n posesia surselor programelor prezentate n aceast carte i a fiierelor cu date (imagini, sunete, text) sunt rugai s ne viziteze site-ul http://edusoft.inf.ro. Cu convingerea c oricine va studia cu atenie aplicaiile prezentate n aceast carte va face din programarea vizual o pasiune, le doresc cititorilor lectur plcut i compilare fr erori! Autorul
Aplicaia 1
Ceea ce observai dumneavoastr n imaginea de mai sus este un moment din timpul desfurrii jocului pe cazul unui labirint dat (vezi LAB3.LBR). De fapt, vom creea, separat, cu un editor de texte simplu (de pild Notepad) mai multe fiiere cu labirinturi, ca cele de mai jos: LAB1.LBR LAB2.LBR @@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@ @ @ @ * & * @ @ & ** * @ @@@@@@@#@@@@@@@@@@@#@@ @@@@@@@#@@@@@@@ @@#@@ @ * * # * # @ @ # # @ @@@#@@@@@@ @@#@@@@@@ @ & # # @ @ # # @ @@@@@@@@@@@@@@@#@@@@@@ @@@#@@ @@@@@@@@@@#@@ @ # @ @ # $ * * # @ @* $ # @ @ #@@@#@@@@@@#@@ # @ @@@#@@@@@@@@@@@@@@@@@@ @ # * # # # @ @ # * @ @ @#@@@# * @@@# @@@@ @ # @@@@@#@@ @ @ # @@@ # @ @ # * # @ @ &# # ** @ @ @#@@@@ # @ @@@@@@#@@@@ @@@@@@@@@ @ # & # & @ @ * &# & * @ @@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@ LAB3.LBR LAB4.LBR @@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@ @ * ** @ @ @ @@@@@@@#@@@ @@@@@#@@ @ **& * * @ @ ** & # * # @ @@@@@@@#@@@@@@@ @@#@@ @@@#@@@@@@ @@#@@@#@@ @ * # ** & # @ @* # * ** # * # @ @@@@#@@@@@ @@@#@#@@@@ @@@#@@@ @@#@@@@@@@#@@ @ *# # # @ @ # # & ** # @ @@@@#@ @@@@# # @ @ # @#@@@@@@#@@ # @ @* # $ # # * @ @ *# $# * &# *# @ @@@#@@@@@@@@@@#@@#@@@@ @ @#@@@# * @@@# @@@@ @ # * *# # @ @ # @@@ # @ @ #@@@@ @@@@#@@# @ @ &# * # & ** @ @ # & * *# # @ @@@@@@ @@#@ @#@@@@@@@ @ @#@@@@ @#@@@ @ @ * & # # & * @ @ # ** & # * @ @@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@
Aceste fiiere text conin, sub o form codificat, toate informaiile necesare reprezentrii att a labirintului n care se desfoar aciunea din jocul curent, ct i poziiile ciupercilor, poziiile iniiale ale caracatielor i a omuleului. Am spus "iniiale", deoarece, o dat cu trecerea timpului, caracatiele vor avea micri aleatorii prin labirint, vor merge pe diferitele niveluri i vor trece de la un nivel la altul folosindu-se de scri. Micrile lor nu vor fi controlate de juctor, ci de un cronometru (Timer1), pe cnd omuleul va fi deplasat de ctre juctor, prin intermediul tastelor de cursor. Codificarea labirintului respect anumite reguli, de aceea, recomandm cititorului ca, nainte de a-i crea propriile fiiere cu labirinturi (LBR), s le realizeze pe cele date ca model. Fiecare simbol folosit n fiierul LBR are o anumit semnificaie: @ = zid; # = scar; * = ciuperc; & = caracati; $ = omuleul. Iat restriciile folosite n crearea labirinturilor: un labirint este o matrice cu 22 coloane i 16 rnduri; labirintul este bordat pe margini cu ziduri (@); simbolul omuleului apare o singur dat ($); scrile (#) pleac de deasupra unui zid i urc pn la un alt nivel, exact ntre dou ziduri; ciupercile (*) i caracatiele (&) stau pe ziduri; se va folosi simbolul ' ' (spaiu) pentru a marca spaiile n cadrul labirintului.
O alt restricie impus de textul programului, aa dup cum se va vedea, este ca numrul de caracatie s nu depeasc 10, iar numrul de ciuperci s nu fie mai mare de 30. Dac vei crea un labirint greit, atunci vei avea probleme i de acest lucru v vei da seama n timpul execuiei programului. l vei opri i vei corecta labirintul pn nu vor mai aprea probleme. De asemenea, putei porni de la un labirint prezentat i l modificai dup dorin, respectnd restriciile de mai sus.
Declaraiile anterioare definesc o form simpl coninnd doar un buton, dou etichete i un cronometru:
10
Toate celelalte lucruri care vor aprea n joc vor fi desenate direct pe form, folosind metodele de desenare ce vor aciona asupra proprietii Canvas a formei Form1. Firete, imediat dup nceputul jocului, butonul Buton1, coninnd acel text explicativ, va disprea.
implementation {$R *.DFM}
n cadrul programului vom fi nevoii s desenm mai multe figurine (omuleul, ciupercile etc.). De aceea, vom folosi un procedeu de desenare relativ, pixel cu pixel, a unor curbe, implementat de procedura de mai jos. Am ncadrat n chenar aceast procedur, pentru c o considerm de interes general pentru cititor i la fel vom proceda i cu alte proceduri de acest gen, n cadrul crii.
procedure DeseneazaCurba(x0,y0,c: Integer; s: String); var i: Byte; x,y: Integer; begin x:=x0; y:=y0; for i:=1 to Length(s) do begin case s[i] of '1': Dec(y); '2': begin Inc(x); Dec(y) end; '3': Inc(x); '4': begin Inc(x); Inc(y) end; '5': Inc(y);
11
'6': begin Dec(x); Inc(y) end; '7': Dec(x); '8': begin Dec(x); Dec(y) end end; Form1.Canvas.Pixels[x,y]:=c end end;
Aceast procedur deseneaz o curb, punct cu punct, care are culoarea c. Punctul de plecare al curbei are coordonatele x0, y0, iar fiecare din urmtoarele puncte va avea coordonate n funcie de coordonatele punctului precedent. n acest sens se va folosi o codificare prin cifrele '1'..'8', reprezentnd direciile de "deplasare" pentru desenarea punctului urmtor.
De pild, pentru a desena curba din urmtoarea figur, vom apela procedura anterioar astfel: DeseneazaCurba(x0,y0,clBlack,'023354').
Ciupercile i omuleul se deplaseaz prin labirint. Deplasarea va presupune tergerea figurinei din poziia veche i redesenarea ei n noua poziie. tergerea se va face cu ajutorul procedurii de mai 12
jos, ai crei parametri indic zona dreptunghiular care urmeaz s fie tears, prin colorarea tuturor punctelor sale n culoarea fondului (aici clBtnFace).
procedure ClearView(x1,y1,x2,y2: Integer); var i,j: Integer; begin for i:=x1 to x2 do for j:=y1 to y2 do Form1.Canvas.Pixels[i,j]:=clBtnFace end;
Urmeaz declaraiile de constante, variabile i tipuri de date referitoare la toate personajele din scenariul nostru:
const lat=24;
Aceast variabil reprezint mrimea laturii oricrui ptrel din labirint (fie c este zid, scar sau altceva). Labrintul propriu-zis va fi stocat sub forma unei matrice cu 16 linii i 22 coloane:
var L: array[1..16,1..22] of Char;
Prin vieti am notat numrul de viei ale omuleului, care va fi iniial 10 i va scdea de fiecare dat cnd o caracati l va ntlni. Xom, Yom sunt coordonatele curente ale omuleului, iar Xom_i i Yom_i sunt coordonatele sale iniiale.
var vieti, Xom, Yom, Xom_i, Yom_i: Integer;
Numrul maxim de ciuperci i numrul maxim de caracatie este precizat prin declaraiile:
const max_ciup=30; max_carac=10;
Urmeaz declaraiile unor tipuri de date obiectuale, TCiuperca i TCaracatita. O ciuperc este caracterizat de coordonatele sale n labirint i ca metode avem Init pentru iniializare, Display pentru afiare i Clear, pentru tergere, folosit atunci cnd omuleul a cules ciuperca n cauz.
type TCiuperca = object x,y: Integer; procedure Init(x0,y0: Integer); procedure Display;
13
O caracati are, n plus, un atribut mut care reprezint sensul deplasrii caracatiei la un moment dat, deplasarea propriu-zis fcndu-se cu ajutorul metodei Move. Dup definirea tipului de date TCaracatita urmeaz declararea vectorilor cu ciuperci i caracatie.
type TCaracatita = object x,y,mut: Integer; procedure Init(x0,y0: Integer); procedure Display; procedure Clear; procedure Move; end; var Ciup: array[0..max_ciup] of TCiuperca; Carac: array[0..max_carac] of TCaracatita;
Vom avea nevoie i de urmtoarele trei variabile, reprezentnd respectiv numrul de ciuperci rmase n labirint, numrul iniial de ciuperci, numrul de caracatie:
var ciuperci,ciuperci_initiale, caracatite: Integer;
Desenarea omuleului n ptrelul de coordonate i, j din matricea labirintului se va face cu procedura de mai jos, care apeleaz att procedura DeseneazaCurba, ct i ale metode grafice (FloodFill, Rectangle) ce scriu direct n proprietatea Canvas a formei Form1.
procedure Omulet(i,j: Integer); begin DeseneazaCurba(lat*j+3,lat*i+11,clBlack, '0555443433336666653332321244443331'+ '8888833322221776676777777777888'); Form1.Canvas.Brush.Style:=bsSolid; Form1.Canvas.Brush.Color:=clBlue; Form1.Canvas.FloodFill(lat*j+4,lat*i+14, clBlack,fsBorder); Form1.Canvas.Brush.Color:=clBlue; Form1.Canvas.FloodFill(lat*j+12,lat*i+16, clBlack,fsBorder); DeseneazaCurba(lat*j+8,lat*i+7,clYellow, '0565454433333222118187777777');
14
Form1.Canvas.Brush.Color:=clYellow; Form1.Canvas.FloodFill(lat*j+10,lat*i+9, clYellow,fsBorder); Form1.Canvas.Pen.Color:=clGreen; Form1.Canvas.Pen.Width:=1; Form1.Canvas.Brush.Color:=clGreen; Form1.Canvas.Rectangle(lat*j+7,lat*i+3, lat*j+18,lat*i+6); Form1.Canvas.Pen.Color:=clWhite; Form1.Canvas.MoveTo(lat*j+11,lat*i+2); Form1.Canvas.LineTo(lat*j+14,lat*i+2); DeseneazaCurba(lat*j+10,lat*i+8,clFuchsia,'0357'); DeseneazaCurba(lat*j+14,lat*i+8,clFuchsia,'0357'); DeseneazaCurba(lat*j+10,lat*i+11,clRed,'03533313') end;
Procedura Tipar realizeaz afiarea informaiei corespunztoare unei celule a matricei labirintului, n funcie de coninutul acesteia (dat de L[i,j], unde i i j sunt linia, respectiv coloana acelei celule).
procedure Tipar(i,j: Integer); begin case L[i,j] of ' ': ClearView(lat*j+1,lat*i+1, lat*j+lat,lat*i+lat-1); '*': begin Ciup[0].Init(i,j); Ciup[0].Display end; { '&': begin Carac[0].Init(i,j); Carac[0].Display end; } '#': begin Form1.Canvas.Pen.Color:=clmaroon; Form1.Canvas.Pen.Width:=3; Form1.Canvas.Moveto(lat*j+lat div 3,lat*i); Form1.Canvas.LineTo(lat*j+lat div 3, lat*(i+1)-1); Form1.Canvas.MoveTo(lat*(j+1)-lat div 3, lat*i); Form1.Canvas.LineTo(lat*(j+1)-lat div 3, lat*(i+1)-1); Form1.Canvas.MoveTo(lat*j+lat div 3, lat*i+lat div 3); Form1.Canvas.LineTo(lat*(j+1)-lat div 3, lat*i+lat div 3) end; '@': begin
15
Form1.Canvas.Pen.Width:=1; Form1.Canvas.Pen.Color:=clMaroon; Form1.Canvas.Brush.Color:=clRed; Form1.Canvas.Brush.Style:=bsDiagCross; Form1.Canvas.Rectangle(lat*j+1,lat*i, lat*(j+1)-1,lat*(i+1)) end; '$': Omulet(i,j) end end;
n continuare, sunt prezentate cele trei metode ale obiectelor cele trei metode ale obiectelor TCiuperca:
procedure TCiuperca.Init; begin x:=x0; y:=y0 end; procedure TCiuperca.Display; var i,j: Byte; begin i:=x; j:=y; DeseneazaCurba(lat*j+3,lat*i+9,clYellow, '0112223232323334334344445577678777777777767777'); Form1.Canvas.Brush.Color:=clYellow; Form1.Canvas.Brush.Style:=bsSolid; Form1.Canvas.FloodFill(lat*j+4,lat*i+8, clYellow,fsBorder); DeseneazaCurba(lat*j+5,lat*i+7,clRed,'0313557'); DeseneazaCurba(lat*j+13,lat*i+7,clRed,'01353'); DeseneazaCurba(lat*j+13,lat*i+3,clRed,'0753'); DeseneazaCurba(lat*j+18,lat*i+6,clRed,'0135'); DeseneazaCurba(lat*j+11,lat*i+10,clLime, '05556565656544333332212181818111177'); Form1.Canvas.Brush.Color:=clLime; Form1.Canvas.Brush.Style:=bsSolid; Form1.Canvas.FloodFill(lat*j+11,lat*i+17,clLime,fsBorder) end; procedure TCiuperca.Clear; begin ClearView(lat*y+1,lat*x+1,lat*y+lat-1,lat*x+lat-1) end;
De asemenea, TCaracatita:
avem
i 16
metodele
obiectelor
de
tip
n metoda de iniializare, proprietatea (atributul) mut ia una din valorile 0 sau 1, pentru o deplasare pe orizontal.
procedure TCaracatita.Display; var i,j: Byte; begin i:=x; j:=y; DeseneazaCurba(lat*j+1,lat*i+15,clBlue, '01222211112123132333433'+ '44455555644344877877644444554881717171717755'+ '44535558181188166557555611211111'+ '767757557561121222177675773123'); Form1.Canvas.Brush.Color:=clAqua; Form1.Canvas.Brush.Style:=bsSolid; Form1.Canvas.FloodFill(lat*j+10,lat*i+4, clBlue,fsBorder); DeseneazaCurba(lat*j+7,lat*i+6, clGreen,'02334557778333168'); DeseneazaCurba(lat*j+13,lat*i+6, clGreen,'0233455777825331') end;
De remarcat c metoda de Clear a metodei TCaracatita se termin prin apelul procedurii Tipar, pentru a restabili coninutul "de sub" caracati din celula de unde pleac acea caracati (care ar putea fi spaiu, scar sau ciuperc). Urmeaz descrierea metodei TCaracatita.Move de deplasare a unei caracatie. Ea apeleaz la o procedur intern cu numele Muta, ce are ca argument sensul deplasrii (m: Integer).
procedure TCaracatita.Move; procedure Muta(m: Integer); begin
17
Clear; Tipar(x,y); case m of 0: y:=y-1; 1: y:=y+1; 2: x:=x+1; 3: x:=x-1 end; Display end; begin case L[x,y] of ' ','*': if L[x+1,y]='@' then case mut of 0: if y>2 then if L[x+1,y-1] in ['@','#'] then Muta(mut) else mut:=1 else mut:=1; 1: if y<21 then if L[x+1,y+1] in ['@','#'] then Muta(mut) else mut:=0 else mut:=0 end else { # } begin if Random(10)<3 then mut:=2 else if mut=3 then mut:=Random(2) else Muta(mut) end; '#': case mut of 0: if Random(10)<7 then mut:=3 else if y>2 then if L[x+1,y-1] in ['@','#'] then Muta(mut) else mut:=1 else mut:=1; 1: if Random(10)<5 then mut:=3 else if y<21 then if L[x+1,y+1] in ['@','#'] then Muta(mut)
18
else mut:=0 else mut:=0; 2: if L[x+1,y]='@' then if Random(10)<2 then mut:=3 else mut:=Random(2) else Muta(mut); {sus} 3: if L[x+1,y] in ['#','@'] then Muta(mut) else begin if Random(10)<3 then mut:=2 else mut:=Random(2) end end end end;
Dei la prima vedere procedura poate prea complicat, algoritmul este relativ simplu. Astfel, se apeleaz Muta(mut), inndu-se cont de mai multe restricii pe care caracatiele le au n deplasrile lor. Astfel, nu se poate ca o caracati s peasc "n gol", adic s cad de pe scar sau s nu meag pe ziduri. De asemenea, o dat ce a ales s urce o scar, atunci o va face pn la capt, fr a o cobor pe la jumtatea drumului. Tot la fel stau lucrurile i n cazul unei deplasri pe un anumit nivel. Se poate observa c, n urma aplicrii diferitelor restricii, se genereaz urmtoarea valoare a cmpului mut, care indic sensul deplasrii urmtoare a caracatiei, pe baza procedurii Muta. O procedur important (ce va fi apelat mai jos de procedura TForm1.Button1Click. Aceast procedur folosete o variabil F de tip TextFile (atenie la acest tip de date, care, n Turbo Pascal era numit simplu Text!). Fiierul cu labirintul se asociaz lui F, prin AssignFile (atenie la aceast funcie, care n Turbo Pascal era denumit simplu Assign) care este deschis pentru citirea de date, sub forma unei matrice de caractere. Fiecare citire a lui L[i,j] este urmat de desenarea/iniializarea corespunztoare. Fiierul se nchide la sfrit cu CloseFile(F) (atenie, n Turbo Pascal am fi avut Close(F)!)
procedure CitesteLabirint(nf: String); var F: TextFile; i,j: Byte;
19
begin vieti:=10; ciuperci:=0; AssignFile(F,nf); Reset(F); for i:=1 to 16 do begin for j:=1 to 22 do begin Read(F,L[i,j]); {zid, scara sau omulet -> se deseneaza} if L[i,j] in ['#','@','$'] then Tipar(i,j); if L[i,j]='$' then {omuletul} begin L[i,j]:=' '; Xom:=i; Yom:=j end; case L[i,j] of '*': begin {ciuperca se creeaza, initializeaza si afiseaza} Inc(ciuperci); Ciup[ciuperci].Init(i,j); Ciup[ciuperci].Display end; '&': begin {caracatita se creeaza, initializeaza si afiseaza} Inc(caracatite); Carac[caracatite].Init(i,j); Carac[caracatite].Display; L[i,j]:=' ' end; end end; ReadLn(F) end; CloseFile(F); Xom_i:=Xom; Yom_i:=Yom; ciuperci_initiale:=ciuperci end;
Urmtoarele dou procedurii afieaz punctajul curent realizat de juctor i numrul de viei ce i-au mai rmas omuleului. Punctajul se realizeaz pe baza diferenei dintre ciuperci_initiale i ciuperci, adic este dat de numrul ciupercilor colectate pn atunci.
procedure AfisPunctaj; var s: String; begin
20
Str(ciuperci_initiale-ciuperci,s); Form1.Label1.Caption:='PUNCTAJ: '+s; end; procedure AfisVieti; var s: String; begin Str(vieti,s); Form1.Label2.Caption:='VIETI RAMASE: '+s; end;
Numrul de viei i punctajul curent reprezint indicatori de terminare a jocului. Astfel, dac nu au mai rmas ciuperci n labirint, aceasta nseamn c jocul s-a terminat cu succes, iar dac omuleul i-a epuizat vieile, aceasta nseamn c jocul s-a terminat cu eec. Procedura care se ocup de aceste testri este dat mai jos. nainte de a face cele dou testri, procedura verific dac nu cumva vreo caracati ntlnete n drum omuleul, ceea ce presupune penalizarea juctorului prin pierderea unei viei a omuleului.
procedure TestVietiSiPunctaj; var i: Byte; gata: Boolean; begin i:=1; gata:=False; while (i<=caracatite) and (not gata) do if (Carac[i].x=Xom) and (Carac[i].y=Yom) then begin Dec(vieti); gata:=True; AfisVieti; { Xom:=Xom_i; Yom:=Yom_i; } Omulet(Xom,Yom); end else Inc(i); if vieti=0 then begin Form1.Timer1.Interval:=0; ShowMessage('Joc pierdut!'); Application.Terminate end else if ciuperci=0 then begin Form1.Timer1.Interval:=0; ShowMessage('Joc castigat!'); Application.Terminate end end;
21
Procedura MutaCaracatite se ocup de deplasarea ntregului ansamblu de inamici ai omuleului i de testararea ndeplinirii vreuneia dintre condiiile de terminare ale jocului. Iar mutarea efectiv este apelat la un anumit interval de timp, prin procedura TForm1.Timer1Timer.
procedure MutaCaracatite; var i: Byte; begin for i:=1 to caracatite do Carac[i].Move; TestVietiSiPunctaj end; procedure TForm1.Timer1Timer(Sender: TObject); begin MutaCaracatite end;
Procedura care creeaz forma face anumite iniializri asupra caracteristicilor formei i asupra lui Timer1, pentru ca acesta s nu acioneze nc asupra caracatielor.
procedure TForm1.FormCreate(Sender: TObject); begin Label1.Hide; Label2.Hide; Randomize; Timer1.Interval:=0; Caption:='Ciupercile'; Top:=50; Left:=50; Width:=580; Height:=470; BorderStyle:=bsSingle; Label1.Left:=100; Label2.Left:=400; Label1.Top:=420; Label2.Top:=420 end;
Abia dup ce se acioneaz butonul de pornire, jocul ncepe. Mai nti va trebui s dai numrul labirintului, stabilit implicit la 3, apoi procedura TForm1.Button1Click apeleaz la o procedur de citire a labirintului din fiierul corespunztor i se d drumul la cronometru. Intervalul lui Timer1 este stabilit invers proporional cu numrul de caracatie.
procedure TForm1.Button1Click(Sender: TObject); var sir: String; begin Button1.Hide;
22
sir:=InputBox('Ciupercile', 'Dati numarul labirintului [1..4]!','3'); CitesteLabirint('LAB'+sir+'.LBR'); Label1.Show; Label2.Show; AfisVieti; AfisPunctaj; Timer1.Interval:=1000 div caracatite end;
Introducerea numrului labirintului se realizeaz prin apelul funciei InputBox, care deschide o fereastr de dialog ca cea de mai jos. Citirea labirintului se realizeaz de procedura CitesteLabirint, care a fost prezentat mai nainte.
Deplasarea omuleului o face juctorul prin intermediul evenimentelor de tastatur ce au loc asupra lui Form1. n acest sens, se folosete evenimentul OnKeyDown, adic metoda FormKeyDown, n care Key reprezint codul tastei apsate. Cele patru numere (40, 38, 37 i 39) corespund celor patru sensuri de deplasare ale omuleului: jos, sus, stnga i respectiv dreapta. Observai c dac L[Xom, Yom] are valoarea '*', adic omuleul a ntlnit o ciupercu, atunci punctajul crete, ciupercua dispare (se terge de pe ecran, iar variabila ciuperci descrete).
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); var aux: Char; begin case Key of 40: if L[Xom+1,Yom]<>'@' then { jos } begin aux:=L[Xom,Yom]; L[Xom,Yom]:=' '; Tipar(Xom,Yom); L[Xom,Yom]:=aux; Tipar(Xom,Yom); Inc(Xom); Omulet(Xom,Yom); if L[Xom,Yom]='*' then begin L[XOm,YOm]:=' '; Dec(ciuperci); Tipar(Xom,Yom); Omulet(Xom,Yom); AfisPunctaj
23
end end; 38: if (L[Xom,Yom]='#') and (L[Xom-1,Yom] in ['#',' ','*']) then {sus} begin aux:=L[Xom,Yom]; L[Xom,Yom]:=' '; Tipar(Xom,Yom); L[Xom,Yom]:=aux; Tipar(Xom,Yom); Dec(Xom); Omulet(Xom,Yom); if L[Xom,Yom]='*' then begin L[XOm,YOm]:=' '; Dec(ciuperci); Tipar(Xom,Yom); Omulet(Xom,Yom); AfisPunctaj end end; 37: if (L[Xom,Yom] in ['#',' ']) and (L[Xom,Yom-1]<>'@') and (L[Xom+1,Yom-1] in ['@','#']) then {stg} begin aux:=L[Xom,Yom]; L[Xom,Yom]:=' '; Tipar(Xom,Yom); L[Xom,Yom]:=aux; Tipar(Xom,Yom); Dec(Yom); Omulet(Xom,Yom); if L[Xom,Yom]='*' then begin L[XOm,YOm]:=' '; Dec(ciuperci); Tipar(Xom,Yom); Omulet(Xom,Yom); AfisPunctaj end end; 39: if (L[Xom,Yom] in ['#',' ']) and (L[Xom,Yom+1]<>'@') and (L[Xom+1,Yom+1] in ['@','#']) then {dr} begin aux:=L[Xom,Yom]; L[Xom,Yom]:=' '; Tipar(Xom,Yom); L[Xom,Yom]:=aux; Tipar(Xom,Yom); Inc(Yom); Omulet(Xom,Yom); if L[Xom,Yom]='*' then begin L[XOm,YOm]:=' '; Dec(ciuperci); Tipar(Xom,Yom); Omulet(Xom,Yom); AfisPunctaj end end end end;
24
Pentru a nu avea probleme n cazul n care fereastra aplicaiei noastre este acoperit, din ntmplare sau intenionat, de o alt fereastr, vom scrie o procedur de redesenare a ntregului labirint dup cum urmeaz:
procedure TForm1.FormPaint(Sender: TObject); var i,j: Integer; begin for i:=1 to 16 do for j:=1 to 22 do if L[i,j] in ['#','$','@','*','&'] then Tipar(i,j) end; end.
n acest moment, codul surs al unit-ului ciupercile1.pas din aplicaia noastr s-a terminat.
25
Aplicaia 2
n acest program avem dou forme. Forma Form1 (prima activat) va aprea ca mai sus. Trecnd cu mouse-ul peste judeele din harta administrativ a rii, vom alege un anumit jude. Alegerea judeului se face n funcie de coordonatele curente ale cursorului de mouse. Nu se respect exact forma frontierelor de jude, ci doar nite 26
zone dreptunghiulare, alese corespunztor acestor judee. n figura de mai sus este reprezentat zona dreptunghiular a judeului Alba, precum i poziia cursorului de mouse n cadrul acestei zone. De remarcat c zonele dreptunghiulare ale judeelor nu vor figura pe hart, n timpul programului, deci ele vor fi invizibile pentru utilizatorul aplicaiei noastre. Aceste dreptunghiuri sunt memorate ntr-un fiier text special, cu numele romap.txt, fiier cu urmtoarea structur: nume jude x1 y1 x2 y2 .................. Deci, sunt scrise numele judeelor rii, apoi, pentru fiecare jude n parte coordonatele colului stnga sus i ale colului dreapta jos a zonei dreptunghiulare ce ncadreaz o mare parte din jude. Concret, coninutul fiierului romap.txt folosit n aplicaia noastr este:
Alba 179 165 222 217 Arad 41 157 131 200 Arges 268 240 308 339 Bacau 360 136 445 195 Bihor 101 99 148 148 Bistrita-Nasaud 235 77 288 107 Botosani 365 0 437 66 Brasov 279 208 347 248 Braila 440 240 487 305 Buzau 376 234 435 303 Calarasi 380 343 473 371 Caras-Severin 75 242 139 309 Cluj 164 127 233 159 Constanta 480 305 546 396 Covasna 324 187 385 229 Dambovita 307 272 340 321 Dolj 173 339 241 402 Galati
27
441 182 496 242 Giurgiu 337 348 377 396 Gorj 161 267 220 328 Harghita 298 105 348 183 Hunedoara 137 188 186 266 Ialomita 381 302 490 344 Iasi 388 65 481 127 Ilfov 348 310 382 352 Maramures 197 48 276 68 Mehedinti 119 303 182 365 Mures 247 136 293 180 Neamt 334 94 430 143 Olt 238 315 277 403 Prahova 335 248 387 307 Satu Mare 123 17 188 69 Salaj 155 86 201 115 Sibiu 222 199 267 236 Suceava 291 21 375 92 Teleorman 277 339 343 413 Timis 6 200 115 241 Tulcea 487 237 592 309 Vaslui 429 114 496 189 Valcea 219 246 262 326 Vrancea 384 185 438 241 Bucuresti 348 310 382 352
Numele judeului deasupra cruia se afl cursorul de mouse va fi afiat n antetul (proprietatea Caption) ferestrei Form1 (vezi figura de mai sus). Selectarea unui jude pentru a obine informaii 28
despre el se face prin acionarea butonului de mouse n interiorul dreptunghiului corespunztor lui. Astfel, de pild, dac se alege judeul Alba, se va ncrca o nou form, Form2, adic va aprea fereastra a doua de mai jos, prima fiind dezactivat pn la nchiderea celei de a doua.
Aadar, Form2 va conine un control de tip TImage (stnga sus), unul de tip TListBox (dreapta sus) i o caset de tip TMemo, pentru afiarea coninutului unui fiier text, asociat judeului selectat. Firete, avnd 42 de judee, vom avea tot attea fiiere text, al cror nume, aa cum se va vedea n textul programului, vor fi chiar numele judeelor Romniei, completate cu extensia '.txt'. Lista din dreapta sus conine numele unor fiiere '.bmp', coninnd imagini din judeul ales. Aceste nume sunt, de fapt, precizate n cadrul fiierului text al judeului, n primele linii (dar pot fi i n alte linii), i sunt precedate de simbolul '@'. Astfel, de pild, fiierul alba.txt va avea coninutul urmtor: 29
@poartain.bmp @panorab.bmp @ortod_ai.bmp JUDETUL ALBA # Date generale Judet in partea central-vestica a Romaniei, pe cursul mijlociu al Muresului; 6,2 mii kmp; Resedinta: Alba-Iulia. Orase: Sebes, Aiud, Cugir, Blaj, Ocna Mures, Zlatna, Campeni si Abrud. # Relieful si clima Relief accidentat; in vest M-tii Metaliferi, Trascaului si Bihor, cu cateva depresiuni montane (Zlatna, Abrud, Campeni); in est Valea Muresului si culmile de vest ale Podisului Tarnavelor. Clima temperat-continentala, cu ierni mai reci in zona montana; precipitatiile variaza teritorial (600-1100 mm/an). # Economia Exploatari: forestiere, de minereuri auroargentifere (Zlatna, Baia de Aries, Almasu Mare, Rosia Montana) si cuprifere (Bucium), minereuri de cinabru (Izvoru Ampoiului), gaze naturale (Cetatea de Balta), sare (Ocna Muresului), roci de constructie etc. Cele mai importante ramuri industriale ale judetului sunt: extractia minereurilor neferoase si metalurgia neferoasa, exploatarea si prelucrarea lemnului, constructia de masini, chimica (soda calcinata si caustica, antidaunatori), alimentara, materiale de constructie. Suprafata agricola a judetului este de 352 mii ha, din care 158 mii ha sunt terenuri arabile. Pe terenurile arabile ale judetului se cultiva cereale (102,7 mii ha), floarea-soarelui, cartofi si sfecla de zahar; viticultura (podgoriile Alba-Iulia, Blaj, Craciunelu de Jos, Jidvei, Valea Lunga, Aiud); pomicultura; legumicultura. Cresterea animalelor (bovine, porcine si ovine); avicultura. # Turism Zona turistica, cu numeroase locuri pitoresti (Pestera Scarisoara si Pojarul Politei; Detunatele); monumente istorice si de arhitectura.
Observai primele trei linii din acest fiier. Ele conin exact numele acelor fiiere cu imagini, care sunt trecute n lista ListBox1 din dreapta sus a lui Form2 i sunt precedate de simbolul '@'. Urmeaz textul referitor la judeul Alba, care se regsete n componenta Memo1 din cadrul lui Form2. n imaginea anterioar, s-a acionat pe al doilea item din list i s-a afiat imaginea panoramic a municipiului Alba-Iulia, aflat n fiierul 'panorab.bmp'. Atunci cnd vei realiza concret aplicaia dumneavoastr, va trebui s dispunei de mai multe fiiere cu imagini, pentru fiecare jude n parte, precum i de cte un fiier text (l putei crea chiar dumneavoastr), pentru fiecare jude al rii. La toate acestea se adaug imaginea cu harta administrativ a Romniei, pe care o putei scana chiar dup pagina din carte, dac dispunei de un scanner, sau o putei lua de pe Internet, dac dispunei de o astfel de conexiune. Exist multe locaii web, unde sunt date despre Romnia i de acolo ai putea chiar s luai nu doar harta Romniei, ci i alte informaii, chiar texte referitoare la judeele 30
rii sau imagini de pe ntregul cuprins al patriei. ncercai, de pild, adresele http://www.rotravel.com i http://www.ici.ro. Dup nchiderea formei Form2, se revine la forma Form1, de unde se poate alege un alt jude, pentru a fi afiate date corespunztoare lui.
Primul unit corespunde unei forme Form1 foarte simple, n care se gsete o component Image1, care imediat dup crearea formei se ncarc cu coninutul fiierului romania.bmp. De asemenea, procedura TForm1.FormCreate va conine i instruciuni de adaptare a dimensiunilor formei Form1 la dimensiunile imaginii cu harta Romniei. Atenie la declaraia uses harta2 din poriunea implementation a unit-ului harta1.
unit harta1; interface uses Windows, Messages, SysUtils, Classes, Graphics,
31
Controls, Forms, Dialogs, ExtCtrls; type TForm1 = class(TForm) Image1: TImage; procedure FormCreate(Sender: TObject); procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer); procedure Image1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; numejudet: String; implementation uses harta2; {$R *.DFM} procedure TForm1.FormCreate(Sender: TObject); begin Form1.Top:=100; Form1.Left:=100; Image1.Left:=0; Image1.Top:=0; Image1.Picture.LoadFromFile('romania.bmp'); Image1.AutoSize:=True; Form1.ClientWidth:=Image1.Width; Form1.ClientHeight:=Image1.Height end;
Va trebui, n continuare, s scriem procedura asociat evenimentului de deplasare a cursorului de mouse peste imaginea hrii:
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer); var f: TextFile; x1,y1,x2,y2: Integer; gasit: Boolean; begin AssignFile(f,'romap.txt'); Reset(f); gasit:=False; while (not gasit) and (not eof(f)) do
32
begin ReadLn(f,numejudet); ReadLn(f,x1,y1,x2,y2); if (x1<=X) and (X<=x2) and (y1<=Y) and (Y<=y2) then gasit:=True end; if gasit then Form1.Caption:=numejudet else numejudet:=''; CloseFile(f) end;
Dup cum se poate vedea, procedura realizeaz o cutare secvenial a primului dreptunghi (specificat de coordonatele x1, y1, x2 i y2) care cuprinde n interior coordonatele cursorului de mouse: X i Y. Dac se gsete un asemenea dreptunghi, atunci se actualizeaz n mod corespunztor variabila numejudet. Dac, n plus, se realizeaz un click de mouse pe dreptunghiul unui jude, atunci se execut procedura de mai jos, care dezactiveaz forma curent (Form1) i o ncarc pe Form2, de care se ocup unit-ul harta2.pas.
procedure TForm1.Image1Click(Sender: TObject); begin if numejudet<>'' then begin Form1.Enabled:=False; Form2.Show; Form2.Caption:='Informatii despre judetul '+numejudet; Form2.Top:=150; Form2.Left:=150 end end; end.
Unit-ul harta2 implementeaz operaiile de lucru asupra componentelor din Form2. Interfaa acestui unit este urmtoarea:
unit harta2; interface uses Windows, Messages, SysUtils, Classes, Graphics,
33
Controls, Forms, Dialogs, ExtCtrls, StdCtrls; type TForm2 = class(TForm) Image1: TImage; Memo1: TMemo; ListBox1: TListBox; procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure FormShow(Sender: TObject); procedure ListBox1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form2: TForm2;
Seciunea de implementare conine o referire la unit-ul descris anterior, apoi este prezentat procedura care rspunde evenimentului de nchidere a formei, prin care controlul execuiei programului se d formei Form1, activat prin instruciunea Form1.Enabled:=True.
implementation uses harta1; {$R *.DFM} procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction); begin Form1.Enabled:=True end;
Cnd se afieaz Form2, conform apelului din TForm1.Image1Click, se va apela, de fapt, procedura de mai jos, care rspune evenimentului n cauz:
procedure TForm2.FormShow(Sender: TObject); var i,j: Integer; s: String; f: TextFile; begin nr_imagine:=-1; ListBox1.Clear; Memo1.Clear; Image1.Picture.Graphic:=nil;
34
Memo1.ScrollBars:=ssBoth; Image1.Stretch:=True; AssignFile(f,numejudet+'.txt'); {$I-}Reset(f);{$I+} if IOResult=0 then begin while not eof(f) do begin ReadLn(f,s); if s[1]='@' then ListBox1.Items.Add(Copy(s,2,Length(s)-1)) else Memo1.Lines.Add(s) end; CloseFile(f); end else begin Image1.Picture.Graphic:=nil; ListBox1.Clear; Memo1.Clear; Memo1.Lines.Add('Nu detinem informatii'); Memo1.Lines.Add('despre acest judet!') end end;
S analizm pe ndelete procedura de mai nainte. Mai nti au loc unele iniializri importante referitoare la Memo1, ListBox1 i Image1, apoi se adaug componentei Memo1 ambele bare de derulare, pentru a putea citi textul ce va aprea acolo n ntregime, chiar dac s-a putea ca s fie prea lat sau prea lung. Apoi se configureaz proprietatea Stretch a imaginii Image1 la adevrat (True), pentru ca imaginea s nu depeasc marginile stabilite. Firete, acest lucru poate provoca neplceri prin deformarea imaginii din Image1, de aceea este bine s se opteze pentru o soluie mai bun cum ar fi: redimensionarea tuturor imaginilor astfel nct s ncap n componenta Image1, chiar dac Image1.Stretch ar rmne False; realizarea unei redimensionri a lui Image1, astfel nct s existe proporii corespunztoare ntre grosimea i nlimea imaginii din fiierul '.bmp' i proprietile Width i, respectiv, Height ale lui Image1.
nr_imagine:=-1; ListBox1.Clear; Memo1.Clear;
35
n continuare se deschide fiierul text i se citete coninutul su linie cu linie. Liniile care ncep cu simbolul '@' sunt prelucrate separat i se adaug n lista ListBox1. Celelalte sunt adugate n componenta Memo1. Firete, un fiier text poate fi ncrcat ntr-o component de tip TMemo mult mai simplu, printr-un apel de genul Memo1.Lines.LoadFromFile(numejudet+'.txt'), dar acest procedeu nu putea fi aplicat aici, din cauz c trebuia s se preia separat liniile ce ncepeau cu '@'. Oricum, procedeul trebuie reinut pentru utilizri ulterioare. Observai, de asemenea, apariia a dou opiuni speciale de compilare ce ncadreaz pe Reset(f). Prima are rolul de a dezactiva controlul operaiilor de intrare-ieire de ctre compilator, iar cea de al doua are rolul de a reface activarea. Dezactivarea prin {$I-} determin compilatorul Pascal s nu reacioneze n cazul unui eec al lui Reset(f) printr-un mesaj de eroare a unei operaii de intrare/ieire. Astfel, dac o problem oarecare apare, controlul este preluat de programul nostru, care testeaz variabila de sistem predefinit IOResult. Cnd aceasta are valoarea 0, nseamn c totul este n regul, iar cnd nu este 0, valoarea ei indic tipul de eroare detectat. Noi am folosit opiunea de compilare {$I-} i testarea valorii lui IOResult pentru a verifica dac fiierul ce urmeaz a fi deschis pentru a prelua informaii din el exist sau nu Dac acest fiier nu exist, atunci Memo1 va fi ncrcat cu dou linii coninnd un text explicativ corespunztor. De asemenea, prin apelul Image1.Picture.Graphic:=nil realizm o golire a imaginii din Image1. Putem scrie i Image1.Picture:=nil pentru a realiza acelai lucru. De asemenea ListBox1.Clear realizeaz golirea listei ce ar conine numele fiierelor cu imagini '.bmp'.
AssignFile(f,numejudet+'.txt'); {$I-}Reset(f);{$I+} if IOResult=0 then begin while not eof(f) do begin ReadLn(f,s);
36
if s[1]='@' then ListBox1.Items.Add(Copy(s,2,Length(s)-1)) else Memo1.Lines.Add(s) end; CloseFile(f); end else begin Image1.Picture.Graphic:=nil; ListBox1.Clear; Memo1.Clear; Memo1.Lines.Add('Nu detinem informatii'); Memo1.Lines.Add('despre acest judet!') end end;
n sfrit, dac se acioneaz n cadrul listei, se caut itemul selectat i se ncarc imaginea corespunztoare n Image1.
procedure TForm2.ListBox1Click(Sender: TObject); var i: Integer; begin for i:=0 to ListBox1.Items.Count-1 do if ListBox1.Selected[i] then Image1.Picture.LoadFromFile(ListBox1.Items[i]) end; end.
Observaie. Funcia urmtoare se bazeaz pe explicaiile anterior prezentate, referitoare la opiunea de compilare {$I-} i la variabila de sistem IOResult i testeaz existena unui fiier oarecare.
function FileExist(nf: String): Boolean; var F: File; begin Assign(F,nf); {$I-}Reset(F);{$I+} if IOResult=0 then begin Close(F); FileExist:=True end else FileExist:=False end;
37
Aplicaia 3
Este un alt joc cu un labirint, ca i Ciupercile. De aceast dat dispunem de un omule (aici reprezentat printr-un cercule alb ntr-un ptrat rou (linia 4, coloana 2 n figur). El se plimb ntr-un labirint cu 10 linii i 20 coloane, bordat cu ziduri i avnd ziduri i n interior (vezi de pild csuele de pe margine, care vor fi desenate cu verde). Omuleul nostru tie doar s se deplaseze ortogonal prin spaiile dintre ziduri (alb-galbene sau cele marcate cu cerculee). El are la dispoziie un numr de pachete cu marf (ptrelele cu marginea evideniat, ca cel din poziia 3, 3), pe care trebuie s le duc n locurile marcate cu cerculee (ca cele patru din linia 2 de sus). Deplasarea mrfurilor se face doar prin mpingere i, pentru ca 38
problema s devin i mai dificil, nu se pot mpinge mai multe pachete de marf mpreun! Aadar, de pild, n cazul din figura anterioar, omuleul nostru nu poate dect fie s coboare un rnd, fie s se deplaseze n dreapta cu o coloan, ceea ce duce i la mutarea cu o poziie n dreapta a pachetului alturat, care din poziia cu linia=4 i coloana=3 se va duce n poziia cu aceeai linie dar coloana=4. Apoi, printr-o micare n sus, omuleul va poziiona corect marfa din celula de coordonate 3, 3 care va ajunge peste cerculeul din csua de pe linia sau rndul 2 i de pe coloana 3. Firete, va rmne cerculeul de coordonate 2, 2 neacoperit, ceea ce nu este bine, de aceea va trebui ca pachetul de marf despre care tocmai vorbea s ajung n cerculeul din celula 2, 2. Jocul se bazeaz, aadar, pe nite labirinturi bine elaborate, n sensul c ele respect anumite condiii: un labirint are 10 rnduri (linii) i 20 coloane; fiecare labirint este bordat de ziduri; numrul de poziii destinaie (marcate cu cerculee) este identic cu numrul de pachete de marf; exist cel puin nu pachet de marf; exist exact un omule; problema are soluie (adic toate mrfurile pot fi deplasate pn la cerculeele destinaie).
Firete, este de dorit ca problema s fie dificil, soluia s fie greu de gsit, eventual s cuprind elemente de unicitate. Ca i n jocul Ciupercile, i aici labirintul va fi preluat dintr-un fiier text, n care sunt folosite diferite caractere pentru a codifica elementele componente ale labirintului. Astfel, simbolul '#' marcheaz zidurile, omuleul este '$', pachetele cu marf sunt simbolizate prin '*', iar locurile de depozitare a mrfurilor sunt marcate cu '%'. Iat 6 modele de labirinturi, toate solvabile, dar avnd grade diferite de dificultate:
39
1.LAB
#################### #%%%# # # ### # ## # ## # # # # # ##* # # # # ## ## # # # # #*### ## ## # # $ ##### ## # ### * # # # # # # # # ####################
2.LAB
#################### # $ # ### # ## # ### * # * # # # # # # # # ## # # # # #### ## ## ## # # # ## # ####### #* # %# # # %%# ####################
3.LAB
#################### #%%%% # ### # ##*## * * # #$* # #### # # #* #### #%## # # # ##### %## * # # * # # ## # ### # * ####### # #%% ## # ####################
4.LAB
#################### ## ## % %#### # # # # #### # # # ## # # # **# #** ## ## ## *$## # ## ## ## # #### # ## ## #%% * ## # # ##% ## ##% ## ####################
5.LAB
#################### ## ##% %%#### ##% ## # # ## # # ## # # # **# #* * ## ## ## *$## # ## ## ## # #### # ## ## #%% * ## # # # # # # #### # ####################
6.LAB
#################### ## %# %### # ## * # ## # *$## # ## ## # # #### # ## * * # # * %### # # # ## # # # * # #* * %# ## #% # # #% ####% %# ####################
40
nainte de a trece la explicarea elementelor ce apar n seciunea de implementare a unit-ului, vom expune ideea de realizare a programului. Mai nti, comanda 'Joac' din meniu nu va fi disponibil, aa c utilizatorul va fi nevoit s aleag un labirint, care va fi citit dintr-un fiier cu extensia '.LAB' n memorie, ntr-o matrice de caractere, pe care am notat-o cu L. Dup alegerea labirintului, comanda de pornire a jocului devine activ. Acionarea ei determin afiarea labirintului direct n proprietatea Canvas din Form1. Ca i la jocul Ciupercile, se va decodifica labirintul pe baza simbolurilor folosite ('#', '*' etc., cu meniunea c i aici spaiul reprezint lipsa oricrui element din labirint, adic locurile pe unde se poate deplasa omuleul). O funcie boolean, apelat de fiecare dat dup o mutare a omuleului, va testa dac nu cumva toate mrfurile au fost aezate la 41
locurile lor, caz n care jocul s-a terminat. Nu exist o restricie de timp la acest joc (propunem cititorului s o realizeze), dar e clar c exist situaii cnd o mutare greit poate duce la o situaie imposibil de rezolvat. Un exemplu ar fi cnd un pachet de marf ajunge ntr-un col format de dou ziduri diagonal opuse. Deplasarea omului se va face pe baza comenzilor primite de la tastatur, folosind evenimentul OnKeyDown. O procedur special va realiza deplasarea omuleului, care ar putea fi nsoit i de deplasarea n acelai sens a unui pachet de marf. i acum iat seciunea implementation a unit-ului:
implementation {$R *.DFM} var lat: Integer; sir: String; var L: array[1..20,1..10] of Char; Xom,Yom: Integer; Loc: array[1..20] of record x,y: Integer end; NrLoc: Integer;
Labirintul este dat de matricea L. Variabila lat conine mrimea laturii fiecrui ptrat din labirint, adic a fiecrei celule (csue). Xom, Yom sunt coordonatele omuleului n cadrul labirintului, iar vectorul Loc (cu NrLoc componente utilizate efectiv) reprezint o modalitate de memorare a locurilor destinaie pentru mrfuri. Procedura urmtoare deseneaz un dreptunghi colorat n culoarea c, avnd colurile stnga sus (x1,y1) i dreapta jos (x2,y2). Desenarea se face direct n Form1.Canvas.
procedure Bar(x1,y1,x2,y2,c: Integer); var k: Integer; begin with Form1.Canvas do begin Pen.Width:=1; Pen.Color:=c; for k:=0 to (x2-x1) div 2 do Rectangle(x1+k,y1+k,x2-k,y2-k) end end;
42
Procedura Tipar(i,j) realizeaz afiarea coninutului celulei de pr rndul j i coloana i din labirint.
procedure Tipar(i,j: Integer); begin case L[i,j] of '#': begin {zid} Bar(lat*i+1,lat*j+1, lat*(i+1)-1,lat*(j+1)-1, clGreen) end; '*': begin {marfa} Bar(lat*i+1,lat*j+1,lat*(i+1)-1, lat*(j+1)-1,clBlue); Bar(lat*i+3,lat*j+3,lat*(i+1)-3, lat*(j+1)-3,clAqua) end; '$': begin {omuletul gestionar} Bar(lat*i+1,lat*j+1,lat*(i+1)-1, lat*(j+1)-1,clRed); Form1.Canvas.Pen.Color:=clWhite; Form1.Canvas.Ellipse(lat*i+3,lat*j+3, lat*(i+1)-3, lat*(j+1)-3) end; ' ': begin {spatiu} Form1.Canvas.Pen.Width:=1; Form1.Canvas.Pen.Color:=clYellow; Form1.Canvas.Rectangle(lat*i+1,lat*j+1, lat*(i+1)-1,lat*(j+1)-1) end end end;
Procedura DisplayLoc este responsabil de afiarea locurilor de destinaie a mrfurilor, de care procedura Tipar nu se ocup. Aceasta deoarece, n urma poziionrii unui pachet de marf ntr-o celul destinaie sau a trecerii omuleului pe acolo, nu mai trebuie afiat cerculeul. Procedura Tipar nu ar proceda corect. Exist dou variante de afiare, date de parametrul p: Byte. Prima (cu p=1) se folosete cnd se deplaseaz omuleul i ine cont de observaia fcut de noi referitoare la omuleul care ar ajunge prin zonele marcate ca destinaii. A doua variant (cu p=0) este folosit atunci cnd au loc desenri sau redesenri ale labirintului.
procedure DisplayLoc(p: Byte); var i: Integer; begin
43
case p of 1: for i:=1 to NrLoc do if (Abs(Loc[i].x-Xom)<2) and (Abs(Loc[i].y-Yom)<2) and (L[Loc[i].x,Loc[i].y] in [' ','$']) then begin Form1.Canvas.Pen.Color:=clBlue; Form1.Canvas.Pen.Width:=2; Form1.Canvas.Ellipse(lat*Loc[i].x+1, lat*Loc[i].y+1, lat*(Loc[i].x+1)-1, lat*(Loc[i].y+1)-1) end; 0: for i:=1 to NrLoc do if L[Loc[i].x,Loc[i].y]=' ' then begin Form1.Canvas.Pen.Color:=clBlue; Form1.Canvas.Pen.Width:=2; Form1.Canvas.Ellipse(lat*Loc[i].x+1, lat*Loc[i].y+1, lat*(Loc[i].x+1)-1, lat*(Loc[i].y+1)-1) end end; for i:=1 to NrLoc do if L[Loc[i].x,Loc[i].y]='$' then Tipar(Xom,Yom) end;
Responsabil de verificarea dac toate celulele destinaie au fost ocupate de mrfuri este funcia logic de mai jos:
function LocuriOcupate: Boolean; var Ocup,i: Integer; begin Ocup:=0; for i:=1 to NrLoc do if L[Loc[i].x,Loc[i].y]='*' then Inc(Ocup); LocuriOcupate:=NrLoc=Ocup end;
Astfel, ea contorizeaz numrul de elemente de la coordonatele memorate n vectorul Loc, care conin mrfuri. Rezultatul este comparat cu NrLoc. Procedura CitesteLabirint ncearc s deschid fiierul text cu numele nume_fis. 44
Dac nu exist un asemenea fiier (lucru testat de IOResult, ca i n aplicaia Harta), atunci jocul se oprete). Dac ns el exist, prin dou cicluri for se citete informaia depozitat n el referitoare la labirint. Dac se ntlnete simbolul '$', atunci se memoreaz Xom i Yom. Dac se ntlnete simbolul '%', atunci se memoreaz n vectorul Loc coordonatele acestui loc destinaie. La sfrit, se apeleaz DisplayLoc pentru a afia i aceste locuri.
procedure CitesteLabirint(nume_fis: String); var F: TextFile; i,j: Integer; begin NrLoc:=0; AssignFile(F,nume_fis); {$I-}Reset(F);{$I+} if IOResult<>0 then begin ShowMessage('Labirint inexistent!'); Application.Terminate end else begin for j:=1 to 10 do begin for i:=1 to 20 do begin Read(F,L[i,j]); if L[i,j]='$' then begin Xom:=i; Yom:=j end; if L[i,j]='%' then begin Inc(NrLoc); Loc[NrLoc].x:=i; Loc[NrLoc].y:=j; L[i,j]:=' ' end; Tipar(i,j) end;
45
Procedura urmtoare realizeaz efectiv desenrile necesare, precum i actualizrile asupra coninutului labirintului (matricea L), n cazul n care se execut o mutare a omuleului din poziia curent Xom, Yom, n noua poziie. Deplasarea omuleului se face pe axa OX cu dx, iar pe axa OY cu dy. Aadar, noile coordonate ale omuleului vor fi Xom+dx, Yom+dy. Vom vedea c valorile lui dx i dy pot fi -1, 0 sau 1.
procedure DoMove(dx,dy: Integer); begin if L[XOm+dx,YOm+dy]=' ' then begin Sch(Xom,Yom,XOm+dx,YOm+dy); Inc(Xom,dx); Inc(Yom,dy) end else if (L[Xom+dx,Yom+dy]='*') and (L[Xom+dx+dx,Yom+dy+dy]=' ') then begin Sch(Xom+dx+dx,Yom+dy+dy,Xom+dx,Yom+dy); Sch(Xom+dx,Yom+dy,Xom,Yom); Inc(Xom,dx); Inc(Yom,dy) end end;
46
begin dx:=Form1.ClientWidth div 22; dy:=Form1.ClientHeight div 12; if dx<dy then lat:=dx else lat:=dy; CitesteLabirint(sir); L[Xom,Yom]:=' '; Tipar(Xom,Yom); L[Xom,Yom]:='$'; Tipar(Xom,Yom); DisplayLoc(0) end; procedure TForm1.Joaca1Click(Sender: TObject); begin IncarcaSiDeseneazaTabla; Form1.SetFocus end; procedure TForm1.FormCreate(Sender: TObject); begin Form1.BorderStyle:=bsSingle; Joaca1.Enabled:=False; sir:='0'; Form1.Caption:='Transporta marfurile' end; procedure TForm1.Terminare1Click(Sender: TObject); begin Application.Terminate end; procedure TForm1.Alegelabirint1Click(Sender: TObject); begin sir:=InputBox('Alegere labirint', 'Dati numarul labirintului','1'); sir:=sir+'.lab'; Joaca1.Enabled:=True end;
Procedura care st la baza mutrilor executate de DoMove este dat mai jos. Se verific s nu se testeze nici una dintre tastele reci Shit sau Ctrl, prin intersectarea mulimii acestora cu mulimea tastelor de tip TShiftState eventual apsate. Exemplul poate fi folosit i n alte cazuri, aici fiind pur didactic.
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if Shift*[ssAlt,ssCtrl]=[] then begin case Key of
47
end; DisplayLoc(1); if (LocuriOcupate) and (sir<>'0') then ShowMessage('Joc terminat cu succes!') end end;
n fine, pentru a evita problemele de acoperire a ferestrei aplicaiei noastre de alte ferestre, este bine s realizm procedura FormPaint care restaureaz coninutul labirintului prin reafiarea acestuia i a locaiilor destinaie.
procedure TForm1.FormPaint(Sender: TObject); var i,j: Integer; begin for i:=1 to 20 do for j:=1 to 10 do Tipar(i,j); DisplayLoc(0) end; end.
Dac ai reuit s elaborai aceast aplicaie, atunci v propunem s creai cu Notepad cele ase fiiere reprezentnd ase labirinturi de diferite nivele de dificultate i, dac inventai dumneavoastr labirinturi mai dificile, ateptm propunerile dumneavoastr prin e-mail.
48
Aplicaia 4
49
Mutrile legale n cazul de mai sus sunt: piesa 11 jos, piesa 18 sus, piesa 8 la dreapta, piesa 23 la stnga. Cnd se mut o pies adiacent spaiului liber, practic se realizeaz o interschimbare ntre cele dou ptrele.
n continuare s trecem la scrierea codului asociat acestei forme. Interfaa unit-ului puzzle1.pas arat clasic astfel:
unit puzzle1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Edit1: TEdit; Button1: TButton; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); private procedure ClickButon(Sender: TObject);
50
Ct privete implementarea unit-ului, aici vom declara un vector de butoane, adic variabila Buton de tip array, cu componente de tip TButton. Elementele acestui vector vor fi generate i iniializate n mod dinamic, imediat dup acionarea butonului Button1. n continuare, variabilele ii i jj sunt linia i coloana csuei libere, x este chiar indicele csuei libere. De asemenea n este numrul de linii i coloane, adic sunt n2 csue cu butoane. Numerotarea csuelor se face de la 0 la n2-1.
implementation {$R *.DFM} const MaxButoane = 121; var Buton: array[0..MaxButoane-1] of TButton; ii,jj,n,x: Integer;
Cnd se acioneaz butonul Buton1, dac el este buton de oprire (Caption='Stop'), atunci se oprete aplicaia. Dac ns avem Caption='Start', atunci nseamn c jocul pornete: se creeaz i iniializeaz csuele matricei (adic elementele din vectorul Buton). n principal, trebuie reinute urmtoarele linii de program: Buton[k]:=TButton.Create(Form1); Buton[k].Parent:=Form1; Pentru a determina indicele csuei de pe linia i i coloana j, vom folosi formula: k:=n*i+j;. OnClick determin ce se ntmpl cnd se va apsa pe un buton din tablou. De aceea, va trebui s scriem ceva de genul Buton[k].OnClick:=ClickButon urmnd ca mai trziu s descriem procedura ClickButon.
procedure TForm1.Button1Click(Sender: TObject); var i,j,k,c,lat: Integer; s: String;
51
begin if Button1.Caption='Stop' then Application.Terminate else begin Val(Edit1.Text,n,c); Button1.Caption:='Stop'; lat:=Width div (n+2); for i:=0 to n-1 do for j:=0 to n-1 do begin k:=n*i+j; Buton[k]:=TButton.Create(Form1); Buton[k].Parent:=Form1; Str(k+1,s); Buton[k].Left:=lat+lat*j; Buton[k].Top:=100+lat*i; Buton[k].Width:=lat; Buton[k].Height:=lat; Buton[k].Caption:=s; Buton[k].Show; Buton[k].OnClick:=ClickButon end; x:=n*n-1; {casuta libera} Buton[x].Caption:=''; {are Caption vid} ii:=n; jj:=n; {indicele casutei libere} { Amestecare imagini } Buton[n*n-2].Click; Buton[n*n-3].Click; Randomize; for i:=1 to 2000*n do begin k:=Random(n*n); Buton[k].Click end end end;
Aadar, s descriem procedura ClickButon, care verific mai nti cine este emitorul, adic ce valoare are argumentul 52
Sender, ceea ce nseamn c se determin indicele butonului acionat. Apoi se determin linia i coloana butonului pe care s-a apsat cu mouse-ul, memorate n i1 i j1. Dac acest buton este vecin cu butonul corespunztor csuei libere (dat de ii i jj), atunci se interschimb proprietile Caption ale celor dou butoane ntre ele. De asemenea, se actualizeaz valorile lui x, ii i jj. Apoi, folosind variabila boolean ok, se testeaz dac toate butoanele sunt la locul lor, iar dac da, ok ia valoare True i jocul se oprete.
procedure TForm1.ClickButon(Sender: TObject); var k,i,i1,j1: Integer; ok: Boolean; s: String; begin for i:=0 to n*n-1 do if Buton[i]=Sender then k:=i; i1:=k div n + 1; j1:=k mod n + 1; if ((ii=i1) and (abs(jj-j1)=1)) or ((jj=j1) and (abs(ii-i1)=1)) then begin Buton[x].Caption:=Buton[k].Caption; Buton[k].Caption:=''; x:=k; ii:=i1; jj:=j1; ok:=True; for i:=0 to n*n-1 do begin Str(i+1,s); if (Buton[i].Caption<>'') and (Buton[i].Caption<>s) then ok:=False end; if ok then begin ShowMessage('Bravo! Ai rezolvat puzzle-ul!'); Application.Terminate end end end; end.
n lucrarea Aplicaii n Visual Basic am prezentat i o variant a acestui program care folosete imagini ce trebuie refcute.
53
Aplicaia 5
Expresia funciei ce va fi reprezentat este scris n textul programului, iar n cazul figurii anterioare ea este:
function f(x,y: real): real; begin f:=sin(sqrt(abs(x*x-y*y))) end;
54
Reprezentarea grafic a acestei funcii va fi o suprafa care poate fi privit dintr-un punct de observare care depinde de cele trei unghiuri (alfa, beta, gama) corespunztoare celor trei axe de coordonate. Cele ase butoane care pot fi vzute n figura anterioar vor fi folosite pentru modificarea prin scdere sau adunare a acestor trei unghiuri. Dup cum se poate observa i n figur, reprezentarea grafic va fi fcut printr-o reea discret de curbe care se intersecteaz n anumite puncte. Pentru ca s fie reprezentat alt funcie, ar trebui ca textul surs al programului s fie rescris, adic s se rescrie funcia f, care a fost prezentat anterior. Aceasta nseamn c aplicaia nu poate funciona ca fiier executabil, fiind dependent att de textul surs al programului, ct i de mediul Delphi. Ideal ar fi ca s adugm programului un evaluator de expresii cu dou variabile, care, n plus, s verifice i corectitudinea sintactic a unei expresii algebrice. O variant de evaluator matematic i analizor sintactic pentru o expresie algebric cu o singur variant a fost prezentat n lucrarea mea "nvai limbajul Pascal n 12 lecii". Propunem cititorului ca, plecnd de la acele proceduri, valabile pentru curbe, s realizeze proceduri similare pentru suprafee, astfel nct aplicaia Supraf s fie ct mai util i independent de mediul Delphi, dup compilarea i creare fiierului executabil.
55
Button3: TButton; Button4: TButton; Button5: TButton; Button6: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure Button5Click(Sender: TObject); procedure Button6Click(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1;
Aadar, forma aplicaiei are o imagine Image1 i ase butoane n partea de sus, cu proprietile Caption dup cum se vd n figura urmtoare:
56
Urmeaz descrierea procedurilor care acioneaz asupra acestei forme, n cadrul seciunii de implementare a unit-ului. Procedura de baz ReprezentareSuprafata se bazeaz pe un rezultat teoretic i un program prezentate n lucrarea Limbajul Turbo Pascal (vezi bibliografia). Constantele i variabile folosite n program sunt urmtoarele:
implementation {$R *.DFM} const nx=40; ny=40; pas=5; a=-6; b=6; c=-6; d=6; var dy: Integer; x,y,z,ux,uy: Real; xmin,xmax,ymin,ymax: Real; xx,yy: array[1..nx,1..ny] of Real; {reteaua} alfa,beta,gama: Integer; {unghiurile exprimate in grade} al,be,ga: Real; {unghiurile exprimate in radiani} c1,c2,c3,s1,s2,s3: Real; {cosinusurile si sinusurile unghiurilor}
Astfel, nx este numrul de puncte din reeaua de reprezentare folosite pe axa OX, ny este numrul de puncte din aceeai reea, folosite pentru axa Oy, iar pas este pasul cu care se modific fiecare din cele trei unghiuri folosite n reprezentarea suprafeei. Variabilele x, y, z sunt coordonatele curente ale unui punct de pe suprafa, iar ux i uy sunt unitile de reprezentare pe cele dou axe Ox i Oy. Funcia va fi reprezentat pentru x lund valori n intervalul [a,b], iar y lund valori n intervalul [c,d]. Reeaua punctelor de reprezentare este memorat prin vectorii xx i yy. Expresia suprafeei este scris n funcia urmtoare:
function f(x,y: real): real; begin f:=sin(sqrt(abs(x*x-y*y))) end;
O funcie special permite folosirea unghiurilor exprimate n radiani (aa cum este nevoie n funciile trigonometrice).
function radiani(u: integer): real; begin radiani:=u/180*pi
57
end;
58
Form1.Image1.Picture:=nil; {se deseneaza curbele pe axa Ox} for i:=1 to nx do begin Form1.Image1.Canvas.MoveTo(Round((xx[i,1]-xmin)/ux), dy-Round((yy[i,1]-ymin)/uy)); for j:=2 to ny do Form1.Image1.Canvas.LineTo(Round((xx[i,j]-xmin)/ux), dy-Round((yy[i,j]-ymin)/(uy))) end; {se deseneaza curbele pe axa Oy} for j:=1 to ny do begin Form1.Image1.Canvas.MoveTo(Round((xx[1,j]-xmin)/ux), dy-Round((yy[1,j]-ymin)/uy)); for i:=1 to nx do Form1.Image1.Canvas.LineTo(Round((xx[i,j]-xmin)/ux), dy-Round((yy[i,j]-ymin)/uy)) end; end;
n cadrul procedurii anterioare, am pus n chenar formulele care stau la baza reprezentrii proieciei punctului P(x,y,z) din spaiul tridimensional n planul ecranului, n punctul Q(xx[i],yy[i]). Urmtoarele ase proceduri corespund celor ase butoane i constau n modificarea cu valoarea pas a valorilor unghiurilor alfa, beta i gama i reprezentarea din nou a suprafeei.
procedure TForm1.Button1Click(Sender: TObject); begin alfa:=alfa-pas; ReprezentareSuprafata end; procedure TForm1.Button2Click(Sender: TObject); begin alfa:=alfa+pas; ReprezentareSuprafata end; procedure TForm1.Button3Click(Sender: TObject); begin beta:=beta-pas; ReprezentareSuprafata end;
59
procedure TForm1.Button4Click(Sender: TObject); begin beta:=beta+pas; ReprezentareSuprafata end; procedure TForm1.Button5Click(Sender: TObject); begin gama:=gama-pas; ReprezentareSuprafata end; procedure TForm1.Button6Click(Sender: TObject); begin gama:=gama+pas; ReprezentareSuprafata end;
Crearea formei presupune o iniializare a celor trei unghiuri, precum i o prim reprezentare a suprafeei. Programul este gata.
procedure TForm1.FormCreate(Sender: TObject); begin alfa:=165; beta:=-135; gama:=135; Caption:='Reprezentare suprafata'; ReprezentareSuprafata end; end.
60
Aplicaia 6
De pild, n figura de mai sus, s-a introdus fraza "orice brbat iubete o femeie frumoas i deteapt", care a fost acceptat, drept corect. n tabel este reprezentat diagrama de analiz sintactic a frazei, care n rndul etichetat cu "0" i coloana etichetat cu "deteapt".
61
Lexicul folosit n analiz, scris n fiierul text 'LEX.TXT', a fost urmtorul: Det->orice Det->fiecare Det->o Det->un Pron->el N->barbat N->femeie V->iubeste V->uraste A->frumoasa A->desteapta C->si C->sau Astfel, fraza considerat anterior conine doar cuvinte din vocabularul ales. Pe de alt parte, fraza "orice cine urte o pisic", dei corect sintactic n limba romn, nu este acceptat, deoarece conine cuvinte ce nu sunt trecute n vocabularul nostru. Fiierul conine, pe fiecare rnd, o regul de forma:
categorie gramatical (sau parte de vorbire) -> cuvnt din limba romn
Regulile de sintax (din fiierul 'GRAM.TXT') sunt o submulime a regulilor de sintax ale limbii romne: S->NP VP NP->Pron NP->N NP->Det N NP->NP AP AP->A AP->AP CP CP->C A VP->V VP VP->V NP Astfel, folosind notaiile consacrate (S (sentence) = propoziie, NP (noun phrase) = grup verbal, VP (verb phrase) = grup verbal, N (noun) = substantiv, Det (determiner) = determinator (de pild articol nehotrt), AP = grup adjectival, A (adjective) = adjectiv, C = 62
conjuncie, V = verb, CP = grup format dintr-o conjuncie i un adjectiv. Regulile de sintax sunt date, cte una pe linie, astfel: categorie gramatical 1 -> categ. gram. 2 categ. gram. 3, cu sensul c prima categorie gramatical se formeaz din concatenarea celorlalte dou, din dreapta sgeii. Conform regulilor de sintax folosite n aplicaia noastr, fraza considerat la nceput este corect, pe cnd n figura urmtoare este prezentat un caz considerat incorect.
De remarcat c algoritmul implementat de noi folosete doar gramatici scrise n forma normal Chomsky (CNF). O gramatic CNF este o gramatic liber de context, n care produciile sunt de forma: A -> B C, n care A este un neterminal, iar B i C sunt neterminali sau preterminali. De asemenea, am considerat ca acceptabile i reguli de forma A->B. Aadar, n partea dreapt a regulilor de producie vor fi dou sau doar un singur element. Detalii referitoare la subiectul tratat, pentru cititorul neavizat, pot fi studiate n lucrri cu un pronunat caracter tiinific, ca cele menionate la bibliografie.
63
n continuare, vom prezenta algoritmul de baz de analiz (numit i parsare "chart-parsing") elaborat de Cocke, Kasami i Younger i numit algoritmul CKY de baz. Algoritmul folosete o matrice (o diagram) chart ca cele din figurile anterioare. Mai nti avem nevoie de nite definiii: Se definete operaia: Star(X,Y)=C|(A este n X) i (B este n Y) i C->AB este o regul din gramatic. Aceasta reprezint faptul c produsul a dou celule din matrice este creat prin combinarea tuturor perechilor de itemi din cele dou celule ce satisfac nite reguli din gramatic. O alt operaie ce se definete este: Closure(S) = A|(A este n S) sau ((B este n Closure(S)) i (A->B este o regul din gramatic. Aceasta reprezint faptul c nchiderea unei celule S este format din coninutul lui S plus rezultatul adugrii oricrei categorii ce deriv dintr-un membru existent al nchiderii lui S. De pild, dac N este n S atunci N aparine lui Closure(S); apoi, dac exist o regul NP->N, i NP va fi adugat n Closure(S); lucrurile continu n acest mod ct timp se mai pot aduga noi membri n Closure(S). n fine, exist i o funcie Lookup, de forma: Lookup(k) = A|A->cuvntul k, adic ne d lista de categorii gramaticale pe care le cuvntul al k-lea din fraza noastr (uneori un cuvnt poate avea mai multe categorii gramaticale, de pild "duce" poate fi att verb, ct i substantiv). Cu definiiile de mai nainte, algoritmul de baz CKY este:
for k:=1 to n do begin chart[k-1,k]:=Closure(Lookup(k)); for i:=k-2 downto 0 do begin chart[i,k]:=; for j:=k-1 downto i+1 do chart[i,k]:=chart[i,k] Star(chart[i,j], chart[j,k]; chart[i,k]:=Closure(chart[i,k]); end end;
64
Algoritmul prezentat mai sus va fi implementat n programul care urmeaz, n care se vor realiza proceduri pentru cele trei funcii definite, din cauza unor restricii ale limbajului Pascal.
unit parser1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Grids, ExtCtrls; type TForm1 = class(TForm) StringGrid1: TStringGrid; Edit1: TEdit; Label1: TLabel; procedure Edit1KeyPress(Sender: TObject; var Key: Char);
65
procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} const max=20; maxreg=20; maxcuv=5; maxcuvinte=20;
Pentru a memora elementele care apar n stnga i n dreapta unei reguli din gramatic, folosim tipul Cuvint.
type Cuvint=String[30]; MultimeDeCuvinte=object nc: Integer; cuv: array[1..maxcuv] of Cuvint; procedure Adauga(c: Cuvint); end;
O regul are dou pri, una n stnga i una n dreapta, iar o gramatic este constituit din mai multe astfel de reguli.
regula=record st,dr: Cuvint end; type Gramatica=object nr: Integer; reg: array[1..maxreg] of regula; procedure Citeste(nf: String); end;
Diagrama este o matrice n care fiecare celul este o mulime de cuvinte. G este gramatica cu regulile, iar D este, de fapt, dicionarul de cuvinte folosit.
var chart:array[0..max, 0..max] of MultimeDeCuvinte; G: Gramatica; D: Gramatica; Propoz: array[1..maxcuvinte] of Cuvint;
66
Desenarea diagramei se face conform coninutului matricei chart, folosind controlul StringGrid1:
procedure DeseneazaChart(i,j: Integer); var t: String; k,kk: Integer; begin if chart[i,j].nc>=1 then begin t:='{'; for k:=1 to chart[i,j].nc-1 do t:=t+chart[i,j].cuv[k]+','; t:=t+chart[i,j].cuv[chart[i,j].nc]+'}'; Form1.StringGrid1.Cells[j,i+1]:=t; Form1.StringGrid1.Refresh end end;
Funcia urmtoare verific dac un cuvnt dat x se afl sau nu ntr-o mulime de cuvinte M.
function EsteIn(x: Cuvint; M: MultimeDeCuvinte): Boolean; var este: Boolean; i: Integer; begin este:=False; for i:=1 to M.nc do if x=M.cuv[i] then este:=True; EsteIn:=este end;
Pentru obiectele de tip MultimeDeCuvinte am prevzut o procedur de adugare a unui nou cuvnt:
procedure MultimeDeCuvinte.Adauga(c: Cuvint); begin nc:=nc+1; cuv[nc]:=c end;
Pe baza ultimelor dou subprograme descrise, putem defini operaiile Star, Closure i Lookup, precum i operaia de citire a unei gramatici, a crei reguli sunt scrise ntr-un fiier text dat.
procedure Star(X,Y: MultimeDeCuvinte; var Z: MultimeDeCuvinte); var i,j,k: Integer; begin Z.nc:=0;
67
for i:=1 to X.nc do for j:=1 to Y.nc do for k:=1 to G.nr do if G.reg[k].dr=X.cuv[i]+' '+Y.cuv[j] then Z.Adauga(G.reg[k].st) end; procedure Closure(S: MultimeDeCuvinte; var C: MultimeDeCuvinte); var i: Integer; gata: Boolean; begin C:=S; repeat gata:=True; for i:=1 to G.nr do if EsteIn(G.reg[i].dr,C) then if not EsteIn(G.reg[i].st,C) then begin C.Adauga(G.reg[i].st); gata:=False end until gata end; procedure Gramatica.Citeste(nf: String); var f: TextFile; s: String; p: Byte; begin nr:=0; AssignFile(f,nf); Reset(f); while not eof(f) do begin nr:=nr+1; ReadLn(f,s); p:=Pos('->',s); reg[nr].st:=Copy(s,1,p-1); reg[nr].dr:=Copy(s,p+2,Length(s)-(p+1)) end; CloseFile(f) end; procedure Lookup(k: Integer; var L: MultimeDeCuvinte); var i: Integer; begin L.nc:=0; for i:=1 to D.nr do if D.reg[i].dr=Propoz[k] then begin L.nc:=L.nc+1; L.cuv[L.nc]:=D.reg[i].st
68
end end;
n fine, procedura urmtoare realizeaz analiza gramatical a frazei date (ss), pe baza algoritmului descris teoretic n primul paragraf.
procedure Parseaza(ss: String); var i,j,k,kk: Integer; n: Integer; p: Byte; L,C,S: MultimeDeCuvinte; t: String; begin ss:=ss+' '; n:=0; while ss<>'' do begin p:=Pos(' ',ss); n:=n+1; Propoz[n]:=Copy(ss,1,p-1); Form1.StringGrid1.Cells[n,0]:=Propoz[n]; Str(n-1,t); Form1.StringGrid1.Cells[0,n]:=t; Delete(ss,1,p) end; Form1.StringGrid1.RowCount:=1+n; Form1.StringGrid1.ColCount:=1+n; Form1.StringGrid1.Show; for k:=1 to n do begin Lookup(k,L); Closure(L,C); chart[k-1,k]:=C; for i:=k-2 downto 0 do begin chart[i,k].nc:=0; for j:=k-1 downto i+1 do begin Star(chart[i,j],chart[j,k],S); for kk:=1 to S.nc do if not EsteIn(S.cuv[kk], chart[i,k]) then chart[i,k].Adauga(S.cuv[kk]) end; Closure(chart[i,k],C); chart[i,k]:=C end end; for i:=0 to n do
69
for j:=0 to n do DeseneazaChart(i,j); if EsteIn('S',chart[0,n]) then Form1.Label1.Caption:= 'Fraza acceptata.' else Form1.Label1.Caption:= 'Fraza rejectata.' end; procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); begin if Key=Chr(13) then Parseaza(Edit1.Text) end; procedure TForm1.FormCreate(Sender: TObject); begin Caption:='Basic CKY Parser'; StringGrid1.Hide; G.Citeste('gram.txt'); D.Citeste('lex.txt'); end; end.
Iat coninuturile celor dou fiiere (gramatica i dicionarul) folosite de noi n exemplele considerate: GRAM.TXT
S->NP VP NP->Pron NP->N NP->Det N NP->NP AP AP->A AP->AP CP CP->C A VP->V VP VP->V NP
LEX.TXT
Det->orice Det->fiecare Det->o Det->un Pron->el N->barbat N->femeie V->iubeste V->uraste A->frumoasa A->desteapta C->si C->sau
Atenie! Algoritmul funcioneaz doar dac n partea din dreapta a fiecrei reguli din gramatic sunt cel mult dou simboluri, deci va trebui s avei grij s rescriei gramaticile pe care le folosii astfel nct s ndeplineasc aceast restricie. 70
Aplicaia 7
Dup cum se poate observa n figur, o pagin web creat cu programul nostru, pe care l-am denumit AutoWeb, este compus din trei cadre (frames) i are urmtoarea structur: un cadru n partea de sus, neredimensionabil, n care este scris numele persoanei i deviza (motto-ul); un cadru n partea din stnga, redimensionabil, ce cuprinde hiperlegturi ctre diferite subiecte ce vor fi tratate n fiiere html separate i vor fi afiate n dreapta; 71
un cadru n partea din dreapta, n care vor fi afiate fiierele html la care se face referin din cadrul cuprinsului din cadrul stng. Structura fiierului principal index.html este urmtoarea:
<html> <head> <title>Pagina mea web</title> <frameset rows="25%,*"> <frame name="sus" src="sus.html" noresize> <frameset cols="30%,*"> <frame name="stg" resizable src="stanga.html" scrolling="yes"> <frame name="drp" resizable src="sub1.html" scrolling="yes"> <noframes> </head> <body> Aceasta pagina are cadre, dar exploratorul dumneavoastra nu le accepta. </body> </noframes> </frameset> </html>
Aadar, am definit dou trei cadre: sus avnd coninutul din fiierul sus.html; stg cu sursa luat din fiierul stanga.html; drp, care se ncarc, pentru nceput, cu fiierul sub1.html, referitor la subiectul 1. Coninutul fiierului sus.html este simplu:
<html> <head><title>sus</title></head> <body background="fundal.jpg"> <h1><i><font face="Arial" color="black"> <marquee behaviour="alternate" scrolldelay="30" scrollamount="2" bgcolor="#00FFFF" height="25" align=center> Ioana Ionescu </marquee> </i></h1> <h4> <marquee scrolldelay="35" bgcolor="#eeee88", height="20" align=top> Si miine e o zi! </marquee> </h4> </body></html>
72
Cu excepia rndurilor subliniate, care reprezint numele persoanei i motto-ul su, toate celelalte linii ale fiierului vor fi generate prin program i rmn neschimbate. Totui, trebuie ca s existe un fiier sus.html creat aprioric. Acesta va fi ca cel descris anterior, n care n locurile subliniate apar dou texte sugestive: '(dati numele dumneavoastra aici)' i '(dati motto-ul dumneavoastra aici)'. Numele persoanei i motto-ul su vor fi introduse sau modificate n timpul execuiei aplicaiei AutoWeb, care va genera acest fiier. De asemenea, se presupune existena n directorul curent a unui fiier pentru realizarea fundalului, cu numele fond.jpg. Atenie! Pagina web creat va putea fi afiat doar n Internet Explorer (din cauza modului de definire a cadrelor, ca i din cauza faptului c se definesc cele dou csue de text defilant, cu ajutorul marcatorului marquee. Dac dorii s realizai o variant care s funcioneze i n Netscape Communicator, propunem s renunai de tot la cadrul sus, punnd coninutul acestuia n cadrul din stnga. Cadrul stg va conine ntotdeauna fiierul stg.html, care ar avea o structur de cuprins cu referiri la diferite fiiere html ce vor fi afiate n cadrul drp. De aceea va trebui s folosii atributul target n cadrul definirii hiperlegturilor. Astfel, atunci cnd se alege un subiect din cuprinsul din stnga, datorit faptului c inta este cadrul drp, trimiterea se va face ctre un fiier ce va fi afiat n partea din dreapta. Dac atributul target ar lipsi, noul fiier s-ar afia tot n cadrul din stnga.
<html> <head><title>Meniul</title></head> <body background="fundal.jpg"> <h3><b>Cuprins:</b></h3> <h4><ul> <li><a href="sub1.html" target="drp"> Despre mine </a> <li><a href="sub2.html" target="drp"> Pasiunile mele </a> <li><a href="sub3.html" target="drp"> Familia mea </a> </ul> </body></html>
73
Cu excepia rndurilor subliniate, care vor fi introduse de utilizatorul aplicaiei AutoWeb, toate celelalte rnduri ale fiierului stanga.html de mai sus vor fi generate prin program. Corespunztor celor trei subiecte la care se face referin n cadrul lui stanga.html, vor exista trei fiiere sub1.html, sub2.html i sub3.html. Acestea vor conine fraze simple, neformatate, dar e posibil s existe i o imagine n interior. Dac ea exist, numele ei este precizat de ctre utilizatorul programului i imaginea va fi afiat naintea textului. De pild, n exemplul nostru, am folosit fiierul sub1.html cu urmtorul coninut, n care rndurile subliniate au fost introduse de la tastatur, iar restul au fost generate prin program:
<html><head><title> Despre mine </title></head> <body background="fundal.jpg"> <img src= "fata.jpg"> <p>Ma numesc Ioana Ionescu.</p> <p>Sint eleva in clasa a X-a, la Colegiul National "Ferdinand I" din Bacau.</p> <p>Sint nascuta in zodia Gemeni si am 17 ani.</p> <p></p> </body> </html>
Celelalte pagini (sub2.html i sub3.html) vor fi create asemntor. Dup cum am spus, toate aceste fiiere sunt scrise de programul AutoWeb, aplicaie pe care o vom prezenta mai jos. Excepie face fiierul index.html care va fi realizat de dumneavoastr manual i nu l vei modifica. Fiierele sub1.html, sub2.html i sub3.html vor fi i ele create anterior, dar vor fi modificate de ctre program. La nceput, ele vor arta ca mai jos (pstrai propoziiile scrise ntre parenteze rotunde, pentru c ele trebuie s se regseasc n acea procedur din textul programului care se ocup de salvarea ntregii pagini web).
74
<html><head><title> (scrieti aici denumirea subiectului) </title></head> <body background="fundal.jpg"> <p>(tratati aici pe larg acest subiect)</p> </body> </html>
Pentru a le edita, putei folosi un editor de texte precum Notepad. n timpul execuiei, aplicaia AutoWeb arat ca n figura urmtoare. Aadar, exist o csu de text n care se introduce numele persoanei, una n care aceasta i scrie motto-ul, precum i trei tabulatori (controale de tip TTabControl), corespunztori celor trei subiecte. n cadrul fiecruia se va nscrie numele subiectului corespunztor, coninutul fiierului respectiv, adic textul, precum i numele fiierului n care se afl imaginea ce va aprea n acel fiier.
75
De pild, n exemplul considerat n figuri, subiectul 1 va figura n meniul din cadrul stng cu numele "Despre mine", va avea coninutul (textul) "M numesc Ioana Ionescu....", iar fiierul cu imagine va fi "fata.jpg". Firete, se presupune c exist pe disc aceste fiiere cu imagini, scanate. n figura urmtoare se observ cum arat forma aplicaiei n timpul proiectrii acesteia. Aadar, pe lng controalele descrise, exist un buton pentru salvarea fiierelor HTML create, adic pentru nscrierea pe disc a paginii web realizate. S remarcm i csua de introducere a numelui fiierului cu imagini, n fiecare din cei trei tabulatori. Exist i un control de tip TOpenDialog, care este apelat de ctre un buton, pentru a putea alege un fiier cu o imagine, n mod interactiv, prin intermediul unei ferestre de dialog de tip "deschidere de fiier".
76
77
Procedura urmtoare realizeaz trecerea de la un tabulator la altul. Astfel, schimbndu-se subiectul curent, salvm datele din subiectul curent i apoi le ncrcm pe cele ale subiectului urmtor. Urmrii cu atenie comentariile din cadrul textului procedurii!
procedure TForm1.TabControl1Change(Sender: TObject); var s: String; i: Integer; f: TextFile; begin { salvam ce este scris in subiectul curent } Str(nr_sub_tratat,s); subiect[nr_sub_tratat]:=Edit4.Text; AssignFile(f,'sub'+s+'.html'); Rewrite(f); WriteLn(f,'<html><head><title>'); WriteLn(f,Edit4.Text); WriteLn(f,'</title></head>'); WriteLn(f,'<body background="fundal.jpg">'); if (Edit3.Text<>'(dati numele fisierului)') and (Edit3.Text<>'') then begin WriteLn(f,'<img src='); WriteLn(f,'"'+Edit3.Text+'">') end; if Memo1.Lines.Count=0 then WriteLn(f,'<p>(tratati aici subiectul'+ ' pe larg)</p>') else for i:=0 to Memo1.Lines.Count-1 do WriteLn(f,'<p>'+Memo1.Lines[i]+'</p>'); WriteLn(f,'</body>'); WriteLn(f,'</html>'); CloseFile(f); { incarcam ce era scris in subiectul nou tratat } Memo1.Clear; Edit3.Clear; Edit4.Clear; nr_sub_tratat:=TabControl1.TabIndex+1; Str(nr_sub_tratat,s); AssignFile(f,'sub'+s+'.html'); Reset(f); ReadLn(f,s); {<html><head><title>}
78
ReadLn(f,s); Edit4.Text:=s; subiect[nr_sub_tratat]:=Edit4.Text; ReadLn(f,s); {</title></head>} ReadLn(f,s);{f,'<body background="fundal.jpg">} ReadLn(f,s); if Copy(s,1,4)='<img' then begin ReadLn(f,s); { ghilimele, numele imaginii, ghilimele } Edit3.Text:=Copy(s,2,Length(s)-3); end else Memo1.Lines.Add(Copy(s,4,Length(s)-7)); repeat ReadLn(f,s); if s<>'</body>' then Memo1.Lines.Add(Copy(s,4,Length(s)-7)) { fara <p> si </p>} until s='</body>'; Memo1.Refresh; if Edit3.Text='' then Edit3.Text:='(dati numele fisierului)'; { urmeaza, in fisier: </html>} CloseFile(f); end;
Legat de procedura anterioar, trebuie neaprat ca fiierele sub1.html, sub2.html i sub3.html s existe de la bun nceput, adic nainte de lansarea prima dat a aplicaiei, chiar dac ulterior ele vor fi rescrise. De pild, dac vom alege subiectul 3, pentru a-l modifica, vom observa c textul din fiierul sub3.html va fi preluat n cadrul aplicaiei, astfel:
79
O alt procedur important din program este cea care creeaz forma. La creare, trebuie ca s citim datele corespunztoare primului subiect, care este cel implicit n cadrul tabulatorilor. De asemenea, se citesc datele din fiierul sus.html, care trebuie s fie creat i el anterior primei execuii a aplicaiei AutoWeb.
procedure TForm1.FormCreate(Sender: TObject); var f: TextFile; s: String; i: Integer; begin nr_sub_tratat:=1; { incarcam ce era scris in subiectul nou tratat } Memo1.Clear; Edit3.Clear; Edit4.Clear; nr_sub_tratat:=TabControl1.TabIndex+1; Str(nr_sub_tratat,s); AssignFile(f,'sub'+s+'.html'); Reset(f); ReadLn(f,s); {<html><head><title>} ReadLn(f,s); Edit4.Text:=s; subiect[nr_sub_tratat]:=Edit4.Text;
80
ReadLn(f,s); {</title></head>} ReadLn(f,s);{f,'<body background="fundal.jpg">} ReadLn(f,s); if Copy(s,1,4)='<img' then begin ReadLn(f,s); { ghilimele, numele imaginii, ghilimele } Edit3.Text:=Copy(s,2,Length(s)-3); end else Memo1.Lines.Add(Copy(s,4,Length(s)-7)); repeat ReadLn(f,s); if s<>'</body>' then Memo1.Lines.Add(Copy(s,4,Length(s)-7)) { fara <p> si </p>} until s='</body>'; Memo1.Refresh; { urmeaza, in fisier: </html>} CloseFile(f); OpenDialog1.Filter:='Fisiere Jpeg|*.JPG'; OpenDialog1.FilterIndex:=1; AssignFile(f,'sus.html'); Reset(f); for i:=1 to 7 do ReadLn(f,s); { se sare pana la nume } ReadLn(f,s); Edit1.Text:=s; for i:=1 to 5 do ReadLn(f,s); { se sare pana la motto } ReadLn(f,s); Edit2.Text:=s; CloseFile(f); if Edit3.Text='' then Edit3.Text:='(dati numele fisierului)' end;
Procedura TForm1.Button1Click va apela la OpenDialog1.Execute pentru a afia o fereastr de dialog i pentru a alege un fiier de pe disc.
procedure TForm1.Button1Click(Sender: TObject); begin if OpenDialog1.Execute then Edit3.Text:=OpenDialog1.FileName; end;
81
n fine, procedura TForm1.Button2Click va crea i salva toat pagina web, n urmtoarele etape: se va salva numele persoanei i motto-ul acesteia, n fiierul sus.html; se va salva n fiierul stanga.html cuprinsul i referinele din cuprins, adic denumirile celor trei subiecte; instruciunile care realizeaz acest lucru sunt de forma:
WriteLn(f,'<li><a href="sub1.html" '+ 'target="drp">'); WriteLn(f,subiect[1]); WriteLn(f,'</a>');
82
{ salvam meniul din stanga } AssignFile(f,'stanga.html'); Rewrite(f); WriteLn(f,'<html>'); WriteLn(f,'<head><title>Meniul</title></head>'); WriteLn(f,'<body background="fundal.jpg">'); WriteLn(f,'<h3><b>Cuprins:</b></h3>'); WriteLn(f,'<h4><ul>'); WriteLn(f,'<li><a href="sub1.html" '+ 'target="drp">'); WriteLn(f,subiect[1]); WriteLn(f,'</a>'); WriteLn(f,'<li><a href="sub2.html" '+ 'target="drp">'); WriteLn(f,subiect[2]); WriteLn(f,'</a>'); WriteLn(f,'<li><a href="sub3.html" '+ 'target="drp">'); WriteLn(f,subiect[3]); WriteLn(f,'</a>'); WriteLn(f,'</ul>'); WriteLn(f,'</body></html>'); CloseFile(f); { salvam ce este scris in subiectul curent } Str(nr_sub_tratat,s); AssignFile(f,'sub'+s+'.html'); Rewrite(f); WriteLn(f,'<html><head><title>'); WriteLn(f,Edit4.Text); WriteLn(f,'</title></head>'); WriteLn(f,'<body background="fundal.jpg">'); if (Edit3.Text<>'(dati numele fisierului') and (Edit3.Text<>'') then begin WriteLn(f,'<img src='); WriteLn(f,'"'+Edit3.Text+'">') end; if Memo1.Lines.Count=0 then WriteLn(f,'<p>(tratati aici pe larg'+ ' acest subiect)</p>') else for i:=0 to Memo1.Lines.Count-1 do WriteLn(f,'<p>'+Memo1.Lines[i]+'</p>'); WriteLn(f,'</body>'); WriteLn(f,'</html>'); CloseFile(f); end; end.
83
Aplicaia 8
n meniul programului, avem trei opiuni: Persoane - care cuprinde comenzile: 1. Noua - pentru nscrierea datelor despre o persoan nou; 2. Veche - pentru citirea datelor despre o persoan deja nscris i afiarea datelor zodiacale ale respectivei persoane (ca n exemplul din figura anterioar); 3. Exit - pentru oprirea programului
84
Zodii - care cuprinde dousprezece comenzi purtnd numele celor dousprezece zodii (Berbec ... Peti) i care ne permit afiarea de date despre o zodie oarecare, la fel ca i n cazul comenzii Veche (vezi figura de mai sus); Compatibiliti - care cuprinde dou comenzi, una pentru stabilirea compatibilitii a dou persoane i una pentru stabilirea compatibilitii a dou zodii.
Programul folosete dousprezece fiiere BMP ce cuprind simbolurile zodiacale. Aceste fiiere pot fi create de dumneavoastr n felul urmtor. Pornii un editor de imagini ce permite i scrierea de texte (de pild Paint), alegei fontul Wingdings (cu simboluri) i acolo vei gsi simbolurile cutate. Apoi selectai i decupai zona dreptunghiular ce ncadreaz fiecare astfel de simbol i salvai-o ntr-un fiier corespunztor.
Zodii de foc Zodii de pamnt Zodii de aer Zodii de ap
berbec.bmp
taur.bmp
gemeni.bmp
rac.bmp
leu.bmp
fecioara.bmp
balanta.bmp
scorpion.bmp
sagetator.bmp
varsator.bmp capricorn.bmp
pesti.bmp
Modul de lucru al programului este urmtorul: la nceput, n cmpul de text nu se afieaz nimic. Ulterior, utilizatorul programului poate s nscrie o persoan, preciznd doar acele date despre ea, care sunt necesare: numele i prenumele, data naterii i sexul. Datele despre o persoan nou sunt nscrise pe disc, ntr-un fiier text cu extensia PRS, al crui nume poate fi solicitat utilizatorului, dar se propune ca numele acesta s fie identic cu numele persoanei nscrise, mai ales c n mediul Windows 95/98 sunt acceptate asemenea nume lungi de fiiere. Un exemplu de asemenea fiier este urmtorul: 85
Daniel Niescu este un brbat Sgettor, aa nct se vor afia datele generale despre zodia Sgettorului i particularitile brbailor din aceast zodie. nscrierea datelor despre o persoan nou se va realiza cu ajutorul unor controale speciale, care sunt grupate n partea din stnga a ferestrei aplicaiei i care sunt n general invizibile, ele fiind afiate doar n cazul introducerii de date.
Cnd se va dori s se afle datele zodiacale despre o persoan deja existent n baza de date, se va aciona comanda Veche din submeniul Persoane, dup cum se vede n figura urmtoare: 86
Submeniul Zodii cuprinde comenzi pentru afiarea datelor despre cele dousprezece zodii. Se vor afia informaii ce sunt cuprinse n nite fiiere text, cu numele zodiilor, care au urmtoarea structur: Numele zodiei Date generale despre zodie $ Barbat Date generale despre brbaii din acea zodie $ Femeie Date generale despre femeile din acea zodie Cnd se acioneaz butonul pentru o zodie, se vor afia att datele generale, ct i particularitile pentru cele dou sexe. Acest lucru nu este valabil n cazul selectrii unei persoane deja introduse, pentru care se vor afia datele generale i doar datele care privesc sexul persoanei respective.
87
Ultimul submeniu se refer la compatibiliti zodiacale. Dac se apas pe comanda De zodii, atunci se vor introduce numele celor dou zodii, iar la comanda De persoane se vor alege, de pe disc, fiierele a dou persoane, din care se vor citi datele i deci se vor stabili zodiile. Aadar stabilirea compatibilitii a dou persoane revine la stabilirea compatibilitii zodiilor din care cele dou persoane fac parte. La compatibilitatea zodiilor, se vor introduce numele zodiilor. Propunem cititorului s modifice aceast parte a programului, astfel nct numele zodiei s poat fi selectat dintr-o list.
88
Dac s-au ales dou persoane pentru a le stabili compatibilitatea, atunci datele de compatibilitate (citite dintr-un fiier text COMPAT.TXT) vor fi afiate ca n figura urmtoare:
n cazul compatibilitii de zodii, nu vor mai aprea numele celor dou persoane. Fiierul COMPAT.TXT cuprinde 12+11+10+...+1 articole (corespunztoare tuturor combinaiilor zodiacale), de forma urmtoare: zodia1-zodia2 Date despre compatibilitatea celor dou zodii stop De exemplu, la zodia Racului vom avea combinaii cu toate zodiile de la Rac pn la Peti, dar nu i combinaii ale Racului cu zodii dinaintea Racului. (S-a procedat n acest mod, deoarece se consider c o combinaie de genul zodia1-zodia2 este identic unei combinaii de tipul zodia2-zodia1.)
89
spnAnul BitBtn1
90
n afara elementelor evideniate mai sus, exist cele patru etichete (Label1, ..., Label4) corespunztoare textelor: Numele, Ziua, Luna i Anul. Controalele spnZiua i spnAnul sunt de tip TSpinEdit i se gsesc n mulimea de controale Samples din mediul Delphi 3.0. Controlul PersoanaNoua este de tip TGroupBox. Iniial el va fi invizibil, urmnd ca s fie artat, la cerere, din program. Controlul rgSexul este de tip TRadioGroup, cu primul item selectat. Controlul Dialog (de tip TOpenDialog) este aleas din mulimea de controale Dialogs a mediului Delphi 3.0. n sfrit, butonul de tip TBitBtn este ales din mulimea de controale adiionale (Additional) i este un buton de tip bkOK (vezi proprietatea Kind). Programul Zodiac are la baz unit-ul zodiac1.pas, listat i comentat n continuare. Interfaa unit-ului corespunde elementelor descrise n ultima figur.
unit zodiac1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Menus, StdCtrls, ExtCtrls, Buttons, Spin; type TForm1 = class(TForm) MainMenu1: TMainMenu; Persoane1: TMenuItem; Noua1: TMenuItem; Veche1: TMenuItem; Exit1: TMenuItem; Zodii1: TMenuItem; Berbec1: TMenuItem; Taur1: TMenuItem; Gemeni1: TMenuItem; Rac1: TMenuItem; Leu1: TMenuItem; Fecioara1: TMenuItem; Balanta1: TMenuItem; Scorpion1: TMenuItem; Sagetator1: TMenuItem; Capricorn1: TMenuItem; Varsator1: TMenuItem;
91
Pesti1: TMenuItem; Compatibilitati1: TMenuItem; Dezodii1: TMenuItem; Depersoane1: TMenuItem; Dialog: TOpenDialog; imgZodia: TImage; PersoanaNoua: TGroupBox; lblZodia: TLabel; Label1: TLabel; txtNume: TEdit; rgSexul: TRadioGroup; Label2: TLabel; Label3: TLabel; Label4: TLabel; BitBtn1: TBitBtn; memZodia: TMemo; spnZiua: TSpinEdit; lstLuna: TListBox; spnAnul: TSpinEdit; procedure BitBtn1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Balanta1Click(Sender: TObject); procedure Berbec1Click(Sender: TObject); procedure Taur1Click(Sender: TObject); procedure Gemeni1Click(Sender: TObject); procedure Rac1Click(Sender: TObject); procedure Leu1Click(Sender: TObject); procedure Fecioara1Click(Sender: TObject); procedure Scorpion1Click(Sender: TObject); procedure Sagetator1Click(Sender: TObject); procedure Capricorn1Click(Sender: TObject); procedure Varsator1Click(Sender: TObject); procedure Pesti1Click(Sender: TObject); procedure Exit1Click(Sender: TObject); procedure Dezodii1Click(Sender: TObject); procedure Noua1Click(Sender: TObject); procedure Veche1Click(Sender: TObject); procedure Depersoane1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1;
92
n partea de implementare sunt declarate, mai nti nite variabile de lucru referitoare la persoana curent, respectiv numele celor dou persoane, n cazul verificrii unei compatibiliti zodiacale:
implementation {$R *.DFM} var Sex, Nume, DataNast, nume1, nume2: String;
Procedura urmtoare verific compatibilitatea dintre dou zodii z1 i z2, citind date din fiierul 'COMPAT.TXT' pn la ntlnirea unui ir de caractere s identic lui 'z1-z2' sau lui 'z2-z1'. Dac se gsete un asemenea ir, toate rndurile care urmeaz, pn la ntlnirea cuvntului 'stop' sunt citite i adugate irului t, care, la nceput cuprinde un text explicativ de genul 'Compatibilitate ntre ... i ...'. n finalul procedurii au loc urmtoarele operaii:
Form1.memZodia.Text := t; t := ''; Form1.imgZodia.Picture:=Nil;
Prin prima se pune n caseta de tip Memo (cu numele memZodia) textul din irul de caractere t, apoi acesta devine vid, pentru utilizri ulterioare, iar zona de imagine (pentru simbolul zodiei) se terge (pentru c este vorba despre dou zodii).
procedure AfiseazaCompatib(z1, z2: String); var f: TextFile; gata: Boolean; s, t, ss: String; begin Form1.Caption := 'Zodiac - ' + z1 + ' <-> ' + z2; Nume := ''; Form1.lblZodia.Caption := ''; AssignFile(f,'compat.txt'); Reset(f); gata := False; while (not eof(f)) and (not gata) do begin ReadLn(f,s); if (s = z1 + '-' + z2) or (s = z2 + '-' + z1) then begin t:='Compatibilitate intre'+Chr(13)+Chr(10); t := t+z1+' ('+nume1+')'; t := t+' si '+z2+' ('+nume2+'):'; t := t+Chr(13)+Chr(10)+Chr(13)+Chr(10); repeat ReadLn(f,ss);
93
if ss <> 'stop' then t := t + ss + Chr(13) + Chr(10) until ss = 'stop'; gata := True end end; Close(f); {sau CloseFile(f)} Form1.memZodia.Text := t; t := ''; Form1.imgZodia.Picture:=Nil; end;
O funcie foarte important, de interes general este cea care determin, n funcie de variabila global DataNast zodia corespunztoare unei anumite date de natere. Mai nti se determin ziua i luna din irul DataNast, folosind funcia Copy. Apoi modul ei de funcionare este simplu, bazat pe regulile astrologilor.
function DeterminaZodia: String; var ziua, luna, z: String; zi, lu, cod: Integer; begin ziua := Copy(DataNast, 1, 2); Val(ziua, zi, cod); luna := Copy(DataNast, 4, 2); Val(luna, lu, cod); case lu of 3: if zi <= 20 then z := 'Pesti' else z := 'Berbec'; 4: if zi <= 20 then z := 'Berbec' else z := 'Taur'; 5: if zi <= 20 then z := 'Taur' else z := 'Gemeni'; 6: if zi <= 21 then z := 'Gemeni' else z := 'Rac'; 7: if zi <= 22 then z := 'Rac' else z := 'Leu'; 8: if zi <= 22 then z := 'Leu' else z := 'Fecioara'; 9: if zi <= 22 then z := 'Fecioara' else z := 'Balanta'; 10: if zi <= 22 then z := 'Balanta' else z := 'Scorpion'; 11: if zi <= 21 then z := 'Scorpion' else z := 'Sagetator'; 12: if zi <= 20 then z := 'Sagetator' else z := 'Capricorn'; 1: if zi <= 19 then z := 'Capricorn' else z := 'Varsator'; 2: if zi <= 18 then z := 'Varsator' else z := 'Pesti' end; DeterminaZodia := z end;
94
O procedur foarte important a aplicaiei Zodiac este cea care afieaz informaii despre o anumit zodie, n cmpul memZodia. Dac parametrul sx (indicnd sexul) este irul vid, aceasta nseamn afiarea n ntregime a fiierului text corespunztor zodiei (adic BERBEC.TXT, TAUR.TXT etc.). Dac ns sx este 'barbat' atunci se vor afia datele generale i particularitile brbailor. Dac sx este 'femeie', atunci se vor afia datele generale, se va sri peste datele despre brbai i se vor afia datele despre femei. De asemenea, simbolul zodiacal va fi afiat prin apelul:
Form1.imgZodia.Picture.LoadFromFile(zodia + '.bmp') procedure AfiseazaInformatii(zodia, sx: String); var f: Text; s, t: String; begin if Nume <> '' then Form1.Caption := 'Zodiac' + ' - ' + Nume + ' (' + DataNast + ')' else Form1.Caption := 'Zodiac'; Form1.lblZodia.Caption := zodia; AssignFile(f,zodia + '.txt'); Reset(f); if sx='' then {ambele sexe - informatii complete} begin t := ''; while not eof(f) do begin ReadLn(f,s); t := t + s + Chr(13) + Chr(10) end; end else if sx='barbat' then {se afiseaza pina la $ Femeie } begin t:=''; repeat ReadLn(f,s); if s<>'$ Femeie' then t := t + s + Chr(13) + Chr(10) until s='$ Femeie' end else { sexul este femeie, se afiseaza datele generale, apoi se sare peste datele despre barbat, apoi se continua } begin t:='';
95
repeat ReadLn(f,s); if s<>'$ Barbat' then t := t + s + Chr(13) + Chr(10) until s='$ Barbat'; repeat ReadLn(f,s); until s='$ Femeie'; t := t + s + Chr(13) + Chr(10); while not eof(f) do begin ReadLn(f,s); t := t + s + Chr(13) + Chr(10) end end; Close(f); Form1.memZodia.Text := t; t := ''; Form1.imgZodia.Picture.LoadFromFile(zodia + '.bmp') end;
Dup cum am spus, comanda 'Nou' din submeniul 'Persoane' permite adugarea de noi persoane. Acest lucru se realizeaz dup ce grupul de controale PersoanaNoua devine vizibil, deci accesibil.
procedure TForm1.Noua1Click(Sender: TObject); begin PersoanaNoua.Visible := True end;
O dat devenit vizibil, grupul PersoanaNoua permite acionarea asupra mai multor controale. Din rgSexul se determin sexul persoanei noi, ce urmeaz a fi introdus n baza de date. Parcurgnd (cu instruciunea for) lista lstLuna pentru a vedea care item este selectat, se determin indicele lunii alese (L). Apoi se transform L n LL pentru a lucra cu un ir de caractere. Variabilele ZZ i AA vor conine iruri de caractere, reprezentnd ziua i, respectiv, anul de natere ale persoanei nou introduse. Valoarea lui ZZ este preluat din controlul spnZiua, iar a lui AA din controlul spnAnul. Cele dou controale de tip "spin" vor avea valori limitate (stabilite la crearea formei): ziua va fi ntre 1 i 31, iar anul ntre 1900 i 2000. 96
procedure TForm1.BitBtn1Click(Sender: TObject); var ZZ,LL,AA: String; prompt: String; L, i: Integer; f: TextFile; zod: String; NumeFisier: String; begin if rgSexul.ItemIndex=0 then Sex := 'femeie' Else Sex := 'barbat'; L:=1; for i := 0 to lstLuna.Items.Count-1 do if lstLuna.Selected[i] then L := i + 1; Str(L,LL); Str(spnAnul.Value,AA); Str(spnZiua.Value,ZZ); Nume := txtNume.Text; DataNast := ZZ + '.' + LL + '.' + AA; prompt := 'Dati numele fisierului'; prompt := prompt + 'in care se vor salva datele:'; NumeFisier := InputBox('Salvare date persoana', prompt, Nume); AssignFile(f,NumeFisier + '.prs'); Rewrite(f); WriteLn(f, Nume); WriteLn(f, Sex); WriteLn(f, DataNast); CloseFile(f); { ce ar fi sa nu mai existe acest fisier! } PersoanaNoua.Visible := False; zod := DeterminaZodia; AfiseazaInformatii(zod,Sex) end;
Dup ce s-au determinat toate datele despre persoana nou, se va cere s se introduc numele fiierului n care se vor pstra aceste date. Date sunt scrise pe disc: numele, sexul i data naterii. Procedura se termin prin afiarea datelor despre persoana nou introdus, folosind secvena de instruciuni:
PersoanaNoua.Visible := False; zod := DeterminaZodia; AfiseazaInformatii(zod,Sex)
O atenie deosebit trebuie acordat listei cu denumirile prescurtate ale lunilor. Trebuie s se selecteze un element al listei, altfel se va lua n considerare luna ianuarie.
97
Lista cu denumirile lunilor, precum i valorile minime, maxime i implicite ale cmpurilor spnZiua i spnAnul sunt stabilite la nceput, prin procedura TForm1.FormCreate.
procedure TForm1.FormCreate(Sender: TObject); var i: Integer; Item: String; begin Sex := ''; Nume := ''; DataNast := ''; spnZiua.MinValue:=1; spnZiua.MaxValue:=31; spnZiua.Value:=15; spnAnul.MinValue:=1900; spnAnul.MaxValue:=2000; spnAnul.Value:=1970; lstLuna.Items.Add('ian'); lstLuna.Items.Add('feb'); lstLuna.Items.Add('mar'); lstLuna.Items.Add('apr'); lstLuna.Items.Add('mai'); lstLuna.Items.Add('iun'); lstLuna.Items.Add('iul'); lstLuna.Items.Add('aug'); lstLuna.Items.Add('sep'); lstLuna.Items.Add('oct'); lstLuna.Items.Add('nov'); lstLuna.Items.Add('dec'); PersoanaNoua.Visible := False end;
n continuare sunt date cele dousprezece procedurii care se apeleaz din meniu, pentru toate zodiile. Fiecare procedur este foarte simpl i face apel la AfiseazaInformatii. Primul argument este numele zodiei, iar ce de-al doilea argument este irul vid, tocmai pentru ca s se afieze informaiile complete.
procedure TForm1.Balanta1Click(Sender: TObject); begin Nume := ''; AfiseazaInformatii('Balanta','') end; procedure TForm1.Berbec1Click(Sender: TObject); begin Nume := ''; AfiseazaInformatii('Berbec','') end; procedure TForm1.Taur1Click(Sender: TObject); begin Nume := ''; AfiseazaInformatii('Taur','') end;
98
procedure TForm1.Gemeni1Click(Sender: TObject); begin Nume := ''; AfiseazaInformatii('Gemeni','') end; procedure TForm1.Rac1Click(Sender: TObject); begin Nume := ''; AfiseazaInformatii('Rac','') end; procedure TForm1.Leu1Click(Sender: TObject); begin Nume := ''; AfiseazaInformatii('Leu','') end; procedure TForm1.Fecioara1Click(Sender: TObject); begin Nume := ''; AfiseazaInformatii('Fecioara','') end; procedure TForm1.Scorpion1Click(Sender: TObject); begin Nume := ''; AfiseazaInformatii('Scorpion','') end; procedure TForm1.Sagetator1Click(Sender: TObject); begin Nume := ''; AfiseazaInformatii('Sagetator','') end; procedure TForm1.Capricorn1Click(Sender: TObject); begin Nume := ''; AfiseazaInformatii('Capricorn','') end; procedure TForm1.Varsator1Click(Sender: TObject); begin Nume := ''; AfiseazaInformatii('Varsator','') end; procedure TForm1.Pesti1Click(Sender: TObject); begin Nume := ''; AfiseazaInformatii('Pesti','') end;
99
O alt procedur ce face apel la AfiseazaInformatii este procedura chemat din comanda Veche (submeniul Persoane). Acum se folosete controlul Dialog. Se stabilete titlul ferestrei de dialog, tipul de fiiere ce vor fi deschise, apoi se apeleaz metoda (funcia) Execute. Dac ea returneaz True (adic s-a apsat butonul OK i nu Cancel), atunci cmpul Dialog.Filename conine numele fiierului ce a fost selectat. Acesta se deschide, din el se citesc datele necesare, apoi se determin zodia i se afieaz informaiile corespunztoare.
procedure TForm1.Veche1Click(Sender: TObject); var zod: String; f: TextFile; begin Dialog.Title := 'Alegeti persoana dorita'; Dialog.Filter := 'Persoane (*.prs)|*.prs'; if Dialog.Execute then begin AssignFile(f,Dialog.filename); Reset(f); ReadLn(f, Nume); ReadLn(f, Sex); ReadLn(f, DataNast); CloseFile(f); zod := DeterminaZodia; AfiseazaInformatii(zod,Sex) end end;
O alt procedur ce folosete (de dou ori) controlul de dialog este cea cae afieaz compatibilitatea dintre dou persoane:
procedure TForm1.Depersoane1Click(Sender: TObject); var zod1, zod2: String; f: TextFile; begin Dialog.Title := 'Alegeti prima persoana'; Dialog.Filter := 'Persoane (*.prs)|*.prs'; if Dialog.Execute then begin AssignFile(f,Dialog.filename); Reset(f); ReadLn(f,Nume); nume1 := Nume; ReadLn(f,Sex); ReadLn(f,DataNast); CloseFile(f); zod1 := DeterminaZodia end; Dialog.Title := 'Alegeti a doua persoana'; Dialog.Filter := 'Persoane (*.prs)|*.prs'; if Dialog.Execute then begin
100
AssignFile(f,Dialog.filename); Reset(f); ReadLn(f,Nume); nume2 := Nume; ReadLn(f,Sex); ReadLn(f,DataNast); CloseFile(f); zod2 := DeterminaZodia end; AfiseazaCompatib(zod1, zod2) end;
Firete, putei face mult mai interesant acest program, dac vei avea i fotografiile persoanelor din baza de date, pe care s le afiai, aa cum se fcea n aplicaia Clubul nsingurailor. De asemenea, putei combina cele dou aplicaii, pentru realizarea unei complexe, n care criteriul compatibilitii zodiacale s fie luat n considerare atunci cnd se dorete formarea unor cupluri, n cadrul clubului nsingurailor.
101
Aplicaia 9
De pild, n captura de ecran de mai sus, cu alimentele disponibile (listate n stnga) se pot gti una din cele dou preparate, listate n dreapta. n partea de jos este dat reeta de preparare a mncrii selectate n lista din dreapta. 102
Alimentele pot fi adugate, scriind n csuele de text din stnga sus att cantitatea, ct i denumirea alimentului. Butonul pe care scrie "Adaug" permite adugarea unui aliment (n cantitatea disponibil), iar cellalt permite eliminarea unui aliment. Cu butonul "Stop" se oprete programul. Dac se elimin cele 1000 grame de mrar din colecia de alimente, atunci, de pild, nu se mai poate prepara tocana de cartofi. Firete, pentru a putea determina ce preparate culinare pot fi gtite cu disponibilul de alimente dat, trebuie s existe un fiier special, care s cuprind mai multe reetele culinare. Putei scrie aici o ntreag carte de bucate, dar innd cont de formatul fiecrei reete, care poate fi dedus din urmtorul exemplu:
Omleta cu cascaval si sunca 2 oua 100 cascaval 50 faina 50 sunca 10 sare 20 ulei Se bat ouale si se amesteca cu faina si sunca. Se pune continutul in tigaie si se prajeste la foc mic, timp de 2-3 minute. Inainte de a lua de pe foc se presara sare si cascaval dat prin razatoare. Se serveste imediat Tocana de cartofi 100 bulion 8 cartofi 50 patrunjel 50 marar 10 sare 2 cepe 20 ulei Se curata ceapa si se taie marunt. Se fierbe in tigaia cu ulei si apa. Se adauga bulion rezultind un sos. Intre timp, se curata cartofii si se taie bucati. Se fierb cartofii intr-un vas, in care s-a turnat sare. La sfirsit, se toarna sosul peste cartofii fierti, dupa ce sa scos apa din vas. Se amesteca sosul uniform si se adauga bucati de patrunjel si marar.
Aadar, reetele se scriu n fiierul 'RETETE.TXT', una dup alta, fiecare reet artnd astfel: 103
numele reetei c1 a 1 c2 a 2 .... cn a n modul de preparare al reetei respective, pe unul sau mai multe rnduri (un rnd liber) ci sunt cantitile de alimente, iar ai sunt chiar denumirile acestor alimente. ntre cantiti i denumirile alimentelor exist un spaiu. Dup cum se vede, tocana de cartofi are nevoie de mrar pentru a fi preparat, iar eliminarea mrarului din lista alimentelor disponibile va nsemna c nu putem prepara tocana. Aadar, exist un fiier special care cuprinde lista alimentelor de care dispunem n buctria noastr: 'ALIMENTE.TXT'. Acest fiier poate fi iniial vid, dar trebuie s existe. Practic, atunci cnd se fac verificri asupra alimentelor disponibile, se lucreaz cu o list de alimente, pstrat n memorie. Lista respectiv se actualizeaz permanent n timpul programului, ori de cte ori se acioneaz butoanele 'Adaug' i 'Elimin', de ctre utilizatorul programului. Cu toate acestea, fiierul 'ALIMENTE.TXT' este necesar, deoarece, la fiecare nou pornire a aplicaiei GASTRO, trebuie s cunoatem lista curent de alimente. Aadar, la orice lansare n execuie a aplicaiei se ncarc datele din acest fiier n lista cu alimente, iar la orice oprire a programului, lista curent de alimente este salvat n fiierul text 'ALIMENTE.TXT'. n exemplul nostru, fiierul 'ALIMENTE.TXT' are urmtorul coninut:
1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 bulion cartofi cascaval cepe faina marar oua patrunjel sare sunca ulei
104
Propunem cititorului s porneasc de la ideea de baz a acestui program pentru a realiza un program gastronomic complex, care s sugereze buctarului att reetele preparabile cu alimentele disponibile, dar i acele preparate culinare care ar putea fi realizate dac s-ar mai dispune de un aliment care lipsete. Un alt caz ce ar putea fi luat n considerare ar fi acela cnd s-ar putea prepara o anumit mncare, toate alimentele sunt n buctrie, dar unul sau mai multe din ele sunt insuficiente. Lund n considerare diverse astfel de cazuri, am putea realiza un program care s vin n ajutor oricrei persoane ce se ocup de pregtirea mesei. Firete, trebuie ca fiierul cu reete culinare s cuprind ct mai multe articole.
txtCant lstAlimente
cmdStop memReteta
105
Observai existena unui buton suplimentar, invizibil, pe care scrie 'Reete'. Dac dorii, putei s-l lsai vizibil, n orice caz, acest buton rspunde la evenimentul de Click i procedura asociat este apelat automat att la nceputul programului (la crearea formei), ct i la adugarea sau eliminarea unui aliment din list (realizat cu unul din cele dou butoane). n continuare, s descriem unit-ul 'gastro1.pas' care st la baza programului nostru.
unit gastro1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
Dup cum se vede, Form1 conine exact elementele de interfa descrise n figura anterioar.
type TForm1 = class(TForm) txtCant: TEdit; txtAliment: TEdit; lstAlimente: TListBox; cmdAdauga: TButton; cmdElimina: TButton; cmdRetete: TButton; memReteta: TMemo; lstRetete: TListBox; cmdStop: TButton; procedure FormCreate(Sender: TObject); procedure txtCantKeyPress(Sender: TObject; var Key: Char); procedure cmdAdaugaClick(Sender: TObject); procedure cmdEliminaClick(Sender: TObject); procedure cmdReteteClick(Sender: TObject); procedure cmdStopClick(Sender: TObject); procedure lstReteteClick(Sender: TObject); procedure txtAlimentKeyPress(Sender: TObject; var Key: Char); private { Private declarations } public { Public declarations } end;
106
n primul rnd, avem nevoie de urmtoarea funcie logic, ce verific dac un anumit aliment a este disponibil n cantitatea c n buctria noastr. Astfel, funcia verific dac alimentul cu denumirea a figureaz n lista de alimente (lstAlimente), iar dac da, dac cantitatea respectiv (cc) este cel puin c.
function EsteDisponibil(a: String; c: Integer): Boolean; var g: Boolean; p, cc, cod, i: Integer; aa, s: String; begin g := False; for i := 0 to Form1.lstAlimente.Items.Count - 1 do begin s := Form1.lstAlimente.Items[i]; p := Pos(' ',s); Val(Copy(s, 1, p - 1),cc,cod); aa := Copy(s, p+1, 255); If (a = aa) And (c <= cc) Then g := True end; EsteDisponibil := g end;
Dup cum am spus, lista de alimente se pstreaz, de la o execuie a programului, la alta, n fiierul text 'ALIMENTE.TXT'. La crearea formei aplicaiei, lista de alimente se ncarc cu datele din acest fiier. De asemenea, au loc i alte iniializri importante asupra cmpului de tip Memo memReteta, dar acestea puteau fi realizate chiar n faza de proiectare a aplicaiei, de ctre programator.
procedure TForm1.FormCreate(Sender: TObject); var f: TextFile; s: String; begin memReteta.Text:=''; memReteta.ScrollBars:=ssBoth; cmdRetete.Hide; AssignFile(f,'alimente.txt'); Reset(f); while not eof(f) do begin ReadLn(f,s); lstAlimente.Items.Add(s)
107
n final, procedura de mai sus acioneaz automat butonul invizibil cmdRetete, prin click de mouse. Aceasta determin apelarea procedurii TForm1.cmdReteteClick, adic listarea n lstRetete a reetelor ce se pot realiza pe baza lui lstAlimente, iar n partea de jos (n controlul Memo memReteta) va fi afiat prima reet din cele disponibile n lstRetete. Atunci cnd se introduce un text n csua txtCant destinat introducerii de cantiti pentru alimente, la sfrit utilizatorul va apsa tasta Enter. Aceasta va determina focalizarea csuei urmtoare, adic cea pentru scrierea denumirii alimentului de introdus: txtAliment.
procedure TForm1.txtCantKeyPress(Sender: TObject; var Key: Char); begin If Key = Chr(13) then txtAliment.SetFocus end;
Dac se acioneaz tasta Enter n csua txtAliment, dup ce s-a scris denumirea alimentului, atunci se focalizeaz butonul de adugare a alimentului, aa cum demonstreaz i procedura urmtoare:
procedure TForm1.txtAlimentKeyPress(Sender: TObject; var Key: Char); begin if Key = Chr(13) then cmdAdauga.SetFocus end;
Aadar, dup ce s-au introdus cele dou date din csuele de text, s-a ajuns, n final, la acionarea prin click de mouse a butonului 'Adaug'. Aceasta nseamn concatenarea celor dou iruri din txtCant i txtAliment (desprite de un spaiu) i adugarea n lstAlimente a irului rezultat. Apoi se focalizeaz txtCant, i se afieaz reetele ce pot fi preparate n noile condiii.
108
procedure TForm1.cmdAdaugaClick(Sender: TObject); begin lstAlimente.Items.Add(txtCant.Text + ' ' + txtAliment.Text); cmdRetete.Click end;
Procedura urmtoare realizeaz tocmai operaia invers. Ea const n cutarea alimentului ce este selectat pentru a i determina poziia i a-l elimina (dac este selectat unul). Apoi se afieaz reetele ce pot fi preparate, n noile condiii i se focalizeaz txtCant.
procedure TForm1.cmdEliminaClick(Sender: TObject); var i: Integer; g: Boolean; begin i := 0; g := False; while (not g) and (i < lstAlimente.Items.Count) do if lstAlimente.Selected[i] then g := True else i := i + 1; if g then lstAlimente.Items.Delete(i); cmdRetete.Click; txtCant.SetFocus end;
Procedura TForm1.cmdReteteClick realizeaz partea de baz a programului, fiind cea care determin ce reete pot fi realizate pe baza alimentelor disponibile n lista dat. Astfel, se ia fiecare reet pe rnd. Pentru fiecare se verific dac ncepe cu un numr. Dac da, atunci e clar c suntem n "zona" de nceput a reetei, adic cea n care sunt specificate alimentele i cantitile lor. De aceea se determin despre ce cantitate este vorba i despre ce aliment. Se apeleaz, apoi, la funcia EsteDisponibil pentru a verifica dac aceast reet poate fi preparat. n caz c alimentul determinat ca fiind necesar n reeta curent nu este disponibil cel puin n cantitatea precizat n reet, atunci o variabil special logic ok marcheaz c nu se poate prepara reeta. Dac toate alimentele i cantitile lor cerute de reeta respectiv nu modific pe ok, fcndu-l False, el rmne True, iar denumirea reetei respective este adugat n lista lstRetete. 109
Firete, exist posibilitatea extrem ca, conform alimentelor din buctria noastr, s nu putem prepara nici una din reetele din cartea de bucate stocat pe calculator. n acest caz, se afieaz un mesaj de eroare ca n figura urmtoare, n care se observ c nu dispunem de ulei, ns fiecare din cele dou reete din cartea noastr de bucate au nevoie de ulei.
procedure TForm1.cmdReteteClick(Sender: TObject); var f: TextFile; denumire, s: String; v, p, cod: Integer; ok: Boolean; cantitate: Integer; aliment: String; begin lstRetete.Clear; memReteta.Text := ''; AssignFile(f,'retete.txt'); Reset(f); while not eof(f) do begin ReadLn(f,denumire); ok := True; repeat ReadLn(f,s); if s <> '' then begin Val(Copy(s,1,1),v,cod); if v > 0 then {este numar} begin p := Pos(' ',s); Val(Copy(s,1,p-1),cantitate,cod); aliment := Copy(s,p+1,255); if not EsteDisponibil(aliment, cantitate) then ok := False end end until s = ''; if ok then lstRetete.Items.Add(denumire) end; CloseFile(f); if lstRetete.Items.Count > 0 then begin lstRetete.ItemIndex := 0; Form1.lstReteteClick(Sender) end else begin lstRetete.Items.Add('<nici o reteta disponibila>'); memReteta.Text := '<nici o reteta disponibila>' end end;
110
Procedura de mai jos realizeaz afiarea, n cmpul Memo memReteta a reetei selectate din lista de reete lstRetete. Dup ce se determin indicele ei, se compar itemul selectat cu numele fiecrei reete din fiierul cu reete, pn se descoper una cu numele identic. n acel moment, se citete reeta n ntregime i irul t, iniial vid, se modific n sensul c se concateneaz cu fiecare ir s citit din fiierul f ('RETETE.TXT') i sfritul de linie corespunztor. n final, acest ir t se memoreaz n memReteta.
procedure TForm1.lstReteteClick(Sender: TObject); var s, t: String; f: TextFile; denumire: String; i, gasit: Integer; begin gasit:=-1; t := ''; for i:=0 to lstRetete.Items.Count-1 do if (lstRetete.Selected[i]) then gasit:=i; if gasit>=0 then begin AssignFile(f,'retete.txt'); Reset(f); while not eof(f) do
111
begin ReadLn(f,denumire); if denumire=lstRetete.Items[gasit] then repeat ReadLn(f,s); if s <> '' then t := t + s + Chr(13) + Chr(10) until s = '' else repeat ReadLn(f,s) until s = '' end; CloseFile(f) end; memReteta.Text := t end;
O procedur foarte simpl este cea care oprete aplicaia, prin apelul Application.Terminate. Oprirea aplicaiei se face, ns, dup ce datele din lista de alimente sunt stocate n fiierul de pe disc.
procedure TForm1.cmdStopClick(Sender: TObject); var f: TextFile; i: Integer; begin AssignFile(f,'alimente.txt'); Rewrite(f); for i := 0 to lstAlimente.Items.Count - 1 do WriteLn(f,lstAlimente.Items[i]); CloseFile(f); Application.Terminate end; end.
Poft bun!
112
113
Aplicaia 10
Calculator de buzunar
10.1. Prezentare general
Pentru nceput ne propunem s realizm un calculator de buzunar, aa cum este cel din mediile Windows, care s permit realizarea de calcule aritmetice simple (adunri, scderi, nmuliri i mpriri) cu numerele reale. Pentru aceste patru operaii vom avea patru butoane distincte n forma aplicaiei noastre. Un alt buton va fi pentru operaia de schimbare de semn (+/-). Pentru a lucra cu numere reale, deci cu zecimale, vom avea nevoie i de un buton pentru virgula (punctul) zecimal. Dac greim introducerea unui numr, atunci vom folosi un buton Clear pentru a terge ecranul i a relua introducerea respectivului numr. De asemenea, s nu uitm butonul egal care ne va afia rezultatul unei operaii sau a unui ir de mai multe operaii consecutive. n faza de proiectare, calculatorul nostru va arta ca n figura de mai jos, n care butonul On este pentru pornirea efectiv a calculatorului. Dup pornire, acest buton se schimb ntr-un buton Off, de oprire a calculatorului. Butonul de terminare a aplicaiei este butonul Stop.
115
Aplicaia noastr este format dintr-o form cu grosimea simpl fix i nu are buton de maximizare. Interiorul este de culoarea verde deschis. Descrierea formei (numit frmCalculator) este dat mai jos:
VERSION 2.00 Begin Form frmCalculator BackColor = &H00C0FFC0& BorderStyle = 1 'Fixed Single Caption = "Bogdan's Calculator" ClientHeight = 4335 ClientLeft = 1470 ClientTop = 2325 ClientWidth = 3120 Height = 5025 Icon = CALCUL.FRX:0000 Left = 1410 LinkTopic = "Form1" MaxButton = 0 'False ScaleHeight = 4335 ScaleWidth = 3120 Top = 1695 Width = 3240
Printre componentele din aceast form se numr butoanele de schimbare de semn (Sch), de virgul zecimal (Virgula), precum i cele pentru operaiile elementare: adunare (btnPlus), scdere (btnMinus), nmulire (btnOri), mprire (btnDiv). Butonul de egal este: btnEgal, iar celelalte butoane au fost numite: btnClear, btnTerminare (Stop), respectiv btnOnOff. Toate sunt obiecte de tipul CommandButton. Butoanele corespunztoare celor zece cifre sunt grupate ntr-un tablou (vector, array), cu numele btnCifra. Cifra 0 are Index=0, cifra 1 are Index=1 .a.m.d.. Toate butoanele vor fi accesate prin evenimentul Click. n plus, exist un control de tip TextBox cu numele txtEcran, care este inaccesibil (deci nu se poate scrie n el), cu alinierea la dreapta i care folosete drept ecran al minicalculatorului:
Begin TextBox txtEcran Alignment = 1 'Right Justify Enabled = 0 'False MultiLine = -1 'True
116
TabIndex End
Am prevzut i un mic meniu cu dou comenzi care permit obinerea (prin apelul lui MsgBox) a unor informaii despre program i despre autorul su.
Begin Menu mnu1 Caption = "&Despre program" Begin Menu mnuCalc Caption = "Despre &calculator" End Begin Menu mnuBog Caption = "Despre &Bogdan Patrut" End End
Iat cum va arta aplicaia noastr n timpul rulrii (n figur s-a activat meniul.
afar de btnTerminare). De aceea, vom avea nevoie de o variabil logic, pe care o vom numi pornit:
Dim pornit As Integer
Vom nota cu s irul de caractere care apare n ecranul minicalculatorului, adic n txtEcran.Text.
Dim s As String
ntre valoarea lui v, irul s i txtEcran.Text va fi mereu pstrat o coresponden. Operaia curent o vom nota cu oper:
Dim oper As String
Toate declaraiile de mai sus se fac la (general declarations) din Form1. Valoarea variabile pornit, precum i coninutul butonului btnOnOff se schimb la acionarea acestuia prin Click:
Sub btnOnOff_Click () If pornit Then pornit = False btnOnOff.Caption = "On" s = " ": v = 0 txtEcran.Text = s Else pornit = True btnOnOff.Caption = "Off" s = " ": v = 0 txtEcran.Text = " 0" End If End Sub
Observai c au loc iniializri asupra textului din ecranul minicalculatorului, asupra lui v i lui s. n momentul acionrii (prin evenimentul Click) a unei cifre, irul s se concateneaz cu valoarea cifrei (care este, de fapt, indexul acelui buton btnCifra), obinndu-se noul ir s, care se afieaz pe ecranul minicalculatorului:
Sub btnCifra_Click (valoare_cifra As Integer) If pornit Then s = s & valoare_cifra txtEcran.Text = s Else Beep End If End Sub
118
Firete, aceste lucruri se desfoar doar dac minicalculatorul este pornit. Altfel, prin apelul lui Beep, se semnalizeaz eroare. Asemntor lucreaz i butonul cu virgula zecimal, numai c irului s i se adaug punctul zecimal, deoarece acesta va putea fi luat n calcule.
Sub btnVirgula_Click () If pornit Then s = s & "." txtEcran.Text = s Else Beep End If End Sub
Observai c subrutina de mai nainte permite i acionarea de dou ori consecutiv a virgulei zecimale, ceea ce nu este corect. Modificai programul pentru a evita acest lucru! Butonul Sch permite schimbarea semnului numrului de pe ecran:
Sub Sch_Click () If pornit Then Dim w w = Val(s) w = -w: s = Str(w) txtEcran.Text = s Else Beep End If End Sub
Dac se acioneaz butonul Clear, atunci trebuie ca s se tearg ecranul minicalculatorului, dar, aa cum se ntmpl i la minicalculatoarele de buzunar adevrate, ecranul va conine valoarea zero. De asemenea, se consider oper= (nu exist nici o operaie de fcut).
Sub btnClear_Click () If pornit Then txtEcran.Text = "0" oper = "": v = 0: s = " " Else Beep End If End Sub
119
Cele patru butoane pentru operaii realizeaz urmtoarele trei lucruri: determin rezultatul calculelor pn n acel moment, apelnd la subrutina btnEgal_Click (deci e ca i cum s-ar aciona, mai nainte, butonul egal; stabilesc noua valoare a lui oper; modific irul s la irul spaiu, fr a actualiza ecranul txtEcran.Text n vreun fel.
Sub btnPlus_Click () btnEgal_Click oper = "+": s = " " End Sub Sub btnMinus_Click () btnEgal_Click oper = "-": s = " " End Sub Sub btnOri_Click () btnEgal_Click oper = "*": s = " " End Sub Sub btnDiv_Click () btnEgal_Click oper = "/": s = " " End Sub
Cea mai important subrutin a programului nostru este cea care se asociaz evenimentului Click asupra butonului egal. Valoarea rezultatului curent este pstrat n variabila v. Se evalueaz irul s i se actualizeaz valoarea lui v n funcie de operaia oper. Probleme pot apare la mpririle prin zero, unde am pus un mesaj de eroare. Rezultatul final este afiat pe ecranul minicalculatorului.
Sub btnEgal_Click () If pornit Then Select Case oper Case "+" v = v + Val(s) Case "-" v = v - Val(s) Case "*"
120
v = v * Val(s) Case "/" If Val(s) <> 0 Then v = v / Val(s) Else MsgBox "Impartire prin zero!", 48 s = "" End If End Select If oper = "" Then v = Val(s) s = Str(v) txtEcran.Text = s oper = "" Else Beep End If End Sub
121
Aplicaia 11
Prinde mutele
11.1. Prezentare general
Vom realiza mpreun un simplu joc de ndemnare n care, ntr-un interval dat (s zicem un minut) mai multe mute vor apare pe ecran pentru puin timp (fiecare musc are intervalul ei de existen pe ecran), iar noi va trebui s le strivim, prin click cu mouse-ul. Imaginea de mai jos red un moment din acest joc, unde avem trei mute, din care dou vii i una moart, timpul de joc rmas este de 57 de secunde, iar punctajul realizat este 1.
122
Punctajul (iniial zero) va crete cu o unitate la fiecare musc vie lovit (omort) i va scade cu o unitate pentru fiecare musc vie care a apucat s-i ia zborul de pe ecran. La lovirea unei mute se va produce un sunet n boxele Sound Blaster-ului, sunet care va fi dat de un fiier WAV. Realizarea acestei aplicaii presupune definirea unei forme neredimensionabile
VERSION 2.00 Begin Form Form1 BorderStyle ...
'Fixed Double
Pentru scurgerea timpului de joc, avem nevoie de un cronometru, pe care l vom numi Timer2. Timpul de joc rmas va fi afiat n cadrul unei etichete (Label3). Mutele care vor apare pe ecran vor apare n cinci poziii diferite (cele patru coluri plus centrul), iar fiecare va fi reprezentat de o imagine care se va ncrca ntr-o zon proprie. Vom avea nevoie, aadar, de un vector (array) cu 5 imagini, numerotate de la 0 la 4. De exemplu, pentru imaginea cu numrul de ordine 4 avem urmtoarea declaraie, n care se remarc valoarea 2 (cruce) pentru proprietatea MousePointer. Aceasta nseamn c atunci cnd ne vom afla cu mouse-ul deasupra imaginii, forma acestuia se va transforma ntr-o cruce.
Begin Image Image1 Height Index Left MousePointer Top Width End = = = = = = 1095 4 5640 2 'Cross 4560 1095
123
Image1(0)
Label5
MMControl Sunet
Begin Label Label3 Alignment = 2 Caption = "60" End Begin Label Label4 Alignment= 2 'Center Caption = "Secunde ramase" d
Begin Label Label1 Alignment= 2 'Center Caption = "0" End Timer i 1(4)
Sub fiecare imagine din cele cinci am pus cte un cronometru. Astfel, avem un vector cu cinci cronometre, numit Timer1. Fiecare din acestea are proprietatea Interval egal cu intervalul de timp ct rezist pe ecran musca din imaginea corespunztoare.
124
n procedura de iniializare a aplicaiei (Form_Load) se vor stabili intervalele de timp pentru cele cinci mute, precum i starea 0 (musca nu e pe ecran) pentru fiecare din cele cinci imagini. De asemenea, se seteaz intervalul de timp pentru joc (timp=60 corespunde lui Timer2.Interval=1000) i se iniializeaz dispozitivul MultiMedia Sunet. Fiierul cu sunetul produs la lovirea i strivirea unei mute vii se numete aici AU.WAV, dar poate fi schimbat de la caz la caz.
Sub Form_Load () Form1.Caption = "Muste mii si mii" For i = 0 To 4 Timer1(i).Interval = Int(1000 + Rnd * 2000) Image1(i).Tag = 0 Next i Timer2.Interval = 1000 timp = 60 If Not Sunet.Mode = MCI_MODE_NOT_OPEN Then Sunet.Command = "Close" End If Sunet.FileName = "C:\AU.WAV" Sunet.Command = "Open" End Sub
Ce se ntmpl dac se acioneaz butonul de mouse pe una din cele cinci imagini? Dac musca era moart sau nu era pe ecran (n zona de imagine corespunztoare), deci dac Tag=2 sau Tag=0, atunci nu se ntmpl nimic. Dac, ns musca era pe ecran i era vie (Tag=1), atunci ea trebuie s moar (Tag devine 2), iar imaginea se schimb. n program am presupus c dispunem de dou fiiere cu imagini BMP, unul pentru musca moart (GAZAM.BMP) i unul pentru musca vie (GAZAV.BMP), dup cum urmeaz:
GAZAM.BMP
GAZAV.BMP
Subrutina care omoar o musc continu cu mrirea corespunztoare a punctajului i cu afiarea acestuia. Apoi se produce sunetul respectiv cu apelul comenzii Play i apoi a
125
comenzii Prev, pentru revenire, astfel nct data viitoare s se poat produce din nou acelai sunet.
Sub Image1_Click (Index As Integer) If Image1(Index).Tag = 1 Then Image1(Index).Tag = 2 Image1(Index).Picture = LoadPicture("C:\gazam.bmp") scor = scor + 1 Label1.Caption = Str(scor) Sunet.Command = "Play" Sunet.Command = "Prev" End If End Sub
Dar mutele nu stau pe ecran s atepte s le lovim noi! Ele dispar dup scurgerea intervalului de timp corespunztor fiecreia dintre ele. De fapt, dac nu erau pe ecran, atunci ele apar vii, iar dac erau pe ecran atunci dispar, indiferent dac sunt moarte sau vii. Numai c, atunci cnd sunt vii, trebuie ca punctajul s scad (i s se afieze noua sa valoare) deoarece nseamn c am avut pentru un anumit interval de timp o anumit musc vie pe ecran i nu am reuit s o strivim, deci am ratat. tergerea imaginii mutii se realizeaz prin apelul procedurii LoadPicture, fr parametri sau cu parametrul irul vid.
Sub Timer1_Timer (Index As Integer) If Image1(Index).Tag = 0 Then Image1(Index).Tag = 1 Image1(Index).Picture = LoadPicture("C:\gazav.bmp") Else If Image1(Index).Tag = 1 Then Image1(Index).Tag = 0 Image1(Index).Picture = LoadPicture() scor = scor - 1 Label1.Caption = Str(scor) Else Image1(Index).Tag = 0 Image1(Index).Picture = LoadPicture() End If End If End Sub
Cronometrul Timer2 controleaz i scade timpul de joc, la fiecare secund. Dac timpul de joc s-a scurs n ntregime, atunci se va afia un mesaj corespunztor i, de asemenea, se va cnta un 126
127
Aplicaia 12
Vntoarea de berze
12.1. Prezentare general
Jocul pe care vi-l propunem n acest capitol este foarte asemntor celui din capitolul anterior. Dispunem de o puc cu ajutorul creia trebuie s omorm mai multe berze ce apar n partea superioar a ecranului, n una din trei poziii. Puca se poate deplasa n trei poziii (stnga, centru i dreapta), cu ajutorul tastelor de sgei (stnga i dreapta), iar acionarea focului se face cu una din tastele: spaiu, sgeat sus i sgeat jos.
128
Figura anterioar prezint un moment din acest joc, n care punctajul este egal cu trei, avem dou berze, una vie i una mpucat, iar puca este n poziia dreapta. Forma aplicaiei este definit dup cum urmeaz:
Begin Form Form1 BackColor BorderStyle Caption ClientHeight ClientLeft ClientTop ClientWidth ForeColor Height Left LinkTopic ScaleHeight ScaleWidth Top Width End = = = = = = = = = = = = = = = &H00FFFF00& 3 'Fixed Double "Vanatoarea de berze" 5865 2250 1605 5835 &H00000000& 6270 2190 "Form1" 5865 5835 1260 5955
n cadrul ei avem: o etichet pentru punctajul realizat; un array cu trei imagini (pentru cele trei berze, fiecare putnd avea trei stri); un array cu trei cronometre (pentru timpul de apariie al celor trei berze); o imagine pentru puc (care poate avea trei poziii fr foc i trei cu foc); un cronometru pentru focul putii. Aceste controale sunt puse n eviden n figura de mai jos.
129
Label Label1
Image b (0
2)
Timer timp(0..2)
Timer timp_foc
Image pusca
La nceputul programului nu vom pune nici o barz pe ecran. Acestea vor apare n timpul programului, n mod aleator, n funcie de intervalele de timp stabilite celor trei cronometre asociate lor. Fiecare barz are trei stadii: lips (cu proprietatea Tag=0, iar Picture=LoadPicture()); prezent i vie (Tag=1, imaginea n fiierul barzav.bmp) prezent i mpucat (Tag=2, imaginea n barzam.bmp)
130
Cele dou ipostaze ale berzei (cnd se afl pe ecran) sunt prezentate mai jos:
1. BARZAV.BMP 2. BARZAM.BMP
n subrutina Form_Load avem i iniializarea proprietii Interval a unui cronometru, numit timp_foc. Acesta permite ca pentru un anumit interval de timp s vedem focul din vrful putii (vezi subrutina timp_foc.Timer).
Sub Form_Load () Randomize For i = 0 To 2 barza(i).Picture = LoadPicture() barza(i).Tag = 0 timp(i).Interval = Int(500 + Rnd * 500) Next i pusca.Picture = LoadPicture("p.bmp") pusca.Tag = 1 Punctaj = 0 timp_foc.Interval = 300 End Sub
Puca cu care se va trage va avea 6 poziii, trei fr foc i trei cu foc. n tabelul urmtor sunt prezentate cele ase poziii, mpreun cu numele fiierelor BMP care pstreaz imaginile corespunztoare. Putei realiza singuri aceste imagini cu ajutorul unui editor grafic precum PaintBrush sau Paint.
131
0. PSTG.BMP
1. P.BMP
2. PDR.BMP
3. PFSTG.BMP
4. PF.BMP
5. PFDR.BMP
Observai c valorile peste 2 corespund imaginilor cu foc i, de asemenea, diferena ntre o imagine fr foc i aceeai imagine a putii, dar cu foc este de 3. n cadrul programului se va apela de mai multe ori o subrutin (declarat i definit global), numit AfiseazaPusca. Ea afieaz puca cu care se trage n berze, n partea inferioar a ecranului, n cadrul controlului pusca, apelnd LoadPicture cu numele fiierului corespunztor drept argument.
Sub AfiseazaPusca () Select Case pusca.Tag Case 0: pusca.Picture Case 1: pusca.Picture Case 2: pusca.Picture Case 3: pusca.Picture Case 4: pusca.Picture
132
Puca se deplaseaz spre stnga, spre dreapta sau poate porni un glon la apariia evenimentului KeyDown. Subrutina este mai jos prezentat. Observai c la comanda foc se verific dac barza din dreptul poziiei putii este vie. Dac da, ea se omoar. Punctajul va crete i va fi afiat n Label1. Programul poate fi oprit la acionarea tastei Escape.
Sub Form_KeyDown (KeyCode As Integer, Shift As Integer) If KeyCode = 27 Then End Select Case KeyCode Case 37: If pusca.Tag > 0 Then pusca.Tag = pusca.Tag - 1 AfiseazaPusca End If Case 39: If pusca.Tag < 2 Then pusca.Tag = pusca.Tag + 1 AfiseazaPusca End If Case 40, 32, 38: b = pusca.Tag If b < 3 Then pusca.Tag = b + 3 AfiseazaPusca Beep If barza(b).Tag = 1 Then barza(b).Tag = 2 barza(b).Picture = _ LoadPicture("barzam.bmp") Punctaj = Punctaj + 1 Label1.Caption = Punctaj End If End If End Select End Sub
La scurgerea intervalului de timp dat de fiecare din cele trei cronometre asociate celor trei berze, dac nu era nici o barz n poziia respectiv (dat de valoarea parametrului Index), atunci ea apare pe ecra, dac era i era vie, atunci ea dispare i punctajul scade, iar dac era moart atunci ea dispare de pe ecran. 133
Sub timp_Timer (Index As Integer) If barza(Index).Tag = 0 Then barza(Index).Tag = 1 barza(Index).Picture = LoadPicture("barzav.bmp") Else If barza(Index).Tag = 1 Then barza(Index).Tag = 0 barza(Index).Picture = LoadPicture() Punctaj = Punctaj - 1 Label1.Caption = Punctaj Else barza(Index).Tag = 0 barza(Index).Picture = LoadPicture() End If End If End Sub
134
Aplicaia 13
Tetris
13.1. Prezentare general
Tetris e numele dat unei ntregi clase de jocuri, cu cele mai diferite nume (Tetris, Tetris for Windows, Sextris, TetWin, Hextris, Pentix), jocuri care toate au la baz o idee de provenien ruseasc, jocul clasic de Tetris. Jocul Tetris este foarte cunoscut n ntreaga lume. l ntlnim i pe calculatoare (sub diferite variante) i sub forma unor jocuri electronice de buzunar sau chiar la unele televizoare color. Este un joc solitar, n care dispui de o cutie dreptunghiular din a crei parte superioar ncep s cad, una cte una, diferite piese. Piesele sunt nite tetrominouri, adic piesele care pot fi formate prin alipirea, n toate modurile posibile, a patru ptrele. Alipirea se face doar pe laturi. Exist apte tipuri de piese, dac se consider i oglindirile, nu ns i rotirile. Piesele cad i se depun la baza cutiei dreptunghiulare, lipindu-se de celelalte piese, care se afl deja la baza cutiei. Dac prin aceste lipiri se formeaz una sau mai multe linii pline (din stnga n dreapta), acestea se elimin automat, locul lor fiind luat de cele de deasupra, care coboar. n cderea lor piesele sunt controlate de juctor, prin patru taste: deplasare la stnga (sgeat stnga); deplasare la dreapta (sgeat dreapta); rotire cu 90 de grade (sgeat sus); coborre forat (sgeat jos). Juctorul trebuie s aeze fiecare pies n cutie ct mai bine, n sensul interptrunderii ct mai mult a pieselor de la baza cutiei ntre ele. La umplerea/eliminarea unei linii, numrul de puncte (iniial zero) crete, iar strategia de joc este de a obine ct mai multe puncte, prin pstrarea ct mai goal a cutiei. Jocul se termin cnd nu mai pot ptrunde alte piese n cutie, deoarece aceasta este umplut pn la partea superioar a sa. 135
Jocul se poate termina i forat prin apsarea tastei Escape. Unele variante de jocuri au diferite nivele, viteza de coborre a noilor piese mrindu-se de la un nivel la altul, sau este afiat urmtoarea pies. Sunt i variante n care se folosesc pentominouri (piese formate din cinci ptrate elementare) sau n care la unele nivele cutia dreptunghiular conine deja anumite ptrele. n figura urmtoare este prezentat un moment din acest joc:
Mai jos este dat descrierea formei acestei aplicaii, care cuprinde: un cronometru Timer1 care controleaz micarea piesei care cade; un vector cu 3 forme geometrice Shape1(0..2) care reprezint cele trei margini ale cutiei dreptunghiulare; 136
Este bine s folosii chiar valorile care sunt date mai jos, pentru a obine un joc n care piesele (a cror mrime va fi stabilit prin program) s se potriveasc bine n interiorul cutiei.
VERSION 2.00 Begin Form Form1 BackColor = Caption = ClientHeight = ClientLeft = ClientTop = ClientWidth = Height = Left = LinkTopic = ScaleHeight = ScaleWidth = Top = Width = Begin Timer Timer1 Interval Left Top End Begin Label Label1 Alignment BackColor BorderStyle Caption Height Left TabIndex Top Width End Begin Shape Shape1 BorderStyle FillColor FillStyle Height Index Left Top Width
&H00FFFFFF& "Tetris" 5985 2970 1455 3630 6390 2910 "Form1" 5985 3630 1110 3750 = = = 350 1560 600
= = = = = = = = =
2 'Center &H0000FF00& 1 'Fixed Single "Punctaj: 0" 255 480 0 5520 2535
= = = = = = = =
137
End Begin Shape Shape1 BorderStyle FillColor FillStyle Height Index Left Top Width End Begin Shape Shape1 BorderStyle FillColor FillStyle Height Index Left Top Width End End
= = = = = = = =
= = = = = = = =
Timer1
Shape1
Label
138
Piesa care vine de sus va fi reprezentat printr-un dreptunghi umplut (haurat) care va fi trasat n cadrul formei cu metoda Line.
Piesa curent (figura) (care urmeaz s cad sau chiar cade) este specificat prin variabila fig:
Dim fig As Integer
Piesa respectiv are patru ptrele elementare care o formeaz. Coordonatele acesteia (ca indici n matricea mat) sunt date de urmtoarele opt variabile (xi - abscisele, yi - ordonatele):
Dim x1, y1, x2, y2, x3, y3, x4, y4
n urma deplasrii pe orizontal, sau pe vertical (coborre forat sau prin cdere) sau a rotirii piesei, ea ajunge ntr-o nou poziie, avnd coordonatele:
Dim ax1, ay1, ax2, ay2, ax3, ay3, ax4, ay4
Limea i lungimea unei piese sunt date de variabilele latx, laty. x00 i y00 sunt coordonatele colului stnga sus al cutiei, necesare calculelor pentru afiarea piesei curente. La fiecare moment de timp al cronometrului, acesta trebuie fie s genereze o nou pies (dac cea curent a ajuns la baza cutiei, peste celelalte deja depuse acolo), fie s coboare piesa curent cun un rnd mai jos. Acest lucru este controlat de variabila logic gen_piesa.
Dim gen_piesa, latx, laty, x00, y00
La pornirea aplicaiei, se iniializeaz att matricea asociat cutiei, ct i alte variabile, precum gen_mat sau punctaj.
Sub Form_Load () Init_Mat End Sub
139
Sub Init_Mat () For i = 0 To 19 For j = 0 To 9 mat(i, j) = 0 Next j Next i For i = 0 To 9 mat(20, i) = 1 Next i Randomize gen_piesa = True x00 = 615: y00 = 200 latx = 240: laty = 240 punctaj = 0 End Sub
Desenarea piesei curent se realizeaz de ctre subrutina Df de mai jos. Ea are drept argument o anumit culoare c.
Sub Df (c) Dp x1, Dp x2, Dp x3, Dp x4, End Sub y1, y2, y3, y4, c c c c
Subrutina apeleaz subrutina Dp care deseneaz doar un ptrat. Dac parametrul c este 0, atunci are loc, de fapt, tergerea ptratului respectiv (de indici x,y), adic se deseneaz n alb (ca i fondul cutiei).
Sub Dp (x, y, c) If c = 0 Then cul = RGB(255, 255, 255) Else cul = RGB(100, 100, 50) End If Line (x00+latx*x,y00+laty*y)(x00+latx*x+latx-20,y00+laty*y+laty-20),cul,BF End Sub
Deplasarea la dreapta, la stnga sau n jos (coborre) a piesei curente presupune calculul lui ax1, ax2 etc..
Sub Dreapta () ax1 = x1 + 1: ax2 = x2 + 1: ax3 = x3 + 1: ax4 = x4 + 1
140
ay1 = y1: ay2 = y2: ay3 = y3: ay4 = y4 End Sub Sub Stinga () ax1 = x1 - 1: ax2 = x2 - 1: ax3 = x3 - 1: ax4 = x4 - 1 ay1 = y1: ay2 = y2: ay3 = y3: ay4 = y4 End Sub Sub Jos () ax1 = x1: ax2 = x2: ax3 = x3: ax4 = x4 ay1 = y1 + 1: ay2 = y2 + 1: ay3 = y3 + 1: ay4 = y4 + 1 End Sub
Calcule similare se realizeaz la rotirea unei piese, dar subrutina Rotire de mai jos este foarte complicat, deoarece ine cont att de tipul piesei curente, ct i de poziia curent pe care o are ea. Iat cele 7 tipuri de piese care apar n jocul de TETRIS
Sub Rotire () Select Case fig Case 0 Select Case poz Case 0 ax1 = x2 - 1: ax2 = x2: ax3 = x2 + 1: ax4 = x2 + 2 ay1 = y2: ay2 = y2: ay3 = y2: ay4 = y2 poz = 1 Case 1
141
ax1 = x2: ax2 ay1 = y2 - 1: poz = 0 End Select Case 1 Select Case poz Case 0 ax1 = x3 - 1: ay1 = y3: ay2 poz = 1 Case 1 ax1 = x2: ax2 ay3 = y2 - 2: poz = 2 Case 2 ax3 = x3 - 1: ay3 = y3: ay2 poz = 3 Case 3 ax1 = x2: ax2 ay1 = y2: ay2 poz = 0 End Select Case 2 Select Case poz Case 0 ax1 = x2 - 1: ay1 = y2: ay2 poz = 1 Case 1 ax1 = x1: ax2 ay3 = y1 - 1: poz = 2 Case 2 ax3 = x1: ax4 ay3 = y1: ay2 poz = 3 Case 3 ax1 = x2: ax2 ay1 = y2 - 2: poz = 0 End Select Case 3 Select Case poz Case 0 ax1 = x3: ax2 ay1 = y3: ay2 poz = 1 Case 1 ax1 = x3: ax2
142
ay1 = y3 - 2: ay2 = y3 - 1: ay4 = y3 - 1: ay3 = y3 poz = 2 Case 2 ax1 = x4 - 1: ax2 = x4: ax4 = x4: ax3 = x4 + 1 ay1 = y4: ay2 = y4: ay3 = y4: ay4 = y4 + 1 poz = 3 Case 3 ax1 = x1: ax2 = x1: ax3 = x1: ax4 = x1 + 1 ay1 = y1 - 1: ay2 = y1: ay4 = y1: ay3 = y1 + 1 poz = 0 End Select Case 4 ax1 = x1: ax2 = x1: ax3 = x3: ax4 = x3 ay1 = y1: ay3 = y1: ay2 = y2: ay4 = y2 Case 5 Select Case poz Case 0 ax1 = x2: ax2 = x2: ax3 = x2 + 1: ax4 = x2 + 1 ay4 = y2 - 1: ay2 = y2: ay3 = y2: ay1 = y2 + 1 poz = 1 Case 1 ax1 = x2 - 1: ax2 = x2: ax3 = x2: ax4 = x2 + 1 ay1 = y2: ay2 = y2: ay3 = y2 + 1: ay4 = y2 + 1 poz = 0 End Select Case 6 Select Case poz Case 0 ax1 = x3 - 1: ax2 = x3 - 1: ax3 = x3: ax4 = x3 ay1 = y3 - 1: ay2 = y3: ay3 = y3: ay4 = y3 + 1 poz = 1 Case 1 ax1 = x3 - 1: ax2 = x3: ax3 = x3: ax4 = x3 + 1 ay3 = y3: ay4 = y3: ay1 = y3 + 1: ay2 = y3 + 1 poz = 0 End Select End Select End Sub
Dar cine cheam toate aceste patru subrutine? Firete, juctorul, prin acionarea unei taste. De aceea s-a scris urmtoarea subrutin asociat formei i evenimentului KeyDown:
Sub Form_KeyDown (KeyCode As Integer, Shift As Integer) Select Case KeyCode Case 27 End Case 37
143
Stinga Case 39 Dreapta Case 38 Rotire Case 40 Do Jos If pot_mut() Then Df 0 x1 = ax1: x2 = ax2 x3 = ax3: x4 = ax4 y1 = ay1: y2 = ay2 y3 = ay3: y4 = ay4 Df 1 End If Loop Until Not pot_mut() End Select If pot_mut() Then Df 0 x1 = ax1: x2 = ax2: x3 = ax3: x4 = ax4 y1 = ay1: y2 = ay2: y3 = ay3: y4 = ay4 Df 1 End If End Sub
Subrutina face apel la o funcie boolean, numit pot_mut. Aceast funcie verific dac se poate sau nu muta piesa curent, controlnd-o prin acionarea uneia dintre cele patru taste. Exist mai multe situaii cnd piesa nu se poate muta. Este la una din marginile cutiei sau nu mai poate fi deplasat din cauz c a ajuns jos sau ntre dou piese deja aezate. Aceste lucruri sunt verificate de funcia respectiv:
Function pot_mut () As Integer bol = True If (ax1 < 0) Or (ax2 < 0) Or (ax3 < 0) Or (ax1 > 9) Or (ax2 > 9) Or (ax3 > 9) Or bol = False Else If (mat(ay1, ax1) = 1) Or (mat(ay2, ax2) (mat(ay3, ax3) = 1) Or (mat(ay4, ax4) bol = False End If End If pot_mut = bol End Function
= 1) Or = 1) Then
144
De o importan deosebit n cadrul programului este subrutina care deplaseaz sau genereaz (dup caz) piesa curent, care este apelat de Timer1. Dac gen_piesa este adevrat, atunci se genereaz una din cele apte variante de piese, care apoi se afieaz n partea de sus a cutiei. Acest lucru presupune anumite iniializri pentru x1, y1, ..., x4, y4. Dac locul unde trebuie s apar aceast nou pies este deja ocupat (chiar i parial), nseamn c jocul s-a terminat (acest lucru este controlat de o variabil local cu numele gata). Dac locul este complet liber, atunci piesa se deseneaz prin apelul Df 1. Dac gen_piesa este fals, atunci piesa trebuie s coboare sau s se mute dup comenzile de la tastatur. Dar dup aceasta se verific dac nu cumva s-au realizat linii pline (se folosete, n acest sens, o funcie numit plina). Dac da, acestea sunt eliminate din matrice i de pe ecran (din cutie).
Sub Timer1_Timer () Dim lin As Integer If gen_piesa Then fig = Int(7 * Rnd) poz = 0 If (fig = 1) Or (fig = 2) Or (fig = 5) Or (fig = 6) Then punctaj = punctaj + 2 Else punctaj = punctaj + 1 End If Select Case fig Case 0 x1 = 4: x2 = 4: x3 = 4: x4 = 4 y1 = 0: y2 = 1: y3 = 2: y4 = 3 Case 1 x1 = 4: x2 = 4: x3 = 4: x4 = 5 y1 = 0: y2 = 1: y3 = 2: y4 = 2 Case 2 x1 = 5: x2 = 5: x3 = 5: x4 = 4 y1 = 0: y2 = 1: y3 = 2: y4 = 2 Case 3 x1 = 4: x2 = 4: x3 = 4: x4 = 5 y1 = 0: y2 = 1: y4 = 1: y3 = 2 Case 4 x1 = 4: x2 = 4: x3 = 5: x4 = 5 y1 = 0: y3 = 0: y2 = 1: y4 = 1 Case 5
145
x1 = 4: x2 = 5: x3 = 5: x4 = 6 y1 = 0: y2 = 0: y3 = 1: y4 = 1 Case 6 x1 = 4: x2 = 5: x3 = 5: x4 = 6 y3 = 0: y4 = 0: y1 = 1: y2 = 1 End Select gata = False If (mat(y1, x1) = 1) Or (mat(y2, x2) = 1) Or (mat(y3, x3) = 1) Or (mat(y4, x4) = 1) Then gata = True End If If gata Then MsgBox "Joc Terminat": End Df 1 End If Jos If pot_mut() Then Df 0 x1 = ax1: x2 = ax2: x3 = ax3: x4 = ax4 y1 = ay1: y2 = ay2: y3 = ay3: y4 = ay4 Df 1 gen_piesa = False Else mat(y1, x1) = 1: mat(y2, x2) = 1: mat(y3, x3) = 1 mat(y4, x4) = 1 lin = 19 Do While lin > 2 If plina(lin) Then Beep punctaj = punctaj + 10 For i = lin To 1 Step -1 For j = 0 To 9 mat(i, j) = mat(i - 1, j) If mat(i, j) = 1 Then Dp j, i, 1 Else Dp j, i, 0 End If Next j Next i For i = 0 To 9 mat(0, i) = 0 Next i Else lin = lin - 1 End If Loop gen_piesa = True End If Label1.Caption = "Punctaj: " & punctaj End Sub
146
Funcia boolean care testeaz dac o anumit linie este sau nu umplut este urmtoarea:
Function plina (linia As Integer) As Integer bol = True i = 0 Do While (i < 10) And (bol) If mat(linia, i) = 0 Then bol = False Else i = i + 1 End If Loop plina = bol End Function
147
Aplicaia 14
Puzzle cu numere
14.1. Prezentarea general a problemei
Este vorba despre binecunoscutul joc solitar de Puzzle (comercializat n Romnia mai demult sub numele de Perspico). Pe o tabl cu nn spaii ptrate se afl aezate n2-1 piese ptrate care se pot muta prin culisare n spaiul liber. Se mut doar una din piesele alturate spaiului liber. La nceput se amestec piesel, apoi trebuie refcut poziia lor iniial. De obicei se pleac de la o poziie n care piesele (care sunt numerotate de la 1 la n2-1) sunt aezate n ordine, pe linii, de la stnga la dreapta. Iat un exemplu din jocul cu n=4 pe care l vom realiza n cadrul acestui capitol:
Este vorba despre o form fix cuprinznd n ea un vector cu 16 butoane, ale cror poziii nu conteaz la nceput. Caracteristicile acestora sunt date n figura urmtoare:
Begin CommandButton Command1(0..15) Height = 375 Index = 0..15 Left = 0 TabIndex = 0..15 Top = 0 Visible = 0 'False Width = 375 End
148
Semnificaiile lor sunt: n este numrul de ptrate de pe fiecare latur a tablei de joc; asfel, vom avea n2 ptrate ale tablei, cu n2-1 piese, numerotate ncepnd cu 1; dar piesa 1 corespunde, de fapt, lui Command1(0) .a.m.d., piesa n2-1 corespunznd lui Command1(n2-2); x reprezint numrul de ordine al csuei libere (la nceput are valoarea n2-1); i i j sunt coordonatele acestei csue; m este numrul de mutri efectuat de juctor. La nceput se iniializeaz valorile lui n, m, x, i, j, precum i valorile Command1(i).Caption, pentru toate butoanele (care reprezint piesele i spaiul liber):
Sub Form_Load () n = 4 l = Command1(1).Width + 15 For i = 0 To n - 1 For j = 0 To n - 1 Command1(n * j + i).Left = 100 + l * i Command1(n * j + i).Top = 100 + l * j Command1(n * j + i).Caption = n * j + i + 1 Command1(n * j + i).Visible = True Next j Next i Command1(n * n - 1).Caption = "" spatiul liber i = n: j = n: x = n * n - 1 m = 0 End Sub
Jocul se realizeaz prin acionarea pieselor. Acionarea unei piese care nu este vecin cu spaiul liber nu are nici un efect. Dar, dac se acioneaz pe o pies vecin cu spaiul liber, atunci se realizeaz un schimb ntre cele dou butoane (cel al piesei acionate i cel al spaiului liber). Acest lucru se face, de fapt, prin interschimbarea valorilor proprietilor Caption. Se memoreaz noul x i noile coordonate i i j. 149
De asemenea, crete numrul m de mutri efectuate, iar dac se ajunge n configuraia iniial, atunci se afieaz un mesaj corespunztor i jocul se ncheie. Toate acestea sunt realizate de subrutina Command1_Click de mai jos, n care Index este indicele butonului care se mut:
Sub Command1_Click (Index As Integer) i1 = Index \ n + 1: j1 = Index Mod n + 1 If ((i = i1) And (Abs(j - j1) = 1)) Or ((j = j1) And (Abs(i - i1) = 1)) Then Command1(x).Caption = Command1(Index).Caption Command1(Index).Caption = "" x = Index i = x \ n + 1: j = x Mod n + 1 ok = True For k = 0 To n * n - 1 If Command1(k).Caption <> "" Then If Command1(k).Caption - k <> 1 Then ok = False End If Next k m = m + 1 If ok Then MsgBox "Felicitari !" + Chr$(13) + Chr$(10) + "Ai rezolvat problema in " & m & " mutari !", 48, "Joc terminat" End End If End If End Sub
150
Aplicaia 15
Puzzle cu imagini
151
Forma are dimensiune fix i marginea dubl, aa dup cum se prezint n figura urmtoare. n interiorul ei avem urmtoarele controale: un buton de Start/Stop:
Begin CommandButton Command1 Caption = "Start" Height = 300 Left = 4200 TabIndex = 2 Top = 120 Width = 975 End
152
o zon de imagine, unde se vor ncrca fragmente din imaginea din fiierul BMP:
Begin Image Image1 Height = Index = Left = Top = Width = End 495 0 240 600 495
Controlul Image1 este, de fapt, o matrice, de aceea am i pus n eviden proprietatea Index (setat deocamdat la 0). Fiecare component din cele n2 ale acestui vector va fi ncrcat i redimensionat n cadrul programului, n funcie de n i Index-ul ei. un control de tip PictureClip, care reprezint matricea de decupare a imaginii din fiier; fragmentele din PicClip1 vor fi puse n vectorul Image1:
Begin PictureClip PicClip1 Location = "375,375,120,3600" End
Urmtoarea figur pune n eviden toate aceste elemente de interfa ale programului.
153
Image1
PicClip1
Begin Form Form1 BorderStyle Double Caption ClientHeight ClientLeft ClientTop ClientWidth Height Left LinkTopic MaxButton MinButton ScaleHeight ScaleMode ScaleWidth Top
= = = = = = = = = = = = = = =
'Fixed
"Form1" 5520 2325 1770 5400 6210 2265 "Form1" 0 'False 0 'False 5000 0 'User 5500 1140
154
Stabilirea parametrilor programului (numrul de linii/coloane i numele fiierului cu imagine) se face prin subrutinele:
Sub Text1_GotFocus () For i = n * n - 1 To 1 Step -1 Unload Image1(i) Next i Image1(0).Visible = False Command1.Caption = "Start" End Sub Sub Text1_KeyPress (KeyAscii As Integer) If KeyAscii = 13 Then Text2.SetFocus End Sub Sub Text2_KeyPress (KeyAscii As Integer) If KeyAscii = 13 Then Command1.SetFocus End Sub
Acionarea butonului Start/Stop schimb rolul acestuia i, dac este Start devine Stop i face ncrcarea dinamic a pieselor care compun imaginea din fiier. Apoi aceast subrutin amestec singur imaginile, prin apelarea subrutinii de mutare a pieselor care este Image1_Click. n subrutina Command1_Click se face i setarea lui PicClip1 ca avnd n linii i n coloane i cu imaginea ncrcat din fiierul dat de Text1.Text. Dac nu se precizeaz un nume de fiier corect, atunci n execuia subrutinii se genereaz eroare i se afieaz un mesaj corespunztor (eroarea este inut sub control prin program):
Sub Command1_Click () If Command1.Caption = "Stop" Then End End If Command1.Caption = "Stop" On Error GoTo sf PicClip1.Picture = LoadPicture(Text1.Text + ".bmp") n = Val(Text2.Text) PicClip1.Cols = n PicClip1.Rows = n L = Form1.ScaleWidth \ n Image1(0).Width = Form1.ScaleWidth \ n Image1(0).Picture = PicClip1.GraphicCell(0) For i = 1 To n * n - 1 Load Image1(i) Next i For i = 0 To n - 1
155
For j = 0 To n - 1 Image1(n * i + j).Picture=PicClip1.GraphicCell(n*i+j) Image1(n * i + j).Top = L * i + 500 Image1(n * i + j).Left = L * j Image1(n * i + j).Width = L Image1(n * i + j).Height = L Image1(n * i + j).Visible = True Image1(n * i + j).Stretch = True Image1(n * i + j).Tag = n * i + j Next j Next i Image1(n * n - 1).Picture = LoadPicture() x = n * n - 1 ii = n: jj = n ' amestecare imagini Image1_Click (n * n - 2) Image1_Click (n * n - 3) m = 0 Randomize For i = 1 To 5000 mm = m k = Int(Rnd * (n * n)) Image1_Click (k) m = mm Next i Exit Sub sf: MsgBox "Imagine negasita!", 48, "Eroare" End End Sub
Jocul propriu-zis se desfoar prin click-uri de mouse fcute peste componentele vectorului Image1. Lucrurile se desfoar precum i n aplicaia din capitolul precedent, ns aici n loc de proprietatea Caption vom lucra simultan cu proprietile Tag (care pstreaz numerele) i Picture (care pstreaz fragmentele de imagine luate din matricea PicClip1). Utilizarea proprietii Tag a fost necesar pentru a verifica dac s-a ajuns sau nu la configuraia corect, cea care corespunde i imaginii mari restaurate.
Sub Image1_Click (Index As Integer) i1 = Index \ n + 1: j1 = Index Mod n + 1 If ((ii = i1) And (Abs(jj - j1) = 1)) Or ((jj = j1) And (Abs(ii - i1) = 1)) Then Image1(x).Picture = Image1(Index).Picture Image1(Index).Picture = LoadPicture()
156
aux = Image1(x).Tag Image1(x).Tag = Image1(Index).Tag Image1(Index).Tag = aux x = Index ii = x \ n + 1: jj = x Mod n + 1 ok = True For i = 0 To n * n - 1 If " " + Image1(i).Tag <> Str(i) Then ok = False Next i m = m + 1 If ok Then MsgBox "Felicitari !" + Chr$(13) + Chr$(10) + "Ai rezolvat problema in " & m & " mutari !", 48, "Joc terminat" Command1_Click End If End If End Sub
157
Aplicaia 16
Bila
De asemenea, vom avea i trei controale de tip Spin, cu ajutorul crora vom regla viteza de deplasare a bilei, pasul acestuia, precum i pasul de deplasare al paletei.
158
O descriere sumar, dar relevant a interfeei aplicaiei propuse este dat mai jos.
VERSION 2.00 Begin Form Form1 BackColor = &H00C0FFC0& BorderStyle = 3 'Fixed Double Caption = "Bila" FillColor = &H00FFFFFF& Begin SpinButton Spin3 Delay = 100 Height = 255 Left = 2760 Top = 3600 Width = 135 End Begin SpinButton Spin2 Delay = 100 End Begin SpinButton Spin1 Delay = 100 End Begin Timer Timer1 Interval = 1 Left = 2280 End Begin Label Label1 Alignment = 2 'Center BackColor = &H0000FFFF& Caption = "Label1" End Begin Shape Paleta FillColor = &H00FF0000& FillStyle = 0 'Solid Height = 135 Width = 735 End Begin Shape Bila FillColor = &H000080FF& FillStyle = 0 'Solid Height = 255 Shape = 3 'Circle Width = 255 End End
159
Begin Shape Bila FillColor = &H000080FF& FillStyle = 0 'Solid Height = 255 Shape = 3 'Circle Width = 255 End
Timer
Spiner Spiner
Label
Deplasarea bilei are la baz urmtoarea idee: bila se afl ntr-un anumit punct , captul ei stnga-sus fiind de coordonate (Bila.Left, Bila.Top). Noile valori pentru aceste atribute vor fi (Bila.Left+pasb*dx, Bila.Top+pasb*dy). Am notat prin dx i dy dou variabile globale, care vor lua doar valorile -1 i +1. Combinaia dx=1, dy=-1, corespunde, de exemplu, unei creteri pe axa OX, simultan cu o descretere pe axa OY. Deci bila se va deplasa spre dreapta sus (pe diagonal). Pasul de deplasare al bilei pe direcia stabilit este pasb. Cnd ajunge la una din margini bila i schimb una dintre valorile dx sau dy, n sensul schimbrii semnului. n testarea marginilor de jos i dreapta trebuie s se in cont i de dimensiunile bilei (Bila.Height, Bila.Width). De asemenea, atunci cnd bila ajunge la palet (venind de sus), se schimb dy, iar punctajul crete cu 1. Dac bila ajunge pe 160
marginea de jos a formei, punctajul scade cu 1. Punctajul se afieaz n Label1. Toate acestea se desfoar conform subrutinei urmtoare:
Sub Timer1_Timer () If (Bila.Left <= 0) Or (Bila.Left + Bila.Width >= Form1.ScaleWidth) Then dx = -dx End If If (Bila.Top <= 0) Or (Bila.Top + Bila.Height >= Form1.ScaleHeight) Then dy = -dy If dy = -1 Then punctaj = punctaj - 1 Label1.Caption = punctaj End If End If mij = Bila.Left + Bila.Width \ 2 If (dy = 1) And (Paleta.Left <= mij) And (mij <= Paleta.Left + Paleta.Width) And (Bila.Top + Bila.Height >= Paleta.Top) Then dy = -dy Beep punctaj = punctaj + 1 Label1.Caption = punctaj End If Bila.Left = Bila.Left + pasb * dx Bila.Top = Bila.Top + pasb * dy End Sub
Micarea paletei se face conform subrutinei urmtoare. Se folosesc tastele de sgei stnga i dreapta. Deplasarea se face cu pasul pasp.
Sub Form_KeyDown (KeyCode As Integer, Shift As Integer) Select Case KeyCode Case 37 If Paleta.Left > 0 Then Paleta.Left = Paleta.Left - pasp End If Case 39 If Paleta.Left + Paleta.Width < Form1.ScaleWidth Then Paleta.Left = Paleta.Left + pasp End If End Select End Sub
161
Urmtoarele ase subrutine se refer la cele trei controale de tip Spin. Astfel, Spin1 controleaz timpul de deplasare al bilei, Spin2 - pasul de deplasare al bilei, iar Spin3 - pasul de micare stnga-dreapta al paletei. Viteza de micare a bilei este dat de valorile lui Spin1 i Spin2.
Sub Spin1_SpinDown () If Timer1.Interval > 2 Then Timer1.Interval = Timer1.Interval - 1 End If End Sub Sub Spin1_SpinUp () Timer1.Interval = Timer1.Interval + 1 End Sub Sub Spin2_SpinDown () pasb = pasb - 1 End Sub Sub Spin2_SpinUp () pasb = pasb + 1 End Sub Sub Spin3_SpinDown () pasp = pasp - 1 End Sub Sub Spin3_SpinUp () pasp = pasp + 10 End Sub
162
Aplicaia 17
Test gril
163
164
Font3D = 0 'None Height = 1575 Left = 120 TabIndex = 5 Top = 120 Width = 5535 Begin TextBox Intreb Enabled = 0 'False Height = 1095 Left = 240 MultiLine = -1 'True ScrollBars = 3 'Both TabIndex = 8 Top = 360 Width = 5175 End End Begin SSFrame Raspunsuri Caption = "Alegeti raspunsul" Font3D = 0 'None Height = 2775 Left = 120 TabIndex = 0 Top = 2280 Width = 8295 Begin SSCheck Raspuns Font3D = 0 'None Height = 375 Index = 1 Left = 360 TabIndex = 6 Top = 840 Width = 7935 End Begin SSCheck Raspuns Font3D = 0 'None Height = 375 Index = 4 Left = 360 TabIndex = 4 Top = 2280 Width = 7935 End Begin SSCheck Raspuns Font3D = 0 'None Height = 375 Index = 3 Left = 360 TabIndex = 3 Top = 1800
165
Width = 7935 End Begin SSCheck Raspuns Font3D = 0 'None Height = 375 Index = 2 Left = 360 TabIndex = 2 Top = 1320 Width = 7935 End Begin SSCheck Raspuns Font3D = 0 'None Height = 375 Index = 0 Left = 360 TabIndex = 1 Top = 360 Width = 7935 End End Begin Image Imagine Height = 1935 Left = 5760 Picture = INTREB.FRX:0000 Stretch = -1 'True Top = 120 Width = 2655 End End
Toate aceste elemente descrise n textul anterior se regsesc n figura urmtoare, unde este prezentat forma aplicaiei i controalele ei. Unele controale nu se folosesc n program, ele fiind puse doar pentru design.
166
SSFrame Intrebare
TextBox Intreb
Begin CommandButton Buton (iniial Caption="Start", apoi el devine "Next") SSFrame SSCheck Image Imagine
167
i o pasare i o floare
Acesta este fiierul TEST.TST care conine dou ntrebri: Din ce oras este imaginea alaturata? i Ce se vede in imaginea alaturata?, fiecare fiind urmat de numele unui fiier grafic, care conine o imagine de tip bitmap, astfel:
BACAU.BMP
ELEFANT.BMP
Dup numele fiierelor grafice urmeaz cele cinci rspunsuri posibile, fiecare fiind urmat de o liter care poate fi i sau c. Dac este i, atunci nseamn c rspunsul nu este corect, iar dac este c acesta este corect. n program, ntrebarea curent va fi citit n irul quest$, iar cele cinci rspunsuri n vectorul rasp. Faptul c al i-lea rspuns este sau nu corect se va indica prin poziionarea pe True, respectiv False, a componentei cor(i) din vectorul cor.
Dim rasp(5), cor(5), quest$, punctaj As Integer
La nceput, se deschide fiierul TEST.TST, din care se vor citi, ulterior, ntrebrile i rspunsurile. Acest fiier se nchide abia la sfritul programului. De asemenea, am fcut nite iniializri pentru vectorii rasp i cor, cu rspunsuri aiurea i valori i (incorecte). Se folosete o variabil punctaj, iniial 0, care contorizeaz numrul de puncte obinute de persoana examinat.
Sub Form_Load () For i = 1 To 5 rasp(i) = "aaaa"
168
cor(i) = "i" Next i Open "test.tst" For Input As #1 punctaj = 0 End Sub
Principala aciune a programului se realizeaz pe baza evenimentului click asupra butonului de comand Buton. Iniial acesta are Caption=Start, dar imediat dup prima apsare el va avea Caption=Next. Se pleac de la premisa c toate rspunsurile corecte sunt alese ca fiind corecte, adic sunt marcate (bifate), iar toate rspunsurile incorecte sunt alese ca fiind incorecte. Dar la proba contrarie, variabila corect (iniial True) se schimb n False. Apoi au loc citiri pentru tura viitoare: ntrebarea: quest$. fiierul grafic coninnd imaginea: nume_desen$. Dac nu este nevoie de o imagine, se poate pune irul vid, ceea ce va nsemna c nu va fi afiat nici o imagine, sau se poate opta pentru un fiier general, ca de exemplu: componentele vectorilor cor i raspuns, Apoi, au loc actualizrile pentru raspuns(i).Caption (care reprezint textele rspunsurile ce vor fi afiate pe ecran) i respectiv raspuns(i).Value (componentele lui raspuns primesc valoarea False, adic iniial nu sunt marcate).
Sub Buton_Click () Buton.Caption = "Next" Dim i As Integer, s$, nume_desen$ corect = True For i = 0 To 4 If (cor(i + 1) = "c" And raspuns(i).Value = False) Or (cor(i + 1) = "i" And raspuns(i).Value = True) Then corect = False Next i If corect Then punctaj = punctaj + 1 s$ = Str(punctaj - 1) If EOF(1) Then MsgBox "Puncte obtinute: " & NL & s$, 0, "Rezultat" End End If Input #1, quest$ intreb.Text = quest$ Input #1, nume_desen$
169
Imagine.Picture = LoadPicture(nume_desen$) For i = 0 To 4 Input #1, cor(i + 1) Input #1, rasp(i + 1) raspuns(i).Caption = rasp(i + 1) raspuns(i).Value = False Next i End Sub
n subrutina anterioar, dac se ajunge la sfritul fiierului, atunci se afieaz punctajul, iar fiierul se nchide la terminarea programului, care va apela subrutina urmtoare:
Sub Form_Unload (Cancel As Integer) Close #1 End Sub
170
Aplicaia 18
Astfel, o serie de triplete de semne de circulaie vor aprea, pe rnd, n partea din stnga a ferestrei. n partea din dreapta sunt tot attea csue libere, care trebuie completate cu semnele de circulaie corespunztoare. Corespondena se realizeaz n funcie 171
de cele trei texte aferente, fiecare din ele corespunznd exact unuia dintre cele trei semne de circulaie din stnga. Semnele rutiere din partea stng se deplaseaz n partea dreapta prin agare i mutare cu mouse-ul (operaie numit drag and drop). Trecerea la un nou triplet de semne rutiere se face cu butonul Apasa! care se activeaz doar dup ce cele trei semne au fost amplasate n csuele din dreapta. Dac cele trei semne sunt amplasate corect, punctajul (iniial zero) crete cu o unitate. Observaie: aplicaia poate fi modificat de dumneavoastr n funcie de necesiti. Se pot concepe astfel o serie de programe educaionale, nu neaprat pe tema circulaiei rutiere. O descriere minuioas a caracteristicilor formei aplicaiei i a controalelor pe care aceasta le include este dat mai jos:
Begin Form Form1 BackColor = &H00C0C0FF& BorderStyle = 3 'Fixed Double Caption = "Circulatia rutiera" ClientHeight = 5520 ClientLeft = 2115 ClientTop = 1650 ClientWidth = 7080 Height = 5925 Left = 2055 LinkTopic = "Form1" MaxButton = 0 'False ScaleHeight = 5520 ScaleWidth = 7080 Top = 1305 Width = 7200 Begin CommandButton btnStop Caption = "Stop" Height = 375 Left = 1800 TabIndex = 5 Top = 2520 Width = 735 End Begin CommandButton buton Caption = "Apasa!" Height = 375 Left = 1800 TabIndex = 0 Top = 4320
172
Width = End Begin Label punctaj Alignment = BorderStyle = Caption = Height = Left = TabIndex = Top = Width = End Begin Label raspuns BorderStyle = Height = Index = Left = TabIndex = Top = Width = End Begin Label raspuns BorderStyle = Height = Index = Left = TabIndex = Top = Width = End Begin Label raspuns BorderStyle = Height = Index = Left = TabIndex = Top = Width = End Begin Image casuta BorderStyle = Height = Index = Left = Stretch = Top = Width = End Begin Image casuta BorderStyle =
735
'Fixed Single
173
Height Index Left Stretch Top Width End Begin Image casuta BorderStyle Height Index Left Stretch Top Width End Begin Image semn Height Index Left Stretch Top Width End Begin Image semn Height Index Left Stretch Top Width End Begin Image semn Height Index Left Stretch Top Width End End
= = = = = =
= = = = = = =
= = = = = =
= = = = = =
= = = = = =
174
Label punctaj
Image (0
CommandButton b t
GRNLIGHT.ICO Apoi este nevoie de un fiier text, s zicem AUTO.TXT structurat astfel:
nume-de-fiier-imagine-semn-de-circulaie-1 (pe un singur rnd) explicaie-asociat-semnului-de-circulaie-1 (eventual pe mai multe rnduri) # nume-de-fiier-imagine-semn-de-circulaie-2 (pe un singur rnd) explicaie-asociat-semnului-de-circulaie-2 (eventual pe mai multe rnduri) #
175
nume-de-fiier-imagine-semn-de-circulaie-3 (pe un singur rnd) explicaie-asociat-semnului-de-circulaie-3 (eventual pe mai multe rnduri) # ....................... (alte triplete asemntoare).
Deoarece sunt menionate, trebuie s existe fiierele grafice (de tip BMP) care s conin imagini ale semnelor de circulaie. De exemplu, putem avea nite imagini ca n cazul pe care l-am menionat:
SO7.BMP
SU13.BMP
ES22.BMP
SI4.BMP 176
SI6.BMP
SU30.BMP
S le explicm: ordine este un vector care memoreaz ordinea n care vor trebui aezate n csuele din dreapta semnele de circulaie din stnga (ne referim la momentul execuiei aplicaiei); perm este un tablou care conine toate cele ase permutri ale mulimii {1,2,3}, n funcie de care se stabilete (pe cale aleatoare) i coninutul curent al vectorului ordine; s$ este un ir de lucru, care va reprezenta textul explicaiei asociate unui anumit semn de circulaie; valoarea lui s$ va fi pasat unei dintre etichetele vectorului raspuns (vezi descrierea de la paragraful anterior) p este punctajul realizat de juctor; nr este numrul de ntrebri la care nu a dat nc rspuns (pentru un anumit triplet de semne rutiere); la fiecare plasare a unui semn ntr-o csu din stnga, variabila nr scade cu o unitate, iar cnd devine zero, se verific dac aranjarea semnelor este corect i n caz afirmativ, p crete cu o unitate; cale este calea ctre locul unde se gsesc fiierele programului i va trebui s o adaptai necesitilor dumneavoastr concrete; avei ns grij ca s punei toate fiierele acestei aplicaii ntr-un singur director.
Programul ncepe prin a face anumite iniializri, prin subrutina Form_Load; se iniializeaz tabloul cu permutrile i se stabilesc anumite valori pentru proprietile Tag asociate imaginilor care vor cuprinde semnele de circulaie. De asemenea, se deschide fiierul cu ntrebri i se stabilete iconia pentru operaia drag and drop a elementelor semn(i), cu i ntre 0 i 2.
Sub Form_Load () cale = "C:\DISC1\CARTI\CARTEA7\AUTO\" p = 0
177
For i = 0 To 2 semn(i).DragMode = 0 semn(i).DragIcon = LoadPicture(cale semn(i).Tag = -1 Next i Open cale + "AUTO.TXT" For Input As #1 perm(1, 1) = 1: perm(1, 2) = 2: perm(1, perm(2, 1) = 1: perm(2, 2) = 3: perm(2, perm(3, 1) = 2: perm(3, 2) = 1: perm(3, perm(4, 1) = 2: perm(4, 2) = 3: perm(4, perm(5, 1) = 3: perm(5, 2) = 1: perm(5, perm(6, 1) = 3: perm(6, 2) = 2: perm(6, End Sub
+ "grnlight.ico")
3) 3) 3) 3) 3) 3)
= = = = = =
3 2 3 1 2 1
Programul propriu-zis este realizat de dou subrutine: buton_Click() i casuta_DragDrop(...): S explicm paii celor dou subrutine. n prima din ele, se verific mai nti dac nu s-a ajuns la sfritul fiierului, adic dac nu s-au terminat cumva ntrebrile. Dac da, atunci se afieaz punctajul realizat i se oprete programul. Dac nu, butonul devine inaccesibil (tocmai pentru a nu mai putea fi accesat dect dup terminarea amplasrii celor trei semne de circulaie n csuele din dreapta). Apoi se genereaz un numr ntre 1 i 6, valoare ce se atribuie lui k. n funcie de k se alege o permutare i aceasta determin ordinea n care vor trebui s apar explicaiile (vectorul de etichete raspuns), deci i ordinea care d soluia corect a acestui set de ntrebri. n continuare se citesc, de trei ori: numele fiierului (fr extensie) care conine imaginea semnului de circulaie; rndurile care compun explicaia asociat lui (pn la ntlnirea unui semn #); se fac atribuirile: semn(i).Tag = i i semn(i).DragMode = 1; prima are rolul de a nsoi fiecare imagine de poziia de unde pleac (pentru verificarea corectitudinii aezrii ei), iar a doua d libertate de micare acestei imagini. La sfrit, se completeaz elementele vectorului cu explicaii: raspuns. De asemenea, se stabilete valoarea lui nr la 3.
Sub buton_Click () If EOF(1) Then Close #1
178
MsgBox "Ai obtinut " & p & " puncte.", 48, "Oprire" End End If buton.Enabled = False k = Int(1 + Rnd * 6) For i = 0 To 2 ordine(i) = perm(k, i + 1) - 1 casuta(i).Picture = LoadPicture() Input #1, fis_imag$ semn(i).Picture = LoadPicture(cale + fis_imag$ + ".bmp") semn(i).Tag = i semn(i).DragMode = 1 s$(i) = "" Do Input #1, un_text If un_text <> "#" Then s$(i) = s$(i) + " " + un_text Loop Until un_text = "#" Next i For i = 0 To 2 raspuns(i).Caption = s$(ordine(i)) Next i nr = 3 End Sub
179
Ea verific dac se ncearc aezarea unui semn de circulaie neaezat nc. Dac aa stau lucrurile, acesta se aaz n csua respectiv i se verific dac este ultimul (nr a devenit, dup aezare, zero). Dac da, atunci se va calcula i afia noul punctaj, n funcie de corectitudinea acestei aezri. Apoi butonul de comand buton devine activ i focalizat. Urmtoarea figur prezint un moment din desfurarea programului. Punctajul curent este 3, este doar un semn aezat (n locul corect), iar unul din semne tocmai este deplasat din stnga n dreapta. Semaforul este iconia care reprezint cursorul de mouse.
semnul de circulaie care se deplaseaz prin drag and drop din stnga n dreapta
Butonul Stop (care poate fi acionat oricnd) oprete execuia programului: 180
Cnd forma se nchide (accidental sau datorit terminrii forate sau normale a programului), atunci i fiierul AUTO.TXT trebuie nchis:
Sub Form_Unload (Cancel As Integer) Close #1 End Sub
181
Aplicaia 19
Bioritm
19.1. Prezentare general
Problema trasrii grafice a celor trei curbe corespunztoare evoluiei fizice, psihice i intelectuale a unei persoane este studiat i rezolvat n lucrarea mea, Aplicaii n C i C++ (vezi bibliografia). Este vorba despre ceea ce specialitii numes bioritm. Noi ne propunem s realizm o aplicaie cu mai multe ferestre, ca n figura urmtoare:
De fapt, este o aplicaie de tip MDI (Multi Document Interface). Se pot citi datele despre mai multe persoane i n fereastre separate se reprezint grafic bioritmul persoanelor respective, aa cum se vede n figura de mai sus. Se pot citi date despre persoane noi sau se pot lua date despre persoane deja existente (aplicaia pe care o vom prezenta nu
182
rezolv aceast a doua problem, pe care v-o propunem drept exerciiu). Ferestrele se pot minimiza, transformndu-se n iconie. Ele se pot aranja sub forma unor crmizi sau n cascad. Fiierul proiect MDIBIO.MAK va arta astfel:
Observai c el cuprinde formele: frmBioritm - pentru trasarea unui anumit bioritm (se va genera dinamic un tablou de astfel de forme, numit Persoana); frmDate - pentru citirea datelor pentru un anumit bioritm; frmMDI - printele formelor de tip frmBioritm,
i modulul MDIBIO.BAS care cuprinde funcii i subrutine de interes general, precum i declaraii de variabile globale. S le prezentm i s le analizm pe rnd: fiierul MDI.FRM Acest fiier conine forma (i codul aferent) pe care o prezentm n figura urmtoare:
183
Begin MDIForm frmMDI Caption = "Bioritm" LinkTopic = "MDIForm1" Begin Menu mnuFile Caption = "&Persoane" Begin Menu mnuFNew Caption = "&Noua" End Begin Menu mnuFOpen Caption = "&Veche" End Begin Menu mnuFExit Caption = "E&xit" End End
Spre deosebire de toate formele cu care am lucrat n aplicaile anterior prezentate n aceast carte, aceast form este o form de tip MDI (Atenie! Se creeaz din meniul File, cu opiunea New MDI Form!). Nu cuprinde dect un meniu, care va intra n joc atunci cnd toate ferestrele (forme obinuite, de tip frmBioritm) vor fi nchise. fiierul FRMDATE.FRM Acest fiier conine o form (i codul ei) obinuit, care este caracterizat foarte bine de figura de mai jos:
184
.... End
fiierul BIORITM.FRM Avem de a face acum cu principala form a aplicaiei, n care se traseaz efectiv un triplet de curbe bioritmice. Descrierea sa este dat mai jos:
Begin Form frmBioritm AutoRedraw = -1 'True Caption = ":1" LinkTopic = "Form1" MDIChild = -1 'True Visible = 0 'False Begin PictureBox Picture1 AutoRedraw = -1 'True End Begin Label Label3 BorderStyle = 1 'Fixed Single Caption = "rosu=fizic, verde=psihic, albastru=intelect" End Begin Label Label2 BackColor = &H00C0C0FF& Height = 255 Left = 120 Top = 480 Width = 3015 End Begin Label Label1 BackColor = &H00C0C0FF& Height = 255 Left = 120 TabIndex = 0 Top = 120 Width = 3015 End Begin Menu mnuFile Caption = "&Persoane" Begin Menu mnuFNew Caption = "&Noua" End Begin Menu mnuFOpem Caption = "&Veche" End Begin Menu mnuFClose Caption = "&Sterge" End
185
Begin Menu mnuFSep Caption = "-" End Begin Menu mnuFExit Caption = "E&xit" End End Begin Menu mnuWindow Caption = "&Ferestre" WindowList = -1 'True Begin Menu mnuWCascade Caption = "&Cascada" End Begin Menu mnuWTile Caption = "Carami&zi" End Begin Menu mnuWArrange Caption = "&Aranjeaza iconite" End End End
Atenie mare la toate proprietile evideniante n descrierea anterioar. Aceast descriere se regsete sub forma grafic din figura alturat:
Label1 Label2
Label3
Picture
186
Avem: nume_pers este numele persoanei curente, care va fi afiat n antetul (proprietatea Caption) ferestrei copil de tip frmBioritm, cnd se va reprezenta un nou bioritm; data_nast este data de natere a persoanei pentru care se reprezint bioritmul (este de forma zz.ll.aaaa, deci zilele i lunile vor ocupa exact dou cifre, folosindu-se eventual zerouri n fa; de exemplu, luna ianuarie se scrie sub forma 01); nu uitai s scriei punctele despritoare (sau orice alt simbol); data_ref este prima din cele 31 de zile pentru care se reprezint curbele bioritmice (este n format zz.ll.aaaa); n plus: prima_data este o variabil boolean special, care indic dac se reprezint primul bioritm sau nu; este utilizat pentru a indica crearea (iniial) a celor doi vectori: Sters() i Persoana() Persoana() - cuprinde ferestre de tip frmBioritm, generate dinamic sau pe baza altora mai vechi, care au fost nchise (cnd se nchide o fereastr i, acest lucru se marcheaz prin Sters(i)=True) 187
Sters() - Sters(i) are valoarea adevrat dac i numai dac cea de a i-a ferestr este nchis
Pe baza vectorului Sters se poate verifica dac mai exist ferestre copil (de tip frmBioritm) sau nu n fereastra MDI:
Function MaiSuntFerestre () As Integer Dim i As Integer For i = 1 To UBound(Persoana) If Not Sters(i) Then MaiSuntFerestre = True Exit Function End If Next i MaiSuntFerestre = False End Function
Tot pe baza vectorului Sters se poate depista primul indice disponibil pentru un nou bioritm. Dac nu se gsete printre ferestrele mai vechi care au fost ntre timp nchise, atunci se creeaz o nou fereastr, prin:
ReDim Preserve Persoana(nr + 1) ReDim Preserve Sters(nr + 1)
Observai c acest lucru se realizeaz cu pstrarea informaiilor deja existente, adic a ferestrelor anterioare (i a strilor lor).
Function PrimulIndiceDisponibil () As Integer Dim i As Integer Dim nr As Integer nr = UBound(Persoana) For i = 1 To nr If Sters(i) Then PrimulIndiceDisponibil = i Sters(i) = False Exit Function End If Next i ReDim Preserve Persoana(nr + 1) ReDim Preserve Sters(nr + 1) PrimulIndiceDisponibil = UBound(Persoana) End Function
188
Urmeaz dou subprograme care realizeaz trasarea bioritmului. Despre ideea reprezentrii celor trei curbe care caracterizeaz dispoziia unui om, se poate citi n cartea mea Aplicai n C i C++, de aceea nu voi insista aici asupra acestui lucru.
Function NrZile (d, m, y) Dim p, n n = Z(m) + 365 * y + Int(y / 4) + d + 1 n = n - Int(y / 100) + Int(y / 400) If Not((Int(y/4)<>y/4)Or(Int(y/400)=y/400)Or (Int(y/100)=y/100)Or(m>2)) Then n = n - 1 End If NrZile = n End Function Sub TraseazaBioritm (pers As Form) pers.Label1.Caption = "Data nasterii:" + data_nast pers.Label2.Caption = "Data de referinta:" + data_ref Dim PI, UR, SR, SE, SEN Dim D1, M1, Y1, D2, M2, Y2, NZ Dim n As Integer, i As Integer PI = 3.1415926 ReDim p(3), Q(3) Z(1)=31: Z(2)=28: Z(3)=31: Z(4)=30: Z(5)=31: Z(6)=30 Z(7)=31: Z(8)=31: Z(9)=30: Z(10)=31: Z(11)=30: Z(12)=31 p(1) = 23: p(2) = 28: p(3) = 33 D1 = Left(data_nast, 2) M1 = Mid(data_nast, 4, 2) Y1 = Mid(data_nast, 7, 4) If Y1 / 4 <> Int(Y1 / 4) Then Z(2) = 28 If (Y1/400=Int(Y1/400)) Or (Y1/100<>Int(Y1/100)) Then Z(2)=29 End If If D1 > Val(Z(M1)) Then MsgBox "Data de nastere incorecta!", 48, "Eroare !" Exit Sub End If D2 = Left(data_ref, 2) M2 = Mid(data_ref, 4, 2) Y2 = Mid(data_ref, 7, 4) If Y2 / 4 <> Int(Y2 / 4) Then Z(2) = 28 If (Y2/400=Int(Y2/400)) Or (Y2/100<>Int(Y2/100)) Then Z(2)=29 End If If D2 > Val(Z(M2)) Then MsgBox "Data de referinta incorecta!", 48, "Eroare !"
189
Exit Sub End If NZ = NrZile(D2, M2, Y2) - NrZile(D1, M1, Y1) Dim pas As Integer, L As Integer, x As Integer pers.Picture1.Top = 1000 pers.Picture1.Left = 100 pers.Picture1.Width = pers.Width - 300 pers.Picture1.Height = pers.Height - 1500 pas = pers.Picture1.Width \ 31 x = pers.Picture1.Height - 200 L = Int(pers.Picture1.Height / 2.5) Dim cul For i = 1 To 3 Select Case i Case 1: cul = RGB(255, 0, 0) Case 2: cul = RGB(0, 255, 0) Case 3: cul = RGB(0, 0, 255) End Select Q(i) = NZ Mod p(i) UR = 2 * PI * Q(i) / p(i) SR = Sin(UR) SE = Int((SR + 1) * L) For n = NZ To NZ + 31 Q(i) = n Mod p(i) UR = 2 * PI * Q(i) / p(i) SR = Sin(UR) SEN = Int((SR + 1) * L) pers.Picture1.Line (pas*(n-NZ-1),x-SE)(pas*(n-NZ),x-SEN),cul SE = SEN Next n Next i End Sub
Dup cum se vede, bioritmul se traseaz printr-o serie de 31 de segmente de dreapt, folosind metoda Line n cadrul zonei Picture1 (din fereastra pentru care se realizeaz acest lucru). Fiierul MDI.FRM Aplicaia pornete cu ncrcarea acestei forme, deci avei grij s precizai acest lucru mediului de programare Visual Basic (la Startup form). Pentru a nu mai avea probleme cu directoarele i cile ctre fiierele folosite, schimbm directorul curent, astfel nct acesta s fie identic cu calea ctre aplicaia noastr. Crem, deocamadat, o singur persoan, deci vectorul Persoana (care conine ferestrele 190
tuturor bioritmurilor) se redimensioneaz cu o singur component. Se apeleaz forma de date (frmDate) pentru ca s se citeasc datele acestei prime persoane i s se afieze pe ecran, n fereastra corespunztoare (care va fi etichetat cu numele persoanei respective i cifra 1), bioritmul. De asemenea, se face o redimensionare a vectorului Sters.
Sub MDIForm_Load () Show ChDir App.Path prima_data = True ReDim Persoana(1) ReDim Sters(1) Persoana(1).Tag = 1 frmMDI.Enabled = False frmDate.Show End Sub
Prsirea programului se poate face doar dac nu mai sunt bioritmuri pe ecran:
Sub MDIForm_Unload (Cancel As Integer) If Not MaiSuntFerestre() Then End End If End Sub
Crearea unei noi ferestre copil, deci a unui nou bioritm, se face apelnd mai nti forma de date, pentru a citi datele noii persoane:
Sub mnuFNew_Click () frmMDI.Enabled = False frmDate.Show End Sub
191
Fiierul FRMDATE.FRM Aici avem trei casete de text. Dup ce se completeaz fiecare din ele, se poate trece mai departe (la urmtoarea, respectiv la butonul Command1):
Sub Text1_KeyPress (keyAscii As Integer) If keyAscii = 13 Then Text2.SetFocus End Sub Sub Text2_KeyPress (keyAscii As Integer) If keyAscii = 13 Then Text3.SetFocus End Sub Sub Text3_KeyPress (keyAscii As Integer) If keyAscii = 13 Then Command1.SetFocus End Sub
Butonul Command1 preia datele persoanei (numele, data naterii i data de referin, adic prima din cele 31 de zile pentru care se dorete a i se reprezenta grafic bioritmul). Se caut apoi primul indice de fereastr copil disponibil i se afieaz fereastra respectiv, cu trasarea bioritmului corespunztor.
Sub Command1_Click () nume_pers = Text1.Text data_nast = Text2.Text data_ref = Text3.Text frmDate.Hide Dim i As Integer If prima_data Then i = 1 prima_data = False Else i = PrimulIndiceDisponibil() End If Persoana(i).Tag = i Persoana(i).Caption = nume_pers & ":" & i TraseazaBioritm Persoana(i) Persoana(i).Show frmMDI.Enabled = True End Sub
Iat un exemplu de introducere de informaii pentru o anumit persoan. Aadar, trebuie ca datele de natere i de referin s fie introduse n formatul zz.ll.aaaa, aa cum este indicat n figur: 192
Fiierul BIORITM.FRM Acesta este fiierul care cuprinde codul corespunztor unei ferestre copil, de tip frmBioritm. Subrutinele care urmeaz prezint principalele operaii care se pot face cu o astfel de fereastr, aa cum suntei obinuii n programele MDI de sub Windows: a) nchiderea unei ferestre, din meniu - cuprinde un apel interesant care nchide fereastra curent: Unload Me.
Sub mnuFClose_Click () Unload Me End Sub
193
End Sub
194
Am scris i subrutina asociat evenimentului Paint (de desenare) a ferestrei, care nseamn a reprezenta grafic bioritmul:
Sub Form_Paint () TraseazaBioritm Me End Sub
195
Aplicaia 20
Editor de hri
n partea din stnga, pe trei coloane, sunt prezentate mai multe pictograme, fiecare din ele reprezentnd ceva anume: o autogar, o agenie de voiaj, o catedral, un aeroport, un oficiu potal, o universitate, un stadion, un atelier de service auto, o 196
discotec, un centru comercial i tot felul de alte lucruri care pot fi vzute ntr-un ora. Prin drag and drop astfel de pictograme pot fi copiate n restul ferestrei aplicaiei. Aici, fiecare astfel de obiect poate primi o anumit denumire. Apoi, prin apsarea butonului din stnga al mouse-ului putem afla informaii despre respectivul obiect amplasat. De exemplu, n figura de mai nainte, prin acionarea butonului din stnga al mouse-ului peste pictograma cu dirijorul, se obine urmtoarea informaie: Ateneul G. Enescu. Prin acionarea butonului din dreapta peste un astfel de obiect vom realiza tergerea obiectului respectiv de pe hart. Dac se acioneaz unul din butoanele mouse-ului de dou ori pe hart (dar nu peste vreun obiect deja amplasat), atunci se poate trasa o strad pe hart (subire sau groas, adic mai puin important sau principal, n funcie de butonul de mouse apsat (stnga sau dreapta)). Ce reprezint fiecare din pictogramele din legenda din partea stng se poate afla, acionnd butonul din stnga al mouse-ului peste repectiva pictogram. Meniul aplicaiei conine cteva opiuni simple care realizez comenzile corespunztoare. Observaie. O asemenea aplicaie este prezentat i n lucrarea mea Grafic n OOP i nu numai (vezi bibliografia). Pentru a realiza aplicaia descris, trebuie creat un fiier proiect HARTA.MAK. Acesta trebuie s cuprind trei fiiere: HARTA.FRM, HARTA_D.FRM i HARTA.BAS. Ultimul fiier conine doar cod. Fiierul HARTA.FRM n acest fiier este descris forma principal a aplicaiei, cea care cuprinde harta oraului, precum i codul asociat acestei forme, despre care vom vorbi n paragraful urmtor.
Begin Form Form1 AutoRedraw Caption ClientHeight ClientLeft ClientTop ClientWidth FontBold = = = = = = = -1 'True "Harta orasului" 2430 4410 2055 3375 0 'False
197
FontItalic = 0 'False FontName = "Arial" FontSize = 8.25 FontStrikethru = 0 'False FontUnderline = 0 'False Height = 3390 Left = 4350 LinkTopic = "Form1" ScaleHeight = 2430 ScaleWidth = 3375 Top = 1155 Width = 3495 Begin Image ob Height = 735 Index = 0 Left = 120 Top = 1080 Visible = 0 'False Width = 615 End Begin Image obiectiv DragMode = 1 'Automatic Height = 615 Index = 0 Left = 120 Top = 120 Visible = 0 'False Width = 615 End Begin Menu mnuHartaNoua Caption = "&Harta noua" End Begin Menu mnuClear Caption = "&Stergere strazi" End Begin Menu mnuExit Caption = "&Oprire program" End Begin Menu mnuDespre Caption = "&Despre program" End End
n principiul, forma este foarte srac n coninut, ea cuprinznd, pe lng un meniu cu patru opiuni, dou obiecte de tip Image, ambele avnd cmpul Index=0. Cele dou obiecte (purtnd numele de Obiectiv, respectiv Ob) ajut la generarea de vectori cu astfel de obiecte. Vectorul Obiectiv va conine cele 45 de 198
pictograme din legend, iar vectorul Ob va conine obiectele care vor fi amplasate pe hart. Iat i figura corespunztoare descrierii formei anterioare:
Form1
Obiectiv(0)
Ob(0)
Fiierul HARTA_D.FRM Un al doilea fiier este fiierul HARTA_D.FRM care conine descrierea unei forme Form2, foarte simpl. Ea cuprinde o etichet explicativ (Label1) i o caset de text (Text1). n respectiva caset se va introduce denumirea obiectului (ob-ului) curent, care urmeaz a se amplasa pe harta oraului.
Begin Form Form2 BorderStyle = 3 'Fixed Double Caption = "Form2" ClientHeight = 945 ClientLeft = 3195 ClientTop = 3345 ClientWidth = 3930 Height = 1350 Left = 3135 LinkTopic = "Form2" MaxButton = 0 'False MinButton = 0 'False ScaleHeight = 945 ScaleWidth = 3930 Top = 3000 Width = 4050 Begin TextBox Text1 Height = 285
199
Left = TabIndex = Top = Width = End Begin Label Label1 Caption = Enter!" Height = Left = TabIndex = Top = Width = End End
Label1
Text1
200
Prin n notm numrul de obiecte amplasate pe hart, iar prin ns numrul de strzi. Att strzile, ct i obiectele se amplaseaz n acea parte a ferestrei Form1, care ncepe dup ce se termin legenda cu obiective, deci cu cea mai din stnga abscis X_min. Pentru trasarea strzilor trebuie memorate coordonatele punctului de nceput ale mouse-ului: first_x i first_y. Avem nevoie i de o variabil logic desen, pentru realizarea acestui lucru. Prin Strada() am declarat un vector care pstreaz strzile desenate pe hart. Este nevoie de memorarea lor, pentru c subrutina Form_Paint care restaureaz imaginea hrii, dup fiecare redimensionare sau tergere a sa, s funcioneze corect. De asemenea, aceast subrutin realizeaz redesenarea coninutului formei pe baza unei proporii stabilite n funcie de dimensiunile iniiale ale formei. De aceea sunt necesare i variabilele rap_x, rap_y, x_0 i y_0. S vedem, acum ce se ntmpl la pornirea programului, deci ce se realizeaz n subrutina Form_Load. Imaginea ferestrei Form1, dup pornirea programului este ca n figura urmtoare:
201
Aadar, are loc ncrcarea pictogramelor i dispunerea lor pe cele trei coloane, fiecare cu cte 15 elemente. Pictogramele se citesc din nite fiiere WMF. Numele acestora, precum i semnificaia lor sunt date ntr-un fiier text, numit HARTA.TXT. Acest fiier are urmtorul coninut. Fiierul HARTA.TXT
BUS->Autogara PLANE->Aeroportul SOCCER->Stadionul PRESENT4->Magazinul MONEYBAG->Banca BIGTREE->Parcul STOPSIGN->Politia DOCTOR->Spitalul TRAIN->Gara SCALES->Judecatoria BURDEN->Agentia de turism
202
MOVIES->Cinema PHONE->Telefoane PHARMACY->Farmacia SEMINAR->Liceul LUGGAGE->Agentia de voiaj LETTERS->PTTR CAR->Service auto CROSS2->Cimitirul TENNIS->Terenul de sport COFFEE->Barul CTYBLOCK->Zona rezidentiala OFFICBLD->Institutia TULIPS->Florarie BOOKS->Biblioteca DRAMA->Teatrul WEDDING->Oficiul starii civile OLYMPIAN->Sala de atletism DESSERT->Cofetaria EAGLE_GR->Primaria CHURCH1->Catedrala GRADUATE->Universitatea DISCO->Discoteca DISK->Firma de calculatoare CONDUCTR->Ateneul CHURCH2->Biserica ELEPHANT->Gradina zoologica ARTIST->Galeriile de arta PLATE->Restaurantul WINEBOTL->Crama NEWYEAR->Monumentul/Statuia CADUCEUS->Camera de comert PALMTREE->Gradina botanica ORBIT->Institutul QUESTION->
n fiierul de mai sus putei vedea denumirile fiierelor WMF (fr extensie) pe care le-am folosit noi n realizarea aplicaiei noastre. Dumneavoastr putei cut pe calculatorul propriu aceste fiiere, care pot veni o dat cu diverse programe ce conin clipart-uri (Power Point, Word etc.) sau le putei cuta la prieteni. Dac nici ei nu le au, putei s le nlocuii cu fiierele WMF (sau BMP, ICO) mai mici (de tip clip-art) pe care le avei. n cel mai ru caz, v creai singuri astfel de pictograme. Observai c lui QUESTION.WMF nu-i corespunde nici o denumire. Asta nseamn c acest simbol (un semn de ntrebare mare i rou) poate fi folosit pentru orice alt obiectiv nespecificat de vreunul din celelalte pictograme. 203
Sub Form_Load () desen = False n = 0: ns = 0 has = Form1.ScaleHeight \ 15 lat = has x_min = 3 * has + 50 rap_x = 1: rap_y = 1 x_0 = ScaleWidth - x_min: y_0 = ScaleHeight ChDir app.Path Open "HARTA.TXT" For Input As #1 For j = 1 To 3 For k = 1 To 15 i = 15 * (j - 1) + k Line Input #1, s p = InStr(s, "->") fis_ob = Left(s, p - 1) + ".WMF" nume_ob = Right(s, Len(s) - p - 1) Load obiectiv(i) obiectiv(i).BorderStyle = 1 obiectiv(i).Stretch = True obiectiv(i).Left = lat * (j - 1) obiectiv(i).Top = has * (k - 1) obiectiv(i).Width = lat obiectiv(i).Height = has obiectiv(i).Tag = nume_ob obiectiv(i).Picture = LoadPicture(fis_ob) obiectiv(i).DragMode = 1 obiectiv(i).Visible = True Next k Next j Close #1 End Sub
Dac utilizatorul programului vrea s neleag ce reprezint fiecare din cele 45 de pictograme din legenda din stnga lui Form1, atunci el va aciona butonul din dreapta al mouse-ului, n dreptul obiectivului corespunztor. Acest lucru va avea ca efect apelarea subrutinei:
Sub obiectiv_Click (Index As Integer) MsgBox obiectiv(Index).Tag, 64, "Ce reprezinta asta?" End Sub
204
X_min
Trasarea strzilor se face cu subrutina urmtoare, care ine cont de faptul c variabila desen s-a iniializat cu False, n Form_Load:
Sub Form_MouseDown (Button As Integer, Shift As Integer, x As Single, y As Single) If desen Then Select Case Button Case 1: DrawWidth = 3 ForeColor = RGB(150, 200, 200) Case 2: DrawWidth = 8 ForeColor = RGB(200, 200, 50) End Select
205
If x < x_min Then x = x_min Line (first_x, first_y)-(x, y) ns = ns + 1 ReDim Preserve Strada(ns) Strada(ns).X1 = first_x: Strada(ns).X2 = x Strada(ns).Y1 = first_y: Strada(ns).Y2 = y Strada(ns).tip = Button desen = False MousePointer = 0 Else desen = True first_x = x: first_y = y MousePointer = 2 End If End Sub
Iat i cum se realizeaz amplasarea obiectelor pe hart, prin drag and drop:
Sub Form_DragDrop (source As Control, x As Single, y As Single) If x - source.Width \ 2 < x_min Then MsgBox "Nu se poate aseza aici !", 16, "Atentie!" Exit Sub End If n = n + 1 Load ob(n) ob(n).Width = source.Width ob(n).Height = source.Height ob(n).Stretch = True ob(n).Tag = source.Tag ' se va adauga si denumirea ! ob(n).Left = x - (ob(n).Width / 2) ob(n).Top = y - (ob(n).Height / 2) ob(n).BorderStyle = 0 ob(n).Picture = source.Picture ob(n).Visible = True ob(n).MousePointer = 10 Form1.Enabled = False 'Form1.Hide ' pentru a evita problemele
206
Form2.Caption = "Denumire " + ob(n).Tag Form2.Text1.SelStart = 0 Form2.Text1.SelLength = Len(Form2.Text1.Text) Form2.Show ' preia denumirea lui ob(n) Do DoEvents ' asteapta pana se termina lucrul in Form2 Loop Until Form1.Enabled = True ob(n).Tag = ob(n).Tag + " " + Form2.Text1.Text End Sub
Observai apelui celei de a doua forme (Form2) care preia denumirea obiectului amplasat. Ciclul:
Do DoEvents ' asteapta pana se termina lucrul in Form2 Loop Until Form1.Enabled = True
are rolul de a bloca temporar execuia programului, la nivelul lui Form2. tergerea unui obiect, sau aflarea de informaii despre el se realizeaz cu subrutina urmtoare:
Sub ob_MouseDown (Index As Integer, Button As Integer, Shift As Integer, x As Single, y As Single) Select Case Button Case 2 ' se sterge afisajul si obiectul CurrentX = ob(n).Left CurrentY = ob(n).Top - 200 ForeColor = QBColor(15) Print ob(n).Tag x = ob(n).Left: y = ob(n).Top ob(Index).Visible = False ob(Index) = ob(n) ob(Index).Left = x: ob(Index).Top = y ob(Index).Visible = True Unload ob(n) n = n - 1 Case 1 ' afisez informatii MsgBox ob(Index).Tag, 64, "Informatii" End Select End Sub
207
Iat i subrutina care redeseneaz, la nevoie, coninutul formei Form1. Ea redeseneaz (la noile proporii) legenda cu obiective din stnga formei, apoi strzile i obiectele de pe hart.
Sub Form_Paint () Cls ' reafisez meniul cu obiective din stanga has = ScaleHeight \ 15 lat = has x_min_vechi = x_min 'pentru calcule ulterioare x_min = 3 * has + 10 For i = 1 To 45 obiectiv(i).Visible = False Next i For j = 1 To 3 For k = 1 To 15 i = 15 * (j - 1) + k obiectiv(i).Left = lat * (j - 1) obiectiv(i).Top = has * (k - 1) obiectiv(i).Width = lat obiectiv(i).Height = has obiectiv(i).Visible = True Next k Next j ' redesenez strazile rap_x=(ScaleWidth-x_min)/x_0: rap_y=ScaleHeight/y_0 x_0 = ScaleWidth - x_min: y_0 = ScaleHeight For i = 1 To ns Select Case Strada(i).tip Case 1: DrawWidth = 3 ForeColor = RGB(150, 200, 200) Case 2: DrawWidth = 8 ForeColor = RGB(200, 200, 50) End Select Strada(i).X1=x_min+rap_x*(Strada(i).X1-x_min_vechi) Strada(i).Y1 = rap_y * Strada(i).Y1 Strada(i).X2=x_min+rap_x*(Strada(i).X2-x_min_vechi) Strada(i).Y2 = rap_y * Strada(i).Y2 Line (Strada(i).X1, Strada(i).Y1)(Strada(i).X2, Strada(i).Y2) Next i ' sterg ob-urile, pentru a le reafisa la noile dimensiuni For i = 1 To n ob(i).Visible = False Next i For i = 1 To n ' calculez noile coordonate
208
ob(i).Left=(ob(i).Left-x_min_vechi)*rap_x+x_min ob(i).Top = ob(i).Top * rap_y ' calculez noile dimensiuni in functie de un obiectiv ob(i).Width = obiectiv(1).Width ob(i).Height = obiectiv(1).Height 'afisez din nou ob(i).Visible = True Next i End Sub
(din
fiierul
HARTA.FRM)
sunt
Sub Form_Unload (Cancel As Integer) Unload Form2 ' ca sa se opreasca ! End Sub Sub mnuDespre_Click () s1 = "Acesta este un editor de harti!" NL = Chr$(13) + Chr$(10) s2 = "(C) 1998 B.P., tel. 092-738341" MsgBox s1 + NL + s2, 64, "Despre program" End Sub Sub mnuExit_Click () End End Sub Sub mnuHartaNoua_Click () For i = n To 1 Step -1 Unload ob(i) Next i n = 0 mnuClear_Click End Sub
209
Fiierul HARTA_D.FRM Acest fiier conine doar o singur subrutin, uor de neles, care acioneaz la finele introducererii unui text n caseta Text1:
Sub Text1_KeyPress (KeyAscii As Integer) If KeyAscii = 13 Then Form1.Enabled = True Form2.Hide End If End Sub
210
Bibliografie
1. Bogdan Ptru - Aplicaii n Delphi, Editura Teora, Bucureti, 1998. 2. Bogdan Ptru - Aplicaii n Visual Basic, Editura Teora, Bucureti, 1998
* * *
Observaie. Sursele aplicaiilor din aceast carte au fost realizate, compilate i testate n mediile de programare Delphi 3 i Visual Basic 3. Adaptarea acestor aplicaii la versiuni ulterioare ale celor dou medii de programare rmne n seama cititorului. Orice adaptare la Delphi 6 sau Visual Basic 6 pe care o realizai o putei trimite prin e-mail la bogdanpatrut@yahoo.com sau bogdan@edusoft.ro.
211