![]() |
Neue Komponente mit 2 TEdit Komponenten
Hallo,
ich muss eine Komponente mit 2 Editfeldern und 6 Feldern mit Textausgabe erstellen. Bevor ich damit beginne wollte ich eure Meinung hören wie man das am besten angeht. Verwende ich TEdit oder besser TCustomEdit? Wie erzeuge ich die Editfelder an besten?Wie bekomme ich die Onchance Ereignise der Editfelder in ein gemeinsames OnChance Ereignis? Wie positioniert man die Editfelder auf seiner Komponente? usw. Ich bin für jeden Hinweis und Erklärung dankbar denn mit solchen zusammengesetzten Komponenten habe ich mich überhaupt noch nicht beschäftigt. Gruss Dieter |
Re: Neue Komponente mit 2 TEdit Komponenten
Schau dir doch mal die TFrame-Komponente an.
|
Re: Neue Komponente mit 2 TEdit Komponenten
TCustom<Kompo> sind die dafür geschaffenen Basisklassen
|
Re: Neue Komponente mit 2 TEdit Komponenten
|
Re: Neue Komponente mit 2 TEdit Komponenten
Falls schon etwas Basiswissen da ist, ist vielleicht auch der Quellcode von LabeledEdit ganz interessant. Der ist ja bei Delphi mit dabei (zumindest in der kostenlosen Turbo 2006, hoffe dann auch in der 2005 PE?).
|
Re: Neue Komponente mit 2 TEdit Komponenten
Hallo zusammen,
danke für eure schnellen Antworten. Zitat:
Zitat:
Zitat:
Dieter |
Re: Neue Komponente mit 2 TEdit Komponenten
Zitat:
TFRame ist einfach, Menü -> Datei -> Neu -> Frame. Ist dann wie ein neues Formular, in das man seine Einzelkomponenten plazieren kann. Das ergebnis ist aber mE. keine neue Komponente, sondern eben ein Frame. |
Re: Neue Komponente mit 2 TEdit Komponenten
Zitat:
Gründe: * unflexibel, die Komponente verbirgt das Innenleben meistens so stark, dass man häufig die Schnittstelle der Komponente ändern muss * umständliche Weitergabe des Projekts - das Installieren der Komponente nervt * die Komponente lässt sich nur in einem Projekt verwenden, weil sie zu speziell ist * wenn man öfters solche Spezialkomponenten erstellt, müllt man sich die IDE zu und schleppt den Code im Speicher mit Wie Bernhard Geyer schon gesagt hat, wäre ein Frame hier das Richtige. Dabei wichtig: vom Formular aus möglichst nicht direkt auf die Controls auf dem Frame durchgreifen, sondern über Properties abkapseln. |
Re: Neue Komponente mit 2 TEdit Komponenten
Zitat:
1. Warum nicht durchgreigen? 2. Wenn die Propertys aller enthaltenen Controls abkapseln soll, dann kann er doch auch gleich ein CustomPanel nehmen und daraus eine Komponente mit den enthaltenen Edits usw. schreiben. Das Kapseln macht er dann über einen zentralen Wrapper. Den hat er schön im Inspektor und setzt mit einem Ruck die Eigenschaften aller enthaltenen Controls (z.B. Color, Font etc.). Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
Zitat:
Beispiel: Das Edit-Control mit Namen EditKanal soll in eine HistoryCombobox (neuer Name CboKanal) umgewandelt werden. Diese Änderung könnte mehrere Änderungen am Rest der Anwendung nach sich ziehen. Wenn man dem Frame das Property "Kanal" gibt, dann beschränken sich die Änderungen auf das Frame. Ausserdem zeigt man so explizit, was in das Frame rein- und rausgeht. Zitat:
Aber ein Frame ist leichtgewichtig; man kann schnell mal etwas ändern und darf dann auch man direkt auf die innerliegenden Controls zugreifen. Eine Komponente ist dagegen schwergewichtig; die innerliegenden Controls sind nicht direkt erreichbar. Wenn man z.B. die Farbe eines inneren Controls von Aussen ändern möchte, dann muss man ein neues Property einführen. Dazu muss man das Package neu kompilieren und kann dann erst in der Anwendung das neue Property benützen. Und dann fällt einem ein, dass das mit der Farbe doch keine so tolle Idee war und man muss den Prozess wieder durchlaufen. Ich schätze mal, dass eine Änderung an einer Komponente 5 Mal mehr Zeit benötigt, als eine Änderung an einem Frame. |
Re: Neue Komponente mit 2 TEdit Komponenten
Hallo shmia,oki
Natürlich ist das eine Komponente die nur für ein spezielles Projekt ist, und auch nur dort verwendet wird. Die beiden Editfelder sich auch die einzigen Komponenten die auf der Komponente erstellt werden sollen. die Textausgaben werden im Paint der Komponente mit Outtext ausgegeben(deshalb TCostumControl). Bis jetzt hat mir aber auch noch keiner gesagt wie man zwei Editfelder am besten erstellet, macht man das besser im Constructeur mit self oder nil oder verwendet man eine andere Procedure. Wie führt man die beiden OnChance Ereignisse zusammen in ein Ereignis wie auch immer sie heißen mag. Das sind die Probleme die mich zur Zeit am meisten beschäftigen und auf die ich gerne eine Antwort hätte. Das mit den Frames ist für mich momentan nur Bömische Dörfer, da ich wie gesagt diese noch nie richtig benutzt habe.Von meiner Komponente benötige ich im eigentlichen Programm bis zu 120Stk geht das mit Frames überhaupt. Hallo Satty67, Natürlich vermisse ich die Quelltexte der Komponenten, da man dort doch sehr viel lernen kann. |
Re: Neue Komponente mit 2 TEdit Komponenten
@shmia: Überzeugt :mrgreen:
Nur am Rande, ich hasse Frames. Hatte ich nur Ärger mit. Vielleicht hatte ich mich damit auch nur zu blöd angestellt. So ist das halt mit Vorurteilen. Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
grrr roter Kasten!
@optiplex: Im Constructor mit Owner nil creieren, im CreateWnd Parent self zuweisen (natürlich für die enthaltenen Komponenten). Das mit den Ereignissen erklär noch mal. Wies geht kann ich dir sagen, aber obs das ist, was du willst? gruß |
Re: Neue Komponente mit 2 TEdit Komponenten
Zitat:
Niemals Frames ableiten; das funktioniert nicht richtig. Unterhalb Delphi 5.02 sind Frames ebenfalls sehr zickig. Und man muss immer mal wieder mit ALT-F12 kontrollieren, welche Properties des Frames in der DFM-Datei des Formulars überschrieben werden und diese ggf. von Hand löschen. Wenn man das beachtet und es nicht übertreibt mit den Frames, dann kann man damit ganz gut leben. |
Re: Neue Komponente mit 2 TEdit Komponenten
Hallo Oki,
soll das heißen das ich eine Procedure CreateWnd überschreiben muss ? (dann bräuchte ich die genaue Syntax dieser Procedure) um dort die Eigenschaft Parent zuzuweisen. Ich dachte eigentlich dass die Eigenschaften immer im Constructeur zugewiesen werden. Das mit Edit.Create(nil) im Constructeur habe ich verstanden. Aber warum Nil und nicht Self kannst du mir das erklären? Das neue Ereignis soll folgendermaßen funktionieren, wird ein Editfeld verändert, soll dadurch das neue Ereignis ausgelöst werden. Ich muss praktische auf Änderungen in den Editfeldern reagieren können. Ist es besser TEdit oder TCostumEdit zu verwenden? Dieter |
Re: Neue Komponente mit 2 TEdit Komponenten
Delphi-Quellcode:
Der Owner muss nil sein, weil sonst beim Speichern des Formulares in dem du deine neue Kompo verwendest deine Edits mit serialisiert werden (stehen dann in der dfm mit drin). Beim nächsten Laden wird dann ein Edit durch den Construktor erstellt und eins durch die IDE beim Lesen der dfm. Wieder speichern, sind beim nächsten mal schon 3 ....
TMyCustomControl = class(TCustomControl)
privat FEdit1 : TEdit; FEdit2 : TEdit; FOnEditChange : TNotifyEvent; ... protected procedure CreateWnd; override; procedure EditChangeEvent(Sender : TObject); published property OnEditChange : TNotifyEvent read FOnEditChange write FOnEditChange; ... procedure TMyCustomControl.CreateWnd; begin Edit1.Parent := self; Edit1.OnChange := EditChangeEvent; Edit2.Parent := self; Edit2.OnChange := EditChangeEvent; ... end; ... procedure TMyCustomControl.EditChangeEvent(Sender : TObject); begin if Assigned(FOnEditChange) then FOnEditChange(Sender); end; Verpasse deiner Komponente ein eigenes Ereignis OnEditChange vom Typ TNotifyEvent. Dann so wie oben. Willst du nicht mit dem Sender arbeiten, so kannst du dir da auch eine eigene Ereignisprocedure anlegen und alles was so interessant ist mit hoch reichen. Bsp.:
Delphi-Quellcode:
Das dann anstelle TNotifyEvent, die Ereignisproceduren anpassen, Text des sendenden Edit mit hochreichen etc.
TEditChangeTextEvent = procedure(Sender : TObject; EditText : String) of Object;
Oder was immer du oben auch brauchst. Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
Hallo Oki,
Vielen Dank für die Infos, ich muss also im Constructer die Edit-Felder Createn und in der CreateWnd die Eigenschaften setzen. Ich gehe davon aus, dass ich die Edit-Felder im Destructor wieder freigeben muss.? Da du FEdit1 : TEdit; im Code genommen hast nehme ich an,dass du TEdit und nicht TCustomEdit als Vorgänger bevorzugst oder hat dies einen besonderen Grund, dass du TEdit gemommen hast. Muss ich sonst noch irgendwelche Besonderheiten beachten? Ich werde am Wochenende mit dieser Komponente beginnen und hoffe, dass ich mit diesen Infos (die sehr gut sind, vor allem weil du sie erklären kannst) diese Komponente erstellen kann ohne allzuviele Fehlermeldungen. Gruss Dieter, |
Re: Neue Komponente mit 2 TEdit Komponenten
Hallo Optiplex,
ob die Edits von TCustomEdit oder TEdit kommen ist in diesem Fall egal. Du willst sie ja in deiner Komponente verwenden und nicht vererben. Da der Owner nil ist, werden sie ja nicht gestreamt. Somit sind die Published-Felder hier uninteressant. Bei einer Vererbung eines Edits würde ich TCustomEdit verwenden, hier TEdit. Und ja, im Destructor frei geben. Das ist besserer Stil. Normalerweise wird beim Beenden der Anwendung automatisch alles frei gegeben, aber sollte deine Componente zur Laufzeit frei gegeben werden, so bleibt eine Speicherleiche. Das automatische Freigeben übernimmt sonst der Owner, der ist hier aber nil. Sonst noch beachten? Ja, da gibt es einiges, aber im Moment fällt mir konkret nichts bei deinem Ansatz auf. Ich würde mir nur ein ordentliches Handling für dein OnEditChange ausdenken. Da alle Edits das gleiche Ereignis benutzen sollen ist die Identifizierung im Programm recht umständlich, welches Edit nun gerade geändert wurde. Da ich nicht weis, was du vor hast kann ich hier schlecht raten. Auf jeden Fall würde ich nicht TNotifyEvent verwenden sonder eine eigene Ereignisprocedure erstellen. Enthalten sollte die mindestens: - Sender -> das Panel - Text -> neuer Text des betroffenen Edits - EditItent -> Identifier des Edits, das betroffen ist (Name, Index oder sonst was) Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
Danke Oki,
Hier mal ein paar nähere Infos zur Komponente. Also das ganze gehört zu einem Turnierprogram und in dieser Komponente soll das Ergebnis einer Partie eingetragen werden, im Programm muss ich dann auf diese Eingabe reagieren können, um das Ergebnis auszuwerten(dafür soll das neue Ereignis sein).Die Textausgaben sind nur Texte Spielrunde Startnummern der beiden Spieler und deren Spielernamen sowie der Spielort. Ich habe mich entschlossen eine Komponente dafür zu entwickeln weil bei 16 Spielern in einem Doppel KO system schon 30 Partien notwendig sind und normalerweise wir 32 Spieler bzw 64 Spielern pro Turnier in der Vergangenheit hatten. Dann kannst du dir vorstellen wie viele Edits und Labels notwendig sind um eine Ergebnistabelle zu erstellen und gut aussehen soll das ganze auch noch da der Bildschirm auf eine Leinwand bzw großen LCD-Bildschirm gezeigt wird. Das ist mein erstes größeres Projekt an den ich bestimmt noch einige Wochen arbeiten werde, da ich nur Hobbyprogrammierer bin, und nur in meiner Freizeit mich damit beschäftigen kann. Deshalb bin ich auch für jede Hilfe oder Tip dankbar. Gruß Dieter |
Re: Neue Komponente mit 2 TEdit Komponenten
Joop, schönes Projekt.
Überleg dir erst mal, was die Edits melden sollen und was du im Programm von denen als Angaben brauchst um die Eingabe auswerten/verarbeiten zu können. Dann schreib dir dein Ereignis und implementiere das. Solltest du da nicht weiter kommen, helfe ich gerne. Aber erst mal musst du nachdenken. Was du im Programm von den Edits brauchst kann ich schwer sagen. Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
Mir fällt grad ein, brauchst du da nicht ein Panel wo du dynamisch angeben kannst wieviele Paare spielen und dir dann die richtige Anzahl an Edits erstellt?
Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
Ja Oki, die Tabelle muss sich dynamisch erweiten von 8,16,32,64 Spielern, das heißt wenn die Spieleranzahl welche mitspielen diese Grenzen erreichen muss die Tabelle entsprechend erweitert werden. wird das Turnier dann gestartet müssen die fehlenden Spielern mit Freilosen aufgefüllt werden, deren Positionen in einem Array gespeichert sind.(damit nicht ein Freilos ins Finale kommt) Ist eine Partie zu ende wird der Gewinner und Verlierer ermittelt (Aufgabe für das neue Ereignis) und die Spieler werden dann entsprechend in der Tabelle einer neuen Partie zugeordnet ist eine Partie vollständig dann kann diese gespielt werden. usw.
Schön dass du mir deine Hilfe anbietest werde ich auch bestimmt nutzen und gebrauchen können. Es kann allerdings sein, dass ich mich erst wieder nächsten Dientag melden kann, ich hoffe dass ich bis dahin recht weit bei meinem Komponentenbau gekommen bin. Bis dann Dieter |
Re: Neue Komponente mit 2 TEdit Komponenten
Joop, wenn du Fragen hast, her damit. Ich bin aber auch erst am Mittwoch wieder da, und da wo ich bis dahin bin, gibs kein INet. :cry:
Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo oki,
dank deiner Hilfe habe ich die Komponente TDartResultEdit am WE entwickelt. Siehe Anhang ( TDartResultEdit.Jpg ), was noch nicht implementiert ist das OnEdirChance Ereignis. Hier muss ich mir noch genau überlegen wie ich dieses Ereignis mache. Ich habe am WE viel dazugelernt aber es haben sich auch viele Fragen ergeben die du mir vielleicht beantworten kannst Leider musste ich feststellen, dass der Focus zum Aktiven Control auf meiner Komponente wandert. Kann man das Irgendwie verhindern in dem das Control zwar aktiviert wird aber der Focus bei meiner Komponenete bleibt. Dadurch musste ich, weil ich eine Cursorsteuerung implementiert hatte, diese Auf die OnKeyDown Ereignisse des jeweiligen Controls umleiten dies hatte zur folge das die Cursorsteuerung nicht mehr so richtig funktionierte( Vor alle bei einem TButtom). Mit
Delphi-Quellcode:
kann man bei seiner Komponente die CursorTasten abfangen. Wie macht man das bei den SubKomponenten? Dies ist das Aktuelle OnKeyDown Ereignis das wie gesagt an den Subkomponenten hängt(Sorry hatte noch keine Zeit den Code zu bereinigen)
Protected
procedure WMGetDLGCode(var msg: TMessage); message WM_GETDLGCODE; .... Procedure TDartResultEdit.WMGetDLGCode(var msg: TMessage); begin inherited; msg.Result:= msg.Result or DLGC_WantArrows; end;
Delphi-Quellcode:
Funktioniert mit den TEdits . Ich wollte noch einen Button (Auswahl Spielort) rechts am Ende der Komponenete integrieren,(was mit deiner Beschreibung kein Problem war, auch ein eigenes Ereignis war schnell integiert),doch leider bringt dieser Button meine Cursorsteuerung durcheinandert, bei hoch und runter springt er nicht zur nächsten Komponente innerhalb der TDartResultEdit Klasse(was bei den Edits einwandfrei funktioniert) sondern auf das erste bzw. zweite Editfeld innerhalb der Subkomponenten. (Warum?).
procedure TDartResultEdit.EditKeyDownEvent(Sender: TObject; var Key: Word; Shift: TShiftState);
var iii:integer; obj: tObject; edi:TEdit; btn:TButton; begin obj:=nil; If(Key=vk_Down) or (Key=vk_up)then begin for iii :=0 to Parent.ComponentCount-1 do if Parent.Components[iii].classtype=TDartResultEdit then begin if TDartResultEdit(Parent.Components[iii]).enabled and TDartResultEdit(Parent.Components[iii]).Visible then case Key of vk_Down: begin if (TDartResultEdit(Parent.Components[iii]).Left = Left) and (TDartResultEdit(Parent.Components[iii]).Top > Top)then if obj=nil then obj:= Parent.Components[iii] else if (TDartResultEdit(Parent.Components[iii]).Top < TDartResultEdit(obj).Top) then obj:=Parent.Components[iii]; end; vk_up: begin if (TDartResultEdit(Parent.Components[iii]).Left = Left) and (TDartResultEdit(Parent.Components[iii]).Top < Top)then if obj=nil then obj:= Parent.Components[iii] else if (TDartResultEdit(Parent.Components[iii]).Top > TDartResultEdit(obj).Top) then obj:=Parent.Components[iii]; end; end; end; if obj<>nil then if TDartResultEdit(obj).Enabled and TDartResultEdit(obj).Visible then TDartResultEdit(obj).Setfocus; end; if Key=vk_Right then begin if Sender.ClassType =TEdit then begin edi:=TEdit(Sender); if edi.Name = FPlayerEdit1.Name then FPlayerEdit2.SetFocus; // if edi.Name = FPlayerEdit2.Name then FBoardButton.SetFocus; end; // if Sender.ClassType =TButton then FPlayerEdit1.SetFocus; end; // btn:=TButton(Sender); // if edi.Name = FPlayerEdit1.Name then FPlayerEdit2.SetFocus; // if edi.Name = FPlayerEdit2.Name then FBoardButton.SetFocus; // end; if Key=vk_Left then begin if Sender.ClassType =TEdit then begin edi:=TEdit(Sender); if edi.Name = FPlayerEdit2.Name then FPlayerEdit1.SetFocus; // if edi.Name = FPlayerEdit1.Name then FBoardButton.SetFocus; end else FPlayerEdit2.SetFocus; // if Sender.ClassType =TButton then FPlayerEdit2.SetFocus; end; // if Key=VK_Space then Click; inherited; end; Kann man den Text ein einem Editfeld zentrieren? So und nun noch mal zu OnEditChance. Die Eingabe der Edits habe ich auf Zahlen beschränkt(funktioniert). Ich muss in diesem Ereignis 1.Die Gültigkeit des eingegebenen Ergebnisses auswerten (best of) 2 Ist das Spiel durch dieses Ergebnis beendet oder läuft es noch weiter 3.Ist es ein Endergebnis (best of) dann Verlierer und Gewinner auswerten und diese in die nächste Runde springen bzw. ausscheiden lassen. Dazu brauche ich alle TEdit.Texte und natürlich den Sender da das im meinem Programm eigentlich nur eine Routine werden soll. Was meinst du wie ich das hier am besten löse? Du hast mir ja schon zwei eventuelle Lösungen genannt welche soll ich bevorzugen? Da du für das Projekt Intresse hast, habe ich dir ein PrintScreen von einem Spielplan (Ist aber auch noch nicht 100% fertig) so wie er Später im LCD-Fernseher oder Leinwand zu sehen sein wird angehängt. Über die Komponente TDartResultEdit werden die Ergebnisse und die daraus folgenden Partien in diesen Spielplan eingetragen. Sind genug Fragen auf einmal (hoffe ich werde dir nicht lästig). Gruß Dieter Natürlich kann sich jeder hier beteiligen der eine Lösung für meine Fragen kennt |
Re: Neue Komponente mit 2 TEdit Komponenten
Huch hab ich euch jetzt erschreckt?
Dieter |
Re: Neue Komponente mit 2 TEdit Komponenten
Hallo,
ja, eine ganze Menge Fragen, erschreckt aber nicht. Bin seit heute erst wieder zurück. Ich denke, wir machen eins nach dem anderen. Das ganze Thema mit dem Fokus habe ich noch nicht so richtig geschnallt. Ich würd lieber mit den Ereignissen anfangen. Du hast ja eine recht eindeutige Nummerierung deiner Spiele. Ich denke mal, dass du im Programmablauf auf die Eigaben bezüglich der Auswertung usw. reagieren willst. Wobei ich denke, dass das auch in der Kompo klappen sollte. Ich würde der Kompo erst ein mal eine Property PlayerCount verpassen. Da gibt man dann die Anzahl der Spieler ein. Daraus wird dann auch automatisch das Schema in deiner Abbildung Spielplan_16 erstellt. Die Anzahl der TDartResultEdits ergibt sich ja automatisch. Jedes Edit sollte als Name den Text erhalten, den du über die Spielerpaare geschrieben hast mit dem suffix 1 oder 2 erhalten. Bsp.:
Delphi-Quellcode:
Das ist nur ein erster Ansatz. Die Logik, wieviel Spiele wie zusammengestellt werden kennst du selbst am besten.
For PlayRount := 1 to PlayerCount div 2 do begin
For Player := 1 to 2 do begin with TDartResultEdit.create(nil) do begin Name := Format('R1_%d_%d', [PlayRound, Player]); OnChange := self.OnDartResultEditChange; ... Jetzt nimmst du dein Ereignis On Change und wertest die Einträge aus. Somit kannst du gleich alle folgenden Felder mit den Spielernamen füllen.
Delphi-Quellcode:
Ich denke, dass es Sinn macht, die einzelnen Cases in Funktionen auszulagern. Desgleichen würde ich für die einzelnen Spieler eine eigene Klasse TPlayer = class TObject anlegen. Die dann alle in einer ObjectList halten. So wird die Verwaltung einfacher. An das Programm würde ich eigentlich nichts hoch senden. Die Einträge in die weiteren Felder kann eigentlich deine Komponente machen. Über das Programm steuerst du eigentlich nur den Input wie Anzahl Spieler, Spielername, zufällige oder feste Paarbildung usw.
procedure MyCustomControl.OnDartResultEditChange(Sender : TObject);
var SenderName : String; RoundChar : Char; PLayRound, Player : Integer; begin // hier macht es Sinn, den Namen des sendenden Edit zu zerlegen. if not Sender is TDartResultEdit then Exit; SenderName := TComponent.Name; // jetzt zerlegen und auf die einzelnen Segmente prüfen case ORD(RoundChar) of // das ist z.B. das R ORD('R') : begin case PlayRound of // das ist das Spiel der Runde 1 : begin .... // usw. end; end; end; ... end; end; Ich glaube, ich hab hier versehentlich TDartResultEdit als Eingabefeldt (Edit) des Spielstandes gesetzt. Wenn das falsch ist, sag bescheid. Jo, sag mal, wie dier das gefällt. Dann können wir ja konkret weiter machen. Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
Hallo Oki,
wie ich sehe hast du dir den Spielplan genau angesehen, nun noch ein paar Erklärungen dazu. also R1-1 bis R1-8 bedeutet Runde 1 Spiel 1-8 da dies ein Doppel OK System ist gibt es eine Gewinnerseite G und eine Verliererseite V daraus ergibt sich die Gewinnerrunde1 Spiel 1-4 und eine Verlierer Runde 1 Spiel1-4 usw. Verliert ein Spieler ein zweites mal scheidet er aus, verliert ein Spieler in der Gewinnerrunde kommt er in die Verliererrunde und zwar an die Sprungpunkte A-G je nachdem wie weit er in der Gewinnerrunde gekommen ist. ich wollte bei unserer Komponente noch zweit Eigenschaften hinzufügen und zwar einmal Gewinner und Verlierer als String und hier sollten die Runde eingetragen werden bei der es weitergeht( zB G1-2/2 oder V1-2/1) die /1 bzw. /2 bedeutet, Spieler1 bzw. Spieler2 in unser Komponte. Hinterlegt wird in der entsprechenden Runde G1-2 usw. Damit könnte ich den kompletten Ablauf des Turniers mit der Ergebnistabelle steuern. (Natürlich könnte man auch einen Array erstellen, aber darüber habe ich noch nicht nachgedacht wie der Aussehen müsste.) So hatte ich mir das ungefähr Theoretisch vorgestellt. Allzu weit weg sind deine Gedanken über den Ablauf ja nicht von den meinen und ich werde mir heute Abend darüber nachdenken ob deine Idee nicht besser ist, mit der Auswertung innerhalb der Komponente, wobei die Auswertung recht komplex ist. bis dann Dieter |
Re: Neue Komponente mit 2 TEdit Komponenten
Hallo Oki,
also gegen eine Auswertung in der Komponente ist nichts einzuwenden, allerdings bräuchten wir hier noch ein Property BestOf damit die Eingabe ausgewertet werden kann (best of 3,5,7,9,11), dann muss noch ein Property Gewinner/Verlierer(Startnummer/Name des Spielers) dazu damit diese an die nächste Runde weitergegeben werden kann. Wie du sicherlich gemerkt hast ist jede Runde mit einer weiteren Runde Verlierer/Gewinner fest verknüpft und diese Verknüpfung wollte ich fest in die Komponente den Propertys Verlierrunde/Gewinnerrunde(siehe gestern) mit angeben. Diese beiden Propertys sind eigentlich nur Dummys und werden von unserer Komponente nicht beeinflust. So habe ich alle wichtigen Infos an die Komponente gelegt. Die weiteren Eintragungen in der Ergebnistabelle und im Spielplan würde ich dem eigentlichen Programm überlassen.(man braucht nur die Captions zu vergleichen und die Propertys auszutauschen) Die Anzahl der Spieler ist immer gleich bei einem Doppel OK System (entweder 8,16,32,64,128) fehlende Spieler werden durch Freilose deren Position in Runde 1 fest vorgegeben sind (damit nicht ein Freilos in die Ränge kommen kann ).Der benötigte Spielplan wird dann über die angemeldeten Spieler ausgewählt. Ich werde dir am WE einmal zusammenschreiben wie so ein Turnier abläuft und auf was zu achten ist damit du auch die Infos alle hast.(Hoffentlich vertreibe ich dich damit nicht, weil so ein Event doch ziehmlich Komplex ist). |
Re: Neue Komponente mit 2 TEdit Komponenten
Hallo,
ich sehe das mit der Auswertung nicht so problematisch. Sobald ein Ergebnis in eingegeben wird ist ja klar, wer gewonnen hat. Somit ist auch gleich eindeutig, in welchem Feld (G, V usw.) der entsprechende Spieler eingetragen wird. Auch die Numerik für das entsprechende Spiel ist logisch nachvollziehbar, unabhängig von der Anzahl der Spieller und Runden. So gehen z.B. die Gewinner der Runde R1-1 und R1-2 in die Runde G1-1, aus R1-3 und R1-4 in G1-2 usw. Das ist imho wieder eine 2 zu 1 Beziehung in einer weiteren Ebene. Diese Gewinner gehen in G2-1 auf(Gewinner aus G1-1 und G1-2 nach G2-1). Wieder eine 2 zu 1 Beziehung. Der Vorteil ist sogar, dass somit ein relativ einfaches mathematisches Prinzip abgebildet werden kann. Und das gilt dann eigentlich sehr universell. Aus zwei mach eins eine Ebenen höher. Rieht ganz deutlich nach einer universellen Funktion mit einem simplen Vergleich und Inkrementieren der Spielebene. Wenn man das im Griff hat, kann man die Ermittlung des neuen Eintragfeldes für einen Spieler recht universell über eine Funktion ermitteln, bei der man die Aktuelle Runde, beide Spielpartner und den Spielstand übergiblt. Zurück bekommt man die Bezeichner der Felder, in die die Spieler neu einzutragen sind. Für die Übersicht des aktuellen Standes würde ich über die Spieler und die Spielerliste gehen. Dazu würde ich noch eine zusätzliche Liste der Spiele anlegen. Das in etwa so:
Delphi-Quellcode:
Jetzt hast du folgende Möglichkeiten. Deine neues Control hat die Eigenschaften PlayerList. Wird PlayerList verändert, so kann dein Control aus dem Member Count der PlayerList den Spielaufbau generieren. Alle weiteren Eigenschaften wie zufällige Spielpaarzusammenstellung usw. lassen wir mal außer acht. Das ist Finetuning und berührt die generelle Funktion der Komponente nicht.
TPlayResult : TPoint; // TPoint zweckentfremden für Spielstand oder gleich eigener Typ
// Vorward-Declaration TPlayerClass : class; TPlayClass = class(TObject) private FPlayResult : TPlayResult; FPlayer1: TPlayerClass; FPlayer2: TPlayerClass; FRound : String; .... end; // Liste der durchgeführten Spiele jedes Spielers TPlayList = class(TObjectList) ... end; TPlayerClass = class(TObject); private FParentList : TPlayList; FPlayerName : String; .... FPlayList : TPlayList; FPosition : Integer; // aktueller Platz des Spielers im Turnier ... end; // Liste aller Spieler TPlayerList = class(TObjectList) private FParentControl : TMyControl; .... end; Über das Programm kannst du die Bearbeitung der PlayerListe incl. des einzelnen Players steuern. Ähnlich, als ob du mit einer Datenbank arbeitest. Durch die Zuweisung, Änderung reagiert deine Komponente. Willst du das automatisieren, so erhält deine PlayerList noch einen Member ParentControl. Der kann dann vom Typ deiner Komponente sein. Ist dieser Member ungleich nil, so informiert die PlayerListe automatisch das ParentControl über Änderungen, damit diese sich in der Ansicht aktualisieren kann. Das gleiche kannst du natürlich auch mit der PlayerClass machen. Der Witz währe folgender. Mitten im Spiel bemerkt ein Spieler, dass seine Name Falsch eingetragen ist. Du änderst über die Eingabemaske den Namen in seinem zugehörigen Klassenobjekt TPlayerClass. Der Setter für den Namen ruft eine eigene Methode ChangePlayer der PlayerList auf, die ja als Member in TParentList abgelegt ist. Diese wiederum ruft die eigene Methode Change der ParentComponent auf. Die Parent Component zeichnet sich damit neu. Dabei macht es dann Sinn, wenn die Edits zusätzlich einen Member der Player haben, denen das entprechende Player-Object zugewiesen wurde. Du trägst also nicht den Namen des Spielers ein, sondern weist einen zu. Der Name wird beim zeichnen also aus der Eigenschaft PlayerName des PlayerObjectes eingetragen. Durch die Methode Change wird ja die Componente neu gezeichnet. Der Name in PlayerName des Edits hat sich in der Zwischenzeit geändert und somit neu gezeichnet. Alles automatisch, keiner rekursiven Funktionen nötig, ändern an der Stelle wo man ändern muss, neu zeichnen und alles fertig und up to date. Großer Zauber Manitu. Hast du in den Edits die neuen Spielstände eingetragen, so wird über die EditChange-Methode die oben beschriebene Auswertefunktion angestoßen. Ein Object der TPlayClass wird erstellt, dem Player zugewiesen, den neuen Eintragsfeldern (Edits) das richtige Playerobject zugewiesen, refresh (Change deiner Komponente) fertig. Aktuelle Listen für Spielstand, Platz jedes Spielers usw. implementierst du als Funktionen deiner PlayerList, die du über das Hauptprogramm aufrufst und das entsprechende Ergebnis von der Funktion zurück bekommst. Im Prinzip passiert hier folgendes. Du hast: - Datenklassen wie TPlayClass, TPlayList (Spiel, Spielliste), TPlayerClass, TPlayerList (Spieler, Spielerliste). - Visualisierungsklassen -> deine Komponente - Ein-/Ausgabeklassen; deine Komponente für Spielstände, Eingabemaske für die Spieler - GUI; Hauptprogramm, auf dem die visuellen Komponenten platziert sowie die Menüs und Buttons für die Spielsteuerung und Einstellungen platziert sind. Joo, so würde ich es in etwa machen. Das Thema mit den PlayClass ist mir noch nicht ganz sauber. Finde im Moment aber keinen Ansatz das zu vereinfachen oder zu zentralisieren. Ich habe den Eindruck, man muss das über die PlayList aufziehen und denen die Player zuweisen und nicht umgekehrt. Kann man ja noch drüber nachdenken. Aber ich glaube so ist es richtig. Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
Hatte wohl etwas lange an dem letzten Beitrag geschrieben.
Hallo optiplex :lol: sorry für die Monologe. Ich empfehle folgendes. Wir erstellen erst die Datenmodelle. Dann die visuellen Komponenten. Das mit den Funktionen und Eigenschaften ist dann ganz einfach. Die können sucsessive erweitert werden. Wenn die Struktur und die Klassen stimmen ist das andere alles ein Klacks. Vertrau mir, ich wies was ich tu ( :mrgreen: kennst du den Spruch? stammt aus einer alten Serie) gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
Also, ich hab noch mal nachgedacht. Du solltest die PlayList nicht den Playern zuordnen, sondern die Player den PlayClasses. Das macht mehr Sinn.
Wenn du Hilfe bei der Erstellung der Klassen brauchst sag bescheid, dann mach ich das (brauch mal ne kleine Ablenkung mit ner leichten Aufgabe). Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
Puuu Oki, das ist harte Kost für mich.
Mit Klassen habe ich mich noch nicht so viel beschäftigt und kenne deren Vor- und Nachteile überhaupt nicht. Ich habe auch noch nicht geschnallt, wie du das mit den Verknüpfungen, wann und wo welcher spieler weiterspielt oder ausscheidet über die Klassen regeln willst. Das mit einer kompletten Teilnehmerliste ist OK die hätte ich sowieso gebraucht auch schon wegen der eventuellen Namensänderung. Du schreibst das so als wäre das alles kein Problem. Für mich sind da ein paar böhmische Dörfer schon dabei, werde allerding versuchen mich am Wochenende schlau zu machen, und wie gesagt fehlen dir noch ein paar Infos die ich am Wochenende zusammenschreiben werde, nur damit du nicht unnötig zeit investierst und sagst das hättest du mir gleich sagen sollen. Datenmodel erstellen ? wie ? aber ich bin lernbereit, denn wie gesagt ich bin Hobbyprogrammierer. Leider habe ich am Wochenende kein Inet, es kann daher sein, dass ich mich erst am Montag wieder melde.(bin aber heute bis 16 Uhr erreichbar und schaue nochmal hier vorbei. Zitat:
Gruss Dieter |
Re: Neue Komponente mit 2 TEdit Komponenten
Slatch Hammer (ob richtig geschrieben, keine Ahnung?), das war der Bulle, der in seine Knarre verliebt war und ständig mit der rumgeballert hat (z.B. Fliegen von der Wand :mrgreen: ).
das zum wichtigen Teil der der Nachricht. :mrgreen: So, nun zum Datenmodell. das ist gar nicht so schwer. Fangen wir mal ganz vorne an; die Player. Hier mal auf die Schnelle die Klassen für die Spieler und die Spielerliste. Alle Funktionen für die Statistiken usw. die direkt die Spieler betreffen generieren wir dann in der PlayerList.
Delphi-Quellcode:
Alles, was die Spiele betrifft kommt dann in die PlayList. Das später.
unit Player_Classes;
interface type // Forward TPlayerList = class; // Der ShowMode definiert, wie der Name des Spielers ausgegeben wird TShowNameMode = (snm_FullName, // Vorname und Name anzeigen snm_Name, // nur Nachname anzeigen snm_ShortFirstName // Vorname abgekürzt, Nachname voll ); (* Klasse eines einzelnen Spielers. Der Member PlayerList gibt die übergeordnete Liste an, die den Spieler hält. *) TPlayerClass = class(TObject) private FBirthday: TDateTime; FFirstName: String; FName: String; FPlayerList: TPlayerList; FShowNameMode: TShowNameMode; function GetFullName: String; procedure SetBirthday(const Value: TDateTime); procedure SetFirstName(const Value: String); procedure SetName(const Value: String); procedure SetShowNameMode(const Value: TShowNameMode); public property PlayerList : TPlayerList read FPlayerList write FPlayerList; property Birthday : TDateTime read FBirthday write SetBirthday; property FirstName : String read FFirstName write SetFirstName; property Name : String read FName write SetName; property ShowNameMode : TShowNameMode read FShowNameMode write SetShowNameMode; property FullName : String read GetFullName; end; TPlayerList = class(TObjectList) private protected function GetItem(Index: Integer): TPlayerClass; procedure SetItem(Index: Integer; AObject: TPlayerClass); public function Add(AObject: TPlayerClass): Integer; function Extract(Item: TPlayerClass): TPlayerClass; function Remove(AObject: TPlayerClass): Integer; function IndexOf(AObject: TPlayerClass): Integer; procedure Insert(Index: Integer; AObject: TPlayerClass); function First: TPlayerClass; function Last: TPlayerClass; procedure PlayerChanged; property Items[Index: Integer]: TPlayerClass read GetItem write SetItem; default; end; implementation { TPlayerClass } function TPlayerClass.GetFullName: String; begin case FShowNameMode of snm_FullName: Result := Format('%s %s', [FFirstName, FName]); snm_Name: Result := FName; snm_ShortFirstName: Result := Format('%s. %s', [FFirstName[1], FName]); end; end; procedure TPlayerClass.SetBirthday(const Value: TDateTime); begin if FBirthday = Value then Exit; FBirthday := Value; if Assigned(FPlayerList) then FPlayerList.PlayerChanged; end; procedure TPlayerClass.SetFirstName(const Value: String); begin if FFirstName = Value then Exit; FFirstName := Value; if Assigned(FPlayerList) then FPlayerList.PlayerChanged; end; procedure TPlayerClass.SetName(const Value: String); begin if FName = Value then Exit; FName := Value; if Assigned(FPlayerList) then FPlayerList.PlayerChanged; end; procedure TPlayerClass.SetShowNameMode(const Value: TShowNameMode); begin if FShowNameMode = Value then Exit; FShowNameMode := Value; if Assigned(FPlayerList) then FPlayerList.PlayerChanged; end; { TPlayerList } function TPlayerList.Add(AObject: TPlayerClass): Integer; begin AObject.FPlayerList := self; Result := inherited Add(AObject); end; function TPlayerList.Extract(Item: TPlayerClass): TPlayerClass; begin Result := TPlayerClass(inherited Extract(Item)); result.FPlayerList := nil; end; function TPlayerList.First: TPlayerClass; begin Result := TPlayerClass(inherited First); end; function TPlayerList.GetItem(Index: Integer): TPlayerClass; begin Result := TPlayerClass(inherited GetItem(Index)); end; function TPlayerList.IndexOf(AObject: TPlayerClass): Integer; begin Result := inherited IndexOf(AObject); end; procedure TPlayerList.Insert(Index: Integer; AObject: TPlayerClass); begin AObject.FPlayerList := self; inherited Insert(Index, AObject); end; function TPlayerList.Last: TPlayerClass; begin Result := TPlayerClass(inherited Last); end; {----------------------------------------------------------------------------- Procedure: PlayerChanged Author: Dipl. Ing. Olaf Keitsch Date: 05-Jun-2009 Arguments: None Result: None Describtion: Diese Methode wird von den Settern der Player aufgerufen. An dieser Stelle kann die Aktualisierung des anzeigenden Controls aufgerufen werden. -----------------------------------------------------------------------------} procedure TPlayerList.PlayerChanged; begin // do it end; function TPlayerList.Remove(AObject: TPlayerClass): Integer; begin AObject.FPlayerList := nil; Result := inherited Remove(AObject); end; procedure TPlayerList.SetItem(Index: Integer; AObject: TPlayerClass); begin AObject.FPlayerList := self; inherited SetItem(Index, AObject); end; end. In der Klasse PlayerList kannst du jetzt in den Propertys noch ergänzen, was du direkt zu den Spielern an Angaben erfassen willst. Hier kommt aber nichts zu den Spielen rein. Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
Hallo Oki,
Mensch du bist schneller als die Polizei erlaubt, hast du das alles fertig in der Schublade? Hab dir schnell mal zusammengeschrieben, damit du alle Infos hast die du brauchst. Da ein Dart Event aus mehreren Turnieren (Einzel Doppel in den jeweiligen Klassen) besteht die meist gleichzeitig laufen, habe ich mich entschlossen einen Eventmanager (Eigenständiges Programm) zu programmieren Im Manager sollen alle wichtigen eingaben die alle Turniere Betreffen erfolgen -Eingabe der Anzahl der vorhandenen Automaten -Sperrung eines Automaten -Eingabe der Spieler zum jeweiligen Turnier -starten eines Turniers (eigenständiges Programm je nach Anzahl der Spieler hier sind wir gerade mit unserer Komponente.) -unterbrechen eines Turniers -Turnier wieder aufnehmen. -ein Spieler gibt auf weil er dringend weg muss dann soll der Spieler an allen Turnieren an denen er angemeldet ist praktisch als Freilos weitergeführt werden, bis das Freilos ausgeschieden ist. Alles was alle Turniere betrifft wird in einer zentralen Datei gespeichert. hier rufen die einzelnen Turniere ihre Daten ab oder schreiben hinein. Ist wichtig bei Stromausfall, damit der Event an diesem Punkt weitergeführt werden kann. (sonst kann es passieren dass eine Horde über mich herfällt) Ablauf Erstellung eines Events Eingabe der Anzahl der vorhandenen Automaten Eingabe des Event namens = Dateiname der Ini Speicherung des Events in einem Verzeichnis. Anlegen einer INI - Datei mit den Grundeinstellungen Belegung der Automaten (Überwachung) jeder Automat an dem gespielt wird muss als belegt gekennzeichnet werden, damit er zur Auswahl in den Turnieren nicht mehr zur Verfügung steht. Automatenausfall Es muss möglich sein einen Automaten zu sperren (defekt) wird gerade dort gespielt muss eine Info zur Partie gehen, damit dort ein neuer Automat ausgewählt werden kann. Freilose(Überwachung) ist eine Partie mit einem Freilos komplett so wird sofort das Freilos als Verlierer gewertet und wird dementsprechend weitergereicht bzw. scheitet aus dem Turnier aus. Spieler spielt bereits in einem anderen Turnier (Überwachung) Jeder Spieler der gerade eine Partie spielt muss gekennzeichnet werden, damit er nicht nochmals zu einer Partie aufgerufen werden kann. Ein Spieler beendet vorzeitig das Turnier. gibt ein Spieler das Turnier vorzeitig auf so wird er ab dieser Runde behandelt wie ein Freilos. So nun hast du denke ich alle Infos die du brauchst. Ich hab dir ja gesagt dass das ganze ziemlich komplex ist. (Hoffentlich habe ich dich jetzt nicht verjagt) Wenn du willst kannst du dir ja ein Paar Gedanken machen. Ich werde mich mal mit deinen Klassen beschäftigen, scheint auf den zweiten Blick die Lösung für einige Probleme zu sein, bin aber noch nicht ganz durchgestiegen. Richtig ! An die Serie kann ich mich noch erinnern. Gruss bis Montag Dieter. |
Re: Neue Komponente mit 2 TEdit Komponenten
Hallo Dieter,
Dank für deine Erläuterungen. Bin zwar noch nicht ganz durchgestiegen bei den Themen Automaten, Events usw., fehlt mir die Praxis. Trotzdem sehe ich das alles nicht so wild. Entscheidend ist hier einfach nur der intelligente Klassenaufbau für die Spiele. Da hab ich das Thema Turniere usw. noch nicht ganz geschnallt. Vorallem die Abhängikeiten usw. Das ignoriere ich jetzt aber mal geflissentlich. Hat in der Ebene in der wir gerade stecken wohl noch keine Auswirkungen. Ich versuche gerade herauszubekommen, was wir alles für die PlayClass an Eigenschafen brauchen. Auf deutsch, was ist alles für ein Spiel (Paar) an Informationen relevant. Die beiden Spieler ist sicher klar. Dann der Spielstand. Was gehört noch alles zu einem Spiel? Der Automat vielleicht? Da musst du mal paar Angaben machen. Die PlayList hält dann zwar die einzelnen Spiele eines Turniers (denke ich), muss dann aber sicher noch einige Funktionen haben. Da musst du wieder helfen. Ich hatte mir das Händling dafür so vorgestellt. Die PlayList hält die PlayClass'es. wird in einer PlayClass ein Spielstand gespeichert, so wird die Playlist davon informiert (so wie das mit den Änderungen bei den Playern). Dabei wird dann aber das PlayObject mitgeliefert (Methode ChangePlayClass der PlayList). Das die PlayList dann weis, in welchem Spiel sich der Spielstand wie geändert hat, kann sie automatisch ermitteln, in welche weiteren Play-Objekte seiner Liste die entsprechenden Spieler eingetragen werden müssen. Ein Status für das entsprechende Spiel zum Sperren usw. kann dann die Playlist mit vergeben. Dazu kann man der PlayClass eine entsprechende Eigenschaft verpassen. Das alles ist schon mit Sicht auf die spätere Anzeige gedacht. Da machen sich solche Statusflags immer toll um die Anzeige anzupassen. Naja, ich denk mir da mal was aus. bis die Tage, Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
Hi,
ich hab mal die Klassen für die Spieler, Spielerliste, Spiele und Spielliste fertig gestellt. Leider hab ich das System für den Eintrag eines Spielers in ein neues Spiel noch nicht ganz durchschaut. Für die Gewinner ist das ja recht einfach. Bei den Verlierern hab ich da so meine Probleme. Ich pack hier mal die Procedure rein.
Delphi-Quellcode:
Hier noch einige Bemerkungen zum Ablauf. Wird in einem Spiel ein Spielstand (oder eine andere Eigenschaft) geändert so ruft das Spiel die Procedure ChangePlay seiner PlayList auf und übergibt sich selbst sowie den Namen der Eigenschaft an die Procedure.
procedure TPlayList.ChangePlay(APlayClass: TPlayClass; AProperty : String);
var AWinner, ALooser : TPlayerClass; procedure PlayerinGame(APlayer : TPlayerClass; AOldPlay : TPlayClass; winning : Boolean); var NewPlay : TPlayClass; begin if winning then begin // Spieler ist Gewinner des alten Spieles case ORD(AOldPlay.FGame) of ORD('R') : begin // Gewinner der ersten Runde if Odd(AOldPlay.Number) then begin NewPlay := GetPlay('G', 1, (AOldPlay.Number+1) div 2); if Assigned(NewPlay) then NewPlay.FPlayer1 := APlayer; end else begin NewPlay := GetPlay('G', 1, (AOldPlay.Number) div 2); if Assigned(NewPlay) then NewPlay.FPlayer2 := APlayer; end; end; ORD('G') : begin if Odd(AOldPlay.Number) then begin NewPlay := GetPlay('G', AOldPlay.FRound+1, (AOldPlay.FNumber+1) div 2); if Assigned(NewPlay) then begin NewPlay.FPlayer1 := APlayer; end else begin // das war das letzte Gewinnerspiel, ab ins Filale NewPlay := GetPlay('F', 0, 0); if Assigned(NewPlay) then NewPlay.Player1 := APlayer; end; end else begin NewPlay := GetPlay('G', AOldPlay.FRound+1, (AOldPlay.FNumber) div 2); if Assigned(NewPlay) then begin NewPlay.FPlayer2 := APlayer; end else begin // das war das letzte Gewinnerspiel, ab ins Filale NewPlay := GetPlay('F', 0, 0); if Assigned(NewPlay) then NewPlay.Player1 := APlayer; end; end; end; ORD('V') : begin NewPlay := GetPlay('V', AOldPlay.Round + 1, 1); if Assigned(NewPlay) then begin NewPlay.FPlayer1 := APlayer; end else begin // das war das letzte Gewinnerspiel, ab ins Filale NewPlay := GetPlay('F', 0, 0); if Assigned(NewPlay) then NewPlay.Player2 := APlayer; end; end; end; end else begin // Spieler ist Verlierer des alten Spieles case ORD(AOldPlay.FGame) of ORD('R') : begin // Gewinner der ersten Runde if Odd(AOldPlay.Number) then begin NewPlay := GetPlay('V', 1, (AOldPlay.Number+1) div 2); if Assigned(NewPlay) then NewPlay.FPlayer1 := APlayer; end else begin NewPlay := GetPlay('V', 1, (AOldPlay.Number) div 2); if Assigned(NewPlay) then NewPlay.FPlayer2 := APlayer; end; end; ORD('G') : begin // jo, da hab ich noch kein System entdeckt ??????????? end; ORD('V') : begin // das wars dann, kein weiteres Spiel :-( // oder doch? :-) hab ich das System aber auch noch nicht durchschaut end; end; end; end; begin // ein Spieler ist ein Freilos? if ((AProperty = 'Player1') or (AProperty = 'Player2')) and (Assigned(APlayClass.Player1) and Assigned(APlayClass.Player2)) and ((APlayClass.Player1.FPlayerType = pt_Looser) or (APlayClass.Player2.FPlayerType = pt_Looser)) then begin if APlayClass.Player1.FPlayerType = pt_Looser then begin // Player2 hat gewonnen AWinner := APlayClass.FPlayer2; ALooser := APlayClass.FPlayer1; end else begin // Player1 hat gewonnen AWinner := APlayClass.Player1; ALooser := APlayClass.Player2; end; // neues Spiel für Gewinner ermitteln PlayerinGame(AWinner, APlayClass, True); // neues Spiel für Verlierer ermitteln PlayerinGame(ALooser, APlayClass, False); // Counter der gespielten Spiele der Spieler erhöhen AWinner.PlayCount := AWinner.PlayCount + 1; ALooser.PlayCount := ALooser.PlayCount + 1; // aktuelle Platzierung der Spieler setzen AWinner.Position := FPlayerList.Count - AWinner.PlayCount; ALooser.Position := FPlayerList.Count - ALooser.PlayCount - 1; // Spielstatus auf beendet APlayClass.Status := ps_finish; Exit; end; // der Spielstand hat sich geändert if AProperty = 'PlayResult' then begin if (APlayClass.FPlayResult.Player1 <> 0) or (APlayClass.FPlayResult.Player2 <> 0) then begin // es gibt einen Spielstand <> 0 if APlayClass.FPlayResult.Player1 > APlayClass.PlayResult.Player2 then begin // Player1 hat gewonnen AWinner := APlayClass.Player1; ALooser := APlayClass.Player2; end else begin // Player2 hat gewonnen AWinner := APlayClass.Player2; ALooser := APlayClass.Player1; end; // neues Spiel für Gewinner ermitteln PlayerinGame(AWinner, APlayClass, True); // neues Spiel für Verlierer ermitteln PlayerinGame(ALooser, APlayClass, False); // Counter der gespielten Spiele der Spieler erhöhen AWinner.PlayCount := AWinner.PlayCount + 1; ALooser.PlayCount := ALooser.PlayCount + 1; // aktuelle Platzierung der Spieler setzen AWinner.Position := FPlayerList.Count - AWinner.PlayCount; ALooser.Position := FPlayerList.Count - ALooser.PlayCount - 1; // Spielstatus auf beendet APlayClass.Status := ps_finish; end; end; end; Die Procedure der PlayList macht jetzt folgendes. Erstens wird geschaut, ob der geänderte Parameter des Spiels der Spielstand oder der Typ des Spielers ist (Freilos bei Spieler). Ist ein gültiger Spielstand eingetragen, so wird der Gewinner und Verlierer des Spiels ermittelt. Dann werden diese an die Procedure PlayerinGame übergeben. Diese erhält folgender Werte: APlayer = Spieler der in ein neues Spiel eingetragen werden soll AOldPlay = Spiel das gerade zu Ende gespielt wurde winning = gibt an, ob der Spieler der Gewinner oder Verlierer des Spieles ist. Nun prüft die Procedure erst mal, welches Spiel das gerade gespielte Spiel ist, um das neue Spiel zu ermitteln, in das der aktuelle Spieler jetzt eingetragen werden soll. Der erste Block der Abfrage für den Gewinner sollte eigentlich soweit ok sein. Keinen Plan hab ich noch für den zweiten Block, Eintrag des Verlierers. Das vorallem wenn er ein V- oder G-Spiel verloren hat. Da die Procedure eine eigene Methode der PlayList ist, wird hier auf eigene Hilfsfunktionen zugegriffen. Das ist z.B. die Funktion GetPlay. GetPlay sucht in der Liste nach einem Spiel mit der angegebenen Bezeichnung und gibt die Instanz dieses Spieles als Ergebnis zurück. Wurde kein Spiel mit dieser Bezeichnung gefunden, so liefert die Funktion nil. Das ergibt natürlich, dass in der Liste alle Spiele schon eingetragen sind. Erledigt wird das bei der Übergabe der Spielerliste an die PlayList. Dabei wird auf Basis der Anzahl der in der PlayerList eingetragenen Spieler der gesamte Spielplan automatisch erstellt und alle Spiele eingetragen. Das ist auch schon fertig und hat so keine großen Probleme bereitet. Hier mal diese Funktion zur Kontrolle:
Delphi-Quellcode:
Du brauchst die Methoden hier nicht rauskopieren. Ich hab die Unit mit den Klassen schon fertig und hänge sie hier bei einem der nächsten Beiträge mit ran, dann kannst du sie dir direkt downloaden.
procedure TPlayList.UpdatePlayCount;
var APlayerCount : Integer; Counter, CRound : Integer; procedure AddPlays(AGame : Char; ARound, ANumberCount : Integer); var BCounter : Integer; begin for BCounter := 1 to ANumberCount div 2 - 1 do begin if not Assigned(GetPlay(AGame, ARound, BCounter)) then begin // Spiel ist noch nicht angelegt Add(TPlayClass.Create(AGame, ARound, BCounter)); end; end; end; begin if not Assigned(FPlayerList) or (FPlayerList.Count = 0) then begin Clear; end else begin Clear; APlayerCount := FPlayerList.Count; // bei ungerade Anzahl von Spielern einen dazu if odd(APlayerCount) then inc(APlayerCount); // R-Spiele anlegen AddPlays('R', 1, APlayerCount div 2); // G-Spiele anlegen Counter := APlayerCount div 4; CRound := 1; while (Frac(Counter/2) = 0) do begin AddPlays('G', CRound, Counter); inc(CRound); Counter := Counter div 2; end; // V-Spiele anlegen Counter := APlayerCount div 4; CRound := 1; while (Frac(Counter/2) = 0) do begin AddPlays('V', CRound, Counter); inc(CRound); if not odd(CRound) then Counter := Counter div 2; end; // Finale anlegen Add(TPlayClass.Create('F', 0, 0)); end; end; Ja, somit müssen wir nur noch die kleinen Probleme mit der Spielzuordnung für die Verlierer lösen und das Datenmodell ist fertig. Dann können wir uns um den visuellen Teil kümmern. Dann bis die Tage, Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
hallo Oki,
ich werde versuchen dir den Ablauf zu erklären, nimm bitte dazu den Spielplan den ich hier mal an ein post von mir angehängt habe zur Hand. R ist Runde1 hier beginnt das Turnier. Die Anzahl der Spieler beträgt entweder 8 oder 16 oder 32 oder 64 usw., sonst funktioniert ein Doppel KO System nicht. Die Gewinner jeder Paarung gehen in die Gewinnerrunde G1 die Verlierer in die Verliererrunde V1. Der Gewinner der Gewinnerrunde G1 geht weiter in die Gewinnerrunde G2 der Verlierer der Gewinnerrunde G1 geht in die Verliererrunde V2 und war an die jeweilige Position A,B,C usw. (gelbes Kästchen unterhalb der Paarung) welche bei der Gewinnerrunde G1 angegeben ist (Verlierer -> A). Der Verlierer aus der Verliererrunde V1 scheidet aus dem Turnier aus, weil er ein zweites mal verloren hat, der Gewinner der Verliererrunde V1 kommt weiter in die Verliererrunde V2 und trifft dort mit einem Verlierer aus der ersten Gewinnerrunde G1 zusammen. Schau dir den Plan genau an, dann weist du wann ein Verlierer der Gewinnerrunde in die Verlierrunde kommt und an welcher Position er weiterspielt, den Zwischendurch gibt es auch runden bei den Paarung rein von der Verlierrunde zusammentreffen(haben kein gelbes Kasten unterhalb der Paarung). Der Automat gehört nur bedingt zur Paarung, er ist für den eigentlichen Ablauf nicht notwendig.(praktisch nur der Spielort wo das Spiel stattfindet) sollte aber bei der paarung dabei sein wegen der Übersichtlichkeit. Ich hoffe, dass du das nun verstanden hast, solltest du dennoch noch fragen haben, stehe ich dir gerne zur Verfügung. bis dann Dieter |
Re: Neue Komponente mit 2 TEdit Komponenten
Hallo optiplex,
soweit habe ich den Spielplan verstanden. Der Punkt ist, dass es Sinn macht ein allgemeines Zuordnungsprinzip, einen Algorithmus zu finden, mit dem man mathematisch das nächste Spiel ermitteln kann. Somit wird die procedure universell und gilt für alle Anzahlen von Spielern, egal ob 8, 16, 32 usw. Man muss halt nicht für jede Anzahl von Spielern eine eigene Zuordnungsprocedure schreiben. Das habe ich versucht, bin an der erwähnten Stelle gescheitert. Da hatte ich auch noch nicht begriffen, dass das KO-System auf eine Spieleranzahl zur Basis 8 definiert ist. Ich erkläre mal meine Vorgehensweise für den Teil den ich bis jetzt in den Griff bekommen haben (dabei war die Annahme, dass ausschließlich eine gerade Anzahl von Spielern notwendig ist). [edit] Sorry, hier noch mal was die Eigenschaften bei TPlayClass (und somit für den Parameter APlayClass) bedeuten.
Delphi-Quellcode:
Bsp.:
FGame: Char; // Spiel; R, V, G
FNumber: Integer; // Spielnummer 1..x FPlayer1: TPlayerClass; // Spieler 1 FPlayer2: TPlayerClass; // Spieler 2 FPlayList: TPlayList; // übergeordnete PlayList FPlayResult: TPlayResult; // Ergebnis des Spiels FRound: Integer; // Runde FStatus: TPlayState; // Spielstatus R1-2 R = Game 1 = Round 2 = Number - = nur optischer Trenner im String. [/edit] In der procedure wird der Spieler der einem neuen Spiel zugeordnet werden soll, das aktuell gespielte Spiel sowie ein Flag ob der übergebene Spieler der Gewinner der Partie war übergeben:
Delphi-Quellcode:
Als erstes prüfe ich, ob der Spieler der einem neuen Spiel zugeordnet werden soll ein Gewinner ist:
procedure TPlayList.ChangePlay(APlayClass: TPlayClass; AProperty : String);
Delphi-Quellcode:
Jetzt berücksichtige ich, welches Spiel das gerade gespielte Spiel war:
if winning then begin
// Spieler ist Gewinner des alten Spieles
Delphi-Quellcode:
Also, der Spieler hat in einer Runde 'R' (erste Spielrunde aller Spieler) gewonnen. Anhand des Spielplanes hab ich eine recht einfache logische Folge entdeckt. Der Gewinner einer Runde "R" geht wie folgt als Spieler in ein Spiel der Runde "G" :
case ORD(AOldPlay.FGame) of
ORD('R') : begin // Gewinner der ersten Runde Gewinner aus: R1 wird erster Spieler G1 R2 wird zweiter Spieler G1 R3 wird erster Spieler G2 R4 wird zweiter Spielr G2 .... und endlos so weiter. Daraus folgt, das der Gewinner aller ungeraden Spielnummern für "R" immer der erste Spieler des folgenden Gewinnerspieles wird und der Gewinner mit den geraden Spielnummern für "R" immer der zweite Spieler. Die Nummer des zu ermittelnden "G"-Spieles ist immer das Ergebnis die auf eine gerade Zahl aufgerundete Spielnummer von "R" geteilt durch 2. R-Nummer wird zur G-Nummer 1 -> 1 2 -> 1 3 -> 2 4 -> 2 ......... 17 -> 9 18 -> 9 19 -> 10 20 -> 10 ......... somit entstehr folgender Code:
Delphi-Quellcode:
Soweit so gut. Das lässt so einfach für alle Spieler ermitteln, die Gewinner einer Runde sind, Egal ob sie vorher einmal verloren haben. Ich denke unter dieser Erläuterung sollte der Code für die gewonnenen "G" und "V"-Spiele nachvollziehbar sein, auch wenn er einer etwas anderen Logik folgt.
if Odd(AOldPlay.Number) then begin // Gewinner einer ungeraden Spielrunde
NewPlay := GetPlay('G', 1, (AOldPlay.Number+1) div 2); // das Spiel "G1-x" holen, wobei x gleich (y+1)/2 aus R1-y für das ungerade Spiel ist if Assigned(NewPlay) then NewPlay.FPlayer1 := APlayer; // wird Spieler 1 end else begin // Gewinner einer geraden Spielrunde NewPlay := GetPlay('G', 1, (AOldPlay.Number) div 2); // // das Spiel "G1-x" holen, wobei x gleich y/2 aus R1-y für das gerade Spiel ist if Assigned(NewPlay) then NewPlay.FPlayer2 := APlayer; // wird Spieler 2 end; In den weiteren Spielrunden für "G" und "V" prüfe ich zusätzlich, ob wirklich ein neues Spiel gefunden wurde. Ist dies nämlich nicht der Fall, so ist der Gewinner ein Teilnehmer für das Finale. Da gibt es keinen Ausweg.
Delphi-Quellcode:
Dabei ist recht einfach zu erkennen, dass der Gewinner eines "G"-Spieles der erste Spieler des Finales und der Gewinner des "V"-Spieles der zweite Spieler des Finales wird (für mich ist das aber wurscht, es bleiben eh nur zwei übrig; wer das erster oder zweiter Spieler ist scheint wohl nur für die Profis wichtig zu sein).
if Assigned(NewPlay) then begin
NewPlay.FPlayer1 := APlayer; end else begin // das war das letzte Gewinnerspiel, ab ins Finale NewPlay := GetPlay('F', 0, 0); if Assigned(NewPlay) then NewPlay.Player1 := APlayer; end; Soweit war das alles recht logisch. Meine Probleme fangen mit den Verlierern der "G" und "V"-Spiele an. Für die Verlierer der R-Spiele ist das so einfach wie oben beschrieben bei den Gewinnern. Ich habe folgendes gesehen (für 16 Spieler) Verlierer des Spiels geht in Spiel: Gx-y -> Va-b G1-1 -> V2-4 2.Spieler G1-2 -> V2-3 2.Spieler G1-3 -> V2-2 2.Spieler G1-4 -> V2-1 2.Spieler G2-1 -> V4-2 2.Spieler G2-2 -> V4-1 2.Spieler G3-1 -> V6-1 2.Spieler da steckt auch ein System dahinter. Schau mal, ob du das umgesetzt bekommst. Das sollte rein mathematisch gehen. Ein Ansatz ist, dass für 16 Spieler 4 G1 Spiele existieren, wobei das Va-b Spiel a=x*2 und b=16/4-y-1 ist. x und y entnimmt man Gx-y. Jo, so klappts. Das ganze muss jetzt noch für die Verliererspiele ausgetüftelt werden. Läßt sich mit dem neu ermittelten Spielnamen kein Spiel in der Spielliste finden, so ist das Turnier für diesen Spieler zu ende. Die Berechnung der Platzierung ist bei mir noch falsch, aber dazu später. Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
Ähhm, das sieht jetzt aber nicht so schwer aus:
Delphi-Quellcode:
"V"-Spiele muss noch. Neu ist der Teil:
// Spieler ist Verlierer des alten Spieles
case ORD(AOldPlay.FGame) of ORD('R') : begin // Gewinner der ersten Runde if Odd(AOldPlay.Number) then begin NewPlay := GetPlay('V', 1, (AOldPlay.Number+1) div 2); if Assigned(NewPlay) then NewPlay.FPlayer1 := APlayer; end else begin NewPlay := GetPlay('V', 1, (AOldPlay.Number) div 2); if Assigned(NewPlay) then NewPlay.FPlayer2 := APlayer; end; end; ORD('G') : begin NewPlay := GetPlay('V', AOldPlay.Round*2, 16/4 - AOldPlay.Number - 1); if Assigned(NewPlay) then NewPlay.FPlayer2 := APlayer; end; ORD('V') : begin // das wars dann, kein weiteres Spiel :-( // oder doch? :-) hab ich das System aber auch noch nicht durchschaut end; end;
Delphi-Quellcode:
umgesetzt wie vorher beschrieben. Sieht irgendwie zu einfach aus. :gruebel:
ORD('G') : begin
NewPlay := GetPlay('V', AOldPlay.Round*2, 16/4 - AOldPlay.Number - 1); if Assigned(NewPlay) then NewPlay.FPlayer2 := APlayer; end; Hab ich da was verpennt? Gruß oki |
Re: Neue Komponente mit 2 TEdit Komponenten
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Olaf,
das mit der Basis 8 ist falsch, damit ein Spielplan aufgeht egal ob Doppel KO oder einfach KO benötigst du immer 2,4,8,16,32,64,128 usw spieler sonst geht das Turnier einfach nicht auf. deshaslb musst du fehlende Spieler durch Freilose ersetzen und diese Freilose must du an bestimmten stellen einsetzen damit jedes Freilos Spätestens in der 2 Verliererrund ausscheidet. Siehe Beispiel (Anhang Plan.jpg) dies sind 12 Parungen=24 Spieler und das Turnier geht nicht auf. Ist ein Spieler in der Verliererrunde und verliert dann ist er ausgeschieden hier kommt nur der Gewinner weiter. Ich habe dir gleich gesagt, dass es so einfach nicht ist, wie du dir das am Anfang vorgestellt hast. Gruss Dieter |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:44 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