Compare commits

..

9 Commits
master ... main

Author SHA1 Message Date
matias-mas-viehl 89b09af588 . 2025-04-02 20:02:16 +02:00
Matias Mas Viehl b91b67dd1b In der Konsole die Auswahl wer beginnt hinzugefügt 2025-03-22 11:40:23 +01:00
Matias Mas Viehl 09ad9aa452 Kleine Änderungen an der GUI + Implementierung der AI mittels Minmax
Algorithmus
2025-03-22 11:40:23 +01:00
Matias Mas Viehl 6fb91240da README.md aktualisiert 2025-01-09 13:09:54 +01:00
Matias Mas Viehl 1dddf81176 kleine letze Änderungen 2025-01-09 12:13:48 +01:00
Matias Mas Viehl 63089a832a . 2025-01-04 15:59:34 +01:00
Matias Mas Viehl b69dc9fcb1 kleine änderungen 2025-01-04 15:59:26 +01:00
Matias Mas Viehl df670019b7 Merge branch 'master' 2025-01-04 14:41:25 +01:00
Matias Mas Viehl 27265f2530 Initial commit 2024-12-10 08:37:26 +01:00
6 changed files with 465 additions and 209 deletions

62
.gitignore vendored 100644
View File

@ -0,0 +1,62 @@
# ---> Eclipse
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# CDT- autotools
.autotools
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Annotation Processing
.apt_generated/
.apt_generated_test/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
# Uncomment this line if you wish to ignore the project description file.
# Typically, this file would be tracked if it contains build/dependency configurations:
#.project

3
README.md 100644
View File

@ -0,0 +1,3 @@
# TicTacToe
Semesteraufgabe von Matias Mas Viehl für Pr1.

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;
@ -23,7 +25,7 @@ public class GUI extends JFrame{
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) {
@ -35,10 +37,17 @@ public class GUI extends JFrame{
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.setLayout(new GridLayout(3,1));
jp2.setPreferredSize(new Dimension(500, 150));
neuesSpiel.setFocusPainted(false);
@ -50,15 +59,42 @@ public class GUI extends JFrame{
neuesSpiel.setBorderPainted(false);
neuesSpiel.addActionListener(new ActionListener() {
@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);
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) {
ttt.setCounter(0);
ttt.setAktuellerSpieler('X');
spielEnde = false;
ttt.fuelleTreeMap();
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) {
@ -67,16 +103,20 @@ public class GUI extends JFrame{
}
ttt.resetGame();
//if(ttt.getAktuellerSpieler() == 'O' && ttt.isAiIstDran()) ttt.actionListenerInhalt(buttons.get(ttt.makeTurn()));
revalidate();
repaint();
}
});
jp2.add(neuesSpiel, BorderLayout.NORTH);
jp2.add(modus);
konsole.setFocusPainted(false);
konsole.setFont(new Font("Arial", Font.PLAIN, 30));
konsole.setBackground(new Color(90,90,90));
konsole.setBackground(new Color(65, 65, 65));
konsole.setForeground(Color.WHITE);
konsole.setText("Zurück zur Konsole?");
konsole.setPreferredSize(new Dimension(500, 75));
@ -99,8 +139,7 @@ public class GUI extends JFrame{
});
jp2.add(konsole, BorderLayout.SOUTH);
jp2.add(konsole);
this.add(jp2, BorderLayout.SOUTH);
this.setVisible(true);
@ -115,8 +154,6 @@ public class GUI extends JFrame{
JButton b = new JButton();
//System.out.println("Button "+i+" wurde erstellt.");
b.setFocusPainted(false);
b.setFont(new Font("Arial", Font.PLAIN, 70));
@ -136,48 +173,36 @@ public class GUI extends JFrame{
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(() -> {
actionListenerInhalt(b);
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);
if (ttt.getAktuellerSpieler() == 'X') {
ttt.setAktuellerSpieler('O');
} else {
ttt.setAktuellerSpieler('X');
public ArrayList<JButton> getButtons() {
return buttons;
}
repaint();
}
public void setButtons(ArrayList<JButton> buttons) {
this.buttons = buttons;
}
}

View File

@ -1,6 +1,6 @@
package Logik;
public class main {
public class Main {
public static void main(String[] args) {
@ -10,5 +10,4 @@ public class main {
e.printStackTrace();
}
}
}

View File

@ -1,20 +1,27 @@
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;
private GUI gui;
private char aktuellerSpieler;
private int ausgewaehltesFeld;
private TreeMap<Integer, Character> belegteFelder = new TreeMap<>();
private int[] gewinnerIndexe = new int[3];
private int counter, ausgewaehltesFeld, modus;
private char aktuellerSpieler, spielerBeginn;
private boolean aiIstDran;
public TicTacToe() throws Exception {
ermittleEingabeAnfang();
@ -23,39 +30,54 @@ public class TicTacToe {
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?");
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();
ermittleModus();
if (befehl.equalsIgnoreCase("gui")) {
if (modus == 1 || modus == 2) {
System.out.println("Um ein Feld für ein Zug auszuwählen muss dieses Feld angeklickt werden...");
gui = new GUI(this);
if (aktuellerSpieler == 'O' && aiIstDran)
actionListenerInhalt(gui.getButtons().get(makeTurn()));
}
} else if (befehl.equalsIgnoreCase("konsole")) {
gibAktuellenStandAus();
if(modus == 1) ermittleBeginner();
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...");
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();
for (int i = 0; i < 9; i++) {
erfasseEingabe();
gibAktuellenStandAus();
if (i >= 4 && pruefeSieg()) break;
if (i >= 4 && werHatGewonnen(belegteFelder) != null)
break;
if (aktuellerSpieler == 'X')aktuellerSpieler = 'O';
else aktuellerSpieler = 'X';
wechselAktuellerSpieler();
}
if (werHatGewonnen(belegteFelder) == 'D')
System.out.println("Unentschieden!");
else
System.out.println(aktuellerSpieler + " hat gewonnen!");
ermittleEingabeAnfang();
} else if (befehl.equalsIgnoreCase("ende")) {
@ -67,7 +89,58 @@ public class TicTacToe {
}
}
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 ime) {
System.out.println("Es wurde keine gültige Eingabe getätigt!");
ermittleModus();
}
}
private void ermittleBeginner() {
Scanner s = new Scanner(System.in);
System.out.println("Wer soll beginnen? (X oder O eingeben...)");
System.out.print(">>");
try {
char eingabe = s.next().toUpperCase().charAt(0);
if (eingabe == 'X' || eingabe == 'O')
aktuellerSpieler = eingabe;
else
throw new Exception();
} catch (Exception e) {
System.out.println("Gib genau ein X oder O ein...");
ermittleBeginner();
}
/*
* else if(modus == 2) {
* System.out.println("Soll der Spieler oder die AI beginnen?");
* System.out.print(">>"); String befehl = s.nextLine(); try {
* if(befehl.equalsIgnoreCase("ai")) { aiIstDran = true;
*
* System.out.println("Die AI beginnt also..."); }else
* if(befehl.equalsIgnoreCase("spieler")) {
* System.out.println("Der Spieler beginnt also..."); spielerBeginn =
* aktuellerSpieler = 'X'; }else { throw new Exception(); } }catch(Exception e)
* { System.out.println("Eingabe konnte nicht verarbeitet werden!");
* ermittleBeginner(); } }
*/
}
private void fuelleTreeMap() {
for (int i = 0; i < 9; i++) {
belegteFelder.put(i, (char) (48 + i));
}
@ -77,7 +150,8 @@ public class TicTacToe {
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));
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 +164,196 @@ public class TicTacToe {
System.out.print("\nSpieler " + aktuellerSpieler + " ist am Zug!\nGeben Sie eine Feldnummer ein:\n>>");
try {
if (modus == 1) {
ausgewaehltesFeld = s.nextInt();
} else if (modus == 2 && !aiIstDran && aktuellerSpieler == 'X') {
ausgewaehltesFeld = s.nextInt();
} else if (modus == 2 && aiIstDran && aktuellerSpieler == 'O') {
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...");
if (modus == 1 || modus == 2 && aktuellerSpieler == 'X')
erfasseEingabe();
}
}
public synchronized boolean pruefeBelegtesFeld(int index) {
public int makeTurn() {
int bestMove = -1;
int bestScore = Integer.MIN_VALUE; // Maximierung für AI (O)
System.out.println("Zu pruefender Index: "+index);
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);
}
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;
}
public void setAktuellerSpieler(char aktuellerSpieler) {
this.aktuellerSpieler = aktuellerSpieler;
// 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 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()));
}
}
}
private void wechselAktuellerSpieler() {
if (aktuellerSpieler == 'X') {
aktuellerSpieler = 'O';
} else {
aktuellerSpieler = 'X';
}
if(modus == 2) aiIstDran = !aiIstDran;
}
public void resetGame() {
counter = 0;
aktuellerSpieler = 'X';
aiIstDran = false;
fuelleTreeMap();
}
public int getModus() {
return modus;
}
public void setModus(int modus) {
this.modus = modus;
}
public boolean isAiIstDran() {
return aiIstDran;
}
public char getAktuellerSpieler() {
return aktuellerSpieler;
}
public TreeMap<Integer, Character> getBelegteFelder() {
return belegteFelder;
}
public int[] getGewinnerIndexe() {
return gewinnerIndexe;
}
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB