Sunteți pe pagina 1din 211

Bogdan Ptru

20 APLICAII DELPHI I VISUAL BASIC

EduSoft Bacu 2005

Redactor: Tiberiu Socaciu

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

PARTEA I APLICAII N DELPHI

Aplicaia 1

Ciupercile - un joc de ndemnare i perspicacitate


1.1. Prezentare general
Ne propunem s realizm un joc de ndemnare i perspicacitate clasic, pe care l-am denumit Ciupercile i n care, mnuind un omule printr-un labirint de ziduri i scri, trebuie s culegem cu el nite ciuperci amplasate n diferite poziii ale acestui labirint. De asemenea, trebuie s ne ferim de dumani, care n cazul acestui joc sunt nite caracatie. Firete, dei scenariul pare imposibil (ciuperci, caracatie, omulei, ziduri i scri), trebuie s ne gndim c totul este doar un joc! Iat cum va arta jocul nostru n timpul execuiei programului:

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.

1.2. Textul explicat al programului


n continuare vom prezenta unit-ul ciupercile1.pas, folosit n proiectul Delphi ciupercile.dpr. Acest unit conine toate declaraiile de variabile i toate procedurile i alte elemente folosite n cadrul aplicaiei. Vom comenta fiecare din aceste proceduri i algortmii pe care ele i implementeaz.
unit ciupercile1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, ExtDlgs; type TForm1 = class(TForm) Timer1: TTimer; Button1: TButton; Label1: TLabel; Label2: TLabel; procedure Timer1Timer(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure FormPaint(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1;

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

procedure Clear; end;

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

procedure TCaracatita.Init; begin x:=x0; y:=y0; mut:=Random(2); end;

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;

procedure TCaracatita.Clear; begin ClearView(lat*y+1,lat*x+1,lat*y+lat-1,lat*x+lat-1); Tipar(x,y) 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

Harta - un program despre Romnia turistic


2.1. Prezentare general
Aplicaia pe care o concepem n continuare poate constitui model pentru o serie ntreag de programe complexe, n care se dorete prezentarea unor informaii pe o tem dat. Informaiile vor fi grupate pe articole i vor fi att texte, ct i imagini, selectabile dintro list de imagini disponibile. Alegerea articolului se va face n mod interactiv, dup un model similar celui din aplicaia "Harta".

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.

2.2. Textul explicat al programului


Proiectul nostru, harta.dpr, va conine referiri la cele dou unit-uri folosite: harta1.pas i harta2.pas:
program harta; uses Forms, harta1 in 'harta1.pas' {Form1}, harta2 in 'harta2.pas' {Form2}; {$R *.RES} begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.CreateForm(TForm2, Form2); Application.Run; end.

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

Image1.Picture.Graphic:=nil; Memo1.ScrollBars:=ssBoth; Image1.Stretch:=True;

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

Mrfuri - un joc de logic


3.1. Prezentare general
Jocul este o variant n Delphi a binecunoscutului Sokoban. Un omule se afl ntr-un depozit i trebuie s deplaseze la n nite locuri dinainte stabilite nite pachete cu marf.

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
#################### ## %# %### # ## * # ## # *$## # ## ## # # #### # ## * * # # * %### # # # ## # # # * # #* * %# ## #% # # #% ####% %# ####################

3.2. Textul explicat al programului


Pentru a nelege textul unit-ului marf1.pas va trebui, pentru nceput, s proiectm interfaa din forma aplicaiei. Form1 va conine un meniu principal MainMenu1 (de tip TMainMenu), cu trei comenzi, dup cum se vede n figur:

40

Aadar, vom avea interfaa lui marf1.pas:


unit marf1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Menus; type TForm1 = class(TForm) MainMenu1: TMainMenu; Alegelabirint1: TMenuItem; Joaca1: TMenuItem; Terminare1: TMenuItem; procedure Joaca1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Terminare1Click(Sender: TObject); procedure Alegelabirint1Click(Sender: TObject); procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure FormPaint(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1;

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

ReadLn(F) end; CloseFile(F); DisplayLoc(0) end end;

Procedura Sch interschimb valorile a dou csue din labirint.


procedure Sch(x1,y1,x2,y2: Integer); var aux: Char; begin aux:=L[x1,y1]; L[x1,y1]:=L[x2,y2]; L[x2,y2]:=aux; Tipar(x1,y1); Tipar(x2,y2) end;

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;

Procedurile urmtoare se explic singure:


procedure IncarcaSiDeseneazaTabla; var dx,dy: Integer;

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

38: 40: 37: 39:

DoMove(0,-1); DoMove(0,+1); DoMove(-1,0); DoMove(+1,0)

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

Puzzle - un joc de perspicacitate


4.1. Prezentare general
Dorim s realizm un joc de puzzle, n care dispunem de n2-1 piese numerotate cu 1, 2, 3, ... n2-1, dispuse sub forma unei matrice ptratice pe o tabl cu n rnduri i n coloane. Rmne un spaiu liber, n care poate fi deplasat oricare pies din cele adiacente lui (din stnga, din dreapta, de sus sau de jos). La nceputul programului, se introduce ntr-o caset Edit1 numrul n de piese, apoi piesele se amestec (prin mutri legale). Scopul jocului este ca, prin mutri kegake s se aranjeze piesele n matrice, n ordinea 1, 2, ..., n (pe primul rnd), n+1, n+2, ... pe al doilea rnd .a.m.d.. Colul din dreapta jos va rmne liber.

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.

4.2. Textul explicat al programului


Forma aplicaiei noastre arat astfel:

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

{ Private declarations } public { Public declarations } end; var Form1: TForm1;

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;

Procedura urmtoare conine anumite iniializri ale formei.


procedure TForm1.FormCreate(Sender: TObject); begin Caption:='Puzzle cu imagini'; Height:=400; Width:=300; Button1.Caption:='Start' 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

Supraf - reprezentarea grafic a suprafeelor


5.1. Prezentare general
Urmtoarea aplicaie Delphi va reprezenta grafic o suprafa, ntr-o proiecie bidimensional a spaiului tridimensional. Suprafaa va fi dat printr-o ecuaie, n care cota este o funcie de abscis i ordonat: z=f(x,y).

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.

5.2. Textul explicat al programului


Unitul de baz al acestui program are urmtoarea seciune de interfa:
unit supraf1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, Menus, StdCtrls; type TForm1 = class(TForm) Image1: TImage; Button1: TButton; Button2: TButton;

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;

Urmeaz procedura de reprezentare a suprafeei:


procedure ReprezentareSuprafata; var i,j: Byte; begin {se transforma unghiurile din grade in radiani si se calculeaza cosinusurile si sinusurile lor} al:=radiani(alfa); c1:=cos(al); s1:=sin(al); be:=radiani(beta); c2:=cos(be); s2:=sin(be); ga:=radiani(gama); c3:=cos(ga); s3:=sin(ga); {se determina unitatea de masura pe Ox si pe Oy} ux:=abs(b-a)/(nx-1); uy:=abs(d-c)/(ny-1); {se determina valorile minime si maxime pentru xx si yy} x:=a; y:=c; z:=f(x,y); xx[1,1]:=x*c1+y*c2+z*c3; yy[1,1]:=x*s1+y*s2+z*s3; xmin:=xx[1,1]; xmax:=xmin; ymin:=yy[1,1]; ymax:=ymin; for i:=1 to nx do begin y:=c; for j:=1 to ny do begin {cota este functie de celelalte doua coordonate} z:=f(x,y); {se determina cele doua puncte ale proiectiei} xx[i,j]:=x*c1+y*c2+z*c3; yy[i,j]:=x*s1+y*s2+z*s3; if xmin>xx[i,j] then xmin:=xx[i,j] else if xmax<xx[i,j] then xmax:=xx[i,j]; if ymin>yy[i,j] then ymin:=yy[i,j] else if ymax<yy[i,j] then ymax:=yy[i,j]; y:=y+uy end; x:=x+ux end; {se determina unitatea de reprezentare pe Ox si pe Oy} ux:=(xmax-xmin)/Form1.Image1.Width; uy:=(ymax-ymin)/Form1.Image1.Height; dy:=Form1.Image1.Height;

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

Parser - analiza sintactic i lexical a unei fraze


6.1. Prezentare general
Programul, de o complexitate deosebit, face parte din categoria programelor de prelucrare a limbajului natural. Se introduce ntr-o csu de text o fraz, iar programul spune dac fraza este corect sau nu, n conformitate cu un vocabular i o mulime de reguli sintactice aprioric stabilite i care formeaz o gramatic.

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

if S chart[0,n] then Accept else Reject

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.

6.2. Textul explicat al programului

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

AutoWeb - Creator de pagini web


7.1. Prezentare general
Ne propunem, n continuare, s realizm un program cu ajutorul cruia s realizm pagini web personale, dup un anumit format, redat n figura de mai jos:

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

7.2. Textul explicat al programului


n continuare, s vedem cum funcioneaz toate controalele din forma aplicaiei AutoWeb.
unit web1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TForm1 = class(TForm) Label1: TLabel; Edit1: TEdit; Label2: TLabel; Edit2: TEdit; TabControl1: TTabControl; Memo1: TMemo; Button1: TButton; Edit3: TEdit; Label3: TLabel; Button2: TButton; Edit4: TEdit; OpenDialog1: TOpenDialog; procedure TabControl1Change(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM}

77

var nr_sub_tratat: Integer; subiect: array[1..3] of String;

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

apoi salvm textul subiectului curent (adic al tabulatorului


curent) n fiierul corespunztor; variabila nr_sub_tratat indic care subiect este cel curent; trebuie s ne ngrijim de existena sau inexistena unei imagini n subiectul curent; dac imaginea nu exist, atunci nseamn c avem n csua respectiv textul: '(dati numele fisierului)', scris automat n locul irului vid; astfel de probleme apar i n procedurile anterioare i se rezolv la fel.
procedure TForm1.Button2Click(Sender: TObject); var f: TextFile; s: String; i: Integer; begin { salvam numele persoanei si motto-ul } AssignFile(f,'sus.html'); Rewrite(f); WriteLn(f,'<html>'); WriteLn(f,'<head><title>sus</title></head>'); WriteLn(f,'<body background="fundal.jpg">'); WriteLn(f,'<h1><i>'+ '<font face="Arial" color="black">'); WriteLn(f,'<marquee behaviour="alternate" '+ 'scrolldelay="30"'); WriteLn(f,'scrollamount="2" bgcolor="#00FFFF"'+ ' height="25"'); WriteLn(f,'align=center>'); WriteLn(f,Edit1.Text); WriteLn(f,'</marquee>'); WriteLn(f,'</i></h1>'); WriteLn(f,'<h4>'); WriteLn(f,'<marquee scrolldelay="35"'); WriteLn(f,'bgcolor="#eeee88", height="20" '+ 'align=top>'); WriteLn(f,Edit2.Text); WriteLn(f,'</marquee>'); WriteLn(f,'</h4>'); WriteLn(f,'</body></html>'); CloseFile(f);

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

Zodiac - ce ne este scris n stele?


8.1. Prezentare general
Fie c dumneavoastr credei sau nu n cele scrise n tot felul de zodiace i horoscoape, un astfel de program este ntotdeauna interesant, constituind o modalitate de amuzament. Ne propunem, n acest capitol, s realizm un astfel de program

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 Nitescu barbat 11.12.1980

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

Iat un exemplu de fragment din fiierul COMPAT.TXT:


... Rac-Sagetator Racul e iritat de nestatornicia si inexactitatea Sagetatorului care, in plus, il poate rani in modul cel mai inocent pe acesta. Daca nu se menajeaza reciproc, nu e nici o speranta de intelegere, iar daca o fac totusi, sansele raman in continuare destul de mici. stop Rac-Capricorn Nu e destul de sigur ca relatia va dura, desi la prima vedere au multe in comun (tenacitate, cultivarea mediului familial, munca indarjita, etc.). Pot cadea de acord in ceea ce priveste familia, in domeniul sentimental si sexual lucrurile complicandu-se insa, capricornul fiind putin prea rece si nereceptiv la punctele de vedere ale racului. Divergente serioase. stop ...

8.2. Textul explicat al programului


Din cele prezentate anterior se poate deduce modul de funcionare al aplicaiei Zodiac. Mai jos este prezentat forma aplicaiei, n care sunt identificate toate controalele:

MainMenu1 lblZodia Dialog imgZodia memZodia

txtNume PersoanaNoua rgSexul spnZiua lstLuna

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;

Compatibilitatea a dou zodii se verific cu ajutorul procedurii de mai jos.


procedure TForm1.Dezodii1Click(Sender: TObject); var zod1, zod2: String; begin nume1 := ''; nume2 := ''; zod1 := InputBox('Compatibilitate de zodii', 'Dati prima zodie:','Zodia'); zod2 := InputBox('Compatibilitate de zodii', 'Dati a doua zodie:','Zodia'); AfiseazaCompatib(zod1, zod2) end;

n final, acionarea comenzii 'Exit' determin prsirea aplicaiei Zodiac.


procedure TForm1.Exit1Click(Sender: TObject); begin Application.Terminate end; 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

Gastro - program pentru reete culinare


9.1. Prezentare general
Dispunem de anumite alimente n anumite cantiti i cunoatem, de asemenea, cteva reete culinare. Vrem s tim care din reetele culinare pe care le cunoatem pot fi folosite pentru a prepara ceva de mncare, n conformitate cu alimentele i cantitile disponibile.

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.

9.2. Textul explicat al programului


nainte de a prezenta unit-ul de baz al acestei aplicaii, s vedem cum se numesc toate controalele folosite n acest program.
txtAliment lstRetete cmdAdauga cmdElimina cmdRetete (invizibil)

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

var Form1: TForm1; implementation {$R *.DFM}

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

end; CloseFile(f); cmdRetete.Click; end;

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

PARTEA II APLICAII N VISUAL BASIC

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

...... (urmeaz descrierile componentelor din form)


End

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.

10.2. Textul explicat al programului


Spuneam c vom folosi un buton btnOnOff care va fi de pornit/oprit calculatoru. Astfel, cnd calculatorul este oprit (butonul btnOnOff este pe Off), celelalte butoane nu vor putea fi folosite (n 117

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

Cu v vom nota valoarea curent a calculelor efectuate.


Dim v

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

Oprirea aplicaiei se face la acionarea butonului de terminare (cu Caption=Stop):


Sub btnTerminare_Click () End End Sub

iat i cele dou subrutine asociate comenzilor din meniu:


Sub mnuBog_Click () frmCalculator.Enabled = False form1.Visible = True form1.Enabled = True End Sub Sub mnuCalc_Click () MsgBox "Acesta este un calculator de buzunar...", 48, "Nimic deosebit" 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 producerea sunetului avem nevoie de un control MultiMedia invizibil:


Begin MMControl Sunet Visible = End 0 'False

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 Label2 Alignment=2 'Center Caption

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.

11.2. Textul explicat al programului


Fiecare musc poate avea una din trei stri: s nu fie pe ecran (starea 0), s fie pe ecran i s fie vie (starea 1) sau s fie moart pe ecran (starea 2). Aceste stri vor fi pstrate n proprietile Tag ale celor cinci imagini care compun vectorul Image1. S notm cu timp intervalul de timp care mai este disponibil pentru joc, msurat n secunde, iar cu scor punctajul curent al juctorului:
Dim scor As Integer Dim timp As Integer

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

mic cntecel, pe care n exemplul nostru se presupune c l avem n fiierul BAROQ.WAV.


Sub Timer2_Timer () If timp = 0 Then Sunet.Command = "Close" Sunet.FileName = "C:\BAROQ.WAV" Sunet.Command = "Open" Sunet.Command = "Play" MsgBox "Timpul a expirat!", 48, "Joc terminat" End Else timp = timp - 1 Label3.Caption = Str(timp) End If End Sub

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

12.2. Textul explicat al programului


Declarm o variabil global pentru punctaj. Acesta va crete cu 1 la omorrea unei berze i va scdea cu 1 atunci cnd o barz vie i ia zborul.
Dim Punctaj As Integer

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

Subrutina pentru focul putii:


Sub timp_foc_Timer () If pusca.Tag > 2 Then pusca.Tag = pusca.Tag - 3 AfiseazaPusca End If 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

= LoadPicture("pstg.bmp") = LoadPicture("p.bmp") = LoadPicture("pdr.bmp") = LoadPicture("pfstg.bmp") = LoadPicture("pf.bmp")

132

Case 5: pusca.Picture = LoadPicture("pfdr.bmp") End Select End Sub

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

o etichet Label1 care va conine punctajul curent realizat (iniial 0).

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

= = = = = = = =

0 'Transparent &H000080FF& 0 'Solid 420 2 600 5000 2475

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

= = = = = = = =

0 'Transparent &H000080FF& 0 'Solid 5295 1 3010 120 375

= = = = = = = =

0 'Transparent &H000080FF& 0 'Solid 5295 0 240 120 385

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.

13.2. Textul explicat al programului


Pentru a putea controla umplerea cutiei (deci i formarea liniilor pline) trebuie s dispunem de un tablou (o matrice) cu elemente 0 (spaiu gol) sau 1 (spaiu umplut):
Dim mat(0 To 20, 0 To 9) As Integer

Piesa curent (figura) (care urmeaz s cad sau chiar cade) este specificat prin variabila fig:
Dim fig As Integer

Ea poate avea una din mai multe poziii:


Dim poz 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

Variabila punctaj pstreaz numrul de puncte realizat de juctor:


Dim punctaj

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

= x2: ax3 = x2: ax4 = x2 ay2 = y2: ay3 = y2 + 1: ay4 = y2 + 2

ax2 = x3: ax3 = x3 + 1: ax4 = x3 + 1 = y3: ay3 = y3: ay4 = y3 - 1

= x2: ax3 = x2: ax4 = x2 - 1 ay4 = y2 - 2: ay2 = y2 - 1: ay1 = y2

ax4 = x3 - 1: ax2 = x3: ax1 = x3 + 1 = y3: ay1 = y3: ay4 = y3 + 1

= x2: ax3 = x2: ax4 = x2 + 1 = y2 + 1: ay3 = y2 + 2: ay4 = y2 + 2

ax2 = x2: ax3 = x2 + 1: ax4 = x2 + 1 = y2: ay3 = y2: ay4 = y2 + 1

= x1: ax3 = x1: ax4 = x1 + 1 ay4 = y1 - 1: ay1 = y1 + 1: ay2 = y1

= x1: ax2 = x1 + 1: ax1 = x1 + 2 = y1: ay1 = y1: ay4 = y1 - 1

= x2: ax3 = x2: ax4 = x2 - 1 ay2 = y2 - 1: ay3 = y2: ay4 = y2

= x3 + 1: ax4 = x3 + 1: ax3 = x3 + 2 = y3: ay3 = y3: ay4 = y3 - 1

= x3: ax3 = x3: ax4 = x3 - 1

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

(ax4 < 0) Or (ax4 > 9) Then

= 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

14.2. Textul explicat al programului


Realizarea acestei aplicaii presupune definirea urmtoarelor variabile:
Dim n, x, i, j, m

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

15.1. Prezentare general


n capitolul precedent s-a pus problema realizrii unui joc de Puzzle n care s se poat aloca dinamic memorie pentru piesele jocului, n funcie de numrul lor, dat de juctor la nceput. Aplicaia din acest capitol va rezolva acest lucru i, n plus, va avea un meniu de unde se pot apela dou texte explicative. Vom dispune i de un buton de oprire forat a programului, dar ceea ce este mai interesant n acest program este c se folosesc imagini n loc de butoane cu numere. Imaginile sunt, de fapt, fragmente ptrate egale dintr-o imagine mai mare, care se ncarc i se ncadreaz (eventual redimensionndu-se) ntr-o zon ptrat din forma aplicaiei. Avem, astfel, de a face cu un joc de Puzzle cu imagini. Numele fiierului BMP care conine imaginea care trebuie refcut se d fr extensie ntr-o caset de text special, iar numrul n (de linii i coloane) se introduce ntr-o alt caset de text. Imaginea de mai jos prezint o variant a acestei aplicaii, cu n=3 i imaginea unei fete. Numele fiierului este, dup cum se vede i din figur, C:\DISC1\VB\HEAD.BMP (extensia se adaug prin program). Dumneavoastr vei introduce n caseta respectiv numele unui fiier corespunztor, de pe discul dumneavoastr.

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

o caset de text care va conine numele fiierului cu imagine:


Begin TextBox Text1 Height = Left = TabIndex = Top = Width = End 285 240 0 120 2415

152

o caset de text care va conine numrul de linii/coloane (implicit 3)


Begin TextBox Text2 Height = Left = TabIndex = Text = Top = Width = End 285 2880 1 "3" 120 615

un meniu cu dou opiuni:


Begin Menu mnuDespre Caption = End Begin Menu mnuCe Caption = End "Despre program"

"Ce trebuie sa dai"

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

CommandButton TextBox TextBox Text1

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

15.2. Textul explicat al programului


Ca i n aplicaia precedent, avem variabilele globale urmtoare (cu excepia faptului c ii a luat locul lui i i jj locul lui j):
Dim ii, jj, n, x, m

Subrutina Form_Load face iniializri asupra formei i ascunde piesa Image1(0):


Sub Form_Load () Form1.BackColor = RGB(0, 105, 100) Form1.Caption = "Puzzle cu imagini" Form1.ScaleWidth = 5000 Form1.ScaleHeight = 5500 Image1(0).Visible = False End Sub

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

n sfrit, avem urmtoarele dou proceduri asociate meniului:


Sub mnuCe_Click () MsgBox "Trebuie sa dai numele unui fisier BMP," + " fara extensie!", 48, "Date de intrare" End Sub Sub mnuDespre_Click () MsgBox "(C) 1998 Bogdan Patrut, bogdan@lbbc.sorosis.ro", 48, "Cine a facut programul?" End Sub

157

Aplicaia 16

Bila

16.1. Prezentare general


S realizm un joc de ndemnare n care o bil se mic la infinit ca pe o mas de biliard, iar atunci cnd se izbete de perei (margininel formei) s se reflecteze. De asemenea, n partea de jos a formei aplicaiei vom dispune de o palet pe care o vom putea deplasa orizontal pentru ca bila, lovindu-se i reflectndu-se de ea, s nu mai cad pe marginea de jos. Dac se lovete de palet, atunci punctajul crete, iar dac scap pe marginea de jos, atunci el scade. La nceput, punctajul va fi zero.

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

Iat figura pe care am marcat diferitele controale ale formei:

159

Begin Shape Bila FillColor = &H000080FF& FillStyle = 0 'Solid Height = 255 Shape = 3 'Circle Width = 255 End

Timer

Spiner Spiner

Shape Paleta Spiner Spiner3

Label

16.2. Textul explicat al programului


S considerm urmtoarele variabile globale:
Dim dx, dy, pasb, pasp, punctaj

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

Iniializrile se fac n subrutina Form_Load, astfel:


Sub Form_Load () dx = 1: dy = 1 pasb = 70: pasp = 300 punctaj = 0 Label1.Caption = punctaj End Sub

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

Oprirea programului se realizeaz printr-un simplu click pe form:


Sub Form_Click () End End Sub

162

Aplicaia 17

Test gril

17.1. Prezentare general


La coal (la orele de biologie, geografie sau istorie), la examenul de bacalaureat, la admiterea n unele faculti, la concursurile televizate, la examenul pentru obinerea permisului auto, ct i n multe alte locuri, se folosesc adesea testele gril, ca form simpl, dar totodat eficient i obiectiv de examinare i notare. Asfel, persoanei examinate i se prezint mai multe ntrebri cu mai multe variante de rspuns. Uneori exist un singur rspuns corect, alteori se opteaz pentru varianta cu ma multe rspunsuri corecte sau chiar nici unul. Ne propunem s realizm un astfel de program. Fiecare ntrebare va avea cel mult cinci rspunsuri ataate, iar dintre acestea vor putea fi corecte fie nici unul, fie doar unul, fie dou sau mai multe. ntrebrile vor putea fi nsoite i de imagini. De exemplu, putem s punem urmtoarea nrebare: ce se vede n figura alturat? Dac n figura alturat (adic n imaginea citit dintr-un fiier BMP) avem un elefant, att un mamifer, un animal, ct i un elefant ar putea fi rspunsuri corecte, deci ar trebui bifate. Iat cum ne propunem s arate acest program n timpul execuiei:

163

Descrierea ei este dat mai jos:


VERSION 2.00 Begin Form Test Caption = "Test grila" ClientHeight = 5160 ClientLeft = 1290 ClientTop = 1380 ClientWidth = 8520 Height = 5565 Left = 1230 LinkTopic = "Form1" ScaleHeight = 5160 ScaleWidth = 8520 Top = 1035 Width = 8640 Begin CommandButton Buton Caption = "Start" Height = 375 Left = 2400 TabIndex = 7 Top = 1800 Width = 1335 End Begin SSFrame Intrebare Caption = "Intrebare"

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

(iniial avem o imagine dintr-un fiier question.wmf, ulterior se schimb)

17.2. Textul explicat al programului


Pentru a nelege cum funcioneaz programul nostru, trebuie s considerm un fiier de test drept exemplu:
Din ce oras este imaginea alaturata? bacau.bmp i din Timisoara c din Bacau i din Bucuresti i din Sibiu i din Suceava Ce se vede in imaginea alaturata? elefant.bmp i un lup c un elefant c un mamifer

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

Test de circulaie rutier

18.1. Prezentare general


n vederea trecerii cu succes de proba sal la examenul pentru obinerea permisului auto, ai consultat, probabil, att legislaia necesar, ct i cri sau teste de circulaie corespunztoare. Aplicaia pe care v-o propunem n continuare v poate i ea ajuta n a nva mai repede semnificaia semnelor de circulaie.

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

2 'Center 1 'Fixed Single "0" 255 1800 4 840 735

1 'Fixed Single 1455 2 2640 3 3840 2655

1 'Fixed Single 1455 1 2640 2 2040 2655

1 'Fixed Single 1455 0 2640 1 240 2655

1 'Fixed Single 1695 2 5400 -1 'True 3720 1575

'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

= = = = = =

1695 1 5400 -1 'True 1920 1575

= = = = = = =

1 'Fixed Single 1695 0 5400 -1 'True 120 1575

= = = = = =

1695 2 120 -1 'True 3720 1575

= = = = = =

1695 1 120 -1 'True 1920 1575

= = = = = =

1695 0 120 -1 'True 120 1575

n figura urmtoare sunt puse n eviden aceste controale:

174

Label punctaj

Image (0

Image 2) CommandButton btnStop Label Raspuns(0..2

CommandButton b t

18.2. Textul explicat al programului


Programul are nevoie de mai multe fiiere. Unul din ele este o iconi care se folosete pentru operaia de drag and drop:

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).

Iat un exemplu concret, care conine doar dou triplete:


so7 Intersectie cu sens giratoriu obligatoriu # su13 Intoarcerea interzisa # es22 Presemnalizarea unei intersectii cu sens giratoriu obligatoriu # si4 Prioritate pentru circulatia din sens invers # si6 Prioritate fata de circulatia din sens invers # su30 Terminarea zonei in care depasirea este interzisa #

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:

Pentru primul triplet de ntrebri Pentru al doilea triplet de ntrebri

SO7.BMP

SU13.BMP

ES22.BMP

SI4.BMP 176

SI6.BMP

SU30.BMP

Declaraiile de variabile globale sunt urmtoarele:


Dim Dim Dim Dim Dim Dim ordine(0 To 2) As Integer perm(6, 3) s$(0 To 2) cale As String p As Integer nr As Integer

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

O subrutin interesant i deosebit este urmtoarea:


Sub casuta_DragDrop (Index As Integer, Source As Control, X As Single, Y As Single) If Source.Tag <> -1 Then casuta(Index).Picture = Source.Picture Source.Picture = LoadPicture() casuta(Index).Tag = Source.Tag Source.DragMode = 0 Source.Tag = -1 nr = nr - 1 If nr = 0 Then bine = True For i = 0 To 2 If casuta(i).Tag <> ordine(i) Then bine = False End If Next i If bine Then p = p + 1 punctaj.Caption = p buton.Enabled = True buton.SetFocus

179

End If End If End Sub

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

Sub btnStop_Click () End End Sub

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:

Text Text Text

Command Avem urmtoarele proprieti ale acestei forme:


Begin Form frmDate BorderStyle=3'Fixed Double Caption="Date despre persoana" LinkTopic="Form1" Visible=0 'False

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

19.2. Textul explicat al programului


Fiierul MDIBIO.BAS Acest fiier cuprinde urmtoarele declaraii:
Option Explicit Global Global Global Global Global Global Global Global Global Const modal = 1 Const CASCADE = 0 Const TILE_HORIZONTAL = 1 Const TILE_VERTICAL = 2 Const ARRANGE_ICONS = 3 nume_pers As String data_nast As String data_ref As String prima_data As Integer

Global Z(12) Global Sters() As Integer Global Persoana() As New frmBioritm

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

... sau acest lucru se cere explicit, din meniu:


Sub mnuFExit_Click () End 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

nchiderea unei ferestre nseamn, de fapt, i marcarea ei ca fiind tears:


Sub Form_Unload (Cancel As Integer) Sters(Me.Tag) = True End Sub

b) Terminarea programului, care presupune nchiderea ferestrei printe, frmMDI:


Sub mnuFExit_Click () Unload frmMDI End Sub

c) Crearea unei noi ferestre copil:


Sub mnuFNew_Click () frmMDI.Enabled = False frmDate.Show End Sub

d) Aranjarea iconielor corespunztoare ferestrelor minimizate:


Sub mnuWArrange_Click () frmMDI.Arrange ARRANGE_ICONS

193

End Sub

Figura urmtoare prezint cum se realizeaz acest lucru:

e) Aranjarea n cascad a ferestrelor copil deschise i neminimizate:


Sub mnuWCascade_Click () frmMDI.Arrange CASCADE End Sub

f) Aranjarea n sistem crmizi, dispuse pe orizontal, a ferestrelor copil deschise i neminimizate:


Sub mnuWTile_Click () frmMDI.Arrange TILE_HORIZONTAL End Sub

Iat cum poate arta acest lucru pentru dou ferestre:

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

20.1. Prezentare general


Privii cu atenie figura de mai jos. Ea reprezint o aplicaie care v permite realizarea hrii (turistice a) unui ora.

acesta este simbolul ateneului

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

240 1 480 3495

"Scrieti denumirea si apoi apasati 255 240 0 120 3495

Label1

Text1

20.2. Textul explicat al programului


S trecem acum la implementarea programului. Vom ncepe cu: Fiierul HARTA.BAS Aici avem doar descriereaunui tip de date pentru memorarea strzilor. O strad este vzut ca un segment de dreapt, cu extremitile de coordonate (X1,Y1) i (X2,Y2) i avnd un anumit tip. Dac tip=1, atunci strada este subire, iar dac tip=2, atunci strada este groas.
Type TStrada X1 As Single Y1 As Single X2 As Single Y2 As Single tip As Integer End Type

200

Fiierul HARTA.FRM n acest fiier avem urmtoarele declaraii generale:


Dim Dim Dim Dim Dim Dim Dim n As Integer 'numarul de ob-uri ns As Integer ' numarul de strazi x_min As Single first_x, first_y desen As Integer Strada() As TStrada rap_x, rap_y, x_0, y_0

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

Figura de mai jos prezint modul de afiare a semnificaiei pictogramei cu pachetul:

204

X_min

pictograma cu pachetul, care reprezint un magazin

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

tergerea tututor strzilor se realizeaz apelnd din meniu, comanda corespunztoare:


Sub mnuClear_Click () ns = 0: ReDim Strada(0) Form_Paint 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

Form_Resize apeleaz, la rndu-i, subrutina Form_Paint descris anterior:


Sub Form_Resize () Form_Paint End Sub

Restul subrutinelor prezentate mai jos:

(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

S-ar putea să vă placă și