Zustände programmieren

Viele Anwendungen kennen verschiedene Zustände. Zum Beispiel eine Stoppuhr: ruhend bei 00:00:00, Zeit laufend, Zwischenzeit anzeigen, Endzeit anzeigen oder ähnlich.
Die kleine Simulation oben zeigt die Grundidee von Zuständen in Anwendungen.

Aufgabe 1

Welche Zustände kennt die kleine Applikation?

Lösungshinweis 1

Richtig, drei Zustände 'Start', 'Spiel' und 'Ende'.

Aufgabe 2

Welche Ereignisse lassen die Simulation von einem Zustand in einen anderen wechseln?

Lösungshinweis 2

  • Vom Startbildschirm kann mit der Taste 'S' zum Spiel gewechselt werden.
  • Das Spiel wechselt zur Anzeige 'Ende', wenn der Punkt das Spielfeld verlässt.
  • Mit der Taster ENTER kann wieder zum Start gewechselt werden.

Grafisch lässt sich dies als Zustandsdiagramm darstellen:

Aufgabe 3

Mit welcher Programmstruktur lassen sich Zustände einfach programmieren?

Lösungshinweis 3

Mit der switch-Anweisung lässt sich die Unterscheidung der Zustände einfach realisieren:

  switch(zustand)
  {
  case 0:
    startBildschirm();
    break;
  case 1:
    spielen();
    break;
  case 2:
    endBildschirm();
    break;
  }

Aufgabe 4

Erweitern Sie das Programm so, dass mit der Taste 's' vom Zustand 2 (Ende) direkt in den Zustand 1 (Spiel) gewechselt werden kann. Prüfen Sie zwei Möglichkeiten...

Processing Code zu Aufgabe 4

// Deklaration globale Variablen
int zustand = 0;
int xPos;
int yPos;
int richtung;
int v = 2;

// setup()-Prozedur wird beim Programmstart einmal ausgeführt
void setup() {
  size(300, 300);
  textSize(24);
  textAlign(CENTER, CENTER);
}

// draw()-Prozedur wird während Laufzeit dauernd wiederholt 
void draw() {
  switch(zustand)
  {
  case 0:
    startBildschirm();
    break;
  case 1:
    spielen();
    break;
  case 2:
    endBildschirm();
    break;
  }
}

// wird bei jedem Tastendruck einmal ausgeführt
void keyPressed() {
  if (key=='S' || key=='s' && zustand == 0) {  
    initialisieren();
    zustand = 1;
  }
  if (key==ENTER && zustand == 2 || key==RETURN && zustand == 2) {  
    zustand = 0;
  }
}

void startBildschirm() {
  background(150,150,255);
  fill(30, 50, 255);
  text("Anleitung...\n\nlos gehts mit 's'", width/2, height/2);
}

void initialisieren() {
  xPos = int(random(width));
  yPos = int(random(height));
  richtung = int(random(4));
}

void spielen() {
  background(255, 196, 47);
  fill(210, 40, 50);
  bewegen();
  test();
  ellipse(xPos, yPos, 20, 20);
}

void bewegen() {
  switch(richtung)
  {
  case 0:
    yPos = yPos - v;
    break;
  case 1:
    xPos = xPos + v;
    break;
  case 2:
    yPos = yPos + v;
    break;
  case 3:
    xPos = xPos - v;
    break;
  }
}

void test() {
   if (xPos < 0 || xPos > width || yPos < 0 || yPos > height) {
     zustand = 2;
   } 
}

void endBildschirm() {
  background(30, 255, 50);
  fill(30, 50, 255);
  text("Ende\nrestart mit RETURN", width/2, height/2);
}

Lösungshinweis 4

Man kann einerseits eine weitere if-Anweisung in die Prozedur keyPressed einfügen:

if (key=='S' || key=='s' && zustand == 2) {  
    initialisieren();
    zustand = 1;
}

Einfacher lässt sich dies mit einer erweiterten if-Anweisung in der Prozedur keyPressed realisieren:

if (key=='S' || key=='s' && zustand == 0 || zustand == 2) {  
    initialisieren();
    zustand = 1;
}

Aufgabe 5

Wie sieht das erweiterte Zustandsdiagramm aus? Zeichnen Sie!

Aufgabe 6

Zustände werden optimal als Konstanten implementiert. Z.B. in Java:
final int START = 0;
final int SPIEL = 1;

etc.

Verbessern Sie den Programmcode aus Aufgabe 4 entsprechend. Wo braucht es überall Anpassungen?

Lösungshinweis 6

Haben Sie die Konstante START bei der Initialisierung der Variable Zustand verwendet (vgl. Zeile 7)?

// Deklaration Zustands-Konstanten
final int START = 0;
final int SPIEL = 1;
final int ENDE = 2;

// Deklaration globale Variablen
int zustand = START;
int xPos;
int yPos;
int richtung;
int v = 2;


// setup()-Prozedur wird beim Programmstart einmal ausgeführt
void setup() {
  size(300, 300);
  textSize(24);
  textAlign(CENTER, CENTER);
}

// draw()-Prozedur wird während Laufzeit dauernd wiederholt 
void draw() {
  switch(zustand)
  {
  case START:
    startBildschirm();
    break;
  case SPIEL:
    spielen();
    break;
  case ENDE:
    endBildschirm();
    break;
  }
}

// wird bei jedem Tastendruck einmal ausgeführt
void keyPressed() {
  if (key=='S' || key=='s' && zustand == START) {  
    initialisieren();
    zustand = SPIEL;
  }
  if (key==ENTER && zustand == 2 || key==RETURN && zustand == 2) {  
    zustand = START;
  }
}

void startBildschirm() {
  background(150,150,255);
  fill(30, 50, 255);
  text("Anleitung...\n\nlos gehts mit 's'", width/2, height/2);
}

void initialisieren() {
  xPos = int(random(width));
  yPos = int(random(height));
  richtung = int(random(4));
}

void spielen() {
  background(255, 196, 47);
  fill(210, 40, 50);
  bewegen();
  test();
  ellipse(xPos, yPos, 20, 20);
}

void bewegen() {
  switch(richtung)
  {
  case 0:
    yPos = yPos - v;
    break;
  case 1:
    xPos = xPos + v;
    break;
  case 2:
    yPos = yPos + v;
    break;
  case 3:
    xPos = xPos - v;
    break;
  }
}

void test() {
   if (xPos < 0 || xPos > width || yPos < 0 || yPos > height) {
     zustand = ENDE;
   } 
}

void endBildschirm() {
  background(30, 255, 50);
  fill(30, 50, 255);
  text("Ende\nrestart mit RETURN", width/2, height/2);
}
fs in ksw - sci