# GUI-Button Inhalte vergleich - TicTacToe Grundriss



## kuzdu (23. Sep 2011)

Hi Leute,
habe eine allgemeine Frage für die Vorgehensweise für ein grafisches TicTacToe-Spiel und ein akutes Problem mit Button, was (meiner Meinung nach) ein wenig zusammenhängt.

(1)
Zuerst das Problem:
Ich habe zwei Buttons, die geklickt werden sollen. Wenn sie geklickt wurden, sollen sie den Text ändern und zwar von "Klick mich" in "x".
Wenn dieser Zustand erreicht wird, soll in einem Label stehen: "super, alle Buttons sind X"

Der Fehler ist im Code markiert und unten ausgeführt, hier der Code:  


```
package tictactoe;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;



public class Tictactoe {

    JFrame frame;
    JLabel label;
    
    public static void main(String[] args) {
    
        Tictactoe gui = new Tictactoe();
        gui.los();
        
    }
    
    public void los() {
        
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        JButton einButton = new JButton("Klick mich");
        einButton.addActionListener(new testListener());
        
        JButton zweiButton = new JButton("Klick mich");
        zweiButton.addActionListener(new testZweiListener());
        
        label = new JLabel("Ich bin ein Label");
        
        frame.getContentPane().add(BorderLayout.SOUTH, einButton);
        frame.getContentPane().add(BorderLayout.NORTH, zweiButton);
        frame.getContentPane().add(BorderLayout.CENTER, label);
        
        frame.setSize(420,300);
        frame.setVisible(true);
        
    }
    
    class testListener implements ActionListener {
        
        public void actionPerformed(ActionEvent event) {
            
           label.setText("blalalal"); //Funktioniert bei Knopfdruck
           einButton.setText("X"); //HIER KOMMT EIN FEHLER
        }
        
    }
    class testZweiListener implements ActionListener {
        
        public void actionPerformed(ActionEvent event) {
            
            //selber Code wie oben, nur anderer Button
        }
        
    }
    
    
}
```

Also der Compiler sagt: Fehlermeldung: cannot find symbol,  symbol:   variable einButton,  location: class tictactoe.TicTacToe.testListener

Also anscheinend findet der Compiler nicht den einButton bzw. kann nicht darauf zugreifen. Das verwundert mich aber iwie, weil das label.setText funktioniert. Was habe ich übersehen?

(2)
Vorrausgesetzt das "Programm" funktioniert wie ich es möchte:
Wie gehe ich jetzt vor, wenn ich kontrollieren möchte, ob alle Buttons X sind? 
Baue ich eine if-Abfrage bei jedem Event ein? Mit einer Anweisung so ähnlich: if(einButton.Text == "X" && zweiButton.Text == "X") {mach das} else {nichts unternehmen}


(3)
Jetzt die allgemeine Frage:
Ist mein prinzipielles Vorgehen für ein TicTacToe-Spiel richtig? Mir kommt das wahnsinnig kompliziert vor. Ich müsste ja in ein Gitternetz mit 9 Buttons mit je einem Event einbauen. Und beim TicTacToe muss ich ja deutlich mehrere Abfragen machen, weil es ja einige Gewinnmöglichkeiten gibt.
Was gibt es hier für Alternativen bzw. einfach gehaltenere Lösungen? Wie funktioniert sowas mit noch komplexeren Programmen wie z.B. Vier gewinnt oder einem Sudoko-Löser?


Vielen Dank für Antworten 


Gruß


----------



## Gast2 (23. Sep 2011)

zu 1)
Du hast label als Klassenvariable deklariert. einButton hingegen ist nur im Konstruktor sichtbar.

zu 2)
Ja könntest du so machen, Strings vergleicht man allerdings mit equals. Besser wäre aber du legst die Buttons in ein Array.



> Ist mein prinzipielles Vorgehen für ein TicTacToe-Spiel richtig? Mir kommt das wahnsinnig kompliziert vor. Ich müsste ja in ein Gitternetz mit 9 Buttons mit je einem Event einbauen. Und beim TicTacToe muss ich ja deutlich mehrere Abfragen machen, weil es ja einige Gewinnmöglichkeiten gibt.
> Was gibt es hier für Alternativen bzw. einfach gehaltenere Lösungen? Wie funktioniert sowas mit noch komplexeren Programmen wie z.B. Vier gewinnt oder einem Sudoko-Löser?


Jo ist richtig, du machst das schon ziemlich kompliziert 
Ein erster Schritt wäre z.b. die Buttons in ein Array oder eine Liste zu legen. Die Buttons kannst du dir dann in einer (Factory-)Methode erstellen lassen, komplett mit Listener etc.


----------



## kuzdu (24. Sep 2011)

Hallo, vielen Dank für deine Antworten!

(1)
Habe ich jetzt so gelöst, dass ich es auch als Klassenvariable deklariert habe:

```
public class TicTacToe {

    JFrame frame;
    JLabel label;
    JButton einButton;

//.....restlicher Code
}
```

(2)

Zwei ist mir in der Theorie dann auch klar, das habe ich sogar schonmal iwo gemacht, wusste nur nicht, dass es mit Grafik genauso gehändelt wird.

(3) 
Hier muss ich mich ehrlich gesagt erst mal reinarbeiten, weil ich nicht (mehr) genau weiß, wie ich eine Liste usw. erstelle in Kombination mit Buttons.

Was genau ist eine (Factory)-Methode? (also ich frage morgen auch mal google, aber vllt hast ja auch Bock das hinzuschreiben.)

(4)
Jetzt tut sich mir allerdings eine Frage auf.

Wäre es nicht eign. ganz klug, eine einmalige Methode zu schreiben, die alle Kobinationen im TicTacToe abfragt und die dann immer beim Event zu checken?

Also quasi so:

```
public void actionPerformed(ActionEvent event) {
            
          einButton.setText("X"); 
          kontrolliereObJemandGewonnen();
        }
```
 
Ist der Ansatz so richtig? (Also ich probiere es wie gesagt noch selbst aus, aber fragen kostet ja nichts )

Gruß


----------



## GUI-Programmer (24. Sep 2011)

Also das mit den Buttons in einem Array und die dazugehörigen Aktionen:


```
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;

public class Test {
    private MyActions myActions;
    private JButton[] btn = new JButton[9];
    
    public Test() {
        myActions = new MyActions();
        
        for(int i=0; i<btn.length; i++) {
            btn[i] = new JButton("Klick mich");
            btn[i].addActionListener(myActions);
        }
    }
    
    private class MyActions implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent evt) {
            Object src = evt.getSource();
            if(src.equals(btn[0])) {
                // Action des 1.Buttons
            }
            if(src.equals(btn[1])) {
                // Action des 2.Buttons
            }
            // .......
        }
    }
}
```

Noch als kleiner Tipp:
Ich würde ein JPanel erstellen und ihm das GridLayout verpassen, dass ist nämlich für TicTacToe ideal, und die 9 JButtons dann diesem Panel hinzufügen!


----------



## kuzdu (4. Okt 2011)

Hallo,
ist zwar schon ein bisschen her, aber ich habe mich nochmal an die Kontrolle, der möglichen TicTacToe Lösungen gemacht, weil ich meine Abfrage der Lösungen iwie viel zu undynamisch fand.
Dabei ist ein kleines Problem aufgetreten. Und zwar bereitet mir die Abfrage, ob es in der Waagerechten und in der Senkrechten drei gleiche Zeichen gibt, keine Schwierigkeiten. In der Diagonale jedoch kommt es immer zu einem Fehler. Hier erstmal der Code:

(Der Code bezieht sich in keinster Weise auf den obigen Code, ich will damit nur mein Lösungsprinzip deutlicher machen.)  


```
package kontrollearray;

public class KontrolleArray {

	
	
	public static void main(String[] args) {
	
		
        //Mögliche Lösung (das X gewinnt mit einer Diagonalen)		
	String[] reihe1 = { "o","o","x" };
	String[] reihe2 = { "o","x","o" };
	String[] reihe3 = { "x","o","x" };

	//Counter, der zählt, wie viel Kreuze bzw. Kreise senkrecht, diagonal oder waagerecht sind
	int counter = 0;

	boolean gewonnen = false;
	
	
	
	
	do{		
	for(int i = 0; i < reihe1.length; i++)
	{
		
		//Waagerechtsprüfung
		if(reihe1[i] == reihe1[i+1] || reihe2[i] == reihe2[i+1] || reihe3[i] == reihe3[i+1])
		{
		counter = counter +1;
		}
		//Längsprüfung
		else if(reihe1[i] == reihe2[i] && reihe2[i] == reihe3[i])
		{
		counter = counter +1;
		}
		//Diagonale
		else if(reihe1[i] == reihe2[i+1] && reihe2[i+1] == reihe3[i+2])
		{
		counter = counter +1;
		System.out.println(counter);	
		}
		else if(reihe1[i] == reihe2[i+1] && reihe2[i+1] == reihe3[i])
		{
		counter = counter +1;
		System.out.println(counter);	
		}
		else
		{
		counter = 0;
		gewonnen = true;
		}
		
				
		if(counter == 2)
		{
			System.out.println("Drei/Vier in einer Reihe");
			gewonnen = true;
			counter = 0;
			break;
		}
		
	}
	}
	while(gewonnen == false);
	
	}

}
```
Das Prinzip ist eben, dass die Arrays die gleich und hintereinander sind ( "o" oder "x") gezählt werden, wenn sie die Kette jedoch unterbrochen wird, dann wird der Counter wieder auf 0 gesetzt und die Zählung beginnt von vorne.

Bei der Diagonalen kommt es jedoch zu einem Fehler: java.lang.ArrayIndexOutOfBoundsException
Den kann ich auch nachvollziehen, es wird halt versucht auf ein Arrayinhalt zuzugreifen, der nicht existiert.

```
else if(reihe1[i+2] == reihe2[i+1] && reihe2[i+1] == reihe3[i])
```
Wenn i in diesem Fall halt 1 ist und nochmal plus 2 gerechnet wird, soll theoretisch Array[3] verglichen werden, dieses existiert aber nicht.

Fällt euch eine bessere Lösung dazu ein?

Freudlichen Gruß


----------



## Michael... (4. Okt 2011)

Zunächst ein paar Hinweise:
 - Objekte vergleicht man mit equals, vergleiche wie 
	
	
	
	





```
"x"=="x"
```
 können in die Hose gehen
    ==> 
	
	
	
	





```
"x".equals("x")
```
 - man kann Arrays verschachteln: 
	
	
	
	





```
String[][] feld
```

Da Deine Auswertung (die so nicht funktionieren wird) ja relativ fix auf ein 3x3 Spielfeld ausgelegt ist,  könntest die Felder ja direkt abfragen:

```
if ((reihe1[0].equals(reihe2[1]) && reihe2[1].equals(reihe2[2]) || (reihe3[0].equals(reihe2[1])...
```

Üblicher Weise und insbesondere bei solchen Spielen verwendet man ein Datenmodel und trennt das von der Darstellung. Man könnte z.B. mit einem int[][] oder arbeiten und z.B. eine 0 als frei, eine 1 als "X" und eine 2 als "O" darstellen.


----------



## GUI-Programmer (5. Okt 2011)

> Michael: [...]könntest die Felder ja direkt abfragen:[...]


Das wäre bei TicTacToe nun wirklich die einfachste Lösung, da es ja nur 8 Fälle gibt, bei denen jemand gewonnen hat (3x senkrecht, 3x waagrecht, und 2x die Diagonalen).


----------



## kuzdu (7. Okt 2011)

Michael... hat gesagt.:


> Zunächst ein paar Hinweise:
> - Objekte vergleicht man mit equals, vergleiche wie
> 
> 
> ...



Hi, danke für den Ansatz, allerdings verstehe ich ihn nicht genau. Wonach kann ich mal googlen oder hast du vllt iein Code, wo mit Datenmodelen gearbeitet wird, den ich mal anschauen kann?
Was meinst du mit einem int[][], ist das dann ein zweidimensionales Array?
Verstehe zwar das Prinzip, wie du es meinst, weiß aber nicht wie ich das in Java umsätze. Eine kleiner Denkanstoß wäre cool.

Nicht, dass ich mich jetzt irre, aber ein Algorithmus wie ich Diagonalen abfragen kann ist immer noch nicht gegeben oder?

Ich möchte die Abfrage nicht einzeln machen, bzw. alle Möglichkeiten "per Hand" angeben. Bei einem Feld 7*8 sähen die Möglichkeiten zum Gewinnen schon ganz anders aus.

Grüße

Kuzdu


----------



## Michael... (7. Okt 2011)

kuzdu hat gesagt.:


> Was meinst du mit einem int[][], ist das dann ein zweidimensionales Array?


Ich bezeichne sowas eher als verschachteltes Array, aber zweidimensional ist die wohl gängigere Bezeichnung.


kuzdu hat gesagt.:


> Ich möchte die Abfrage nicht einzeln machen, bzw. alle Möglichkeiten "per Hand" angeben. Bei einem Feld 7*8 sähen die Möglichkeiten zum Gewinnen schon ganz anders aus.


korrekt, und die Überprüfung ist wesentlich komplexer. Man könnte in vier Schleifen jeweils die Horizontale, Vertikale und die zwei Diagonalen prüfen. Letztere sind nicht unbedingt einfach zu prüfen - soweit ich mich an mein Vier-Gewinnt erinnern kann...


----------



## Michael... (7. Okt 2011)

Hier mal ein simples Beispiel mit einem int[][] als "Model". Der Nutzen ist vielleicht nicht direkt ersichtlich und erfahrene Programmierer würden vermutlich die Hände über dem Kopf zusammenschlagen... (professioneller wäre z.B. die Anwendung eines MVC Patterns)
Ich hoffe aber, dass dennoch deutlich wird, dass hiermit bei der (nicht implementierten) Prüfung ein einfach int Vergleich ausreicht und die Darstellung der "Spielsteine" 
	
	
	
	





```
playSymbol
```
 beliebig ohne grossartigen Eingriff in den Code ausgetauscht werden kann.

```
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class SimpleDemo extends JFrame {
	private int[][] field;
	private JLabel[][] labels;
	private int rows, cols;
	private int currentPlayer;
	private String[] playSymbol = {"A", "B"};
	
	public SimpleDemo() {
		rows = 5;
		cols = 8;
		field = new int[rows][cols];
		
		final JPanel panel = new JPanel(new GridLayout(rows, cols));
		labels = new JLabel[rows][cols];
		for (int row = 0; row<rows; row++) {
			for (int col = 0; col<cols; col++) {
				labels[row][col] = new JLabel("", JLabel.CENTER);
				labels[row][col].setBorder(BorderFactory.createEtchedBorder());
				panel.add(labels[row][col]);
			}
		}
		this.getContentPane().add(panel);
		
		panel.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount()>=2) {
					int w = panel.getWidth();
					int h = panel.getHeight();
					field[e.getY()*rows/h][e.getX()*cols/w] = currentPlayer +1;
					currentPlayer = ++currentPlayer%playSymbol.length;
					refreshField();
				}
			}
		});	
	}
	
	private void refreshField() {
		for (int r = 0; r<field.length; r++) {
			for (int c = 0; c<field[r].length; c++) {
				if (field[r][c]>0)
					labels[r][c].setText(playSymbol[field[r][c]-1]);
			}
		}
	}
	
	public static void main(String[] args) {
		JFrame frame = new SimpleDemo();
		frame.setBounds(0, 0, 400, 260);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	}
}
```


----------



## Marco13 (7. Okt 2011)

Michael... hat gesagt.:


> Hier mal ein simples Beispiel mit einem int[][] als "Model". Der Nutzen ist vielleicht nicht direkt ersichtlich und erfahrene Programmierer würden vermutlich die Hände über dem Kopf zusammenschlagen... (professioneller wäre z.B. die Anwendung eines MVC Patterns)
> Ich hoffe aber, dass dennoch deutlich wird, dass hiermit bei der (nicht implementierten) Prüfung ein einfach int Vergleich ausreicht und die Darstellung der "Spielsteine"
> 
> 
> ...



Ja, das wäre der wichtigste Punkt. Kommt ja öfter mal vor, dass jemand versucht, ein TicTacToe zu implementieren, oder ein Memory oder Vier Gewinnt, und dann versucht wird mit 
someButton.equals(firstButton) && ...
oder 
someButton.getText().equals(firstButton.getText()) && ...
oder gar Krämpfen wie
((ImageIcon)someButton.getIcon()).getImage().equals(((ImageIcon)firstButton.getIcon()).getImage() && ...
irgendwelche Muster (Gewinnsituationen) zu erkennen. Ein Modell (und sei es "nur" ein Int-Array, das kann OK sein) einigermaßen getrennt vom GUI ist schon wichtig.

Das mit dem TicTacToe kommt genaugenommen so oft vor, dass ich das mal in diesem MVC-Tutorial ( http://www.java-forum.org/allgemeines/91829-mvc.html#post595758 ) verwendet habe, AAABER ...WICHTIG @kuzdu : Das dort verlinkte TicTacToe soll NICHT als Beispielimplementierung für ein "gutes TicTacToe" angesehen werden. Genaugenommen ist es so, wie es dort beschrieben ist, eigentlich unnötig kompliziert. Dort ging es nur um das MVC-Prinzip. Das wiederum braucht mal sehr oft, in ähnlicher Form an vielen Stellen vor, und einige Prinzipien davon lassen sich auch auf Spiele übertragen (auch wenn das nicht immer der Fall ist, und Spiele eigentlich nicht die "klassische" Domäne für MVC sind).


----------



## kuzdu (8. Okt 2011)

Hi, vielen Dank für die zahlreichen Beiträge!
Helfen mir weiter und ich werde mich jetzt schön einlesen und vllt dann auch zum Schluss mein gezieltes Ergebnis erreichen. 
Also Dank!


----------

