diff --git a/Semesterprojekt/src/Anzeige/GUI.java b/Semesterprojekt/src/Anzeige/GUI.java index 50ab5d3..46db7ae 100644 --- a/Semesterprojekt/src/Anzeige/GUI.java +++ b/Semesterprojekt/src/Anzeige/GUI.java @@ -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 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 getButtons() { + return buttons; + } - if (ttt.getAktuellerSpieler() == 'X') { - ttt.setAktuellerSpieler('O'); - } else { - ttt.setAktuellerSpieler('X'); - } - - repaint(); - } + public void setButtons(ArrayList buttons) { + this.buttons = buttons; } } diff --git a/Semesterprojekt/src/Logik/TicTacToe.java b/Semesterprojekt/src/Logik/TicTacToe.java index fdeb311..6dc4e04 100644 --- a/Semesterprojekt/src/Logik/TicTacToe.java +++ b/Semesterprojekt/src/Logik/TicTacToe.java @@ -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 belegteFelder = new TreeMap<>(); - - private int counter, ausgewaehltesFeld; + private GUI gui; - private char aktuellerSpieler; + + private TreeMap 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 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 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 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; } } diff --git a/Semesterprojekt/src/icon.jpg b/Semesterprojekt/src/icon.jpg new file mode 100644 index 0000000..27beb71 Binary files /dev/null and b/Semesterprojekt/src/icon.jpg differ