Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi TicTacToe - Frage zum Programmablauf (https://www.delphipraxis.net/192359-tictactoe-frage-zum-programmablauf.html)

ShadowDeath 11. Apr 2017 19:18

TicTacToe - Frage zum Programmablauf
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Leute,
ich hoffe ihr könnt mir helfen, denn ich habe versucht ein TicTacToe zu programmieren doch irgendwie funktioniert es nicht ganz so wie es soll. Um genau zu sein scheint die Gewinner Abfrage nicht zu funktionieren, doch ich finde den Fehler einfach nicht. Hoffentlich kann mir jemand von euch helfen :) PS: Ich habe das game unten als Zip angehangen.



Delphi-Quellcode:
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Buttons;

type
  TForm1 = class(TForm)
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    SpeedButton4: TSpeedButton;
    SpeedButton5: TSpeedButton;
    SpeedButton3: TSpeedButton;
    SpeedButton6: TSpeedButton;
    SpeedButton7: TSpeedButton;
    SpeedButton8: TSpeedButton;
    SpeedButton9: TSpeedButton;
    SpeedButton10: TSpeedButton;
    procedure SpeedButton1Click(Sender: TObject);
    procedure SpeedButton10Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);

  private
    { Private-Deklarationen }
    Spielfeld: array[0..2, 0..2] of Byte;
    Spieler, Starter, Gewinner: Byte;
    function ErmittelGewinner() : Byte;

  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

// FELD 1 bis Feld 9
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  X, Y: Byte;
  Index: Byte;
begin
  // Ist der Sender überhaupt ein Speedbutton?
  If not (Sender is TSpeedButton) then Exit;

  // Ist das Spiel schon vorbei?
  If Gewinner <> 0 then
    begin
      ShowMessage( 'Das Spiel ist bereits vorbei. Bitte starte ein neues Spiel.' );
      Exit;
    end;

  // Welcher Button wurde gedrückt?
  X := 90;
  Y := 90;
  for Index := 0 to Self.ControlCount - 1 do
  begin
   If Sender = Self.Controls[ Index ] then
   begin
     X := Index mod 3;
     Y := Index div 3;
   end;
  end;

  // Wurde der Button gefunden?
  if ( X > 2 ) or ( Y > 2 ) then exit;

  // Ist das gewünschte Feld noch frei?
  If Spielfeld[ X, Y ] <> 0 then
  begin
    ShowMessage( 'Das Feld ist schon besetzt. Bitte wähle ein anderes aus.' );
    Exit;
  end;

  // Zug optisch ausführen
  case Spieler of
    1: TSpeedButton( Sender ).Caption := 'X';
    2: TSpeedButton( Sender ).Caption := 'O';
    else
    begin
      ShowMessage( 'Unerwarteter Fehler!' );
      Exit;
    end;
  end;

  // Zug intern ausführen
  Spielfeld[ X, Y ] := Spieler;

  // Spieler wechseln
  Spieler := Spieler mod 2 + 1;

  // Sieger ermitteln
  Gewinner := ErmittelGewinner();

  // Entsprechend des Siegers eine Meldung ausgeben
  case Gewinner of
    0: Exit;
    1,2: ShowMessage( Format( 'Spieler %d hat gewonnen.', [ Gewinner ] ) );
    3: ShowMessage( 'Das Spiel endet unentschieden.' );
  end;
end;

// NEUES SPIEL
procedure TForm1.SpeedButton10Click(Sender: TObject);
var
  X, Y: Byte;
begin
   // Spiel intern und optisch löschen
   for X := 0 to 2 do
   begin
     for Y := 0 to 2 do
     begin
       Spielfeld[ X, Y ] := 0;
       TSpeedButton( Self.Controls[Y * 3 + X ] ).Caption := '';
     end;
   end;

   // Entscheiden, wer die nächste Runde beginnen darf
   case Gewinner of
     1,2: Starter := Gewinner mod 2 + 1; // Der Verlierer startet
     0,3: Starter := Starter mod 2 + 1; // Spieler 2 aus der vorherigen Runde ist nun Spieler 1
   end;

   // Vorbereitungen abschließen
   Spieler := Starter;
   Gewinner := 0;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Spieler := 1;
  Starter := 1;
end;

function TForm1.ErmittelGewinner() : Byte;
var
  X, Y: Byte;
begin
  // Gewinner ermitteln

  // Gibt es einen Gewinner auf der Y-Achse?
  for X := 0 to 2 do
  begin
    Result := Spielfeld[ X, 0 ];
    if ( Result <> Spielfeld[ X, 1 ] ) or ( Result <> Spielfeld[ X, 2 ] ) then Result := 0;
    If Result <> 0 then Exit;
  end;

  // Gibt es einen Gewinner auf der X-Achse?
  for Y := 0 to 2 do
  begin
    Result := Spielfeld[ 0, Y ];
    if ( Result <> Spielfeld[ 1, Y ] ) or ( Result <> Spielfeld[ 2, Y ] ) then Result := 0;
    If Result <> 0 then Exit;
  end;

  // Gibt es diagonal einen Gewinner? (Von Links-Oben nach Rechts-Unten)
  for Y := 0 to 2 do
  begin
    Result := Spielfeld[ 0, 0 ];
    if ( Result <> Spielfeld[ 1, 1 ] ) or ( Result <> Spielfeld[ 2, 2 ] ) then Result := 0;
    If Result <> 0 then Exit;
  end;

  // Gibt es diagonal einen Gewinner? (Von Rechts-Oben nach Links-Unten)
  for Y := 0 to 2 do
  begin
    Result := Spielfeld[ 2, 0 ];
    if ( Result <> Spielfeld[ 1, 1 ] ) or ( Result <> Spielfeld[ 0, 2 ] ) then Result := 0;
    If Result <> 0 then Exit;
  end;

  // Gibt es noch ein freies Feld? (Das Spiel geht dann noch weiter)
  Result := 0;
  for X := 0 to 2 do
  begin
    for Y := 0 to 2 do
    begin
      if Spielfeld[ X, Y ] = 0 then Exit;
    end;
  end;

  // Es gab bisher keinen Gewinner. Freie Felder gibt es auch nicht mehr.
  // Das Spiel endet also unentschieden.
  Result := 3;

end;

end.

hoika 11. Apr 2017 21:10

AW: Finde den Fehler einfach nicht
 
Hallo,
F5, Strg+F5, F7 und F8 schon probiert?

a.def 11. Apr 2017 21:23

AW: Finde den Fehler einfach nicht
 
Zitat:

Um genau zu sein scheint die Gewinner Abfrage nicht zu funktionieren, doch ich finde den Fehler einfach nicht.
Hier muss man präzisieren was denn überhaupt gewünscht ist. Denn "funktioniert nicht" kann alles bedeuten.

P.S.:
Code:
Code-Tags sind cool
aber
Delphi-Quellcode:
Delphi-Tags sind
cool-er
8-)

himitsu 11. Apr 2017 22:40

AW: Finde den Fehler einfach nicht
 
PS: [DELPHI]fdfd[/DELPHI]


Zitat:

Delphi-Quellcode:
  // Welcher Button wurde gedrückt?
  X := 90;
  Y := 90;
  for Index := 0 to Self.ControlCount - 1 do
  begin
   If Sender = Self.Controls[ Index ] then
   begin
     X := Index mod 3;
     Y := Index div 3;
   end;
  end;

Du versuchst also auch einer "zufälligen" Reihenfolge "Aller" Controls auf der Form zu bestimmen welcher Knopf es ist?
Ich wäre da nicht sicher, dass X und Y immer richtig sind. :stupid:
Aber das würde dir auch der Debugger sagen, wenn man ihn verwendet, auf einen Knopf drückt und dann schaut was danach wirklich in X und Y steht.

PS: Alle VCL-Komponenten haben ein "Tag" property, das der Entwickler frei verwenden kann.
z.B. könnte man bei den Knöpfen 11, 12, 13, 21, ... 33 eintragen und dann

Delphi-Quellcode:
X := (Sender as TSpeedButton).Tag mod 10;
Y := (Sender as TSpeedButton).Tag div 10;

nahpets 11. Apr 2017 23:05

AW: Finde den Fehler einfach nicht
 
Hallo,

bisher kann ich den Fehler auch nicht finden.

Aber:
Delphi-Quellcode:
  // Gibt es diagonal einen Gewinner? (Von Links-Oben nach Rechts-Unten)
  for Y := 0 to 2 do
  begin
    Result := Spielfeld[ 0, 0 ];
    if ( Result <> Spielfeld[ 1, 1 ] ) or ( Result <> Spielfeld[ 2, 2 ] ) then Result := 0;
    If Result <> 0 then Exit;
  end;
Die For-Schleife erscheint mir hier überflüssig, es gibt nur eine entsprechende Diagonale, da Y in der Schleife nicht genutzt wird, dürfte das dreifache Durchlaufen der Schleife überflüssig sein.
Gilt analog auch für die andere Diagonale.

Zitat:

Um genau zu sein scheint die Gewinner Abfrage nicht zu funktionieren, doch ich finde den Fehler einfach nicht.
Könntest Du das bitte präzisieren?

Funktioniert das grundsätzlich nicht oder nur ab und an oder nur bei bestimmten Konstellationen?

Der Vorschlag von himitsu ist meiner Meinung nach sehr sinnvoll. Über die "Liste" der Controls kann man zwar abfragen, ob man eine passende Komponenten gefunden hat, aber die Reichenfolge ist nicht zwingend aufsteigend nach Namen, sondern wohl von der Erstellungsreihenfolge in der IDE abhängig. Speedbutton1 muss also nicht unbedingt Self.Controls[0] sein. Allerdings müsste das auffallen, weil dann die Caption nicht korrekt gesetzt wird.

Bei himitsus Ansatz müsste meiner Meinung nach aber
Delphi-Quellcode:
Spielfeld: array[0..2, 0..2] of Byte;
in
Delphi-Quellcode:
Spielfeld: array[1..3, 1..3] of Byte;
geändert werden und die For-Schleifen entsprechend von 1 bis 3 gehen. Ebenso müsste dann
Delphi-Quellcode:
// Wurde der Button gefunden?
  if ( X > 2 ) or ( Y > 2 ) then exit;
geändert werden in
Delphi-Quellcode:
// Wurde der Button gefunden?
  if ( X > 3 ) or ( Y > 3 ) then exit;
Wobei diese Abfrage eigentlich überflüssig sein dürfte, da ja nur den SpeedButtons 1 bis 9 die Ereignisroutine zugewiesen wurde. Sollte die Bedingung also erfüllt sein, ist noch irgendwas anderes nicht in Ordnung.

Daniel 12. Apr 2017 08:08

AW: TicTacToe - Frage zum Programmablauf
 
Moin und willkommen in der DP. :hi:
Ich habe mal den Titel Deines Themas angepasst, damit dieser für Dritte aussagekräftiger ist.

DeddyH 12. Apr 2017 08:12

AW: TicTacToe - Frage zum Programmablauf
 
Dann hättest Du aber auch gleich die Code- durch Delphi-Tags ersetzen können :mrgreen:

ShadowDeath 12. Apr 2017 09:27

AW: TicTacToe - Frage zum Programmablauf
 
Also, erstmal danke für die zahlreichen Antworten. Ich habe erst einmal die falschen Tags korrigiert, damit es übersichtlicher ist. Zum Punkt, was den nun nicht funktioniert: Er erkennt nie den Gewinner, egal welche Gewinn Konstellation ich teste (sei es diagonal, vertikal oder horizontal). Gewünscht ist, dass er eine Message mit dem Gewinner ausgibt.

Delphi-Quellcode:
 // Sieger ermitteln
  Gewinner := ErmittelGewinner();

  // Entsprechend des Siegers eine Meldung ausgeben
  case Gewinner of
    0: Exit;
    1,2: ShowMessage( Format( 'Spieler %d hat gewonnen.', [ Gewinner ] ) );
    3: ShowMessage( 'Das Spiel endet unentschieden.' );
  end;
Und bezüglich der Tags von VCL-Komponenten: Ich war mir bewusst, dass es diese Eigenschaft für VCL Komponente gibt. Doch leider habe ich mich vorher nie damit auseinander gesetzt, deswegen nahm ich die 'unsaubere' Methoden anhand der Erstellungsreihenfolge (1-9 gehören zum Spielfeld, 10 ist der Neustart Button). Also dürfte es da keine Probleme geben (außer die Unsauberkeit des Codes).
Ich werde versuchen weitgehend alles Überflüssige zu entfernen (For-Schleifen) bzw. zu optimieren (Tags). Dennoch bin ich überzeugt, dass der Fehler woanders liegt. Ich würde mich sehr freuen, wenn ich noch ein paar Lösungsansätze finden würdet bzw. mit mir teilen würdet. :)

a.def 12. Apr 2017 09:58

AW: TicTacToe - Frage zum Programmablauf
 
Delphi-Quellcode:
case Gewinner of
    0: Exit;
Das ist überflüssig, da nach dem Case ja eh kein Code mehr kommt.

Whookie 12. Apr 2017 10:11

AW: TicTacToe - Frage zum Programmablauf
 
1. Die Lösung steht doch vermutlich schon in #4
2. Verwende den Debugger und schau dir deine Variablen an
3. Wozu soll die angehängte .exe im Zipfile gut sein? Wenn du möchtest das sich das jemand hier ansieht dann häng das Projekt an, so das man es compilen kann (.exe erstellen kann hier sogut wie jeder ;) )


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:20 Uhr.
Seite 1 von 3  1 23      

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