Informaticasite van het Sondervick College te Veldhoven                 © L.J.M van Haperen (bron : R.J. van der Beek)
 

Delphi Hoofdstuk 8: Meer arrays, vier op een rij

  8.1. Gooien met twee dobbelstenen.

We willen een programma maken waarvan de interface er uit ziet zoals hiernaast.

Als je op de button met de tekst "Gooi 1000 keer met twee dobbelstenen" klikt dan wordt er inderdaad 1000 keer met twee dobbelstenen gegooid en er wordt dan afgedrukt hoe vaak het resultaat van de twee dobbelstenen samen 2, 3, 4 t/m 12 was.

De computer moet die aantallen bijhouden.
Daarvoor heb je 11 variabelen nodig, en dat kan natuurlijk het handigst met een array.

Het aantal keren dat er 2 is gegooid wordt vastgelegd met de variabele aantalkeer[2]
In de variabele aantalkeer[3] wordt vastgelegd hoevaak er 3 is gegooid, enz. t/m aantalkeer[12]

Je moet die variabelen declareren, en dat doe je zo:
var aantalkeer array[1..12] of integer;

  8.2. Een array van controls.

Er moeten 11 labels en 11 editboxen op het formulier worden gezet.
Daar kun je ook arrays van maken.

Je kunt de labels bijvoorbeeld lblAantal[2], lblAantal[3], lblAantal[4] t/m lblAantal[12] noemen.
En de editboxen noem je bijv. txtAantal(2], txtAantal(3], txtAantal(4] t/m txtAantal(12]

Maar als je een array van labels en editboxen hebt dan kun je die niet rechtstreeks op het formulier plaatsen, in het ontwerpvenster.
Je moet die labels en editboxen aanmaken met behulp van programmaregels.

Die labels en editboxen moeten direkt als het programma start al op het formulier staan.
Dat moet dus in de procedure TForm1.FormCreate staan.
En verder moeten de labels en editboxen worden gedeclareerd als globale variabelen (in ieder geval de editboxen) omdat er in verschillende procedures gebruik van wordt gemaakt.
Dus onder de regel, waarin het formulier wordt gedeclareerd, komen die declaraties:

var
  Form1: TForm1;
  lblAantal: Array[1..12] of TLabel;
  txtAantal: Array[1..12] of TEdit;

Verder moet je een opdracht geven om zo'n object (in het geheugen) te maken, bijv.:
lblAantal[2] := TLabel.Create(self);
En je moet er voor zorgen dat het object op het formulier wordt geplaatst, bijv. door middel van de opdracht:
lblAantal[2].Parent := Form1;
Tenslotte moet je de objecten nog de juiste kleurtjes, opschriften, enz. geven. Dat doe je in de procedure TForm1.FormCreate(Sender: TObject); Die wordt dan als volgt:

procedure TForm1.FormCreate(Sender: TObject);

var i: integer;

begin
  for i:=2 to 12 do
  begin
      lblAantal[i] := TLabel.Create(self);
      lblAantal[i].Parent := Form1;
      lblAantal[i].autosize:=false;
      lblAantal[i].alignment:=taCenter;
      lblAantal[i].width:=170;
      lblAantal[i].left:=10;
      lblAantal[i].top:= i*25 - 10;
      lblAantal[i].height:= 20;
      lblAantal[i].caption:='aantal keer '+ inttostr(i);
      lblAantal[i].color:=clTeal;
      lblAantal[i].Visible:=true;
      lblAantal[i].Font.Color := clWhite;
      lblAantal[i].Font.Name := 'Tahoma';
      lblAantal[i].Font.Size := 12;
      lblAantal[i].Font.Style := lblAantal[i].Font.Style+[fsBold];

      txtAantal[i] := TEdit.Create(self);
      txtAantal[i].Parent := Form1;
      txtAantal[i].autosize:=false;
      txtAantal[i].width:=60;
      txtAantal[i].left:=200;
      txtAantal[i].top:= i*25 - 10;
      txtAantal[i].height:= 20;
      txtAantal[i].text := '';
      txtAantal[i].color:=clWhite;
      txtAantal[i].Visible:=true;
      txtAantal[i].Font.Color := clBlack;
      txtAantal[i].Font.Name := 'Tahoma';
      txtAantal[i].Font.Size := 12;
  end;
end;

De knop waarop staat dat er 1000 keer gegooid moet worden is cmdGooi genoemd.
Als daar op geklikt wordt moet de volgende procedure worden uitgevoerd:

procedure TForm1.cmdGooiClick(Sender: TObject);
var aantal: array[1..12] of integer;
    i, som, d1, d2: integer;

begin
Randomize;
For i := 2 To 12 do
  begin
    aantal[i] := 0;
  end;
For i := 1 To 1000 do
begin
    d1 := random(6) + 1;
    d2 := random(6) + 1;
    som := d1 + d2;
    aantal[som] := aantal[som] + 1;
end;

For i := 2 To 12 do
begin
    txtAantal[i].Text := IntToStr(aantal[i]);
end;

end;

  8.3. Vier op een rij, tweedimensionale rij.

We willen nu een programma maken waarvan de interface er uit ziet zoals hieronder.
Om en om moeten X en O op de button boven een kolom klikken. De X of de O valt dan in die kolom naar beneden.
En dan gaat het er om wie het eerst vier op een rij heeft.

Hier moet in ieder geval door het programma worden bijgehouden welk teken in welk hokje zit.
Het is handig om daar een tweedimensionale rij voor te nemen.
Met vulling[1,1] = 1 geef je bijv. aan dat in het hokje linksonder een X staat, met vulling[1,2] = 2 geef je aan dat in het hokje boven de vorige een O staat.
En de inhoud van het hokje rechtsboven geef je aan met vulling[10,5]
Je gebruikt dus de volgende rij (met twee indices): vulling[kolom,rij]
Die rij moet als globale variabele worden gedeclareerd, dus boven implementation declareer je: (de eerste regel staat er al)

var Form1: TForm1;
        vulling: array[1..10,1..5] of integer;

  8.4. De interface van vier op een rij

Je begint natuurlijk weer met de interface.
Die kun je maken zoals in de figuur hier onder.
Je kunt de 50 hokjes op het formulier via het programma laten genereren.
Hetzelfde geldt voor de buttons boven de kolommen.
Het label linksboven heb ik lblBeurt genoemd.
De labels rechts heb ik lblAantalx en lblAantalo genoemd.
De button waarop "Nieuw spel" staat heb ik cmdNieuwspel genoemd.
Verder staan er 10 buttons met het woord "Gooi" boven de kolommen. Die heb ik cmdGooi1 (links) tot en met cmdGooi10 (rechts) genoemd.
Die plaats je allemaal op het formulier.
De 50 hokjes heb ik lblHok[1] t/m lblHok[50] genoemd, en daarbij zit lblHok[1] linksonder.
Die plaats je niet rechtstreeks op het formulier, maar die genereer je met het programma.
Ik heb even één zo'n hokje op het formulier geplaatst, en in het propertiesvenster de eigenschappen opgezocht, zodat ik die gegevens in het programma (zie verderop) kon gebruiken.



Je moet die labels lblHok[1] t/m lblHok[50] en aanmaken met behulp van programmaregels.
Die labels moeten direkt als het programma start al op het formulier staan, dat moet dus in de procedure TForm1.FormCreate staan.
En verder moeten de labels en buttons worden gedeclareerd als globale variabelen omdat er in verschillende procedures gebruik van wordt gemaakt.
Dus onder de regel, waarin het formulier wordt gedeclareerd, komen meer declaraties:

var
      Form1: TForm1;
      vulling: array[1..10,1..5] of integer;
      lblHok: Array[1..50] of TLabel;

Verder moet je een opdracht geven om zo'n object (in het geheugen) te maken.
En je moet er voor zorgen dat het object op het formulier wordt geplaatst.
Tenslotte moet je de objecten nog de juiste kleurtjes, opschriften, enz. geven. Dat doe je in de procedure TForm1.FormCreate(Sender: TObject); Die wordt dan als volgt:

procedure TForm1.FormCreate(Sender: TObject);

var i, rij, kolom: integer;

begin
  for i:=1 to 50 do
  begin
      lblHok[i] := TLabel.Create(self);
      lblHok[i].Parent := Form1;
      lblHok[i].autosize:=false;
      lblHok[i].alignment:=taCenter;
      lblHok[i].width:=35;
      kolom := (i - 1) div 5 + 1;
      rij := (i - 1) Mod 5 + 1;

      lblHok[i].Top := 275-rij*35;
      lblHok[i].Left := 42*kolom-32;
      lblHok[i].height:= 30;
      lblHok[i].caption:= '';
      lblHok[i].color:=clWhite;
      lblHok[i].Visible:=true;
      lblHok[i].Font.Color := clBlack;
      lblHok[i].Font.Name := 'Tahoma';
      lblHok[i].Font.Size := 12;
      lblHok[i].Font.Style := lblHok[i].Font.Style+[fsBold];
 end;

end;

  8.5. Het volledige programma.

We beginnen met de globale variabelen.
We hebben er al een stel, maar er komen nog een aantal bij.
Namelijk:
speler, aantalwinstx, aantalwinsto, aantalkeergegooid: Integer;
teken: array[1..2]: string;
uitslag: boolean;

Dus onder de regel, waarin het formulier wordt gedeclareerd, komen meer declaraties:

var
      Form1: TForm1;
      vulling: array[1..10,1..5] of integer;
      lblHok: Array[1..50] of TLabel;
      speler, aantalwinstx, aantalwinsto, aantalkeergegooid: Integer;
      teken: array[1..2] of string;

Als het programma start moeten er, behalve de opbouw van de interface, nog een aantal dingen gebeuren.
Voeg dus toe aan de opdrachten van procedure TForm1.FormCreate(Sender: TObject);

teken[1] := 'X';
teken[2] := 'O';
speler := 1;
cmdNieuwspel.Visible := False;

Als er op een gooi-button wordt geklikt, dan moet er van alles gebeuren. Wat er dan moet gebeuren heb ik in een aparte procedure gooiverwerking gezet (zie verderop).
Die procedure wordt gedeclareerd als procedure TForm1.gooiverwerking(kolom:integer);
Er wordt dus achter de naam van de procedure een getal verwacht, namelijk de kolom waarin gegooid moet worden.
Dat heeft tot gevolg dat de event-handlers voor de gooi-buttons heel eenvoudig zijn, want je kunt daarbij gebruik maken van die procedure gooiverwerking

De event-handler voor gooi-button1 is bijvoorbeeld als volgt:

procedure TForm1.cmdGooi1Click(Sender: TObject);
begin
      gooiverwerking(1);
end;

En de event-handler voor gooi-button2 is als volgt:

procedure TForm1.cmdGooi2Click(Sender: TObject);
begin
      gooiverwerking(2);
end;

En de andere event-handlers voor gooi-button3 tot en met gooi-button10 zien er net zo uit.
Bij de opdracht gooiverwerking(1); krijgt kolom de waarde 1, en bij de opdracht gooiverwerking(2); krijgt kolom de waarde 2.
De parameter (het wordt dus geen variabele genoemd maar een parameter) krijgt de waarde die bij de opdracht tussen haakjes staat, daarom wordt deze parameteroverdracht call by value genoemd.

En dan nu de procedure gooiverwerking:

procedure TForm1.gooiverwerking(kolom:integer);

var i, j, rijaanbeurt: integer;
    uitslag:boolean;
begin
  rijaanbeurt := 0;
  If (cmdNieuwspel.Visible = True) Then
  begin
    Beep;
    Exit;
  End;
    
  j := 1;
  rijaanbeurt:=0;
  while (rijaanbeurt=0) do
  begin
    If (vulling[kolom, j] = 0) Then
    begin
    	rijaanbeurt := j;
    End
    else
    begin
      j:=j+1;
      if (j=6) then  rijaanbeurt := 6;
    end;
  end;

  If (rijaanbeurt = 6) Then
  begin
    Beep;
    Exit;
  end
  Else
  begin
    vulling[kolom, rijaanbeurt] := speler;
    i := (kolom - 1) * 5 + rijaanbeurt;
    lblHok[i].Caption := teken[speler];
    aantalkeergegooid := aantalkeergegooid + 1;
    controleer(uitslag) ;
    If (uitslag = True) Then
    begin
      lblBeurt.Caption := ' ' + teken[speler] + ' heeft gewonnen !';
      If (teken[speler] = 'X') Then aantalwinstx := aantalwinstx + 1
        Else aantalwinsto := aantalwinsto + 1;
      lblAantalx.Caption := ' Aantal keer dat X gewonnen heeft:' + 
                                             IntToStr(aantalwinstx);
      lblAantalo.Caption := ' Aantal keer dat O gewonnen heeft:' + 
                                             IntToStr(aantalwinsto);
      cmdNieuwspel.Visible := True;
    end
    Else
    begin
      If (aantalkeergegooid = 50) Then
      begin
        lblBeurt.Caption := ' Geen van beiden heeft gewonnen !';
        cmdNieuwspel.Visible := True;
      end
      Else
    begin
      speler := speler + 1;
      If (speler = 3) Then speler := 1;
      lblBeurt.Caption := ' ' + teken[speler] + ' is aan de beurt !';
    End;
  End;
End;

End;

Hierboven wordt een procedure gebruikt die controleer genoemd is.
In die procedure wordt er gecontroleerd of er ook vier gelijke tekens op een rij zijn.
Dat kan op vier manieren: vertikaal, horizontaal en diagonaal in twee richtingen.
Als in die procedure blijkt dat er vier-op-een-rij is dan krijgt de variabele uitslag de waarde true.

De procedures gooiverwerking en controleer moeten gedeclareerd worden onder (of boven) de declaratie van de procedures die er al staan (bijv. die van FormCreate).
Daarkomt dus o.a.:

procedure FormCreate(Sender: TObject);
procedure gooiverwerking(kolom:integer);
procedure controleer(var uitslag:boolean);

De procedure controleer zie je hieronder. De procedure wordt gedeclareerd als:
procedure TForm1.controleer(var uitslag:boolean); Er wordt dus achter de naam van de procedure een variabele verwacht, namelijk de naam van de variabele waarin de uitslag (wel og geen vier-op-een-rij) wordt vastgelegd.
Dat heeft tot gevolg dat als je ergens de opdracht controleer(watishet); geeft, de variabele watishet (die moet van het type boolean zij) de waarde true heeft als er wel vier-op-een-rij was, en de waarde false als er geen vier-op-een-rij was.
Je kunt nu geen vaste waarde tussen haakjes achter de naam van de procedure zetten, zoals bij de vorige procedure gooiverwerking, het moet beslist een variabele zijn.
Daarom wordt deze parameteroverdracht call by reference genoemd, en het verschil zit hem dus in het woordje var bij de declaratie van de procedure:
TForm1.controleer(var uitslag:boolean);

De procedure controleer voer je op de volgende manier in:

procedure TForm1.controleer(var uitslag:boolean);
var kolom, rij, i, j: Integer;

begin
  uitslag := False;
  For kolom := 1 To 10 do
  begin
    For j := 1 To 2 do
    begin
      If (vulling[kolom, j] = speler) And
		   (vulling[kolom, j + 1] = speler) And
	 	   (vulling[kolom, j + 2] = speler) And
		   (vulling[kolom, j + 3] = speler) Then
      begin
        uitslag := True;
        lblHok[(kolom - 1) * 5 + j].Color := clRed;
        lblHok[(kolom - 1) * 5 + j + 1].Color := clRed;
        lblHok[(kolom - 1) * 5 + j + 2].Color := clRed;
        lblHok[(kolom - 1) * 5 + j + 3].Color := clRed;
        Exit;
      End;
    end;
  end;

  For rij := 1 To 5 do
  begin
    For j := 1 To 7 do
    begin
      If (vulling[j, rij] = speler) And
      (vulling[j + 1, rij] = speler) And
	    (vulling[j + 2, rij] = speler) And
      (vulling[j + 3, rij] = speler) Then
      begin
        uitslag := True;
        lblHok[(j - 1) * 5 + rij].Color := clRed;
        lblHok[j * 5 + rij].Color := clRed;
        lblHok[(j + 1) * 5 + rij].Color := clRed;
        lblHok[(j + 2) * 5 + rij].Color := clRed;
        Exit;
      End;
    end;
  end;

  For kolom := 1 To 7 do
  begin
    For j := 1 To 2 do
    begin
      If (vulling[kolom, j] = speler) And
		    (vulling[kolom + 1, j + 1] = speler) And
		    (vulling[kolom + 2, j + 2] = speler) And
		    (vulling[kolom + 3, j + 3] = speler) Then
      begin
        uitslag := True;
        lblHok[(kolom - 1) * 5 + j].Color := clRed;
        lblHok[kolom * 5 + j + 1].Color := clRed;
        lblHok[(kolom + 1) * 5 + j + 2].Color := clRed;
        lblHok[(kolom + 2) * 5 + j + 3].Color := clRed;
        Exit;
      End;
    end;
  end;

  For kolom := 1 To 7 do
  begin
    For j := 4 To 5 do
    begin
        If (vulling[kolom, j] = speler) And
		      (vulling[kolom + 1, j - 1] = speler) And
		      (vulling[kolom + 2, j - 2] = speler) And
		      (vulling[kolom + 3, j - 3] = speler) Then
        begin
          uitslag := True;
          lblHok[(kolom - 1) * 5 + j].Color := clRed;
          lblHok[kolom * 5 + j - 1].Color := clRed;
          lblHok[(kolom + 1) * 5 + j - 2].Color := clRed;
          lblHok[(kolom + 2) * 5 + j - 3].Color := clRed;
          Exit;
        End;
      end;
   end;
End;

Als een spelletje is afgelopen kan er met een nieuw spel worden begonnen door op de button met "Nieuw spel" te klikken.
Dat moet ook nog geprogrammeerd worden.
Dat doe je als volgt:

procedure TForm1.cmdNieuwspelClick(Sender: TObject);
var i, j: Integer;

begin
For i := 1 To 50 do
begin
lblHok[i].Caption := '';
lblHok[i].Color := clWhite;
end;

For i := 1 To 10 do
begin
  For j := 1 To 5 do
  begin
    vulling[i, j] := 0;
  end;
end;
aantalkeergegooid := 0;
speler := speler + 1;
If (speler = 3) Then speler := 1;
lblBeurt.Caption := ' ' + teken[speler] + ' is aan de beurt !';
cmdNieuwspel.Visible := False;
end;

en daarmee is het spel klaar !