www.r-krell.de
Webangebot für Schule und Unterricht, Software, Fotovoltaik und mehr

Willkommen/Übersicht  >  Informatik  >  Java-Seite b4) (2018)

Informatik mit Java

Teil b4)_2018
Programme P10 bis P12: Verschiedene Autorennen



Eine vollständige Übersicht aller meiner Seiten "Informatik mit Java" gibt's auf der Informatik-Hauptseite!
Diese Seite ist der letzte Teil einer Reihe über das Programmieren in der Einführungsphase eines gymnasialen Informatik-Grundkurses (erstes Jahr der SII) unter Verwendung des Java-Editors und seines GUI-Builders; frühere Seiten der Reihe sind die Seiten b1)_2017, b2)_2017 und b3)_2018.

Auf dieser Seite b4)_2018 finden Sie:


zum Seitenanfang / zum Seitenende



Autorennen

Bereits auf vielen anderen Seiten meines Webangebots finden sich Programme zur Simulation eines Autorennens - in durchaus unterschiedlichen Programmier-Sprachen, u.a.

Die Schülerinnen und Schüler in meinen früheren Informatik-Kursen hatten ihre Autorennen immer gerne entwickelt. Für mich lag es daher nahe, eine ähnliche Aufgabenstellung für 'normales' Java (ohne Stift&Co) auch in meinem letzen Anfänger-Grundkurs zu stellen. Nachdem ich aber mit den Programmen P7 bis P9 Vorschläge der Kursteilnehmer(innen) aus dem laufenden Unterricht aufgegriffen hatte (siehe Seite b2)_2017), blieb am Schuljahresende für das Autorennen keine Zeit mehr - vielleicht zum Glück, denn in im Folgenden zeigt sich, dass der in allen früheren Beispielen schnell erfolgreiche intuitive Ansatz bei der Java-Swing-Oberfläche auf Schwierigkeiten stößt, die erst mit zusätzlicher Hilfe bzw. Kenntnissen über so genannte threads überwunden werden können.

Die Aufgabe besteht darin, Autos auf einer geraden, mehrspurigen Rennstrecke fahren zu lassen und schließlich den Sieger auszurufen. Dabei fährt jedes Auto in einer eigenen Spur. Statt mit einem echten Formel-1-Rennen auf einer komplexen Rennstrecke ist die Aufgabe also eher mit der Kamel- oder Pferderennsimulation auf der Kirmes vergleichbar, wo Blech-Kamele bzw. Blech-Pferde auf parallelen Bahnen weiter rücken, wenn es dem zum Tier gehörenden Kirmes-Besucher gelingt, einen Ball in ein passendes Loch zu werfen. Ob die Autos durch die Schriftzeichen "O>o" in Textzeilen oder durch Grafiken repräsentiert werden, spielt nur eine untergeordnete Rolle.

Die Bilder zeigen zwei unterschiedliche Autorenn-Varianten vorm (Neu-)Start bzw. nach Erreichen des Ziels mit dem schnellsten Fahrzeug:

Bildschirmfoto: Autorennen von zufällig bewegten Autos in Textzeilen, hier: vorm Start   Bildschirmfoto: Autorennen von grafischen Autos mit Tastenantrieb beim Ziel



Spielbare Versionen der beiden abgebildeten Programme finden sich fast am Ende dieser Seite. Zuvor wird ihre Programmierung erläutert. Ganz unten folgen weiterführende Aufgaben.


zum Seitenanfang / zum Seitenende

Programmierprojekt für den Informatik-Grundkurs

Die Programme P10 bis P12 bilden - wie oben erwähnt - den Abschluss einer Programmier-Reihe in meinem Webangebot für Schülerinnen und Schüler im Anfängerjahr, d.h. in der Einführungsphase eines Informatik-Grundkurses der gymnasialen Oberstufe. Dabei steht nicht die Beherrschung kleinster Eigenheiten einer Programmiersprache, sondern die Erarbeitung wichtiger Konzepte und grundsätzlicher Ideen der Informatik am motivierenden Beispiel im Vordergrund. Ein handliches, leicht zu bedienendes Werkzeug erleichtert die Arbeit und erlaubt wegen der automatischen Quelltext-Erzeugung den Verzicht auf die detaillierte theoretische Auseinandersetzung mit einigen Programmkonstruktionen. 'Rapid prototyping' soll durch schnelle Ergebnisse und Rückmeldung zur Auseinandersetzung mit dem Programmierten und der sinnvollen Weiterentwicklung motivieren.



Werkzeug Javaeditor ..

Das Autorennen wird in Java programmiert. Die Oberfläche soll - wie schon bei den Programmen P1 bis P9 - weitgehend mit dem integrierten GUI-Builder des gut zu bedienenden Javaeditors erstellt werden (Diesen sehr empfehlenswerten Javaeditor von Gerhard Röhner stelle ich im Teil a) von Informatik mit Java vor, habe ihn immer im Schul-Unterricht und für eigene Java-Projekte benutzt und zeige ihn auf ganz vielen Seiten meines Webangebots; seine offizielle Webseite ist javaeditor.org).

.. und Vorgehensweise

Das Vorgehen beim Autorennen-Projekt folgt dem Spiralmodell, d.h. nach Erstellen der Oberfläche werden die Funktionen nach und nach hinzugefügt, getestet und evtl. verbessert bzw. ergänzt. Insofern soll die erste lauffähige Version nach Erstellen der Oberfläche (bei P10 mit Textfeldern) beim Start zunächst nur ein Fahrzeug automatisch ins Ziel fahren. Der Weg dahin und weitere Stationen bzw. Phasen der Programmentwicklung werden im Folgenden beschrieben und mit Java-Quelltext vorgestellt.




zum Seitenanfang / zum Seitenende

Programm P10: Autorennen mit Textfeldern
und automatischem Antrieb der Autos durch das Programm



Oberfläche P10_Autorennen.java,
intuitiver Ansatz

Die Oberfläche wird dadurch erstellt, dass mit dem im Javaeditor eingebauten GUI-Builder verschiedene Objekte aus der Swing-1-Werkzeugleiste mit der Maus auf einen JFrame gezogen werden. Den zugehörigen Quelltext erzeugt der Javaeditor automatisch, wobei Namen und Texte im Objekt-Inspektor verändert bzw. eingegeben werden können und so in den Programmtext gelangen.

Bildschirmfoto: Javaeditor mit GUI-Builder für Oberfläche P10_Autorennen

Der automatisch erzeugte Quelltext wird - anders als z.B. in "P9_A3_Galgenmann.java" auf Seite b3_2018) - jetzt nicht vollständig aufgeführt, sondern z.T. durch [...] abgekürzt. Das Augenmerk soll auf die Rahmenmethoden für die beiden Schaltflächen gerichtet werden, die am Ende der Datei "P10_Autorennen.java" vom Javaeditor (bzw. seinem integrierten GUI-Builder) automatisch leer erzeugt wurden:




import [...]
/**
  * Autorennen mit Textfeldern, hier: GUI-Oberfläche
  * @author  R. Krell,  5/2018
  */

public class P10_Autorennen extends JFrame {
  
// Anfang Attribute
    [...]   
    setVisible(
true);
  } 
// end of public P10_Autorennen
  
  
// Anfang Methoden
  
  
public static void main(String[] args) {
    
new P10_Autorennen();
  } 
// end of main
  
  
  
public void jBtAufstellen_ActionPerformed(ActionEvent evt) {
    
// TODO hier Quelltext einfügen

  } // end of jBtAufstellen_ActionPerformed

  public void jBtStart_ActionPerformed(ActionEvent evt) {
    // TODO hier Quelltext einfügen

  } 
// end of jBtStart_ActionPerformed

  // Ende Methoden
// end of class P10_Autorennen
 

Das vorstehende Programm kann schon kompiliert und fehlerfrei gestartet werden, reagiert aber noch nicht auf das Drücken der Schaltflächen. Das Programm weiß ja noch nicht, was es dann tun soll. Dazu muss erst an den beiden mit  // TODO hier Quelltext einfügen  gekennzeichneten Stellen von Hand passender Programmtext eingetippt werden:

Das führt jetzt zu folgenden Methoden:

  
  
public void jBtAufstellen_ActionPerformed(ActionEvent evt) {
    jTfAuto1.setText(
"O>o");
    jTfAuto2.setText(
"O>o");
    jTfAuto3.setText(
"O>o");
    jTfAusgabe.setText(
"");
  } 
// end of jBtAufstellen_ActionPerformed

  public void jBtStart_ActionPerformed(ActionEvent evt) {
    
// erster Versuch
    String auto1; // Auto mit führenden Leerzeichen "O>o", " O>o", .. 
    for (int schritt=0; schritt<47 ;schritt++)  // nach 47 Schritten stößt das Auto rechts an
    {
      auto1 = jTfAuto1.getText();  
// Auto mit bisher zurückgelegter Strecke
      jTfAuto1.setText(" "+auto1); // Auto rückt um eine zusätzliche Stelle " "+ weiter nach rechts
    } // end of for
  
// end of jBtStart_ActionPerformed
 

Der so ergänzte Programmtext wird wieder kompiliert und ausgeführt. Bei Knopfdruck passiert jetzt tatsächlich auch etwas, allerdings ist der Erfolg beim Betätigen des "Start"-Knopfes noch unbefriedigend: Offenbar ist der Computer viel zu schnell und man sieht gar nicht, wie das Auto fährt, weil es sofort am rechten Rand ankommt (oder etwas früher hält, wenn man statt 47 z.B. nur 35 angegeben hat. Bei 54 wäre das Auto hingegen sofort verschwunden, weil es über das Ziel hinaus geschossen und nicht mehr im Textfeld sichtbar ist). Wenigstens setzt der "Auf die Plätze.."-Knopf das Auto in jedem Fall wieder an den linken Rand auf die Startposition.

In dieser Situation würde ich meinen Schülerinnen und Schülern dann die (Hilfs-)Methode warte angeben bzw. austeilen, mit der das Fahren verlangsamt werden kann. Sicher kommen die Programmierer(innen) dann rasch auf die Idee, nach jedem Schleifendurchgang etwa eine zehntel Sekunde zu warten, bevor das Auto eine (Leer-)Stelle weiter nach rechts geschoben wird. Möglicherweise muss noch überlegt werden, dass dazu der warte-Aufruf in den Schleifenkörper und nicht davor oder dahinter geschrieben werden muss. Der schließlich gefundene, nachfolgende Programmtext sollte aber den gewünschten Effekt haben:

  
  
private void warte (int ms)  // Zeit in Millisekunden, 100 = 0,1 Sekunde Wartezeit
  {
    
try
    {
      Thread.sleep (ms);
    }
    
catch (InterruptedException e)
    {
      System.out.println(
"** Fehler beim Warten: "+e);
    }
  }
  
  
public void jBtAufstellen_ActionPerformed(ActionEvent evt) {
    jTfAuto1.setText(
"O>o");
    jTfAuto2.setText(
"O>o");
    jTfAuto3.setText(
"O>o");
    jTfAusgabe.setText(
"");
  } 
// end of jBtAufstellen_ActionPerformed

  public void jBtStart_ActionPerformed(ActionEvent evt) {
    
// zweiter Versuch, mit warte
    String auto1; // Auto mit führenden Leerzeichen "O>o", " O>o", .. 
    for (int schritt=0; schritt<47 ;schritt++)  // nach 47 Schritten stößt Auto rechts an
    {
      auto1 = jTfAuto1.getText();  
// Auto mit zurückgelegter Strecke
      jTfAuto1.setText(" "+auto1); // Auto rückt eine Stelle weiter nach rechts
      warte(100);  // extra hinzugefügt, weil Auto sonst sofort am Ziel anlangt
    } // end of for
  } // end of jBtStart_ActionPerformed
 


Leider ist das Ergebnis wieder enttäuschend: Der "Start"-Knopf bleibt auch nach kurzem Antippen jetzt immer 4,7 Sekunden lang eingedrückt, ohne dass währenddessen irgendeine Bewegung des Autos sichtbar wird - es bleibt offenbar am Start stehen. Wenn die Schaltfläche dann endlich wieder hochkommt, als wäre sie gerade erst losgelassen worden, steht das Auto schlagartig rechts am Ziel. Vom erhofften allmählichem Fahren ist nichts zu erkennen. Dabei war die Programmieridee durchaus richtig, wie man mit einer zusätzlichen Zeile im Programmtext leicht nachweist (nachfolgende rot eingerahmte Zeile 125): in der Konsole bzw. im Meldungsfenster des Javaeditors wird während der 4,7 Sekunden, in der sich in der Oberfläche nichts ändert, trotzdem alle Zehntelsekunde immer wieder eine neue Zeile mit einem jeweils etwas weiter gerücktem Auto ausgedruckt! Nur dummerweise nicht in der eigentlichen Oberfläche.



Bildschirmfoto; Javaeditor mit Zusatzzeile 125, Kontrollausgabe und laufendem Programm P10

Das überrascht sogar etwas, denn die Variable auto1 holt sich ja den Bewegungsfortschritt zwischendurch immer wieder aus dem Textfeld jTfAuto1 (Zeile 122). Bliebe das Auto wirklich am Start stehen, dürfte auch im Kontrollausdruck im Meldungsfenster kein Fahren erfolgen. Offenbar wird das Textfeld intern durchaus aktualisiert, nur ist auf dem Bildschirm davon nichts zu sehen. Tatsächlich stoßen wir hier auf eine Eigenart der Swingoberfläche: Wird eine Schaltfläche gedrückt, führt Java erst die damit verbundene Aktion vollständig aus (wartet hier also, bis der Programmtext der Zeilen 118 bis 126 bis zum Ende abgearbeitet ist), bevor Java zur Oberfläche zurück kehrt und diese neu darstellt. Zwischendurch gemachte Änderungen bleiben unsichtbar; erst der Zustand der Oberfläche am Ende der Aktion wird wieder angezeigt! Bei Objekt-PAL (s.o.), bei der Java-Bibliothek Stift&Co (vgl. mein älteres java-autorennen.htm) oder bei der Webseiten-Sprache Javascript (vgl. if-javascript2.htm) gibt es dieses Warten der Oberfläche auf den Abschluss der ausgelösten Aktion nicht, sodass dort eine vergleichbare Programmkonstruktion wie die oben entwickelte wunderbar funktioniert.




zum Seitenanfang / zum Seitenende

Nebenläufige Prozesse:
P10_AutoThread.java und Zufallsrennen

Die Lösung des beschriebenen Java-Swing-Warte-Problems gelingt mit einem zusätzlichen Thread, einem nebenläufigen Programmprozess, der quasi gleichzeitig mit dem Haupt-Thread der Oberfläche ausgeführt werden kann. Die Oberfläche würde nur noch warten, bis der neue Thread zu Bewegung des Autos angestoßen ist, und sofort zur Anzeige zurück kehren. Der "Start"-Knopf würde sofort losgelassen und die Oberflächengraphik erneut angezeigt, während gleichzeitig in dem parallelen Thread das Auto noch bewegt wird. Meldet der neue Thread ständig die aktuelle Position des Autos an die Oberfläche, könnte sie dort laufend angezeigt werden, d.h. das Auto würde auch in der Oberfläche fahren.

Die Entwickler von Java haben in der Swing-Bibliothek auch den Bauplan für einen so genannten Swingworker-Thread bereit gestellt, der im Hintergrund arbeiten und über zwei vorgegebene Methoden mit der Swingoberfläche kommunizieren kann. Dieser Swingworker-Thread soll aber im Folgenden nicht verwendet werden. Die richtige Nutzung der vorgegebenen Methoden erfordert einige Einarbeit und sie sind trotzdem für unsere Aufgabenstellung ungünstig. Vielmehr lässt sich der sinnvolle Thread-Gebrauch leichter am allgemeinen Fall studieren. (Solche vom allgemeinen Fall abgeleitete Threads werden übrigens auch in einigen anderen meiner Programme verwendet, z.B. im Client und im Server auf meiner Netzwerk-Seite Informatik mit Java, Teil i) oder auch im Kartenspiel "Rot & Schwarz", einem Beispielprojekt zum Software-Engineering SWE-2.)

Ein Thread muss in Java von der allgemeinen Klasse Thread abgeleitet werden ("erben") und eine Methode run ohne Parameterübergabe und ohne Rückgabewert erhalten, die sich um die auszuführende Aktion kümmert. Würde in der ActionPerformed-Methode des "Start"-Knopfes allerdings diese run-Methode des Thread-Objekts direkt aufgerufen, wird wie bisher auf den Abschluss der Aktion gewartet und es wäre nichts gewonnen. Anders hingegen, wenn bei der Schaltflächen-Betätigung die im Programmtext gar nicht sichtbare vordefinierte Methode start aufgerufen wird: dann wird run automatisch im Hintergrund angestoßen, die Kontrolle kehrt sofort zur aufrufenden Stelle zurück und es wird nicht gewartet: Das aufrufende Programm läuft wieder weiter, während parallel noch der angestoßene Thread arbeitet. Beide Prozesse laufen praktisch unabhängig voneinander nebeneinander her.

Der Austausch von Informationen kann trotzdem gelingen. Dazu werden im Hauptprogramm definierte und erzeugte (nicht-primitive) Variablen bzw. Objekte einmal an den Thread übergeben (bevor run oder start aufgerufen werden, also am besten schon mit dem Konstruktor). Der Thread kennt dann die (Speicheradresse der) Variablen oder Objekte und kann deren Inhalte ändern, sodass automatisch auch im Hauptprogramm (und jeder anderen Stelle, die auf die gleichen Objekte zugreift) sofort die neuen Inhalte bekannt sind. Im Beispiel soll die ganze Textzeile, in der das Auto fährt, an einen Thread übergeben werden, der dort das Auto jeweils nach kurzer Wartezeit mit immer mehr führenden Leerzeichen hinein schreibt. Im Hauptprogramm bzw. der Swingoberfläche steht das veränderte Oberflächenobjekt unmittelbar zur Verfügung und zwingt die Oberfläche nach jeder Änderung zur Aktualisierung seiner Darstellung auf dem Bildschirm.

Der Thread wird in einer eigenen Datei "P10_AutoThread.java" notiert:


// R. Krell, 27.5.2018 (www.r-krell.de)
// Bewegung eines Autos für P10_Autorennen

import javax.swing.*;

public class P10_AutoThread extends Thread
{
  
int nummer;          // Nummer des Autos, also 1, 2 oder 3
  JTextField rennbahn; // Textfeld der Oberfläche, in dem das Auto fährt
  int max;             // Anzahl der Leerzeichen bis zum Ziel (hier 47)
  JTextField ausgabe;  // Textfeld der Oberfläche für die Siegermeldung
  
  
public P10_AutoThread (int nr, JTextField tf, int ende, JTextField jTfAus)
  {
    
super();           // Konstruktoraufruf der Thread-Oberklasse
    nummer = nr;       // Übernahme der kopierten primitiven Werte ...
    rennbahn = tf;     // .. und der übergebenen Objekte.
    max = ende;        // Achtung: Nur die Objekte ..
    ausgabe = jTfAus;  // .. werden gemeinsam mit Hauptprogramm genutzt.
  }
  
  
private void warte (int ms)  // Zeit in Millisekunden, 100 = 0,1 Sekunde Wartezeit
  {                    // (Aus der Oberfläche kann warte wieder weg)
    try
    {
      Thread.sleep (ms);
    }
    
catch (InterruptedException e)
    {
      System.out.println(
"** Fehler beim Warten: "+e);
    }
  }
  
  
public void run()                // diese Methode wird bei start aufgerufen!
  {                                // Variante 1: mit fester Wartezeit von 100 Millisekunden
    String auto; 
                  // Auto mit führenden Leerzeichen "O>o", " O>o", .. 
    for (int schritt=0; schritt<max ;schritt++)  // nach max Schritten stößt Auto rechts an
    {
      auto = rennbahn.getText();   
// Auto mit zurückgelegter Strecke
      rennbahn.setText(" "+auto);  // Auto rückt eine Stelle weiter nach rechts
      warte(100);  // damit sich das Auto langsam bewegt
    } // end of for   
    ausgabe.setText (ausgabe.getText()+"Auto "+nummer+" im Ziel!  ");
  }
}
 


Der AutoThread wurde extra so allgemein gehalten, dass er nicht nur fürs erste, sondern später auch für weitere Autos unverändert benutzt werden kann. In der Oberfläche "P10_Autorennen,java" wird eine entsprechend abgeänderte Methode jBtStart_ActionPerformed aufgerufen (s.u.); die Methode jBtAufstellen_ActionPerformed bleibt natürlich unverändert, während die warte-Methode jetzt ja inP10_AutoThread steht, in der Oberfläche nicht mehr gebraucht wird und dort gelöscht werden kann.


  public void jBtStart_ActionPerformed(ActionEvent evt) {
    
// dritter Versuch, mit einem Thread (für Auto 1)
    P10_AutoThread t1 = new P10_AutoThread( 1, jTfAuto1, 47, jTfAusgabe);      
    t1.start();    
// stößt die run-Methode des Threads t1 an
  } // end of jBtStart_ActionPerformed
 
 


Der Test zeigt, dass nun das aus den beiden Klassen P10_Autorennen und P10_AutoThread bestehende Programm endlich wunschgemäß funktioniert: Nach Druck auf "Start" bewegt sich das erste Auto sichtbar bis ins Ziel. Das ermutigt, jetzt auch die anderen beiden Auto ebenso zu bewegen, wofür wieder nur die ActionPerformed-Methode in der Oberfläche P10_Autorennen erweitert werden muss:




  public void jBtStart_ActionPerformed(ActionEvent evt) {
    
// vierter Versuch, mit je einem Thread pro Auto
    P10_AutoThread t1 = new P10_AutoThread( 1, jTfAuto1, 47, jTfAusgabe);  
    P10_AutoThread t2 = 
new P10_AutoThread( 2, jTfAuto2, 47, jTfAusgabe); 
    P10_AutoThread t3 = 
new P10_AutoThread( 3, jTfAuto3, 47, jTfAusgabe);     
    t1.start();    
// stößt die run-Methode des Threads t1 an: Auto 1 startet
    t2.start();    // stößt die run-Methode des Threads t2 an: Auto 2 startet
    t3.start();    // stößt die run-Methode des Threads t3 an: Auto 3 startet    
  } // end of jBtStart_ActionPerformed
 
 


Nach dieser Änderung funktioniert das Rennen schon. Allerdings sind die Autos auf dem Bildschirm scheinbar gleich schnell: sie fahren immer nebeneinander. Das verwundert nicht, denn die drei Autos fahren mit den parallel laufenden Threads t1, t2 und t3 (alle nach dem gleichen Bauplan von P10_AutoThread) auch alle gleich schnell: immer wird die Strecke von einem Leerzeichen in einer Zehntelsekunde zurück gelegt. Deshalb erstaunt es eher, dass bei mehrfachem Start unterschiedliche Zieleinläufe gemeldet werden: mal findet sich dort die Meldung "Auto 3 im Ziel! Auto 1 im Ziel! Auto 2 im Ziel! ", während beim nächsten Rennen "Auto 2 im Ziel! Auto 3 im Ziel! Auto 1 im Ziel! " oder irgendein anderer, nicht vorhersehbarer Zieleinlauf gemeldet wird, obwohl kein Auto sichtbar schneller war. Grund hierfür ist, dass unvermeidbare minimalste Zeitunterschiede von vielleicht weniger als einer tausendstel Sekunde zwischen den Threads (die in Wirklichkeit übrigens nicht unbedingt parallel, sondern meist abwechselnd stückchenweise quasi-parallel abgearbeitet werden) sich bei der Zielmeldung auswirken. Außerdem können nicht alle drei Threads wirklich gleichzeitig jeweils mit ihrer ausgabe auf das eine Objekt jTfAusgabe zugreifen - Java muss solche Zugriffe immer irgendwie hintereinander ausführen.

Natürlich eignet sich diese Variante schon, um spannende Wetten auf den Zieleinlauf abzuschließen. Schöner wäre allerdings, wenn die Autos auch erkennbar unterschiedlich schnell wären. Das kann erreicht werden, wenn im Thread nicht immer die gleiche Zeit für die Bewegung um eine Leerstelle gewartet wird, sondern zufällige Zeiten verwendet werden. Da der Zufallsgenerator Math.random() Kommazahlen zwischen 0.00000 und 0.99999 liefert, muss mit verschiedenen Mindestwartezeiten (hier 70) und Faktoren (im Beispiel 200) experimentiert werden, bis man mit der Bewegung aller drei Rennwagen zufrieden ist. Die nachfolgend nicht mehr aufgeführte warte-Methode bleibt natürlich ebenso wie der Konstruktor unverändert im AutoThread.



  
  
private void warteZufällig()
  {
    warte((
int)(70.0 + 200.0*Math.random()));   // erzeugt zufällige Wartezeit, hier zwischen 70 und 269 Millisekunden
  }
  
  
public void run()                // diese Methode wird bei start aufgerufen!
  {                                // Variante 2: mit variabler Wartezeit
    String auto; 
                  // Auto mit führenden Leerzeichen "O>o", " O>o", .. 
    for (int schritt=0; schritt<max &&(ausgabe.getText()).equals(""); schritt++)  // nach max Schritten stößt Auto rechts an
    {
      auto = rennbahn.getText();   
// Auto mit zurückgelegter Strecke
      rennbahn.setText(" "+auto);  // Auto rückt eine Stelle weiter nach rechts
      warteZufällig();             // damit sich das Auto mit zufälliger Geschwindigkeit bewegt
    } // end of for   
    ausgabe.setText (ausgabe.getText()+"Auto "+nummer+" im Ziel!  ");
  }

 


Mit dem optionalen Zusatz && (ausgabe.getText()).equals("") (im Programmtext braun hervor gehoben) stoppen die Autos übrigens, wenn das erste im Ziel ist (und wegen dessen Ziel-Ankufts-Meldung das gemeinsam genutzte Ausgabefeld nicht mehr leer ist). Dann erinnert das Autorennen mehr an das anfangs erwähnte Kirmesspiel. Allerdings sollte dann oben auch noch die Meldung über den angeblichen Zieleinlauf des zweiten und dritten Autos unterdrückt werden, der ja gar nicht mehr stattfindet. Ohne die Zusatzbedingung in der for-Schleife endet das Zufalls-Rennen erst, wenn alle Autos im Ziel sind. Beidesmal lädt das jetzt erfolgreich programmierte Zufallsrennens aber zum Wetten auf das Spielergebnis ein.




zum Seitenanfang / zum Seitenende

Programm P11: Autorennen mit Textfeldern
und Antrieb der Autos durch Benutzereingaben



Nach der ersten Freude über den Programmiererfolg von P10 und einigen gewonnene Wetten über den Zieleinlauf der Autos stellt sich beim Spiel von P10 möglicherweise bald Langeweile ein, weil man nicht ins Spielgeschehen eingreifen kann, sondern zum passiven Zuschauen verurteilt ist. Das soll jetzt geändert werden. Drei menschliche Spieler sorgen durch Tastendrücke jeweils für die Bewegung 'ihres' Autos. Gespielt wird gemeinsam an einem Computer; die Tasten [Q], [B] und [P] liegen weit genug auseinander und können ohne große gegenseitige Behinderung von drei Leuten auf einer Tastatur erreicht werden.

Nach jedem Tastendruck darf sich das Auto aber nur um ein kleines Stückchen (etwa um eine Leerstelle) bewegen. Würde es dann von alleine weiterfahren, wäre ja keine weitere Interaktion mehr nötig und möglich. Anders als oben in P10_AutoThread darf also keine for-Schleife mehr im Programmtext stehen. Nach jedem Bewegungsschritt muss das Auto vielmehr auf den erneuten Druck seiner Taste warten.

Sicher gibt es verschiedene Möglichkeiten, diese Anforderungen umzusetzen. In der Schule sollen auf jeden Fall verschiedene Ideen und Ansätze überlegt und diskutiert werden. Für die Darstellung auf dieser Webseite habe ich mich für eine Realisierung entschieden, die auf dem bisher (in den Programmen P1 bis P10) Erlernten fußt und auch von Schülerinnen und Schülern nach einem Jahr Informatik-Grundkurs gefunden, zumindest aber durchschaut und programmiert werden kann. Um die direkte Tastenabfrage nicht auch noch selbst programmieren zu müssen, soll eine Eingabezeile genutzt werden (Eingabezeilen sind schon seit dem Programm P1 bekannt und im Gebrauch). Alle Tastendrücke erfolgen in dieses gemeinsame Textfeld und können dann von einer zentralen Auswerte-Methode gelesen und in die Bewegungen der Autos umgesetzt werden:

Struktogramm der Auswertung für P11

[Das Struktogramm wurde übrigens auch mit dem Javaeditor erstellt (Datei > Neu > Struktogramm). Bei Druck auf das J am linken Rand erzeugt der Javaeditor aus dem Diagramm sogar automatisch eine Java-Klasse mit passendem Programmtext, der dann allerdings noch stark nachgearbeitet werden muss, weil zur Diagramm-Beschriftung ja Umgangs- statt Programmiersprache verwendet wurde. Aber die Kontrollstrukturen sind alle mit richtiger Klammerung zu finden und der deutsche Text zeigt, wo welche Java-Formulierung hin muss. Insofern ist der kompakte, aber vielseitige und trotzdem leicht zu bedienende Javaeditor mit dem zusätzlich zum Texteditor u.a. integrierten GUI-Builder, dem UML-Klassen- und dem Struktogrammeditor inzwischen ein echtes CASE-Tool für die computerunterstützte Softwareentwicklung (Computer-Aided Software Engineering)].

Klar ist, dass die Auswertung wieder parallel zur Oberfläche ausgeführt werden muss (also in einem eigenen Thread), damit in der Oberfläche gleichzeitig weitere Tastendrücke empfangen und die Bewegungen der Autos dargestellt werden können. Denn die Schleife in der Auswertung und damit die Auswertung selbst endet ja erst, wenn - je nach Wunsch - ein Auto oder alle Autos im Ziel sind. Und zwischendurch soll die Oberfläche ja ständig den aktuellen Zwischenstand anzeigen. Die Bewegung eines Autos um ein kleines Stück muss hingegen nicht mehr unbedingt in einem Thread erfolgen - sie nimmt ja nur ganz kurze Zeit in Anspruch, sodass die Auswertung (die ohnehin auf Tastendrücke warten muss und meist im ganz rechten Kanal leer läuft), problemlos darauf warten kann.

Entsprechend scheint zur Bewegung eines/jedes Autos um ein Stück die nachfolgende Klasse P11_Auto geeignet (die als abgespeckte Version aus P10_AutoThread hervorging):


// R. Krell, 28.5.2018 (www.r-krell.de)
// Bewegung eines Autos um ein Stück für P11_Autorennen bzw. P11_Auswertung

import javax.swing.*;

public class P11_Auto
{
  
int nummer;          // Nummer des Autos, also 1, 2 oder 3
  JTextField rennbahn; // Textfeld der Oberfläche, in dem das Auto fährt
  int max;             // Anzahl der Leerzeichen bis zum Ziel 
  JTextField ausgabe;  // Textfeld der Oberfläche für die Siegermeldung
  int schritte = 0;    // zählt mit, wie weit das Auto ist (=Anzahl Leerzeichen)
  
  
public P11_Auto (int nr, JTextField tf, int ende, JTextField jTfAus)
  {
    nummer = nr;       
// Übernahme der kopierten primitiven Werte ...
    rennbahn = tf;     // .. und der übergebenen Objekte.
    max = ende;        // Achtung: Nur die Objekte ..
    ausgabe = jTfAus;  // .. werden gemeinsam mit Hauptprogramm genutzt.
  }
  
  
public void einStückWeiter()
  {
    
if (schritte < max)
    {
      String auto = rennbahn.getText(); 
// Auto mit bisher gefahrenem Weg  
      rennbahn.setText(" "+auto);  // Auto rückt eine Stelle weiter nach rechts
      schritte++;      // Auto hat einen Schritt mehr zurückgelegt
    }
    
if (schritte == max)
    {
      ausgabe.setText (ausgabe.getText()+
"Auto "+nummer+" im Ziel!  ");  
      schritte++;      
// damit später nicht noch eine Ziel-Erreicht-Meldung kommt
    }  
  }
}
 


In einStückWeiter muss und darf nicht gewartet werden, sodass die warte-Methode in die Auswertung wandert, die - entsprechend obigem Struktogramm - damit folgende Gestalt erhalten kann:


// R. Krell, 28.5.2018 (www.r-krell.de)
// Auswertung der Tastendrücke für P11_Autorennen

import javax.swing.*;

public class P11_Auswertung extends Thread
{
  JTextField bahn1;
  JTextField bahn2;
  JTextField bahn3;
  
int bahnlänge;
  JTextField eingabefeld;
  JTextField ausgabefeld;
  
  
public P11_Auswertung (JTextField b1, JTextField b2, JTextField b3, int max, JTextField ein, JTextField aus)
  {
    bahn1 = b1;   bahn2 = b2;   bahn3 = b3;   bahnlänge = max;
    eingabefeld = ein;   ausgabefeld = aus;
  } 
      
  
private void warte (int ms)  // Zeit in Millisekunden, 100 = 0,1 Sekunde Wartezeit
  {                    
    try
    {
      Thread.sleep (ms);
    }
    
catch (InterruptedException e)
    {
      System.out.println(
"** Fehler beim Warten: "+e);
    }
  }
  
  
public void run()    // diese Methode wird aus der Oberfläche mit start angestoßen
  {
    P11_Auto wagen1 = 
new P11_Auto (1, bahn1, bahnlänge, ausgabefeld);
    P11_Auto wagen2 = 
new P11_Auto (2, bahn2, bahnlänge, ausgabefeld);
    P11_Auto wagen3 = 
new P11_Auto (3, bahn3, bahnlänge, ausgabefeld);
    
while ((ausgabefeld.getText()).equals(""))  // noch kein Auto im Ziel
    { 
      
if ((eingabefeld.getText()).length() > 0// Es gab (mind.) einen Tastendruck
      {
        String alleZeichen = eingabefeld.getText(); 
// sichert den gesamten Inhalt der Eingabezeile
        char eingabe = (alleZeichen.toUpperCase()).charAt(0); // vorderstes Zeichen
        eingabefeld.setText(alleZeichen.substring(1)); // alles ohne vorderstes Zeichen zurück
        eingabefeld.requestFocus(); // damit der Cursor im Eingabefeld steht
        switch (eingabe)
        {
          
case 'Q':  wagen1.einStückWeiter();   break;
          
case 'B':  wagen2.einStückWeiter();   break;
          
case 'P':  wagen3.einStückWeiter();   break;
        } 
// end of switch    
      }
      warte(
5);
    } 
// end of while
  }  
}  
 


In der - hier nicht vollständig wiedergegebenen Oberfläche P11_Autorennen - müssen die Schaltflächen-Methoden dann wie folgt gefüllt werden. Zu beachten ist, dass in der Oberfläche P11 noch ein Textfeld jTfEingabe hinzugekommen ist, das die Tastendrück aufnimmt und bei "Auf die Plätze.." ebenfalls geleert wird.

  
  
public void jBtAufstellen_ActionPerformed(ActionEvent evt) {
    jTfAuto1.setText(
"O>o");
    jTfAuto2.setText(
"O>o");
    jTfAuto3.setText(
"O>o");
    jTfAusgabe.setText(
"");
    jTfEingabe.setText(
"");
  } 
// end of jBtAufstellen_ActionPerformed
 
  public void jBtStart_ActionPerformed(ActionEvent evt) {
    jTfEingabe.requestFocus();
    P11_Auswertung auswertung = new P11_Auswertung(jTfAuto1,jTfAuto2,jTfAuto3,47,jTfEingabe,jTfAusgabe);   
    auswertung.start();
  } // end of jBtStart_ActionPerformed    

jTfEingabe.requestFocus() sorgt dafür, dass das Eingabefeld in der Oberfläche ausgewählt wird und die Tastendrücke hier ankommen. Damit hat das Autorennen P11 seine endgültige Fassung erhalten und kann gespielt werden. Dabei nützt es übrigens keinem Spieler, wenn er seine Taste festhält und hofft, durch die einsetzende automatische Tastenwiederholung schneller ans Ziel zu gelangen, als mit ständigem Tippen: sowie eine Mitspielerin ihre Taste drückt, ist die Wiederholung aufgehoben und das Auto der Mitspielerin bewegt sich. Die Taste des Spielers muss losgelassen und erneut gedrückt werden, damit wieder sein Auto fährt.

Entstanden ist also das geplante interaktive Wettkampf-Geschicklichkeitsspiel P11 für drei Personen, wo die spielende Person gewinnt, die am häufigsten ihre Taste drücken kann. Langweile kommt beim Spiel mit dieser Version nicht mehr auf - höchstens Sehnenscheidenentzündung.




zum Seitenanfang / zum Seitenende

Programm P12: Autorennen mit bunten Fahrzeug-Bildern
und Antrieb der Autos durch Benutzereingaben



Jetzt soll noch erklärt werden, wie man die grafische Darstellung etwas aufhübscht und bunte Bildchen statt der "O>o"-Texte für die Autos verwendet. Natürlich ist die neue Darstellung sowohl für das Zufallsrennen (wie in P10) als auch für das Tastenrennen (wie in P11) möglich. Sofern es überhaupt Unterschiede in der Oberfläche gibt, wird im Folgenden die letzte Variante mit dem Eingabefeld für die Tastendrücke gezeigt.

Entscheidend ist der Ersatz der Textfelder durch Oberflächenobjekte, die Grafiken aufnehmen können. Das geht z.B. wie in P9_A3 mit JPanels, aber auch mit Objekten vom Typ JLabel, die im Kurs bisher nur für Beschriftungs-Texte auf der Oberfläche verwendet wurden. Für JLabel bietet der Objekt-Inspektor des Javaeditors die Möglichkeit an, bequem Grafiken auszuwählen und zu positionieren. Insofern wurden hier JLabels gewählt, um die meiste Arbeit mit der Maus im GUI-Builder erledigen zu können und sich den Programmtext automatisch erzeugen zu lassen.

Als Vorarbeit sollten aber - entweder durch eigenes Zeichnen oder durch Abändern, Färben und Verkleinern frei benutzbarer Cliparts mit Hilfe irgendeines geeigneten Grafikprogramms - drei verschiedene Autobilder angefertigt und am besten in einem Unterordner mit dem Namen "images" unter dem Arbeitsordner für den Programmtext von P12 gespeichert werden. Dann kann die Oberfläche wie folgt erzeugt werden. (Natürlich muss die Oberfläche von P12 nicht wieder von null an neu erzeugt werden, sondern kann als Abwandlung aus P11 hervor gehen. Dazu öffnet man die Oberfläche von P11 im Javaeditor, gibt bei 'Datei' > Speicher unter' den neuen Namen "P12_Autorennen" ein, löscht danach die drei Textfelder der Autos und zieht stattdessen dort JLabels (in der Swing-1-Werkzeugleist mit J gekennzeichnet) hin. Im Objekt-Inspektor werden wieder sprechende Namen vergeben, der Vorschlagstext 'text' gelöscht und dafür das Icon über die drei Punkte am rechten Rand ausgewählt:

Bildschirmansicht Javaeditor mit Programm P12 und Icon-Auswahl für ein JLabel

Grundsätzlich sind verschiedene Bildformate möglich; Ende Mai 2018 hatte der Objekt-Inspektor des Javaeditors aber mal Probleme mit gif-Bildern: Einfach ausprobieren und ggf. ein anderes Format wählen! Seit Version 15.23 (Anfang Juni 2018) lassen sich aber wieder alle Formate fehlerfrei auswählen.

Typischerweise erzeugt(e) der Java-Editor automatischen Text für die Attribute (wobei im Objekt-Inspektor die Namensvorschläge jLabelX auf jLbAuto1 bis jLbAuto3 abgeändert wurden):

 private JLabel jLbAuto1 = new JLabel();
   private ImageIcon jLbAuto1Icon = new ImageIcon("images/F1_rt.png");
 private JLabel jLbAuto2 = new JLabel();
   private ImageIcon jLbAuto2Icon = new ImageIcon("images/F1_bl.png");
 private JLabel jLbAuto3 = new JLabel();
   private ImageIcon jLbAuto3Icon = new ImageIcon("images/F1_gr.png");

(Achtung/Zusatz: Vorstehende Attribute reichen für den Gebrauch in der Entwicklungsumgebung völlig aus. Wer aber sein Programm P12 später in eine ausführbare .jar-Datei packen will, muss den Javatext ggf. von Hand durch die rot eingefügten Teile ergänzen, damit die Bildchen dann auch unter den Einschränkungen einer .jar-Datei wirklich geladen und sichtbar werden. Auf meine Anregung hin werden künftige Versionen des Javaeditors den Quelltext wohl automatisch direkt mit dem roten Zusatztext erstellen, sodass keine händische Nachbearbeitung mehr nötig wird:

 private JLabel jLbAuto1 = new JLabel();
   private ImageIcon jLbAuto1Icon = new ImageIcon(
getClass().getResource("images/F1_rt.png"));
 private JLabel jLbAuto2 = new JLabel();
   private ImageIcon jLbAuto2Icon = new ImageIcon(
getClass().getResource("images/F1_bl.png"));
 private JLabel jLbAuto3 = new JLabel();
   private ImageIcon jLbAuto3Icon = new ImageIcon(
getClass().getResource("images/F1_gr.png"));
)

Etwas später wird dann beschrieben, wo und wie groß die Label auf der Oberfläche cp sind:

 jLbAuto1.setBounds(32, 40, 107, 33);
 jLbAuto1.setText("");
 jLbAuto1.setIcon(jLbAuto1Icon);
 cp.add(jLbAuto1);
 jLbAuto2.setBounds(32, 72, 115, 33);
 jLbAuto2.setText("");
 jLbAuto2.setIcon(jLbAuto2Icon);
 cp.add(jLbAuto2);
 jLbAuto3.setBounds(32, 104, 107, 33);
 jLbAuto3.setText("");
 jLbAuto3.setIcon(jLbAuto3Icon);
 cp.add(jLbAuto3);

Die Angaben in Klammern hinter setBounds geben die x- und y-Koordinaten der linken oberen Ecke sowie die Breite und Höhe der JLabels mit den Autobildchen an. Die Details müssen gar nicht alle verstanden werden. Wichtig ist lediglich, dass die Koordinaten der linken oberen Ecken in die setLocation-Aufrufe in der jBtAufstellen_ActionPerformed-Methode (für den "Auf die Plätze.."-Knopf) übernommen werden, damit die Autos nach einem Rennen wieder an die gleichen Stellen zurückgestellt werden. Die ganze jBtAufstellen_ActionPerformed-Methode von P12 ist in der vorstehenden Bildschirmansicht in den Zeilen 126 bis 132 sichtbar und braucht nicht nochmal wiederholt werden. Vom Aufbau entspricht sie der gleichnamigen Methode von P11 nur mit anderen Rücksetz-Befehlen.

Bevor auf die in der Bildschirmansicht teilweise verdeckte "Start"-Methode eingegangen wird, soll zunächst der Auswertungs-Thread betrachtet werden. Statt den Text "O>o" durch eine davor eingefügte Leerstelle nach rechts zu schieben, wird jetzt das ganze JLabel mit dem Autobild um 5 Pixel nach rechts gerückt, d.h. die x-Koordinate der rechten oberen Ecke (deren letzter Wert mit getX() abgefragt werden kann) wird um 5 vergrößert. Die y-Koordinate bleibt unverändert. Bei P11 fand das einStückWeiter-Schieben des Autos in einer eigenen Klasse P11_Auto statt. Allerdings wurde dort mehr Programmtext für die Parameter-Übergabe, den Konstruktor und den Methodenkopf geschrieben, als für das Weiterrücken. Im Folgenden wird auf eine eigene Klasse P12_Auto (und auf nach ihrem Bauplan erzeugte Objekte wagen1, wagen2 und wagen3) verzichtet, und die Arbeit direkt in P12_Auswertung erledigt:


// R. Krell, 29.5.2018 (www.r-krell.de)
// Auswertung der Tastendrücke für P12_Autorennen


import javax.swing.*;

public class P12_Auswertung extends Thread
{
  JLabel bahn1;
  JLabel bahn2;
  JLabel bahn3;
  
int bahnlänge;
  JTextField eingabefeld;
  JTextField ausgabefeld;
  
  
public P12_Auswertung (JLabel lb1, JLabel lb2, JLabel lb3, int max, JTextField ein, JTextField aus)
  {
    
super();
    bahn1 = lb1;   bahn2 = lb2;   bahn3 = lb3;   bahnlänge = max;
    eingabefeld = ein;   ausgabefeld = aus;
  }
  
  
private void warte (int ms)  // Zeit in Millisekunden, 100 = 0,1 Sekunde Wartezeit
  {
    
try
    {
      Thread.sleep (ms);
    }
    
catch (InterruptedException e)
    {
      System.out.println(
"** Fehler beim Warten: "+e);
    }
  }
  
  
public void run()
  {
    
while ((bahn1.getX() < bahnlänge) && (bahn2.getX() < bahnlänge) && (bahn3.getX() < bahnlänge)) 
    { 
      
if ((eingabefeld.getText()).length() > 0// Es gab (mind.) einen Tastendruck
      {
        String alleZeichen = eingabefeld.getText();
        
char eingabe = (alleZeichen.toLowerCase()).charAt(0); // vorderstes Zeichen
        eingabefeld.setText(alleZeichen.substring(1)); // alles ohne vorderstes Zeichen
        eingabefeld.requestFocus(); // damit der Cursor im Eingabefeld steht
        switch (eingabe)
        {
          
case 'q':  bahn1.setLocation(bahn1.getX()+5, bahn1.getY());  break;
          
case 'b':  bahn2.setLocation(bahn2.getX()+5, bahn2.getY());  break;
          
case 'p':  bahn3.setLocation(bahn3.getX()+5, bahn3.getY());  break;         
        }          
      } 
// end of if
      warte(5);      
    } 
// end of while
    
    
if (bahn1.getX() >= bahnlänge) 
    {
      ausgabefeld.setText (
"Auto 1 (rot, 'q') hat gewonnen!");
    } 
    
else if (bahn2.getX() >= bahnlänge) 
    {
      ausgabefeld.setText (
"Auto 2 (blau, 'b') hat gewonnen!");
    } 
    
else if (bahn3.getX() >= bahnlänge) 
    {
      ausgabefeld.setText (
"Auto 3 (grün, 'p') hat gewonnen!");
    }                                        
  }  
}

 

Bitte beachten: trotz z.T. gleicher Namen in P11_Auswertung und P12_Auswertung verbergen sich dahinter oft Variablen verschiedener Typen! So ist z.B. bahn1 in P11 ein JTextField, aber hier in P12 ein JLabel. Zur Abwechslung wurde außerdem die Eingabe jetzt in Klein- statt in Großbuchstaben übersetzt und das Ende wird hier nicht mehr durch die Kontrolle des Ausgabefelds auf eine Ziel-Meldung erkannt, sondern mit einer Mehrfachbedingung in der while-Schleife überwacht: Das P12-Spiel läuft nur, wenn noch alle drei Autos vorm Ziel sind. Die Bahnlänge bleibt zwar eine Ganzzahl, wird jetzt aber in Pixeln statt in Leerzeichen gemessen. (Dass, wie in P11_Auto noch befürchtet, die Ziel-Meldung eines Autos eventuell mehrfach kommt, ist bei der Kirmes-Variante ausgeschlossen, da das Spiel endet, wenn ein Auto das Ziel erreicht hat. Will man alle Autos ins Ziel lassen, müsste hier noch passende Vorsorge getroffen werden).

Damit kann der "Start"-Knopf in der Oberfläche P12_Autorennen schließlich wie folgt programmiert werden:

 
  
public void jBtStart_ActionPerformed(ActionEvent evt) {
    jTfEingabe.requestFocus();
    P12_Auswertung fahre = 
new P12_Auswertung (jLbAuto1, jLbAuto2, jLbAuto3, 285, jTfEingabe, jTfAusgabe);
    fahre.start();   
  } 
// end of jBtStart_ActionPerformed
   

Das geschieht ganz analog zur Programmierung in P11. Und ob das Objekt vom Typ P12_Auswertung nun fahre oder wie bei P11_Auswertung etwas fantasielos auswertung heißt, ist natürlich egal.

Jedenfalls funktioniert das Tastenrennen jetzt in P12 mit ansprechender Grafik. Wer der drei Mitspieler seine Taste am häufigsten drückt, wird für den körperlichen Einsatz mit dem Sieg seines schönen Rennwagens belohnt!




zum Seitenanfang / zum Seitenende

 Ausprobieren von P10 und P12



Zum Ausprobieren biete ich zwei lauffähige, spielbare Autorennen an, wie sie hier entwickelt wurden. Das Aussehen der Programme kennen Sie ja bereits vom Anfang dieser Seite.

Wenn Ihr Browser das (noch) zulässt (und Sie die Java-JRE 1.8 oder höher installiert haben), können Sie jedes der beiden Programme direkt hier aus dem Browser heraus starten ("Webstart"). Gibt es Probleme oder wollen Sie das Autorennen 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/starten und so das Autorennen 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)].



Webstart
(jnlp-Datei nicht speichern/herunterladen, sondern mit dem Launcher öffnen, Risiko akzeptieren und ausführen)
Download
(jar-Datei auf den eigenen Rechner herunter laden und dort später ausführen) *)
Zufallsrennen mit "O>o"-Autos Webstart   P10_Autorennen.jnlp P10_Autorennen(r-krell.de).jar   (8kB)
Tastenrennen mit Auto-Bildchen Webstart   P12_Autorennen.jnlp P12_Autorennen(r-krell.de).jar   (16kB)

*) 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

Übungs-Aufgaben zu P10 bis P12



  1. Oben wurde zwischen dem "Kirmes-Modus" - das Autorennen endet wie das Kirmes-Pferde- oder -Kamel-Rennen in dem Moment, wo das erste Auto ins Ziel kommt - und dem "Renn-Modus" unterschieden, bei dem die Bewegung auf dem Bildschirm erst endet, wenn alle Autos das Ziel erreicht haben.
    1. Am Ende des Programms P10 wird behauptet, durch den Zusatz && (ausgabe.getText()).equals("") würde der Kirmes-Modus erreicht, aber die Zielmeldungen müssten noch angepasst werden. Begründen Sie die Behauptung und nehmen Sie die entsprechende Anpassung der Ausgabe vor!
    2. Analysieren Sie auch, für welchen Modus die vorgestellten Programme P11 und P12 geschrieben wurden, und ändern Sie für alle drei Programme P10 bis P12 den Programmtext auf den jeweils anderen Modus ab!
    3. Spielt man das hier vorgestellte Programm P10 per Webstart oder nach dem Herunterladen, verblüfft die Zieleinlauf-Meldung: Manche der gemeldeten Autos sind noch gar nicht im Ziel und auch die Reihenfolge von Zweitem und Dritten überrascht gelegentlich. Erklären Sie dieses Verhalten und prüfen Sie, ob ihre Anpassung aus der Teilaufgabe 1 Besserung bringt.
  2. Drückt man bei P10 während eines laufenden Rennens den "Auf die Plätze.."-Knopf, werden die Autos an den Start zurückgestellt, das Rennen geht aber weiter. Die Autos fahren dann nur noch die Reststrecke, die sie bei Betätigen der Schaltfläche vom Ziel getrennt hat, und bleiben mitten auf der Bahn stehen. Trotzdem wird der Zieleinlauf gemeldet. Drückt man hingegen während eines Rennens nochmal auf den "Start"-Knopf, schließt sich nach dem ersten Zieleinlauf ein zweites Rennen an. Die Autos fahren von den Endpositionen des ersten Rennens weiter, fahren bald aus den Bahnen heraus und werden unsichtbar. Trotzdem wird später ein weiterer Zieleinlauf gemeldet. Überlegen Sie, was sinnvollerweise bei Knopfdrücken während des Rennens passieren sollte, und sorgen Sie für ein entsprechendes Verhalten des Programms. Prüfen Sie, ob bei P11 und P12 Gleiches gilt.
  3. Bei den Tastenrennen P11 und P12 fühlt sich eventuell der Spieler benachteiligt, der den "Start"-Knopf betätigt, während seine Mitspieler ihre Hände schon auf ihren Tasten haben. Ergänzen Sie eines der beiden Tasten-Programme daher um einen Countdown: Nach Druck auf die "Start"-Taste wird im Ausgabefeld zunächst im Sekundentakt von 3, 2, 1 auf 0 runter gezählt und erst ab dann gelten die Tastendrücke und können die Autos bewegt werden.
  4. Mit bunten Bildchen für die Autos wurde in P12 nur das Tastenrennen P11 realisiert. Für das Zufallsrennen P10 gibt es noch keine Variante mit schöner Grafik. Holen Sie das nach und verwenden Sie Autobildchen auch für das Zufallsrennen.
  5. Zwischen dem Programm P10 einerseits und den Programmen P11 und P12 andererseits besteht ein Unterschied im Gebrauch der bzw. des Threads: Während in P10 die Autos unabhängig voneinander jeweils einen eigenen Thread haben, ist dies bei den anderen Programmen nicht mehr der Fall. Insbesondere gibt es in der verkürzten Programmierung von P12 gar keine Autoklasse(n) mehr, sondern nur noch den zentralen Auswertungs-Thread.
    1. Probieren Sie, auch das Zufallsrennen mit nur einem zentralen Thread zu programmieren.
    2. Überlegen Sie, ob umgekehrt das Tastenrennen auch mit jeweils einem eigenen Thread pro Auto realisierbar ist (und auf die zentrale Auswertung ganz verzichtet werden kann?). Untermauern Sie Ihre Überlegungen durch ein passendes Programm, das erfolgreich läuft.
    3. Die Aufteilung eines Projekts in Klassen soll weniger der Programmierer-Willkür, als der Sachlogik folgen, die von der Alltagserfahrung mit echten Objekten und Abläufen nahegelegt wird. Diskutieren Sie unter diesem Aspekt die oben vorgenommene und hier angeregte Aufteilung des Quelltextes auf verschiedene Klassen bzw. Dateien.
  6. Erweitern Sie eines der Programme P10 bis P12 auf ein Rennen mit 4 Autos.
  7. Ergänzen Sie die Oberfläche durch Namensfelder neben den Autos, in die vor dem Start die Spieler ihre Namen eintragen können. Natürlich soll am Ende dann nicht mehr die dürre Meldung "Auto 2 ist im Ziel!" oder "Auto 2 hat gewonnen" erscheinen, sondern etwa "Glückwunsch Moni, du hast gesiegt!".


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
zur Vertiefungs-Seite für P9 (Programme P9_A3x)  Seite b3)_2018


zum Anfang dieser Seite
Willkommen/Übersicht  -  Was ist neu?  -  Software  -  Mathematik  -  Physik  -  Informatik  -   Schule: Lessing-Gymnasium und -Berufskolleg  -  Fotovoltaik  -  & mehr  -  Kontakt: e-Mail,  News-Abo, Gästebuch, Impressum  -  Grußkarten, site map, Download und Suche

Diese Seite ist Teil des Webangebots http://www.r-krell.de.