Parse-Codestyle verbessern?



  • Hm, ja, das klingt schon Mal nicht schlecht. Mein Code wird dadurch jedoch nur ungleich ordentlicher, fürchte ich...



  • Eisflamme schrieb:

    Hm, ja, das klingt schon Mal nicht schlecht. Mein Code wird dadurch jedoch nur ungleich ordentlicher, fürchte ich...

    Das ist keine Schande.
    Selbst der Game-Coders-Gott John Carmack hat schonmal ordentlichen Code geschrieben.
    ~Und sogar immer mehr davon, der ist echt abgekippt.~



  • Oh, ich meinte aber, dass mein Code nicht wirklich viel ordentlicher dadurch wird. 🙂



  • Eisflamme schrieb:

    Oh, ich meinte aber, dass mein Code nicht wirklich viel ordentlicher dadurch wird. 🙂

    Ja, ich hab's befürchtet. Es ist halt ein unordentliches Problem. Aber naja, vielleicht hat je ein anderer noch einen Trick in der Tasche, der besser passt, hoffen wir mal.



  • Ja, vielleicht. 🙂

    Eh das Ding hier auf Seite2 abrutscht, genehmige ich mir Mal einen Push, den ich aber nicht zu wiederholen gedenke. Vielleicht hat noch jemand eine Idee. Es wird jedenfalls immer schlimmer, weil die Formate jetzt noch willkürlicher werden.

    Microgaming - €0.50 NL (6 max) - Holdem - 6 players

    BB: 100 BB
    UTG: 353.66 BB
    MP: 112.82 BB
    CO: 50.56 BB
    BTN: 100 BB
    Hero (SB): 101.5 BB

    Hero posts SB 0.5 BB, BB posts BB 1 BB

    Pre Flop: (pot: 1.5 BB) Hero has Ks 2c

    fold, MP raises to 3 BB, fold, BTN calls 3 BB, Hero raises to 8.5 BB, fold, fold, BTN calls 5.5 BB

    Flop: (21 BB, 2 players) 4c Js Ts
    Hero bets 11 BB, BTN calls 11 BB

    Turn: (43 BB, 2 players) 2s
    Hero checks, BTN checks

    River: (43 BB, 2 players) 3c
    Hero bets 32 BB, fold

    Hero wins 40.86

    In solchen Fällen konvertiere ich diese HH zu einer im Format, das bereits gelesen werden kann. Dann ist das ein Schritt mehr, das kostet aber kaum Zeit und dann ist es nicht ganz so viel Zusatzcode und vermischt sich vor allem nicht mit dem aktuellen zu stark.

    Blöd ist die Identifizierung, die ich gerade nur über Zeile1 machen kann. Auch hat das Programm zum Kopieren in die Zwischenablage jetzt zwei Buttons zum Exportieren. Die liefern aber je nach Seite zwei unterschiedliche Formate (man hätte ja gehofft, dass einer etwas standardisiertes liefert, aber da kann man lange träumen).

    Und in dieser HH wird nicht Mal die Mühe gemacht immer brav die Währung hinzuschreiben und solche Dinge wie:

    Hero bets 32 BB, fold

    finde ich auch wilkürlich ohne Ende. Wieso kann man nicht schreiben "BTN folds"? Wieso der plötzliche Bruch und einfach nur "fold"? Dann hätte man vorher auch "bet 32 BB" schreiben können. Auch die Leerzeile zwischen "Pre Flop" und den Aktionen finde ich niedlich, kommt sie doch nur dort und später nicht mehr vor. :p



  • 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.

    Was Tips angeht...
    Regexen könnten hier helfen.
    Das Unterscheiden verschiedener Formate + Einschleifen eines passenden Präprozessors pro Format ist sicher eine gute Idee.
    Evtl. wäre sogar 1 Parser pro Format angesagt. So lange die Formate ähnlich genug bleiben kann sich der ja diverser Hilfsfunktionen aus einem gemeinsamen Pool bedienen, so dass der Parser für jedes Format wieder ausreichend klein wird.

    Was die Identifizierung angeht, wieso kannst du die nur über Zeile 1 machen? Wenn du Live-Streams parsen willst, dann muss der User das Format halt vorher einstellen. Und wenn das Spiel bereits fertig vorliegt, dann lass das komplette Spiel einfach durch alle Parser laufen die du hast, und nimm den der am wenigsten Zahnräder gespuckt hat.

    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.



  • Ohne das groß wirklich durchdacht zu haben (das ist ja deine Aufgabe ;)): Ein Parser muss nicht nur ein Symbol generieren. Er kann auch eine Liste von möglichen Resultaten ausgeben, die du dann im nächsten Schritt weiter verwendest.
    So wie ich das verstanden habe, hast du mehrere Formate, die zwar alle formal spezifizierbar sind, aber du weißt nicht, welches du gerade hast. Du könntest also entweder nach dem Generieren des Syntaxbaums prüfen, welches Format du überhaupt haben kannst, oder schon während dem Parsen die möglichen Formate einschränken.



  • 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:

    1. die ersten zwei Zeilen können fehlen oder komplett anders aussehen
    2. Statt "Seat 1: Spielername" kann da auch einfach "Spielername" stehen
    3. diverse Leerzeichen zwischen ( und $ sind möglich
    4. statt $ kann überall auch ein anderes Währungszeichen stehen; es kann auch fehlen
    5. Die Blind-Postings-Lines können fehlen; es werden dann dennoch Blinds geposted, das aber nicht angezeigt (sind Pflichtbeträge)
    6. "*** HOLE CARDS ***" und die anderen haben ein paar Alternativbezeichnungen je nach Seite
    7. "Dealt to Player1" kann anders heißen oder fehlen, kommt manchmal auch in der "*** HOLE CARDS ***"-Zeile vor
    8. Die Aktionen, die hier immer die Form "Spieler: does" hat kann ohne Doppelpunkt dargestellt werden
    9. Die Aktionen (siehe 😎 können bei raise ohne das "$x to $y" erfolgen, also einfach nur "to $y"
    10. Die Aktionen können statt in unterschiedlichen Zeilen auch in einer Zeile mit Komma separiert vorkommen; auch dann mit und ohne Doppelpunkt
    11. Auch die Karten von "*** FLOP ***" können statt in eckigen Klammern ohne eckige Klammern dort stehen
    12. Es können 2-10 Spieler mitspielen
    13. Das Spiel kann vorbei sein, bevor Flop/Turn/River überhaupt erscheinen
    14. 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) || parseWert
    

    Wenn 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.


Anmelden zum Antworten