AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Projekte Tipps und Ratschläge für Spiel & für effizientes Programmieren

Tipps und Ratschläge für Spiel & für effizientes Programmieren

Ein Thema von Danny92 · begonnen am 27. Aug 2017 · letzter Beitrag vom 5. Sep 2017
Antwort Antwort
Seite 3 von 4     123 4   
Benutzerbild von Danny92
Danny92
Registriert seit: 18. Aug 2014
Jetzt habe ich das Schiffe versenken soweit fertig gestellt und stelle es euch hier zur Verfügung. Ich bin für jeden Tipp, Verbesserungsvorschlag, konstruktive Kritik oder neuen Einfall jederzeit dankbar. Schlussendlich geht es mir neben der Kritik nun nur noch darum, etwas an Performance rauszuholen, denn bei aktivem Spielen mit der KI läuft meine - wenn auch nicht gerade schnellste - CPU immerhin schon bei ca. 33%. Ist das eurer Meinung nach normal bei einem Programm diesem Formats? Oder ist das schlichtweg "schlecht" programmiert? Wie sieht eure CPU-Auslastung aus? Denn bis jetzt konnte ich nicht erkennen woran das liegt, da ich mit effizientem Programmieren noch nicht so vertraut bin bzw. es selten das Problem war. Das Neuzeichnen der StringGrids oder das Vorausberechnen des nächsten Schusses der KI scheint es schon mal nicht zu sein.
Vielen Dank für eure kompetente Hilfe!

PS: Da das Programm wegen der Resource zu groß ist, muss es hier verlinkt werden. Minen sind noch nicht implementiert.
Angehängte Dateien
Dateityp: zip Schiffe versenken - fertig - Kopie.zip (958,6 KB, 29x aufgerufen)

Geändert von Danny92 (27. Aug 2017 um 20:32 Uhr)
 
Benutzerbild von Danny92
Danny92

 
Delphi 10.2 Tokyo Starter
 
#21
  Alt 29. Aug 2017, 06:31
Okay jetzt habe ich die Speicherlecks gefunden. Einmal hab ich in TFlotte.Destroy vergessen, die einzelnen Schiffe im Array wieder freizugeben.
Und das andere Leck war etwas schwieriger zu finden: Im Destroy-Ereignis der Form hat noch ein
Delphi-Quellcode:
BTMemoryFreeLibrary(mp_MemoryModule);
  FreeMemory(mp_DllData);
gefehlt.
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

 
Delphi 12 Athens
 
#22
  Alt 29. Aug 2017, 07:08
Hallöle...
Zitat:
Ja cnPack hab ich soeben schon mal installiert.
Ein großer Werkzeugkasten, wo man nicht alles braucht, der dir aber das Leben erleichtert. Ein paar Optionen in der IDE solltest du deakivieren. (Optionen1+2)
1: Die Linien von Castalia können weg, es sein denn du magst die dünnen Linien lieber. Dann mußt du die Linien im CnPack abschalten.
2. Programmierhilfe: Die vom CnPack ist deutlich besser. Die findet auch Teilstrings. Nur doppelt suchen ist langsam...
Zitat:
Duplicate Code Finder 1.0 konnte ich lediglich eine Stelle in der SetzeFlotte-Prozedur finden, die sich wiederholt
DRY ist was anderes. Du hast Glück. Für meine Sprachtherapie habe ich einen Mini Vortrag über DRY gehalten. (Anhang) Fällt dir zwischen Seite 5 und 6 was auf?



Nachtrag DRY:

aus deinem Code:
Delphi-Quellcode:
procedure TForm1.LeuchteKnopfTimer(Sender: TObject);
begin
  if not playerhasshot
  then
  if blinkcount<25 then
  begin
    if BoxPlayer.Color=clRed
    then BoxPlayer.Color:=clNavy
    else BoxPlayer.Color:=clRed;
    LeuchteKnopf.Interval:=random(80)+21;
    Inc(blinkcount);
  end else
  begin
    blinkcount:=0;
    LeuchteKnopf.Enabled:=False;
    LeuchteKnopf.Interval:=1;
    BoxPlayer.Color:=clNavy;
  end
  else
  if blinkcount<25 then
  begin
    if BoxKI.Color=clRed
    then BoxKI.Color:=clNavy
    else BoxKI.Color:=clRed;
    LeuchteKnopf.Interval:=random(80)+21;
    Inc(blinkcount);
  end else
  begin
    blinkcount:=0;
    LeuchteKnopf.Enabled:=False;
    LeuchteKnopf.Interval:=1;
    BoxKI.Color:=clNavy;
  end;
end;
besser:
Delphi-Quellcode:
procedure TForm1.LeuchteKnopfTimer(Sender: TObject);
begin
  if not playerhasshot then
  begin
    LightingButton(BoxPlayer);
  end
  else
  begin
    LightingButton(BoxKI);
  end
end;
ausgelagert z.B. als private procedure:
Delphi-Quellcode:
procedure TForm1.LightingButton(GroupBox: TGroupbox);
begin
  if blinkcount < 25 then
  begin
    if GroupBox.Color = clRed then
      GroupBox.Color := clNavy
    else
      GroupBox.Color := clRed;
    LeuchteKnopf.Interval := random(80) + 21;
    Inc(blinkcount);
  end
  else
  begin
    blinkcount := 0;
    LeuchteKnopf.Enabled := False;
    LeuchteKnopf.Interval := 1;
    GroupBox.Color := clNavy;
  end;
end;
Miniaturansicht angehängter Grafiken
optionen1.png   optionen2.png  
Angehängte Dateien
Dateityp: pdf Logo Vortrag.pdf (93,9 KB, 21x aufgerufen)

Geändert von haentschman (29. Aug 2017 um 07:51 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Danny92
Danny92

 
Delphi 10.2 Tokyo Starter
 
#23
  Alt 2. Sep 2017, 09:42
Ich habe noch einen Fehler gefunden, aber ich weiß noch nicht, wie der zustande kommt. Setzt man die spielfeldgröße auf 5x5 Felder (spinedit. Max in der form2 muss evtl auf 5 gesenkt werden), und benutzt 1 fünfer Schiff sowie 1 dreier Schiff, so kommt es beim spielstart beim Klick auf zufällig in ca 10% der Fälle dass das fünfer Schiff nicht erscheint, sondern nur das kleine. Weiß jemand, wieso? Ich hab nicht den blassesten Schimmer und weiß auch nicht, wie man den Fehler schnell und einfach finden kann. Das passiert ja nur relativ selten...
  Mit Zitat antworten Zitat
Delphi-Laie

 
Delphi 10.1 Berlin Starter
 
#24
  Alt 2. Sep 2017, 10:22
Wird randomize zum Programmstart aufgerufen?

Falls ja, schalte es ab.

Beobachte daraufhin Dein Programm: Ist die Reihenfolge bzw. die Verteilung dieser ominösen 10% in der Menge aller Spiele ab / nach dem Programmstart immer die gleiche? Falls ja, hat es mit dem (delphiinternen) Zufallsgenerator zu tun.
  Mit Zitat antworten Zitat
HolgerX

 
Delphi 6 Professional
 
#25
  Alt 2. Sep 2017, 13:05
Hmm..

Ist dieses Problem auch mit dem Source deines ersten Post reproduzierbar?

Ansonsten (ohne in den Source hinein zu schauen):
- Hast Du beim Platzieren der Schiffe eine Kollisionsabfrage (damit nicht Schiffe aufeinander liegen)?
- Wird die linke oder obere Position des äußeren Kästchen des 5er Schiff auch auf 1/x bzw y/1 gesetzt?
Denn ansonsten würde das 5er Schiff ja aus dem Feld 'herausragen'.
- Was passiert mit dem Schiff, wenn es 'herausragt'? Wird es dann komplett nicht gezeichnet, oder nur der sichtbare Teil?

(Nur mal ein paar Fragen )
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

 
Delphi 12 Athens
 
#26
  Alt 2. Sep 2017, 15:39
Wird randomize zum Programmstart aufgerufen?
Bei Verwendung der VCL wird das automatisch erledigt, weil Viele es sändig vergessen.
(in der Initialisierung der VCL wird einmal Randomize ausgeführt)

Was man aber "normalerweise" niemals machen sollte.
"immer wieder" Randomize aufzurufen oder RandSeed zu setzen.
Es gab schon Leute, die dachten es wird "zufälliger", wenn sie vor jedem Auslesen von Random das gemacht haben und sich dann wunderten, dass dem nicht so ist. (eher das Gegenteil)



Ja, der TE hat einmal im OnCreate der Hauptform ein Randomize drin, also so weit alles OK.

Geändert von himitsu ( 2. Sep 2017 um 15:43 Uhr)
  Mit Zitat antworten Zitat
Delphi-Laie

 
Delphi 10.1 Berlin Starter
 
#27
  Alt 2. Sep 2017, 16:34
Wird randomize zum Programmstart aufgerufen?
Bei Verwendung der VCL wird das automatisch erledigt, weil Viele es sändig vergessen.
(in der Initialisierung der VCL wird einmal Randomize ausgeführt)
Ich weiß nicht, auf welche Delphiversion bzw. ab welcher Delphiversion Du Dich beziehst, aber bis Delphi 7, das ich zur Compilierung meines Sortieranimationsprogrammes benutze, ist es sehr wohl unterschiedlich, ob ich randomize (im Initialisationsteil der ersten VCL-Unit) aufrufe oder es unterlasse. Verzichte ich auf diesen Befehl, sind die Zufallszahlen nach jedem Programmstart die gleichen.

Edit: Ein "automatisches" Randomize ist ein zweischneidiges Schwert. Wie will man "vernünftig", "produktiv" debuggen, wenn man keine reproduzierbaren Ergebnisse erhält?

Geändert von Delphi-Laie ( 2. Sep 2017 um 17:45 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

 
Delphi 12 Athens
 
#28
  Alt 2. Sep 2017, 18:20
Edit: Ein "automatisches" Randomize ist ein zweischneidiges Schwert. Wie will man "vernünftig", "produktiv" debuggen, wenn man keine reproduzierbaren Ergebnisse erhält?
Nicht mit Zufalls werten arbeiten, oder vorher den Test "ordentlich" initialisieren (RandSeed), wenn es nötig ist.

Das mit der Automatik wurde nicht gleich zu beginn eingebaut.
Dachte bei D7 ist es schon da, aber spätestens mit XE ist es so.
  Mit Zitat antworten Zitat
Benutzerbild von Danny92
Danny92

 
Delphi 10.2 Tokyo Starter
 
#29
  Alt 3. Sep 2017, 20:50
Ja es wird alles beachtet was beim Plazieren der Schiffe notwenig ist; dass es aus dem Feld nicht herausragt, dass dieses Schiff mit anderen nicht kollidiert oder sie sich berühren. Und solange nicht all diese Bedingungen erfüllt sind, wird das Schiff zufällig neu plaziert. Und mir ist schon unverständlich wie man da aus der Schleife rauskommt, solange noch nicht alle Schiffe plaziert worden sind^^
Wenn ich randomize in FormCreate (wird ja nur einmal aufgerufen) auskommentiere, bleibt der Fehler weiterhin.
  Mit Zitat antworten Zitat
Benutzerbild von Danny92
Danny92

 
Delphi 10.2 Tokyo Starter
 
#30
  Alt 3. Sep 2017, 21:09
Hier noch einmal die Prozedur zum Setzen der Flotte:

Delphi-Quellcode:
procedure TForm1.SetzeFlotte(ofPlayer: boolean);
var
  zahl, i, j, k, fehlenNoch, vofPlayer: Integer;
  done, alleProbiert: Boolean;
  richtungProbiert: array[0..3] of Boolean;
begin
  vofPlayer:=StrToInt(BoolToStr(ofPlayer, false)) + 1;
  flotte[vofPlayer].Free;
  flotte[vofPlayer]:=TFlotte.Create(flottengroesse[5], flottengroesse[4],
  flottengroesse[3], flottengroesse[2], flottengroesse[1]);
  for i:=5 downto 1 do
  begin
    j:=1;
    fehlenNoch:=flotte[vofPlayer].AnzahlFehlenderSchiffeDerGroesse(i);
    while j <= fehlenNoch do
    begin
      done:=false;
      repeat
        zahl:=random(feldgroesse * feldgroesse) + 1;
        for k:=0 to 3 do richtungProbiert[k]:=false;
        a:=Point(((zahl - 1) mod feldgroesse) + 1, ((zahl - 1) div feldgroesse) + 1);
        repeat
          k:=random(4);
          alleProbiert:=true;
          case k of
          0: if a.Y >= i then b:=Point(a.X, a.Y - i + 1);
          1: if a.Y + i - 1 <= feldgroesse then b:=Point(a.X, a.Y + i - 1);
          2: if a.X >= i then b:=Point(a.X - i + 1, a.Y);
          3: if a.X + i - 1 <= feldgroesse then b:=Point(a.X + i - 1, a.Y);
          end;
          if not richtungProbiert[k]
          then done:=flotte[vofPlayer].SetzeSchiff(a, b);
          richtungProbiert[k]:=true;
          for k:=0 to 3 do
            if not richtungProbiert[k] then alleProbiert:=false;
        until alleProbiert or done;
      until done;
      Inc(j);
    end;
  end;
  FeldPlayer.Repaint;
end;
Hier die darin benutzte boolsche Funktion SetzeSchiff(a, b: TPoint) aus der Klasse TFlotte, die genau dann true zurückgibt, wenn das Platzieren des Schiffes im Feld unter den bekannten Regeln erfolgreich war:

Delphi-Quellcode:
function TFlotte.SetzeSchiff(a: TPoint; b: TPoint): boolean;
var
  laenge, vmin, vmax, i: integer;
begin
  if IstGueltig(a, b) then
  begin
    if a.X=b.X
    then laenge:=max(a.Y,b.Y)-min(a.Y,b.Y)+1
    else laenge:=max(a.X,b.X)-min(a.X,b.X)+1;
    if laenge>1
    then Inc(schiffsanzahl);
    SetLength(vSchiff[laenge],Length(vSchiff[laenge])+1);
    vSchiff[laenge,high(vSchiff[laenge])]:=TSchiff.Create;
    if a.X=b.X then
    begin
      vmin:=min(a.Y,b.Y); vmax:=max(a.Y,b.Y);
      for i:=vmin to vmax do
      if laenge=1
      then feld[a.X-1,i-1]:=3
      else feld[a.X-1,i-1]:=1;
      result:=vSchiff[laenge,high(vSchiff[laenge])].SetzeSchiff(Point(a.X,vmin),Point(a.X,vmax));
    end else
    if a.Y=b.Y then
    begin
      vmin:=min(a.X,b.X); vmax:=max(a.X,b.X);
      for i:=vmin to vmax do
      if laenge=1
      then feld[i-1,a.Y-1]:=3 //Setze Mine
      else feld[i-1,a.Y-1]:=1;
      result:=vSchiff[laenge,high(vSchiff[laenge])].SetzeSchiff(Point(vmin,a.Y),Point(vmax,a.Y));
    end
    else
      result:=false
  end
  else
    result:=false
end;
Und hier zuletzt die darin benutzte boolsche Funktion SetzeSchiff(a, b: TPoint) aus der Klasse TSchiff, die bei Erfolg ebenfalls true liefert:

Delphi-Quellcode:
function TSchiff.SetzeSchiff(a,b: TPoint): boolean;
var
  istSchiff: boolean;
  i: integer;
begin
  istSchiff:=(a.X=b.X) and (abs(a.Y-b.Y)<5) or (a.Y=b.Y) and (abs(a.X-b.X)<5);
  if istSchiff then
  begin
    vStart:=a; vEnde:=b;
    if a.X=b.X
    then laenge:=abs(a.Y-b.Y)+1
    else laenge:=abs(a.X-b.X)+1;
    vWaagerecht:=a.Y=b.Y;
    SetLength(vSchiff,laenge);
    for i:=1 to laenge do vSchiff[i-1]:=false;
  end;
  result:=istSchiff;
end;
So und jetzt kommt das Irre: bleibt die äußerste Zählschleife in SetzeFlotte als i:=5 downto 1, wird in manchen Fällen das 5er Schiff nicht gezeichnet, die Flotte besteht also nur aus dem 3er (linkes Bild im Anhang). Ändere ich die Zählschleife aber in i:=1 to 5, wird (wenn auch noch seltener) das 3er Schiff vergessen und nur das 5er Schiff plaziert (rechtes Bild im Anhang).

Was ist da los?! Bug?
Miniaturansicht angehängter Grafiken
5fehlt_downto.jpg   3fehlt_to.jpg  

Geändert von Danny92 ( 3. Sep 2017 um 21:12 Uhr)
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:14 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz