tutorials.de Buch-Aktion 05/2012
Like Tree5Danke
  • 1 Beitrag von DosCoder
  • 1 Beitrag von Matthias Reitinger
  • 1 Beitrag von Matthias Reitinger
  • 1 Beitrag von Matthias Reitinger
  • 1 Beitrag von Matthias Reitinger
ERLEDIGT
JA
ANTWORTEN
11
ZUGRIFFE
1250
EMPFEHLEN
  • An Twitter übertragen
  • An Facebook übertragen
AUF DIESES THEMA
ANTWORTEN
  1. #1
    anfängerregnäfna anfängerregnäfna ist offline Mitglied Silber
    Registriert seit
    May 2009
    Beiträge
    88
    Hallo,
    ich bin ein Neueinsteiger in Java und nach dem Lesen von 2 Büchern habe ich es geschafft, mit Anleitungen aus einem der Bücher, und viel Improvisation ein TicTacToe Spiel zu programmieren. Das Spiel funktioniert, aber die künstliche Intelligenz ist erstaunlich dumm. Die Züge des Computers basieren auf dem Bruteforce-Algorithmus und einer Bewertungsfunktion, eigentlich sollte der Computer in der Lage sein gegen jeden beliebigen Menschen unentschieden zu spielen, doch das tut er nicht. Weiß jemand, wie ich ihn dazu bringen kann?

    Der Quellcode liegt im Anhang
    Angehängte Dateien Angehängte Dateien
     

  2. #2
    Avatar von DosCoder
    DosCoder DosCoder ist offline Mitglied Gold
    Registriert seit
    Sep 2008
    Ort
    Kreis Würzburg(Bayern)
    Beiträge
    228
    Hi,
    seit ner halben Stunde versuche ich nun schon, deinen Code zu verstehen. Ergebnislos. Und zwar aus folgenden Gründen:
    1. gesamter Code ist unformatiert( gut, das habe ich mit Netbeans wieder hingebogen)
    2. aussagelose Methoden und Variablen (z.B.: "k", "anzuege")
    3. keine Kommentare außer denen von Netbeans

    zu 1): Wenn man deine Datei mit den Editor öffnet, wird man erst mal von unformatierten Code erschlagen. Also muss man die txt-Datei umbennen und in eine IDE einfügen. Warum stellst du nicht gleich die *.java-Datei zu Verfügung?
    zu 2) Wie schon in den Beispielen: Was soll game.k darstellen oder was macht value()? Irgendendeinen Wert zurückgeben? So wie ich das verstanden habe, prüft sie, ob jemand gewonnen hat. Ist das richtig oder habe ich deinen Code falsch interpretiert?
    zu 3) Wenn du schon aussagelose Methoden hast, dann verwende doch bitte Kommentare.

    Also, wenn du deinen Code nochmal überarbeitest, wird man dir auch bereitwilliger helfen.

    Ciao
    DosCoder
     
    Man kann mich für das verantwortlich machen, was ich hier schreibe, nicht für das, was andere verstehen.

    Sollte ich mal Mist labern weist mich bitte darauf hin.

    Ich freue mich über ein Danke, wenn ich helfen konnte!

  3. #3
    anfängerregnäfna anfängerregnäfna ist offline Mitglied Silber
    Registriert seit
    May 2009
    Beiträge
    88
    Entschuldigung , hier die neue, kommentierte Version.
    Einen Java File kann ich leider nicht hochladen, es sollte aber reichen, den Text zu markieren und in die jeweilige IDE zu kopieren
    quellcode.txt
     

  4. #4
    Avatar von DosCoder
    DosCoder DosCoder ist offline Mitglied Gold
    Registriert seit
    Sep 2008
    Ort
    Kreis Würzburg(Bayern)
    Beiträge
    228
    Hi,
    irgendwie ist es entwedern zu heiß, zu spät oder ich bin einfach zu doof, aber selbst nach 2.5 Stunden habe ich das Problem nicht gepackt . Ich wüde es ja morgen nochmal versuchen, aber da fahre ich 2 Wochen in Urlaub (zwar mit Notebook, aber ohne Netz). Sorry.
    Ich muss aber dazu sagen, dass dein Quellcode ziehmlich vermurkst ist:
    Beipsiel:
    Code java:
    1
    2
    3
    4
    5
    
    int wert=0; [...]
    wert=value(); //das geht doch kürzer!
    [...]
    if(wert!=0){return wert;} 
    wert=0; //ist das notwendig, nach der Abfrage?
    Zudem erschwert die fehlende OOP das ganze. Aber wie gesagt, es muss auch irgendwie an mir liegen. Tut mir echt leicht.
    *Sache an den nächsten weitergeb*

    Ciao
    DosCoder
    anfängerregnäfna bedankt sich. 
    Man kann mich für das verantwortlich machen, was ich hier schreibe, nicht für das, was andere verstehen.

    Sollte ich mal Mist labern weist mich bitte darauf hin.

    Ich freue mich über ein Danke, wenn ich helfen konnte!

  5. #5
    Registriert seit
    Dec 2001
    Ort
    Bayern
    Beiträge
    5.800
    Blog-Einträge
    5
    Hallo,

    also ich denke, es geht hauptsächlich um diese mysteriöse Funktion namens x:
    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
        public int x(int tiefe) {
            int wert = 0;
            int wertneu = 0;
            int anzzuege = 0;
            wert = value();
            if (tiefe == 0) {
                return wert;
            }
            if (wert != 0) {
                return wert;
            }
            wert = 0;
            getzuege();
            for (int i = 0; i < 9; i++) {
                anzzuege += zuege[i];
            }
            if (anzzuege == 0) {
                return 0;
            }
            for (int n = 0; n < 9; n++) {
                if (zuege[n] == 1) {
                    buttons.get(n).setText("X");
                    wertneu = o(tiefe - 1);
                    if (wertneu >= wert) {
                        wert = wertneu;
                        k = n;
                    }
                    buttons.get(n).setText(" ");
                }
            }
            return wert;
        }
    (Einrückungen sind von mir bzw. Eclipse - ansonsten kann man den Code ja nicht vernünftig lesen. Gewöhn dir am besten möglichst schnell an, deinen Code einzurücken.)

    Meine Anmerkungen/Tipps hierzu (manche Sachen wurden schon erwähnt, aber ich will sie trotzdem nochmal nennen):
    • Deklariere lokale Variablen dort, wo du sie brauchst. Die Variable wertneu deklarierst du beispielsweise ganz am Anfang, und man hat keine Ahnung a) welchen Sinn die Variable erfüllt und b) warum sie auf 0 gesetzt wird. Wenn die Variable dann ziemlich zum Schluss der Funktion hin benutzt wird, muss man mit dem Blick erst wieder ganz nach oben wandern, um rauszufinden, wie sie denn jetzt initialisiert wurde.
    • Ergebnisse einer Funktion "hintenrum", also über das Setzen von Membervariablen, zurückzugeben, ist auch erst mal verwirrend. Das kann in bestimmten Fällen schon Sinn ergeben, aber dann nennt man die Methode nicht getzuege, sondern eher updatezuege o.ä. (noch besser: updateMoves, ein Gemisch aus verschiedenen Sprachen bei der Benennung lenkt nur ab.)
    • Womit wir schon bei der Benennung wären: die Benennung einer Funktion/Variable macht idealerweise Kommentare zu ihrem Verwendungszweck überflüssig. Namen wie x, o, k oder value sind daher tabu! Eine Ausnahme stellen höchstens Laufvariablen in Schleifen dar, aber nur wenn aus dem Kontext unmittelbar klar ist, wozu sie verwendet wird.
    • Die Funktionen x und o sind bis auf 2 Zeilen absolut identisch. Code, der an mehreren Stellen steht, ist schlecht, da man bei einer Änderung an einer Stelle auch die andere Stelle ändern muss (und wehe man vergisst das mal...). Überleg dir mal, ob du die beiden Funktionen nicht zusammenfassen könntest.
    • Trenne die grafische Oberfläche (View) von den Daten (Model) deines Programms! Die ständige Aktualisierung von zuege aus den Werten der Buttons ist durch die Brust ins Auge. Und ganz nebenbei auch der Grund, warum dein Algorithmus nicht funktioniert. Du setzt nämlich nach einem ausprobierten Zug und dem rekursiven Aufruf den jeweiligen Button wieder zurück, aber das Array zuege ist immer noch auf dem alten Stand. Mein Vorschlag: ersetze das Array zuege durch
      Code java:
      1
      
      int spielfeld[9];
      Dieses Array ist dann dein "Modell" des Spielfeldes. Eine 0 im Array steht für ein freies Feld, eine 1 für ein "X" und eine -1 für ein "O". Verwende dieses Array und nur dieses Array für alle Abfragen, die den Status des Spielfeldes betreffen (also z.B. auch zur Ermittlung, ob jemand gewonnen hat). Setze die Textwerte der Buttons nur dann, wenn tatsächlich ein Zug erfolgt, den der Benutzer auch mitbekommen soll. Wenn du dich daran hältst, sollte ein a) viel kürzerer b) leichter verständlicher und wartbarer und c) vor allem funktionierender Quellcode rauskommen.
    Und jetzt viel Erfolg bei der Umsetzung

    Grüße, Matthias
    anfängerregnäfna bedankt sich. 
    „Gib einem Menschen einen Fisch, und er wird für einen Tag satt. Lehre ihn Fischen, und er wird ein Leben lang satt.“
    “For every complex problem, there is an answer that is short, simple and wrong.”
    “Pessimism is safe, but optimism is a lot faster!”


    Aktuelles Coding Quiz: #17 - Wörter kreuz und quer

  6. #6
    anfängerregnäfna anfängerregnäfna ist offline Mitglied Silber
    Registriert seit
    May 2009
    Beiträge
    88
    Habe versucht, deine Ratschläge so gut wie möglich zu befolgen und den Quellcode neu geschrieben. Ich meine bereits eine leicht Vebesserung zu spüren, aber der Rechner ist immer noch zu besiegen. Der Quellcode ist im Anhang.
    quellcode2.txt
     

  7. #7
    Registriert seit
    Dec 2001
    Ort
    Bayern
    Beiträge
    5.800
    Blog-Einträge
    5
    Hallo,

    das sieht doch schon mal viel besser aus! So lässt sich der Quellcode gleich besser verstehen. Noch ein paar Anmerkungen zum Programmierstil:
    • Das hier:
      Code java:
      1
      2
      
      int wert=0;
      wert=bewerten();
      ist unschön, da die Initialisierung mit 0 überhaupt keinen Sinn ergibt. Kürzer und klarer ist
      Code java:
      1
      
      int wert=bewerten();
    • Mehrere Anweisungen in einer Zeile tragen nicht zur Lesbarkeit des Quellcodes bei. Zeilenumbrüche kosten nichts, ebenso wie Leerzeichen hie und da.
    • Deine Art der Einrückung ist etwas unkonventionell. Benutzt du eine IDE wie Eclipse oder Netbeans? Diese können dir die Formatierungsarbeit größtenteils abnehmen. In Eclipse z.B. mit Strg+Umschalt+F.
    Unter Berücksichtigung dieser Punkte könnte deine KI-Funktion so aussehen:
    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    public int computer() {
        int wert = bewerten();
        if (wert != 0) return wert;
        if (zählefreieFelder() == 0) return wert;
        int wertneu = 0;
        for (int i = 0; i < 9; i++) {
            if (spielfeld[i] == 0) {
                spielfeld[i] = 1;
                wertneu = anwender();
                if (wertneu >= wert) {
                    errechneterZug = i;
                    wert = wertneu;
                    if (wert == 1) {
                        spielfeld[i] = 0;
                        // Es gibt nur -1,0,1-> 1 ist perfekt->sofort abbrechen
                        // und Rechenzeit sparen
                        return wert;
                    }
                }
                spielfeld[i] = 0;
            }
        }
        return 0;
    }
    Der Fehler ist nun folgender: überleg dir mal, was passiert, wenn sich der Computer in einer auswegslosen Situation befindet. anwender() gibt also bei jedem Schleifendurchlauf eine -1 zurück (egal wo der Computer sein X setzt, er verliert zwangsläufig in der Situation). Dann wird
    1. errechneterZug nie gesetzt
    2. am Ende eine 0 zurückgegeben, obwohl eigentlich -1 richtig wäre (es soll ja der bestmögliche Ausgang ermittelt werden, und der ist hier nun mal leider -1)
    Beide Fehler könntest du dadurch beheben, dass du wert am Anfang auf -1 initialisierst und am Schluss wert zurückgibst statt 0 (in anwender() natürlich entsprechend!).

    Dann ist mir grade noch ein Fehler aufgefallen. Angenommen der Computer kann bestenfalls ein Unentschieden erzielen, und zwar indem er auf Position 8 setzt. Dann wird errechneterZug auf 8 gesetzt und probiert dann noch aus, was passiert, wenn er auf Position 9 setzt. Angenommen er würde dann verlieren, wenn er das macht. Dann kann es vorkommen, dass beim Abarbeiten aller möglichen Spielausgänge errechneterZug zwischendrin auf einen ganz anderen Wert gesetzt wird. Dadurch ergibt sich am Schluss ein möglicherweise falscher Zug. Du darfst also errechneterZug nur setzen, wenn der Computer den Zug ermittelt hat, den er tatsächlich als nächstes ausführen soll.

    Grüße, Matthias
    anfängerregnäfna bedankt sich. 
    „Gib einem Menschen einen Fisch, und er wird für einen Tag satt. Lehre ihn Fischen, und er wird ein Leben lang satt.“
    “For every complex problem, there is an answer that is short, simple and wrong.”
    “Pessimism is safe, but optimism is a lot faster!”


    Aktuelles Coding Quiz: #17 - Wörter kreuz und quer

  8. #8
    anfängerregnäfna anfängerregnäfna ist offline Mitglied Silber
    Registriert seit
    May 2009
    Beiträge
    88
    @Mathias Reitinger ich verwende NetBeans und habe den Quellcode erneut verbessert. Leider ist der Computer keine Spur schlauer
    Hier der neue Quellcode.
    tictactoe.txt
     

  9. #9
    Registriert seit
    Dec 2001
    Ort
    Bayern
    Beiträge
    5.800
    Blog-Einträge
    5
    Hallo,

    die Methode zahlefreieFelder gibt entgehen ihrer Benennung die Anzahl der belegten Felder zurück. Außerdem wird errechneterZug immer noch in jeder Rekursionstiefe gesetzt.

    Grüße, Matthias
    anfängerregnäfna bedankt sich. 
    „Gib einem Menschen einen Fisch, und er wird für einen Tag satt. Lehre ihn Fischen, und er wird ein Leben lang satt.“
    “For every complex problem, there is an answer that is short, simple and wrong.”
    “Pessimism is safe, but optimism is a lot faster!”


    Aktuelles Coding Quiz: #17 - Wörter kreuz und quer

  10. #10
    anfängerregnäfna anfängerregnäfna ist offline Mitglied Silber
    Registriert seit
    May 2009
    Beiträge
    88
    Ich könnte natürlich noch zahlefreiefelder in bestimmefelder umbenennen,
    und versuchen errechneterZug nicht immer zu setzen. Aber ich glaube, dass würde keinen Unterschied machen. zahlefreiefelder tut ihre aufgabe und im Bezug auf den Wert von errechneterZug denke ich, ist die Hauptsache doch, dass er ganz am Ende korrekt gesetzt wird. Mein Problem liegt irgendwo im Algorithmus. Das Programm muss kein Musterbeispiel an Eleganz sein, aber es sollte seine Arbeit tun. Ich glaube nicht, dass diese Verbesserungen den Fehler zeigen würden. Weiß jemand, was an der Theorie falsch ist?
     

  11. #11
    Registriert seit
    Dec 2001
    Ort
    Bayern
    Beiträge
    5.800
    Blog-Einträge
    5
    Zitat Zitat von anfängerregnäfna Beitrag anzeigen
    Ich könnte natürlich noch zahlefreiefelder in bestimmefelder umbenennen,
    und versuchen errechneterZug nicht immer zu setzen. Aber ich glaube, dass würde keinen Unterschied machen.
    Glauben heißt nicht wissen. Hast du es denn schon mal ausprobiert?

    Zitat Zitat von anfängerregnäfna Beitrag anzeigen
    zahlefreiefelder tut ihre aufgabe
    Eben nicht! Die Methode gibt die Anzahl der belegten Felder zurück, nicht die Anzahl der freien. Verwenden tust du sie aber unter der Annahme, dass die Anzahl der freien Felder bestimmt wird.

    Zitat Zitat von anfängerregnäfna Beitrag anzeigen
    und im Bezug auf den Wert von errechneterZug denke ich, ist die Hauptsache doch, dass er ganz am Ende korrekt gesetzt wird.
    Das war ja genau mein Einwand, dass am Ende eben nicht das korrekte Ergebnis rauskommt.

    Zitat Zitat von anfängerregnäfna Beitrag anzeigen
    Mein Problem liegt irgendwo im Algorithmus. Das Programm muss kein Musterbeispiel an Eleganz sein, aber es sollte seine Arbeit tun. Ich glaube nicht, dass diese Verbesserungen den Fehler zeigen würden. Weiß jemand, was an der Theorie falsch ist?
    Die von mir aufgezeigten Fehler sind Fehler im Algorithmus (bzw. in dessen Implementierung). Das Vorgehen ist grundsätzlich richtig. Wenn du mir nicht glaubst, dass meine Aussagen richtig sind, dann kann ich dir leider auch nicht weiterhelfen.

    Grüße, Matthias
    anfängerregnäfna bedankt sich. 
    „Gib einem Menschen einen Fisch, und er wird für einen Tag satt. Lehre ihn Fischen, und er wird ein Leben lang satt.“
    “For every complex problem, there is an answer that is short, simple and wrong.”
    “Pessimism is safe, but optimism is a lot faster!”


    Aktuelles Coding Quiz: #17 - Wörter kreuz und quer

  12. #12
    anfängerregnäfna anfängerregnäfna ist offline Mitglied Silber
    Registriert seit
    May 2009
    Beiträge
    88
    @Mathias Reitinger: Du hattest Recht:
    Habe erneut den Code überarbeitet und nun scheint das Programm zu funktionieren, habe noch kein einziges Mal gewonnen, bin aber momentan auch sehr müde.
    Vielen Dank.
    Für alle die gerne das fertige, funktionierende Programm sehen würden, hier der Quellcode:
    quellcode.txt
     

Ähnliche Themen

  1. TicTacToe bauen
    Von lisali im Forum Java
    Antworten: 28
    Letzter Beitrag: 28.06.10, 18:45
  2. TicTacToe / Netzwerk
    Von Nehemia im Forum Java
    Antworten: 5
    Letzter Beitrag: 27.01.10, 10:37
  3. TicTacToe gewinnstellungen
    Von seji im Forum Java Grundlagen
    Antworten: 6
    Letzter Beitrag: 05.01.09, 16:56
  4. TicTacToe Strategie des Computers
    Von sawamin im Forum C/C++
    Antworten: 6
    Letzter Beitrag: 16.02.05, 22:41
  5. [C++] TicTacToe
    Von js-mueller im Forum C/C++
    Antworten: 5
    Letzter Beitrag: 20.08.03, 14:24