Kleine Änderungen an der GUI + Implementierung der AI mittels Minmax

Algorithmus
main
Matias Mas Viehl 2025-02-11 14:57:44 +01:00
parent 6fb91240da
commit 09ad9aa452
3 changed files with 354 additions and 200 deletions

View File

@ -9,7 +9,9 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
@ -17,165 +19,188 @@ import javax.swing.SwingUtilities;
import Logik.TicTacToe;
public class GUI extends JFrame{
public class GUI extends JFrame {
private TicTacToe ttt;
private ArrayList<JButton> buttons = new ArrayList<>();
private JButton neuesSpiel = new JButton();
private JButton konsole = new JButton();
private boolean spielEnde = false;
private JButton modus = new JButton();
public GUI(TicTacToe ttt) {
this.ttt = ttt;
this.setTitle("TicTacToe");
this.setSize(500,600);
this.setSize(500, 600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setLayout(new BorderLayout());
ImageIcon img = new ImageIcon(getClass().getResource("/icon.jpg"));
if (img.getIconWidth() == -1) {
System.out.println("Bild konnte nicht geladen werden!");
} else {
this.setIconImage(img.getImage());
}
fuelleArrayList();
JPanel jp2 = new JPanel();
jp2.setLayout(new BorderLayout());
jp2.setPreferredSize(new Dimension(500,150));
jp2.setLayout(new GridLayout(3,1));
jp2.setPreferredSize(new Dimension(500, 150));
neuesSpiel.setFocusPainted(false);
neuesSpiel.setFont(new Font("Arial",Font.PLAIN, 30));
neuesSpiel.setBackground(new Color(65,65,65));
neuesSpiel.setFont(new Font("Arial", Font.PLAIN, 30));
neuesSpiel.setBackground(new Color(65, 65, 65));
neuesSpiel.setForeground(Color.WHITE);
neuesSpiel.setText("Neues Spiel beginnnen?");
neuesSpiel.setPreferredSize(new Dimension(500,75));
neuesSpiel.setPreferredSize(new Dimension(500, 75));
neuesSpiel.setBorderPainted(false);
neuesSpiel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ttt.setCounter(0);
ttt.setAktuellerSpieler('X');
spielEnde = false;
ttt.fuelleTreeMap();
for(JButton b: buttons) {
b.setForeground(Color.WHITE);
b.setText("");
}
@Override
public void actionPerformed(ActionEvent e) {
ttt.resetGame();
resetGui();
revalidate();
repaint();
}
}
});
jp2.add(neuesSpiel);
modus.setFocusPainted(false);
modus.setFont(new Font("Arial", Font.PLAIN, 30));
modus.setBackground(new Color(90, 90, 90));
modus.setForeground(Color.WHITE);
jp2.add(neuesSpiel, BorderLayout.NORTH);
if(ttt.getModus() == 1) modus.setText("Modus wechseln? (Player vs. Player aktiv)");
else if(ttt.getModus() == 2) modus.setText("Modus wechseln? (Player vs. AI aktiv)");
modus.setPreferredSize(new Dimension(500, 75));
modus.setBorderPainted(false);
modus.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (ttt.getModus() == 2) {
modus.setText("Modus wechseln? (Player vs. Player aktiv)");
ttt.setModus(1);
} else if (ttt.getModus() == 1) {
modus.setText("Modus wechseln? (Player vs. AI aktiv)");
ttt.setModus(2);
}
for (JButton b : buttons) {
b.setForeground(Color.WHITE);
b.setText("");
}
ttt.resetGame();
revalidate();
repaint();
}
});
jp2.add(modus);
konsole.setFocusPainted(false);
konsole.setFont(new Font("Arial",Font.PLAIN, 30));
konsole.setBackground(new Color(90,90,90));
konsole.setFont(new Font("Arial", Font.PLAIN, 30));
konsole.setBackground(new Color(65, 65, 65));
konsole.setForeground(Color.WHITE);
konsole.setText("Zurück zur Konsole?");
konsole.setPreferredSize(new Dimension(500,75));
konsole.setPreferredSize(new Dimension(500, 75));
konsole.setBorderPainted(false);
konsole.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
dispose();
ttt.ermittleEingabeAnfang();
} catch (Exception e1) {
System.out.println("Eingabe von GUI zur Konsole konnte nicht aufgerufen werden");
}
}
});
jp2.add(konsole, BorderLayout.SOUTH);
jp2.add(konsole);
this.add(jp2, BorderLayout.SOUTH);
this.setVisible(true);
}
private void fuelleArrayList() {
JPanel jp = new JPanel(new GridLayout(3,3));
for(int i = 0; i < 9; i++) {
JPanel jp = new JPanel(new GridLayout(3, 3));
for (int i = 0; i < 9; i++) {
JButton b = new JButton();
//System.out.println("Button "+i+" wurde erstellt.");
b.setFocusPainted(false);
b.setFont(new Font("Arial",Font.PLAIN, 70));
if(i%2 == 0) {
b.setBackground(new Color(90,90,90));
}else {
b.setBackground(new Color(65,65,65));
b.setFont(new Font("Arial", Font.PLAIN, 70));
if (i % 2 == 0) {
b.setBackground(new Color(90, 90, 90));
} else {
b.setBackground(new Color(65, 65, 65));
}
b.setForeground(Color.WHITE);
b.setBorderPainted(false);
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(() -> {
actionListenerInhalt(b);
});
}
@Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(() -> {
ttt.actionListenerInhalt(b);
//System.out.println("Button "+buttons.indexOf(b)+" wurde gedrückt!");
});
}
});
b.setVisible(true);
buttons.add(b);
jp.add(b);
this.add(jp);
}
}
private void actionListenerInhalt(JButton b) {
public void resetGui(){
int index = buttons.indexOf(b);
if (!ttt.pruefeBelegtesFeld(index) && !spielEnde) {
ttt.belegteFelder.put(index, ttt.getAktuellerSpieler());
b.setText("" + ttt.getAktuellerSpieler());
for (JButton b : buttons) {
if (ttt.getCounter() >= 4 && ttt.pruefeSieg()) {
spielEnde = true;
int[] gewinnerIndexe = ttt.getGewinnerIndexe();
b.setForeground(Color.WHITE);
b.setText("");
for (int i = 0; i < 3; i++) {
buttons.get(gewinnerIndexe[i]).setForeground(Color.RED);
buttons.get(gewinnerIndexe[i]).setText("" + ttt.getAktuellerSpieler());
}
}
}
}
ttt.setCounter(ttt.getCounter() + 1);
public ArrayList<JButton> getButtons() {
return buttons;
}
if (ttt.getAktuellerSpieler() == 'X') {
ttt.setAktuellerSpieler('O');
} else {
ttt.setAktuellerSpieler('X');
}
repaint();
}
public void setButtons(ArrayList<JButton> buttons) {
this.buttons = buttons;
}
}

View File

@ -1,73 +1,115 @@
package Logik;
import java.awt.Color;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.Random;
import java.util.Scanner;
import java.util.TreeMap;
import javax.swing.JButton;
import Anzeige.GUI;
public class TicTacToe {
public TreeMap<Integer, Character> belegteFelder = new TreeMap<>();
private int counter, ausgewaehltesFeld;
private GUI gui;
private char aktuellerSpieler;
private TreeMap<Integer, Character> belegteFelder = new TreeMap<>();
private int[] gewinnerIndexe = new int[3];
private int counter, ausgewaehltesFeld, modus;
private char aktuellerSpieler;
private boolean aiIstDran;
public TicTacToe() throws Exception {
ermittleEingabeAnfang();
}
public void ermittleEingabeAnfang() throws Exception {
//setzt praktisch werte zurück
fuelleTreeMap();
aktuellerSpieler = 'X';
counter = 0;
resetGame();
Scanner s = new Scanner(System.in);
System.out.println("\nWollen Sie mit einer GUI('gui' eingeben) oder mit der Konsole('konsole' eingeben) spielen?\nZum beenden einfach 'ende' eingeben.");
System.out.println(
"\nWollen Sie mit einer GUI('gui' eingeben) oder mit der Konsole('konsole' eingeben) spielen?\nZum beenden einfach 'ende' eingeben.");
System.out.print(">>");
String befehl = s.nextLine();
if (befehl.equalsIgnoreCase("gui")) {
System.out.println("Um ein Feld für ein Zug auszuwählen muss dieses Feld angeklickt werden...");
ermittleModus();
gui = new GUI(this);
if (modus == 1 || modus == 2) {
if (modus == 2)
System.out.println("Sie sind X die AI ist O.");
System.out.println("Um ein Feld für ein Zug auszuwählen muss dieses Feld angeklickt werden...");
gui = new GUI(this);
}
} else if (befehl.equalsIgnoreCase("konsole")) {
ermittleModus();
System.out.println(
"Die Felder sind von 0 bis 8 durchnummerriert.\nUm ein Feld für ein Zug auszuwählen muss die Zahl des Feldes eingegeben werden...");
gibAktuellenStandAus();
System.out.println("Die Felder sind von 0 bis 8 durchnummerriert.\nUm ein Feld für ein Zug auszuwählen muss die Zahl des Feldes eingegeben werden...");
for (int i = 0; i < 9; i++) {
erfasseEingabe();
gibAktuellenStandAus();
if (i >= 4 && pruefeSieg()) break;
if (aktuellerSpieler == 'X')aktuellerSpieler = 'O';
else aktuellerSpieler = 'X';
if (i >= 4 && werHatGewonnen(belegteFelder) != null)
break;
wechselAktuellerSpieler();
}
if (werHatGewonnen(belegteFelder) == 'D')
System.out.println("Unentschieden!");
else
System.out.println(aktuellerSpieler + " hat gewonnen!");
ermittleEingabeAnfang();
}else if(befehl.equalsIgnoreCase("ende")) {
} else if (befehl.equalsIgnoreCase("ende")) {
System.out.println("Spiel wird beendet.");
System.exit(0);
}else {
} else {
System.out.println("Eingabe ungültig!\nVersuchen Sie es erneut.");
ermittleEingabeAnfang();
}
}
public void fuelleTreeMap() {
private void ermittleModus() {
Scanner s = new Scanner(System.in);
System.out.println(
"Geben Sie die jeweilige Zahl für den Spielmodus ein: (Player vs. Player -> 1), (Player vs. AI -> 2)");
System.out.print(">>");
try {
modus = s.nextInt();
if (modus < 1 || modus > 3) {
throw new InputMismatchException();
}
} catch (InputMismatchException e) {
System.out.println("Es wurde keine gültige Eingabe getätigt!");
ermittleModus();
}
}
private void fuelleTreeMap() {
for (int i = 0; i < 9; i++) {
belegteFelder.put(i, (char) (48 + i));
}
@ -76,8 +118,9 @@ public class TicTacToe {
private void gibAktuellenStandAus() {
System.out.println("\n-------------");
for (int i = 0; i < 9; i+=3) {
System.out.printf("|%2s |%2s |%2s |", belegteFelder.get(i), belegteFelder.get(i+1), belegteFelder.get(i+2));
for (int i = 0; i < 9; i += 3) {
System.out.printf("|%2s |%2s |%2s |", belegteFelder.get(i), belegteFelder.get(i + 1),
belegteFelder.get(i + 2));
System.out.println("\n-------------");
}
System.out.println();
@ -90,103 +133,189 @@ public class TicTacToe {
System.out.print("\nSpieler " + aktuellerSpieler + " ist am Zug!\nGeben Sie eine Feldnummer ein:\n>>");
try {
ausgewaehltesFeld = s.nextInt();
if (modus == 1 || modus == 2 && aktuellerSpieler == 'X')
ausgewaehltesFeld = s.nextInt();
if (modus == 2 && aiIstDran || modus == 3) {
ausgewaehltesFeld = makeTurn();
}
System.out.println("Die Eingabe: " + ausgewaehltesFeld);
if(pruefeBelegtesFeld(ausgewaehltesFeld)) throw new Exception();
if (pruefeBelegtesFeld(ausgewaehltesFeld))
throw new Exception();
} catch (Exception e) {
System.out.println("Es wurde keine passende Eingabe getätigt,\nversuchen Sie es erneut...");
erfasseEingabe();
if (modus == 1 || modus == 2 && aktuellerSpieler == 'X')
erfasseEingabe();
}
}
public synchronized boolean pruefeBelegtesFeld(int index) {
System.out.println("Zu pruefender Index: "+index);
private int makeTurn() {
int bestMove = -1;
int bestScore = Integer.MIN_VALUE; // Maximierung für AI (O)
for (int i = 0; i < 9; i++) {
if (belegteFelder.get(i) != 'X' && belegteFelder.get(i) != 'O') {
belegteFelder.put(i, 'O'); // Temporär setzen
int moveScore = minimax(new TreeMap<>(belegteFelder), false);
belegteFelder.put(i, (char) ('0' + i)); // Zug zurücksetzen
if (moveScore > bestScore) {
bestScore = moveScore;
bestMove = i;
}
}
}
return bestMove;
}
private int minimax(TreeMap<Integer, Character> board, boolean isMaximizing) {
Character winner = werHatGewonnen(board);
if (winner != null) {
if (winner == 'X')
return -1;
if (winner == 'O')
return 1;
if (winner == 'D')
return 0;
}
if (isMaximizing) {
int bestScore = Integer.MIN_VALUE;
for (int i = 0; i < 9; i++) {
if (board.get(i) != 'X' && board.get(i) != 'O') {
board.put(i, 'O');
int score = minimax(board, false);
board.put(i, (char) ('0' + i)); // Zug zurücksetzen
bestScore = Math.max(score, bestScore);
}
}
return bestScore;
} else {
int bestScore = Integer.MAX_VALUE;
for (int i = 0; i < 9; i++) {
if (board.get(i) != 'X' && board.get(i) != 'O') {
board.put(i, 'X');
int score = minimax(board, true);
board.put(i, (char) ('0' + i)); // Zug zurücksetzen
bestScore = Math.min(score, bestScore);
}
}
return bestScore;
}
}
private boolean pruefeBelegtesFeld(int index) {
// System.out.println("Zu pruefender Index: " + index+"| Inhalt:
// "+belegteFelder.get(index));
if (belegteFelder.get(index) == 'X' || belegteFelder.get(index) == 'O') {
System.out.println("Feld " + index + " schon belegt, machen Sie eine erneute Eingabe...");
return true;
} else {
belegteFelder.put(index, aktuellerSpieler);
}
belegteFelder.put(index, aktuellerSpieler);
System.out.println("Feld " + index + " wurde mit " + aktuellerSpieler + " belegt");
return false;
}
public boolean pruefeSieg() {
private Character werHatGewonnen(TreeMap<Integer, Character> board) {
if ((belegteFelder.get(0) == aktuellerSpieler) && (belegteFelder.get(1) == aktuellerSpieler) && (belegteFelder.get(2) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der ersten Waagerechte und hat somit gewonnen!");
gewinnerIndexe[0] = 0;
gewinnerIndexe[1] = 1;
gewinnerIndexe[2] = 2;
return true;
} else if ((belegteFelder.get(3) == aktuellerSpieler) && (belegteFelder.get(4) == aktuellerSpieler) && (belegteFelder.get(5) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der zweiten Waagerechte und hat somit gewonnen!");
gewinnerIndexe[0] = 3;
gewinnerIndexe[1] = 4;
gewinnerIndexe[2] = 5;
return true;
} else if ((belegteFelder.get(6) == aktuellerSpieler) && (belegteFelder.get(7) == aktuellerSpieler) && (belegteFelder.get(8) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der dritten Waagerechte und hat somit gewonnen!");
gewinnerIndexe[0] = 6;
gewinnerIndexe[1] = 7;
gewinnerIndexe[2] = 8;
return true;
} else if ((belegteFelder.get(0) == aktuellerSpieler) && (belegteFelder.get(3) == aktuellerSpieler) && (belegteFelder.get(6) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der ersten Senkrechte und hat somit gewonnen!");
gewinnerIndexe[0] = 0;
gewinnerIndexe[1] = 3;
gewinnerIndexe[2] = 6;
return true;
} else if ((belegteFelder.get(1) == aktuellerSpieler) && (belegteFelder.get(4) == aktuellerSpieler) && (belegteFelder.get(7) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der zweiten Senkrechte und hat somit gewonnen!");
gewinnerIndexe[0] = 1;
gewinnerIndexe[1] = 4;
gewinnerIndexe[2] = 7;
return true;
} else if ((belegteFelder.get(2) == aktuellerSpieler) && (belegteFelder.get(5) == aktuellerSpieler) && (belegteFelder.get(8) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der dritten Senkrechte und hat somit gewonnen!");
gewinnerIndexe[0] = 2;
gewinnerIndexe[1] = 5;
gewinnerIndexe[2] = 8;
return true;
} else if ((belegteFelder.get(0) == aktuellerSpieler) && (belegteFelder.get(4) == aktuellerSpieler) && (belegteFelder.get(8) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der ersten Diagonale und hat somit gewonnen!");
gewinnerIndexe[0] = 0;
gewinnerIndexe[1] = 4;
gewinnerIndexe[2] = 8;
return true;
} else if ((belegteFelder.get(2) == aktuellerSpieler) && (belegteFelder.get(4) == aktuellerSpieler) && (belegteFelder.get(6) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der zweiten Diagonale und hat somit gewonnen!");
gewinnerIndexe[0] = 2;
gewinnerIndexe[1] = 4;
gewinnerIndexe[2] = 6;
return true;
int[][] gewinnKombinationen = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 }, // Reihen
{ 0, 3, 6 }, { 1, 4, 7 }, { 2, 5, 8 }, // Spalten
{ 0, 4, 8 }, { 2, 4, 6 } // Diagonalen
};
for (int[] kombi : gewinnKombinationen) {
if (board.get(kombi[0]) == board.get(kombi[1]) && board.get(kombi[1]) == board.get(kombi[2])
&& (board.get(kombi[0]) == 'X' || board.get(kombi[0]) == 'O')) {
gewinnerIndexe = kombi;
return board.get(kombi[0]); // Gibt 'X' oder 'O' zurück
}
}
return false;
// Prüfen, ob noch leere Felder existieren
for (int i = 0; i < 9; i++) {
if (board.get(i) != 'X' && board.get(i) != 'O') {
return null; // Es gibt noch leere Felder → Spiel geht weiter
}
}
return 'D';
}
public void setAktuellerSpieler(char aktuellerSpieler) {
this.aktuellerSpieler = aktuellerSpieler;
public void actionListenerInhalt(JButton b) {
int index = gui.getButtons().indexOf(b);
if (werHatGewonnen(belegteFelder) == null && !pruefeBelegtesFeld(index)) {
b.setText("" + aktuellerSpieler);
if (counter >= 4 && werHatGewonnen(belegteFelder) != null) {
if (werHatGewonnen(belegteFelder) != 'D') {
for (int i = 0; i < 3; i++) {
gui.getButtons().get(gewinnerIndexe[i]).setForeground(Color.RED);
gui.getButtons().get(gewinnerIndexe[i]).setText("" + aktuellerSpieler);
}
}
return;
}
counter++;
wechselAktuellerSpieler();
if (modus == 2 && aiIstDran) {
actionListenerInhalt(gui.getButtons().get(makeTurn()));
}
}
}
public char getAktuellerSpieler() {
return aktuellerSpieler;
private void wechselAktuellerSpieler() {
if (aktuellerSpieler == 'X') {
aktuellerSpieler = 'O';
if (modus == 2)
aiIstDran = true;
} else {
aktuellerSpieler = 'X';
if (modus == 2)
aiIstDran = false;
}
}
public TreeMap<Integer, Character> getBelegteFelder() {
return belegteFelder;
public void resetGame() {
counter = 0;
aktuellerSpieler = 'X';
aiIstDran = false;
fuelleTreeMap();
}
public int[] getGewinnerIndexe() {
return gewinnerIndexe;
public int getModus() {
return modus;
}
public int getCounter() {
return counter;
public void setModus(int modus) {
this.modus = modus;
}
public void setCounter(int counter) {
this.counter = counter;
public boolean isAiIstDran() {
return aiIstDran;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB