![]() |
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. |
AW: Finde den Fehler einfach nicht
Hallo,
F5, Strg+F5, F7 und F8 schon probiert? |
AW: Finde den Fehler einfach nicht
Zitat:
P.S.:
Code:
aber
Code-Tags sind cool
Delphi-Quellcode:
8-)
Delphi-Tags sind
cool-er |
AW: Finde den Fehler einfach nicht
PS: [DELPHI]fdfd[/DELPHI]
Zitat:
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; |
AW: Finde den Fehler einfach nicht
Hallo,
bisher kann ich den Fehler auch nicht finden. Aber:
Delphi-Quellcode:
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.
// 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; Gilt analog auch für die andere Diagonale. Zitat:
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:
in
Spielfeld: array[0..2, 0..2] of Byte;
Delphi-Quellcode:
geändert werden und die For-Schleifen entsprechend von 1 bis 3 gehen. Ebenso müsste dann
Spielfeld: array[1..3, 1..3] of Byte;
Delphi-Quellcode:
geändert werden in
// Wurde der Button gefunden?
if ( X > 2 ) or ( Y > 2 ) then exit;
Delphi-Quellcode:
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.
// Wurde der Button gefunden?
if ( X > 3 ) or ( Y > 3 ) then exit; |
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. |
AW: TicTacToe - Frage zum Programmablauf
Dann hättest Du aber auch gleich die Code- durch Delphi-Tags ersetzen können :mrgreen:
|
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:
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).
// 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; 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. :) |
AW: TicTacToe - Frage zum Programmablauf
Delphi-Quellcode:
Das ist überflüssig, da nach dem Case ja eh kein Code mehr kommt.
case Gewinner of
0: Exit; |
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 ;) ) |
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 :/
|
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. |
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:
anders befüllt wird, als die optische Darstellung der Captions suggeriert.
Spielfeld[ X, Y ] := Spieler;
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:
(Hoffentlich hab' ich hier jetzt nicht X und Y vertauscht.)
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])); 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. |
AW: TicTacToe - Frage zum Programmablauf
Zitat:
|
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... |
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.
|
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 |
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::|
|
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.
|
AW: TicTacToe - Frage zum Programmablauf
Zitat:
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 ![]() 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. |
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. |
AW: TicTacToe - Frage zum Programmablauf
Zitat:
|
AW: TicTacToe - Frage zum Programmablauf
Zitat:
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 |
AW: TicTacToe - Frage zum Programmablauf
Dashier ist jetzt so nicht mehr richtig:
Delphi-Quellcode:
Dashier sollte ausreichen:
// 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;
Delphi-Quellcode:
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.
X := (Sender as TSpeedButton).Tag mod 10;
Y := (Sender as TSpeedButton).Tag div 10; 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. |
AW: TicTacToe - Frage zum Programmablauf
Ist erledigt. Trotzdem wird weiterhin 13 & 23 nicht gecleart.
|
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:
Hier würd' ich es mal so versuchen:
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;
Delphi-Quellcode:
(Ist jetzt nur ungetestet hingedaddelt. Schreibfehler (wie immer) nicht ausgeschlossen.)
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; |
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. |
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