Technik, Gothic und Anderes

Technik ist Spiel, Gothic ist ernst und Zeit hat man zuviel

  • Kategorien

  • Tags

  • Archiv

  • Links

    zu Bee5

    blog.oncode.info läuft bei Cyon und ich bin sehr glücklich damit.

Ein Programmierwettbewerb vom Linux Magazin

Geschrieben von skaldrom am 10. August 2010

Dieser Beitrag ist Teil 1 von 3 in der Serie Linux-Magazin Wettbewerb

    Nachtrag vom 19.08.2010: Ich wurde Opfer von “mod_security”, damit konnte ich keine Artikel mehr editieren, weil ich immer ein “404 Not Found” präsentiert bekommen habe. DAS ist doch ein Heuler…

    Nun, ich habe eine neue Version des Clients unten hingehäng: Stabiler, so dass er auch eine ganze Nacht über viele Stunden und Verbindungsprobleme hinweg weiterspielt.

    WuerfelOh nein. Frisch erkältet wollte ich mich auf die Arbeiten stürzen, die eigentlich schon letztes Jahr hätten erledigt sein müssen, und nun das: Das Linux-Magazin veranstaltet einen Wettbewerb für Programmierer! Die Details sind in der Ausgabe 09/10 (auch online) beschrieben und es gibt eine Wettbewerbs-Seite dazu.

    Grundsätzlich geht es darum, dass zwei Programme gegeneinander würfeln. Wer zuerst 50 Punkte oder mehr erreicht, hat gewonnen. Ist man am Zuge, so kann man entweder Würfeln oder “Save”n. Bei einem Wurf werden die Augen zu der eigenen Punktzahl hinzugezählt, ausser man würfelt eine 6, dann werden alle Punkte seit dem letzten Save wieder abgezogen. Bei einem Save werden die Punkte gespeichert und der Gegner ist am Zuge.

    Gespielt wird richtig Harte-Männer-mässig über einen TCP-Port mit einem definierten, menschenlesbaren Protokoll.

    Die Programmiersprache kann frei gewählt werden, insofern sie in Ubuntu 10.04 vorhanden ist und mit einem Makefile gebuildet und gestartet werden kann. Ich hab mich mal wieder für PHP (Kommandozeile) entschieden weil das Problem, hmm, überhaupt nicht zeitkritisch ist (nein, auch diesmal kein Monte-Carlo) und auch sonst, hmm, und überhaupt nicht weil ich zu faul war ein Makefile für Java zu schreiben oder meine C-Kenntnisse aufzufrischen :) .

    Als Erstes habe ich einen Simulator für einen Benchmark implementiert. Als Messgrösse habe ich die Anzahl “Transaktionen” gewählt, also wie oft mein Programm dran war mit Würfeln bis die 50 erreicht wurden. Dabei habe ich ein paar interessante Erkenntnisse gewonnen: So sind zum Beispiel die Resultate sehr unterschiedlich wenn zufällig mit Wahrscheinlichkeit 1/4 oder wenn jedes 4. mal gesaved wird. Auch die optimale Wahrscheinlichkeit habe ich so nicht erwartet wie sie gemessen wurde.

    Um meine Arbeit etwas zu teilen und um den Einstieg zu erleichtern, publiziere ich hier meine Spielerumgebung. Der geneigte Wettbewerbsteilnehme müsste nun nur noch:

    • Den Spielernamen ändern (Konstante NAME),
    • Die Methode play mit Intelligenz füllen.

    Zuerst das Makefile:

    PHP = /usr/bin/php

    build:
            echo "PHP muss nit gebuildet werden :)."
    game: wettbewerb.php
            $(PHP) ./wettbewerb.php

    Und hier der Spieler wettbewerb.php, bereit getuned zu werden:

    <?php
    /**
     * Wettbewerbsgeruest von Skaldrom Y. Sarg (http://blog.oncode.info)
     */


    define("NAME","test.spieler"); // UNBEDINGT AENDERN!!!! Kleinbuchstaben, keine Sonderzeichen
    define("LOGFILE","dice.log"); // Schreibt keines wenn leer (fuer den Wettbewerb)
    define("SERVER", "wettbewerb.linux-magazin.de");
    define("PORT", 3333);

    class wettbewerb {

        /**
         * Dies ist die Spielfunktion.
         *
         * Macht was cleveres, aber stehlt mir nicht die Preise!
         *
         *
         * @param $numThrowsThisTransaction int Anzahl Würfe seit letzter 6 oder letztem Save
         * @param $currentTransactionPoints int Punkte seit letzter 6 oder letztem Save
         * @param $pointsAtLastSave         int Punkte bei letztem Save
         * @param $transactions             int Anzahl gemachter Züge
         * @param $numSaves                 int Anzahl Saves
         * @param $enemyPoints              int Punkte des Gegners
         * @param $rollsThisTransaction     Array Würfe in diesem Durchgang
         *
         * @return String "roll" für einen Wurf, "save" um zu speichern und abzugeben.
         */


        function play($numThrowsThisTransaction, $currentTransactionPoints, $pointsAtLastSave, $transactions, $numSaves, $enemyPoints, $rollsThisTransaction) {
            $this->log("[PLAY-RESULT]: ROLL.");
            return "roll";
        }

        function log($message) {
            if(LOGFILE) {
                error_log(date("r").": ".$message."\n", 3, LOGFILE);
            }
        }

        function secureFGetS() {
            $response="";
            if(!$this->socket) {
                do {
                    $this->connect();
                } while(!$this->socket);
            }
            $response=fgets($this->socket);
            $info = stream_get_meta_data($this->socket);
            if ($info['timed_out']) {
                $this->log("Got a Socket Timeout! (unread:".$info['unread_bytes'].")");
                fclose($this->socket);
                $this->socket=0;
                $response="";
                sleep(5);
            }
            return $response;

        }

        function connect() {
            // Kontaktaufnahme
            $this->socket=0;
            while(!$this->socket) {
                $this->log("CONNECTING");
                $this->socket = @fsockopen(SERVER , PORT, $errno , $errstr , 180);
                if(!$this->socket) {
                    $this->log("Verbindungsversuch fehlgeschlagen: ($errno) $errstr.");
                    sleep(5);
                }
            }
            if(function_exists("stream_set_timeout")) {
                $this->log("Set Socket Timeout.");
                stream_set_timeout($this->socket, 120); //two Mins
            }
            // Antwort des Servers
            $this->log("CONNECTED");
        }

        function playARound() {
            $this->log("----------------- NEW GAME ".SERVER .":".PORT." -----------------");

            do {
                $this->connect();
            } while(!$this->socket);
            do {
                $response=$this->secureFGetS();
            } while (!$response);
            $resInfo=split(" ", $response);
            $serverCommand=strtoupper(trim($resInfo[0]));
            $this->log("[SERVER]: ".trim($response)." (".$serverCommand.")");
            switch($serverCommand) {
                case "HELO": break; // Alles OK
                case "DENY": fclose($this->socket);
                    return;
                    break; // Dann halt nicht
                default: fclose($this->socket);
                    $this->log("[CLIENT]: Exit on Hello because we do not understand [$serverCommand].");
                    exit(); // DAS verstehen wir nicht!
            }

            // Authentifizierung
            $message="AUTH ".strtolower(NAME);
            $this->log("[CLIENT]: ".$message);
            fputs($this->socket, $message."\n");

            // Los gehts!
            $currentPlayer=1; // Wer ist am Zug? 0 == wir, 1 == der Andere
            $numThrowsThisTransaction=0;
            $currentTransactionPoints=0;
            $pointsAtLastSave=0;
            $transactions=1;
            $numSaves=0;
            $enemyPoints=0;
            $rollsThisTransaction=array();
            do {
                $response="";
                $response=$this->secureFGetS();
                if(!$response) {
                    $this->log("INGAME TIMEOUT: Forgetting this game.");
                    return;
                }
                $this->log("[SERVER]: ".trim($response));
                $resInfo=split(" ", $response);
                $serverCommand=strtoupper(trim($resInfo[0]));
                switch($serverCommand) {
                    case "THRW": // Ein Wurf
                        if($currentPlayer==0) { // Wir sind dran
                            if($resInfo[1]==6) { // Ooops, eine 6 gewürfelt
                                $currentPlayer=1; // Der Andere ist dran
                                $currentTransactionPoints=0;
                                $numThrowsThisTransaction=0;
                                $transactions++;
                                $rollsThisTransaction=array();
                            } else {
                                $numThrowsThisTransaction++;
                                $currentTransactionPoints+=$resInfo[1];
                                $rollsThisTransaction[]=$resInfo[1];
                            }
                        }
                        break;
                    case "DENY": break; // Dann halt nicht
                    case "TURN":
                        $currentPlayer=0; // Wir sind dran!
                        $enemyPoints=$resInfo[2];
                        $pointsCurrentOurs=$resInfo[1];
                        $currentTransactionPoints=$pointsCurrentOurs-$pointsAtLastSave;
                        // Here we play!
                        $ret=$this->play($numThrowsThisTransaction, $currentTransactionPoints, $pointsAtLastSave, $transactions, $numSaves, $enemyPoints, $rollsThisTransaction);
                        if(strtolower($ret=="roll")) { // Rollin' Baby
                            $this->log("[CLIENT]: ROLL");
                            fputs($this->socket, "ROLL\n");
                        } else { // Save
                            $this->log("[CLIENT]: SAVE");
                            fputs($this->socket, "SAVE\n");
                            $transactions++;
                            $rollsThisTransaction=array();
                            $numThrowsThisTransaction=0;
                            $numSaves++;
                            $pointsAtLastSave=$pointsCurrentOurs;
                            $currentTransactionPoints=0;
                            $currentPlayer=1;
                        }
                        break;
                    case "DEF":
                    case "WIN": break;
                    default: fclose($this->socket);
                        $this->log("[CLIENT]: Exit because we do not understand [$serverCommand].");
                        exit(); // DAS verstehen wir nicht!
                }
            } while (!in_array($serverCommand, array("DENY", "DEF", "WIN")));
            // Feddich Lustig
            fclose($this->socket);
        }

    }

    $w=new wettbewerb();
    for($i=0;$i<1000;$i++) {
        echo "*** $i ***\n";
        $w->playARound();
    }
    ?>


    4 Antworten zu “Ein Programmierwettbewerb vom Linux Magazin”

    Kommentare

    1. ncorpse Schreibt:

      Vielen Dank für den Beispielcode

      Was für eine spannende Aufgabe… Ich hoffe ich finde die Zeit auch noch einen Bot zu schreiben:)
      Eigentlich ein guter Vorwand um PHP-Sockets anzuschauen, aber Ich würde es warscheinlich in Java implementieren..

    2. skaldrom Schreibt:

      PHP Sockets sind “interessant”. Es gibt anscheinend mehrere Möglichkeiten, die auch kombiniert werden können :).

      Auf jeden Fall wünsche ich Viel Glück!!!!

    Trackbacks


    1. alea iacta est | Bits, Bytes and my 5 cents

      [...] weiteres sehr gutes PHP-Beispiel gibt es auch auf blog.oncode.info, dank welcher Seite ich überhaupt auf den Wettbewerb aufmerksam geworden [...]


    2. Bits, Bytes and my 5 cents

      alea iacta est – Teil 3…

      Mein Bot ist fertig und hat bereits die ersten Testläufe absolviert. Begonnen habe ich auf dem offiziellen Server, doch dummerweise ist dieser mehr als nur langsam. So kann es da problemlos mal vorkommen, dass man 15 Minuten auf ein neues Spiel wartet….

    3. Lassen Sie eine Antwort hier...

      XHTML: Sie können folgende Tags verwenden: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


      nine − = 4