TicTacToe KI

java
javascript
softwareentwicklung
#1

Moin Freunde, ich verzweifle gerade sehr zu folgender TicTacToe Vorlage eine KI mit MiniMax Algorithmus zu programmieren.
Eine (mehr oder wenig dumme) KI habe ich schon, allerdings bin ich mit ihr nicht zufrieden da sie aus sehr vielen if-Anweisungen besteht…

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;


public class tictactoe extends JFrame {
  // Anfang Variablen

  // Anfang Attribute
  private int SpielerAmZug;
  private int[][] Feld = new int[3][3];
  private boolean spielende;
  private boolean KI1;
  private boolean KI2;
  //KIs deklarieren und initialisieren

  KI ki1 = new KI();
  //JustKIn2 ki2 = new JustKIn2();
  // Ende Attribute

  // Ende Variablen

  public tictactoe(String title) {
    // Frame-Initialisierung
    super(title);
    addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent evt) { System.exit(0); }
    });

    addMouseListener(new java.awt.event.MouseAdapter() {
      public void mousePressed(MouseEvent e) {
        Spielzug(e);
        //JOptionPane.showMessageDialog(null,"X-Position: "+e.getX()+"\nY-Position: "+e.getY());
      }
    });

    int frameWidth = 300;
    int frameHeight = 300;
    setSize(frameWidth, frameHeight);
    Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
    int x = (d.width - getSize().width) / 2;
    int y = (d.height - getSize().height) / 2 ;
    setLocation(x, y);
    Container cp = getContentPane();
    cp.setLayout(null);
    // Anfang Komponenten

    // Ende Komponenten

    setResizable(false);
    setVisible(true);
    
    // Feldarray auf Null setzen
    for (int i = 0; i<=2; i++)
      for (int j = 0; j<=2; j++) Feld[i][j] = 0;
    //Spieler 1 am Zug
    SpielerAmZug = 1;
    //Spiel läuft
    spielende = false;
    
    //KIs eingeschaltet?
    KI1 = true;
    KI2 = false;
    


  }

  // Anfang Methoden

  public void Spielerwechsel() {
    if (SpielerAmZug == 1) SpielerAmZug = 2;
      else SpielerAmZug = 1;
  }
  

  //Methode zum Spielende
  private void Spielende(boolean sieger) {
    if (sieger) JOptionPane.showMessageDialog(null,"Spieler " + SpielerAmZug + " hat gewonnen");
      else JOptionPane.showMessageDialog(null,"unentschieden");

    //Spiel ist zu Ende
    spielende = true;
  }


  //Methode untersucht die aktuelle Spielsituation!!!

  public void Spielsituation(){
    boolean sieger = false;
    boolean unentschieden = false;
    //Zeile oder Reihe komplett?
    for (int i=0; i<3; i++)
    {
      if ((Feld[i][0]!=0) && (Feld[i][0]==Feld[i][1]) && (Feld[i][1]==Feld[i][2])) sieger = true;
      if ((Feld[0][i]!=0)&&(Feld[0][i]==Feld[1][i])&&(Feld[1][i]==Feld[2][i])) sieger = true;
    }

    //Diagonale komplett?
    if (((Feld[0][0]!=0)&&(Feld[0][0]==Feld[1][1])&&(Feld[1][1]==Feld[2][2])) ||
       ((Feld[0][2]!=0)&&(Feld[0][2]==Feld[1][1])&&(Feld[1][1]==Feld[2][0]))) sieger = true;

    //unentschieden - alle Felder belegt und kein Sieger?
    if ((Feld[0][0]!=0)&&(Feld[0][1]!=0)&&(Feld[0][2]!=0)&&
      (Feld[1][0]!=0)&&(Feld[1][1]!=0)&&(Feld[1][2]!=0)&&
      (Feld[2][0]!=0)&&(Feld[2][1]!=0)&&(Feld[2][2]!=0)) unentschieden = true;

    //Wenn es einen Sieger oder unentschieden gibt, gib die entsprechende Meldung aus, ansonsten wieterspielen.
    if (sieger) Spielende(true);
    else if (unentschieden) Spielende(false);
      else Spielerwechsel();


  }

  private void Spielzug(MouseEvent e) {
    //Mausklick wird nur akzeptiert, wenn Spiel noch läuft
    
    if (!spielende){
      // ungültige Initialisierung von Zeile und Spalte
      int Zeile = -1;
      int Spalte = -1;

      //Spielt eine KI oder spielt der Mensch?
      
      if (SpielerAmZug == 1 && KI1){
      
        ki1.setSituation(Feld);
        Zeile = ki1.getZeile();
        Spalte = ki1.getSpalte();

      } else if (SpielerAmZug == 2 && KI2) {

        //ki2.setSituation(Feld);
        //Zeile = ki2.getZeile();
        //Spalte = ki2.getSpalte();

      } else {

        //Spalte, in die geklickt wurde (0..2) lässt sich leicht berechnen
        Spalte = 3*e.getX() / this.getWidth();

        //Zeile leider nicht, da wieder der obere Rand dazugehört...
        if (e.getY() < (this.getHeight()-23)/3+23) Zeile = 0;
          else if (e.getY() < 2* (this.getHeight()-23)/3+23) Zeile = 1;
            else Zeile = 2;
      }

      //Array Feld mit dem neuen Eintrag belegen, falls schon belegt, Meldung machen
      if (Feld[Zeile][Spalte] == 0) {
        Feld[Zeile][Spalte] = SpielerAmZug;
        repaint();
        Spielsituation();
      }
        else JOptionPane.showMessageDialog(null,"Feld ist schon belegt! Wähle ein anderes Feld aus.");
    }

  }

  // Anfang Ereignisprozeduren

  public void paint(Graphics g) {
    //Hier wird das Spielfeld gezeichnet
    g.setColor(Color.white);
    g.fillRect(0,0,this.getWidth(),this.getHeight());
    g.setColor(Color.black);
    g.drawLine(0,(this.getHeight()-23)/3+23,this.getWidth(),(this.getHeight()-23)/3+23); //Ausgleich um 23 Pixel, da Rahmen oben zur Höhe mitzählt (23 Pixel)
    g.drawLine(0, 2* (this.getHeight()-23)/3+23,this.getWidth(),2* (this.getHeight()-23)/3+23);
    g.drawLine(this.getWidth()/3,0,this.getWidth()/3,this.getHeight());
    g.drawLine(2*this.getWidth()/3,0,2*this.getWidth()/3,this.getHeight());

    //und jetzt noch die gesetzten Kreise und Kreuze je nach Feldbelegung
    
    for (int i = 0; i<=2; i++)
      for (int j = 0; j<=2; j++) {
        switch (Feld[i][j]) {
          case 1 : {
            g.setColor(Color.blue);
            g.fillOval(j*this.getWidth()/3 +12 , i*((this.getHeight()-23)/3)+23 + 12, this.getHeight()/3 - 25 ,this.getHeight()/3 - 25 );
            break;
          }
           case 2 : {
            g.setColor(Color.red);
            g.fillOval(j*this.getWidth()/3 +12 , i*((this.getHeight()-23)/3)+23 + 12, this.getHeight()/3 - 25 ,this.getHeight()/3 - 25 );
          }
        }
      }



  }



  // Ende Ereignisprozeduren

  public static void main(String[] args) {
    new tictactoe("tictactoe");
  }
  // Ende Methoden
}
0 Likes

#2

Was genau ist die Frage? Den Code habe ich (nur formatiert aber) (noch?) nicht im Detail nachvollzogen, aber grundsätzlich wird man für die Überprüfung, wer gewonnen hat, schon einige if-Abfragen brauchen. Die kann man vielleicht kompakter, eleganter oder sonstwie “schöner” schreiben. Aber wenn da eine KI drum soll, sind die if-Abfragen das geringste Problem. Dann ist viel wichtiger, dass diese Abfragen sauber in einer Methode gekapselt sind, z.B. als

int computeWinner() {
    // Gibt zurück, wer gewonnen hat: 1 oder 2, oder 0 für unentschieden, 
    // und vielleicht -1 für "NOCH niemand" oder so...
    ...
}

Ein bißchen relevant könnte https://wiki.byte-welt.net/wiki/Entwurfsmuster_(Design_Patterns)#Beispiel_von_TicTacToe hier sein, aber das sollte nicht als “Referenz” für ein eigenes TicTacToe angesehen werden - dort geht es eher darum, ein kleines (aber eben nicht mehr ganz triviales) MVC-Beispiel zu zeigen.

0 Likes