Dakuan
Anmeldungsdatum: 2. November 2004
Beiträge: 6518
Wohnort: Hamburg
|
Also ich programmiere ja schon set einigen Tagen vorwiegend in C und durch PHP und Javascript hatte ich auch schon etwas Kontakt mit OOP. Aber ich glaube nicht, das dies mein Punktekonto wesentlich beeinfusst. Jetzt benötige ich aber dringend eine GUI und habe mich da mit einigen beschäftigt:
GTK* erscheint mir zu schwerfällig und monströs LessTif kann kein UTF-8 SDL funktioniert gut, aber dazu muss ich alle Menüs und Buttons etc. selber basteln wxWidgets soll zwar eine C Schnittstelle haben, jedoch konnte ich dazu nichts konkretes finden.
Daher ist meine Wahl auf FLTK gefallen. Das bedeutet jedoch, das ich mich jetzt doch in C++ einarbeiten muss. Das ist jetzt mein dritter Anlauf dazu und nach einer Woche gibt es auch schon die ersten Erfolgserlebnisse. Also ich versuche gerade sowas wie einen Go Trainer zu programmieren. Die vorhandene unfertige html/Javascript Version hat bereits Grundfunktion und soll nun in C++ umgesetzt werden, damit die Auswahl einer zu lösenden Aufgabe auch ohne Server möglich ist. Das Programm soll kein Ersatz für bereits bestehende Programme sein sondern nur das Nachstellen von Aufgaben aus Büchern erleichtern, damit man nicht immer die Steine auf dem Brett neu hinlegen muss. Es geht also eigentlich nur um die grafische Darstellung der Situation. Erschwert wird die Aufgabe allerdings dadurch, da die Gültigkeit der Züge zusätzlich geprüft werden sollte. Bei meiner ersten Übung bedeutet das, dass die Zentrale Class "BoardWindow" im Endzustand etwa 20 Methoden enthalten wird und damit ca. 2/3 des gesamten Codes belegt. Ist das mormal oder akzeptabel? Oder deutet das eher auf einen Designfehler hin? Bisher habe ich ja hauptsächlich in C programmiert und da auch viel mit globalen Daten und Strukturen gearbeitet. Wenn ich das Ganze jetzt nur in eine "class" kapsele funktioniert das zwar, aber ist das so auch im Sinne des Erfinders und vor allem auch effektiv? Also ich betrachte das jetzt erstmal nur als Programmierübung, denn für mein Hauptprojekt, einen Morsedecoder, möchte ich die Ergebnisse einer Fourieranalyse irgendwannmal in Echtzeit darzustellen. Das ist auch der Grund, weswegen ich mich von GTK* verabschiedet habe (Gnome habe ich nur als Resourcenbremse erlebt). Was meint Ihr dazu?
|
TraumFlug
Anmeldungsdatum: 16. Juli 2009
Beiträge: 999
Wohnort: zwischen den ohren
|
"Design" an sich ist Geschmacks- und Konventionsfrage. Mit C++ kannst du wunderbar c99-elemente mit c++ Features allerbuntestens mischen, und meistens funktioniert's sogar. Ich selbst bin der Ansicht, solange es gut strukturiert ist, schadet in kleinen Teilen C-typisches Vorgehen bei C++ Projekten manchmal weniger als unnötig abstrahierter und verkapselter C++ Purismus. Man sollte sich natürlich schon ein bisschen mit den gängigen Ansichten darüber, wie so ein Programm aufgebaut sein sollte beschäftigen. Gute Strukturierung ist auch ein Thema, über das ich grade ein bisschen nachdenke, bevor ich mich mal in ein anständiges Projekt stürzen will. Solche Strukturierung besteht im einfachsten Fall darin, logische Einheiten und Funktionsgruppen gut und sinnvoll aufzutrennen. Im Falle "Go" etwa einen Kernklasse/Datenstruktur, die rein einen Spielstatus/zustand darstellt, und mit Methoden gekoppelt ist, die diesen manipulieren, prüfen (auf Gültigkeit, oder die Punktezahlen, oder ob das Spiel vorbei ist), vergleichen (etwa mit einem anderen Objekt gleicher Klasse) und dergleichen. Erstmal nichts weiter, keine Darstellung und nichts. Wenn deine Klasse "BoardWindow" jetzt quasi den gesammten Code innehat, auch die Darstellung und die Nutzereingabe, dann ist meiner Meinung nach nicht genug differenziert, wo es wirklich Sinn geben würde: es böte sich etwa an, eine Klasse zu erschaffen, die über deine GUI aus dem Spielzustand die Grafik darstellt, und eventuell Nutzereingaben verarbeitet und den Spielzustand über dessen Methoden manipuliert. Der Spielzustand wäre dann eine eigenständige Dateneinheit (die man auch nicht weiter teilen könnte, höchstens AI und solche Geschichten auslagern), und andere Dateneinheiten haben so einen als Dateneinheit in sich (pointer oder fest), oder bekommen ihn überhaupt von Anderer Logik her zugeliefert, um Aktionen damit durchzuführen. So liesse sich dann etwa ein Programm schneller modifizieren um mehrere Spielstände gleichzeitig zu verwalten, oder eine verlustfreie Historie aufzubauen (etwa ein wachsender Vektor an Spielständen), der Spielstand mitsammt seinen Methoden könnte ohne viel Veränderung in einer weiteren klasse genutzt werden, um eine künstliche Intelligenz aufzubauen, ... Ich sehe das meist so umgesetzt, dass das fertige Hauptprogramm (in main) eigentlich nur sehr klein ist, und die entsprechenden "Module" zusammenführt, und die Startarbeit und das initialisieren der Biblotheken anstösst. Das gibt dann durchaus "Baumstrukturen", wenn ein Modul sich wiederum aus der Interaktion anderer zusammensetzt, die im Zweifel schon für diesen Zweck entworfen wurden. Die Aufbaufähigkeit ist, was bei richtiger Anwendung C++ so attraktiv macht - dabei wird bei vielen fortgeschritteneren C Programmen durchaus ähnliches praktiziert, auch wenn es dort oft nicht so übersichtlich ist, und Erweiterungen manchmal mehr Anpassungsarbeit am ganzen bedeuten. Früher bestanden viele Programme oft nur aus einem Block ohne wirkliche Unterfunktionen, so komplexe Software wie heutzutage üblich hätte man so nie Programmieren können - daraus entwickelten sich dann die objektorientierten und modularisierten Modelle. Die man freilich nicht unbedingt zwingend anwenden muss, aber doch ihre Vorteile haben. Zumal so ein "Teilen und Herrschen" grade in der Teamarbeit den Vorteil hat, dass sich einzelne um fest abgesteckte Komponenten kümmern können, und so mehr gleichzeitiges Arbeiten entsteht.
|
barcc
Anmeldungsdatum: 13. Juli 2007
Beiträge: 696
Wohnort: Dortmund
|
Hallo, was TraumFlug schreibt ist natürlich alles richtig, vielleicht noch ein paar Anmerkungen 20 Methoden in einer Klasse ist zunächst kein Problem, viel wichtiger sind andere Parameter,
Dateigröße: 500-1000 Zeilen als Richtwert Wie lang sind die einzelnen Methoden, eine Funktion sollte normalerweise so kurz sein, dass sie auf dem Bildschirm vollständig sichtbar ist. Welcher Art sind die Methoden? In BoardWindow würde man normalerweise nur die Eventhandler des Toolkits erwarten, wenn der Code nicht sehr komplex ist, kannst du aber auch die Logik dort hineinpacken. (siehe oben und TraumFlug) Das wichtigste ist immer, dass das Programm funktioniert. Designregeln sind kein Selbstzweck.
Keine Ahnung, warum du GTK schwerfällig und monströs findest. Das Toolkit ist in den seltesten Fällen das Problem. Ich bezweifle dass FLTK merklich schneller ist, und "light" heißt in dem Zusammenhang nur "kann weniger".
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6518
Wohnort: Hamburg
|
Wenn deine Klasse "BoardWindow" jetzt quasi den gesammten Code innehat, auch die Darstellung und die Nutzereingabe, dann ist meiner Meinung nach nicht genug differenziert, ...
Ganz so schlimm ist es zwar nicht, aber die Tendenz ist schon erkennbar. Und hier zeigt sich wohl auch, das ich nach einer Woche und ca. 100 Buchseiten noch nicht wirklich in der OOP Welt angekommen bin. Mittelpunkt des ganzen Programms ist eigentlich eine Statusmatrix, aus der alles abgeleitet wird. Ich habe auch erst seit gestern eine schwache Vorstellung davon, wie ich diese übergeben kann.
...um mehrere Spielstände gleichzeitig zu verwalten, oder eine verlustfreie Historie aufzubauen...
Also eine Historie aufzubauen, habe ich mir abgeschminkt (dazu müste ich wohl SGF implementieren. Ich begnüge mich daher damit einen Spielstand einzufrieren, indem die komplette Matrix gespeichert wird.
Momentan liege ich bei 510 ☺
Da ist alles vertreten, von einer Zeile bis zu lang. Zwei der Methoden sich ziemlich lang (70 Zl.) und lassen sich auch nicht wirklich gut aufteilen. Es handelt sich dabei um eine Breitensuche in der Matrix (testen der Nachbarfelder, verfolgen von Steinketten usw.).
Keine Ahnung, warum du GTK schwerfällig und monströs findest.
Na ja, ich habe immer wieder mit Probleme mit der Spantanität von Nautilus. Von daher habe ich vielleicht etwas übertrieben. Aber als ich dann gelesen hatte das GDK out ist und man Cairo benutzen soll, habe ich eben etwas anderes gesucht. Ich hatte ja schon erwähnt, das ich für mein anderes Projekt eine einfache Möglichkeit brauche, farbige Pixel in Echtzeit darzustellen.
Ich bezweifle dass FLTK merklich schneller ist, und "light" heißt in dem Zusammenhang nur "kann weniger".
Ob das nun wirklich schneller ist, ist eigentlich egal, es muss nur schnell genug sein und der Aufwand sollte sich in Grenzen halten. Auf FLTK bin ich auch deshalb gestoßen, weil es sowas schon bei einem anderen Programm gesehen habe.
|
wirdbald
Anmeldungsdatum: 2. Januar 2009
Beiträge: 57
|
Hallo Dakuan, dieses Getue von einer eigenen "OOP Welt", was man überall liest, finde ich etwas übertrieben. Lass Dich davon nicht aus der Ruhe bringen. Versuche nur nicht zu viel C ins C++ zu schmuggeln, sondern benutze die C++-Standardbibliothek. Und denke Dir einfach schöne Klassen aus, die Dir logisch erscheinen. Es gibt ganz viele Wege zum Ziel! Wenn man Algorithmen einbaut, hat man eigentlich automatisch längere Methoden/Funktionen. Die kann man zwar manchmal aufteilen, aber wenn man die Unterfunktionen dann an keiner anderen Stelle braucht, ist das von ziemlich begrenztem Nutzen. Ich kann mich allerdings meinem Vorredner nur anschließen: Versuche GUI und Logik soweit möglich zu trennen. Idealerweise sieht das so aus, dass Du einfach alternative GUIs dranstricken könntest, die eigentlichen Spielfunktionen aber dabei nur einmal implementiert hast. Diese Trennung ist allerdings manchmal etwas umständlich zu realisieren und für Kleinstprojekte kann man schon auch mal einiges in die GUI-Methoden reinschreiben. Wenn es auch nicht so schön ist. Diese Statusmatrix sollte wohl auf jeden Fall in einer Klasse gekapselt werden. Da gehören dann lauter Abfrage-Methoden dazu wie z.B. vielleicht "istAufDemFeldEinStein()". Um eine Historie zu speichern, müsstest Du nur die Züge speichern oder alternativ, die Bewegungen, mit denen man sie rückgängig machen kann.
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6518
Wohnort: Hamburg
|
aber wenn man die Unterfunktionen dann an keiner anderen Stelle braucht, ist das von ziemlich begrenztem Nutzen.
Genau so habe ich das auch gesehen.
"istAufDemFeldEinStein()"
Ein wenig komplizierter ist das schon. Sowas ist eigentlich nur ein Kriterium für einen vorzeitigen Abbruch der Prüfungen. Eigentlich muss geprüft werden ob mindestens eines der Nachbarfelder auch frei ist und falls das nicht der Fall ist, ob durch den Zug Steine gefangen genommen werden, wodurch dann mindestens ein freies Feld entstehen könnte. Das ist ein mehrstufiger Vorgang bei dem auch intensiver Gebrauch von meinem speziellen Fifo Objekt gemacht wird. Zu meiner Überraschung läuft das sogar in der Javascript Version ohne merkbare Verzögerung.
Diese Statusmatrix sollte wohl auf jeden Fall in einer Klasse gekapselt werden.
Das sehe ich jetzt auch so. Aber ich muss noch herausfinden, wie ich die dann an das Ausgabefenster übergeben kann. Jedenfalls muss ich das Ganze wohl erstmal mitsamt den Zustandsvariablen zu einer neuen Struktur zusammenfassen.
Um eine Historie zu speichern, müsstest Du nur die Züge speichern...
Ok, wenn ich die ggf. dadurch gefangenen Steine dazunehme, könnte es gehen. Aber bevor ich da rangehe muss ich erstmal die grundlegenden Probleme lösen.
Und denke Dir einfach schöne Klassen aus,...
Da hast Du was gesagt. Ich habe oft Probleme mir brauchbare Namen auszudenken, zumal ich etwas tipfaul bin.
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6518
Wohnort: Hamburg
|
Jetzt stehe ich komplett auf dem Schlauch. Ich habe versucht eure Ratschläge umzusetzen und das Ausgabefenster von der Spielelogik zu trennen. Aber das führt zu einer Flut von Fehlermeldungen mit denen ich nichts anfangen kann. Ich kann nur vermuten, das es irgendwie damit zusammenhängt, das alle auf die Zustandsmatrix zugreifen müssen. Die ersten der gefühlten 1000 Fehlermeldungen lauten:
go01.cpp:200: Fehler: Definition des implizit deklarierten »GoBoard::GoBoard()«
go01.cpp:200: Fehler: Deklaration von »GoBoard::GoBoard()« wirft andere Ausnahmen
go01.cpp:102: Fehler: von vorheriger Deklaration »GoBoard::GoBoard() throw ()«
...
und hier der Quelltext dazu:
typedef struct {
int bsize;
int bstatus;
int mxstatus[ 19 * 19 ]; // maximum board size
} BoardStatus;
class BoardWindow : public Fl_Double_Window { // Fensterklasse von FLTK mit doppelter Pufferung
public:
BoardWindow( int W, int H, const char *L = 0 );
~BoardWindow();
void draw();
int handle( int event );
};
class GoBoard { // <<<< Zeile 102
public:
BoardStatus * bmx;
void init();
int is_valid( int x, int y, int color );
int get_liberties( int x, int y, int color, int no_clear );
void clean_status( int mode );
int chk_capture( int x, int y, int my_color );
};
...
und hier Zeile 200:
// Create the Go matrix
GoBoard::GoBoard() {
bmx = new BoardStatus;
bmx->bsize = 19;
bmx->bstatus = 1; // first stone is black
init();
}
Irgendwelche Tipps?
|
wirdbald
Anmeldungsdatum: 2. Januar 2009
Beiträge: 57
|
So etwas wird dir noch 1000 mal passieren, wenn du mit C++ unterwegs bist. Da musst du Fehlermeldungen lesen und auch mal nachgooglen falls nötig. Du hast im 2. Codeschnipsel einen Standarkonstruktor für deine Klasse definiert. Den musst du wie die anderen Methoden auch vorher deklarieren. Dann klappts auch mit dem bösen Compiler 😉
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17625
Wohnort: Berlin
|
Wenn man Algorithmen einbaut, hat man eigentlich automatisch längere Methoden/Funktionen. Die kann man zwar manchmal aufteilen, aber wenn man die Unterfunktionen dann an keiner anderen Stelle braucht, ist das von ziemlich begrenztem Nutzen.
Das ist von großem Nutzen, denn wenn man eine Teilfunktion auslagern und separat verstehen kann, dann kann man erstens die beiden Teile leichter verstehen, zweitens kann man dann die ausgelagerte Funktion separat testen. Drittens sind die Chancen hoch, dass in der äußeren Funktion Variablen vorkommen, die die innere nicht zu kennen braucht und die innere welche kennt, die die äußere nicht kennen muss. Hält man diese also getrennt verringert sich das Fehlerrisiko und die Komplexität wird vermindert. Viertens ist der Name einer Methode eine automatische Form von Dokumentation. Fünftens bieten manche Sprachen - ich weiß nicht ob C++ inzwischen auch - die Möglichkeit teilautomatisierter Kommentare, so dass man ermuntert wird jede Methode zu dokumentieren. Viele kleine Methoden würden hier also die Tendenz zu mehr Kommentaren erhöhen.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17625
Wohnort: Berlin
|
| bmx->bstatus = 1; // first stone is black
|
Schritt eins:
| const int black = 1;
// ...
bmx->bstatus = black;
|
Schritt zwei wäre wohl
| enum color {white, black};
// ...
bmx->bstatus = black;
|
aber ich bin aus C++ raus - womöglich muss man das anders machen. Auch ist die Frage wo man color definiert - nicht unbedingt in der gl. Klasse.
|
wirdbald
Anmeldungsdatum: 2. Januar 2009
Beiträge: 57
|
Also, heute morgen (mit echter Tastatur, nicht mit Handy) noch mal richtig. Der Fehler sollte verschwinden, wenn die Klassendeklaration wie folgt aussieht:
class GoBoard {
public:
BoardStatus * bmx;
BoardStatus(); // <- Standardkonstruktor deklariert
void init();
int is_valid( int x, int y, int color );
int get_liberties( int x, int y, int color, int no_clear );
void clean_status( int mode );
int chk_capture( int x, int y, int my_color );
};
Wenn Du keinen Konstruktor deklarierst erzeugt C++ automatisch den Standardkonstruktor. Und der beißt sich bei Dir mit der eigenen Definition. Ich für meinen Geschmack würde jetzt das struct BoardStatus direkt in die Klasse GoBoard kapseln. Und zwar private oder protected:
class GoBoard {
public:
BoardStatus();
...
private:
BoardStatus * bmx;
}; Oder wahrscheinlich noch zweckmäßiger direkt (Und hier mit einigen Möglichkeiten aus C++11, keine Ahnung ob Du das nutzt):
// In .h-Datei:
class GoBoard {
public:
enum class StoneColor {none=0, black, white};
BoardStatus();
...
private:
const int bsize = 19; // Direkt definieren geht aber glaub ich erst in C++11, sonst in .cpp-Datei definieren
StoneColor nextStone; // Ist das der Sinn von bstatus?
std::array<StoneColor,19*19> fieldStatus; // Auch std::array ist C++11, sonst std::vector oder ein C-Array
};
// In .cpp-Datei:
GoBoard::GoBoard() :
nextStone(StoneColor::black),
fieldStatus()
{
fieldStatus.fill(StoneColor::none); //Glaube ich unnötig. Müsste der Standardkonstruktor machen. Ausprobieren...
...
} In jedem Fall ist es guter Stil, den Status des Feldes so in der Klasse zu kapseln, dass er nicht aus anderen Teilen des Codes verändert werden kann, ohne dafür die expliziten Methoden Deiner Klasse GoBoard zu benutzen. Das macht das Programm wesentlich zuverlässiger und übersichtlicher weil Du Fehler durch komplizierte Wechselwirkungen vermeidest.
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6518
Wohnort: Hamburg
|
Danke erstmal für die ausführlichen Hinweise. Ich hoffe das ich die bis heute Abend abgearbeitet habe (wenn meine Leute mich lassen). Aber der erste Tipp geht schonmal nicht, denn BoardStatus ist ein Datentyp und keine Methode.
Ich für meinen Geschmack würde jetzt das struct BoardStatus direkt in die Klasse GoBoard kapseln. Und zwar private oder protected:
Genau das würde mein Problem wohl noch verschärfen, denn die Zustandsmatrix wird einmal benötigt um die Steine anzuzeigen und auch für diverse Suchvorgänge, bei denen dann auch temporäre Informationen darin abgelegt werden. Das Ausgabefenster muss also lesenden Zugriff haben. Drittens sind die Chancen hoch, dass in der äußeren Funktion Variablen vorkommen, die die innere nicht zu kennen braucht und die innere welche kennt, die die äußere nicht kennen muss.
Das ist normalerweise richtig. Aber in diesem Fall werden ständig alle Werte benötigt und wenn man einer ausgelagerten Funktion so viele Parameter übergeben muss, das die Zeile zu lang würde, sehe ich darin auch keinen Vorteil. Das schließt aber nicht aus, das dies bei einem optimierten Suchalgorithmus nicht doch möglich ist. Bei den Problemen die ich momentan habe sieht es aber nicht so das ich mich bald darum kümmern kann.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17625
Wohnort: Berlin
|
Aber in diesem Fall werden ständig alle Werte benötigt und wenn man einer ausgelagerten Funktion so viele Parameter übergeben muss, das die Zeile zu lang würde, sehe ich darin auch keinen Vorteil.
Viele Parameter übergeben zu müssen riecht nach mangelnder Objektorientierung. | public int umfang (int x0, int y0, int x1, int y1) ...
public int umfang (rechteck r) ...
|
Wenn die Paramter auch noch für beide Funktionen die gleichen Werte haben ist womöglich eine neue Klasse mit diesen Paramtern als Attributen, und der Funktion als Methode angeraten:
| int umfang = rechteck.umfang ();
|
|
wirdbald
Anmeldungsdatum: 2. Januar 2009
Beiträge: 57
|
Dakuan schrieb: Aber der erste Tipp geht schonmal nicht, denn BoardStatus ist ein Datentyp und keine Methode.
Also, das ist wohl ein Missverständnis. Selbstverständlich kannst Du Daten in Klassen kapseln. Dafür sind sie ja da. Eine Klasse verwaltet Daten und stellt Methoden zur Verfügung, um diese Daten auszulesen und zu verändern. Mein Quellcode oben müsste so eigentlich kompilieren. Auch Dein zweites Argument, dass die GUI den Status des Bretts auslesen muss, passt nicht. Denn genau das ist ja das klassische Beispiel. Die GUI bekommt zum Beispiel einen Pointer auf so ein Brett-Objekt und fragt dieses dann einfach bei jedem Rendern, auf welchen Feldern welche Steine drauf sind. Die Methodik musst Du natürlich in die Klasse einbauen. Wie Du jetzt hypothetische Spielzüge usw. in Deinen Algorithmen speicherst, da müsste ich auch einen Moment drüber nachdenken. Aber das befreit Dich nicht davon, entsprechende Klassen zu schreiben. Davon mal abgesehen ist unter C++ ein struct (was Du jetzt verwendest) das gleiche wie eine Klasse. Einziger Unterschied: Alle Elemente sind per default public. Und das ist eben schlechter Stil. (Es sei denn, Du möchtest keine Objektorientierung, sondern rein prozedural programmieren. Das kannst Du natürlich halten, wie Du willst. Aber Du hast ja explizit danach gefragt, wie man das ganze objektorientiert macht.)
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6518
Wohnort: Hamburg
|
So, ich glaube ich habe wieder in die Spur zurückgefunden. In meiner Verzweiflung habe ich angefangen die Matrix als globale Struktur anzulegen. Dabei habe ich dann entdeckt, dass mir vom GoBoard der Konstruktor abhanden gekommen ist 😳
Wenn Du keinen Konstruktor deklarierst erzeugt C++ automatisch den Standardkonstruktor. Und der beißt sich bei Dir mit der eigenen Definition.
Jetzt habe ich diesen Hinweis verstanden. Hättest Du ruhig drastischer formulieren können. Jetzt habe ich 2 Versionen. Komischerweise ist die Version mit der globalen Matrix etwas kürzer.
Ich für meinen Geschmack würde jetzt das struct BoardStatus direkt in die Klasse GoBoard kapseln. Und zwar private oder protected:
Momentan ist das noch "public:". Ich könnte natürlich das BoardWindow zum Freund vom GoBoard machen ... Oder alternativ bei jeder Änderung eine entsprechende Funktion von BoardWindow aufrufen, wodurch dann auf recht umständliche Weise eine Kopie der Matrix entstehen würde. private:
const int bsize = 19; // Direkt definieren geht aber glaub ich erst in C++11, sonst in .cpp-Datei definieren
StoneColor nextStone; // Ist das der Sinn von bstatus?
...
Das war tatsächlich der ursprüngliche Sinn. Wird jetzt aber auch für setup(black/white) benutzt damit der Eventhandler entsprechend reagieren kann.
Viele Parameter übergeben zu müssen riecht nach mangelnder Objektorientierung.
Möglicherweise hast du recht. Aber ich habe wohl auch etwas übertrieben. Tatsächlich stinkt mir diese lange Methode, zumal ich davon 3 Stück habe, die auch sehr ähnlich arbeiten. Aber ich fand die vielen Bedingungsabfragen bei einer Zusammenfassung noch viel schlimmer. Ich sagte ja bereits, das ich momentan dabei bin den Code von Javascript nach C++ zu portieren und bei Javascript hatte ich natürlich auch die Laufzeit im Hinterkopf. Erst wenn ich alle 3 Funktionen lauffähig portiert habe, möchte ich da nochmal bei gehen. Das Schema ist etwa so:
int GoBoard::get_liberties( int x, int y, int color ) {
...
// Vorlauf ausgelassen
ix = fifo.out();
while( ix >= 0 ) {
y = ix / bmx->bsize;
x = ix % bmx->bsize;
if( x > 0 ) {
tmp = bmx->mxstatus[ix - 1];
bmx->mxstatus[ix - 1] |= MX_VISITED;
if( tmp == 0 ){
liberties++;
}else{
if( tmp == gesucht_wird ){
fifo.in( ix - 1 );
}
}
}
if( x < (bmx->bsize - 1) ) {
// insgesamt 4 mal fuer die Nachbarfelder mit entsprechend veraenderten Koordinaten
...
}
ix = fifo.out();
}
// Nachlauf ausgelassen
return liberties;
}
Da ist sicherlich noch Luft drinne, aber ich will das jetzt erstmal auf den gleiche Funktionsstand bringen wie die html/Javascript Version.
|