8.) Írjunk Unit-ot, mely tartalmazza a Boritek program eljárásait.

 

Ha jól megnézzük, a Boritek programban deklarált eljárásokat, azok bizony olyanok, hogy több programban is használhatónak látszanak. Hogyan lehetne azt elérni, hogy minden program elejére ne kelljen újra és újra ezeket az eljárásokat leírni, de használni mégis lehessen őket. Erre találták ki a Pascal fejlesztői a Unit-okat. Erről már a Crt unit kapcsán szó volt, de nemcsak a fejlesztők által megírt unitok létezhetnek, mi magunk is írhatunk unitokat. Ez lesz számunkra a kód-újrafelhasználás leggyakoribb példája. Nézzük, hogyan néz ki egy Unit felépítése:

 

Unit CrtPlus;

InterFace

Uses NewDelay, Crt;

Implementation

End.

 

A unit neve most CrtPlus, mely kötelezően meg kell, hogy egyezzen a DOS nevével. A névválasztás arra utal, hogy a standard Crt Unit kiegészítését célozza. Az InterFace kulcsszó után kell beírni a Unit-ban használt unitok nevét, valamint a Unit azon eljárásainak a nevét, amelyet a használó programból el szeretnénk érni. Az Implementation szó után kell elhelyezni az eljárások kifejtését. Ezt mutatja a teljes lista:

 

Unit CrtPlus;

InterFace

Uses NewDelay, Crt;

  Procedure WriteXY(X,Y: Byte; Sz: String);

  Procedure VVonal(Xk,Xv,Y: Byte);

  Procedure FVonal(X,Yk,Yv: Byte);

  Procedure Keret(Bfx,Bfy,Jax,Jay: Byte);

Implementation

Procedure WriteXY(X,Y: Byte; Sz: String);

Begin

  GoToXY(X,Y);

  Write(Sz);

End;

Procedure VVonal(Xk,Xv,Y: Byte);

Var I: Byte;

Begin

  For i:= Xk To Xv Do

  WriteXY(I,Y,Chr(196));

End;

Procedure FVonal(X,Yk,Yv: Byte);

Var I: Byte;

Begin

  For I:= Yk To Yv Do

  WriteXY(X,I,Chr(179));

End;

Procedure Keret(Bfx,Bfy,Jax,Jay: Byte);

Begin

  VVonal(Bfx+1,Jax-1,Bfy);

  VVonal(Bfx+1,Jax-1,Jay);

  FVonal(Bfx,Bfy+1,Jay-1);

  FVonal(Jax,Bfy+1,Jay-1);

  WriteXY(Bfx,Bfy,Chr(218));

  WriteXY(Jax,Bfy,Chr(191));

  WriteXY(Jax,Jay,Chr(217));

  WriteXY(Bfx,Jay,Chr(192));

End;

 

End.

 

Mint látható, a listát két End zárja, egyik az utolsónak leirt eljárás End-je, a másik a Unit End-je. Unit csak önállóan szerkeszthető, programon belül nem. Használata előtt még le kell fordítani, méghozzá lemezre. Ezt a Compile menü Destination almenüpontjában állíthatjuk be. A lemezen nem *.exe, hanem *.tpu állomány jön létre. (TPU: Turbo Pascal Unit).

 

 

A meglévő Unit-unkat bővítsük még a következőkkel: gyakran kell a háttér és a betűszint beállítani, lehessen ezt egy Szinek(HSzin,KSzin) eljárással megadni.

 

Procedure Szinek(HSzin,KSzin: Byte);

Begin

  TextBackGround(HSzin);

  TextColor(KSzin);

End;

 

Illesszük a Szinek eljárást a Unitba első eljárásként:

 

Unit CrtPlus;

InterFace

Uses NewDelay, Crt;

  Procedure Szinek(HSzin,KSzin: Byte);

  Procedure WriteXY(X,Y: Byte; Sz: String);

  Procedure VVonal(Xk,Xv,Y: Byte);

  Procedure FVonal(X,Yk,Yv: Byte);

  Procedure Keret(Bfx,Bfy,Jax,Jay: Byte);

Implementation

Procedure Szinek(HSzin,KSzin: Byte);

Begin

  TextBackGround(HSzin);

  TextColor(KSzin);

End;

...

End.

 

A továbbiakban, ha olyan eljárást írunk, amely több programban is használható, újra így járjunk el. Ne felejtsük újra lemezre lefordítani a Unit-ot. Nézzük meg, a CrtPlus segítségével hogyan néz ki a Boritek listája:

 

Program Boritek;

Uses NewDealay, Crt, CrtPlus;

Begin

  TextMode(CO80);

  Szinek(Blue,Yellow);

  ClrScr;

  Keret(1,1,80,24);

  Keret(69,2,78,6);

  Keret(4,17,15,19);

  Keret(55,21,57,23);

  Keret(59,21,61,23);

  Keret(63,21,65,23);

  Keret(67,21,69,23);

  VVonal(7,30,3);

  VVonal(7,25,5);

  VVonal(20,60,12);

  VVonal(55,76,17);

  VVonal(55,74,19);

  WriteXY(2,2,'F.a.:');

  WriteXY(6,18,'AJÁNLOTT');

  WriteXY(71,4,'BÉLYEG');

  GotoXY(1,25);

  ReadLn;

End.

 

Unit-unk eljárásait tehát csak akkor használhatjuk, ha magát a Unit-ot használatba vettük. Ez történt a program második sorában: Uses Crt, CrtPlus;.

 

9.) Irjunk programot, melyet futtatva a gép véletlen méretű és szinű keretes, cimkés, árnyékos ablakokat rajzol a képernyőre, közben változó magasságú hangot ad. A program futása bármely billentyű megnyomására álljon le.

 

Gyakran fordul elő felhasználói programokban, hogy a képernyőn keretes ablak jelenik meg, melyben a gép valamire figyelmeztet, esetleg valamilyen választást kínál fel, vagy menüt. Szépen megírt programjainkban nekünk is szükségünk lehet ilyen ablakokra többször is, így célszerű az ablak eljárást a CrtPlus Unit-unkban megírni. Hogyan is néz ki egy ablak? Már rajzoltunk téglalapot is, keretet is. Ha ezeket „egymásra” helyezzük, akkor keretes ablakot kapunk, legyen az ablaknak címkéje, és igény szerint árnyéka is. Helyezzük ezt az eljárást utolsó helyre a CrtPlus-ban.

 

Unit CrtPlus;

InterFace

Uses NewDelay, Crt;

  ... 

  Procedure Ablak(HSz,KSz,Bfx,Bfy,Jax,Jay: Byte; Arny: Boolean; C: String); 

Implementation

  ...

Procedure Ablak(HSz,KSz,Bfx,Bfy,Jax,Jay: Byte; Arny: Boolean; C: String);

Var Px,Py: Byte;

Begin

  If Arny Then

  Begin

    Px:= Jax+2;

    Py:= Jay+1;

    If Px>80 Then Px:= 80;

    If Py>25 Then Py:= 25;

    Window(Bfx+2,Bfy+1,Px,Py);

    Szinek(Black,Black);

    ClrScr;

  End;

  Window(Bfx,Bfy,Jax,Jay);

  Szinek(HSz,KSz);

  ClrScr;

  Window(1,1,80,25);

  Keret(Bfx+1,Bfy,Jax-1,Jay);

  If C<>'' Then WriteXY(Round(Bfx+(Jax-Bfx-Length(C))/2-1),Bfy,' '+C+' ');

End;

 

End.

 

Az Ablak eljárás készítésekor szükség van egy tesztprogramra, melyet minden módosítás után futtathatunk, és így láthatjuk a kialakítás helyes menetét.

 

Program AblTeszt;

Uses NewDelay, Crt, CrtPlus;

Begin

  TextMode(CO80);

  Szinek(Cyan,White);

  ClrScr;

  Ablak(15,0, 20,5,60,15, True, ’Ablak’);

  GoToXY(1,25);

  ReadLn;

End.

 

 

Az Ablak eljárás számos új dolgot tartalmaz.  Az első mindjárt az Arny: Boolean deklaráció. A feladat azt kéri, hogy lehessen az ablaknak árnyéka is, azaz, az eljárást ilyen szempontból lehessen kétféleképpen meghívni, árnyékkal és árnyék nélkül is. Ezt úgy érhetjük el, hogy az eljárás paraméterei közé felveszünk egy logikai változót, ez az Arny, melyet a Pascal Boolean típusnak nevez. Egy logikai változó két értéket kaphat, illetve vehet fel, ezek: True vagy False, azaz Igaz vagy Hamis. Az eljárásunknak biztosan lesz egy olyan szakasza, melyet a szerint kell vagy nem végrehajtani, hogy kell-e árnyék vagy nem. Mivel az árnyék egy, az ablakhoz képest jobbra és lefelé eltolt fekete téglalap, ezért ezt kell hamarabb megrajzoltatni az eljárásban. Az eljárás tehát ezzel kezdődik, és itt a második új dolog az If ThenElse szerkezetű programszakasz. A lista utasításainak végrehajtási sorrendjét (a szekvenciát), eddig a For ciklus (iteráció) változtatta meg. De az IfThen … szerkezet, vagyis a szelekció egyik fajtája is meghatározhatja. Az If azt jelenti: ha, a Then: akkor. Az If-fel kezdődő programrészletet így olvashatjuk: Ha igaz, hogy kell árnyék, akkor a következőt tedd: Begin End;. A Then is csak egy utasításra vonatkozik, mint a Do, ezért, ha több mindent kell tenni, akkor összetett utasítást kell alkalmaznunk. Az Else, ami egyébként-et jelent itt nem alkalmaztuk, mert nem volt rá szükség, az árnyék helyett nem kellett valami mást rajzolni. Lesz majd a későbbiekben olyan program, amelyben szükség lesz rá. Maga az árnyékrajzolás nagyon egyszerű, kiolvasható a jobbra kettővel, lefelé eggyel való ablakeltolás, a fekete háttérszín és írásszín beállítása. (A későbbiekben lehet, hogy ezt nem árt pontosítani, megvizsgálni, hogy az így keletkezett Window ablak megnyitható-e, mert ha nem, akkor a ClrScr másra vonatkozhat. De most ezzel nem bonyolítjuk az eljárásunkat.) Az eljárás további része az ablakot rajzolja. Először megnyitja a megfelelő ablakot, és a megadott színnel törli, majd visszaállítja a teljes képernyőt és a megnyitott ablak területre egy szűkebb keretet rajzol (+1 és –1 értékek), így lesz keretes ablak a képernyőn. Még az utolsó sor is tartogat újdonságokat. Ez csak akkor hajtódik végre, ha a C címke nem üres szöveg. A címkét a keretre, annak felső részére, sőt középre illik helyezni. Ezt a pozíciót határozza meg a WriteXY első paraméterében leírt kifejezés. A kiírás helye az ablak (közepe-címke hossza)/2. A C címke hosszát a Length függvény határozza meg, mely természetesen bármely szöveges változóra alkalmazható. (A szöveges változókra vonatkozó műveletekkel, eljárásokkal és függvényekkel egy külön szakasz foglalkozk.) A kettővel való osztás a változók típusát valósra állítja, a WriteXY viszont Byte-ot, azaz egészet vár, ezért a Round függvény, mely a benne lévő kifejezést kerekíti és egész típusúra változtatja. Azért, hogy a címke betűjéhez a keretvonal ne érjen hozzá előtte és utána még egy Space-t is írtunk, ez eggyel csökkenti a kiírás X koordinátáját. A kiírandó szöveget összeadással, a String típusú változók között értelmezett egyetlen művelettel oldottuk meg. Elkészült az ablak eljárásunk, most már nézhetjük a kitűzött feladatot. A véletlen számok előállításának megismerésére, először csak véletlen számokat (például lottószámokat) kiíró programot fogunk írni.

 

Program Ablakok;

Uses NewDelay, Crt, CrtPlus;

Begin

  TextMode(CO80);

  ClrScr;

  Repeat

    Write(Random(90)+1:3);

    Delay(300);

  Until KeyPressed;

End.

 

Megfigyelhetjük, hogy Randomize nélkül minden gép, mindig ugyanazon számokat adja. A +1 a random függvény meghívása után a megfelelő intervallum beállítására szolgál. Használjuk most már az ablak paramétereinek előállítására a Random függvényt.

 

Program Ablakok;

Uses NewDelay, Crt, CrtPlus;

Begin

  TextMode(CO80);

  Repeat

    Ablak(Random(8), Random(16), Random(80)+1, Random(25)+1,

          Random(80)+1, Random(25)+1, true, ’’);

    Sound(100*Random(80));

    Delay(150);

  Until KeyPressed;

  NoSound;

End.

 

A program természetesen használja a Crt és CrtPlus unitot is. Azt, hogy egy programrészletet akárhányszor, azaz bizonytalan sokszor ismételjen a program, nem lehet (vagy nem célszerű) a már ismert ismétlő eljárással, a For ciklussal megoldani. Olyan ismétlő eljárás kellene, amely leállító feltétele tőlünk, egy programon kívüli tevékenységtől függ. Ilyen ismétlő eljárás a Repeat Until. Repeat: ismételd, Until: mindaddig, míg nem. Az ismétlésre szánt programrészletet a két kulcsszó közé, természetesen bekezdésesen kell leírni. Az ismétlő eljárás leállító feltételét az Until után kell írni. Az ismétlés mindaddig folytatódik, ameddig a feltétel igazzá nem válik. Mivel a feltétel az ismételendő programrészlet után van, vagyis ez egy hátul tesztelő ciklus, így a ciklusmag legalább egyszer végrehajtódik. A feltétel valamilyen logikai kifejezés, vagy függvény, esetleg konstans. Most a leállítás a billentyűzeten való írás, ezért olyan logikai függvény kell, amely hamis, ha nem nyomtak meg billentyűt, (azaz a billentyűzet puffer üres), és igaz ellenkező esetben. Ez a logikai függvény a KeyPressed (= kulcs megnyomva).

 

Az ismétlés három eljárás hívására vonatkozik: Ablak, Sound és Delay. Mindhárom tartalmaz ismeretlen dolgokat. Az Ablak paramétereinek véletlen értékeket kell tartalmaznia. Véletlen számok előállítására a Random függvény alkalmas. Paraméteresen használva: Random(n), a paramétere egész, és visszaadott értéke 0 és n-1 közötti egész szám. Paraméter nélkül a visszaadott érték 0 és 1 közötti valós szám. Ahhoz tehát, hogy 1 és 80 közötti számot kapjunk a Random(80)-hoz még hozzá kell adni 1-et. (Ha 50 és 60 közötti véletlen számot akarunk, akkor Random(60-50+1)+1 a helyes függvényhívás.) Az ötödik és hatodik paraméter a háttér és karakterszín, ezért 8 és 16 a két argumentum. A hetedik paraméter az árnyék kérését szabályozza, mivel True, lesz árnyék. A nyolcadik paraméter, mint címke, most még üres. A kész programban nem lesz az. A következő eljárás a Sound. Ez a gép hangszórójából olyan frekvenciájú hangot szólaltat meg, amilyen egész számot adunk meg paraméterként. A változatosságot a Random biztosítja, a 100 szorzó az egymás után következő hangok közötti különbséget, illetve hallhatóságot. Az ismétlődő rész utolsó eljárása a Delay, amely várakozás jelent, a várakozás időtartamát az argumentum adja úgy, hogy a beirt érték ezredmásodperceket jelent. Ennek az eljárásnak a paramétere határozza meg a program futásának a gyorsaságát. Ha az értéket növeljük, lassabban rajzolódnak az ablakok, és egy-egy hangot tovább hallunk. A program utolsó eljárása a Nosound, mely a hangadást leállítja. Erre azért van szükség, mert a hangszóró egy periféria, a leállításáról ugyanúgy gondoskodni kell, mint az elindításáról. Ha ezt nem tennénk meg, a hangszóró addig szólna, ameddig a gépet ki nem kapcsolnánk, vagy egy másik program le nem állítaná.

 

 

 

 

Gondolhatnánk, hogy ezzel a feladatot megoldottuk. De azért ez nem teljesen van így. Ha jól megnézzük a képernyőt futás közben, bizony furcsa dolgokat látunk, pl.: változik a hang, a képernyő viszont nem. Ez hogy lehet? Miért nem rajzol néha a program. Hát azért nem, mert az Ablak eljárást vizsgálat nélküli véletlen paraméterekkel hívtuk meg. Így előfordul, hogy nem tudja a gép az ablakot felrajzolni. A következő változat ezen próbál segíteni. Azt kellene elérni, hogy a véletlen értékek nagyságrendje megfeleljen a hívási feltételeknek. Ezért a választás után még meg is kell vizsgálni. Ez csak úgy lehetséges, ha az értékeket változókba mentjük, sőt a cseréhez még egy ötödik változó is kell, ez a Puf nevű. A másik gond az, hogy így mindig ugyanazon értéksor szerint jönnek létre az ablakok, akárhányszor indítjuk el a programot, sőt akármelyik gépen. Azért, hogy a véletlen, valóban véletlen legyen, a véletlen függvény meghívása előtt meg kell hívni a Randomize eljárást. Ezt láthatjuk a Repeat előtt.

 

Program Ablakok;

Uses NewDelay, Crt, CrtPlus;

Var Bfx,Bfy,Jax,Jay,Puf: Byte;

Begin

  TextMode(CO80);

  Randomize;

  Repeat

    Bfx:= Random(80)+1;

    Bfy:= Random(25)+1;

    Jax:= Random(80)+1;

    Jay:= Random(25)+1;

    If Bfx>Jax Then

    Begin

      Puf:= Bfx;

      Bfx:= Jax;

      Jax:= Puf;

    End;

    If Bfy>Jay Then

    Begin

      Puf:= Bfy;

      Bfy:= Jay;

      Jay:= Puf;

    End;

    Ablak(Random(8),Random(16),Bfx,Bfy,Jax,Jay, true, ’’);

    Sound(100*Bfx);

    Delay(150);

  Until KeyPressed;

  NoSound;

End.

 

 

A véletlen értékek tehát a Bfx, Bfy, Jax, Jay változókba kerültek. Külön teljesülni kell, hogy Bfx<Jax és Bfy<Jay. Ha ez nem így van, akkor a program a két If utáni összetett utasításban az értékekeket felcseréli. A cserét úgynevezett ciklikus cserével oldja meg. A Puf változó a valódiak ideiglenes tárolására lettek deklarálva. Vegyük figyelembe azt is, hogy a Bfx, Bfy maximális értéke kisebb mint 80 illetve kisebb mint 25, a Jax és Jay minimális értéke pedig nagyobb mint 1. Ezen korlátozások miatt a random függvény argumentumát rendre 76,22,78,23-ra kell módosítani, illetve a Jax és Jay esetén a korrigáló érték 1-ről háromra növekszik. Ha most futtatjuk a programot, akkor minden ciklusban látható változás a képernyőn. Már csak egy dolog van hátra, a címke. Legyen az ablakok címkéje a létrejöttük sorszáma. A végleges program:

 

Program Ablakok;

Uses NewDelay, Crt, CrtPlus;

Var Bfx,Bfy,Jax,Jay,Puf: Byte;

    I: Integer;

    Sz:String;

Begin

  TextMode(CO80);

  Randomize;

  Repeat

    Inc(I);

    Bfx:= Random(76)+1;

    Bfy:= Random(22)+1;

    Jax:= Random(78)+1;

    Jay:= Random(23)+1;

    If Bfx>Jax Then

    Begin

      Puf:= Bfx;

      Bfx:= Jax;

      Jax:= Puf;

    End;

    If Bfy>Jay Then

    Begin

      Puf:= Bfy;

      Bfy:= Jay;

      Jay:= Puf;

    End;

    Str(I,Sz);

    Ablak(Random(8),Random(16),Bfx,Bfy,Jax,Jay,True,Sz);

    Sound(100*Bfx);

    Delay(150);

  Until KeyPressed;

  NoSound;

End.

 

Most is látunk újdonságot. Az I típusa: Integer, azaz egész. Értéke -32768 –től +32767-ig változhat most azért, hogy lehetőségünk legyen akár több száz, vagy ezer ablakot is a képernyőre rajzolni, illetve eddig számolni. A következő az Inc eljárás, melyet I-re alkalmazva annyit tesz, hogy értékét 1-el növeli, azaz számol. Az I változóban az ablak sorszáma lesz tárolva, tehát ezt kell szövegként az ablak eljárásnak átadni, és itt az utolsó újdonság, a számot String kell alakítani. Ezt végzi az Str eljárás, melynek első paramétere egy szám, második paramétere az a String, amelybe a számot String-ként írja, itt Sz. A keltett hangot, az egyébként is véletlenül létrejövő Bfx segítségével határoztuk meg. Ezzel a feladatot maradéktalanul megoldottuk.