Mirko134 Geschrieben 26. März Geschrieben 26. März Hallo Liebe Programmierfreunde, Ich habe ein Mastermind Spiel Programmiert, das man als Nutzer so einfach spielen kann oder auch durch (unter-) Klassen, erstellen und automatisch lösen lassen kann. Ich habe mich selber bisher an ein paar verschieden Klassen versucht, die das Mastermind Rätsel ohne weitere Hilfe lösen können, jedoch brauchen diese noch relativ viele Versuche dafür, und es existieren Fälle, bei denen sie es auch gar nicht lösen können. Deshalb wollte ich euch mein Hauptprogramm zur Verfügung stellen, damit ihr wenn ihr Lust habt euch ebenfalls an so einer einfachen "schein KI" zu versuchen. Natürlich wenn jemand Bock hat und Erfahrung damit hat, kann versuchen eine "KI" zu gestalten, die sich selbst verbessert und von alleine den schnellstmöglichen weg zur Lösung findet... würde mich freuen zu sehen wie so etwas gemacht wird. Hier das Mastermind Interface (die Hauptklasse): import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random; /** * Ein Programm, das beim aufruf des Konstruktors ein Mastermind Interface ertellt * * @author (Mirko Feldheim) * @version (25.03.2024) */ public class MastermindInterface extends JFrame { /*------public static------*/ public static final int SPALTEN = 4; public static final int ZEILEN = 10; public static final String[] FARB_STRING = {"Red", "Yellow", "Green", "Cyan", "Blue", "Magenta"}; public static final Color[] COLORS = new Color[]{Color.RED, Color.YELLOW, Color.GREEN,Color.CYAN, Color.BLUE, Color.MAGENTA}; /* -----privat---- */ private static int DURCHMESSER = 40; // vom kreisdurchmesser hängt die komplette Spielfeldgroeße ab private JPanel mainPanel; private JPanel codePanel; private JPanel guessPanel; private JPanel colorPanel; private JButton checkButton; private JButton showButton; private JPanel checkingPanel; private int loesungsFeld[][]= new int[ZEILEN][SPALTEN]; private Color[][] feldFarbe = new Color[ZEILEN][SPALTEN]; private Color loesungsFarbe[] = new Color[SPALTEN]; private Color vorschauFarbe[] = new Color[SPALTEN]; private int versuch=0; private boolean geloest=false; private int richtig; private int fastRichtig; private int falsch; private JComboBox<String>[] colorComboBoxes; public MastermindInterface() { loesungErstellen(); init(); //erstellen des Interface } /** * Überprüft wie gut die geratenen/ uebergebenen Farben waren, und gibt die Bewertung als Array zurück * Zudem wird auch die Zeile und Bewertung eingezeichnet * @param Ein FarbArray, dass genau so lang wie die SPALTEN ist; * @return An Stelle 0: Anzahl der richtigen Steine ,Stelle 1: Anzahl der fast richtigen */ public int[] check(Color[] farben){ Color[] farbenCut= new Color[SPALTEN]; if(farben.length < SPALTEN){ return null; }else if(farben. length >= SPALTEN){ for(int i=0; i<SPALTEN; i++){ farbenCut[i]=farben[i]; } } //bis hier nur Sicherung, dass Nullpointerexception entstehen for(int i=0; i<SPALTEN; i++){ feldFarbe[versuch][i]= farbenCut[i]; } geloest=vergleichen(); // vergleichen sorgt dafür dass Richtige und fast richtige berechnet werden versuch++; repaint(); final int[] anzahl =new int[]{richtig,fastRichtig}; return anzahl; } /** * Zum auszuprobieren wie es aussieht wenn dir richtige loesung in check() uebergeben wird. * @return Gibt die Lösung als FarbArray aus */ public Color[] loesung(){ return loesungsFarbe; } public int getVersuch(){ return versuch; } public static Color getColorFromString(String colorName) { switch (colorName) { case "Red": return Color.RED; case "Yellow": return Color.YELLOW; case "Green": return Color.GREEN; case "Cyan": return Color.CYAN; case "Blue": return Color.BLUE; case "Magenta": return Color.MAGENTA; default: return null; } } public static void main(String[] args) { SwingUtilities.invokeLater(MastermindInterface::new); } /* Bis hier lesen - um die Klasse nutzen zu können (public) * -------------------------------------------------------------- * Alles ab hier ist nur wichtig um zu vertehen wie die Klasse im inneren Funktioniert */ /** * Errechnet wie viele Punkte geanau richtig sind, wie viele and der Falsch stelle sind und wie viele garnicht drauf sind. * @ return Gibt true zurück */ private boolean vergleichen(){ richtig =0; fastRichtig=0; falsch=0; boolean[] fertig= new boolean[SPALTEN]; // hier werden erst die genau richtigen aus der weiteren bewertung herausgenommen for(int x=0; x<SPALTEN; x++){ if(feldFarbe[versuch][x] == loesungsFarbe[x]){ fertig[x]=true; }else{ fertig[x]=false; } } //hier werden die Richtigen, fastRichtigen und falschen errechnet for(int i=0; i<SPALTEN; i++){ if(feldFarbe[versuch][i] == loesungsFarbe[i]){ richtig++; }else{ boolean fast=false; for(int j=0; j<SPALTEN; j++){ if(fertig[j]){ //System.out.println(i +"|||"+j); }else if((feldFarbe[versuch][i])== (loesungsFarbe[j])){ fast=true; fertig[j]=true; break; } } if(fast){ fastRichtig++; }else{ falsch++; } } } //hier wird das Zeichenen der Schwarzen und weißen Bewertungspunkte vorbereitet for(int i =0; i<SPALTEN; i++){ if(i<richtig){ loesungsFeld[versuch][i]= 2; }else if(i<(richtig+fastRichtig)){ loesungsFeld[versuch][i]= 1; } } if(richtig==SPALTEN){ JOptionPane.showMessageDialog(MastermindInterface.this, "You won with " + (versuch+1) + "tries"); } return (richtig == SPALTEN); } private void loesungErstellen(){ for(int i=0; i<SPALTEN; i++){ Random random = new Random(); loesungsFarbe[i]= getColorFromString(FARB_STRING[random.nextInt(FARB_STRING.length)]); } } /** * Das Interface wird ertellt und alles darin befindliche Initialisiert * (kann einfach ignoriert werden) */ private void init(){ setTitle("Mastermind"); setSize((DURCHMESSER*3/2)*(SPALTEN +2), (DURCHMESSER*3/2)*(ZEILEN+3)); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mainPanel = new JPanel(new BorderLayout()); codePanel = new JPanel(new GridLayout(1, SPALTEN)); guessPanel = new JPanel(new GridLayout(ZEILEN, SPALTEN)); checkingPanel = new JPanel(new GridLayout(ZEILEN, SPALTEN)); colorPanel = new JPanel(new GridLayout(1, SPALTEN + 1)); checkButton = new JButton("Check"); showButton= new JButton("Clear"); colorComboBoxes = new JComboBox[4]; //Erstellen der SPALTEN for (int i = 0; i < SPALTEN; i++) { final int ii =i; JPanel codeCircle = new JPanel() { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if(!geloest){ g.setColor(Color.BLACK); }else{ g.setColor(loesungsFarbe[ii]); } g.fillOval(DURCHMESSER/4, DURCHMESSER/4, DURCHMESSER, DURCHMESSER); } }; codeCircle.setPreferredSize(new Dimension((DURCHMESSER*3)/2, (DURCHMESSER*3)/2)); codeCircle.setBackground(Color.LIGHT_GRAY); codePanel.add(codeCircle); } for (int i = 0; i < ZEILEN; i++) { JPanel guessRow = new JPanel(new GridLayout(1, 4)); for (int j = 0; j < SPALTEN; j++) { final int jj = j; final int ii = i; JPanel guessCircle = new JPanel() { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Color color = feldFarbe[ii][jj]; if (color != null) { g.setColor(color); } g.fillOval(DURCHMESSER/4, DURCHMESSER/4, DURCHMESSER, DURCHMESSER); } }; guessCircle.setPreferredSize(new Dimension((DURCHMESSER*3)/2,(DURCHMESSER*3)/2)); guessCircle.setBackground(Color.LIGHT_GRAY); guessRow.add(guessCircle); } guessPanel.add(guessRow); } for (int i = 0; i < ZEILEN; i++) { JPanel solve = new JPanel(new GridLayout(1, SPALTEN)); for (int j = 0; j < SPALTEN; j++) { final int jj = j; final int ii=i; loesungsFeld[i][j]=0; JPanel solveCircle = new JPanel() { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if(loesungsFeld[ii][jj]==2){ g.setColor(Color.BLACK); g.fillOval(DURCHMESSER/8, DURCHMESSER/8, DURCHMESSER/2, DURCHMESSER/2); }else if(loesungsFeld[ii][jj]==1){ g.setColor(Color.WHITE); g.fillOval(DURCHMESSER/8, DURCHMESSER/8, DURCHMESSER/2, DURCHMESSER/2); }else{ g.setColor(Color.BLACK); g.drawOval(DURCHMESSER/8, DURCHMESSER/8, DURCHMESSER/2, DURCHMESSER/2); } } }; solveCircle.setPreferredSize(new Dimension((DURCHMESSER*3)/4,(DURCHMESSER*3)/4)); solveCircle.setBackground(Color.LIGHT_GRAY); solve.add(solveCircle); } checkingPanel.add(solve); } for (int i = 0; i < SPALTEN; i++) { JComboBox<String> colorComboBox = new JComboBox<>(FARB_STRING); colorComboBoxes[i] = colorComboBox; colorPanel.add(colorComboBox); } checkButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // Code to check the guess for(int i=0; i<SPALTEN; i++){ String colorName = (String) colorComboBoxes[i].getSelectedItem(); feldFarbe[versuch][i]= getColorFromString(colorName); } geloest=vergleichen(); versuch++; repaint(); } }); for(int i=0; i<SPALTEN; i++){ final int ii=i; colorComboBoxes[i].addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // Code update color String colorName = (String) colorComboBoxes[ii].getSelectedItem(); colorComboBoxes[ii].setBackground(getColorFromString(colorName)); repaint(); } }); } colorPanel.add(checkButton); mainPanel.add(codePanel, BorderLayout.NORTH); mainPanel.add(guessPanel, BorderLayout.CENTER); mainPanel.add(colorPanel, BorderLayout.SOUTH); mainPanel.add(checkingPanel, BorderLayout.EAST); mainPanel.setBackground(new Color(139, 69, 19)); add(mainPanel); setVisible(true); } } Und hier ein (einfaches) Beispiel zum Lösen von mir: import java.awt.Color; import java.util.*; /** * Ein Programm, das versucht innerhalb der vorgegebenen Versuche, die Lösung zu finden. * @author (Mirko Feldheim) * @version (25.03.2024) */ public class MasterMind2 extends MastermindInterface { // Instanzvariablen - ersetzen Sie das folgende Beispiel mit Ihren Variablen private int versuch; private Color[] auswahl; private Color[] richtigeFarben = new Color[SPALTEN]; private int[] bewertung; private List<int[]> Bewertung = new ArrayList<>(); private List<Color[]> Auswahl = new ArrayList<>(); private Map<Color[],int[]> CombiBewertung = new HashMap<>(); private int richtige=0; /** * Konstruktor für Objekte der Klasse MasterMind2 */ public MasterMind2() { auswahl= new Color[SPALTEN]; bewertung = new int[2]; // Alle Farben einzeln ausprobieren while((versuch=getVersuch())<COLORS.length-1){ for(int i=0; i<SPALTEN; i++){ auswahl[i]=COLORS[versuch]; } auswerten(); for(int i=0; i<(bewertung[0]+bewertung[1]); i++){ richtigeFarben[richtige]=auswahl[richtige]; richtige++; } if(richtige==SPALTEN){ break; } } //Hier wird eingaespart, dass die letzte Farbe getestet werden muss if(richtige<SPALTEN){ for(int i=richtige; i<SPALTEN; i++){ richtigeFarben[i]=COLORS[COLORS.length-1]; } } for(int i=0; i<auswahl.length; i++){ auswahl[i]=richtigeFarben[i]; } //die Richtige Reihenfolge für die Farben finden int counter=0; while(getVersuch()<ZEILEN && bewertung[0]<4){ if(isPossible(auswahl)){ auswerten(); } mixUp(); } } private void setToBestGuess(){ auswahl=new Color[SPALTEN]; int best=0; for(int z=0; z<Bewertung.size(); z++){ if(bounty(z)>=best){ best=bounty(z); for(int i=0; i<SPALTEN; i++){ auswahl[i]=Auswahl.get(z)[i]; } } } } private int bounty(int nummer){ return 2*(Bewertung.get(nummer)[0]) + 1*(Bewertung.get(nummer)[1]); } private void mixUp(){ setToBestGuess(); Random random= new Random(); for(int i=0; i<MinimaleTauschversuche();i++){ while(!tauschen(random.nextInt(SPALTEN), random.nextInt(SPALTEN))){ //es wird so lange versucht zufällig zeichen zu tauschen, bis sich wirklich was verändert } } } private boolean tauschen(int eins, int zwei){ Color dasEine = auswahl[eins]; Color dasAndere = auswahl[zwei]; if(dasEine!= dasAndere){ auswahl[zwei] = dasEine; auswahl[eins] = dasAndere; return true; }else{ return false; } } private int MinimaleTauschversuche(){ int fastRichtig=bewertung[1]; return (fastRichtig/2)+(fastRichtig%2); } private boolean gleicheZahlen(int[] eins, int[] zwei){ for(int i=0; i<eins.length; i++){ if(eins[i]!=zwei[i]){ return false; } } return true; } private boolean gleicheFarben(Color[] eins, Color[] zwei){ for(int i=0; i<eins.length; i++){ if(eins[i]!=zwei[i]){ return false; } } return true; } private boolean isPossible(Color[] vermutung){ boolean gleich=true; Color[] listenEintrag= new Color[SPALTEN]; for(int a=0; a<Auswahl.size(); a++){ listenEintrag=Auswahl.get(a); int[] eigenErgebnis = innerCheck(listenEintrag, vermutung); int[] combi = CombiBewertung.get(Auswahl.get(a)); if(!gleicheZahlen(combi,eigenErgebnis)){ gleich=false; } } return gleich; } /** * An erster Stelle das zu überprüfende, und an zweiter die vermeindliche lösung (die auswahl); * Dies ist essentiell, um zu unmögliche versuche zu verhindern */ private int[] innerCheck(Color[] vergleich, Color[] loesung){ int[] ausgabe=new int[2]; int richtig=0; int fastRichtig=0; boolean[] fertig= new boolean[SPALTEN]; // hier werden erst die genau richtigen aus der weiteren bewertung herausgenommen for(int x=0; x<fertig.length; x++){ if(vergleich[x]==loesung[x]){ fertig[x]=true; ++richtig; }else{ fertig[x]=false; } } //hier werden die fastRichtigen und falschen evaluiert for(int v=0; v<vergleich.length; v++){ if(!fertig[v]){ boolean knapp=false; for(int l=0; l<loesung().length; l++){ if(!fertig[l]&& vergleich[v]==loesung[l] && !knapp){ knapp=true; } } if(knapp){ ++fastRichtig; } } } ausgabe[0]=richtig; ausgabe[1]=fastRichtig; return ausgabe; } private void auswerten(){ bewertung=check(auswahl); Bewertung.add(bewertung); Auswahl.add(auswahl); CombiBewertung.put(auswahl,bewertung); auswahl= new Color[SPALTEN]; bewertung = new int[2]; for(int i=0; i<auswahl.length; i++){ auswahl[i]=Auswahl.get(Auswahl.size()-1)[i]; } for(int i=0; i<bewertung.length; i++){ bewertung[i] = Bewertung.get(Bewertung.size()-1)[i]; } versuch=getVersuch(); } } Falls ihr Ergänzungs-, Vereinfachungs- oder Verbesserungsvorschläge habt, gerne her damit und ansonsten viel Spaß und Erfolg dabei das Mastermind Rätsel zu lösen. Zitieren
eulersche_Zahl Geschrieben 26. März Geschrieben 26. März Gnadenlos mit strg c und strg v rein gehauen 😅 Mirko134 reagierte darauf 1 Zitieren
hellerKopf Geschrieben 27. März Geschrieben 27. März vor 8 Stunden schrieb Mirko134: * (kann einfach ignoriert werden) Mirko134 reagierte darauf 1 Zitieren
Barandorias Geschrieben 27. März Geschrieben 27. März (bearbeitet) Warum kein GitHub Link? 😁 Was mir aber aufgefallen ist, dass dein Switch-Case ewig viele Returns hat, seit Java 14 gibt es Switch-Case mit Rückgabewert. Als nächstes die Umsetzung deiner "Farben". Du benutzt schon eine JComboBox (Zeile 275), dann könntest du da auch direkt Enums als ComboBoxModel hinterlegen und sparst dir den Switch-Case gänzlich. Das sind so die 2 Sachen, die mir beim ersten, schnellen drüberfliegen in Notepad direkt ins Auge gesprungen sind. Bearbeitet 27. März von Barandorias Mirko134 reagierte darauf 1 Zitieren
Whiz-zarD Geschrieben 27. März Geschrieben 27. März Wo soll man da anfangen? 😅 Das Problematischste wird wohl sein, dass deine Spiellogik von einem JFrame abhängig ist. D.h. deine Spiellogik kannst du nicht per Unittests testen. Sie sollte aber vollständig unabhängig sein, damit man sie halt auch testen kann. Mirko134 reagierte darauf 1 Zitieren
Mirko134 Geschrieben 27. März Autor Geschrieben 27. März Inwiefern ist die Spiellogik von dem JFrame abhängig? Meinst du, weil die Eingabe der Antworten/ Versuche über die JButtons funktioniert, dafür gibt es ja die Methode public int[] check(Color[Spalten]). Oder meinst du, die Zeilen und Spaltenanzahl, die ist ja variabel einstellbar (zumindest nachdem ich das Programm an ein paar kleinen Stellen noch korrigiert habe, wo ich was falsch gemacht hatte) funktioniert. Also wenn du mir konkret eine Beispiel Stelle nennen könntest, an der ein Unittest nicht möglich ist, dann gerne her damit, ich versuche es zu ändern Zitieren
Mirko134 Geschrieben 27. März Autor Geschrieben 27. März vor 7 Stunden schrieb Barandorias: Warum kein GitHub Link? 😁 Was mir aber aufgefallen ist, dass dein Switch-Case ewig viele Returns hat, seit Java 14 gibt es Switch-Case mit Rückgabewert. Als nächstes die Umsetzung deiner "Farben". Du benutzt schon eine JComboBox (Zeile 275), dann könntest du da auch direkt Enums als ComboBoxModel hinterlegen und sparst dir den Switch-Case gänzlich. Das sind so die 2 Sachen, die mir beim ersten, schnellen drüberfliegen in Notepad direkt ins Auge gesprungen sind. Hab nun ein Github Repository erstellt, hier der Link dazu, da findest du aber auch noch andere Spiele, die ich aus Spaß nachprogrammiert habe. https://gitlab.informatik.uni-bremen.de/mirko5/meine_spiele.git Habe zudem die StringToColor() Methode komplett entfernt/ bin sie umgangen, da dies sowieso überflüssig war, zudem habe ich einen kleinen Fehler korrigiert, weswegen wenn die Spalten und Zeilenanzahl verändert wurden das Programm nicht mehr geladen hat. Zitieren
Whiz-zarD Geschrieben 27. März Geschrieben 27. März (bearbeitet) vor 3 Stunden schrieb Mirko134: Inwiefern ist die Spiellogik von dem JFrame abhängig? public class MastermindInterface extends JFrame { // ... } Deine Spiellogik steckt in einem JFrame, da die Klasse von einem JFrame erbt. Also ist auch deine Spiellogik von einem JFrame (und von anderen UI-Elementen) abhängig. Dadurch ist es nun nicht mehr möglich, mittels Unittests die Spiellogik zu testen. Die Spiellogik sollte aber frei von UI-Elementen sein. Du kannst also nicht per Unittests testen, ob dein Spiel auch wirklich funktioniert. Du musst es also immer wieder starten und alle Testszenarien per Hand durchgehen, was mit der Zeit sehr aufwendig wird. Es sollte daher eine Mastermind-Klasse geben, mit der du das komplette Spiel abbildest und die UI verwendet dann einfach nur diese Klasse und beinhaltet keinerlei Spiellogik. Bearbeitet 27. März von Whiz-zarD Zitieren
Whiz-zarD Geschrieben 27. März Geschrieben 27. März (bearbeitet) gelöscht Bearbeitet 27. März von Whiz-zarD Zitieren
Empfohlene Beiträge
Dein Kommentar
Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.