Entwickler-Ecke

Multimedia / Grafik - Domino - Abgelegte Steine verwalten


galagher - Fr 04.06.10 15:53
Titel: Domino - Abgelegte Steine verwalten
Hallo!

Ich möchte ein Domino-Programm schreiben, habe bisher aber nur die Steine und 1 Bitmap, mit dem ich diese über das Spielfeld bewegen und um 90° drehen kann. Das Spielfeld ist ebenfalls ein Bitmap. Durch Mausklick wird der "Stein" abgelegt, indem er einfach an der aktuellen Position auf das Spielfeld-Bitmap gezeichnet wird:

Delphi-Quelltext
1:
Board.Canvas.Draw(imgStein.Left, imgStein.Top, imgStein.Picture.Graphic);                    


Ich denke, das ist recht gut und einfach gelöst so, denn abgelegte Steine werden ja nicht mehr bewegt.

Doch nun weiss ich nicht weiter: Wie merke ich mir im Programm, wo welcher Stein abgelegt wurde und wieviele Punkte er jeweils hat, damit ich weiss, wo und wie (senk- oder waagrecht) ich neue Steine anfügen kann?

Ich dachte zunächst an ein Array oder eine Liste mit Angaben wie X-, Y-Position, Anzahl der Punkte, senk- oder waagrechte Lage, so etwas in der Art. Aber ich komme nicht weiter.

Habt ihr Ideen, wie man eine einfache Steine-Verwaltung machen könnte?


Stundenplan - Fr 04.06.10 16:22

Mach einfach ein Array von einem Record:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
type TDirection = (dWaagerecht, dSenkrecht);
type TDominostein = record
  X, Y: Integer;
  Points: Integer;
  Direction: TDirection;
end;

{...}

var
  Dominos: array of TDominostein;
  //weitere Anweisungen

Grüße,
Stundenplan.


galagher - Fr 04.06.10 16:58

user profile iconStundenplan hat folgendes geschrieben Zum zitierten Posting springen:
Mach einfach ein Array von einem Record:

Danke für deinen Tipp! Ich fürchte aber, ich weiss nicht so recht, wie ich das handhaben soll, was dort eingetragen wird und wie. Vor allem, wie ich abfragen soll, ob ein anderer Stein passt.
Im Moment habe ich keinen Plan...


Bergmann89 - Fr 04.06.10 18:07

Hey,

ich würd mir ein 2D-Array so groß wie das Spielfeld machen, un da dann sie Punkte eintragen. Und befvor du einen neuen Stein ablegst, guckst du im Array ob die 2 anliegenden Felder belegt sind, wenn ja dann musst du noch gucken ob die Zahl stimm. Wenn das auch der Fall ist, dann kannst du den Stein dort ablegen und die 2 Zahlen des Steins im Array ablegen.

MfG Bergmann


galagher - Fr 04.06.10 18:15

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
ich würd mir ein 2D-Array so groß wie das Spielfeld machen, un da dann sie Punkte eintragen.

Daran habe ich auch schon gedacht, es aber wieder verworfen, denn dabei muss das Spielfeld dem Array entsprechen, wie beim Schach, oder? Ich will nämlich Steine ablegen, wo ich will - es gibt keine Felder, die einem "Platz" im Array entsprechen würden.


Bergmann89 - Fr 04.06.10 18:19

Hey,

das geht auch. DU musst dir dann halt nur ne Prozedur schreiben, die das Array an der entsprechenden Stelle vergrößert un die Daten ggf. verschiebt. Wenn du rechts bzw. unten erweitern willst, dann is das ganz einfach, einfach die Größe des Array ändern. Wenn du oben bzw. links erweitern willst, dann musst du erst das Array vergrößern un dann die Daten nach rechts bzw. unten erschieben. So schwer is das eig gar nich ;)

MfG Bergmann


galagher - Fr 04.06.10 18:42

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
das geht auch. DU musst dir dann halt nur ne Prozedur schreiben, die das Array an der entsprechenden Stelle vergrößert un die Daten ggf. verschiebt. Wenn du rechts bzw. unten erweitern willst, dann is das ganz einfach, einfach die Größe des Array ändern. Wenn du oben bzw. links erweitern willst, dann musst du erst das Array vergrößern un dann die Daten nach rechts bzw. unten erschieben. So schwer is das eig gar nich ;)

Ich weiss nicht, wie ich das machen soll! Es ist einfach, das Image mit dem jeweiligen Stein zu platzieren, aber es ist dann ja nur noch eine Zeichnung.

Ich möchte nur ein einziges Image zum Setzen der Steine verwenden, ziehe es dann an sie richtige Position und klicke darauf, um den Stein abzulegen. Dann wird die Grafik auf das Board gezeichnet und das Stein-Image wird leer, damit man einen neuen Stein aufnehmen kann.

Ich kriege die Grafik mit dem Array im Kopf nicht zusammen. Das Array an sich ist keine grosse Sache, es muss nicht mal dynamisch erweiterbar sein, sondern nur soviele Positionen haben, wie es Steine gibt. Alles, was im Array kein Stein ist, ist eben leer. Logisch.
Ich muss, denke ich, beim Ablegen jedesmal folgende Werte hineinschreiben: X- Y-Position des Steins, waag- oder senkrecht, Punktanzahl links bzw. oben, rechts bzw. unten.
Aber:
1. Wo im Array schreibe ich das? 1. Stein, 1. Position im Array, 2. Stein, 2. Position, 3.-3., 4.-4. usw.?
2. Wie soll das Programm am Board erkennen, wo welcher Stein liegt? Es ist ja alles bloss eine Grafik ohne Koordinaten!


Bergmann89 - Fr 04.06.10 18:51

@1: rischtisch
@2: Eben weil es bloß eine Grafik ist speichert man die Daten ja im Array. Wen du es mit TDominostein machen willst, wie Stundenplan es geschriben hat, dann reicht ein eindimensionales Array, mit der Größe gleich der Anzahl der Steine. Mithilfe der Daten im Array prüfst du dann ob ein Stein abgelegt werden darf oder nicht, wenn ja legst du ihn so ab wie du das grad beschrieben hast und trägst einen neuen Stein ins Array ein. Wenn der Stein nicht passt machst du einfach nix, bzw. sagst dem User, das er da keinen Stein ablegen darf.


galagher - Fr 04.06.10 19:06

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Wen du es mit TDominostein machen willst, wie Stundenplan es geschriben hat, dann reicht ein eindimensionales Array, mit der Größe gleich der Anzahl der Steine.

Soweit klar!

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Mithilfe der Daten im Array prüfst du dann ob ein Stein abgelegt werden darf oder nicht

Ich kriege das nicht in meinen Kopf: Wie soll das Program wissen, ob ich den Stein an einer bestimmten Stelle ablegen darf? Ich meine, wie weiss es, welche Position im Array abgefragt werden muss?

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
wenn ja legst du ihn so ab wie du das grad beschrieben hast und trägst einen neuen Stein ins Array ein.

Wieder taucht die Frage auf: Wie weiss das Program, an welche Position im Array geschrieben werden mus? >>> :idea: Ach ja, mit dem Stein weiss es das! 15. Stein = 15. Position!

Ich glaube, ich begreife das langsam: Ist ja vollkommen wurscht, wie das grafisch aussieht und ob ein Stein Left 27, Top 88 liegt oder sonstwo! Jeder Stein repräsentiert einen Arrayplatz, und dort sind seine Werte gespeichert, und damit weiss ich als Spieler und das Programm weiss als Spieler, ob das Ablegen möglich ist oder nicht!


Bergmann89 - Fr 04.06.10 19:18

Rischtisch, und das Programm muss nich wissen, wo der Stein im Array liegt, den es prüfen muss. Das Programm prüft einfach jeden Stein^^ Wie heißt es doch so schön, der PC is SCHNELL, aber DUMM :)

so sieht das ganze dann aus:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
type
  TDirection = (diVert, diHorz);
  TDomino = packed record
    //X-Koordinate des oberen/linken Steins
    // Y-Koodinate des oberen/linken Steins
    // |  PunktWert des oberen/linken Steins
    // |  |   PunktWert des unteren/rechten Steins
    X, Y, P1, P2: Integer;
    Dir: TDirection;
  end;
  TDominoArray = array of TDomino;

var
  DominoList: TDominoArray;

//berechnet den Abstand zwischen 2 Punkten
//@P1: Punkt 1;
//@P2: Punkt 2;
//@result: Abstand der Beiden Punkte;
function GetDist(P1, P2: TPoint): Integer;
begin
  result := Abs(P1.X-P2.X) + Abs(P1.Y-P2.Y);
end;

//Prüft ob ein Domino abgelegt werden kann oder nicht und
//legt ihn ggf in das Array
//@d: Domino der geprüft/abgelget werden soll;
//@result: TRUE wenn Stein abgelegt wurde, sonst FALSE;
function TForm1.CheckDomino(d: TDomino): Boolean;
var
//aktueller Domino der Liste
  d2: TDomino;
//Koordinaten des oberen/linken Teils des Domino
//|   Koordinaten des unteren/rechten Teils des Domino
//|   |   Koordinaten des oberen/linken Teils von d2
//|   |   |   Koordinaten des unteren/rechten Teils von d2
  P1, P2, P3, P4: TPoint;
//Schleifenariable
  i: Integer;
begin
  result := false;

  //die beiden Punkte des Domino ermitteln
  P1 := Point(d.X, d.Y);
  if d.Dir = diVert then begin
    P2 := Point(d.X + 1, d.Y);
  end else begin
    P2 := Point(d.X, d.Y + 1);
  end;

  for i := 0 to High(DominoList) do begin
    d2 := DominoList[i];
    //die beiden Punkte des Domino ermitteln
    P3 := Point(d.X, d.Y);
    if d.Dir = diVert then begin
      P4 := Point(d.X + 1, d.Y);
    end else begin
      P4 := Point(d.X, d.Y + 1);
    end;

    //Dominozahlen Prüfen
    //d oben/links MIT d2 oben/links
    if (GetDist(P1, P3) = 0then exit; //Abstand ist 0, beide Steine liegen aufeinander
    if (GetDist(P1, P3) = 1and (d.P1 <> d2.P1) then exit; //Abstand ist 1, aber Steine haben nicht den selben Wert
    //d oben/links MIT d2 unten/rechts
    if (GetDist(P1, P4) = 0then exit;
    if (GetDist(P1, P4) = 1and (d.P1 <> d2.P2) then exit;
    //d unten/rechts MIT d2 oben/links
    if (GetDist(P2, P3) = 0then exit;
    if (GetDist(P2, P3) = 1and (d.P1 <> d2.P1) then exit;
    //d unten/rechts MIT d2 unten/rechts
    if (GetDist(P2, P4) = 0then exit;
    if (GetDist(P2, P4) = 1and (d.P1 <> d2.P2) then exit;
  end;
  //Wenn hier noch nicht beendet, dann passt der Stein
  //also Stein in die Liste eintragen
  SetLength(DominoList, Length(DominoList) + 1); //Array um 1 Element erweitern
  DominoList[High(DominoList)] := d; //Stein in Letztes Element eintragen
 
  result := true
end;

ich habs nich getestet, aber eig sollte es so hin haun, wenn nich hast du n Ansatz an dem du weiter arbeiten kannst

MfG Bergmann


galagher - Fr 04.06.10 19:44

Zuerst: Vielen Dank, das hätte ich nicht hinbekommen!
Nun ja, der 1. Stein klappt ja, aber jeder weitere nicht: :(

Delphi-Quelltext
1:
  if not CheckDomino(aValue) then Caption := Caption+'NO!';                    

Die ganze Prozedur sieht so aus:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
procedure TForm1.imgSteinClick(Sender: TObject);
var
  aValue: TDomino;
begin
 if isClick then  //OnClick nicht nach Verschieben+Maustaste loslassen auslösen
 begin
  aValue.X := Left;
  aValue.X := Top;
  aValue.P1 := 4;
  aValue.P2 := 2;
  aValue.Dir := diVert;

  if not CheckDomino(aValue) then caption := caption+'NO!'//
  else
  with (Sender as TImage) do
  begin
   Board.Canvas.Draw(Left, Top, Picture.Graphic);
   Picture.Bitmap := nil;
  end;
 end;

 isClick := True;
end;


Bergmann89 - Fr 04.06.10 20:09

Hey,

du hast die Position des Steins ja fest im Code, da musst du Mausabhängige Koordinaten berechnen. Angenommen jder Stein is 32*64, also muss jedes Feld 32*32 sein, dann wären die FeldKoordinaten so:

Delphi-Quelltext
1:
2:
  aValue.X := Left div 32;
  aValue.Y := Top div 32

Außerdem hast du zwei mal aValue.X ;)

MfG Bergmann


galagher - Fr 04.06.10 20:40

Das Programm macht noch nicht viel, daher weise ich jetzt erstmal einfach iP1 und iP2 die Punktewerte zu. Es macht selbst noch gar nichts.

Also: Ich habe zur Zeit nur 1 Stein (3:6) im Spiel, also:

Delphi-Quelltext
1:
2:
iP1 := 3;
iP2 := 6;

Das erste Setzen ist ok.

Dann weise ich zu:

Delphi-Quelltext
1:
2:
iP1 := 6;
iP2 := 3;

Aber ab da klappt es schon nicht mehr.

Das BoardImage befindet sich an Left := 0, Top := 0. Die Steine sind 38x74 Pixel gross.

Das klappt auch nicht:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
procedure TForm1.imgSteinClick(Sender: TObject);
var
  aValue: TDomino;
begin
 if isClick then  {OnClick nicht nach Verschieben+Maustaste loslassen auslösen}
 begin
(*  aValue.X := Left;
  aValue.X := Top;
*)

  aValue.X := (Sender as TImage).Left div 38;
  aValue.Y := (Sender as TImage).Top div 38;
  aValue.P1 := iP1;//3;
  aValue.P2 := iP2;//6;
  aValue.Dir := diVert;

  if not CheckDomino(aValue) then caption := caption+'NO!'//
  else
  with (Sender as TImage) do
  begin
   Board.Canvas.Draw(Left, Top, Picture.Graphic);
//   Picture.Bitmap := nil;
  end;
 end;

 isClick := True;
end;

Ausserdem verstehe ich immer noch nicht, wie das Programm das Array mit den Mauskooedinaten vergleichen soll, man kann doch nicht pixelgenau einen bestimmten Punkt treffen!


Bergmann89 - Sa 05.06.10 00:18

Hey,

das Programm prüft die Steine ja auch nicht pixelgenau, sondern die Variable X bzw. Y von TDomino gibt die Feldposition an. Ich bin grad bisl durcheinander, was deine Images angeht. Kannst du das Projekt mal hochladen, da guck ich mir das ma an...

€: Ich hab mir ma schnell was zusammen gebastelt. Ich denke es sollte ausreichend kommentiert sein, das du es verstehst. Wenn du trotzdem noch fragen hast, dann kannst du ja nochma schreiben.

MfG Bergmann.


galagher - Sa 05.06.10 09:28

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
€: Ich hab mir ma schnell was zusammen gebastelt.

Alle Achtung!

Ich habe es so, dass ich die Steine beliebig verschieben kann. Das klappt aber dann mit dem Array zusammen nicht.


Bergmann89 - Sa 05.06.10 17:31

Hey,

in meiner Check-Funktion war ein kleiner Fehler drin. Jetz funktioniert es soweit, das du den 6-3-Stein drehen un platzieren kannst. Überall wo ich im Code was geändert hab hab ich n Kommentar drüber geschrieben. Dann is mir noch aufgefallen, das du globale Variablen benutzt:

Delphi-Quelltext
1:
2:
3:
4:
5:
var
  Form1: TForm1;
  isClick: Boolean;
  iP1, iP2: Integer;
  DominoList: TDominoArray;

Die sollte man wenn möglich umgehen, die kannst du bei der private Deklaration von Form1 mit rein schreiben (wie ich es mit dem Domino gemacht hab). Und noch ein kleiner Tipp: guck dir ma den Debugger an, da kann man Schritt für Schritt den Code ausführen ( F7 bzw. F8 ), so merkt man wenn was nich stimmt. Wenn du ne Variable markierst un dann STRG+F5 drückst kannst du dir den Wert der Variable angucken. So findet man schnell und sicher Fehler ;)

MfG Bergmann


galagher - Sa 05.06.10 18:08

Hallo Bergmann89!
user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Dann is mir noch aufgefallen, das du globale Variablen benutzt:

Delphi-Quelltext
1:
2:
3:
4:
5:
var
  Form1: TForm1;
  isClick: Boolean;
  iP1, iP2: Integer;
  DominoList: TDominoArray;

Die sollte man wenn möglich umgehen, die kannst du bei der private Deklaration von Form1 mit rein schreiben (wie ich es mit dem Domino gemacht hab).
Das wusste ich nicht! Dass man globale Variablen vermeiden sollte, weiss ich, aber warum, weiss ich nicht! Was ist der Unterschied?

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Wenn du ne Variable markierst un dann STRG+F5 drückst kannst du dir den Wert der Variable angucken. So findet man schnell und sicher Fehler ;)
STRG+F5 wusste ich auch nicht!

Ich werd's mir anschauen! Danke für deine Geduld und Hilfe!

Grafische Programme - also Spiele - liegen mir offenbar nicht so. Ich hab's mir einfacher vorgestellt. War nahe daran, das Projekt aufzugeben, ich habe z.B. noch keine Ahnung, wie ich das Programm spielen lasse; da gibt es noch so viel... :nixweiss: :?

Dein Code hat einen entscheidenden Vorteil: Er funktioniert! Aber leider verstehe ich ihn nicht. Also, das Prinzip schon, aber nicht so wirklich...


galagher - Sa 05.06.10 18:30

Glück auf, Bergmann!

Habe es erst jetzt getestet! Das ist ja mein Programm! Danke für die Korrektur meines Programms! So stelle ich mir das vor! (Habe zuerst die .exe ausgeführt und dann nur kurz den Sourcecode angeschaut).

Muss erst mal durch den Code durchsteigen, wird sicher etwas dauern! Aber - ja, genau so! :D
Wenn das was wird, wird sicher "bergmann89" im Info aufscheinen!

Ok, andere Steine kommen noch, aber es funktioniert!


Bergmann89 - Sa 05.06.10 18:38

Hey,

warum man globale Variablen nur ur Nt verwenden sollte weiß ich eig auch nich geanz genau^^ Warscheinlich der Übersicht halber, bzw um Ressourcen zu sparen, denn globale Variablen belegen immer Speicher, Objekt-Variablen nur dann, wenn das Objekt daszu exestiert.

Für einen Anfänger sind Spiele mit grafischen Elementen immer die nächste Herausvorderung. Aber ich weiß ja nich was du bis jetzt so alles schon gemacht hast. Dir wird es jetzt vlt schwer fallen, aber wenn du das jetzt durchziehst sollte es dich einen ganzen Schritt weiter bringen.
Oder du legst das Projekt erstmal auf Eis und probierst dich an was einfachen, um das Verständniss fürs Programmieren zu bekommen. Sowas wie Taschenrechner, Notepad-Klone, ... sind relativ einfache Programme. Danch kann man sich dann mit Hex-Editoren, kleinen Spielen oder ähnlichen beschäftigen. Und was du dir auch auf alle Fälle angucken solltest ist DAS [http://www.christian-stelzmann.de/artikel/crashkurs.htm] da steht das Wichtigste drin, was man über Delphi und das Porgrammieren wissen sollte. Und man kann auch immer mal nachgucken wenn man nicht weiter weiß.

So viel hab ich am Code gar nicht geändert, vlt 20 Zeilen oder so. Wenn du Fragen hast schreib einfach, dann helfen wir weiter.

MfG Bergmann.


galagher - So 06.06.10 09:17

Hallo!

Habe bis jetzt kaum Programme mit Grafik gemacht, ansonsten schon auch grössere Sachen als "nur" einen Taschenrechner (ist auch interessant gewesen)!

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Wenn du Fragen hast schreib einfach, dann helfen wir weiter.

Naja, ist nach der ganzen Hilfe ja unverschämt - aber ich frage mal: Wie könnte das Programm als Spieler selbst einen Stein setzen? Ich möchte jetzt keinen Code, aber vielleicht ein paar Tipps oder eine Beschreibung!

Mein Gedanke: Merken, wo jeder Stein liegt, angrenzende Koordinaten errechnen und durchprobieren, ob dort ein Stein passt. Theoretisch...


F34r0fTh3D4rk - So 06.06.10 11:40

Ich sag immer: Logik, Daten und Darstellung trennen. Denn was du zunächst versucht hast, war die Logik über die Darstellung zu implementieren. Das geht zwar, macht in diesem Fall aber nicht viel Sinn. Man kann Domino komplett funktionierend ohne, bzw. unabhängig von der Darstellung entwickeln und dann die Darstellung über eine Schnittstelle einbauen. So muss man, wenn man sich für eine andere Darstellungsform entscheiden sollte (3D statt 2D, text-basiert statt grafisch), nur noch den Code für die Darstellung, aber nicht die eigentliche Logik des Spiels umschreiben.

Dazu vielleicht ganz hilfreich: Model View Controller [http://de.wikipedia.org/wiki/Model_View_Controller] (Für Domino vielleicht etwas übertrieben, aber hier wird das Prinzip deutlich)
und: Beobachter [http://de.wikipedia.org/wiki/Beobachter_%28Entwurfsmuster%29]


Bergmann89 - So 06.06.10 19:03

Hey,

@F34r0fTh3D4rk: in gewisserweiße sind die Daten, Logik un Darstellung getrennt. Die Daten liegen alle in dem Array. Wenn der User eine Eingabe macht, bzw. wenn die KI einen Steni legen soll, werden die Daten im Array geändert (Logik) und zum Schulss greift die OnPaint-Methode des PaintBox auf das Array zu und zeichnet alles (Darstellung). Wozu würdest du den Observer einsetzten? Bsp.: Das der Datenbereich dem Observer sagt das sich die Daten geändert haben un der dann der Darstellung sagt, das neu gezeichnet werden muss?!

@galagher: für die KI (wenn man es so nennen kann) würd ich sowas wie nen BruteForce [http://de.wikipedia.org/wiki/Bruteforce] machen:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
Punkt auf 0,0 setzen (oben links)
while not CheckDomino do
  drehe stein 
  wenn 4 mal gedreht wurde, dan...
    verschiebe Position um ein Feld nach rechts
    wenn Position auserhalb des spielfeldes, dann...
      setze X-Position auf 0
      setze Y-Position in die nächste Zeile


so würde die KI die Steine immer erst versuchen oben links anzulegen. Man könnte das ganze auch auf Random umbauen, da muss man dann aber noch paar Tests einbauen, dass das auch wirklich zum Ende kommt.

MfG Bergmann


galagher - So 06.06.10 20:18

user profile iconF34r0fTh3D4rk hat folgendes geschrieben Zum zitierten Posting springen:
Ich sag immer: Logik, Daten und Darstellung trennen.
Das versuche ich bei jedem Programm, schaffe es aber nicht immer wirklich konsequent!

@Bergmann89:
Nochmals Dank! Einen Code nach diesem Beispiel werde ich einbauen und sehen, wie weit ich komme! Die Anzahl der Möglichkeiten ist ja nicht unbegrenzt, und daher werden Anweisungen nach deinem Vorschlag sicher auch in akzeptabler Zeit ausgeführt.


jaenicke - So 06.06.10 20:29

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
warum man globale Variablen nur ur Nt verwenden sollte weiß ich eig auch nich geanz genau^^ Warscheinlich der Übersicht halber, bzw um Ressourcen zu sparen, denn globale Variablen belegen immer Speicher, Objekt-Variablen nur dann, wenn das Objekt daszu exestiert.
Damit hat das weniger zu tun, ist aber natürlich auch richtig.

Aber stell dir einmal z.B. ein dynamisch erstelltes Formular in einem Editor vor. Du erstellst davon jetzt zwei, weil zwei Dateien parallel geöffnet werden. So, jetzt schreibst du den Dateinamen aber in eine globale Variable. Davon gibt es aber nur eine. Wie willst du dadrin also zwei Dateinamen unterbringen?

Globale Variablen gehören eben nicht zu einem bestimmten Objekt, sondern zu der Unit, in der die Klasse, also der Konstruktionsplan der Objekte, steht. Dementsprechend darf man auch nicht versuchen objektbezogene Dinge darin abzulegen. Was zu einem Objekt, also einer von ggf. mehreren Instanzen der Klasse, gehört, gehört auch in die Klasse.

Das ist auch der Grund weshalb man nicht Form1.Caption oder so schreiben sollte sondern Self.Caption oder nur Caption. Denn sonst greift man ggf. plötzlich auf eine falsche oder nicht existierende Instanz eines Formulars zu. Und noch öfter passiert das bei eigenen Klassen, denn bei denen gibt es natürlich sehr oft mehrere Instanzen.

Zusätzlich sind globale Variablen nicht gerade übersichtlich, sagtest du ja schon, und objektorientiert schon gar nicht.


galagher - So 06.06.10 20:53

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Aber stell dir einmal z.B. ein dynamisch erstelltes Formular in einem Editor vor.
Das war einmal einer meiner Anfangsfehler - plötzlich hat dar Editor glatt unter einem falschen Dateinamen gespeichert! Hier ist es mir seitdem klar. Ansonsten ist es wohl einfach Gewohnheit oder Bequemlichkeit. :oops:
Werde künftig versuchen, ohne globale Variablen auszukommen, gleich mal im Domino-Projekt!


galagher - Mo 07.06.10 17:38

Im Moment suche noch ich den Stein für das Programm aus. Wenn er irgendwo passt, funktioniert es, wenn nicht, rechnet sich das Programm den Speicher wund:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
var
  i, tmp: Integer;
begin
(*
Punkt auf 0,0 setzen (oben links)
while not CheckDomino do
  drehe stein
  wenn 4 mal gedreht wurde, dan...
    verschiebe Position um ein Feld nach rechts
    wenn Position auserhalb des spielfeldes, dann...
      setze X-Position auf 0
      setze Y-Position in die nächste Zeile
*)


 Domino.X := 0;
 Domino.Y := 0;
 while not CheckDomino(Domino) do
 begin
 //drehe stein
  for i := 0 to 3 do
  begin
    if Domino.Dir = diVert then begin
      tmp       := Domino.P1;
      Domino.P1 := Domino.P2;
      Domino.P2 := tmp;
      Domino.Dir := diHorz;
    end else Domino.Dir := diVert;
  end;

  //wenn 4 mal gedreht wurde, dan...
  //verschiebe Position um ein Feld nach rechts
  Inc(Domino.X);

  //wenn Position auserhalb des spielfeldes, dann...
  if Domino.X > Board.Width then
  begin
   Domino.X := 0;  //setze X-Position auf 0
   Inc(Domino.Y);  //setze Y-Position in die nächste Zeile
  end;
 end;

 //Und zeichne das Ganze!
 Board.Canvas.Draw(Domino.X * iStoneWidth, Domino.Y * iStoneWidth, imgStein.Picture.Graphic);
end;
 iStoneWidth, imgStein.Picture.Graphic);
end;

Wo ist / sind meine Fehler? Kann man aus mehreren möglichen Plätzen per Random einen auswählen? Das würde das Spiel interessanter machen.

//Edit: Es funktioniert auch dann nicht immer, wenn der Stein passt!


Bergmann89 - Di 08.06.10 00:35

Hey,

geh mal Schritt für Schritt mit dem Debuger durch. Der Sinn der Sache war ja, das abwechselnd geprüft und dann was geändert wird. Also müsste er nach jedem Drehen, einmal in den While-Kopf spingen, wo das CheckDomino steht.
Damit er die Schleife auch verlässt, wenn kein Stein passt, musst du gucken wann die Y Position außerhalb des Feldes ist und dann setzt du einfach ein break;, das beendet die Schleife.
Das mit dem random-Wert geht auch, dazu musst du nur alle Positionen des Feldes abschreiten (3 for-Schleifen: X, Y, Drehen) und gucken ob der Stein passt. wenn ja schreibst du ihn - ähnlich wie beim CheckDomino - in ein Array. Dazu musst du die CheckDomino-Funktion aber ändern, bzw. ne 2. machen, da die jetzige den Stein ja gleich hinlegt, und die neue soll ja nur gucken ob er passt. Dann hast du alle möglichen Stellen in dem Array un kannst dir eine raussuchen.

MfG Bergmann.


galagher - Di 08.06.10 17:42

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Der Sinn der Sache war ja, das abwechselnd geprüft und dann was geändert wird. Also müsste er nach jedem Drehen, einmal in den While-Kopf spingen, wo das CheckDomino steht.
Macht der Code ja, es klappt aber nicht richtig. ZB. legt wird der Stein immer dann horizontal hingelegt, wenn ich ihn zuvor drehe. Ist er vertikal, wird er vertikal gelegt. Passen zB. 5 Punkte, wenn diese unten sind, dann legt er den Stein nur dann ab, wenn ich ihm vorher die Punkte nach unten drehe. Irgendwie wirkt sich das Drehen nicht aus...

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Damit er die Schleife auch verlässt, wenn kein Stein passt, musst du gucken wann die Y Position außerhalb des Feldes ist und dann setzt du einfach ein break;, das beendet die Schleife.
Ok, klar: if Domino.Y > Board.Height then break;

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Das mit dem random-Wert geht auch
Solange es nicht mal so klappt, ist das vorläufig egal!

Ich habe also 2 Versionen gebastelt, beide funktionieren, aber eben nur wie oben beschrieben:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
//So: -----------------------------------------------------------------
 Domino.X := 0;
 Domino.Y := 0;

 repeat
  for i := 0 to 3 do
  begin
    if Domino.Dir = diVert then begin
      tmp       := Domino.P1;
      Domino.P1 := Domino.P2;
      Domino.P2 := tmp;
      Domino.Dir := diHorz;
    end else Domino.Dir := diVert;
  end;

  //wenn 4 mal gedreht wurde, dan...
  //verschiebe Position um ein Feld nach rechts
  Inc(Domino.X);

  //wenn Position auserhalb des spielfeldes, dann...
  if Domino.X > Board.Width then
  begin
   Domino.X := 0;  //setze X-Position auf 0
   Inc(Domino.Y);  //setze Y-Position in die nächste Zeile
  end;

 until (CheckDomino(Domino)) or (Domino.Y > Board.Height);



//Oder so: -----------------------------------------------------------------
 Domino.X := 0;
 Domino.Y := 0;

 while not CheckDomino(Domino) do
 begin
 //drehe stein
  for i := 0 to 3 do
  begin
    if Domino.Dir = diVert then begin
      tmp       := Domino.P1;
      Domino.P1 := Domino.P2;
      Domino.P2 := tmp;
      Domino.Dir := diHorz;
    end else Domino.Dir := diVert;
  end;

  //wenn 4 mal gedreht wurde, dan...
  //verschiebe Position um ein Feld nach rechts
  Inc(Domino.X);

  //wenn Position auserhalb des spielfeldes, dann...
  if Domino.X > Board.Width then
  begin
   Domino.X := 0;  //setze X-Position auf 0
   Inc(Domino.Y);  //setze Y-Position in die nächste Zeile
  end;
  if Domino.Y > Board.Height then break;
 end;


galagher - Di 08.06.10 20:26

Jetzt funktioniert es, aber es ist langsam, weil ich das Image mitdrehen muss. Habe versucht, einen Integer t zu verwenden und den Stein vor dem Zeichnen t-Mal zu drehen, das klappt aber nicht. Also muss ich etwas finden, das den gelichen Effekt hat wie die Prozedur Drehen90Grad.

Das Programm spielt jetzt und dreht und legt die Steine richtig! :lol: :D

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
var
  i, n, t, tmp: Integer;
begin
(*
Punkt auf 0,0 setzen (oben links)
while not CheckDomino do
  drehe stein
  wenn 4 mal gedreht wurde, dan...
    verschiebe Position um ein Feld nach rechts
    wenn Position auserhalb des spielfeldes, dann...
      setze X-Position auf 0
      setze Y-Position in die nächste Zeile
*)


 Domino.X := 0;
 Domino.Y := 0;
 n := 0;
 while not CheckDomino(Domino) do
 begin
 //drehe stein
//  for i := 0 to 3 do
  begin
   Drehen90Grad(imgStein.Picture.Bitmap);
//   Inc(t);
    if Domino.Dir = diVert then begin
      tmp       := Domino.P1;
      Domino.P1 := Domino.P2;
      Domino.P2 := tmp;
      Domino.Dir := diHorz;
    end else Domino.Dir := diVert;
  end;

  Inc(n);

  if n = 3 then
  begin
  //wenn 4 mal gedreht wurde, dan...
  //verschiebe Position um ein Feld nach rechts
  Inc(Domino.X);
  n := 0;
//  t := 0;
  end;

  //wenn Position auserhalb des spielfeldes, dann...
  if Domino.X > Board.Width then
  begin
   Domino.X := 0;  //setze X-Position auf 0
   Inc(Domino.Y);  //setze Y-Position in die nächste Zeile
  end;
  if Domino.Y > Board.Height then break;
 end;

 //Und zeichne das Ganze!
// for i := 0 to t do Drehen90Grad(imgStein.Picture.Bitmap);
 Board.Canvas.Draw(Domino.X * iStoneWidth, Domino.Y * iStoneWidth, imgStein.Picture.Graphic);
end;

Das kann man aber sicher noch optimieren.


Narses - Di 08.06.10 20:49

Moin!

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Jetzt funktioniert es, aber es ist langsam, weil ich das Image mitdrehen muss. Habe versucht, einen Integer t zu verwenden und den Stein vor dem Zeichnen t-Mal zu drehen, das klappt aber nicht. Also muss ich etwas finden, das den gelichen Effekt hat wie die Prozedur Drehen90Grad.
Naja, die Images werden ja nicht jeweils mehrere MB haben, oder? :? Einfach 4 ImageListen nehmen, für jede Himmelsrichtung eine. :nixweiss:

cu
Narses


galagher - Di 08.06.10 20:58

user profile iconNarses hat folgendes geschrieben Zum zitierten Posting springen:
Naja, die Images werden ja nicht jeweils mehrere MB haben, oder? :? Einfach 4 ImageListen nehmen, für jede Himmelsrichtung eine. :nixweiss:Narses
Ich will nicht auch noch jeden Stein 4x im Programm haben, 1x genügt!
Elegant hat es ja user profile iconBergmann89 in seinem Programm gelöst, da wird jeder Stein aus wenigen Bildteilen sozusagen zusammengesetzt.


Narses - Di 08.06.10 21:00

Moin!

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Ich will nicht auch noch jeden Stein 4x im Programm haben, 1x genügt!
Du kannst ja beim Programmstart die 3 berechenbaren aus einer gespeicherten ableiten. :idea: Klar, muss man nicht 4x statisch ablegen. :nixweiss:

cu
Narses


Bergmann89 - Mi 09.06.10 13:24

Hey,

es würde auch helfen, wenn man das Zeichnen, während der Suche der passenden Stelle einfach weg lässt ;) Also nur die Daten manipulieren und am Ende zeichnen, so sollte es schnell genug sein, das der User es kaum mitbekommt. Kleinen Schönheitsfehler hab ich noch gefunden: if Domino.Y > Board.Height then break; ist nich ganz richtig, da du ja bei 0 anfängst zu Zählen muss die Schleife beendet werden, wenn die Position größer oder gleich der Feldhöhe ist...

MfG Bergmann.


galagher - Mi 09.06.10 17:52

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
es würde auch helfen, wenn man das Zeichnen, während der Suche der passenden Stelle einfach weg lässt ;) Also nur die Daten manipulieren und am Ende zeichnen
Wenn ich das Drehen während der Suche weglasse, wird der Stein falsch hingelegt! Habe schon versucht, je einen Integer für diVert und diHorz mitzählen zu lassen und danach, vor dem Zeichnen, den Stein entsprechend oft zu Drehen - klappt alles nicht. Die meisten Steine liegen dann falsch.

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
if Domino.Y > Board.Height then break; ist nich ganz richtig, da du ja bei 0 anfängst zu Zählen muss die Schleife beendet werden, wenn die Position größer oder gleich der Feldhöhe ist...

Also muss es heissen: if Domino.Y >= Board.Height then break; und auch if Domino.X >= Board.Width then?


Bergmann89 - Mi 09.06.10 19:07

Hey,

wieso sollte das mit dem nicht Zeichnen nicht gehen? Du hast doch, wenn die while-Schleife verlassen wird noch dein n, und das gibt an wie oft du die Image drehen musst, also einfach ne For-Schleife von 0 bis n-1 un da das Drehen90Grad rein. Danach bloß noch zeichnen. Fertig ;)
user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Also muss es heissen: if Domino.Y >= Board.Height then break; und auch if Domino.X >= Board.Width then?
rischtisch :)

MfG Bergmann.


galagher - Mi 09.06.10 19:18

Hallo!

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
wieso sollte das mit dem nicht Zeichnen nicht gehen? Du hast doch, wenn die while-Schleife verlassen wird noch dein n,
Das n wird aber nach je 4x Drehen wieder auf 0 gesetzt!

Nun möchte ich noch, dass das Programm aus mehreren Möglichkeiten eine auswählt.

Hab's jetzt so gelöst, läuft scnhell und korrekt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
 while not CheckDomino(Domino) do
 begin
 //drehe stein
  if Domino.Dir = diVert then begin
     tmp       := Domino.P1;
     Domino.P1 := Domino.P2;
     Domino.P2 := tmp;
     Domino.Dir := diHorz;
   end else
   begin
    Domino.Dir := diVert;
    Inc(h);  //Zähler für horizontal
   end;

  {Zähler für Drehen erhöhen}
  Inc(n);

  if n = 3 then
  begin
   //wenn 4 mal gedreht wurde, dan...
   //verschiebe Position um ein Feld nach rechts
   Inc(Domino.X);
   n := 0;
  end;

  //wenn Position auserhalb des spielfeldes, dann...
  if Domino.X  >= Board.Width then
  begin
   Domino.X := 0;  //setze X-Position auf 0
   Inc(Domino.Y);  //setze Y-Position in die nächste Zeile
  end;
  if Domino.Y >= Board.Height then break;
 end;


 //Und zeichne das Ganze!
 {Den Stein 1x drehen, wenn er horizontal abgelegt werden soll}
 if Domino.Dir = diHorz then Drehen90Grad(imgStein.Picture.Bitmap);
 {Den Stein noch 3x drehen, wenn der Zähler h eine ungerade Zahl ist, dadurch}
 {wird er so gedreht, dass die Punktezahl auf der richtigen Seite ist.}
 if Odd(h) then
 begin
  Drehen90Grad(imgStein.Picture.Bitmap);
  Drehen90Grad(imgStein.Picture.Bitmap);
 end;

 {Den Stein zeichen}
 Board.Canvas.Draw(Domino.X * iStoneWidth, Domino.Y * iStoneWidth, imgStein.Picture.Graphic);

 {Den Stein (imgStein) ggf. wieder vertikal stellen}
 if Domino.Dir = diHorz then Drehen90Grad(imgStein.Picture.Bitmap);


Bergmann89 - Mi 09.06.10 19:23

Hey,

so geht das natürlich auch, aber im endeffekt is es das gleiche was ich grad gesagt hab^^ Wenn das Proggi dann eine random-Stelle auswählen soll, musst du den n eben mit speichern, wenn du dir das Array mit den möglichen Stellen zusammen bastelst. Musst du ja jetzt mit deinem h auch bloß machen.

MfG Bergmann.


galagher - Do 10.06.10 17:37

Hallo!

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Wenn das Proggi dann eine random-Stelle auswählen soll, musst du den n eben mit speichern, wenn du dir das Array mit den möglichen Stellen zusammen bastelst.
:nixweiss: Ok, ich habe vor, die Funktion CheckDomino um einen bool'schen Parameter zu erweitern: False bedeutet, es wird der erste Platz genommen, an dem der Stein passt, True bedeutet, es werden zuerst alle Möglichkeiten gesammelt und dann mit Random eine ausgewählt.

Also soll der Aufruf so aussehen, wenn zB. der Spieler einen Stein setzt: CheckDomino(Domino, False)
und so, wenn das Programm einen setzen soll: CheckDomino(Domino, True)

Kannst du mir Tipps geben, was ich an CheckDomino abändern muss? Ich denke, die Prüfung der angrenzenden Steine bleibt gleich, aber ich will ja nicht sofort etwas ins Array eintragen und True zurückgeben und damit den Stein schon ablegen, sondern erst alle Möglichkleiten erfassen, und erst dann ablegen.


Delphi-Quelltext
1:
2:
  SetLength(DominoList, Length(DominoList) + 1); //Array um 1 Element erweitern
  DominoList[High(DominoList)] := d; //Stein in Letztes Element eintragen

Was muss ich hier ändern?


Bergmann89 - Do 10.06.10 17:41

Hey,

das ist eine schlechte Idee, ich würd's so machen: Das eintragen der Steine von CheckDomino in eine Extra Prozedur auslagern (z.B. PlaceDomino). dann Würd ich ne Prozedur machen, die alle Möglichkeiten durchprobiert, und wenn CheckDomino TRUE ist, dann wird ne Proezur aufgerufen, die die mögliche Position in das Array einträgt (z.B. AddPosiblePos). Am Ende kannst du dann mit random die Position bestimmten, mit PlaceDomino den Stein ablegen und mit Paint neu zeichnen...

MfG Bergmann


galagher - Do 10.06.10 19:05

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
das ist eine schlechte Idee, ich würd's so machen: Das eintragen der Steine von CheckDomino in eine Extra Prozedur auslagern (z.B. PlaceDomino).
Das war einfach! :mrgreen:
Der ganze Rest nicht.

Ja, ich verstehe, was du meinst, ist ja auch verständlich, aber wie giesse ich das in Quellcode?

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
dann Würd ich ne Prozedur machen, die alle Möglichkeiten durchprobiert, und wenn CheckDomino TRUE ist, dann wird ne Proezur aufgerufen, die die mögliche Position in das Array einträgt (z.B. AddPosiblePos).
Jedesmal, wennn CheckDomino TRUE ist, soll genau was in welches Array eingetragen werden?
Also: Die Prozedur, die das tut, heisst AddPosiblePos, und sie trägt wo was ein? Dieses Array kann ja nicht DominoList sein?

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Am Ende kannst du dann mit random die Position bestimmten, mit PlaceDomino den Stein ablegen und mit Paint neu zeichnen...
So in etwa:

Delphi-Quelltext
1:
2:
3:
 i := -1;
 while i = -1 do
  i := Random(High(DominoPossiblePos));  //alles, was nicht -1 ist, ist eine Position


Nie wieder ein Programm, bei dem auch das Programm etwas tun soll! :motz:


//Edit:
Das zB. tut gar nichts:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TForm1.CheckDominos(d: TDomino);
var
  x, y: Integer;
begin
 for x := 0 to Board.Width do
  for y := 0 to Board.Height do
   if CheckDomino(d) then
    memo1.Lines.Add('A');  //natürlich nur zum Testen!
end;


Bergmann89 - Do 10.06.10 19:37

Hey,

da musst du dir ein neues Array anlegen. Die Prozedur CheckDominos würd ich umbenennen, weil man so schnell durcheinander kommt (CheckDomino <> CheckDominos) nenn sie doch GetAllPosiblePos, BruteFocePosition oder sowas in der Art. Am anfang leerst du das Array (Bsp.: PosiblePositionList) mit SetLength(0). Dann kommen 3 Schleifen (2 hast du ja schon richtig). Schleife für X, Schleife für Y un Schleife für Drehen. Dann probierst du alle Schleifen durch, und wenn CheckDomino = TRUE, dann wird AddPosiblePos aufgerufen. Da übergibst du einfach das Domino-Record un schreibst das wie bei dem DominoList-Array in das Array rein.

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
var Domino: TDomino;
für alle X-Positionen mache...
  X-Position von Domino ändern
  für alle Y-Positionen mache... 
    Y-Position von Domino ändern
    für alle drehungen mache...
      wenn CheckDomino(Domin) dann...
        AddPosiblePos(Domino)
      drehe Domino

dann brauchst du nur noch ein beliebiges raus suchen und zu platzieren un das ganze neu zeichnen:

Quelltext
1:
2:
3:
Domino = suche zufälliges Domino aus der Liste der möglichen Positionen
PlaceDomino(Domino)
Zeichne das Feld neu

So in etwas sollte das Funktionieren...

€:
user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Nie wieder ein Programm, bei dem auch das Programm etwas tun soll! :motz:
Das is doch aber grad der Reiz am Programmieren ;) Man brauch da nur bisl Erfahrung, und wenn du das Domino dann fertsch un auch verstanden hast, dann sollte dir das bei andern KIs, die du evtl mal programmierst leichter fallen...

MfG Bergmann


galagher - Do 10.06.10 19:54

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Das is doch aber grad der Reiz am Programmieren ;) Man brauch da nur bisl Erfahrung, und wenn du das Domino dann fertsch un auch verstanden hast, dann sollte dir das bei andern KIs, die du evtl mal programmierst leichter fallen...
Leider fange ich immer wieder mit derartigen - grafischen - Programmen an, zulezt mit einem Mensch ärgere dich nicht, bei dem ich die Grafik, die Kegel, den Würfel, dessen Punkte, die Züge am Brett, Teile der Logik und Teile des Arrays hatte, und scheiterte an der Umsetzung im Array, weil ja jeder der 4 Spieler woanders anfängt... Alles lief mir auseinander, wurde immer komplexer und alle Ideen, die ich hatte, konnte ich letztlich nicht zusammenführen.

Ebenso hier:
http://www.delphi-forum.de/viewtopic.php?t=59658&highlight=bauernspiel
Diverse Zusatzfunktionen, die Oberfläche und Interaktion mit dem Benutzer habe ich selbst entwickelt, aber die eigentliche Logik, das Spiel, stammt von user profile iconalzaimar.

Dennoch gebe ich hier, bei Domino, nicht auf!

@Bergmann89:
Kann aber sein, dass ich noch oft nachfrage!


galagher - Fr 11.06.10 17:26

Hallo!

Ich habe eine Prozedur geschrieben (besser: Copy & Paste), die genau dasselbe macht wie CheckDomino, nur trägt es die möglichen "Plätze" nicht in das Array DominoList, sondern in AllPlacesList ein. Dann dachte ich, braucht man doch nur einen "Platz" per Random auswählen und schon spielt das Programm flexibel.

Ok, hier meine Ergebnisse - kurz dazu: Es funktioniert nur leider nicht.

Zuerst die Prozedur, die alle Möglichkeiten finden soll:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
procedure TForm1.FindAllPlaces(d: TDomino);
var
{aktueller Domino der Liste}
  d2: TDomino;
  {Koordinaten des oberen/linken Teils des Domino
  |   Koordinaten des unteren/rechten Teils des Domino
  |   |   Koordinaten des oberen/linken Teils von d2
  |   |   |   Koordinaten des unteren/rechten Teils von d2}

  P1, P2, P3, P4: TPoint;
  {Schleifenariable}
  i: Integer;

  {zum prüfen ob der Stein mind. 1 Feld von einem anderen entfernt ist
  bzw. ob der Stein an einem an einem anderen anliegt}

  contact: Boolean;

  {berechnet den Abstand zwischen 2 Punkten
  @P1: Punkt 1;
  @P2: Punkt 2;
  @result: Abstand der Beiden Punkte;}

  function GetDist(P1, P2: TPoint): Integer;
  begin
    result := Abs(P1.X-P2.X) + Abs(P1.Y-P2.Y);
  end;

begin
  contact := false;

  {die beiden Punkte des Domino ermitteln}
  P1 := Point(d.X, d.Y);
  if d.Dir = diHorz then begin
    P2 := Point(d.X + 1, d.Y);
  end else begin
    P2 := Point(d.X, d.Y + 1);
  end;

  for i := 0 to High(AllPlacesList) do begin
    d2 := AllPlacesList[i];
    {die beiden Punkte des Domino ermitteln}
    P3 := Point(d2.X, d2.Y);
    if d2.Dir = diHorz then begin
      P4 := Point(d2.X + 1, d2.Y);
    end else begin
      P4 := Point(d2.X, d2.Y + 1);
    end;

    {Dominozahlen Prüfen
    d oben/links MIT d2 oben/links}

    if (GetDist(P1, P3) = 0then exit; {Abstand ist 0, beide Steine liegen aufeinander}
    if (GetDist(P1, P3) = 1then begin  {Abstand ist 1, aber Steine haben nicht den selben Wert}
      Contact := true;
      if (d.P1 <> d2.P1) then exit;
    end;
    {d oben/links MIT d2 unten/rechts}
    if (GetDist(P1, P4) = 0then exit;
    if (GetDist(P1, P4) = 1then begin
      Contact := true;
      if (d.P1 <> d2.P2) then exit;
    end;
    {d unten/rechts MIT d2 oben/links}
    if (GetDist(P2, P3) = 0then exit;
    if (GetDist(P2, P3) = 1then begin
      Contact := true;
      if (d.P2 <> d2.P1) then exit;
    end;
    {d unten/rechts MIT d2 unten/rechts}
    if (GetDist(P2, P4) = 0then exit;
    if (GetDist(P2, P4) = 1then begin
      Contact := true;
      if (d.P2 <> d2.P2) then exit;
    end;
  end;
  if not contact and (Length(AllPlacesList) > 0then exit; {wenn Stein nicht an anderen Steinen anliegt und nicht der 1. Stein ist}

  {Wenn hier noch nicht beendet, dann passt der Stein
  also Stein in die Liste eintragen}

 SetLength(AllPlacesList, Length(AllPlacesList) + 1); {Array um 1 Element erweitern}
 AllPlacesList[High(AllPlacesList)] := d; {Stein in Letztes Element eintragen}
end;


Und das ist die Prozedur, die obiges verwendet:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
procedure TForm1.Button1Click(Sender: TObject);  //Button1 ist nur für Testzwecke da!
var
  x, y, n, h, tmp: Integer;
  aBitmap: TBitmap;
begin
 iTalon := 0;

 aBitmap := TBitmap.Create;

 try
  aBitmap.Assign(imgStein.Picture.Bitmap);

  Domino.X := 0;
  Domino.Y := 0;
  n := 0;
  h := 0;

  for x := 0 to Board.Width do
   for y := 0 to Board.Height do
  begin
   FindAllPlaces(Domino);  //Finde alle möglichen Positionen zum Ablegen des Steins

   {Drehe Stein}
   if Domino.Dir = diVert then begin
      tmp       := Domino.P1;
      Domino.P1 := Domino.P2;
      Domino.P2 := tmp;
      Domino.Dir := diHorz;
    end else
    begin
     Domino.Dir := diVert;
     Inc(h);
    end;

   {Zähler für Drehen erhöhen}
   Inc(n);

   if n = 3 then
   begin
    {Wenn 4x gedreht wurde, dann}
    {verschiebe Position um ein Feld nach rechts}
    Inc(Domino.X);
    n := 0;
   end;

//Braucht man doch hier nicht???
(*
   {Wenn Position auserhalb des spielfeldes, dann...}
   if Domino.X  >= Board.Width then
   begin
    Domino.X := 0;  {Setze X-Position auf 0}
    Inc(Domino.Y);  {Setze Y-Position in die nächste Zeile}
   end;
   if Domino.Y >= Board.Height then
   begin
    break;
   end;
*)

  end;  {end von "for x := 0 to Board.Width do [...]"}

  {Und zeichne das Ganze!}
  {Den Stein 1x drehen, wenn er horizontal abgelegt werden soll}
  if Domino.Dir = diHorz then Drehen90Grad(aBitmap);//imgStein.Picture.Bitmap);
  {Den Stein noch 3x drehen, wenn der Zähler h eine ungerade Zahl ist, dadurch}
  {wird er so gedreht, dass die Punktezahl auf der richtigen Seite ist.}
  if Odd(h) then
  begin
   Drehen90Grad(aBitmap);
   Drehen90Grad(aBitmap);
  end;

  Domino := AllPlacesList[Random(High(AllPlacesList))];
  {Den Stein zeichen}
  Board.Canvas.Draw(Domino.X * iStoneWidth, Domino.Y * iStoneWidth, aBitmap);

 finally
  aBitmap.Free;
 end;
end;

Dabei hängt das Programm, oder es rechnet eben solange. Es klappt einfach nicht, aber es sollte doch? 1. Finde alle Möglichkeiten, 2. wähle eine aus. Was ist da falsch?

//Edit:
Mir ist klar, dass in dem Programm diverser Coder mehrfach vorkommt, ich sollte dies natürlich alles vereinfachen, aber im Moment ist mir das egal, solange es nur läuft!

---Moderiert von user profile iconNarses: Beiträge zusammengefasst---

Beim Prüfen der umgebenden Steine muss ich DominoList verwenden, nicht AllPlacesList. Jetzt hängt das Programm zwar nicht mehr, setzt aber die meisten Steine irgendwo, teilweise auf einen bereits abgelegten Stein drauf.

Wenn ich statt Random den 1. Array-Eintrag verwende - Domino := AllPlacesList[0] - legt er jeden Stein immer an der selben Position ab - übereinander.


Ich blicke da nicht durch. :(


Bergmann89 - Sa 12.06.10 02:35

Hey,

mach es mal so wie ich gesagt hab un teil den Code mal in die Prozeduren und Funktionen auf, dann kann man auch den Überblick behalten. Was du da ganz unten in dem Button machst, da blick ich nämlich auch nich mehr durch :?
Also:
Funktion, die prüft, ob ein Stein da abgelegt werden kann oder nicht (Bsp.: CheckDomino)
Prozedur, die das Array mit den möglichen Positionen neu füllt (Bsp.: FindAllPosiblePlaces)
Prozedur, die das Domino in der Liste der Möglichen Positionen ablegt (Bsp.: AddToPosiblePlaces)
Prozedur, die einen Domino ablegt (Bsp.: PlaceDomino)
Prozedur, die das Domino (ohne Image) dreht (Bsp.: RotateDomino)

So is der Überblick besser un du hast aussagekräftige Namen für die Prodezuren/Funktionen. Dein FindAllPlaces müsste eig. CheckDomino heißen, weil es ja gar keine Plätze sucht, sondern nur überprüft!
Bei der Suche sind die Schleifen X und Y dazu gedacht die Position vom Domino durchzuzählen, also musst du der Position vom Domino auch die Werte der Schleife zuweisen, sonst passiert gar nix. Guck dir nochma den PseudoCode aus meinem Letzten Beitrag an.
Das Drehen passiert mithilfe einer 3. Schleife in den 2 ersten Schleifen, die von 0 bis 3 geht. Damit du weißt, wie oft du die Image drehen musst, musst du dein TDomino noch um einen Wert erweitern, meinetwegen ImgRotate oder so. Das ImgRotate musst du natürlich auch den aktuekllen Wert der Schleife geben.
Wenn CheckDomino mit den aktuellen Werten dann TRUE ist, legst du mit AddToPosiblePlaces den Stein im Array ab, in dem du später die Position per Zufall raus suchst.
Wenn du dann dein Domino per Zufall aus dem Array raus gesucht hast, drehst du die Image mit ner Schleife so oft, wie ImgRotate es dir vorgibt und dann legst du das Domino ab (PlaceDomino).

MfG Bergmann.


galagher - Sa 12.06.10 09:00

Hallo!

Ich verstehe nicht, warum mein Ansatz nicht funktioniert.

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Funktion, die prüft, ob ein Stein da abgelegt werden kann oder nicht (Bsp.: CheckDomino)
Also wie CheckDomino, aber mit einem anderen Array und ohne den Aufruf PlaceDomino?

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Prozedur, die das Array mit den möglichen Positionen neu füllt (Bsp.: FindAllPosiblePlaces)
Das macht CheckDomino doch schon mit PlaceDomino: :gruebel:

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TForm1.PlaceDomino(d: TDomino);
begin
 SetLength(DominoList, Length(DominoList) + 1); {Array um 1 Element erweitern}
 DominoList[High(DominoList)] := d; {Stein in Letztes Element eintragen}
end;

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Prozedur, die das Domino in der Liste der Möglichen Positionen ablegt (Bsp.: AddToPosiblePlaces)
Das verstehe ich leider absolut gar nicht... In ein Array? Das tut doch PlaceDomino auch?

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Prozedur, die einen Domino ablegt (Bsp.: PlaceDomino)
Das verstehe ich wieder! Wie PlaceDomino, aber in ein anderes Array.

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Prozedur, die das Domino (ohne Image) dreht (Bsp.: RotateDomino)
Also einfach dieses als eingene Prozedur schreiben und dann einfach nur die Prozedur aufrufen?

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
   if Domino.Dir = diVert then begin
      tmp       := Domino.P1;
      Domino.P1 := Domino.P2;
      Domino.P2 := tmp;
      Domino.Dir := diHorz;
    end else
      Domino.Dir := diVert;


user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Dein FindAllPlaces müsste eig. CheckDomino heißen, weil es ja gar keine Plätze sucht, sondern nur überprüft!
Das füllt doch aber ein Array, und zwar immer dann, wenn ein Stein passt?

--- Ok, ich werde sehen, wie weit ich komme.

---Moderiert von user profile iconNarses: Beiträge zusammengefasst---

Hallo!

Ok, ich war nicht faul und habe folgendes gebastelt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
var
  DominoList, DominoPossibleList: TDominoArray;
//...

//Funktion, die prüft, ob ein Stein da abgelegt werden kann oder nicht
function TForm1.Check_if_DominoPossible(d: TDomino): Boolean;
var
{aktueller Domino der Liste}
  d2: TDomino;
  {Koordinaten des oberen/linken Teils des Domino
  |   Koordinaten des unteren/rechten Teils des Domino
  |   |   Koordinaten des oberen/linken Teils von d2
  |   |   |   Koordinaten des unteren/rechten Teils von d2}

  P1, P2, P3, P4: TPoint;
  {Schleifenariable}
  i: Integer;

  {zum prüfen ob der Stein mind. 1 Feld von einem anderen entfernt ist
  bzw. ob der Stein an einem an einem anderen anliegt}

  contact: Boolean;

  {berechnet den Abstand zwischen 2 Punkten
  @P1: Punkt 1;
  @P2: Punkt 2;
  @result: Abstand der Beiden Punkte;}

  function GetDist(P1, P2: TPoint): Integer;
  begin
    result := Abs(P1.X-P2.X) + Abs(P1.Y-P2.Y);
  end;


begin  {Check_if_DominoPossible}
  result := false;
  contact := false;

  {die beiden Punkte des Domino ermitteln}
  P1 := Point(d.X, d.Y);
  if d.Dir = diHorz then begin
    P2 := Point(d.X + 1, d.Y);
  end else begin
    P2 := Point(d.X, d.Y + 1);
  end;

  for i := 0 to High(DominoList) do begin
    d2 := DominoList[i];
    {die beiden Punkte des Domino ermitteln}
    P3 := Point(d2.X, d2.Y);
    if d2.Dir = diHorz then begin
      P4 := Point(d2.X + 1, d2.Y);
    end else begin
      P4 := Point(d2.X, d2.Y + 1);
    end;

    {Dominozahlen Prüfen
    d oben/links MIT d2 oben/links}

    if (GetDist(P1, P3) = 0then exit; {Abstand ist 0, beide Steine liegen aufeinander}
    if (GetDist(P1, P3) = 1then begin  {Abstand ist 1, aber Steine haben nicht den selben Wert}
      Contact := true;
      if (d.P1 <> d2.P1) then exit;
    end;
    {d oben/links MIT d2 unten/rechts}
    if (GetDist(P1, P4) = 0then exit;
    if (GetDist(P1, P4) = 1then begin
      Contact := true;
      if (d.P1 <> d2.P2) then exit;
    end;
    {d unten/rechts MIT d2 oben/links}
    if (GetDist(P2, P3) = 0then exit;
    if (GetDist(P2, P3) = 1then begin
      Contact := true;
      if (d.P2 <> d2.P1) then exit;
    end;
    {d unten/rechts MIT d2 unten/rechts}
    if (GetDist(P2, P4) = 0then exit;
    if (GetDist(P2, P4) = 1then begin
      Contact := true;
      if (d.P2 <> d2.P2) then exit;
    end;
  end;
  if not contact and (Length(DominoList) > 0then exit; {wenn Stein nicht an anderen Steinen anliegt und nicht der 1. Stein ist}

  {Stein könnte abgelegt werden}
  result := True;
end;

//Prozedur, die das Array mit den möglichen Positionen neu füllt (Bsp.: FindAllPosiblePlaces)
procedure TForm1.FindAllPosiblePlaces;
begin
//Hier Check_if_DominoPossible aufrufen? Wie?
end;


//Da bin ich völlig ratlos:
//Prozedur, die das Domino in der Liste der Möglichen Positionen ablegt (Bsp.: AddToPosiblePlaces)


//Prozedur, die einen Domino ablegt (Bsp.: PlaceDomino)
procedure TForm1.PlacePossibleDomino(d: TDomino);
begin
 SetLength(DominoPossibleList, Length(DominoPossibleList) + 1); {Array um 1 Element erweitern}
 DominoPossibleList[High(DominoPossibleList)] := d; {Stein in Letztes Element eintragen}
end;

//Prozedur, die das Domino (ohne Image) dreht (Bsp.: RotateDomino)
procedure TForm1.RotateDomino(var d: TDomino);
var
  tmp: Integer;
begin
 with d do
  if Dir = diVert then
  begin
   tmp := P1;
   P1  := P2;
   P2  := tmp;
   Dir := diHorz;
  end
  else
   Dir := diVert;
end;

Das ist alles. Ich habe keine Ahnung, was ich sonst noch tun soll...

@user profile iconBergmann89
Ich stecke hier fest, habe aber Verständnis, dass du mir (oder sonst jemand) den Code nicht als Fertiggericht servierst. Ich möchte ja auch selber kochen, aber hier bin ich echt überfordert!


Bergmann89 - Sa 12.06.10 15:50

Hey,

langsam wirds, aber bisl was hast du noch verdreht. Dein PlacePossibleDomino ist die Prozedur, die ein Domino in der Liste der möglichen Positionen ablegt, die hieß bei mir AddToPosibilePlaces. PlaceDomino war dazu gedacht das Domino auf dem Feld, also im DominoList-Array abzulegen. Bei FindAllPosiblePlaces soll das Proggi alle Stellen des Feldes durchgehen, dazu 3 Schleifen (siehe letzer und vorletzter Beitrag von mir). Du musst auch drauf achten was wir schreiben, kahm schon vieles doppelt. Da hilft es immer, wenn man sich sowas wie ne To-Do-Liste anlegt, wo man alles rein schreibt was hier genannt wurde. Un dann kann man das systematisch abarbeiten ;)
Ma noch ne Frage nebenbei: Wie gut bist du in Englisch? Ich hab so das Gefühl, das du mit den englischen Prozedurnamen bisl durcheinander kommst. (Vlt sollt ich die in Zukunft deutsch machen) :)

MfG Bergmann.


galagher - Sa 12.06.10 16:28

Hallo!

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Dein PlacePossibleDomino ist die Prozedur, die ein Domino in der Liste der möglichen Positionen ablegt,
Naja - das soll es ja auch, oder? PlacePossibleDomino = "platziere mögliches Domino" - es platziert alle Möglichkeiten im Array, je einen pro Aufruf!? Genau wie PlaceDomino, nur in ein anderes Array.

Also der Reihe nach, das soll mein Programm tun:
1. Check_if_DominoPossible - Funktion, die prüft, ob ein Stein da abgelegt werden kann oder nicht (Bsp.: CheckDomino)
2. Prozedur, die das Array mit den möglichen Positionen neu füllt (Bsp.: FindAllPosiblePlaces - hier weiss ich nicht, wie diese Prozedur aussehen soll. Was ist hier zu tun?
3. Prozedur, die das Domino in der Liste der Möglichen Positionen ablegt (Bsp.: AddToPossiblePlaces) - Was ist hier zu tun? Was, wo und wie? (Ich weiss, dass das "füge zu möglichen Plätzen hinzu" bedeutet. :mrgreen: )
4. PlaceDomino - Prozedur, die einen Domino ablegt - Habe ich. Funktioniert.
5. RotateDomino - Prozedur, die das Domino (ohne Image) dreht - Habe ich auch und funktioniert auch.

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Ich hab so das Gefühl, das du mit den englischen Prozedurnamen bisl durcheinander kommst.
Eher mit dem Inhalt der Prozeduren! Soweit reicht mein Englisch schon! Aber mittlerweile habe ich keinen Plan, keinen Ansatz, wie ich das zum Laufen bringe. Ich meine, es steht da als Code, aber es wird nie genutzt. Das Programm kompiliert und spielt, aber eben nur "ich lege am erstmöglichen Platz ab".

Das habe ich bereits:
Ich habe ein Array, das prüft, welcher Stein schon entnommen wurde - klappt. Daher erkennt das Programm auch, dass der letze Stein weg ist - klappt. Wechsel Spieler/Proggi - klappt. Und dass das Programm etwas Sinnvolles tut, wenn keine Steine mehr da sind, kriege ich auch hin. Und dass es das Spiel beginnt, auch - nur ist das zur Zeit eben eher langweilig, wenn es den 1. Stein immer links oben ablegt. Klar, kann ja nicht anders - das ist der erste mögliche Platz.

Ich weiss einfach nicht, wie ich was wo mache! Nein, theoretisch schon, hast du ja beschrieben, aber ich kann's nicht umsetzen. :nixweiss: :cry:


Bergmann89 - Sa 12.06.10 19:14

Hey,

guck ma HIER [http://www.delphi-forum.de/viewtopic.php?p=608346#608346]. Da is sogar schon schöner PseudoCode, den man umsetzen kann. Das is jetzt das 3. ma das ich dir sag, das du das angucken sollst. Das is nämlich genau das, was die FindAllPlaces-Methodem machen soll. Wenn ich's jetzt noch ausführlicher hin schreib hast du Code da stehen. Und einfach 1 zu 1 kopieren bringt ja nix (haste ja auch schon festgestellt). Eig hast du alles was du zum lösen brauchst auf dieser Seite des Themas. Noch ein Kleiner Tipp, da das Ganze ja grad ziemlich durcheinander is. Kopier dir dein Projekt in einen Seperaten Ordner, lösch alles was mit dem Ablegen der Dominos, oder iwie mit der KI zu tun hat un schreib es neu. Zur Hilfe kannst du in den Alten Code, oder hier ins Forum gucken. Les dir nochma alle Beiträge genau durch, es steht eig. ausführlich genug da, was du machen musst ;)

MfG Bergmann.


galagher - So 13.06.10 08:24

Guten Morgen!

Jetzt endlich habe ich es, aber es ist wieder langsamer geworden. Ich habe ein Array DominoPossibleList zur Aufnahme der möglichen Plätze hinzugefügt und TDomino um den Integer Rotate erweitert, der die Anzahl der Drehungen enthält. :dance2:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
var
  DominoList, DominoPossibleList: TDominoArray;
//...

{Prüfen, ob ein Stein da abgelegt werden kann oder nicht}
procedure TForm1.FindAllPossiblePlaces(d: TDomino);
var
{aktueller Domino der Liste}
  d2: TDomino;
  {Koordinaten des oberen/linken Teils des Domino
  |   Koordinaten des unteren/rechten Teils des Domino
  |   |   Koordinaten des oberen/linken Teils von d2
  |   |   |   Koordinaten des unteren/rechten Teils von d2}

  P1, P2, P3, P4: TPoint;
  {Schleifenariable}
  i: Integer;

  {zum prüfen ob der Stein mind. 1 Feld von einem anderen entfernt ist
  bzw. ob der Stein an einem an einem anderen anliegt}

  contact: Boolean;

  {berechnet den Abstand zwischen 2 Punkten
  @P1: Punkt 1;
  @P2: Punkt 2;
  @result: Abstand der Beiden Punkte;}

  function GetDist(P1, P2: TPoint): Integer;
  begin
    result := Abs(P1.X-P2.X) + Abs(P1.Y-P2.Y);
  end;

begin  {FindAllPossiblePlaces}
  contact := false;

  {die beiden Punkte des Domino ermitteln}
  P1 := Point(d.X, d.Y);
  if d.Dir = diHorz then begin
    P2 := Point(d.X + 1, d.Y);
  end else begin
    P2 := Point(d.X, d.Y + 1);
  end;

  for i := 0 to High(DominoList) do begin
    d2 := DominoList[i];
    {die beiden Punkte des Domino ermitteln}
    P3 := Point(d2.X, d2.Y);
    if d2.Dir = diHorz then begin
      P4 := Point(d2.X + 1, d2.Y);
    end else begin
      P4 := Point(d2.X, d2.Y + 1);
    end;

    {Dominozahlen Prüfen
    d oben/links MIT d2 oben/links}

    if (GetDist(P1, P3) = 0then exit; {Abstand ist 0, beide Steine liegen aufeinander}
    if (GetDist(P1, P3) = 1then begin  {Abstand ist 1, aber Steine haben nicht den selben Wert}
      Contact := true;
      if (d.P1 <> d2.P1) then exit;
    end;
    {d oben/links MIT d2 unten/rechts}
    if (GetDist(P1, P4) = 0then exit;
    if (GetDist(P1, P4) = 1then begin
      Contact := true;
      if (d.P1 <> d2.P2) then exit;
    end;
    {d unten/rechts MIT d2 oben/links}
    if (GetDist(P2, P3) = 0then exit;
    if (GetDist(P2, P3) = 1then begin
      Contact := true;
      if (d.P2 <> d2.P1) then exit;
    end;
    {d unten/rechts MIT d2 unten/rechts}
    if (GetDist(P2, P4) = 0then exit;
    if (GetDist(P2, P4) = 1then begin
      Contact := true;
      if (d.P2 <> d2.P2) then exit;
    end;
  end;
  if not contact and (Length(DominoList) > 0then exit; {wenn Stein nicht an anderen Steinen anliegt und nicht der 1. Stein ist}

 {Wenn hier noch nicht beendet, dann passt der Stein, also Stein ins Array eintragen}
 SetLength(DominoPossibleList, Length(DominoPossibleList) + 1); {Array um 1 Element erweitern}
 DominoPossibleList[High(DominoPossibleList)] := d; {Stein in Letztes Element eintragen}
end;


//Vorläufig wird das Spielen des Programms noch manuell ausgelöst,
//ich wollte sehen, ob alles klappt wie erwartet!
procedure TForm1.Button1Click(Sender: TObject);
var
  x, y, n, h: Integer;
  aBitmap: TBitmap;
begin
 aBitmap := TBitmap.Create;

 try
  aBitmap.Assign(imgStein.Picture.Bitmap);

  Domino.X := 0;
  Domino.Y := 0;
  Domino.Rotate := 0;
  n := 0;
  h := 0;

(*
für alle X-Positionen mache...
  X-Position von Domino ändern
  für alle Y-Positionen mache...
    Y-Position von Domino ändern
    für alle drehungen mache...
      wenn CheckDomino(Domin) dann...
        AddPosiblePos(Domino)
      drehe Domino
*)


 SetLength(DominoPossibleList, 0);

 {Gesamtes Spielfeld durchgehen}
 for x := 0 to Board.Width do
  for y := 0 to Board.Height do
 begin
   FindAllPossiblePlaces(Domino);  {Alle Möglichkeiten zum Ablegen ermitteln}

   RotateDomino(Domino);

   Inc(h);
   Domino.Rotate := h;


   {Zähler für Drehen erhöhen}
   Inc(n);

   if n = 3 then
   begin
    {Wenn 4x gedreht wurde, dann}
    {verschiebe Position um ein Feld nach rechts}
    Inc(Domino.X);
    n := 0;
   end;

   {Wenn Position auserhalb des spielfeldes, dann...}
   if Domino.X  >= Board.Width then
   begin
    Domino.X := 0;  {Setze X-Position auf 0}
    Inc(Domino.Y);  {Setze Y-Position in die nächste Zeile}
   end;

   if Domino.Y >= Board.Height then
    break;
 end;  {end von "for x := 0 to Board.Width do [...]"}

caption:=inttostr(High(DominoPossibleList));//

  {Es wurde kein passender Platz zum Ablegen gefuden}
  if High(DominoPossibleList) < 0 then exit;

  {"Zufälligen" Domino auswaählen}
  Domino := DominoPossibleList[Random(High(DominoPossibleList))];

  {Den Domino in die richtige Position drehen}
  if (Domino.Rotate <> 4then
   for n := 0 to Domino.Rotate-1 do Drehen90Grad(aBitmap);

  {Trägt den Domino ins Array DominoList ein}
  CheckDomino(Domino);

  {Den Stein zeichen}
  DrawStone(aBitmap);

 finally
  aBitmap.Free;
 end;
end;


@user profile iconBergmann89
Ist zwar nicht so, wie du es gemeint hast, da blicke ich wirklich nicht durch! So funktioniert es jedenfalls, ich weiss nur nicht, warum es wieder langsamer ist. Kann man vielleicht noch optimieren.

//Edit: @user profile iconBergmann89 - Danke dir für deine vielen Tipps!

//Edit:
Jetzt verstehe ich auch "AddToPossiblePlaces" und "FindAllPossiblePlaces"! - Und genau so oder zumindest sehr ähnlich habe ich es eingebaut - der Rest ist Oberfläche und Drumherum!


galagher - Mi 16.06.10 22:17

Hallo!

Jetzt habe ich ein neues Problem: Wie verhindere ich, dass Steine "abgeschnitten" abgelegt werden können? Keiner meiner Ansätze waren wirklich erfolgreich, zB. klappt dieses nur sehr bedingt - das Programm hält sich nicht dran:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
function TForm1.CheckDomino(d: TDomino): Boolean;
begin
//...

 if d.Dir = diVert then
 begin
  if d.X*iStoneWidth+iStoneHeight > Board.Width then exit;//##
  if d.Y*iStoneWidth+iStoneHeight+iStoneWidth > Board.Height then exit;//##
 end
 else
 begin
  if d.X*iStoneWidth+iStoneHeight+iStoneWidth > Board.Width then exit;//##
  if d.Y*iStoneWidth+iStoneHeight > Board.Height then exit;//##
 end;

  {Wenn hier noch nicht beendet, dann passt der Stein, also Stein ins Array eintragen}
  SetLength(DominoList, Length(DominoList) + 1); {Array um 1 Element erweitern}
  DominoList[High(DominoList)] := d; {Stein in Letztes Element eintragen}

  result := True;

Das Bild im Anhang zeigt, was ich meine! Kann mir bei der Lösung dieses Problems jemand helfen?


Bergmann89 - Mi 16.06.10 22:23

Hey,

setz einfach deine Spielfeldgröße auf ein Vielfaches von 32 (bzw. auf ein Vielfaches der DominoBreite), das sollte das Problem lösen...

MfG Bergmann


galagher - Mi 16.06.10 22:57

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
setz einfach deine Spielfeldgröße auf ein Vielfaches von 32 (bzw. auf ein Vielfaches der DominoBreite), das sollte das Problem lösen...
Meinst du die Breite und Höhe des Spielfeld-Images? Das habe ich derzeit so: Board.SetBounds(00600600); Genau, wie ich es brauche. Wenn ich nun statt 600 wesentlich höhere Werte nehme, wird das an sich transparente Image weiss (?), wenn das Programm seinen Stein berechnet!

Ausserdem brauche ich rechts neben dem Spielfeld einen freien Platz, weil ich dort noch ein paar Buttons unterbringen möchte.

//Edit: Verstehe - multiplizieren! Denke, jetzt hab ich's! Danke! :wink:


galagher - Do 17.06.10 21:36

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
//Edit: Verstehe - multiplizieren! Denke, jetzt hab ich's! Danke! :wink:
Das löst das Problem leider nicht oder ich habe die Lösung nicht verstanden. Ich dachte, wenn ich eine Spielfeldhöhe und -breite wähle, die zB. 15x die Breite der Steine hat, klappt es.

Jeder Stein hat 38 Pixel Breite, also mit zB. 15 multiplizieren, dann hat das Spielfeld 570x570 Pixel und die Steine passen so hinein, dass rechts und unten nichts abgeschnitten wird. Wenn das Programm aber nun einen Stein passend ablegt, dann manchmal auch so, dass entweder eine Hälfte sichtbar ist und die andere nicht mehr auf das Board gezeichnet wird, weil sie ausserhalb ist, oder der ganze Stein nicht im Board ist (im Array schon, nur eben grafisch nicht!).

Ich weiss nicht, wie ich das lösen kann. Die angehängte Grafik zeigt, was ich meine. Der rote Rahmen ist ein Shape.


Bergmann89 - Do 17.06.10 22:11

Hey,

guck dir ma die Prozedur an, die die möglichen Positionen durchzäht. Da hast du doch ne Abfrage drin, wann er nen "Zeilenumbruch" machen soll, und wann er letztendlich komplett aufhören soll. Oder du hast es mittlerweile über die for-Schleifen gemacht, weiß ich nich mehr. Und da änderst du die Werte einfach, das er nich mehr so weit nach außen und unten läuft...

MfG Bergmann.


galagher - Fr 18.06.10 17:12

Hallo!

Die Prozedur FindAllPossiblePlaces durchläuft eine for-Schleife, und das hier sollte sein, was du meinst:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
   if d.X >= Board.Width then
   begin
    d.X := 0;  {Setze X-Position auf 0}
    Inc(d.Y);  {Setze Y-Position in die nächste Zeile}
   end;

   if d.Y >= Board.Height then
    break;
...funktioniert aber nicht, und auch Konstrukte, die die Breite der Steine (38 Pixel) addieren, wie zB. if d.X+38 >= Board.Width then funktionieren nicht.

Hier die ganze Prozedur:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
function TForm1.FindAllPossiblePlaces(d: TDomino): Integer;
var
  x, y, n, h: Integer;
begin
 d.X := 0;
 d.Y := 0;
 d.Rotate := 0;
 n := 0;
 h := 0;
 SetLength(DominoPossibleList, 0);

 {Gesamtes Spielfeld durchgehen}
 for x := 0 to Board.Width do
  for y := 0 to Board.Height do
 begin
   //AddToPossiblePlaces ist wie CheckDomino, nur befüllt es ein anderes Array: DominoPossibleList
   AddToPossiblePlaces(d);  {Alle Möglichkeiten zum Ablegen ermitteln}

   RotateDomino(d);

   Inc(h);
   d.Rotate := h;

   {Zähler für Drehen erhöhen}
   Inc(n);

   if n = 3 then
   begin
    {Wenn 4x gedreht wurde, dann}
    {verschiebe Position um ein Feld nach rechts}
    Inc(d.X);
    n := 0;
   end;

   {Wenn Position auserhalb des spielfeldes, dann...}
   if d.X >= Board.Width then
   begin
    d.X := 0;  {Setze X-Position auf 0}
    Inc(d.Y);  {Setze Y-Position in die nächste Zeile}
   end;

   if d.Y >= Board.Height then
    break;
 end;  {end von "for x := 0 to Board.Width do [...]"}

 Result := High(DominoPossibleList);
end;


//Edit:
Schlimmer noch: Offenbar ist das Spielfeld rechts (und wahrscheinlich auch unten) unbegrenzt! Siehe Grafik!


Bergmann89 - Fr 18.06.10 19:16

Hey,

die Psoition des Dominos (d.X bzw. d.Y) ist in RasterKoordinaten, also in Abhängikeit der Breite ( 38 ). Um auf die FeldKoordinaten umzurechnen musst du mit 38 Multiplizieren, dann stimmt auich die IF-Abfrage. Das ganze kannst du die aber sparen, den du hast ja die Schleifen, die die Position weiter zählen, also einfach am Anfang der beiden Schleifen die Position des Dominos festlegen. Und danach dann eine Schlefe (0-3) die die Drehungen zählt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
for x := 0 to (Board.Width div 38)-1 do begin //X-Koordinaten des Rasters durchzählen
  for y := 0 to (Board.Height div 38)-1 do begin //Y-Koordinaten des Rasters durchzählen
    d.X := 38 * x; //Position festlegen  
    d.Y := 38 * y; 
    for n := 0 to 3 do begin //Drehungen durchzählen
      d.Rotate := n; //Zähler speicher um später zu wissen, wie oft das Bild gedreht werden muss
      AddToPossiblePlaces(d); //Domino in Array schreiben, wenn Platz passen sollte
      RotateDomino(d); //drehen ohne Bild zu drehen
    end;
  end;
end;


MfG Bergmann


galagher - Sa 19.06.10 08:55

Hallo!

Vielen Dank, aber leider funktioniert das nicht. Es werden nun überhaupt keine Steine mehr abgelegt. Habe erst versucht, deinen Code in mein Programm einzubauen - klappt nicht. Dann ihn 1:1 zu übernehmen - klappt nicht. :(

//Edit:
Das scheint zu funktionieren, aber ehrlich gesagt, weiss ich nicht, warum! Ich habe es einfach ausprobiert:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
 for x := 0 to Board.Width do
  for y := 0 to Board.Height do
 begin
  if (d.X*38 < Board.Width-38and (d.Y*38 < Board.Height-38then
   AddToPossiblePlaces(d);  {Alle Möglichkeiten zum Ablegen ermitteln}

//...

   {Wenn Position auserhalb des spielfeldes, dann...}
   if d.X >= Board.Width-1 then
   begin
    d.X := 0;  {Setze X-Position auf 0}
    Inc(d.Y);  {Setze Y-Position in die nächste Zeile}
   end;

   if d.Y >= Board.Height-1 then
    break;


galagher - Mo 21.06.10 20:05

Hallo!

Ok, nächstes Problem:

Zuerst: Es gibt einen gemeinsamen Talon, aus dem beide Spieler (Mensch und Programm) Steine entnehmen (Random).

Ich habe das Spiel jetzt auf 66 Steine erweitert; klappt soweit alles. Ich habe zur Gewinner-Ermittlung einfach für den Spieler und das Programm je einen Integer als Zähler, der pro abgelegtem Stein um 1 erhöht wird. Wer am Ende des Spiels (=wenn alle Steine abgelegt sind), die höhere Zahl hat, ist Sieger. So weit, so klar.

Nur - wie ermittle ich den Gewinner, wenn eine verbleibende Anzahl an Steinen nirgendwo mehr passt? Ich hatte bei 66 Steinen, 608x608 Pixel Spielfeldgrösse und 38x74 Pixle pro Stein schon mehr als 3 "Rest"steine, die nirgendwo mehr passten.

Das Programm ist dzt. so, dass es erkennt, wenn ein Stein nirgendwo passt, und entsprechende Meldungen ausgibt: "Ich kann nicht ablegen, Sie sind dran" bzw. "Sie können nicht ablegen, ich bin dran".

Wohin das am Spielende führt, ist absehbar: Zu einem endlosen Hin und Her:
Das Programm entnimmt einen der verbliebenen Steine aus dem Talon (hier ist bereits gewiss, dass keiner der verbliebenen Steine mehr passt, nur weiss das Programm das eben nicht!) und sagt: "Ich kann nicht - daher du" - dann nimmt der Spieler einen neuen Stein. Das Programm erkennt, dass der nirgends passt, und sagt: "Du kannst nicht, daher ich", nimmt seinerseits einen Stein, der nirgends passt, und sagt: "Ich kann nicht - daher du" -
"Du kannst nicht, daher ich" - "Ich kann nicht - daher du" - "Du kannst nicht, daher ich" - usw. Das würde ewig so weitergehen.

Kurz: An welcher Stelle prüfe ich - und wie prüfe ich - alle verbliebenen Steine auf "Passen"? So in der Art:
if KeinSteinPasstIrgendwo then ?

Den Rest habe ich: Der Gewinner steht dann anhand seines Zählers fest!


F34r0fTh3D4rk - Mo 21.06.10 23:16

Wie prüfst du denn jetzt, ob ein Spieler nicht mehr setzen kann? Das sollte sich doch problemlos auf "prüfen, ob kein Spieler mehr setzen kann" erweitern lassen.


Bergmann89 - Di 22.06.10 10:41

Hey,


Quelltext
1:
2:
3:
4:
5:
6:
7:
für alle verbleibenden Steine, mache...
  wenn Stein abgelegt werden kann (CheckDomino, ohne ablegen!), dann...
    erhöhe Zähler um 1

wenn Zähler gleich null, dann...
  Textausgabe: "Kein Stein kann mehr abgelegt werden"
  Spielende

Das solltest du mit den Funktionen die du bereits hast kein Problem sein ;)

MfG Bergmannn


galagher - Mi 23.06.10 17:34

user profile iconF34r0fTh3D4rk hat folgendes geschrieben Zum zitierten Posting springen:
Wie prüfst du denn jetzt, ob ein Spieler nicht mehr setzen kann?

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:

Quelltext
1:
2:
3:
4:
5:
6:
7:
für alle verbleibenden Steine, mache...
  wenn Stein abgelegt werden kann (CheckDomino, ohne ablegen!), dann...
    erhöhe Zähler um 1

wenn Zähler gleich null, dann...
  Textausgabe: "Kein Stein kann mehr abgelegt werden"
  Spielende

Genau so!

Eigentlich logisch, man braucht keinen eigenen, extra geschriebenen Code dafür! Was ich mit einem Stein mache, muss ich doch bloss in einer Schleife mit allen Steinen machen! :dance2:

Danke euch!


galagher - Mo 28.06.10 17:24

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Globale Variablen gehören eben nicht zu einem bestimmten Objekt, sondern zu der Unit, in der die Klasse, also der Konstruktionsplan der Objekte, steht.

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Zusätzlich sind globale Variablen nicht gerade übersichtlich, sagtest du ja schon, und objektorientiert schon gar nicht.

Stimmt ja alles, nur jetzt möchte ich in einer anderen Unit (Unit2) den Spieler ermitteln, der den 1. Stein legen darf: Wer (Programm oder Mensch) den Stein mit insgesamt mehr Punkten hat, beginnt. Und schon muss zumindest TDomino eine globale Variable sein, denn wenn das im private-Teil von Unit1 steht, kann ich es ja on einer anderen Unit nicht nutzen!

Wie macht man es also?


galagher - Di 29.06.10 19:40

Hallo!

@user profile iconBergmann89:
Würdest du eher
a) jedem Spieler (Mensch/Programm) am Spielbeginn per Random bestimmte Steine, d. h. ein eigenes Array zuweisen, oder
b) mit einem gemeinsamen Array arbeiten, aus dem jeder Spieler jeweils einen Stein entnimmt?

Diese Frage ist mir enorm wichtig, denn momentan arbeitet das Programm mit nur einem Steine-Array!


Bergmann89 - Mi 30.06.10 15:51

Hey,

eig is das doch egal, oder? Kann man so oder so machen. Wenn du es jetzt schon mit einem Array für alle hast dann lass es so, kannst ja später noch als Option einbauen, das jeder sein eigenes hat...

MfG Bergmann


galagher - Mi 30.06.10 18:13

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
eig is das doch egal, oder?
Ich weiss nicht, ob es egal ist, denn jetzt stehen beiden Spieler alle Steine zur Verfügung, dann aber hat jeder Spieler nur seine Steine. Ich weiss nicht, was besser ist!


galagher - So 04.07.10 14:15

Hallo!

Es muss doch möglich sein, mit dem Array DominoList alle bisher abgelegten Steine neu zu zeichnen. Man soll während des Spiels eine andere Farbe einstellen können, dies übermalt aber alle abgelegten Steine. Also muss ich sie neu zeichnen. Dazu muss ich zwar noch einen Integer einbauen, der das Bitmap angibt, das gezeichnet werden muss, aber daran scheitert es nicht.

Das funktioniert nicht:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
var
  i, n: Integer;
  bmp: TBitmap;
begin
 bmp := TBitmap.Create;

 for i := 0 to High(DominoList) do
 begin
  Domino := DominoList[i];

  ImageList1.GetBitmap(0, bmp);  //hier wird statt 0 der Index-Zähler eingesetzt

  for n := 0 to Domino.Rotate do
   Drehen90Grad(bmp);

  DrawStone(bmp);
 end;

 bmp.Free;
end;

Was mache ich falsch?

//Edit:
Im Prinzip nichts, habe nur vergessen, den Zähler für's Drehen nach 4x auf 0 zurückzusetzen und kam dabei auf mehrere tausend Mal Drehen, was das Programm natürlich einige Zeit beschäftigte und es dadurch auf nichts mehr reagierte!


galagher - Mo 05.07.10 17:47

Hallo!

Es gibt doch noch ein Problem: Ich kriege die Berechnung des rechten bzw. unteren Randes nicht hin. Entweder setzt das Programm Steine auch ausserhalb des Spielfeldes, oder es erkennt weder für sich noch für mich einen Stein als passend, wenn dieser zB. ganz rechts angefügt werden muss.

Mit folgendem Code passiert das:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
function TForm1.FindAllPossiblePlaces(d: TDomino): Integer;
var
  x, y, n, h: Integer;
begin
 d.X := 0;
 d.Y := 0;
 d.Rotate := 0;
 n := 0;
 h := 0;
 SetLength(DominoPossibleList, 0);

 {Gesamtes Spielfeld durchgehen}
 for x := 0 to iBoardWidth do
  for y := 0 to iBoardHeight do
 begin
//So legt das Programm rechts und unten einfach weiter an
//  if (d.X*iStoneWidth < iBoardWidth) and
//     (d.Y*iStoneWidth < iBoardHeight) then

//So werden rechts und unten passende Steine nicht erkannt, offenbar aber
//nur dann, wenn der Stein, an den angelegt werden soll, waagrecht liegt!??
  if (d.X*iStoneWidth < iBoardWidth-iStoneWidth) and
     (d.Y*iStoneWidth < iBoardHeight-iStoneWidth) then
   AddToPossiblePlaces(d);  {Alle Möglichkeiten zum Ablegen ermitteln}

   RotateDomino(d);

   {4x Drehen bedeutet, die Ausgangsposition ist wieder erreicht}
   if h >= 4 then h := 0{und h muss nicht weiter erhöht werden}

   Inc(h);
   d.Rotate := h;

   {Zähler für Drehen erhöhen}
   Inc(n);

   if n = 3 then
   begin
    {Wenn 4x gedreht wurde, dann}
    {verschiebe Position um ein Feld nach rechts}
    Inc(d.X);
    n := 0;
   end;

   {Wenn X-Position ausserhalb des Spielfeldes, dann...}
   if d.X >= iBoardWidth-1 then
   begin
    d.X := 0;  {... setze X-Position auf 0,}
    Inc(d.Y);  {setze Y-Position in die nächste Zeile}
   end;

   {Wenn Y-Position ausserhalb des Spielfeldes, dann abbrechen}
   if d.Y >= iBoardHeight-1 then
    break;
 end;  {end von "for x := 0 to iBoardWidth do [...]"}

 Result := High(DominoPossibleList);
end;


Die Grafik verdeutlicht nochmal, was ich meine!


galagher - Mi 07.07.10 19:36

Hallo!

Das Programm ist mittlerweile so komplex geworden, dass eine einfache Antwort nicht so schnell möglich ist,
aber ich war ja nicht faul und habe *gedacht* und auch probiert und siehe da: :dance2:

Ich denke, das ist es jetzt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
function TForm1.FindAllPossiblePlaces(d: TDomino): Integer;
var
  x, y, n, h: Integer;
  b: Boolean;
begin
 d.X := 0;
 d.Y := 0;
 d.Rotate := 0;
 n := 0;
 h := 0;
 SetLength(DominoPossibleList, 0);

 {Gesamtes Spielfeld durchgehen}
 for x := 0 to iBoardWidth do
  for y := 0 to iBoardHeight do
 begin
  if (d.X*iStoneWidth < iBoardWidth) and
     (d.Y*iStoneWidth < iBoardHeight) then
  begin
   b := True;

   // !
   if (d.X*iStoneWidth+iStoneWidth = iBoardWidth) and (d.Dir = diHorz) then
    b := False;

   // !
   if (d.Y*iStoneWidth+iStoneWidth = iBoardHeight) and (d.Dir = diVert) then
    b := False;

   if b then  // !
    AddToPossiblePlaces(d);  {Alle Möglichkeiten zum Ablegen ermitteln}
  end;

  RotateDomino(d);

  //...


galagher - Sa 17.07.10 19:25

Hallo!

Sorry, dass ich mich ein weiteres Mal in Serie melde, aber der Code FindAllPossiblePlaces stimmt immer noch nicht. Entweder bekomme ich es hin, dass er Steine nicht unten bzw. rechts ausserhalb des Spielfeldes anlegt, also korrekt arbeitet, aber dafür nicht alle möglichen Plätze innerhalb des Spielfeldes findet.

Oder er findet alle Plätze, legt dafür aber unten bzw. rechts ausserhalb des Spielfeldes an.

Ich möchte aber, dass er alle möglichen Plätze innerhalb des Spielfeldes findet! Ich kriege das einfach nicht hin.
So findet er nicht alle möglichen Plätze im Spielfeld, legt aber stets korrekt an:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
function TForm1.FindAllPossiblePlaces(d: TDomino): Integer;
var
  x, y, n, h: Integer;
  b: Boolean;
begin
 d.X := 0;
 d.Y := 0;
 d.Rotate := 0;
 n := 0;
 h := 0;
 SetLength(DominoPossibleList, 0);

 {Gesamtes Spielfeld durchgehen}
 for x := 0 to iBoardWidth do
  for y := 0 to iBoardHeight do
 begin
//Was stimmt hier nicht?
  if (d.X*iStoneWidth <= iBoardWidth-iStoneWidth) and
     (d.Y*iStoneWidth <= iBoardHeight-iStoneWidth) then
  begin
   b := False;
(*
//Was stimmt hier ...
   if (d.Dir = diHorz) and (d.X*iStoneWidth+iStoneWidth+iStoneWidth < 684) then
    b := True;
//... und hier nicht?
   if (d.Dir = diVert) and (d.Y*iStoneWidth+iStoneHeight+iStoneHeight < 642) then
    b := True;
*)


//Code vereinfacht, das Problem bleibt aber bestehen - es findet nicht alle möglichen Plätze!
   if (d.Dir = diHorz) then
    if (d.X < 16and (d.Y < 14then b := True;
//wenn hier ein else steht, werden nur die waagrechten Plätze gefunden, aber da nicht alle!
   if (d.Dir = diVert) then
    if (d.X < 17and (d.Y < 13then b := True;

   if b then AddToPossiblePlaces(d);  {Alle Möglichkeiten zum Ablegen ermitteln}
  end;


//Dass der Fehler ab hier irgendwo liegt, schliesse ich aus!
  RotateDomino(d);

  {4x Drehen bedeutet, die Ausgangsposition ist wieder erreicht}
  if h >= 4 then h := 0{und h muss nicht weiter erhöht werden}

  Inc(h);
  d.Rotate := h;

  {Zähler für Drehen erhöhen}
  Inc(n);

  if n = 3 then
  begin
   {Wenn 4x gedreht wurde, dann}
   {verschiebe Position um ein Feld nach rechts}
   Inc(d.X);
   n := 0;
  end;

  {Wenn X-Position ausserhalb des Spielfeldes, dann...}
  if d.X >= iBoardWidth-1 then
  begin
   d.X := 0;  {... setze X-Position auf 0,}
   Inc(d.Y);  {setze Y-Position in die nächste Zeile}
  end;

   {Wenn Y-Position ausserhalb des Spielfeldes, dann abbrechen}
  if d.Y >= iBoardHeight-1 then
   break;
 end;  {end von "for x := 0 to iBoardWidth do [...]"}

 Result := High(DominoPossibleList);
end;


//Edit: Code ist jetzt etwas vereinfacht, das löst das Problem aber nicht!