Delphi-PRAXiS

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 ;) )

ShadowDeath 12. Apr 2017 11:45

AW: TicTacToe - Frage zum Programmablauf
 
Ich habe es jetzt nochmal ausführlich getestet. Dabei ist mir aufgefallen, dass es manchmal funktioniert und manchmal nicht. Leider bin ich nicht in der Lage die Ursache zu finden, die fürs funktionieren/nicht funktionieren verantwortlich ist. Ich denke aber es liegt an gesagtem ControlCount. Leider weiß ich nicht, wie ich das Programm debuggen soll. In der Starter Edition ist die Debug Option leider nicht enthalten :/

a.def 12. Apr 2017 11:48

AW: TicTacToe - Frage zum Programmablauf
 
Dann setz doch mal zur Überprüfung an verschiedenen Stellen Showmessage()-Boxen, welche die aktuellen Variableninhalte oder sonst was ausgeben.
So mache ich das immer, obwohl ich einen Debugger habe.

nahpets 12. Apr 2017 12:06

AW: TicTacToe - Frage zum Programmablauf
 
Nachdem ich jetzt mal 'ne Weile mit der EXE rumgespielt habe, bin ich mir fast sicher, dass das interne Spielfeld hier
Delphi-Quellcode:
 Spielfeld[ X, Y ] := Spieler;
anders befüllt wird, als die optische Darstellung der Captions suggeriert.

Um das zu prüfen, solltest Du zuerst mal mit dem Debugger schauen, was genau passiert. (Sorry, hatte den Hinweis, auf des Fehlen des Debuggers, irgendwie nicht wahrgenommen :-()

Dort, wo Du dem Anwender das Ergebnis per ShowMessage anzeigst, könntest Du auch mal das Spielfeld ausgeben. Das könnte mit 'nem weiteren ShowMessage passieren:
Delphi-Quellcode:
ShowMessage(Format('%d %d %d',[SpielFeld[0,0],SpielFeld[1,0],SpielFeld[2,0]) + #13
+ Format('%d %d %d',[SpielFeld[0,1],SpielFeld[1,1],SpielFeld[2,1]) + #13
+ Format('%d %d %d',[SpielFeld[0,2],SpielFeld[1,2],SpielFeld[2,2]));
(Hoffentlich hab' ich hier jetzt nicht X und Y vertauscht.)
Die Anzeige sollte mit der Darstellung der Speedbutton-Captions übereinstimmen.
Wenn nicht, stimmt die erwartete Reichenfolge der Speedbuttons in Self.Controls nicht mit der tatsächlichen überein.
Anders kann ich mir die etwas chaotische Ergebnisermittlung, mit kaum einem richtigen Ergebnis, nicht vorstellen.

a.def 12. Apr 2017 12:12

AW: TicTacToe - Frage zum Programmablauf
 
Zitat:

Um das zu prüfen, solltest Du zuerst mal mit dem Debugger schauen, was genau passiert.
Seine IDE hat keinen Debugger :stupid:

Whookie 12. Apr 2017 12:31

AW: TicTacToe - Frage zum Programmablauf
 
Nimm einfach die Tag-Eigenschaft und schreib da die Koordinaten rein so wie das in #4 steht!
Die Erstellreihenfolge hat genau NICHTS mit deiner Anordnung der Buttons zu tun...

SProske 12. Apr 2017 12:43

AW: TicTacToe - Frage zum Programmablauf
 
Sofern ich mich nicht ganz falsch erinnere, hat die Starter durchaus einen Debugger, nur wurden einem Variableninhalte nicht beim Mouseover angezeigt, sondern diese mussten zu den überwachten Ausdrücken hinzugefügt werden. Haltepunkte setzen, durchsteppen etc. funktionierte aber ganz normal.

madas 12. Apr 2017 12:58

AW: TicTacToe - Frage zum Programmablauf
 
Schnellschuss ins Blaue:

Delphi-Quellcode:
unit Unit1;

interface

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

type
  TPlayer = (pX = -1, pNobody = 0, pO = 1);

  TForm1 = class(TForm)
    Button11: TButton;
    Button12: TButton;
    Button13: TButton;
    Button21: TButton;
    Button22: TButton;
    Button23: TButton;
    Button31: TButton;
    Button32: TButton;
    Button33: TButton;
    ButtonNewGame: TButton;
    LabelCurrentPlayer: TLabel;

    procedure ButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ButtonNewGameClick(Sender: TObject);
  private
    fCurrentPlayer, fLastWinner: TPlayer;
    dFrom13To31, dFrom11To33, FMoves: Integer;
    fZeilen, fSpalten: array[1..3] of Integer;

    procedure NewGame;
    procedure NextPlayer;
    procedure ResetPlayground;
    procedure UpdateStatus;

    function IsGameOver: Boolean;
    procedure DisablePlayground;
  public
  end;

var
  Form1: TForm1;

implementation

uses TypInfo;

{$R *.dfm}

procedure TForm1.ButtonClick(Sender: TObject);
var
  btn: TButton;
  x, y: Integer;
begin
  if (Assigned(Sender)) and (Sender is TButton) then
  begin
    Inc(FMoves);

    btn := TButton(Sender);

    case fCurrentPlayer of
      pX: btn.Caption := 'X';
      pO: btn.Caption := 'O';
    end;

    btn.Enabled := false;

    x := btn.Tag mod 10;
    y := btn.Tag div 10;

    Inc(fSpalten[x], Ord(fCurrentPlayer));
    Inc(fZeilen[y], Ord(fCurrentPlayer));

    if (x = y) then
    begin
      Inc(dFrom11To33, Ord(fCurrentPlayer));
      if (x = 2) then
        Inc(dFrom13To31, Ord(fCurrentPlayer));
    end else
    begin
      if (Abs(x - y) = 2) then
        Inc(dFrom13To31, Ord(fCurrentPlayer));
    end;

    // Gewinner ermitteln
    if (IsGameOver) then
    begin
      DisablePlayground;
      case fLastWinner of
        pX: ShowMessage('Winner is X');
        pO: ShowMessage('Winner is O');
        pNobody: ShowMessage('draw');
      end;
      Exit;
    end;

    NextPlayer;
  end;
end;

function TForm1.IsGameOver: Boolean;
var
  i: Integer;
begin
  Result := False;
  for i := 1 to 3 do
  begin
    Result := Result or (Abs(fSpalten[i]) = 3) or (Abs(fZeilen[i]) = 3);
  end;
  Result := Result or (Abs(dFrom13To31) = 3) or (Abs(dFrom11To33) = 3);
  if (Result) then
  begin
    fLastWinner := fCurrentPlayer
  end else
  if (FMoves = 9) then
  begin
    Result := True;
    fLastWinner := pNobody;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  fCurrentPlayer := pX;
  fLastWinner := pNobody;
  NewGame;
end;

procedure TForm1.NewGame;
begin
  ResetPlayground;
  NextPlayer;
end;

procedure TForm1.DisablePlayground;
var
  i: Integer;
begin
  for i := 0 to Self.ControlCount - 1 do
  begin
    if (Self.Controls[i] is TButton) and
       (TButton(Self.Controls[i]).Tag > 0) then
    begin
      TButton(Self.Controls[i]).Enabled := false;
    end;
  end;
end;

procedure TForm1.ResetPlayground;
var
  i: Integer;
begin
  FMoves := 0;
  for i := 0 to Self.ControlCount - 1 do
  begin
    if (Self.Controls[i] is TButton) and
       (TButton(Self.Controls[i]).Tag > 0) then
    begin
      TButton(Self.Controls[i]).Caption := EmptyStr;
      TButton(Self.Controls[i]).Enabled := true;
    end;
  end;

  for i := Low(fSpalten) to High(fSpalten) do
    fSpalten[i] := 0;
  for i := Low(fZeilen) to High(fZeilen) do
    fZeilen[i] := 0;
  dFrom13To31 := 0;
  dFrom11To33 := 0;
end;

procedure TForm1.NextPlayer;
begin
  if (fLastWinner = pNobody) then
  begin
    fCurrentPlayer := TPlayer(Ord(fCurrentPlayer) * -1)
  end else
  begin
    fCurrentPlayer := TPlayer(Ord(fLastWinner) * -1);
    fLastWinner := pNobody;
  end;
  UpdateStatus;
end;

procedure TForm1.UpdateStatus;
begin
  case fCurrentPlayer of
    pX: LabelCurrentPlayer.Caption := 'current player: X';
    pO: LabelCurrentPlayer.Caption := 'current player: O';
  end;
end;

procedure TForm1.ButtonNewGameClick(Sender: TObject);
begin
  NewGame;
end;

end.
Delphi-Quellcode:
object Form1: TForm1
  Left = 414
  Top = 203
  BorderStyle = bsToolWindow
  Caption = 'TicTacToe'
  ClientHeight = 264
  ClientWidth = 201
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object LabelCurrentPlayer: TLabel
    Left = 8
    Top = 8
    Width = 67
    Height = 13
    Caption = 'current player:'
  end
  object Button11: TButton
    Tag = 11
    Left = 8
    Top = 32
    Width = 57
    Height = 57
    TabOrder = 0
    OnClick = ButtonClick
  end
  object Button12: TButton
    Tag = 12
    Left = 72
    Top = 32
    Width = 57
    Height = 57
    TabOrder = 1
    OnClick = ButtonClick
  end
  object Button13: TButton
    Tag = 13
    Left = 136
    Top = 32
    Width = 57
    Height = 57
    TabOrder = 2
    OnClick = ButtonClick
  end
  object Button21: TButton
    Tag = 21
    Left = 8
    Top = 96
    Width = 57
    Height = 57
    TabOrder = 3
    OnClick = ButtonClick
  end
  object Button22: TButton
    Tag = 22
    Left = 72
    Top = 96
    Width = 57
    Height = 57
    TabOrder = 4
    OnClick = ButtonClick
  end
  object Button23: TButton
    Tag = 23
    Left = 136
    Top = 96
    Width = 57
    Height = 57
    TabOrder = 5
    OnClick = ButtonClick
  end
  object Button31: TButton
    Tag = 31
    Left = 8
    Top = 160
    Width = 57
    Height = 57
    TabOrder = 6
    OnClick = ButtonClick
  end
  object Button32: TButton
    Tag = 32
    Left = 72
    Top = 160
    Width = 57
    Height = 57
    TabOrder = 7
    OnClick = ButtonClick
  end
  object Button33: TButton
    Tag = 33
    Left = 136
    Top = 160
    Width = 57
    Height = 57
    TabOrder = 8
    OnClick = ButtonClick
  end
  object ButtonNewGame: TButton
    Left = 8
    Top = 224
    Width = 185
    Height = 25
    Caption = 'neues Spiel'
    TabOrder = 9
    OnClick = ButtonNewGameClick
  end
end

ShadowDeath 12. Apr 2017 13:11

AW: TicTacToe - Frage zum Programmablauf
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hab mal die Ausgabe als Screen hochgeladen. Da ist deutlich ersichtlich, dass die Positionen nicht stimmen (wie bereits von euch gesagt). Ich möchte aber nicht einfach copy & pasten, also wäre es super, wenn jemand die Zeit/Lust hätte mir das mit den Tags so zu erklären, dass ich es verstehe & selbst einbauen kann.:oops::|

DeddyH 12. Apr 2017 13:46

AW: TicTacToe - Frage zum Programmablauf
 
Wenn Du Dein Spielfeld als Array[0..2, 0..2] of TSpeedButton deklarierst und entsprechend befüllst, kannst Du doch direkt auf den jeweiligen SpeedButton zugreifen und dort in dessen Tag-Eigenschaft schreiben bzw. daraus lesen. Das ist zwar das genaue Gegenteil von Trennung von Logik und Darstellung, macht die Sache aber viel einfacher IMO.

nahpets 12. Apr 2017 14:17

AW: TicTacToe - Frage zum Programmablauf
 
Zitat:

Zitat von ShadowDeath (Beitrag 1367331)
Hab mal die Ausgabe als Screen hochgeladen. Da ist deutlich ersichtlich, dass die Positionen nicht stimmen (wie bereits von euch gesagt). Ich möchte aber nicht einfach copy & pasten, also wäre es super, wenn jemand die Zeit/Lust hätte mir das mit den Tags so zu erklären, dass ich es verstehe & selbst einbauen kann.:oops::|

Bitte gib' zuerst den Speedbuttons aussagefähige Namen:

Beispiel:

Der Speedbutton unten links bekommt den Namen btnX1Y1.
Der unten in der Mitte btnX2Y1 und unten rechts der btnX3Y1.

Als Tag gibst Du jeweils die beiden Ziffern aus dem Namen ein, also unten links Tag = 11, unten in der Mitte Tag = 21 und unten rechts Tag = 31.

Analog dann auch die übrigen Speedbuttons.

Den Speedbutton 10 benennst Du z. B. btnNeuesSpiel.

Danach übernimmst Du den Vorschlag von himitsu aus Post #4.

Wenn ich jetzt nicht zusehr um die Ecke gedacht habe, könnte es dann schon funktionieren.

Die Eigenschaft Tag ist eigentlich nichts weiter als ein Integer, der grundsätzlich erstmal für nix benötigt wird.
Die Eigenschaft lässt sich aber gut für die Zuordnung von nummerischen Werten, die im Programmablauf benötigt werden, "missbrauchen". Anstatt abzufragen: "Wurde SpeedbuttonX gedrückt" (oder gar den gedrückten Button erst in einer Liste (Self.Controls) zu suchen), kann man hier halt den nummerischen Wert abfragen. Und wenn man den beim Programmdesign so wählt, dass er sich in die Programmlogik einfügt, kann man sich damit sehr viel Arbeit ersparen.

ShadowDeath 12. Apr 2017 15:27

AW: TicTacToe - Frage zum Programmablauf
 
Erstmal danke nahpets, dass du dir die Zeit genommen hast mir das zu erklären. Die Gewinn-Abfrage funktioniert jetzt auch. :) Leider ist ein neues Problem aufgetreten: Der Neustart Button hat den Geist aufgegeben. Beim Klicken sagt er, dass eine Zugriffsverletzung vorliegt? Ich habe aus der 3 eine 10 gemacht, da wir ja weiter oben mit 10 Teilen (Beim Kommentar 'Spiel intern und optisch löschen'). & Sorry für die dämlichen Fragereien, aber ich arbeite noch nicht solange mit Delphi :x

Aktueller Code:
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)
    btnX1Y3: TSpeedButton;
    btnX2Y3: TSpeedButton;
    btnX1Y2: TSpeedButton;
    btnX2Y2: TSpeedButton;
    btnX3Y3: TSpeedButton;
    btnX3Y2: TSpeedButton;
    btnX1Y1: TSpeedButton;
    btnX2Y1: TSpeedButton;
    btnX3Y1: TSpeedButton;
    btnNeuesSpiel: TSpeedButton;
    procedure btnX1Y3Click(Sender: TObject);
    procedure btnNeuesSpielClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);

  private
    { Private-Deklarationen }
    Spielfeld: array[1..3, 1..3] 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.btnX1Y3Click(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 := (Sender as TSpeedButton).Tag mod 10;
     Y := (Sender as TSpeedButton).Tag div 10;
   end;
  end;

  // Wurde der Button gefunden?
  if ( X > 3 ) or ( Y > 3 ) 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();
  //ShowMessage( Format( 'Spieler %d hat gewonnen.', [ Gewinner ] ) );

  // 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.btnNeuesSpielClick(Sender: TObject);
var
  X, Y: Byte;
begin
   // Spiel intern und optisch löschen
   for X := 1 to 3 do
   begin
     for Y := 1 to 3 do
     begin
       Spielfeld[ X, Y ] := 0;
       TSpeedButton( Self.Controls[Y * 10 + 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 := 1 to 3 do
  begin
    Result := Spielfeld[ X, 1 ];
    if ( Result <> Spielfeld[ X, 2 ] ) or ( Result <> Spielfeld[ X, 3 ] ) then Result := 0;
    If Result <> 0 then Exit;
  end;

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

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

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

  // Gibt es noch ein freies Feld? (Das Spiel geht dann noch weiter)
  for X := 1 to 3 do
  begin
    for Y := 1 to 3 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.

DeddyH 12. Apr 2017 15:30

AW: TicTacToe - Frage zum Programmablauf
 
Zitat:

Delphi-Quellcode:
TSpeedButton( Self.Controls[Y * 10 + X ] ).Caption := '';

Das wären bei X=3 und Y=3 ja 34 Controls (Index beginnt bei 0), und alle müssen Speedbuttons sein. Das erscheint mir ein wenig viel.

ShadowDeath 12. Apr 2017 15:33

AW: TicTacToe - Frage zum Programmablauf
 
Zitat:

Zitat von DeddyH (Beitrag 1367372)
Zitat:

Delphi-Quellcode:
TSpeedButton( Self.Controls[Y * 10 + X ] ).Caption := '';

Das wären bei X=3 und Y=3 ja 34 Controls (Index beginnt bei 0), und alle müssen Speedbuttons sein. Das erscheint mir ein wenig viel.

Also wäre 2 der passende Multiplikator?

EDIT: Habe es grade mit 2 getestet. Leider lässt er 12,13 & 23 so wie sie sind & cleart dafür den Neustart-Speedbutton.
EDIT 2: ich habe vom Produkt nochmal 1 abgezogen, damit der neustart Button bleibt. Jetzt werden nur noch 13 & 23 nicht gecleart. :x

nahpets 12. Apr 2017 15:39

AW: TicTacToe - Frage zum Programmablauf
 
Dashier ist jetzt so nicht mehr richtig:
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 := (Sender as TSpeedButton).Tag mod 10;
     Y := (Sender as TSpeedButton).Tag div 10;
   end;
  end;
Dashier sollte ausreichen:
Delphi-Quellcode:
  X := (Sender as TSpeedButton).Tag mod 10;
  Y := (Sender as TSpeedButton).Tag div 10;
Wir wollen ja, weil es nicht funktioniert, vom Durchlaufen von Self.Controls weg. Die Nutzung der Eigenschaft Tag dient ja genau der Abschaffung dieser fehleranfälligen Vorgehensweise.

Wir brauchen durch die Änderung auf die Tag-Nutzung nicht mehr abfragen, welcher Button gedrückt wurde. Der Button teilt uns dies durch den Inhalt von Tag selbst mit.

Der Button mit Tag = 11 sagt dadurch letztlich: "Ich bin der Butten von unten links."
Der Button mit Tag = 33 sagt uns: "Ich bin der Button von oben rechts."

Mit Tag mod 10 bzw. Tag div 10 wird der zweistellige Wert von Tag auf X und Y "verteilt", also in die jeweiligen Koordinaten umgerechnet.

ShadowDeath 12. Apr 2017 15:42

AW: TicTacToe - Frage zum Programmablauf
 
Ist erledigt. Trotzdem wird weiterhin 13 & 23 nicht gecleart.

nahpets 12. Apr 2017 15:49

AW: TicTacToe - Frage zum Programmablauf
 
Dieses ist so nicht korrekt, die "geographische" Lage der Buttons hat sich durch die Nutzung von Tag und die geänderten Namen nicht verändert.
Delphi-Quellcode:
 for X := 1 to 3 do
   begin
     for Y := 1 to 3 do
     begin
       Spielfeld[ X, Y ] := 0;
       TSpeedButton( Self.Controls[Y * 10 + X ] ).Caption := '';
     end;
   end;
Hier würd' ich es mal so versuchen:
Delphi-Quellcode:
  for X := 1 to 3 do for Y := 1 to 3 do Spielfeld[X,Y] := 0;
  for X := 0 to Self.Controls.Count - 1 do
    begin
      if Self.Controls[X] is TSpeedButton then
        begin
          if TSpeedButton(Self.Controls[X]).Tag <> 0 then TSpeedButton(Self.Controls[X]).Caption := '';
        end;
    end;
(Ist jetzt nur ungetestet hingedaddelt. Schreibfehler (wie immer) nicht ausgeschlossen.)

HolgerX 12. Apr 2017 15:54

AW: TicTacToe - Frage zum Programmablauf
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hmm..


Hab mir deinen Source genommen..
- Den Startbutton aber als TBitBtn
- Den Speedbuttons einen Tag von 0-9 verpasst
- Entsprechende Stellen umgestellt oder bereinigt

Und siehe da dein Basis-Code läuft.

ShadowDeath 12. Apr 2017 16:01

AW: TicTacToe - Frage zum Programmablauf
 
@nahpets: Danke, jetzt geht es :) & nochmals danke für die guten Erklärungen.
@Holger: Danke auch an dich. Ich werde mir den Code gleich anschauen :)
@ALLE: Danke für eure hilfreichen Antworten und Lösungsansätze.

PS: Ich habe noch ein Problem, um das ihr euch kümmern könnt, zudem ich nachher einen Thread erstellen werde ^^ (Geht um ein anderes Spiel)


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:15 Uhr.

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