Klassenentwurf, Implementation und Arrays am Beispiel einer Würfelsimulation

Ziele und Überblick

In vielen Brett-, Rollen- und Computerspielen spielen Würfel eine wichtige Rolle: Der Zufall macht die Züge der Spieler weniger planbar, erhöht damit den Wiederspielwert und insgesamt sorgt Zufall für Spannung. Dies beweist auch das große Interesse vieler Personen an Glückspielen wie Lotto.

Würfel gibt es in vielen verschiedenen Formen, welcher insbesondere bei komplexeren Rollenspielen für sogenannte Würfelproben verwendet werden. Möchte ein Spieler ein Schloss mit einem Dietrich öffnen, so muss dieser zum Beispiel einen 20-seitigen Würfel einmal werfen und je nach Würfelergebnis kann das Schloss geöffnet werden (18-20), nichts passieren (4-17), der Dietrich zerbrechen (2-3) oder man wird erwischt (1). Einen zwanzigseitigen Würfen bezeichnet man auch als 1W20, also 1 Würfel, 20 Seiten. Oder bei Kämpfen werden Schadenswerte mit Würfeln ausgewürfelt: Zum Beispiel muss einen Spieler, um den Schaden seines Angriffs zu bestimmen, mit zwei vierseitigen Würfen werfen, also einem 2W4 (2 Würfel, 4 Seiten), und die Augensumme entspricht dem Schaden. Mögliche Schadenswerte sind daher 2, 3, ..., 8, wobei nicht alle gleichwahrscheinlich sind.

Die Lerneinheit hat folgende Ziele bzw. Schritte:

  1. Entwurf und Implementation einer allgemeinen Würfelklasse. Ein Objekt der Klasse simuliert ein oben beschriebenes Würfelset, also zum Beispiel 1W6, 2W4, ...
  2. Zur Untersuchung verschiedener Würfel, soll die Möglichkeit bestehen sehr viele Würfelwürfe mit zu simulieren, die Ergebnisse in einem Array zu speichern und die Häufigkeiten übersichtlich darzustellen. Dazu wird die Klasse Würfel um die Methode häufigkeitsanalyse ergänzt.
  3. Als Anwendung der erstellten Würfelklasse wird dann eine Klasse Arena programmiert. In dieser Treten einzelne Kämpfer gegeneinander an und bestimmten Trefferpunkte durch Würfel.

Die Würfelklasse

Zufall in Java

Eins vorneweg: Echten Zufall gibt es in Java nicht. Es gibt lediglich Algorithmen, die so unvorhersehbare Ergebnisse liefern, dass die zufällig wirken. Solche Zufallszahlen nennt man auch pseudozufällig. Für unsere Zwecke genügen die Pseudozufallszahlen allerdings.

Random Klasse

Die Randomklasse ist eine Möglichkeit Pseudozufallszahlen zu erstellen. Dazu müssen zwei Schritte gemacht werden:

  1. Importiere die Klasse Random am Anfang der Klassendatei: import java.util.Random;
  2. Deklarieren und initialisiere ein Objekt der Klasse Random: Random r = new Random();. Da r der Zufallsgenerator ist, kann dies auch einmalig als Attribut der Klasse geschehen.

Das Objekt der Random Klasse kann nun verwendet werden. Die Dokumentation enthält alle Methoden, hier nur die wesentlichen für die Einheit:

Klasse Würfel

Im Folgenden siehst du das Implementationsdiagramm sowie die Dokumentation der einzelnen Methoden der Klasse Würfel:

Dokumentation der Klasse Würfel

Konstruktor

Würfel(int anzahlWürfel, int anzahlSeiten)

Ein Objekt der Klasse wird mit initialisiert. Dabei werden die Klassenattribute auf die entsprechenden Werte der Parameter gesetzt. 1W20 würde mit dem Konstruktor Würfel(1, 20) aufgerufen werden.

Anfrage

int würfeln()

Es wird ein Würfelwurf mit den im Konstruktor eingestellten Werten simuliert. Gibt es mehr als einen Würfel, so müssen auch entsprechend mehrere Zufallszahlen aufsummiert werden. Die Augensumme wird als Ergebnis zurückgegeben.

Anfrage

int[] häufigkeitsanalyse(int n)

Folgt erst später.

Anfrage

int minAugensumme()

Liefert als Ergebnis die minmal mögliche Augensumme. Bei 4 Würfel ist dies zum Beispiel 4.

Anfrage

String toString()

Hier wird die toString-Methode überschrieben. Als String soll der Würfel in der Form 1W4 zurückgegeben werden - natürlich sollen die Werte aus den Attributen der Klasse kommen!

Implementation Würfel

Erstelle ein neues Projekt Würfel und implementiere die Klasse Würfel. Beachte dabei die Dokumentation und verwende den Zufallsgenerator.


  import java.util.Random;

  /**
   *
   * @author Tim Schellartz
   * @version 21.11.2020
   */
  public class Würfel {
      private final int anzahlWürfel;
      private final int anzahlSeiten;
      private final Random r;
  
      /**
       * Erstellt ein Würfel bzw. Würfelset
       * @param anzahlWürfel Anzahl geworfener Würfel
       * @param anzahlSeiten Anzahl Seiten je Würfel
       */
      public Würfel(int anzahlWürfel, int anzahlSeiten) {
          this.anzahlWürfel = anzahlWürfel;
          this.anzahlSeiten = anzahlSeiten;
          r = new Random();
      }
  
      /**
       * Ein Würfelwurf wird simuliert
       * @return Augensumme des Wurfs
       */
      public int würfeln() {
          int summe = 0;
          for (int i = 0; i < anzahlWürfel; i++) {
              summe += r.nextInt(anzahlSeiten) + 1;
          }
          return summe;
      }
      
      /**
       * Minimal mögliche Augensumme
       * @return Minimal mögliche Augensumme
       */
      public int minAugensumme(){
          return anzahlWürfel;
      }
      
      @Override
      public String toString(){
          return anzahlWürfel+"W"+anzahlSeiten;
      }
  }

Arenasimulation: Die Klasse Kämpfer

Idee

Als Anwendung der Würfelklasse soll nun eine kleine Arenasimulation erstellt werden. Die Idee: Es gibt zwei Kämpfer, diese greifen abwechselnd an und wer als erster keine Lebenspunkte mehr besitzt verliert.

Eine gekürzte Ausgabe der eines Schlagabtausches zwischen 2 Kämpfern siehst du hier:

  1: Hella Wahnsinn(100) vs. Don R. Wetter(100): Hella Wahnsinn fügt Don R. Wetter  3 Schaden zu.
  2: Hella Wahnsinn(100) vs. Don R. Wetter( 97): Don R. Wetter fügt Hella Wahnsinn  3 Schaden zu.
  3: Hella Wahnsinn( 97) vs. Don R. Wetter( 97): Hella Wahnsinn fügt Don R. Wetter  2 Schaden zu.
  4: Hella Wahnsinn( 97) vs. Don R. Wetter( 95): Don R. Wetter fügt Hella Wahnsinn  8 Schaden zu.
  5: Hella Wahnsinn( 89) vs. Don R. Wetter( 95): Hella Wahnsinn fügt Don R. Wetter -2 Schaden zu.
 ...
 38: Hella Wahnsinn(  9) vs. Don R. Wetter( 32): Don R. Wetter fügt Hella Wahnsinn  6 Schaden zu.
 39: Hella Wahnsinn(  3) vs. Don R. Wetter( 32): Hella Wahnsinn fügt Don R. Wetter  8 Schaden zu.
 40: Hella Wahnsinn(  3) vs. Don R. Wetter( 24): Don R. Wetter fügt Hella Wahnsinn  4 Schaden zu.
 Don R. Wetter gewinnt!

Die Klasse Kämpfer

Die Klasse Kämpfer hat folgende Anforderungen:

  1. Jede Kämpfer hat einen Namen.
  2. Ein Kämpfer verfügt über eine gewisse Anzahl aktueller Lebenspunkte (kurz lp). Dieser Wert sollte über 0 liegen, da kleiner oder gleich 0 als "Kampf verloren" gewertet werden wird.
  3. Für den Angriff besitzt ein Kämpfer einen Angriffswürfel, zum Beispiel einen 1W12 oder 2W6. Hier kannst du auf die bereits erstellte Würfelklasse zurückgreifen.
  4. Für die Verteidigung besitzt ein Kämpfer einen Verteidigungswürfel, zum Beispiel einen 1W4 oder 2W3. Hier kannst du auf die bereits erstellte Würfelklasse zurückgreifen.
  5. Zur Berechnung des Angriffs soll es eine Methode angreifen geben, welche das Ergebnis des passenden Würfelwurfs zurückliefert.
  6. Zur Berechnung der Verteidigung soll es eine Methode verteidige geben, welche das Ergebnis des passenden Würfelwurfs zurückliefert.
  7. Während des Kampfes verlieren die Teilnehmer Lebenspunkte. Eine Methode soll dies ermöglichen. Dabei wird ein Parameter benötigt, welcher die die Trefferpunkte (tp) übermittelt.
  8. Der Name und die aktuellen Trefferpunkte sollen zurückgegeben werden können (sogenannte getter-Methode).

Kämpfer Klassendiagramm

Erstelle aus den obigen Anforderungen ein Klassendiagramm. Vergleiche anschließend mit der Lösung und passe entsprechend an.

Kämpfer Implementation

Implementiere die Klasse Kämpfer inklusiver aller geforderten Methoden.

/**
* Ein Kämpfer der Arena. Für Aktionen werden Würfel verwendet.
* @author Tim Schellartz
*/
public class Kämpfer {
    private String name;
    private int lp;
    
    private Würfel angriffsWürfel;
    private Würfel verteidigungsWürfel;

    /**
    * Konstruktor der Klasse Kämpfer
    * @param name Name des Kämpfers
    * @param lp Anfängliche Lebenspunkte
    */
    public Kämpfer(String name, int lp) {
        this.name = name;
        this.lp = lp;
    }
    
    /**
    * Setzt den Angriffwürfel
    * @param anzahlWürfel Anzahl Würfel des Wurfs
    * @param anzahlSeiten Anzahl Seiten je Würfel
    */
    public void setzeAngriffsWürfel(int anzahlWürfel, int anzahlSeiten){
        angriffsWürfel = new Würfel(anzahlWürfel, anzahlSeiten);
    }
    
    /**
    * Setzt den Verteidigungswürfel
    * @param anzahlWürfel Anzahl Würfel des Wurfs
    * @param anzahlSeiten Anzahl Seiten je Würfel
    */
    public void setzeVerteidigungsWürfel(int anzahlWürfel, int anzahlSeiten){
        verteidigungsWürfel = new Würfel(anzahlWürfel, anzahlSeiten);
    }
    
    /**
    * Angriff wird mit dem Angriffswürfel durchgeführt
    * @return Angriffwert 
    */
    public int angreifen(){
        if(angriffsWürfel==null){
            return 0;
        }
        return angriffsWürfel.würfeln();
    }
    
    /**
    * Verteidigung wird mit dem Verteidigungswürfel durchgeführt
    * @return Verteidigungswert 
    */
    public int verteidigen(){
        if(verteidigungsWürfel==null){
            return 0;
        }
        return verteidigungsWürfel.würfeln();
    }
    
    /**
    * Der Kämpfer erleidet Schaden, 
    * welcher von den Lebenspunkten abgezogen wird
    * @param tp Trefferpunkte
    */
    public void treffer(int tp){
        if(tp >0){
            lp -= tp;
        }
    }

    /**
    * Name des Kämpfers
    * @return Name des Kämpfers
    */
    public String getName() {
        return name;
    }

    /**
    * Aktuelle Lebenspunkte
    * @return Aktuelle Lebenspunkte
    */
    public int getLp() {
        return lp;
    }   
}

Assoziation im Klassendiagramm

Beziehungen zwischen Klassen

Die Klasse Kämpfer verwaltet und verwendet Objekte der Klasse Würfel. Diese Beziehung nennt man Assoziation. Assoziationen kommen in der Objektorientierten Programmierung sehr häufig vor.

Assoziation

Assoziationen zwischen Klassen modellieren mögliche Objektbeziehungen zwischen den Instanzen der Klassen.

Diese Beziehung wird durch einen Pfeil gekennzeichnet. Achtung: Die Pfeilspitze muss offen sein, eine geschlossene Pfeilspitze stellt die Vererbungsbeziehung dar (kommt später).

Variante 1: Der Pfeil zeigt die Assoziation, die Attribute stehen weiterhin an der gewohnten Position.

Variante 2: Der Pfeil zeigt die Assoziation und die Attribute sind in der Nähe des Pfeils vermerkt. Diese Variante wird in den Abituraufgaben verwendet.

Arenasimulation: Die Klasse Arena

Die Arena

Die Arenaklasse ist relativ einfach und enthält im Grunde nur eine Methode kampf().

Auszug aus der Dokumentation der Klasse Arena.

Anfrage

void kampf()

Zwei Objekte der Klasse Kämpfer werden initialisiert. Ein Angreifer und ein Verteidiger. Solange beide Kämpfer noch über 0 Lebenspunkte haben wird der Kampf fortgesetzt. Dazu werden die Angriffspunkte des Angreifers bestimmt (Klasse Kämpfer beachten!) und die Verteidigung des Verteidigers. Anschließend bildet die Differenz die Trefferpunkte, welcher der Angreifer beim Verteidiger verursacht. Zur Übersicht sollten die Lebenspunkte und die Schadenswerte in der Konsole ausgegeben werden. Ferner tauschen Angreifer und Verteidiger nach jeder Runde die Rollen.

Klasse Arena

Implementiere die Klasse Arena

/**
* Anwendung der Würfelklasse
*
* @author Tim Schellartz
* @version 21.11.2020
*/
public class Arena {

    private Kämpfer kämpfer1;
    private Kämpfer kämpfer2;

    /**
    * Konstruktor der Klasse Arena. Zwei Kämpfer werden angelegt.
    */
    public Arena() {
        reset();
    }

    /**
    * Für mehrere Durchgänge hintereinander können die Kämpfer wieder
    * zurückgesetzt werden.
    */
    public void reset() {
        kämpfer1 = new Kämpfer("Hella Wahnsinn", 100);
        kämpfer1.setzeAngriffsWürfel(1, 12);
        kämpfer1.setzeVerteidigungsWürfel(1, 4);

        kämpfer2 = new Kämpfer("Don R. Wetter", 100);
        kämpfer2.setzeAngriffsWürfel(2, 6);
        kämpfer2.setzeVerteidigungsWürfel(1, 4);
    }

    /**
    * Der eigentliche Kampf. Ein Kampf endet, wenn ein Kämpfer bei 0
    * Lebenspunkte angekommen ist.
    */
    public void kampf() {
        Kämpfer angreifer;
        Kämpfer verteidiger;

        // Wer fängt an? Münzwurf entscheidet
        Würfel münze = new Würfel(1, 2);
        if (münze.würfeln() == 1) {
            angreifer = kämpfer1;
            verteidiger = kämpfer2;
        } else {
            angreifer = kämpfer2;
            verteidiger = kämpfer1;
        }

        // Der eigentliche Kampf
        int counter = 1;
        while (angreifer.getLp() > 0 && verteidiger.getLp() > 0) {
            int tp = angreifer.angreifen() - verteidiger.verteidigen();
            System.out.printf("%3d: %s(%3d) vs %s(%3d): %s fügt %s %2d Schaden zu.\n",
                    counter,
                    kämpfer1.getName(),
                    kämpfer1.getLp(),
                    kämpfer2.getName(),
                    kämpfer2.getLp(),
                    angreifer.getName(),
                    verteidiger.getName(),
                    tp);

            verteidiger.treffer(tp);
            counter++;
            // Dreieckstausch der Rollen Angreifer und Verteidiger
            Kämpfer temp = angreifer;
            angreifer = verteidiger;
            verteidiger = temp;
        }
        System.out.println(verteidiger.getName() + " gewinnt!");
    }
}

Arrays am Beispiel Häufigkeitsanalyse eines Würfelswurfs

Variablen (eine kleine Wiederholung)

Möchtest du in Java eine Variable verwenden, dann kann diese mit zwei (bzw. sogar einer) Zeile umgesetzt werden:

  1. Deklaration
    int zahl;
  2. Initialisierung
    zahl = 10;

Die Deklaration kannst du dir vorstellen wir das Beschriften einer Kiste mit dem Namen der Variablen. Die Kiste ist zu beginn leer. Die Initialisierung und auch alle weiteren Zuweisungen kannst du dir wie das hineinlegen eines Zettels in diese Kiste vorstellen. Verwendest du die Variable, so wird geschaut, welcher Wert auf dem Zettel in der Kiste mit dem entsprechenden Namen steht.

Übrigens erkennt man hier auch 2 typische Fehler im Umgang mit Variablen:

  1. Die Variable wurde garnicht deklariert. Dies entspricht dem Nichtvorhandensein der Kiste.
  2. Die Variable wird verwendet, wurde aber noch nicht initialisiert. Dies entspricht dem fehlenden Zettel.

Ähnlich funktioniert das auch im Computer. Allerdings gibt es hier keine Kisten und Zettel, sondern Speicher mit Speicheradressen. Diesen kann man sich wie eine Tabelle vorstellen und an irgendeiner Spalte dieser Tabelle wird dann Speicher für deine Variable reserviert. Im Folgenden Beispiel liegt die Variable Zahl an Speicherstelle 0005 im Speicher.

Adresse: 0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 ...
Wert -31 0 0 211 32134 10 18 25 7 2019 ...

Zufallszahlen vergleichen

Erstelle eine Methode, welches 2 Zufallszahlen bestimmt und die größte ausgibt. Du kannst dabei davon ausgehen, dass ein Objekt der Klasse Random mit r deklariert wurde.

public int maxRand(){
  int zufallszahl1 = r.nextInt();
  int zufallszahl2 = r.nextInt();
  if(zufallszahl1 < zufallszahl2){
    return zufallszahl2;
  }
  return zufallszahl1;
}

Programm erweitern

Notiere, welche Probleme bei folgender Aufgabe entstehen: Erweitere das Programm, so dass 100 Zufallszahlen, sowie deren Maximum bestimmt werden.

Probleme:

  1. Für die Erstellung jeder Zufallszahl wird eine neuer Zeile benötigt.
  2. Selbst wenn man clever programmiert, muss für jede Zufallszahl ebenfalls eine bedingte Anweisung eingefügt werden (3 Zeilen).
  3. Insgesamt: 100*1+100*3 = 400 Zeilen Quellcode…
  4. Bei 1000 Zahlen 4000 Zeilen, Bei 10000 Zahlen 40000 Zeilen, …

Die vorherige Aufgabe macht klar: Es muss eine Möglichkeit geben, große Mengen von Zahlen oder Objekten automatisch zu erstellen, zu speichern und damit zu arbeiten. Genau das liefern Arrays.

Die Lösung der vorherigen Aufgabe kann mit Arrays wesentliche kürzer und allgemeiner implementiert werden werden:

public int maxRand(){
  int[] zufallszahlen; // Array für Integer-Zahlen wird mit Namen zufallszahlen deklariert.
  zufallszahl = new int[100]; // Initialisierung des Arrays mit 100 Einträgen. Es werden 100 Speicherblöcke reserviert. 

  // Initialisierung der einzelnen Einträge im Array mit einer Zufallszahl
  for( int i = 0; i < zufallszahl.length; i++ ){
    zufallszahl[i] = r.nextInt();
  }
  
  // Bestimmung des maximalen Arrayeintrags
  int max = Integer.MIN_VALUE;
  for( int i = 0; i < zufallszahl.length; i++ ){
    if( zufallszahl[i] > max ){
      max = zufallszahl[i];
    }
  }
  return max;
}

Arrays allgemein

Arrays sind immer dann praktisch, wenn viele vom Typ her gleiche Objekte oder Zahlen gespeichert werden sollen.

Array

Ein Array (Feld) dient dazu, unter einem Namen mehrere Variablen gleichen Datentyps zu speichern. Der Zugriff auf die einzelnen Elemente erfolgt über Nummerierung der Elemente von 0 bis length - 1. Diese Zahl nennt man Index.

Die Erstellung eines Arrays erfordert in Java 3 Schritte, wobei Schritt 1 und 2 auch zusammengefasst werden können (wie bei Variablen).

  1. Deklaration
    int[] zahlen;
  2. Initialisierung des Arrays
    zahlen = new int[10]; //Zehn mögliche Einträge im Array
  3. Initialisierung der Arrayeinträge
    zahlen[0] = 1;
    zahlen[1] = -1231;
    zahlen[2] = 13451;
    zahlen[3] = 20;
    ...
    zahlen[9] = 99;

Achtung: Da der Index bei 0 beginnt, ist der letzte Index 9 (und nicht 10 wie man gerne anfangs meint).

Anschließend können die Einträge im Array ganz normal wie Variablen verwendet werden. Zum Beispiel speichert zahlen[4]= zahlen[1]+zahlen[3] das Ergebnis -1211 in das Array zahlen an Index 4.

Wichtige Eigenschaften des Arrays:

Darstellung eines Arrays als Tabelle

Ähnlich wie Variablen werden auch Arrays im Speicher des Computer abgelegt. Die Vorstellung kann hier durch einen Schrank unterstützt werden. Dieser Schrank hat einen Namen (den Namen des Arrays) und eine feste Anzahl Schubladen (die größe des Arrays).

Wird ein Wert an einem speziellem Index gespeichert, so wird die entsprechend nummerierte Schublade geöffnet und ein Zettel in die Schublade gelegt. Wird ein spezieller Index ausgelesen, dann wird die Schublade mit dem Index geöffnet und der Wert auf dem Zettel verwendet.

Übrigens erkennt man auch hier wieder 3 typische Fehler im Umgang mit Arrays:

  1. Das Array wurde garnicht deklariert. Dies entspricht dem Nichtvorhandensein des Schranks.
  2. Das Array hat einen Index nicht (IndexOutOfBoundsException). Das Array hat die gesuchte Schublade nicht.
  3. Eine Eintrag wird verwendet, wurde aber noch nicht initialisiert. Dies entspricht dem fehlenden Zettel in der Schublade.

Die in der Informatik vorherrschende Vorstellung und Darstellung ist allerdings die als Tabelle. In der ersten Zeile stehen die Indizes, darunter die Werte.

Beispiel:

Deklaration: int[] zahlen = new int[]

Initialisierung der Werte mit Zufallszahlen (siehe oben):

Auslesen eines Werts: zahlen[] liefert .

Häufigkeitsanalyse

Erstelle in der Klasse Würfel eine Methode int[] häufigkeitsanalyse(int n). Der Parameter n gibt die Anzahl Würfelvorgänge mit diesem Würfel vor. Also es wird n mal gewürfelt und die jeweiligen Ergebnisse sollen in einem Array gezählt werden. Dabei ist die Augensumme der Index und der Wert an dieser Stelle soll der Häufigkeit der Augensumme entsprechen. Also zu Beginn ist alles auf 0. Wird dann zum Beispiel eine 4 gewürfelt, dann wird der Wert an Index 4 auf 1 erhöht. Wird nochmal eine 4 gewürfelt, dann wird der Wert an Index 4 auf 2 erhöht usw.

/**
* Der Würfelwurf wird n mal durchgeführt und die 
* absoluten Häufigkeiten als Array gezählt.
* Der Index entsprich der Augensumme.
* @param n Anzahl Würfeldurchgänge
* @return Häufigkeitsanalyse
*/
public int[] häufigkeitsanalyse(int n) {
    int[] erg = new int[anzahlSeiten*anzahlWürfel+1];
    for (int i = 0; i < n; i++) {
        int wurf = würfeln();
        erg[wurf] += 1;
    }
    return erg;
}

Konsolenausgabe

Erstelle eine weitere Methode, welche das Ergebnis der Häufigkeitsanalyse optisch ansprechend darstellt. Ein Beispiel:

Häufigkeitsanalyse: 1W6
1: 0,20 XXXXXXXXXXXXXXXXXXXX
2: 0,15 XXXXXXXXXXXXXXX
3: 0,11 XXXXXXXXXXX
4: 0,17 XXXXXXXXXXXXXXXXX
5: 0,16 XXXXXXXXXXXXXXXX
6: 0,21 XXXXXXXXXXXXXXXXXXXXX
Häufigkeitsanalyse: 2W6
2: 0,03 XXX
3: 0,04 XXXX
4: 0,03 XXX
5: 0,13 XXXXXXXXXXXXX
6: 0,13 XXXXXXXXXXXXX
7: 0,24 XXXXXXXXXXXXXXXXXXXXXXXX
8: 0,08 XXXXXXXX
9: 0,13 XXXXXXXXXXXXX
10: 0,10 XXXXXXXXXX
11: 0,04 XXXX
12: 0,05 XXXXX
int n = 100;
Würfel w = new Würfel(2,6);
int[] häufigkeiten = w.häufigkeitsanalyse(n);
System.out.println("Häufigkeitsanalyse: " + w);
for (int i = w.minAugensumme(); i < häufigkeiten.length; i++) {
    String absolutX = "";
    int absoluteH = häufigkeiten[i];
    for (int j = 0; j < absoluteH; j++) {
        absolutX += "X";
    }
    System.out.printf("%2d: %3.2f %s\n", i, (float) absoluteH / n, absolutX);
}

Übungen Array

Folgende Übungen beziehen sich auf ein neues Projekt und haben nichts mehr mit der Würfelsimulation zu tun.