www.r-krell.de |
Webangebot für Schule und Unterricht, Software, Fotovoltaik und mehr |
Willkommen/Übersicht > Informatik > Java-Seite b3) (2018) 2019
Teil b3)_2018 (in
der -Fassung von
2019)
Ergänzung zu Teil b2)_2017, Programm P9, Aufgabe 3:
Galgenmann-Spiel mit zusätzlicher Grafik
Eine vollständige Übersicht aller meiner Seiten "Informatik mit Java" gibt's auf der Informatik-Hauptseite! Bezugsseite und Vorgänger dieser Seite ist die Seite b2)_2017.
Auf dieser Seite b3)_2018 finden Sie (im Februar 2019 vollständig neu überarbeitet):
zum Seitenanfang / zum Seitenende
Auf der letzten Seite b2)_2017 war bereits das Programm P9 (auch bekannt als Glücksrad, Hangman bzw. Galgenmann/Galgenmännchen) vorgestellt worden: Die Buchstaben eines geheimen Worts werden durch Unterstriche markiert. Die Spielerin bzw. der Spieler kann einen Buchstaben des Alphabets nachfragen bzw. eintippen. Ist der gefragte Buchstabe im Wort vorhanden, wird er dort jeweils stellenrichtig angezeigt. Es geht darum, das gegebene Wort möglichst schnell, d.h. mit möglichst wenig abgefragten Buchstaben, zu erraten. Da ich inzwischen (Februar 2019) über die üblichen Spielregeln aufgeklärt wurde, muss ich jetzt richtigstellen: Normalerweise ist nicht die Gesamtzahl der abgefragten Buchstaben begrenzt, sondern nur die Zahl 'falscher' Buchstaben. Es dürfen höchstens 10 im Wort nicht vorhandene Buchstaben erfragt werden. Nur für jeden gefragten Buchstaben, der nicht im Wort vorkommt, wird ein Strich zu einer Galgenmännchen-Zeichnung hinzu gefügt. Nach 10 falschen Buchstaben bzw. 10 Strichen (wobei der Kopf als ein Strich gilt) ist die Zeichnung fertig und es kann nur noch das ganze Wort erraten werden, ohne dass weitere Buchstaben abgefragt werden dürfen. Insofern ist das Programm gegenüber P9 von Seite b2)_2017 nicht nur um die Grafik erweitert, sondern folgt auch einer veränderten Spiellogik!
Die Bilder zeigen das neue Spiel nach insgesamt 4 (davon 2 falschen) und - etwas später - nach 12 (davon 7 falschen) erfragten Buchstaben. Da die Taste "Lösung prüfen" am Schluss noch nicht gedrückt wurde, wird im Moment noch keine Richtig- oder Falsch-Meldung zum Lösungsversuch angezeigt. Natürlich kann man auch noch alle weiteren Buchstaben der vermuteten Lösung einzeln erfragen, bis das Wort komplett angezeigt wird. Denn jetzt kosten richtige erfragte Buchstaben ja keine Galgenstriche mehr!
Das Spiel soll in Java programmiert werden. Im Folgenden wird möglicher Java-Quelltext in zwei Versionen für zwei verschiedenen Grafik-Darstellungen (P9_A3a / _A3b) vorgestellt und besprochen. Gegenüber dem ursprünglichen Programm P9 (siehe unten auf der letzten Seite b2)_2017) kommt in der Oberfläche nur das Rechteck bzw. Panel mit der grafischen Darstellung hinzu. Wegen der neuen Spiellogik wäre allerdings auch eine Vergrößerung des Textfelds für die Anzeige der bereits gefragten Buchstaben sinnvoll; das ist hier aber noch nicht verwirklicht. Am Ende dieser Seite können die fertigen Programme auch ausprobiert werden!
zum Seitenanfang / zum Seitenende
Nach den bisherigen Programmierübungen müsste klar sein, dass der Programmtext sinnvollerweise auf mindestens drei Dateien/Klassen verteilt werden sollte:
Für die Klasse 3 stelle ich zwei verschiedene Realisierungen vor:
a) mit Strichzeichnungen per Java-Grafik oder
b) mit dem Aufruf vorgefertigter Bilder, die vorab mit einem externen Zeichen- bzw. Grafikprogramm erstellt wurden,
und jetzt nur passend angezeigt werden.
Der Austausch der beiden Klasse-3-Varianten a) oder b) soll nicht zu Änderungen der Klassen 1 und 2 zwingen, sieht man von den unterschiedlichen Namen ab, d.h. dass einmal die Klasse 3a oder zum anderen die Klasse 3b aufgerufen werden muss. Alles andere soll komplett gleich bleiben!
Sie als Leserin bzw. Leser sind natürlich herzlich aufgefordert, spätestens jetzt die Lektüre dieser Webseite zu unterbrechen und sich selbst Gedanken über eine passende Realisierung zu machen. Nach Fertigstellung Ihrer eigenen Version könnten Sie danach im Folgenden meinen Ansatz nachlesen, der nicht den Anspruch erhebt, die allein seeligmachende Umsetzung zu sein. Ein Vergleich verschiedener Implementationen hilft immer, wichtige Grundzüge zu erkennen.
Und während im Leistungskurs oder später auch hier im Grundkurs vor der Programmierung ein Modell bzw. Konzept erarbeitet werden soll, wobei graphische Mittel wie z.B. UML-Klassendiagramme sowie eine Schnittstellenbeschreibung für die anstehende Modellierung eingesetzt werden, will ich hier im Anfangsunterricht des ersten Grundkursjahrs noch nicht darauf bestehen: Nach meiner Erfahrungen machen solche Mittel im Unterricht erst Sinn, wenn die Schülerinnen und Schüler erfahren haben, dass Sie ohne Konzept das Zusammenspiel verschiedener Klassen nicht ausreichend durchschauen bzw. planen können. Erst dann sind sie bereit, sich mit Modellierung und Modellierungswerkzeugen zu beschäftigen - vorher werden solche Maßnahmen eher als zusätzliche Schwierigkeit statt als willkommene Hilfe angesehen. (Hinweise auf Modellierungen und geschickte Klassenaufteilung findet man auf vielen meiner Ausführlichen Seiten zur Informatik mit Java c) bis k) sowie DB3 bei den jeweils dort entwickelten Programmen, außerdem natürlich konzentriert auch auf den beiden Seiten zum Software-Engineering SWE und SWE-2 -- auf der letztgenannten Seite SWE-2 konkret am Beispiel eines Kartenspiel-Projekts).
zum Seitenanfang / zum Seitenende
Die Programm-Oberfläche wird mit dem im Javaeditor eingebauten GUI-Builder bequem per drag & drop in einen neuen JFrame zusammen gezogen. Der Javaeditor erzeugt dabei den zugehörigen Quelltext automatisch, wobei Namen und Texte im Objekt-Inspektor geändert bzw. eingegeben werden. Nur wenige Zeilen müssen nachher noch "von Hand" in den so erhaltenen Programmtext eingefügt werden. Und wenn die Klasse P9_A3a_Panel.java schon kompiliert wurde, reicht ein Rechtsklick auf das Panel-Icon (das graue Quadrat in der Werkzeugleiste), um an Stelle des normalen JPanels ein P9_A3a_Panel in die Oberfläche einzubinden. (Den sehr empfehlenswerten Javaeditor stelle ich im Teil a) von Informatik mit Java vor; die offizielle Webseite ist javaeditor.org. Die folgende Abbildung zeigen die im Mai 2018 aktuelle Version 15.21 - bei den früher erstellten Programmen in meinem Web-Angebot kamen die damaligen Versionen zum Einsatz. Im Februar 2019 ist die Version 16.18 aktuell).
Im nachfolgenden, vollständigen Quelltext sind die von Hand eingefügten Zeilen durch rote Zeilennummern gekennzeichnet (wobei Zeile 37 eigentlich doch automatisch erstellt wurde - per Rechtsklick auf das JPanel-Werkzeug, s.o.. Und auch Zeile 51 wurde automatisch erstellt und der Titel im Objektinspektor eingetragen. Wirklich 'von Hand' hinzugefügt wurden in den Zeilen 37 und 51 nur die Kommentare). Im Februar 2019 wurde der vorher hier gezeigte Programmtext so ergänzt, dass jetzt nicht mehr alle, sondern nur noch die 'falschen' (=im Wort nicht vorhandenen) erfragten Buchstaben zu Strichen im Galgen führen. Dazu wurde die Methode jBtBuchstabe_ActionPerformed neu entwickelt, die ab Zeile 139 beginnt.
001 import java.awt.*;
002 import java.awt.event.*;
003 import javax.swing.*;
004 import javax.swing.event.*;
005
006 /**
007 * P9_A3a_Galgenmann
008 * Wort-Raten aus erfragten Buchstaben ("Glücksrad", "Galgenmännchen", "Hangman"..)
009 *
010 * @version vom 2.2.2019 für http://www.r-krell.de/if-java-b3_2018.htm
011 * @author R. Krell (www.r-krell.de)
012 *
013 * Lösung der Übungs-Aufgabe 3 zu P9 auf http://www.r-krell.de/if-java-b2_2017.htm;
014 * Variante a: mit per Java-Befehlen gezeichnetem Galgenmännchen (in P9_A3a_Panel;
015 * Variante b: mit Anzeige extern vorgefertigter Galgenbilder (in P9_A3b_Panel;
016 * (vgl. Zeilen 37 und 51; sonst hier und in P9_A3_GalgenmammFkt alles gleich)
017 *
018 * Der Quelltext dieser Klasse wurde im Wesentlichen mit dem GUI-Builder des Javaeditors
019 * (javaeditor.org) erzeugt; von Hand eingetragene Zeilen haben rote Zeilennummern
020 *
021 */
022
023 public class P9_A3a_Galgenmann extends JFrame {
024 P9_A3_GalgenmannFkt f = new P9_A3_GalgenmannFkt(); // für a und b gleich
025
026 // Anfang Attribute
027 private JButton jBtNeu = new JButton();
028 private JTextField jTfAnzeige = new JTextField();
029 private JTextField jTfverbraucht = new JTextField();
030 private JTextField jTfBuchstabe = new JTextField();
031 private JButton jBtBuchstabe = new JButton();
032 private JTextField jTfLoesung = new JTextField();
033 private JButton jBtPruefen = new JButton();
034 private JLabel jLabel1 = new JLabel();
035 private JLabel jLabel2 = new JLabel();
036 private JLabel jLabel3 = new JLabel();
037 private P9_A3a_Panel galgen = new P9_A3a_Panel(); // bzw. _A3b_ für andere Version
038 // Ende Attribute
039
040 public P9_A3a_Galgenmann() {
041 // Frame-Initialisierung
042 super();
043 setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
044 int frameWidth = 444;
045 int frameHeight = 303;
046 setSize(frameWidth, frameHeight);
047 Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
048 int x = (d.width - getSize().width) / 2;
049 int y = (d.height - getSize().height) / 2;
050 setLocation(x, y);
051 setTitle("P9_A3a_Galgenmann (r-krell.de)"); // bzw. _A3b_ für andere Version
052 setResizable(false);
053 Container cp = getContentPane();
054 cp.setLayout(null);
055 // Anfang Komponenten
056
057 jBtNeu.setBounds(24, 16, 233, 25);
058 jBtNeu.setText("Neues Spiel / neues Wort");
059 jBtNeu.setMargin(new Insets(2, 2, 2, 2));
060 jBtNeu.addActionListener(new ActionListener() {
061 public void actionPerformed(ActionEvent evt) {
062 jBtNeu_ActionPerformed(evt);
063 }
064 });
065 jBtNeu.setToolTipText("(Neu-)Start des Spiels mit einem neuen Rate-Wort");
066 cp.add(jBtNeu);
067 jTfAnzeige.setBounds(24, 56, 233, 25);
068 jTfAnzeige.setEditable(false);
069 jTfAnzeige.setToolTipText("Maskierte Anzeige des zu erratenden Worts: _ durch Buchstaben ersetzen!");
070 cp.add(jTfAnzeige);
071 jTfverbraucht.setBounds(24, 112, 145, 25);
072 jTfverbraucht.setEditable(false);
073 jTfverbraucht.setFont(new Font("Courier New", Font.PLAIN, 12));
074 jTfverbraucht.setToolTipText("Anzahl (gesamt/falsch) und Liste der bisher gefragten Buchstaben");
075 cp.add(jTfverbraucht);
076 jTfBuchstabe.setBounds(176, 112, 25, 25);
077 jTfBuchstabe.setHorizontalAlignment(SwingConstants.CENTER);
078 jTfBuchstabe.setToolTipText("Bitte hier Buchstabe eintippen, der im Wort vermutet wird");
079 cp.add(jTfBuchstabe);
080 jBtBuchstabe.setBounds(208, 112, 49, 25);
081 jBtBuchstabe.setText("ok");
082 jBtBuchstabe.setMargin(new Insets(2, 2, 2, 2));
083 jBtBuchstabe.addActionListener(new ActionListener() {
084 public void actionPerformed(ActionEvent evt) {
085 jBtBuchstabe_ActionPerformed(evt);
086 }
087 });
088 jBtBuchstabe.setToolTipText("Eingetippten ('gefragten') Buchstaben im Wort suchen");
089 cp.add(jBtBuchstabe);
090 jTfLoesung.setBounds(24, 168, 233, 25);
091 jTfLoesung.setToolTipText("Hier kann ein vermutetes Wort zur Prüfung eingegeben werden");
092 cp.add(jTfLoesung);
093 jBtPruefen.setBounds(24, 208, 233, 25);
094 jBtPruefen.setText("Lösung prüfen");
095 jBtPruefen.setMargin(new Insets(2, 2, 2, 2));
096 jBtPruefen.addActionListener(new ActionListener() {
097 public void actionPerformed(ActionEvent evt) {
098 jBtPruefen_ActionPerformed(evt);
099 }
100 });
101 jBtPruefen.setToolTipText("Programmbeschreibung auf www.r-krell.de/if-java-b3_2018.htm");
102 cp.add(jBtPruefen);
103 jLabel1.setBounds(24, 88, 144, 19);
104 jLabel1.setText("gefragte Buchstaben");
105 cp.add(jLabel1);
106 jLabel2.setBounds(24, 144, 147, 19);
107 jLabel2.setText("Lösung / erratenes Wort");
108 cp.add(jLabel2);
109 jLabel3.setBounds(176, 88, 99, 17);
110 jLabel3.setText("nächster Bst.");
111 cp.add(jLabel3);
112 galgen.setBounds(272, 16, 137, 217);
113 galgen.setBackground(Color.WHITE);
114 galgen.setToolTipText("Für jeden nicht vorhandenen gefragten Buchstaben wird (mit Java) ein Strich gezeichnet -- maximal 10");
115 cp.add(galgen);
116 // Ende Komponenten
117
118 setVisible(true);
119 } // end of public P9_A3a_Galgenmann
120
121 // Anfang Methoden
122
123 public static void main(String[] args) {
124 new P9_A3a_Galgenmann();
125 } // end of main
126
127 public void jBtNeu_ActionPerformed(ActionEvent evt) {
128 f.nächstesWort();
129 jTfAnzeige.setText(f.anzeige());
130 jTfverbraucht.setText(f.verwendeteBuchstaben);
131 jTfBuchstabe.setText("");
132 jTfLoesung.setText("");
133 jBtPruefen.setEnabled(true);
134 jBtBuchstabe.setEnabled(true);
135 jTfBuchstabe.requestFocus();
136 galgen.loesche();
137 } // end of jBtNeu_ActionPerformed
138
139 public void jBtBuchstabe_ActionPerformed(ActionEvent evt) {
140 if (galgen.zahl() < 10)
141 {
142 String alteAnzeige = jTfAnzeige.getText();
143 int alt = f.verwendeteBuchstaben.length(); // Gesamtzahl der bisher gefragten Buchstaben
144 char bst = (jTfBuchstabe.getText()+"?").charAt(0); // neu gefragter Buchstabe
145 f.nimmBuchstaben(bst); // Übergabe des gefragten Buchstabens an die Funktion
146 int neu = f.verwendeteBuchstaben.length(); // Gesamtzahl jetzt gefragter Buchstaben
147 if (neu > alt) // falls gültiger Buchstabe gefragt wurde
148 {
149 String neueAnzeige = f.anzeige(); // Wort evtl mit neu gefragtem/n Buchstaben
150 if (!neueAnzeige.equals(alteAnzeige))
151 {
152 jTfAnzeige.setText(neueAnzeige); // neuen Buchstaben im Wort anzeigen..
153 }
154 else
155 {
156 galgen.nächsterStrich(); // .. oder nächsten Strich vom Galgenmann zeichnen
157 }
158 jTfverbraucht.setText(""+neu+"/"+galgen.zahl()+": "+f.verwendeteBuchstaben); // Zahl/davon_falsch ..
159 } // ..und alle bis jetzt gefragten Buchstaben anzeigen
160 jTfBuchstabe.setText(""); // letzte Buchstaben-Eingabe löschen
161 jTfBuchstabe.requestFocus(); // Cursor in Eingabefeld für weitere Buchstaben
162 jTfLoesung.setText(""); // evtl. getestetes Lösungs-Wort löschen
163 }
164 if (galgen.zahl() >= 10)
165 {
166 jBtBuchstabe.setEnabled(false); // OK-Knopf für weitere Buchstaben sperren
167 jTfLoesung.requestFocus(); // Feld für Lösungseingabe hervorheben
168 }
169 if ((f.anzeige()).indexOf('_')<0) // keine Lücke mehr: alle
Buchstaben bekannt, Wort vollständig
170 {
171 jTfLoesung.setText("Glückwunsch, Wort erraten!"); // Lob für richtige Lösung
172 jBtPruefen.setEnabled(false); // Weitere Lösungsprüfungen..
173 jBtBuchstabe.setEnabled(false); // ..und Buchstabeneingaben abschalten
174 jBtNeu.requestFocus(); // Neu-Knopf hervorheben
175 }
176 } // end of jBtBuchstabe_ActionPerformed
177
178 public void jBtPruefen_ActionPerformed(ActionEvent evt) {
179 String lsg = jTfLoesung.getText().trim();
180 if (f.prüfeLösung(lsg))
181 {
182 jTfAnzeige.setText(f.aktuellesWort); // Wort vollständig anzeigen
183 jTfLoesung.setText("*"+lsg+"* ist richtig!"); // Lob für richtige Lösung
184 jBtPruefen.setEnabled(false); // Weitere Lösungsprüfungen..
185 jBtBuchstabe.setEnabled(false); // ..und Buchstabeneingaben abschalten
186 jBtNeu.requestFocus(); // Neu-Knopf hervorheben
187 } // end of if
188 else
189 {
190 jTfLoesung.setText("*"+lsg+"* ist falsch!"); // Ablehnen falscher Lösung
191 }
192 } // end of jBtPruefen_ActionPerformed
193
194 // Ende Methoden
195 } // end of class P9_A3a_Galgenmann
In den Programmtext wurden einige Feinheiten eingebaut, um die Bedienung zu erleichtern: mit requestFocus wird dafür gesorgt, dass der Eingabe-Cursor anschließend im angegebenen Feld blinkt bzw. der genannte Button vorausgewählt wird. So wird zu Anfang (Zeile 135) und jeweils nach der Frage nach einem Buchstaben (Zeile 161) die Buchstaben-Eingabe ausgewählt, damit man ohne zusätzlichen Mausklick den nächsten Buchstaben fragen kann. Nach 10 falschen Buchstaben wird hingegen die Lösungs-Eingabe ausgewählt, weil keine Buchstaben mehr erfragt werden dürfen (Zeilen 164 bzw. 167 - deshalb wird der zugehörige OK-Knopf dann auch abgeschaltet und nur noch ausgegraut und funktionslos angezeigt: s. Zeilen 173 und 185). Und nach erfolgreicher Lösung wird man vermutlich ein neues Wort haben wollen - s. Zeilen 174 und 186. Durch Mausklick kann man die Voreinstellungen natürlich überwinden, also z.B. statt einen weiteren Buchstaben zu erfragen, ins Lösungsfeld klicken und dort die erratene Lösung eingeben.
Übrigens: Vor dem if in Zeile 169 hätte gerne noch ein else stehen können, weil der letzte fehlende Buchstabe in einem Wort nicht gleichzeitig mit dem zehnten Galgenstrich möglich ist.
Das Zusammenspiel mit den beiden anderen Klassen bzw. den dadurch erzeugten Objekten f und galgen lässt sich aus den sprechenden Namen der dort selbstprogrammierten Methoden und dem Kommentar entnehmen. Besonders hingewiesen wird nur auf Zeile 156: durch den Aufruf galgen.nächsterStrich() soll das in Zeile 37 eingebundene erweiterte JPanel namens galgen seinen internen Strichzähler erhöhen sowie die entsprechende Grafik erzeugen und darstellen. Der dafür nötige Programmtext steht aber nicht hier, sondern gekapselt in der Klasse P9_A3a_Panel bzw. P9_A3b_Panel (s.u.)! Hier, in der oben dargestellten zentralen Klasse, sollen eben keine Details der Grafik behandelt werden, sodass die beiden Grafikpanels a und b mit ihren ganz unterschiedlichen Anzeigemechanismen problemlos ausgetauscht werden können.
zum Seitenanfang / zum Seitenende
Hier müssen jetzt die Funktionen hinterlegt bzw. programmiert werden, die oben (z.B. in den Zeilen 128 bis 130 oder auch in den Zeilen 143, 145, 146.. der vorstehenden Klasse 1 vom Objekt f verlangt werden. Dabei ist f das nach dem folgendem Bauplan erzeugte Objekt, das alle dort beschriebenen Eigenschaften und Fähigkeiten hat.
Der zugehörige Quelltext kann vollständig von Hand in eine neue Editor-Seite (Datei > Neu > Java) eingetippt werden. Auf Wunsch erzeugt der Java-Editor aber auch halbautomatisch-interaktiv die Rahmen der benötigten Klasse mit Attributen und allen benötigten Methoden, die anschließend 'nur noch' mit dem Programmtext gefüllt werden müssen. Aber schon der leere Rahmen kann kompiliert werden, sodass damit auch der Quelltext der Klasse 1 fehlerfrei kompilierbar ist - auch wenn das so erzeugte Programm noch nicht richtig funktioniert. Ein solcher Rahmen wird mit dem Befehl UML > Neue Klasse erzeugt. Da in Zeile 128 des obigen Programmtextes der 1. Klasse der Aufruf f.nächstesWort() ohne Ausgabe und ohne Zuweisung erfolgt, scheint die Methode nächstesWort keinen Rückgabewert zu benötigen (Rückgabe void bei einer "Prozedur"). Durch den Gebrauch von f.verwendeteBuchstaben() als Argument in setText oben in Zeile 130 wird hingegen offenbar verlangt, dass die Methode verwendeteBuchstaben einen Text zurück gibt, also einen Rückgabewert vom Typ String hat. Sie muss deshalb als Funktion mit passendem Rückgabe-Typ angelegt werden, wie im Bild gezeigt ist:
Tatsächlich habe ich im Folgenden den gerade gezeigten UML-Klasseneditor des Javaeditors selbst nicht benutzt, sondern den Quelltext lieber vollständig von Hand eingegeben. [Der Java-Editor verfügt nicht nur praktisch über alle von BlueJ bekannten Möglichkeiten inkl. dem Testen von Klassen ohne main-Methode; er kann noch mehr und vereint den Programmtext-Editor mit Syntax-Hervorhebung, Blockstrukturdarstellung, Code-Vervollständigung u.v.a.m. noch mit einem GUI-Builder, dem UML-Klasseneditor und einem Struktogrammeditor. Die letzten drei integrierten Zusatzwerkzeuge erzeugen jeweils automatisch passenden Quelltext! Und natürlich lassen sich auch von händisch eingetipptem Programmtext UML-Diagramme zeichnen (s.u.), während der Struktogramm-Editor z.B. auf meiner folgenden Seite b4)_2018 zum Einsatz kommt]
001 public class P9_A3_GalgenmannFkt 002 // eigentliche Spielfunktion von Galgenmann, unabhängig von der Oberfläche und 003 // davon, ob/wie Grafik angezeigt wird -- 5/2018 & 2/2019 r-krell.de 004 { 005 String[] wort = {"Ratespiel","Buchstabe","Wegesrand","Butterbrot","Turboabitur","Suppenteller", 006 "Musikakademie","Altbierglas","Nacktschnecke","Computermaus","Kaffeemaschine","Kuchengabel", 007 "Trinkbecher","Rotstift","Niedrigpreis","Schlussverkauf","Campingplatz","Ledersofa", 008 "Bleistiftspitzer","Filialleiterin"}; // bel. erweiterbare Wortliste 009 int max = wort.length; // Anzahl der vorstehend genannten, möglichen Wörter 010 int nr = 0; // Anzahl der bereits verwendeten Wörter 011 012 String aktuellesWort = ""; // aktuell ausgewähltes/verwendetes Wort 013 boolean[] sichtbZeichen = new boolean[26]; 014 String verwendeteBuchstaben = ""; 015 016 017 public void nächstesWort() // wählt zufällig ein bisher nicht benutzes Wort 018 { 019 String gefunden = ""; 020 if (nr<max) // sofern noch nicht alle Wörter verwendet wurden 021 { 022 int pos; 023 do { 024 pos = (int)(Math.random() * max); // zufällige Position in der Wortliste 025 } while (wort[pos].equals("")); // bei gelöschten Wörtern erneut versuchen 026 gefunden = wort[pos]; // zufällig gefundenes Wort zwischenspeichern 027 wort[pos]=""; // gefundenes Wort aus der Wortliste löschen 028 nr++; // Anzahl verwendeter Wörter um 1 erhöhen 029 } 030 aktuellesWort = gefunden.toUpperCase(); // gefundenes Wort wird aktuelles Wort 031 keineBuchstabenBenutzt(); // für neues aktuelle Wort wurden noch keine.. 032 } // ..Buchstaben geraten: verwendete Buchstaben löschen 033 034 public void keineBuchstabenBenutzt() // löscht verwendete Buchstaben 035 { 036 for (int i=0; i<26; i++) // setzt alle 26 Buchstaben A .. Z des Alphabets 037 { 038 sichtbZeichen[i] = false; // auf bisher nicht gefragt und daher nicht anzuzeigen 039 } // end of for 040 verwendeteBuchstaben = ""; // Liste der bisher gefragten Buchstaben leeren 041 } 042 043 public void nimmBuchstaben (char bst) // prüft und verwendet eingegebenes Zeichen 044 { 045 if ('a' <= bst && bst <='z') // verwandelt ggf. Kleinbuchstaben in Großbuchstaben 046 { 047 bst = (char)((int)bst-(int)'a'+(int)'A'); 048 } // end of if 049 if ('A' <= bst && bst <= 'Z') // nur bei echten Buchstaben (ungültige Zeichen werden ignoriert!) 050 { 051 sichtbZeichen[(int)bst-(int)'A']=true; // Buchstabe als sichtbar markieren und 052 verwendeteBuchstaben = verwendeteBuchstaben + bst; // zu verwendeten Buchstaben dazu 053 } // end of if 054 } 055 056 public String anzeige () // Wort aus Unterstrichen und gefragten/verwendeten Buchstaben 057 { // jeweils mit einem Leerzeichen Zwischenraum 058 String aus = ""; 059 for (int i=0; i<aktuellesWort.length() ;i++) 060 { 061 if (sichtbZeichen[(int)aktuellesWort.charAt(i)-(int)'A']) 062 { 063 aus = aus + " " + aktuellesWort.charAt(i); // sichtbarer Buchstabe 064 } // end of if 065 else 066 { 067 aus = aus + " _"; // Leerzeichen für noch nicht gefragten Buchstaben 068 } // end of if-else 069 } // end of for 070 return (aus); 071 } 072 073 public boolean prüfeLösung (String eingabe) // Prüfen des eingegeben Lösungsversuchs 074 { 075 return (eingabe.equalsIgnoreCase(aktuellesWort)); 076 } 077 } |
Die gleiche Klasse wurde schon in der Original-Version des Programms P9 verwendet, da die Spiellogik unabhängig von der Oberfläche und der Grafik ist.
In der Methode nimmBuchstaben (ab Zeile 43 im gelb unterlegten Text) wird überprüft, ob der übergebene Buchstabe bst gültig ist. Dabei gilt auch ein schon früher erfragter Buchstabe als weitere gültige Frage (und bst wird nochmal zur Liste der bereits gefragten Buchstaben hinzugefügt). Unbeabsichtigt zu langes (=doppeltes) Drücken des OK-Knopfs schadet hingegen nicht, weil der gefragte Buchstabe sofort aus dem Eingabefeld entfernt wird (Zeile 160 in Klasse 1) und dann dort nichts mehr steht, was beim erneuten Knopfdruck bzw. beim Aufruf von jBtBuchstabe_ActionPerformed genommen werden könnte (bzw. ein '?' erkannt wird wegen Zeile 144 in Klasse 1, da ein einzelnes Schriftzeichen vom Typ char nicht leer sein darf). Ziffern, Leer- und Sonderzeichen werden aber einfach ignoriert, nicht gezählt und nicht extra gemeldet. Während sich das bei P9 bewährt hat, wurde bei meinem ersten Versuch der Klasse P9_A3_Galgenmann gelegentlich trotzdem Buchstaben gezählt, obwohl kein gefragter Buchstabe hinzu gekommen war. In der Klasse 1 frage ich daher jetzt mit den Zeilen 143, 146 und 147 ab, ob die Funktion f einen gültigen Buchstaben erkannt hat (wodurch neu größer wird als alt). Da der Galgen seit Februar 2019 nur weiter gezeichnet werden soll, wenn der gefragte Buchstabe nicht im Wort vorkommt, vergleiche ich nun das neue und das alte angezeigte Wort (Klasse 1, Zeilen 142, 149 und 150). Ohne Unterschied zeichne ich dann den nächsten Strich vom Galgen (Klasse 1, Zeile 156). Das bedeutet aber auch, dass ein mehrfach abgefragter Buchstabe, der bereits im Wort vorkam, als falscher Buchstabe gewertet und die Unaufmerksamkeit der Doppelabfrage mit einem Galgenstrich bestraft wird. Ausgelöst wird das Zeichnen dann vom Aufruf galgen.nächsterStrich(); in Zeile 156 der Klasse 1; programmiert wird das Zeichnen in der nächsten Klasse, der Klasse 3.
zum Seitenanfang / zum Seitenende
Im Anfangsunterricht muss nicht unbedingt der Vererbungsmechanismus ausführlich thematisiert werden: Es reicht zu wissen, dass man das von Java mitgelieferte JPanel durch eigene Methoden (wie nächsterStrich oder zahl) ergänzen und die vorhandene Methode paint so abändern (überschreiben) kann, dass sie nicht nur das leere Panel, sondern darauf auch das Galgenmännchen zeichnen kann. Außerdem muss einmal mitgeteilt werden, dass die Methode paint nicht direkt aufgerufen wird. Besser soll die im System vorhandene (nicht selbst zu [über-]schreibende) Methode repaint aufgerufen werden. repaint erledigt neben weiteren benötigten Hintergrund-Aufgaben immer auch den Aufruf von paint (weswegen die paint-Mathode innen zwar abgeändert werden kann, aber nicht anders benannt werden darf). repaint wird auch automatisch vom Computer aufgerufen, wenn das Fenster mit dem Programm verdeckt war oder der Bildschirm erneuert wird. Deswegen ist es sinnvoll, mit paint immer die gesamte bisherige Galgenfigur zu zeichnen und nicht nur den letzten Strich.
Auf Kästchenpapier habe ich zunächst eine grobe Skizze des zu zeichnenden Galgenmännchens angefertigt, um die Koordinaten der Endpunkte der einzelnen Striche (Strecken) ablesen zu können. Dabei habe ich bewusst darauf verzichtet, die echte Pixelzahl des in der Oberflächenklasse per Mausklick recht willkürlich aufgezogenen Panels zu verwenden, sondern gebe im Folgenden die Endpunkte 'relativ' an: so beginnt der Rumpf nicht im Punkt (7|5), sondern die x-Koordinate wird mit 7 Elfteln der Breite b und die y-Koordinate mit 5 Siebzehnteln der Höhe h angegeben: drawLine ((int)(7.0*b/11.0),(int)(5.0*h/17.0), (int)(8.0*b/11.0),(int)(9.0*h/17.0)), weil ich zufällig ein Seitenverhältnis von 11 zu 17 gewählt hatte. Die vielen (int)s führen ein TypeCasting auf die nächste, nicht größere ganze Zahl durch, d.h. schneiden die bei der Division evtl. auftretenden Kommastellen zum Schluss ab. Damit vorher aber richtig mit Kommastellen gerechnet wird, sind die Koordinaten als Dezimalzahlen 7.0 statt 7 angegeben. Der in Java als Rumpf des Männchens gezeichnete Strich ist übrigens etwas kürzer ausgefallen als geplant: nach meiner Skizze müsste der untere Punkt bei (8|10) bzw. (int)(8.0*b/11.0),(int)(10.0*h/17.0) liegen (statt wie in Zeile 71 programmiert bei (8|9)). Da die letztlich in der Oberfläche per Maus gewählte Bildgröße nicht genau das Seitenverhältnis 11 zu 17 hat (vgl. Klasse 1, Zeile 112), erscheint im Programm die Figur etwas schmaler als auf dem Papier und der Kopf wirkt länglich statt kreisrund. Andererseits würde auch bei einer Vergrößerung oder Verkleinerung des Grafikpanels in (oder mit) der Oberfläche die Figur immer bildfüllend angepasst gezeichnet und es müssen nicht immer wieder Pixel gezählt und verwendet werden.
001 import java.awt.*; 002 import javax.swing.*; 003 import javax.swing.event.*; 004 005 public class P9_A3a_Panel extends JPanel // erweitert/ändert normales Java-JPanel 006 // spezielles Grafik-Panel für die Galgenmann-Oberfläche zur Anzeige des Galgenmanns 007 // aus Linien und einem Kreis per Java-Zeichenbefehlen -- 17.5.2018 & 2.2.2019 r-krell.de 008 { 009 int zahl = 0; // Anzahl der bereits gezeichneten Striche 010 011 public void loesche() // löscht das Panel, indem alles weiß überstrichen wird 012 { 013 zahl = 0; 014 setBackground(Color.WHITE); 015 repaint(); // Systembefehl repaint ruft u.a. Methode paint zum Zeichnen auf 016 } 017 018 public void nächsterStrich() 019 { 020 if (zahl<10) 021 { 022 zahl++; // erhöht Anzahl der gezeichneten bzw. zu zeichnenden Striche 023 repaint(); // und ruft indirekt nachfolgende Zeichenmethode paint auf 024 } 025 } 026 027 public int zahl() // gibt auf Wunsch die Zahl der gezeichneten Striche aus 028 { 029 return (zahl); 030 } 031 032 // @Override // kann verdeutlichen, dass eine JPanel-Methode überschrieben wird 033 public void paint (Graphics g) // erweitert Standard-Methode um Galgenmann-Striche 034 { 035 super.paintComponent(g); // übergeordnete Graphikmethode für Hintergrund, Ränder..) 036 Graphics2D g2d = (Graphics2D) g; 037 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 038 RenderingHints.VALUE_ANTIALIAS_ON); 039 g2d.setStroke(new BasicStroke(3 /*, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND */ )); 040 int h = getHeight(); // misst Höhe h und .. 041 int b = getWidth (); // .. Breite b des Panels/Graphikfensters auf Oberfläche 042 043 if (zahl > 4) // ändert Zeichenfarbe fürs Männchen auf blau 044 { 045 g2d.setColor (Color.BLUE); 046 } 047 else // während der Galgen und der Strick schwarz sind 048 { 049 g2d.setColor (Color.BLACK); 050 } 051 052 switch (zahl) 053 // Achtung: Alle Fälle ohne "break", damit auch jeweils alle früheren Teile 054 // nochmal gezeichnet werden. Wichtig, falls Fenster durch anderes Programm 055 // verdeckt war. 056 { 057 case 10: setBackground(new Color(0xFFAFAF)); // farbiger Hintergrund signalisiert Ende 058 g2d.drawLine ((int)(8*b/11.0),(int)(9.0*h/17.0), 059 (int)(9.0*b/11.0),(int)(14.0*h/17.0)); // re. Bein 060 061 case 9: g2d.drawLine ((int)(8.0*b/11.0),(int)(9.0*h/17.0), 062 (int)(6*b/11.0),(int)(14.0*h/17.0)); // li. Bein 063 064 case 8: g2d.drawLine ((int)(7.3*b/11.0),(int)(6.0*h/17.0), 065 (int)(10.0*b/11.0),(int)(9.5*h/17.0)); // re. Arm 066 067 case 7: g2d.drawLine ((int)(7.3*b/11.0),(int)(6.0*h/17.0), 068 (int)(5.0*b/11.0),(int)(10.0*h/17.0)); // li. Arm 069 070 case 6: g2d.drawLine ((int)(7.0*b/11.0),(int)(5.0*h/17.0), 071 (int)(8.0*b/11.0),(int)(9.0*h/17.0)); // Rumpf 072 073 case 5: g2d.drawArc ((int)(5.0*b/11.0),(int)(4.0*h/17.0), 074 (int)(2.0*b/11.0),(int)(2.0*h/17.0),0,360); // Kopf 075 076 case 4: g2d.setColor (Color.BLACK); // Zeichenfarbe für Galgen und Seil 077 g2d.setStroke(new BasicStroke(1)); // Seil wird dünner gezeichnet 078 g2d.drawLine((int)(7.0*b/11.0),(int)(1.0*h/17.0), 079 (int)(7.0*b/11.0),(int)(5.0*h/17.0)); // Seil 080 081 case 3: g2d.setStroke(new BasicStroke(3)); // breitere Strichstärke für Galgen 082 g2d.drawLine((int)(1.0*b/11.0),(int)(3.0*h/17.0), 083 (int)(3.0*b/11.0),(int)(1.0*h/17.0)); // Galgen-Aussteifungs-Schräge 084 085 case 2: g2d.drawLine((int)(1.0*b/11.0),(int)(1.0*h/17.0), 086 (int)(8.0*b/11.0),(int)(1.0*h/17.0)); // Galgen-Arm (horizontaler Ausleger) 087 088 case 1: g2d.drawLine((int)(1.0*b/11.0),(int)(16.0*h/17.0), 089 (int)(1.0*b/11.0),(int)(1.0*h/17.0)); // Galgen-Vertikale 090 091 } // end of switch 092 } 093 } |
Die Mehrfach-Verzweigung switch..case wurde extra absteigend angelegt und ohne break geschrieben, damit bei jeder (Strich-) zahl immer auch alle vorangegangenen Striche mit niedrigerer Nummer nochmal gezeichnet werden, damit nie etwas fehlt, wenn das Bild zwischenzeitlich von einem anderen Programm verdeckt war.
In Zeile 39 könnte beim Einstellen der Strickdicke mit setStroke auch die Form des Zeichenwerkzeugs und die Art der Verbindung aneinander stoßender Linien gewählt werden. Die Standardeinstellungen bewähren sich aber, sodass zusätzliche Parameter wieder auskommentiert wurden. Auch die Zeilen 37 und 38 wären bei dieser einfachen Zeichnung wohl entbehrlich: bei schrägen Linien soll damit die sonst eventuell etwas stärker sichtbare Treppenstruktur reduziert werden.
Und natürlich könnte der jetzt auf zwei Zeilen verteilte Befehl setRenderingHint in den Zeilen 37 und 38 ebenso in eine Zeile geschrieben werden wie alle drawLine-Befehle in der Mehrfach-Verzweigung.
zum Seitenanfang / zum Seitenende
Wer sich nicht mit Koordinaten und dem drawLine-Befehl beschäftigen will, kann alternativ das Galgenmännchen auch ganz anders darstellen. In einem beliebigen Mal- oder Zeichen-Programm wird das Galgenmännchen einfach Strich für Strich gezeichnet und nach jedem Strich das Bild unter einem neuen Dateinamen gespeichert. Wer mit einem geeigneten Grafikprogramm vertraut ist, kann so einigermaßen schnell und ohne langes Nachdenken eine Bilderserie erzeugen (am besten wird die Bildgröße genau passend zur Größe des Panels galgen in Zeile 112 von Klasse 1 gewählt!). Die Bilder könnten etwa wie folgt aussehen, wobei auch ein leeres Bild gespeichert wurde und das letzte Bild andere Farben hat, um auf das Ende der Buchstaben-Abfrage hinzuweisen.
Weiterhin ist es vorteilhaft, die Bilder fortlaufend zu nummerieren, statt Dateinamen wie "eins.gif", "zwei.gif",.. oder .. "mit_linkem_Bein.gif", "mit_rechtem_Bein.gif",.. zu verwenden. Mit Nummer kann nämlich im Programmtext auf eine Mehrfachauswahl verzichtet werden. Weil die Bilder schon vollständig sind, könnten die Fälle in einer switch..case-Anweisung sowohl aufsteigend wie absteigend notiert werden, müssten aber immer mit break abgeschlossen werden. Aber bei Dateinamen mit Nummer kann das Programm den richtigen Dateinamen mit immer der gleichen, einen Zeile 41 erzeugen:
001 import java.awt.*; 002 import javax.swing.*; 003 import javax.swing.event.*; 004 import javax.imageio.*; 005 006 public class P9_A3b_Panel extends JPanel // erweitert/ändert normales Java-JPanel 007 // spezielles Grafik-Panel für die Galgenmann-Oberfläche zur Anzeige des Galgenmanns 008 // aus vorgefertigten Bildern -- 17.5.2018 & 2.2.2019 r-krell.de 009 { 010 int zahl = 0; // Anzahl der bereits gezeichneten Striche 011 012 public void loesche() // löscht das Panel, in dem alles weiß überstrichen wird 013 { 014 zahl = 0; 015 setBackground(Color.WHITE); 016 repaint(); // Systembefehl repaint ruft u.a. Methode paint zum Zeichnen auf 017 } 018 019 public void nächsterStrich() 020 { 021 if (zahl<10) 022 { 023 zahl++; // erhöht Anzahl der gezeichneten bzw. zu zeichnenden Striche 024 repaint(); // und ruft indirekt nachfolgende Zeichenmethode paint auf 025 } 026 } 027 028 public int zahl() // gibt auf Wunsch die Zahl der gezeichneten Striche aus 029 { 030 return (zahl); 031 } 032 033 // @Override // kann verdeutlichen, dass eine JPanel-Methode überschrieben wird 034 public void paint (Graphics g) // erweitert Standard-Methode um Galgenmann-Bildanzeige 035 { 036 if (zahl >=0 && zahl <= 10) 037 { 038 Image bild = null; // kein Bild, falls fertiges Bild nicht gefunden wird 039 super.paintComponent(g); 040 Graphics2D g2d = (Graphics2D) g; 041 String dateiname = "bilder/g_"+zahl+".gif"; // mit Name des Unterverzeichnis .. 042 // .. unter dem Ordner des Programmtextes 043 try 044 { 045 bild = ImageIO.read(getClass().getResource(dateiname)); 046 } // Befehl zum Holen des Bildes 047 catch (Exception ex) 048 { 049 System.out.println("** Grafikprobleme ["+dateiname+"]: "+ex); // falls Bilddatei nicht .. 050 } // .. gefunden/geholt werden konnte 051 052 if ( bild != null ) 053 { 054 g2d.drawImage(bild, 0, 0, this ); // zeigt gefundenes Bild ab linker oberer Ecke (0|0) .. 055 } // .. in diesem Panel ('this') an 056 } 057 } 058 } |
Die Zeilen 1 bis 34 sind natürlich genau wie bei der Variante a). Auch gilt alles dort gesagte zum Einsatz von repaint. Der Quelltext der Methode paint ist ohne switch..case kürzer; allerdings sieht man hier zusätzlich eine try..catch-Anweisung. Weil Java Dateioperationen wie das Holen von Bildern für besonders fehleranfällig hält - vielleicht wurde der Ordner mit den Bildern versehentlich gelöscht, wurde der USB-Stick mit den Dateien gar nicht eingesteckt, sind die Dateinamen doch anders oder wurden die Bilder in einem von Java nicht unterstützten Format abgespeichert - darf die Zeile 45 nicht einfach so hin hingeschrieben werden, sondern kann nur versuchsweise (im try-Block) ausprobiert werden. Wenn das Bild nicht geholt werden kann, springt das Programm in den catch-Block, hier zu einer Fehlermeldung. Ein erfolgreich geholtes Bild wird schließlich in Zeile 54 angezeigt (wobei drawImage aus Zeile 54 eigentlich auch direkt nach Zeile 45 im try-Block stehen könnte, was die if-Abfrage in Zeile 52 überflüssig macht).
Jetzt muss in der Klasse 1 nur noch die Zeile 37 (und der Titel in Zeile 51) auf _A3b_ abgeändert werden, und das Galgenmann-Spiel kann mit dieser Grafik gespielt werden.
zum Seitenanfang / zum Seitenende
Das nachfolgende, mit dem Javaeditor erstellte UML-Diagramm verdeutlicht am Beispiel von P9_A3a nochmal den Zusammenhang zwischen den drei gerade vollständig vorgestellten Klassen. Der Javaeditor zeigt auch die Attribute an (hier f und galgen), die der Verknüpfung der Klassen durch Kompositionen dienen, und hebt sie sogar fett hervor. Normalerweise werden aber die von Java bzw. Swing mitgelieferten Oberklassen JFrame und JPanel nicht dargestellt; sie wurden hier extra angedeutet.
zum Seitenanfang / zum Seitenende
Zum Ausprobieren biete ich die beiden lauffähigen, spielbaren Galgenmann-Programme an, jetzt in der Version vom Februar 2019. Das Aussehen von P3_A3a kennen Sie ja bereits vom Anfang dieser Seite; die andere Version sieht fast genau so aus. Es lässt sich am fertigen Programm nicht ohne Weiteres erkennen, wie die Grafik erzeugt wird.
Wenn Ihr Browser das zulässt (und Sie die Java-JRE 1.5 oder höher installiert haben - es muss hier nicht die allerneueste Fassung sein), können Sie jedes der beiden Programme direkt hier aus dem Browser heraus starten ("Webstart"). Gibt es Probleme oder wollen Sie Galgenmännchen auch unabhängig vom Besuch dieser Webseite spielen, empfiehlt sich das Herunterladen der jar-Datei(en). Der Download sollte immer gelingen. Bei installierter JRE können Sie die herunter geladene (=gespeicherte) jar-Datei später auf Ihrem Computer per Doppelklick ausführen und so das Galgenmännchen spielen.
[Die kostenlose, aktuelle JRE (Java Runtime Environment; Java Ausführungs-Umgebung) erhalten Sie immer direkt von Oracle (https://java.com/de/download/, wo im Februar 2019 die auch für 64-Bit-Betriebssysteme völlig ausreichende und empfohlene 32-Bit-Version 1.8 bzw. "Version 8 Update 201" von Mitte Januar 2019 angeboten wurde), oder - nicht immer ganz so aktuell - auch von den Webseiten vieler Computerzeitschriften, wie etwa vom PC-Magazin)].
Neue Versionen vom Februar 2019 |
Webstart (jnlp-Datei nicht speichern/herunterladen, sondern mit dem Launcher öffnen und Risiko akzeptieren zum Ausführen) |
Download (jar-Datei auf den eigenen Rechner herunter laden und dort später ausführen) *) |
Wort-Raten mit Java-Grafik | Webstart P9_A3a_Galgenmann.jnlp | P9_A3a_Galgenmann(r-krell.de).jar (12kB) |
Wort-Raten mit DiaShow | Webstart P9_A3b_Galgenmann.jnlp | P9_A3b_Galgenmann(r-krell.de).jar (35kB) |
*) Achtung: Manche Browser (wie etwa MS Edge) verändern beim Download einer .jar-Datei deren Endung automatisch und ungefragt in .zip. Sollte das bei Ihnen passieren, müssen Sie die Endung (trotz Warnung) erst wieder in .jar ändern. Durch das Umbenennen wird die Datei keineswegs unbrauchbar, sondern ist im Gegenteil erst dann wieder durch Doppelklick auf den Dateinamen ausführbar. Voraussetzung für den Programmstart per Doppelklick ist eine installierte Java-Laufzeitumgebung (JRE).
zum Seitenanfang / zum Seitenende
zurück zur Informatik-Hauptseite
zurück zur vor-vorigen Seite (Programme P1 bis P4) Seite
b1)_2017
zurück zur vorigen Seite (Programme P5 bis P9) Seite b2)_2017
weiter zur nächsten Seite (Programme P10 bis P12) Seite b4)_2018