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

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

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

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
memZodia

imgZodia

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

txtCant
lstAlimente

lstRetete
cmdAdauga
cmdElimina
cmdRetete (invizibil)

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

'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

Begin Label Label3


Alignment = 2
Caption
= "60"
End

MMControl
Sunet

Begin Label Label2


Alignment=2
'Center
Caption

Begin Label Label4


Alignment= 2 'Center
Caption = "Secunde
ramase"
d

Begin Label Label1


Alignment= 2 'Center
Caption = "0"
End
Timer
i
1(4)

Sub fiecare imagine din cele cinci am pus cte un cronometru.


Astfel, avem un vector cu cinci cronometre, numit Timer1. Fiecare
din acestea are proprietatea Interval egal cu intervalul de timp
ct rezist pe ecran musca din imaginea corespunztoare.

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

144

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

= 1) Or
= 1) Then

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

un meniu cu dou opiuni:


Begin Menu mnuDespre
Caption
=
End
Begin Menu mnuCe
Caption
=
End

285
2880
1
"3"
120
615

"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

Shape Paleta
Spiner Spiner3

Spiner

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")
Image Imagine

SSFrame

(iniial avem o imagine


dintr-un fiier
question.wmf, ulterior
se schimb)

SSCheck

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:

ELEFANT.BMP

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

SI6.BMP

SU30.BMP

176

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
Label3

Label2

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)

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

sunt

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