Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi wiedermal Stringgrid.... (https://www.delphipraxis.net/1867-wiedermal-stringgrid.html)

Madeleine 28. Dez 2002 16:28


wiedermal Stringgrid....
 
Hi an alle,
hier mal eine kleine Herausforderung an die Profis. Ich habe ein StringGrid mit einer Reihe FixedRows. Frage: Gibt es eine Möglichkeit, das man nur diese FixedCols, praktisch die erste Reihe anklicken kann,und das wenn man auf dem Rest klickt, nichts passiert?

thx for any hints!

Sebastian Nintemann 28. Dez 2002 17:26

Das was passiert hängt ja nur von deiner Auswertung des Klicks ab:

Delphi-Quellcode:
procedure TForm1.StringGrid1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  aX,aY : integer;
begin
  StringGrid1.MouseToCell(X,Y,aX,aY);
  if aX = 0  then
    StringGrid1.Cells[aX,aY] := 'hier wurde geklickt'
end;
In diesem Beispiel passiert nur was bei klick auf eine der Zellen in der linken Spalte (FixedCol), hoffe das hilft dir weiter.

Gruß, Sebastian

Madeleine 28. Dez 2002 17:32

hm, das hat natürlich geklappt, und ich habe da sonstwas für komplizierte Prozeduren erwartet, dabei geht es auch so einfach!
Danke! :spin:

Madeleine 28. Dez 2002 18:50

doch nicht
 
gibt es vielleicht auch noch eine andere Möglichkeit, das man eben wirklich nur bestimmte Zellen anklicken kann, und andere nicht? deine Lösung klappt im Zusammenhang mit meinem Programm leider einfach nicht :(

Sebastian Nintemann 28. Dez 2002 19:14

Was man auf eine ganze Spalte anwenden kann, kann man natürlich auch auf eine bestimmte Zelle anwenden :spin: :

Delphi-Quellcode:
if (aX = 0) and (aY = 0) then
  StringGrid1.Cells[aX,aY] := 'Nur hier darf geklickt werden'
Wars das was du gemeint hast?
Gruß, Sebastian

Madeleine 28. Dez 2002 20:35

naja, am besten wäre, wenn anstatt 'Nur hier darf geklickt werden' man wirklich nur dort klicken darf
bin schon die ganze Zeit über am wilden ausprobieren :coder: und das nicht ohne Folgen :freak:
denn dadurch, das man auf die anderen Felder noch draufklicken kann, werden bestimmte Variablen verändert, was ich nicht ganz ausschließen kann,zumindest ist es mir noch nicht gelungen :?

Sebastian Nintemann 28. Dez 2002 20:43

Hallo Madeleine, was ist denn das Problem? Wenn du explizit die Zellenkoordinaten abfragst kannst du doch genau bestimmen was passieren soll, mit obenstehendem Code wird ganz sicher nur die Zelle [0,0] angesprochen. Oder hast du vielleicht im OnMouseup oder OnClick-Event noch anderen Code stehen? Denn diese Abfrage im OnMouseDown Ereignis verhindert natürlich nicht, dass Code im OnClick-Event bei dem nicht auf bestimmte Zellen geprüft wird ausgeführt wird, dann müsstest du die Abfrage vor den Code im OnClick-Event durchführen.

Gruß, Sebastian

Madeleine 29. Dez 2002 18:22

das ist ja das komische, es gibt nur die MouseDown-Prozedur, und in die hab ich alles in diese if-Schleife gepackt, es ist wirklich mysteriös

Sebastian Nintemann 29. Dez 2002 19:05

Poste doch einfach mal den Code, dann wirds einfacher zu helfen :hi:

Gruß, Sebastian

Madeleine 1. Jan 2003 14:16

Liste der Anhänge anzeigen (Anzahl: 1)
Na gut, vielleicht findet ein Profi wie du auch meine Fehler wie EInvalidPointer und EInvalidAdress (oder so),muß irgendwas mit "hoehe" zu tun haben! das wär ganz super und ist total wichtig! :roll:
Gruß M

Sebastian Nintemann 2. Jan 2003 11:57

Hallo Madeleine, jetzt kommts erstmal dick :mrgreen:
Ich hab dir jetzt mal drei Procedures geschrieben. Ich hatte ja nicht deinen Form Entwurf, dehalb hab ich ne Form gemacht mit folgendem drauf
  • Ein DrawGrid mit dem Namen "DrawGrid" (scheint mir hier sinnvoller, Strings werden ja nicht gebraucht, ansonsten ist es genau das gleiche)
    Ein Edit ("EditW") für die Breite
    Ein Edit ("EditH") für die Höhe
    und ein Button ("ButtonStart") mit dem man das Spiel initialisiert.


Zunächst folgende globale Variablen
Delphi-Quellcode:
var
  Form1 : TForm1;
  w,h  : Cardinal; //Höhe und Breite,
                    //Cardinal weil keine negativen Werte erlaubt sind
  field : Array of Array of Integer; //Spielfeld, 0 = leeres Feld, 1 = blau, 2 = gelb
  game : boolean = false; //Wird gespielt?
  Player: integer = 1; //Spieler, 1 = blau, 2 = gelb
Die Kommentare dürften für sich sprechen.
Dann die Initialisierung im OnClick von ButtonStart:
Delphi-Quellcode:
procedure TForm1.ButtonStartClick(Sender: TObject);
var
  i,j: integer;
begin
  try
    begin
      w := StrToInt(EditW.Text);
      h := StrToInt(EditH.Text);               //Höhe und Breite (w und h) auslesen
      DrawGrid.ColCount := w;
      DrawGrid.RowCount := h+1;                //Höhe und Breite auf das Grid anwenden
      SetLength(field,w);                      //Array auf die richtige Größe bringen
      for i := 0 to High(field) do
        begin
          SetLength(field[i],h);
          for j := 0 to h-1 do field[i,j] := 0; //alle Felder auf 0,
                                                //nötig wenn mehrmals gespielt wird
        end;
      DrawGrid.Enabled := true;
      DrawGrid.Repaint;                        //neu zeichnen,
                                                //auch nötig wenn mehrmals gespielt wird
      game := true                             //jetzt wird gespielt
    end
  except                                       //im Fehlerfall: zurück das Kommando
    begin
      ShowMessage('Fehlerhafte Eingabe');
      EditW.SetFocus;
      exit
    end
  end
end;
Ich hab auch hier alles kommentiert, wenn du Fragen hast nur melden.

Jetzt fehlen nur noch zwei Sachen: Was passiert beim Klick (OnMouseDown) und wie werden die Zellen gezeichnet?

Delphi-Quellcode:
procedure TForm1.DrawGridDrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
begin
  if (not game) or (ARow = 0) then exit;
  //wenn kein Spiel am laufen ist, oder die Zeile nicht im Feld ist dann abbrechen
  //ab dem zweiten Spiel ist egal ob ein Spiel am laufen ist, vorher gibts aber nen Fehler
  //weil das Array field dann noch nicht initialisiert ist, deshalb die Abfrage

  case field[ACol,ARow-1] of
    1: begin                                     //wenn Spieler = 1 (blau)
         DrawGrid.Canvas.Brush.Color := clBlue;  //Dann blaunen Kuller malen
         DrawGrid.Canvas.Ellipse(Rect);
       end;
    2: begin                                     //wenn Spieler = 2 (gelb)
         DrawGrid.Canvas.Brush.Color := clYellow; //Dann gelben Kuller malen
         DrawGrid.Canvas.Ellipse(Rect)
       end
  end
end;
Hier werden die Zellen "gemalt". Wenn es sich nicht um eine Zelle handelt, in der ein Kreis sein soll, wird nichts weiter gemacht.
Jetzt das setzen im OnMouseDown:
Delphi-Quellcode:
procedure TForm1.DrawGridMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  aCol,aRow,i : integer;
begin
  DrawGrid.MouseToCell(X,Y,aCol,aRow);
  if (aRow <> 0) or (field[aCol,0]<>0) then exit; //Wenn nicht in die 1. Zeile geklickt wurde
                                                  //oder die Spalte schon voll ist aufhören
  i := h-1;
  while field[aCol,i] <> 0 do dec(i);             //bestimmen welche die erste freie Zelle ist
  field[aCol,i] := Player;                       //Zelle mit Spieler besetzen
  if Player = 2 then dec(Player) else inc(Player);//Spielerwechsel
  DrawGrid.Repaint                               //und neu zeichnen
end;
So, das wars auch schon (fast), zum schluß noch das Array freigeben:
Delphi-Quellcode:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  i: integer;
begin
  for i := 0 to High(field) do    //Array freigeben
    field[i] := nil;
  field := nil
end;
Das Programm funktioniert jetzt soweit, dass man ein spiel starten kann indem man gewünschte Höhe und Breite angibt, und dann auf start klickt. Dann kann man (abwechselnd gelbe und blaue) Kullerdinger auf das Feld werfen. Es prüft jetzt noch nicht auf Sieg und es gibt auch keinen Computerspieler, das müsstest du noch einbauen, oder wir zusammen.
Zum checken auf Sieg hab ich schon was im Kopf, kann ja später mal nen Ansatz posten. Lies dir erstmal den Code durch und guck ob du ihn verstehst, sonnst immer fragen!

Gruß, Sebastian

Madeleine 2. Jan 2003 13:25

Uih :shock:
:dancer: :dancer2: das ist ja super! :spin: Danke!
Ich habs auch gleich ausprobiert, aber zeichnen macht es noch nicht,klappt das bei dir? Ich werd mich ma auf die Fehlersuche bei mir machen!
Um zu prüfen, ob gewonnen oder nicht, kann man ja das field in nen String umwandeln und dann mit Hilfe von pos() Dinge ermitteln wie ('1111')--> gewonnen!

Sebastian Nintemann 2. Jan 2003 14:12

Hallo Madeleine, bei mir klappts, erstell mal ein neues Project und setz die Komponenten die ich oben angegeben hab auf das Formular, und benenn sie so wies oben steht. Dann musst du noch den Code aus der folgenden Textdatei in die Unit kopieren (vorher alles löschen), und wichtig: Die Komponenten markieren und im Objektinspektor noch die jeweiligen Events zuordnen. (Also ButtonStart: OnClick = ButtonStartClick, etc)

Dann sollte es gehen.

Gruß, Sebastian

Sebastian Nintemann 2. Jan 2003 14:15

Ach ja, wegen dem "auf Sieg checken", das könnte man so machen, aber dann hat man keinen Sieg bei 4 diagonalen Steinen. Ich hab aber schon ne Idee wie man das machen kann, kommt heut noch :mrgreen:
Ich hoffe es klappt jetzt erstmal mit dem Code oben! :hi:

Gruß, Sebastian

Sebastian Nintemann 2. Jan 2003 14:59

Liste der Anhänge anzeigen (Anzahl: 1)
Jetzt hab ich doch tatsächlich die Anlage vergessen :oops:
Hier kommt sie! Ich denke immer mit dem "Durchsuchen" ist es getan.

Gruß, Sebastian

Sebastian Nintemann 2. Jan 2003 15:45

Liste der Anhänge anzeigen (Anzahl: 1)
So jetzt kommt gleich nochmal eine neue Version. Das Spiel ist jetzt beendet wenn einer der Spieler eine senkrechte oder waagerechte Viererreihe hat. Diagonal kommt noch! (Ohne wird es meistens damit enden dass keiner gewinnt).
Die Check-Procedure kann man vielleicht noch verkürzen, aber es funktioniert so. Beachte auch, dass es die Variable "game", mit der ich vorher geprüft hab ob ein Spiel läuft nicht mehr gibt, statt dessen überprüfe ich jetzt beim zeichnen ob das Array<>nil ist, der Vorteil ist der: Nach einem Spiel kann man zwar nichts mehr am Spielfeld verändern, es soll aber noch solange angezeigt werden bis ein neues Spiel gestartet wird. Wenn man in der OnCellDraw procedure aber (über die Variable game) prüft ob ein Spiel läuft, zeichnet sie nicht mehr wenn das Spiel vorbei ist.

Gruß, Sebastian

Madeleine 2. Jan 2003 17:02

Zwischenstand
 
So, bei mir werden jetzt endlich auch blaue und gelbe Kreise gemahlt und warum das nicht geklappt hat, na das laß ich lieber unerwähnt :oops:

Habe erstmal so gemacht, das sich das DrawGrid den Feldern der Größe nach anpasst...sieht eben schöner aus :angle2:
DrawGrid.Height:= (h+1)*36 +3;
DrawGrid.Width:=w*36+3;
...dabei müssen die Felder aber 35 groß sein.
Dann mach ich mich auch mal an die Prüfen ob gewonnen-Prozedur!

Madeleine 2. Jan 2003 17:53

Liste der Anhänge anzeigen (Anzahl: 1)
also ich hab dir mal meine prüfen-ob-gewonnen-Funktion angehängt, das klappt bei mir ganz gut, hast aber Recht, ich weiß noch nicht, wie ich das ganze diagonal noch hinkriege
deine Version hab ich noch nicht ausprobiert

*fleißig weiter programmier* :coder:

Sebastian Nintemann 2. Jan 2003 21:34

Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Madeleine, meine Check Funktion sieht jetzt folgendermaßen aus:
Delphi-Quellcode:
function CheckWinner(const Player,X,Y: integer): boolean;
var
  i,j,count,code: integer;
begin
  result := true;
  code := 0;
  count := 0;
  //waagerecht
  for i := X-3 to X+3 do
    begin
      if i in [0..pred(w)] then
      if field[i,Y] = Player then inc(count) else count := 0;
      if count = 4 then
        begin
          code := 1;
          break
        end
    end;
  count := 0;
  //senkrecht
  if code=0 then
  for i := Y-3 to Y+3 do
    begin
      if i in [0..pred(h)] then
      if field[X,i] = Player then inc(count) else count := 0;
      if count = 4 then
        begin
          code := 2;
          break
        end
    end;
  count := 0;
  j := Y-3;
  if code = 0 then
  //diagonal
  for i := X-3 to X+3 do
    begin
      if (i in [0..pred(w)]) and (j in [0..pred(h)]) then
      if field[i,j] = Player then inc(count)
        else count := 0;
      if count = 4 then
        begin
          code := 3;
          break
        end;
      inc(j)
    end;
  count := 0;
  j := Y+3;
  if code = 0 then
  //diagonal
  for i := X-3 to X+3 do
    begin
      if (i in [0..pred(w)]) and (j in [0..pred(h)]) then
      if field[i,j] = Player then inc(count)
        else count := 0;
      if count = 4 then
        begin
          code := 4;
          break
        end;
      dec(j)
    end;
  case code of
    1: begin
         Caption := 'Spieler '+IntToStr(Player)+' hat gewonnen (-)';
         DrawGrid.Enabled := false
       end;
    2: begin
         Caption := 'Spieler '+IntToStr(Player)+' hat gewonnen (|)';
         DrawGrid.Enabled := false
       end;
    3: begin
         Caption := 'Spieler '+IntToStr(Player)+' hat gewonnen (\)';
         DrawGrid.Enabled := false
       end;
    4: begin
         Caption := 'Spieler '+IntToStr(Player)+' hat gewonnen (/)';
         DrawGrid.Enabled := false
       end;
    else result := false
  end
end;
So wie ich das sehe sind so alle Möglichkeiten abgedeckt. Gleichzeitig übernimmt sie noch die Textausgabe wenn jemand gewonnen hat. Dazu hab ich noch ein paar kleinere Änderungen an den vorherigen Procedures vorgenommen:

1. Die Initialisierungsprozedur prüft jetzt auch ob die Werte größer als 4 sind:

Delphi-Quellcode:
try
  begin
    w := StrToInt(EditW.Text);
    h := StrToInt(EditH.Text);               //Höhe und Breite (w und h) auslesen
    if (w<4) or (h<4) then
      raise Exception.Create('Zu kleine Werte');
    DrawGrid.ColCount := w;
    DrawGrid.RowCount := h+1;                //Höhe und Breite auf das Grid anwenden
    SetLength(field,w);                      //Array auf die richtige Größe bringen
    for i := 0 to High(field) do
      begin
        SetLength(field[i],h);
        for j := 0 to h-1 do field[i,j] := 0; //alle Felder auf 0,
                                              //nötig wenn mehrmals gespielt wird
      end;
    DrawGrid.Enabled := true;
    DrawGrid.Repaint;                        //neu zeichnen,
                                                //auch nötig wenn mehrmals gespielt wird
    Caption := 'Spieler 1: blau; Spieler 2 : gelb'; //Caption ändern
    Player := 1                               //blau fängt an
  end
except on E: Exception do                    //im Fehlerfall: zurück das Kommando
  begin
    ShowMessage(E.Message);
    EditW.SetFocus;
    exit
  end
end;
2. Es wird ausgegeben wer dran ist (Im OnMouseDown, da ist auch die Check Funktion untergebracht)
Delphi-Quellcode:
begin
...
  field[aCol,i] := Player;                       //Zelle mit Spieler besetzen
  DrawGrid.Repaint;                              //und neu zeichnen
  if CheckWinner(Player,aCol,i) then exit;

  if Player = 2 then
    begin
      dec(Player);
      Caption := 'Spieler 1 (blau) ist an der Reihe'
    end
  else
    begin
      inc(Player);
      Caption := 'Spieler 2 (gelb) ist an der Reihe'
    end
end;
Ich hoffe ich hab jetzt nichts vergessen, alles zusammen häng ich nochmal als Anlage dran. Nicht falsch verstehen, deine Funktion tuts natürlich auch, und durch selbst machen lernt man natürlich am besten, aber du kannst dir diese ja auch mal angucken und sehen ob du síe verstehst und möglicherweise Teile davon bei dir einbauen.

Gruß, Sebastian

Madeleine 3. Jan 2003 09:58

nee, so wie du das gemacht hast, ist es schon viel ausgeklügelter, da kann ich sogar noch einiges für den Computergegner verwenden,danke!

Sebastian Nintemann 3. Jan 2003 13:05

Hallo Madeleine, guten Morgen (noch nicht so lange auf :mrgreen: )
Freut mich, dass du das gebrauchen kannst, noch ein Tipp für den Computergegner:
1. Screibe dafür auch eine eigene Prozedur und bring die dann auch im OnMouseDown unter, direkt nachdem der Mensch gesetzt hat und geprüft wurde ob er gewonnen hat, so ersparrst du dir Arbeit.
2. Bring ein bisschen random rein, also dass es ein bisschen auch vom Zufall abhängt wo der Computer seinen Stein hinwirft, sonst kann es leicht passieren, dass man schnell einen Weg findet den Computer zu besiegen, dann kann man diesen Weg immer und immer wieder nehmen ohne, dass sich was am Spielablauf ändert.

Gruß, Sebastian


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:44 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz