23. Riešenie úloh zo skúšky


posledná zmena: 10.12.2002

Banner Text 10.12.2002

Obsahom dnešnej prednášky je riešenie 2 úloh zo skúšky v školskom roku 2001/2002. Odporúčam, aby ste si preriešili čo najviac minuloročných úloh. Zadania sú na stránke Skúška.

1. príklad: sovy poštárky

Napíšte program, pomocou ktorého sa budeme môcť zahrať nasledujúcu hru:

  • Harry Potter má dostať pozvánku do čarodejníckej školy. Ako je tu zvykom, listy od čarodejníkov prinášajú sovy. Muklovia, u ktorých Harry býva, tušia, že v tomto liste bude pre Harryho niečo dôležité a preto sa snažia tento list zlikvidovať. Ak by sa totiž aspoň jeden list dostal do rúk Harryho, odíde od nich. Pani Muklová chytila metlu a snaží sa pozametať všetky listy pre Harryho, ktoré prinesú sovy. Ak nejaký list dopadne na úplný spod obrazovky, pani Muklová tam metlou nedočiahne, list sa dostane Harrymu a teda pani Muklová bude nešťastná.
  • Hracia plocha je veľkosti aspoň 600x500 pixelov a celá je pokrytá (vykachličkovaná) bitmapou dlazka.bmp. V hornej časti plochy (nie nižšie ako 100 pixelov) náhodne prilietajú sovy buď zľava doprava alebo sprava doľava. Sova je animovaná dvoma bitmapami sova1.bmp a sova2.bmp. Sova drží v pazúroch list (bitmapa list.bmp) a počas letu niekde, nie bližšie ako 100 pixelov od okraja plochy, list pustí. Takýto list pomaly padá smerom nadol plochy. Ak sa dostane aspoň 50 pixelov od spodného okraja plochy, hra končí a vypíše sa správa, že list sa nám nepodarilo odstrániť a dostal sa k Harrymu.
  • V ploche sa nachádza metla (bitmapa metla.bmp), ktorou môžeme zametať padajúce listy. Metlu riadime pohybmi myši (zrejme v udalosti onMouseMove), pričom musíme zabezpečiť, aby sa táto nedostala vyššie ako 200 pixelov od horného okraja a nižšie ako 50 pixelov od dolného okraja plochy. Keď sa metlou dotkneme nejakého padajúceho listu, tak tento zmizne. Pozíciou metly rozumieme stred dolného okraja obrázka metly a dotyk s listom označuje napr., že metla je od listu vzdialená aspoň 20 pixelov.
  • Ak hráč zvládne pozametať nejaký konkrétny počet listov (napr. konštanta max=100), ďalšie sovy s listami už neprilietavajú a hra končí pochvalnou správou.
  • Všetky bitmapy (okrem dlazka.bmp) majú priesvitné časti označené farbou biela.
  • Sovy sa objavujú v náhodných intervaloch a naraz by ich nemalo byť v ploche viac ako 10. Lietajú rôznou rýchlosťou a v náhodnej výške. Krok pohybu nech je náhodné číslo napr. od 8 do 16. Pri každom tiknutí časovača sa vykreslia inou fázou animácie. Kým list putuje so sovou, má zrejme tú istú rýchlosť. Keď ho sova pustí, zmení sa jeho smer pohybu na zvislý a nastaví sa mu nejaká náhodná rýchlosť.
  • Časovač nastavte napr. na 100 milisekúnd. Pri každom tiknutí časovača zrealizujte všetky pohyby a prekreslite celú plochu:
    • pohnite sovami (odletené sovy z plochy odstráňte z evidencie)
    • ak má nejaká sova pustiť list, tak tento presuňte do poľa padajúcich listov
    • pohnite všetkými listami (môžete testovať, či nejaký nepreletel do dolnej časti)
    • pripadne vygenerujte novú sovu s listom
    • vytvorte pozadie (z bitmapy dlazka.bmp)
    • nakreslite všetky sovy, listy a na záver metlu
  • Pri pohybe myši zabezpečte korektné odstránenie pozametaných listov z evidencie.
  • Vo vašom programe zadefinujte triedy THra, TList a TSova v samostatnej programovej jednotke (napr. Unit2.pas), pričom sa tento unit môže odvolávať (uses) len na štandardné delphi-unity a nesmú v ňom byť definované žiadne globálne premenné. Štruktúra týchto tried by mala byť približne takáto:
    • TList = class
        ...
        bmp:TBitmap;
        constructor Create(...);
        procedure kresli(...);
        procedure posun;
      end;
      
      TSova = class
        ...
        bmp:array[0..1] of TBitmap;    // dve fázy animácie
        list:TList;                    // list, ktorý práve nesie
        constructor Create(...);
        procedure kresli(...);
        procedure posun;
      end;
      
      THra = class
      private
        ...
        s:array of TSova;              // zoznam lietajúcich sov
        l:array of TList;              // zoznam padajúcich listov
        procedure kresli;
      public
        constructor Create(...);
        procedure tik(...);            // volaná z časovača
        procedure mys(...);            // volaná pri onMouseMove
      end;
  • túto štruktúru si môžete prispôsobiť podľa vlastných potrieb, resp. môžete vytvoriť nové triedy. Trieda THra nesmie mať definované žiadne public stavové premenné.
  • Váš program s hlavným formulárom zrejme zadefinuje jednu premennú - inštanciu triedy THra a pracovať s ňou bude len pomocou jej metód.

Súbory na testovanie môžete stiahnuť z udaje.zip

// unit1.pas
//       obsahuje: Image1 a Timer1 s intervalom 100 milisekúnd

uses
  HraUnit;

var
  h:THra;

procedure TForm1.FormCreate(Sender: TObject);
begin
  randomize;
  h:=THra.Create(Image1.Canvas,Image1.Width,Image1.Height);
  DoubleBuffered:=true;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  h.Free;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  h.tik;
end;

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  h.mys(X,Y);
end;

HraUnit.pas:

unit HraUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TList = class
    bmp:TBitmap;
    x,y,dx,dy:integer;
    constructor Create(xx,yy,ddx,ddy:integer);
    destructor Destroy; override;
    procedure kresli(c:TCanvas);
    procedure posun;
  end;

  TSova = class
    bmp:array[0..1] of TBitmap;    // dve fázy animácie
    list:TList;                    // list, ktorý práve nesie
    x,y,dx,p,f:integer;
    constructor Create(xx,yy,ddx,pp:integer);
    destructor Destroy; override;
    procedure kresli(c:TCanvas);
    procedure posun;
  end;

  THra = class
  private
    s:array of TSova;              // zoznam lietajúcich sov
    l:array of TList;              // zoznam padajúcich listov
    c:TCanvas;
    bmp,metla:TBitmap;
    mx,my,sir,vys,poc:integer;
    ok:boolean;
    procedure kresli;
  public
    constructor Create(cc: TCanvas; ssir, vvys: integer);
    destructor Destroy; override;
    procedure tik;
    procedure mys(xx,yy:integer);
  end;

implementation

const
  maxpoc=100;

{ TList }

constructor TList.Create(xx, yy, ddx, ddy: integer);
begin
  x:=xx; y:=yy; dx:=ddx; dy:=ddy;
  bmp:=TBitmap.Create;
  bmp.LoadFromFile('list.bmp');
  bmp.Transparent:=true;
end;

destructor TList.Destroy;
begin
  bmp.Free;
end;

procedure TList.kresli(c: TCanvas);
begin
  c.Draw(x-bmp.Width div 2,y-bmp.Height div 2,bmp);
end;

procedure TList.posun;
begin
  inc(x,dx); inc(y,dy);
end;

{ TSova }

constructor TSova.Create(xx, yy, ddx, pp: integer);
begin
  x:=xx; y:=yy; dx:=ddx; f:=0; p:=pp;
  bmp[0]:=TBitmap.Create;
  bmp[0].LoadFromFile('sova1.bmp');
  bmp[0].Transparent:=true;
  bmp[1]:=TBitmap.Create;
  bmp[1].LoadFromFile('sova2.bmp');
  bmp[1].Transparent:=true;
  list:=TList.Create(xx,yy+25,ddx,0);
end;

destructor TSova.Destroy;
begin
  bmp[0].Free; bmp[1].Free; list.Free;
end;

procedure TSova.kresli(c: TCanvas);
begin
  if list<>nil then list.kresli(c);
  c.Draw(x-bmp[f].Width div 2,y-bmp[f].Height div 2,bmp[f]);
end;

procedure TSova.posun;
begin
  f:=1-f; inc(x,dx); dec(p);
  if list<>nil then list.posun;
end;

{ THra }

constructor THra.Create(cc: TCanvas; ssir,vvys:integer);
begin
  c:=cc; sir:=ssir; vys:=vvys; ok:=false;
  bmp:=TBitmap.Create;
  bmp.LoadFromFile('dlazka.bmp');
  metla:=TBitmap.Create;
  metla.LoadFromFile('metla.bmp');
  metla.Transparent:=true;
  kresli;
end;

destructor THra.Destroy;
var
  i:integer;
begin
  bmp.Free; metla.Free;
  for i:=0 to high(s) do s[i].Free;
  for i:=0 to high(l) do l[i].Free;
end;

procedure THra.kresli;
var
  x,y,i:integer;
begin
  y:=0;
  while y<vys do begin
    x:=0;
    while x<sir do begin
      c.Draw(x,y,bmp); inc(x,bmp.Width);
    end;
    inc(y,bmp.Height);
  end;
  for i:=0 to high(l) do l[i].kresli(c);
  for i:=0 to high(s) do s[i].kresli(c);
  c.Draw(mx-metla.Width div 2,my-metla.Height,metla);
end;

procedure THra.mys(xx, yy: integer);
begin
  mx:=xx; my:=yy;
  if my<200 then my:=200;
  if my>vys-50 then my:=vys-50;
end;

procedure THra.tik;
var
  i,j,dx:integer;
begin
  if ok then exit;
  if (maxpoc=poc)and(l=nil)and(s=nil) then begin
    ok:=true; ShowMessage('hura');
  end;
  if (poc<maxpoc) and (length(s)<10) and (random(20)=0) then begin
    SetLength(s,Length(s)+1); dx:=8+random(8); inc(poc);
    if random(2)=0 then
      s[high(s)]:=TSova.Create(0,30+random(70),dx,10+random((sir-200) div dx))
    else
      s[high(s)]:=TSova.Create(sir,30+random(70),-dx,10+random((sir-200) div dx));
  end;
  j:=0;
  for i:=0 to high(l) do
    with l[i] do begin
      posun;
      if y>vys-20 then begin
        dy:=0; ok:=true; ShowMessage('zle je - nechytil si list');
      end;
      if sqr(mx-x)+sqr(my-y)<400 then Free
      else begin l[j]:=l[i]; inc(j); end;
    end;
  SetLength(l,j);
  j:=0;
  for i:=0 to high(s) do
    with s[i] do begin
      posun;
      if p=0 then begin
        SetLength(l,Length(l)+1);
        list.dx:=0; list.dy:=3+random(10);
        l[high(l)]:=list; list:=nil;
      end;
      if (x<0)or(x>sir) then Free
      else begin s[j]:=s[i]; inc(j) end;
    end;
  SetLength(s,j);
  kresli;
end;

end.

2. príklad: koráliky

Hermione - Harryho spolužiačke a najlepšej kamarátke sa rozsypali korále. Našťastie Harry pozná veľmi pekné kúzlo:

    "zoberie dlhú niť a prechádza jej začiatkom okolo jednotlivých korálikov - tieto sa samé navliekajú - postupne putujú až na koniec nite a tam sa ukladajú tesne vedľa seba"

    Napíšte program, v ktorom budeme môcť predviesť toto Harryho kúzlo.

  • Pri štarte programu sa v grafickej ploche na náhodných pozíciách vygeneruje 40 rôznofarebných guľôčok - korálikov.
  • Hráč má k dispozícii nekonečnú niť. Táto sa začne odvíjať po zatlačení ľavého tlačidla myši a odvíja sa počas ťahania myši - v grafickej ploche sa niť zobrazuje tenkou čiarou (t.j. pomocou myši kreslíme v ploche nejakú čiaru). Ak sa jej momentálny začiatok (na pozícii myši) ocitne nad nejakým korálikom, tento sa navlečie na nakreslenú niť tak, že poputuje zo svojej momentálnej pozície po dráhe niti až na jej voľný koniec. Navliekať sa môžu aj viaceré koráliky naraz a počas toho je možné ťahať niť ďalej.
  • Keď ukončíme ťahanie nite, t.j. pustíme ľavé tlačidlo myši, navlečené koráliky ešte doputujú na svoje pozície a my už môžeme začať ťahať novú niť a navliekať ďalšie koráliky. Raz navlečený korálik na nejakú niť, sa už znovu navliekať nedá (treba si to zapamätať napr. v nejakej množine).
  • Niť v programe reprezentujte dynamickým poľom bodov (array of TPoint), pričom počas ťahania nite si doňho zapamätávajte iba tie body, ktoré sú od svojho predchádzajúceho suseda vzdialené aspoň 10 grafických bodov (body, ktoré by boli veľmi nahusto, sa ignorujú). Navliekajúce sa koráliky potom skáču po týchto bodoch v opačnom poradí, pričom prvý korálik nite putuje do posledného bodu (do miesta, kde sme začali ťahať myš), druhý do predposledného miesta, tretí do predpredposledného atď. Zrejme na ďalšiu niť opäť putujú koráliky až na jej koniec.
  • Koráliky v programe reprezentujte korytnačkami triedy TKoralik (odvodenej od TKor), pričom všetky sú uložené v polymorfnom poli k triedy TVelaKor (nevytvárajte žiadne iné pole korytnačiek). Korytnačky "vizualizujte" farebným kruhom s polomerom 10 (zafarbené náhodne). Triedu TKoralik by ste mali zadefinovať približne takto:
    • type
        pole = array of TPoint;
      
        TKoralik = class(TKor)
          xy:pole;
          ...
          constructor Create;
          procedure start(pxy:pole);
          procedure pohni;
          procedure kresli;
        end;
  • každá korytnačka si zapamätať svoje pole bodov popisujúce časť nite, po ktorej putuje (metóda start dostane toto pole ako parameter vtedy, keď niťou prejdeme popri koráliku). Metóda pohni bude volaná z časovača (Timer), ktorý hýbe všetky navlečené koráliky - každý putuje po svojej dráhe (svojom poli xy). Keď už je korálik na svojej cieľovej pozícii, tak táto metóda nerobí nič.
  • Niť môžete kresliť pomocou nejakej pomocnej korytnačky, ktorá nie je inštanciou triedy TKoralik.
// unit2.pas
// obsahuje Image1 a Timer1 s intervalom 100

uses
  KorUnit, VelaKorUnit;

type
  pole = array of TPoint;

  TKoralik = class(TKor)
    xy:pole;
    ix:integer;
    treba:boolean;
    constructor Create(xx,yy:integer);
    procedure start(pxy:pole);
    procedure pohni;
    procedure kresli;
  end;

{ TKoralik }

constructor TKoralik.Create(xx,yy:integer);
begin
  inherited Create(xx,yy);
  PH; FP:=RGB(random(100)+128,random(100)+128,random(100)+128);
  treba:=true;
end;

procedure TKoralik.kresli;
begin
  with Form1.Image1.Canvas do begin
    Brush.Color:=FP;
    Brush.Style:=bsSolid;
    Pen.Color:=FP;
    Pen.Width:=1;
    Ellipse(round(x)-10,round(y)-10,round(x)+10,round(y)+10);
  end;
end;

procedure TKoralik.pohni;
begin
  if ix<0 then xy:=nil;
  if xy<>nil then begin
    ZmenXY(xy[ix].X,xy[ix].Y);
    dec(ix);
  end;
  kresli;
end;

procedure TKoralik.start(pxy: pole);
begin
  xy:=pxy; ix:=high(xy); treba:=false;
end;

/////////////////////////////////////////////////

var
  v:TVelaKor;
  nite:array of pole;
  tahaj:boolean;
  poc:integer;

procedure TForm1.FormCreate(Sender: TObject);
var
  i:integer;
begin
  zmaz; randomize;
  v:=TVelaKor.Create;
  for i:=1 to 40 do begin
    v.UrobKor(TKoralik.Create(random(sirka),random(vyska)));
  end;
  tahaj:=false;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  v.Free;
end;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  i:integer;
begin
  i:=Length(nite);
  SetLength(nite,i+1);
  SetLength(nite[i],1);
  nite[i,0].x:=X;
  nite[i,0].y:=Y;
  tahaj:=true; poc:=0;
end;

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
  k,i,j:integer;
begin
  if not tahaj then exit;
  i:=high(nite); j:=high(nite[i]);
  if sqr(nite[i,j].x-x)+sqr(nite[i,j].y-y)<100 then exit;
  inc(j); SetLength(nite[i],j+1); nite[i,j].x:=x; nite[i,j].y:=y;
  for k:=0 to v.pk-1 do
    if (poc<=j) and v.k[k].Blizko(X,Y) and TKoralik(v.k[k]).treba then begin
      TKoralik(v.k[k]).start(copy(nite[i],poc,maxint)); inc(poc);
    end;
end;

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  tahaj:=false;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  i:integer;
begin
  zmaz;
  with Image1.Canvas do begin
    Pen.Color:=clBlack; Pen.Width:=3;
    for i:=0 to high(nite) do Polyline(nite[i]);
  end;
  for i:=0 to v.pk-1 do
    TKoralik(v.k[i]).pohni;
end;

Nasledujúce príklady sú zo starších ročníkov. Korytnačie úlohy veľmi často využívajú tvar korytnačky, ktorý je ale v súčasnej triede TKor zrušený.

1. príklad: skladanie rozstrihaného obrázka

  • Napíšte program, ktorý nám umožní poskladať rozstrihaný obraz. Na začiatku hry je v ploche rozstrihaný nejaký veľký obrázok na rôzne veľké kúsky - obdĺžniky, pričom sa tieto kúsky vôbec neprekrývajú. Úlohou hráča je posúvaním týchto kúskov ťahaním myšou poskladať pôvodný obraz. Program mu pritom nedovolí, aby žiaden kúsok "vypadol" z plochy - okraje hracej plochy sú pri ťahaní prekážkou a teda ťahanie sa na ňom zastaví. Kúsky obrazu sa môžu v priebehu hry prekrývať.
  • Všetky vstupné informácie sú v textovom súbore obraz.txt, ktorý obsahuje:
    • v prvom riadku je meno bitmapového súboru s ešte nenastrihaným obrazom
    • v druhom riadku súboru sú dve čísla, ktoré definujú šírku a výšku hracej plochy (napr. komponentu triedy TImage) - v tejto ploche sa budú posúvať nastrihané kúsky obrazu
    • nasledujúce riadky súboru obsahujú po 6 celých čísel - sú to informácie o rozstrihaných kúskoch - obdĺžnikoch: prvé štyri čísla popisujú obdĺžnik v obraze a to súradnice x a y a veľkosť obdĺžnika: šírku a výšku; posledné dve čísla sú súradnice obdĺžnika umiestneného v hracej ploche
    • môžete predpokladať, že súbor je zadaný korektne a obraz je celý rozstrihaný na disjunktné obdĺžniky
    • na "vystrihnutie" obdĺžnika z obrazu môžete využiť metódu CopyRect alebo Draw
  • Kúsky obrazu v programe realizujte ako inštancie triedy TKusok, napr. s takouto deklaráciou (môžete niečo pridať, alebo mierne pozmeniť):
        TKusok = class
          bm:TBitmap;
          x,y,sirka,vyska:integer;
          constructor Create(...);
          destructor Destroy; override;
          procedure kresli(c:Tcanvas; ...);          // nakreslí ho aj s rámikom
          function klik(xx,yy:integer):boolean;      // zistí, či sa doň kliklo
          procedure posun(dx,dy:integer);
        end;
  • Samotnú hru realizujte triedou THra, ktorej deklarácia by mala vyzerať približne takto:
        THra = class
          k:array of TKusok;
          ...
          constructor Create(...);
          destructor Destroy; override;
          procedure pridaj(kk:TKusok);      // pridá nový kúsok do poľa
          procedure kresli(c:Tcanvas);      // nakreslí všetky kúsky
          procedure MouseDown(x,y:integer);
          procedure MouseMove(x,y:integer);
          procedure MouseUp;
        end;
  • THra si musí pamätať, ktorý kúsok sa momentálne ťahá myšou. Tento bude vykresľovaný ako posledný zo všetkých a teda bude nad všetkými ostatnými kúskami. Navyše bude počas ťahania orámovaný červeným rámikom (toto zabezpečia obe metódy kresli).
  • Program nemusí zisťovať, či už je celý obraz poskladaný.
  • Stavové premenné tried TKusok a THra môžete používať len v metódach týchto tried.

Súbory na testovanie môžete stiahnuť z skuska.zip

type
  TKusok = class
    bm:TBitmap;
    x,y,sirka,vyska:integer;
    constructor Create(bmp:TBitmap; xvbmp,yvbmp,nsirka,nvyska,nx,ny:integer);
    destructor Destroy; override;
    procedure kresli(c:Tcanvas; sel:boolean);
    function klik(xx,yy:integer):boolean;
    procedure posun(dx,dy:integer);
  end;

constructor TKusok.Create(bmp:TBitmap; xvbmp,yvbmp,nsirka,nvyska,nx,ny:integer);
begin
  bm:=TBitmap.Create;
  sirka:=nsirka; vyska:=nvyska; x:=nx; y:=ny;
  bm.Width:=sirka; bm.Height:=vyska;
{ bm.Canvas.CopyRect(Rect(0,0,sirka,vyska),bmp.Canvas,
                     Rect(xvbmp,yvbmp,xvbmp+sirka,yvbmp+vyska));}
  bm.Canvas.Draw(-xvbmp,-yvbmp,bmp);
end;

destructor TKusok.Destroy;
begin
  bm.Free;
end;

function TKusok.klik(xx,yy:integer):boolean;
begin
  Result:=(xx>=x) and (xx<x+sirka) and (yy>=y) and (yy<y+vyska);
end;

procedure TKusok.kresli(c:Tcanvas; sel:boolean);
begin
  c.Draw(x,y,bm);
  if sel then begin
    c.Brush.Style:=bsClear;
    c.Pen.Color:=clRed;
    c.Pen.Width:=3;
    c.Rectangle(x+1,y+1,x+sirka-1,y+vyska-1);
  end;
end;

procedure TKusok.posun(dx,dy:integer);
begin
  inc(x,dx); inc(y,dy);
end;

////////////////////////////////////////////////////////////////////////////////

type
  THra = class
    k:array of TKusok;
    x0,y0,sirkaPlochy,vyskaPlochy,sel:integer;
    constructor Create(subor:string);
    destructor Destroy; override;
    procedure pridaj(kk:TKusok);
    procedure kresli(c:Tcanvas);
    procedure MouseDown(x,y:integer);
    procedure MouseMove(x,y:integer);
    procedure MouseUp;
  end;

constructor THra.Create(subor:string);
var
  t:TextFile;
  s:string;
  bmp:TBitmap;
  x1,y1,sir,vys,x2,y2:integer;
begin
  AssignFile(t,subor); Reset(t);
  readln(t,s);
  bmp:=TBitmap.Create;
  bmp.LoadFromFile(s);
  readln(t,sirkaPlochy,vyskaPlochy);
  while not eof(t) do begin
    readln(t,x1,y1,sir,vys,x2,y2);
    pridaj(TKusok.Create(bmp,x1,y1,sir,vys,x2,y2));
  end;
  bmp.Free;
  CloseFile(t);
  sel:=-1;    // žiaden nie je oselektovaný
end;

procedure THra.pridaj(kk:TKusok);
begin
  SetLength(k,Length(k)+1);
  k[High(k)]:=kk;
end;

destructor THra.Destroy;
var
  i:integer;
begin
  for i:=0 to High(k) do
    k[i].Free;
end;

procedure THra.kresli(c:Tcanvas);
var
  i:integer;
begin
  c.Brush.Style:=bsSolid;
  c.Brush.Color:=clLtGray;
  c.FillRect(Rect(0,0,sirkaPlochy,vyskaPlochy));
  for i:=0 to High(k) do
    if i<>sel then
      k[i].kresli(c,false);
  if sel>=0 then              // aby bol vykreslený nad všetkými ostatnými
    k[sel].kresli(c,true);
end;

procedure THra.MouseDown(x,y:integer);
begin
  sel:=High(k);
  while (sel>=0) and not k[sel].klik(x,y) do dec(sel);
  x0:=x; y0:=y;
end;

procedure THra.MouseMove(x,y:integer);
begin
  if sel>=0 then begin
    k[sel].posun(x-x0,y-y0);
    if k[sel].x<0 then k[sel].x:=0;
    if k[sel].x+k[sel].sirka>sirkaPlochy then k[sel].x:=sirkaPlochy-k[sel].sirka;
    if k[sel].y<0 then k[sel].y:=0;
    if k[sel].y+k[sel].vyska>vyskaPlochy then k[sel].y:=vyskaPlochy-k[sel].vyska;
    x0:=x; y0:=y;
  end;
end;

procedure THra.MouseUp;
begin
  sel:=-1;
end;

////////////////////////////////////////////////////////////////////

var
  hra:THra;

procedure TForm1.FormCreate(Sender: TObject);
begin
  hra:=THra.Create('obraz.txt');
  Image1.Width:=hra.sirkaPlochy; Image1.Height:=hra.vyskaPlochy;
  hra.kresli(Image1.Canvas);
  DoubleBuffered:=true;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  hra.Free;
end;

procedure TForm1.Image1MouseDown(...);
begin
  hra.MouseDown(x,y);
  hra.kresli(Image1.Canvas);
end;

procedure TForm1.Image1MouseMove(...);
begin
  hra.MouseMove(x,y);
  hra.kresli(Image1.Canvas);
end;

procedure TForm1.Image1MouseUp(...);
begin
  hra.MouseUp;
  hra.kresli(Image1.Canvas);
end;

2. príklad: práca s korytnačkami

Napíšte program, ktorý umožní zahrať sa takúto hru:

  • zospodu grafickej plochy vyletujú smerom nahor rôznou rýchlosťou rôzne zafarbené balóny
  • úlohou hráča je triafať tieto balóny šípkou - každý trafený balón praskne a na jeho mieste sa napíše text BUM
  • balóny sú reprezentované korytnačkami, ktorých tvar je farebný šesťuholník so stranou 30 - pre toto zadefinujte triedu TBalon:
        TBalon = class(TKor) 
          k:real; 
          constructor Create; 
          procedure Dopredu(d:real); override; 
        end;
  • stavová premenná k slúži ako koeficient pre metódu Dopredu - vďaka tomuto koeficientu bude môcť korytnačka "chodiť trochu pomalšie"
  • konštruktor Create vytvorí korytnačku na náhodnej pozícii ale na dolnej hrane plochy, natočenú na sever tvaru farebného balóna (metódy ZmenTvar, ZmenFarbuTvaru), pričom nastaví stavovú premennú k na náhodnú hodnotu z intervalu <0.5,1>
  • všetky tieto balóny budete uchovávať v polymorfnom poli triedy TVelaKor
  • samotná šípka bude tiež korytnačka (môže mať tvar trojuholníka), ktorá je stále natočená smerom na východ - objaví sa na mieste kliknutia myšou ale len, ak sa kliklo nie viac ako 100 od ľavého okraja plochy. Kliknúť môžeme kedykoľvek, ak šípka ešte neodletela mimo plochy, tak sa presunie na miesto kliknutia.
  • šípka sa stále rovnomerne pohybuje smerom na východ a ak pri svojom pohybe prejde cez nejaký balón, tak tento praskne a zanechá nápis BUM - nemusíte si všímať situáciu, ak šípka uletí von z plochy - zrejme novým kliknutím sa znovu objaví v ploche
  • všetky balóny, ktoré prasknú, alebo vyletia z plochy von, by sa z poľa korytnačiek mali zrušiť metódou ZrusKor
  • v náhodných časových intervaloch, ale za podmienky, že balónov nie je viac ako 20, sa stále generujú nové balóny
  • pohyb šípky, pohyb balónov, ale aj náhodné generovanie balónov a aj rušenie balónov budete robiť pomocou časovača (TTimer)
type
  TBalon = class(TKor)
    k:real;
    constructor Create;
    procedure dopredu(d:real); override;
  end;

constructor TBalon.Create;
const
  f:array[0..5] of TColor = (clRed, clBlue, clGreen, clYellow, clPurple, clBlack);
begin
  inherited Create(random(sirka-100)+100,vyska);
  ZmenTvar([90,15,60,30,60,30,60,30,60,30,60,30,60,15]);
  ZmenFarbuTvaru(f[random(Length(f))]);
  PH; Ukaz;
  k:=0.5+random(6)/10;
end;

procedure TBalon.dopredu(d:real);
begin
  inherited dopredu(d*k);
end;

var
  sip:TKor;
  balony:TVelaKor;

procedure TForm1.FormCreate(Sender: TObject);
begin
  randomize; zmaz;
  sip:=TKor.Create(sirka,0,90);
  sip.PH;
  sip.ZmenTvar([0,50,135,10,0,-10,90,10]);
  balony:=TVelaKor.Create;
end;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if X<100 then begin
    sip.ZmenXY(X,Y); sip.Ukaz;
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  i:integer;
begin
  sip.Dopredu(10);
  if (balony.pk<20) and (random(2)=0) then
    balony.UrobKor(TBalon.Create);
  balony.Dopredu(10);
  with balony do begin
    for i:=0 to pk-1 do
      if k[i].vzd(sip.X,sip.Y)<50 then begin
        ZrusKor(i);
        sip.Pis('BUM');
      end
      else if k[i].Y<0 then ZrusKor(i);
    Uprac;
  end;
  Image1.Repaint;
end;

Ďalšie zadania z predminulého roku nájdete na stránke Zadania


© 2002 AB, KVI
blaho@fmph.uniba.sk