Parse-Codestyle verbessern?
-
Hi,
hustbaer:
Dir ist schon klar dass du hier etwas parst was gar nicht Maschinenlesbar sein soll?
Ich meine den ganzen Online-Casinos liegt nun wirklich nichts daran das Bot-Programmieren zu vereinfachen.Es ist richtig, dass Bots unerwünscht sind. Das Einlesen der HHs zur Statistik-Führung und Vor/Nach-Analyse von Händen (und dafür ist meine Software gedacht) ist jedoch ausdrücklich erlaubt (siehe z.B. die Liste erlaubter Software von PokerStars, von denen sehr viele Programme HHs lesen, was PokerStars auch bewusst ist: http://www.pokerstars.eu/de/poker/room/prohibited/)
Was die Identifizierung angeht, wieso kannst du die nur über Zeile 1 machen?
Weil die auch nicht genormt ist und unterschiedliche Seiten mit unterschiedlichem Format dennoch oft dieselbe erste Zeile haben. Sonst wäre es auf einen Schlag einfacher.

Einen Parser für alle Formate, der nur lokal pro Zeile versucht rauszubekommen wie er diese zu deuten hat, halte ich auf jeden Fall für nicht sehr vielversprechend.
Vielleicht resultiert genau daraus die Unsauberkeit. Aber auf diese Weise kann ich eben auch fremde Formate möglicherweise importieren, da die sich manchmal eben doch (nicht unbedingt oft) an HHs anderer Seiten orientieren. Wenn ich aber eben keinen eindeutigen Anhaltspunkt auf die Deutung habe bzw. mir Zeile1/2/3 zeigt, dass die nachfolgenden Zeilen einem entsprechenden Format folgen, dann ist es schwierig...
GorbGorb:
Ja, könnte ich. Aber inwiefern würde mir das jetzt helfen?Also das Hauptproblem sehe ich weniger in fehlerhaftem Einlesen, da ich nach jeder Erweiterung alle bisherigen Formate prüfe und die auch immer funktionieren. Auch die Performance ist eigentlich egal. Ich frage mich nur, ob es einen Weg gibt den Code sauberer zu strukturieren ohne so ein Riesenmonster zu erzeugen, das voller Verzweigungen ist. Vielleicht bietet sich hier wirklich ein Parserframework an?
Ich glaube, Regexe lösen mein Problem auch nicht. Ich habe die simuliert (ist halt so gewachsen), indem ich Funktionen/Manipulatoren wie
readAnyCharOf<...>eingeführt habe, ich glaube, da bringt ein Regex gar nicht so viel mehr. Unwinding klappt auch, aber eben nur simuliert mit unget()s im Manipulator (zumindest je Zeile, zeilenübergreifend arbeite ich eher mit Kopien)... vielleicht statt Streams mit Manipulatoren etwas eher dafür Angemessenes nehmen? Ja, das sind Regexe vermutlich schon irgendwie. Erscheint mir aber immer noch mäßig viel Gewinn.Vielleicht suche ich auch nach einer Sauberkeit, die es hier wegen der Chaotik gar nicht geben kann. Präprozessoren habe ich nun quasi zwei, wobei einer nur bei Bedarf eingesetzt wird. Vielleicht muss ich mich auch einfach damit zufrieden geben, was ich gerade habe...? Immerhin läuft's. Aber der "Hassliste in fremden Code"-Thread gab mir auch zu denken (große Datei, große Funktionen, weil viele Verzweigungen...).
-
...
-
Wie gesagt, das setzt voraus, dass ich überhaupt erst die Formate kenne.

Gut, ich könnte einfach alle ausprobieren und somit den Identifizierungsprozess auf der Strecke liegen lassen. Wer keine Exception wirft, gewinnt!
-
Kannst du nicht anhand der Programmversion das HH Format bestimmen?
Das sollte wesentlich zuverlässiger funktionieren, als erst beim Parsen zu versuchen, das Format zu erkennen.
Dann würde ich für jeden unterstützten Client/Version einen Parser bauen, der das Format der jeweiligen Version lesen kann. Wenn sich am HH Format nichts ändert kann der HH Reader natürlich auch HH Dateien anderer Programmversionen lesen.
-
Version welchen Programms denn? Des Programms, das die Datenbank verwaltet? Das ist halt nur eine Determinante und ich weiß gar nicht, welchen Einfluss die auf das HH-Format hat.
Oder meinst Du, dass der Benutzer selbst auswählen soll, von welcher Seite die HH kommt? Das wäre zwar möglich, jedoch bietet andere Software eben an es ohne diese Abfrage zu tun, d.h. hier hätte man etwas Komfortverlust. Wobei der verschmerzbar sein sollte, das ist richtig. Nur wäre es ggü. dem, was jetzt gerade ist, ein Rückschritt. Den Code sauberer zu machen, indem ich den Benutzer-Komfort senke, halte ich für ein teures Geschäft.
-
OK, dann halt andersherum:
Deine HH Reader Software liest ja bestimmte Verzeichnisse aus. Bei den HH Readern, die ich kenne, erkennt die Software automatisch den Pfad und weiß, zu welcher Pokersoftware dieser Pfad gehört. Wenn sie weiß, zu welcher Pokersoftware der Pfad gehört, dann kann sie auch die Versionsinformationen der Pokersoftware auslesen.
Abhängig davon kann dann das HH Reader Objekt erzeugt und benutzt werden.
Es gibt allerdings einige Fallstricke (gemischte Formate in einem Verzeichnis nach einem Update der Pokersoftware, doofer Benutzer hat mehrere Pokerclients und schreibt alle HH in ein Verzeichnis, etc. )
-
Achso. Ne, bei mir kommt der Input eh aus der Zwischenablage, welcher wiederum aus der jeweiligen Datenbank-HH-Verwaltungssoftware kommt (nutzt nämlich eh jeder). Da habe ich also leider kein Verzeichnis. Und die Fallstricke sind in der Tat verheerend, zumal die HHs selbst oft aus den Verzeichnissen der Seiten gelöscht werden (oder verschoben), wenn besagte DB-HH-Verwaltungssoftware sie importiert hat. Dann landen sie in einem Sammelverzeichnis, aus dem es kein Entkommen mehr gibt.

-
Was genau hast du eigentlich vor? Wenn die HH aus einer db kommt, vielleicht stehen in der db ja verwertbare Daten, die du benutzen kannst?
-
Das wäre eine Idee, wenn die DB genormt wäre, aber auch hier gibt es unterschiedliche Formate. Es gibt zwei große Softwares, die zum Verwalten der HHs (und vielem mehr) verwendet werden: PokerTracker (4) und Holdem Manager (2). Jedoch gibt es ein paar kleinere. Und die Datenbanken ändern sporadisch auch Mal ihr Format, daher werde ich wohl nicht viel weniger Arbeit haben (darüber hinaus hat ein Überfliegen vor kurzem ergeben, dass die Daten soo verwertbar nicht sind, aber kann gut sein, dass ich etwas übersehen habe).
Mein Vorhaben mit den Daten ist, dass man unter Eingabe der fehlenden Daten (Vermutung darüber, welche Hände der jeweilige Gegner genau spielt) für jede Aktion den Erwartungswert berechnen kann. Funktioniert ja auch hervorragend.
-
Du könntest dir mal monadisches Parsen anschauen. Damit kannst du zumindest deinen Code übersichtlicher und leichter erweiterbar gestalten. Soweit ich weiß gibt es allerdings keine Bibliotheken im C++ Bereich. Google: c++ monadic parsing liefert eine paar grundlegende Infos und Ansätze.
Eisflamme schrieb:
Mögliche Abweichungen:
- die ersten zwei Zeilen können fehlen oder komplett anders aussehen
- Statt "Seat 1: Spielername" kann da auch einfach "Spielername" stehen
- diverse Leerzeichen zwischen ( und $ sind möglich
- statt $ kann überall auch ein anderes Währungszeichen stehen; es kann auch fehlen
- Die Blind-Postings-Lines können fehlen; es werden dann dennoch Blinds geposted, das aber nicht angezeigt (sind Pflichtbeträge)
- "*** HOLE CARDS ***" und die anderen haben ein paar Alternativbezeichnungen je nach Seite
- "Dealt to Player1" kann anders heißen oder fehlen, kommt manchmal auch in der "*** HOLE CARDS ***"-Zeile vor
- Die Aktionen, die hier immer die Form "Spieler: does" hat kann ohne Doppelpunkt dargestellt werden
- Die Aktionen (siehe
können bei raise ohne das "$x to $y" erfolgen, also einfach nur "to $y" - Die Aktionen können statt in unterschiedlichen Zeilen auch in einer Zeile mit Komma separiert vorkommen; auch dann mit und ohne Doppelpunkt
- Auch die Karten von "*** FLOP ***" können statt in eckigen Klammern ohne eckige Klammern dort stehen
- Es können 2-10 Spieler mitspielen
- Das Spiel kann vorbei sein, bevor Flop/Turn/River überhaupt erscheinen
- Spielernamen können eine große Menge an Sonderzeichen enthalten; welche hängt vom Seitenbetreiber ab
Im Prinzip behandelst du die ganzen Eventualitäten, indem du alle Möglichkeiten durch kleine Parser implementierst und einen Parser für einen "Block" dann durch entsprechende Kombinatoren zusammensetzt.
grobes Beispiel für Geldbeträge:
parseBetrag = (parseWährungszeichen && parseWert) || parseWertWenn du dich ein wenig mit Haskell auskennst, kann du dir ja mal Parsec anschauen.
-
Vorallem kann so ein Lexer durchaus Sinn machen. Ob da dann nämlich USD, $ oder was auch immer steht, ist dann egal, weil der Scanner alles in ein Token überführt.
-
wyfrn:
Das klingt ganz interessant! Wenn es wiederum keine C++-Bibliothek gibt, nützt mir das nicht so viel. Andere Sprachen mit einzuflechten erscheint mir (zurzeit) noch sehr aufwendig.jhkhjkhjk:
Aber wenn USD im Namen vorkommt, habe ich schon verloren, oder? Ein Name wie USD12315 ist nämlich nicht verboten. Und je nach Seite kann es auch sein, dass EUR (hab das Zeichen gerade nicht) als Spielwährung gar nicht angezeigt wird und daher im Nickname erlaubt ist oder so.
-
Eisflamme schrieb:
wyfrn:
Das klingt ganz interessant! Wenn es wiederum keine C++-Bibliothek gibt, nützt mir das nicht so viel. Andere Sprachen mit einzuflechten erscheint mir (zurzeit) noch sehr aufwendig.Der erste Treffer in dem Google Link stellt einen Implementation in C++ vor und das ganze gibts auch als Lib auf github. Vielleicht kannst du ja damit was anfangen bzw. für dich anpassen. Kostet zwar jetzt ne Ecke an Arbeit, aber wenn deine Daten wirklich so schlecht strukturiert sind und sich häufig ändern, wirst du im Nachhinein davon profitieren.