Delphi Rechnen mit StringGrid Zellen

Pelzameise

Sachse
Registriert
Apr. 2008
Beiträge
5.221
Guten Morgen,
Sinn meines Projektes ist es dass man auf einem Schachbrett immer nur dorthin klicken kann, wo der Springer hingehen darf. So soll man dann nacheinander alle Zellen anklicken. Mein Ansatz war, den satz des Pytagoras zu nehmen, man also nur dorthin darf wo:
Code:
Die Wurzel aus
  (xalt-xneu)^2 + (yalt-yneu)^2
gleich Wurzel 5 ist.
Hoffe ihr könnt das nachvollziehen.

Soo dann hier mein Versuch:
Code:
unit uspielfeld;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Grids, Buttons;

type
  Tfspielfeld = class(TForm)
    tspielfeld: TStringGrid;
    lthema: TLabel;
    Memo1: TMemo;
    BitBtn1: TBitBtn;
    bausgeben: TButton;
    b_loeschen: TButton;
    procedure FormCreate(Sender: TObject);
    procedure tspielfeldSelectCell(Sender: TObject; ACol, ARow: Integer;
      var CanSelect: Boolean);
    procedure bausgebenClick(Sender: TObject);
    procedure b_loeschenClick(Sender: TObject);
  private
    { Private-Deklarationen}

  public
    { Public-Deklarationen}
  end;

var
  fspielfeld: Tfspielfeld;
  spf : ARRAY[1..8,1..8]OF INTEGER;
  zaehler : INTEGER;
  x,y: ARRAY[1..64] OF INTEGER;
  wurzel: REAL;

  implementation

{$R *.DFM}

procedure Tfspielfeld.FormCreate(Sender: TObject);
VAR i : INTEGER;
begin
 {Initialisierung}
 zaehler:=0;
 wurzel:=sqrt(5);
 FOR i:=1 TO 8 DO
  BEGIN
   tspielfeld.cells[i,0]:=INTTOSTR(i);
   tspielfeld.cells[0,i]:=INTTOSTR(i);
  END;
end;


procedure Tfspielfeld.tspielfeldSelectCell(Sender: TObject; ACol,
  ARow: Integer; var CanSelect: Boolean);
begin

IF zaehler=0 THEN BEGIN
   //Bei Mausklick zaehler um 1 erhöhen
 zaehler:=zaehler+1;
 //in Tabellenzelle den aktuellen Zählwert eintragen
 tspielfeld.Cells[acol,arow]:=IntToStr(zaehler);
 //in Spielfeldkomponente eine 1 eintragen
 spf[acol,arow]:=1;
 x[1]:=TSpielfeld.Col+1;
 y[1]:=TSpielfeld.Row+1;
END;

IF zaehler>0 THEN BEGIN

  //Bei Mausklick zaehler um 1 erhöhen
 zaehler:=zaehler+1;
 x[zaehler]:=TSpielfeld.Col+1;
 y[zaehler]:=TSpielfeld.Row+1;

 IF wurzel = (sqrt(sqr(x[zaehler-1]-x[zaehler])+sqr(y[zaehler-1]-y[zaehler]))) THEN BEGIN

 //in Tabellenzelle den aktuellen Zählwert eintragen
 tspielfeld.Cells[acol,arow]:=IntToStr(zaehler);
 //in Spielfeldkomponente eine 1 eintragen
 spf[acol,arow]:=1;

  END
  ELSE zaehler:=zaehler-1;
END;
end;

Beim ersten Klick geht es, dann aber funktioniert es nicht mehr. Ich vermute also dass er nicht mehr in die zweite IF-Schleife rein kommt.
Hoffe ihr habt Ideen. Gruß
 
du versuchst also, das der user die felder ankliken muss, auf die ein springer gehen kann, stimmts?
also ich würde mit delta x und y arbeiten, und dann über den pytagoras.
wenn du aber ein schachspiel machen willst, würde ich auf das anklicken der figur alle möglichen felder einfärben und dann nur überprüfen ob die fabe da ist.
 
Du vergleichst da "IF wurzel = (sqrt(..." zwei REAL Werte mit '='. Das geht wegen kleinster Rundungsdifferenzen so gut wie immer schief. Entweder du vergleichst mit einer gewissen Toleranz, also so etwa:

epsilon = 0.001;
IF epsilon < abs(wurzel - (sqrt( ... )

oder du verwendest den Vorschlag von blub1991. Es sind ja nur 8 Fallunterscheidungen zu berücksichtigen.
 
Danke für die Antworten. Ich hab jetzt den Vergleich so gelöst, dass ich gesagt hab dass der Wert zwsichen 2,2 und 2,3 liegen muss, das langt von der Genauigkeit völlig zu.
Außerdem hat er anscheinend ein Problem mit TSpielfeld.Col+1, was ich jetzt durch ACol+1 ersetzt hab.

So jetzt kann ich weiter machen.

EDIT: Wie kann ich die Zellen färben? Ich bekomm das irgendwie nicht gebacken....
 
Zuletzt bearbeitet:
ich hab das irgentwie mal gemacht. das letzte mal das ich delphi programmiert habe ist schon ne weile herr, ich mach jetzt c++.
ich hab aber das hier gefunden:
Code:
private
  ColorCol, ColorRow: integer;

Set colored cell column and row with Button1, and force stringgrid to repaint:

procedure TForm1.Button1Click(Sender: TObject);
begin
  ColorCol := 4;
  ColorRow := 3;
  StringGrid1.Repaint;
end;

Color delphi stringgrid cell in OnDrawCell event handler:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  S: string;
begin
  if (ACol = ColorCol) and (ARow = ColorRow) then begin
    StringGrid1.Canvas.Brush.Color := clYellow;
    StringGrid1.Canvas.FillRect(Rect); 
    S := StringGrid1.Cells[ACol, ARow]; 
    StringGrid1.Canvas.TextOut(Rect.Left + 2, Rect.Top + 2, S);  
  end;
end;
das sollte eigentlich gehen. ich persönlich würde das in eine funktion einfassen bei der du nurnoch koordinaten und fabe angeben musst.
 
Ich müsste dann das ja in die Funktion rein schreiben:
Code:
procedure Tfspielfeld.tspielfeldSelectCell(Sender: TObject; ACol,
  ARow: Integer; var CanSelect: Boolean);

Was ich noch nicht so richtig verstanden hab, wie ich dann in dieser Funktion die Koordinaten in x und y als Variable angeben kann...
 
Zuletzt bearbeitet:
also wie ich den quelltext und mein wissen, darüber wie vlc arbeitet richtig interpretiere wird mit
Code:
if (ACol = ColorCol) and (ARow = ColorRow) then begin
überprüft ob er gerade die mit ColorCol und ColorRow im OnButtonClick festgelegten koordinaten zeichnet, da problem ist dabei nur wenn mehrere felder gefärbt werden sollen. die werte müsten dann nur lokal übergeben werden.
irgentwie hab ich das bei meinem kalender auch so gelöst, das auf das anklicken die zelle gefärbt wird, frag mich aber nicht wie ich das angestellt habe, zu lange her und zu schlecht dokumentiert, da steige ich nicht mehr wirklich durch.
ich such das nochmal raus, vieleicht verstehe ich das jetzt und wenn nicht blickst du das vieleicht.

edit: ich weiß es wieder:
ich hab je ein
Code:
array of array of TColor;
für die text und die hintergrund fabe erstellt.
dann mit
Code:
// array größe setzen
  SetLength(schrift, Grid.ColCount, Grid.ColCount);
  SetLength(hintergrund, Grid.ColCount, Grid.ColCount);
die größen gesetzt.
jetzt kannst du die faben in deinen arrays belibig setzten.

danach hab ich mit
Code:
procedure TForm1.GridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
  State: TGridDrawState);
var     S: string;
begin

  S := Grid.Cells[ACol, ARow];
  // hintergrundfabe
  Grid.Canvas.Brush.Color :=hintergrund[ACol, ARow];
  Grid.Canvas.FillRect(Rect);
  // schriftfabe
  Grid.Canvas.Font.Color := schrift[ACol, ARow];
  Grid.Canvas.TextOut(Rect.Left + 2, Rect.Top + 2, S);
end;
den inhalt und die faben geschrieben.
ich hoffe das hat geholfen
 
Zuletzt bearbeitet:
Mh, ich glaube wir müssen nochmal ein Level weiter unten anfangen, ich seh bei den VCL-Anwendungen immer weniger durch...
Grade hakts schon beim Erschaffen der DrawCell-Funktion. Eigentlich, nach meinem anfängerhaften Verständnis, müsste man das doch irgendwie in die SelectCell-Funktion rein bekommen...
 
also DrawCell ist ein ereigniss das beim zeichnen eines stringgrid bei jeder zelle einzelnd ausgelöst wird. daher ist im event header auch enthalten welche zelle das gerade ist( ACol, ARow: Integer; ) auserdem ist enthalten welche position und größe die in pixeln hat( Rect: TRect; ) das was mein programmstück macht ist a)in zwei verschiedenen arrays die wunsch schrift und hintergrundfabe der jeweiligen zelle zu speichen und b) der jeweiligen zelle bei onDrawCell die entsprechenden faben aus den arrays zuzuweisen und c) das ganze an die richtige position zu zeichnen. ich hoffe das hat geholfen.
ansonst würde ich icq oder skyp entfehlen, da geht das erkäleren schneller als über das forum.

edit: ich glaube ich hab in ersten teil an dir vorbei geredet.
also wir sind soweit, das wir aus selectcell, was man auch über OnClick machen könnte, wissen wo der springer ist, und über den pytagoras welche felder wir eigentlich suchen.
das nächste wäre die hintergrundfabeim array zu verändern und dann das neuzeichnen des stringgrids auszulösen, was wir mit StringGrid1.Repaint; ereichen
 
Zuletzt bearbeitet:
So, du hast mir auf jeden Fall schon mal sehr weiter geholfen. Jetzt hab ich zumindest verstanden, was DrawCell überhaupt macht. Da ich aber deine Vorgehensweise nicht so richtig nachvollziehen konnte (Wozu braucht man hier einen Array of Array? Soweit bin ich nicht mit gekommen...), hab ich nochmal Google bemüht und mir dann das gebastelt:
Code:
unit uspielfeld;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Grids, Buttons, math;

type
  Tfspielfeld = class(TForm)
    tspielfeld: TStringGrid;
    lthema: TLabel;
    Memo1: TMemo;
    BitBtn1: TBitBtn;
    bausgeben: TButton;
    b_loeschen: TButton;
    procedure FormCreate(Sender: TObject);
    procedure tspielfeldSelectCell(Sender: TObject; ACol, ARow: Integer;
      var CanSelect: Boolean);
    procedure tspielfeldDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
      State: TGridDrawState);
    procedure bausgebenClick(Sender: TObject);
    procedure b_loeschenClick(Sender: TObject);
  private
    { Private-Deklarationen}

  public
    { Public-Deklarationen}
  end;

var
  fspielfeld: Tfspielfeld;
  spf : ARRAY[1..8,1..8]OF INTEGER;
  zaehler,sp,ze : INTEGER;
  x,y: ARRAY[1..64] OF INTEGER;
  wurzel: EXTENDED;

  implementation

{$R *.DFM}

procedure Tfspielfeld.FormCreate(Sender: TObject);
VAR i : INTEGER;
begin
 {Initialisierung}
 zaehler:=0;
 wurzel:=sqrt(5);
 FOR i:=1 TO 8 DO
  BEGIN
   tspielfeld.cells[i,0]:=INTTOSTR(i);
   tspielfeld.cells[0,i]:=INTTOSTR(i);
  END;
end;


procedure Tfspielfeld.tspielfeldSelectCell(Sender: TObject; ACol,
  ARow: Integer; var CanSelect: Boolean);
//farbe:  array of array of TColor;
begin
  // array größe setzen
  //SetLength(farbe, tspielfeld.ColCount, tspielfeld.ColCount);
  //farbe:=clgreen;
IF zaehler=0 THEN BEGIN
   //Bei Mausklick zaehler um 1 erhöhen
 zaehler:=zaehler+1;
 //in Tabellenzelle den aktuellen Zählwert eintragen
 tspielfeld.Cells[acol,arow]:=IntToStr(zaehler);
 //in Spielfeldkomponente eine 1 eintragen
 spf[acol,arow]:=1;
// x[1]:=TSpielfeld.Col+1;
 //y[1]:=TSpielfeld.Row+1;
 x[1]:=ACol+1;
 y[1]:=ARow+1;
 tspielfeld.repaint;
END;

IF zaehler>0 THEN BEGIN

  //Bei Mausklick zaehler um 1 erhöhen
 zaehler:=zaehler+1;
 x[zaehler]:=ACol+1;
 y[zaehler]:=ARow+1;

 IF ((sqrt(sqr(x[zaehler-1]-x[zaehler])+sqr(y[zaehler-1]-y[zaehler]))) <2.3) AND ((sqrt(sqr(x[zaehler-1]-x[zaehler])+sqr(y[zaehler-1]-y[zaehler]))) >2.2)  THEN BEGIN
  tspielfeld.repaint;
 //in Tabellenzelle den aktuellen Zählwert eintragen
 tspielfeld.Cells[acol,arow]:=IntToStr(zaehler);
 //in Spielfeldkomponente eine 1 eintragen
 spf[acol,arow]:=1;





  END
  ELSE zaehler:=zaehler-1;
END;
CANVAS.TEXTOUT(10,10,INTTOSTR(zaehler));
end;

procedure Tfspielfeld.tspielfeldDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
  State: TGridDrawState);
VAR  i:INTEGER;
begin
with tspielfeld.Canvas do begin
 FOR i:=1 TO 64 DO
       if (ACol=x[i]-1) and (ARow=y[i]-1) then begin
          Brush.Color:= clgreen;
          FillRect(Rect);
       end;
  end;
end;

Damit hab ich schonmal ein super Erfolgserlebnis :)
Die angeklickten Zellen werden Grün ausgemalt :)

Aber, es bleiben zwei (vermutlich kleinere) Probleme: Erstens ist immer nur die aktuelle Zelle grün (mal schauen ob ich das mit einer FOR-Schleife hin bekomme), zweitens übermalt das grüne Feld die Zahl die drin stehen sollte.

Dankeschön auf jeden Fall erstmal :)
Wenn doch noch weitere Probleme kommen sollten tauschen wir mal die ICQ-Nummern aus...

EDIT: Der Code ist geändert, die Zellen bleiben jetzt wegen der Zählschleife grün :) Aber die Zahlen sehe ich immer noch nicht...
 
Zuletzt bearbeitet:
für eine 2-dimensionale fläche(das Grid) brauchst du auch ein 2 dimensionales array um alle felder abdecken zu können
 
für ein zweidimensonales array brauchst du auch 2 indexis also ArrayOfArrayOf[index1,index2].
 
SO, hab das Problem nun gelöst:
Code:
procedure Tfspielfeld.tspielfeldDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
  State: TGridDrawState);
VAR  i:INTEGER;
begin
  with tspielfeld do begin
   FOR i:=1 TO zaehler DO
       if (ACol=x[i]-1) and (ARow=y[i]-1) then begin
          Canvas.Brush.Color:= $00DD00;
          Canvas.Font.Color:= clBlack;
          Canvas.FillRect(Rect);
          Canvas.TextRect(Rect, Rect.Left + 8, Rect.Top + 7, cells[acol, arow]);

       end;
  end;
end;
Dann hab ich auch noch die Zahlen zentriert :)

Danke für die Hilfe!
 
Zurück
Oben