Weitere Aufgaben

Der Hamster steht im Tal und will einen vor sich liegenden Berg erklimmen. Leider weiß er nicht, nach wie vielen Schritten der Berg beginnt, und ebenso wenig, wie hoch der Berg ist. Auf dem Gipfel befindet sich ein Gipfel-Korn.

Hoher Berg Mittelhoher Berg

Schreibe ein Programm, welches den Hamster einen beliebig weit entfernten und beliebig hohen Berg erklimmen lässt.

Fortgeschritten: Der Hamster wird immer erfahrener im Bergsteigen: Er kann nun auch Berge ersteigen, die nicht immer konstant nach oben führen ($100\%$ Steigung), sondern die zwischendurch Plateaus haben können.

Die Aufgabe lässt sich in zwei Teilaufgaben aufspalten, die hintereinander gelöst werden müssen:

void main()
{
    // 1. Zum Berg laufen
    // 2. Berg erklimmen
}

Für Teilaufgabe 2. könntest du dir noch eine Prozedur erklimmeStufe definieren.

Für beide Teilaufgaben 1. und 2. benötigst du eine while-Schleife.

Überlege dir bei Teilaufgabe 2, welche Schleifenbedingung du wählen kannst. Du könntest z.B. prüfen, ob das Gipel-Korn noch nicht erreicht wurde (und in diesem Fall weiterklettern).

void main()
{
    // 1. Zum Berg laufen
    while (vornFrei())
    {
        vor();
    }
    // 2. Berg erklimmen
    while (!kornDa())
    {
        erklimmeStufe();
    }
}

void erklimmeStufe()
{
    linksUm();
    vor();
    rechtsUm();
    vor();
}

void rechtsUm()
{
    linksUm();
    linksUm();
    linksUm();
}

Der Hamster soll ein komplettes Kornfeld abgrasen und seine Beute am Startpunkt ablegen. Auf jeder Kachel befindet sich maximal ein Korn.

Vorher
Nachher

Verwende eine while-Schleife mit der Bedingung vornFrei(). Der Unterschied zur Laufhamster-Aufgabe ist nun lediglich, dass der Hamster vor dem Vorwärtsschritt noch prüfen muss, ob er sich auf einem Korn befindet und dieses gegebenenfalls essen muss. An dieser Stelle muss also eine if-else-Fallunterscheidung in den Rumpf der while-Schleife geschachtelt werden.

Die grobe Struktur des Programms sieht wie folgt aus. Die Zeilen mit // TODO müssen noch ersetzt werden durch passende Befehle.

void main()
{
    while (vornFrei())
    {
        if (kornDa())
        {
            // TODO
        }
        // TODO
    }
    // TODO
}
// Abgras-Hamster v1
// -----------------
// Dieses Programm laesst den Hamster eine Reihe ab-
// grasen. Auf jedem Feld wird ein Korn aufgenommen,
// sofern sich dort eines befindet. Auf dem Start-
// feld werden abschliessend alle Koerner abgelegt.
void main()
{
    // 1. Alle Koerner einsammeln
    while (vornFrei())
    {
        vor();
        // Nimm ein Korn nur dann, falls vorhanden
        sicheresNimm();
    }
    
    // 2. Nun zurueck zum Reihenanfang.
    kehrt();
    while (vornFrei())
    {
        vor();
    }
    
    // 3. Beute ablegen
    while (!maulLeer())
    {
        gib();
    }
}

// Mit "sicher" ist gemeint, dass nur dann ein Korn
// aufgenommen wird, wenn eines vorhanden ist. Dies
// verhindert also einen Programmabsturz bei Ausführung
// eines `nimm`-Befehls bei Abwesenheit eines Korns.
void sicheresNimm()
{
    if (kornDa())
    {
        nimm();
    }
}

void kehrt()
{
    linksUm();
    linksUm();
}

void rechtsUm()
{
    linksUm();
    linksUm();
    linksUm();
}

Wieder soll der Hamster ein komplettes Kornfeld abgrasen. Diesmal soll er auf jeder Kachel jedoch alle Körner einsammeln (statt nur bis zu ein Korn, wie bei der letzten Aufgabe).

Vorher
Nachher

Verwende zwei ineinander verschachtelte while-Schleifen. Solange vorne frei ist, soll Folgendes getan werden: Solange ein Korn da ist, soll dieses genommen werden.

// Abgras-Hamster v2
// -----------------
// Dieses Programm laesst den Hamster eine Reihe ab-
// grasen. Auf jedem Feld werden alle Koerne auf-
// genommen, die sich dort befinden. Auf dem Start-
// feld werden abschliessend alle Koerner abgelegt.
void main()
{
    // 1. Alle Koerner einsammeln
    while (vornFrei())
    {
        vor();
        // Nimm in jedem Schritt alle Koerner.
        nimmAlle();
    }
    
    // 2. Nun zurueck zum Reihenanfang.
    kehrt();
    while (vornFrei())
    {
        vor();
    }

    // 3. Beute ablegen
    while (!maulLeer())
    {
        gib();
    }
}

// Diese Prozedur veranlasst das Nehmen so vieler
// Koerner, wie vorhanden sind.
void nimmAlle()
{
    while (kornDa())
    {
        nimm();
    }
}

void kehrt()
{
    linksUm();
    linksUm();
}

Wieder soll der Hamster ein komplettes Kornfeld abgrasen. Diesmal jedoch ein beliebig großes Feld!

Vorher
Nachher

Es gibt verschiedene Strategien, das Feld abzuräumen. Am effizientesten ist es natürlich, im Slalom nach unten zu wandern. Im Folgenden wird eine naive Strategie vorgestellt, die einfacher zu programmieren ist: Der Hamster nimmt sich immer eine Zeile vor, läuft diese nach rechts und dann zurück nach links, und macht anschließend mit der nächsten Zeile weiter.

  • graseReiheAb

    Der Hamster muss bei Prozeduraufruf nach Süden hin ausgerichtet sein. Er dreht sich nun nach links und geht bis zum Zeilenende, dabei alle Körner aufnehmend. Dann dreht er sich um und läuft wieder zurück. Abschließend dreht er sich in Richtung Süden.

    Der Hamster steht am Ende der Prozedur am gleichen Punkt mit gleicher Blickrichtung wie zu Beginn der Prozedur.

  • nimmAlle

    Alle Körner auf der aktuellen Kachel fressen

Die main-Prozedur ist nun einfach zu schreiben. Zunächst muss die Standard-Blickrichtung, nämlich Süden, eingenommen werden.

Nun wird die erste Reihe abgegrast (graseReiheAb).

So lange dann noch vorne frei ist, wird 1. nach vorne gegangen und dann 2. eine Reihe abgegrast.

Abschließend müssen wir nur noch zurücklaufen und die Beute ablegen.

// Abgras-Hamster v3
// -----------------
// Dieses Programm laesst den Hamster alle Reihen ab-
// grasen. Auf jedem Feld werden alle Koerne auf-
// genommen, die sich dort befinden. Auf dem Start-
// feld werden abschliessend alle Koerner abgelegt.
void main()
{
    // 1. Nach Sueden wenden.
    rechtsUm();

    // 2. Erste Reihe abgrasen
    graseReiheAb();
    
    // 3. Weitere Reihen abgrasen
    // Der Hamster soll nun so lange gen Sueden laufen,
    // wie es Reihen gibt. Jede Reihe, die er passiert,
    // soll er dabei abgrasen.    
    while (vornFrei())
    {
        vor();           // Reihe wechseln
        graseReiheAb();
    }

    // 4. Heimweg nach Norden antreten
    kehrt();
    while (vornFrei())
    {
        vor();
    }

    // 5. Beute ablegen
    while (!maulLeer())
    {
        gib();
    }
}

// Die Anweisungen dieser Prozedur befanden sich
// urspruenglich (in v1 und v2) in der `main`-Prozedur.
// Da wir nun aber mehrere Reihen abgrasen wollen,
// definieren wir uns diese wiederverwendbare Prozedur.
void graseReiheAb()
{
    // (i) Nach Osten wenden
    linksUm();
    
    // (ii) Alle Koerner einsammeln
    nimmAlle();
    while (vornFrei())
    {
        vor();
        // Nimm in jedem Schritt alle Koerner.
        nimmAlle();
    }
    
    // (iii) Nun zurueck zum Reihenanfang.
    kehrt();
    while (vornFrei())
    {
        vor();
    }
    
    // (iv) Zurueck nach Sueden drehen
    linksUm();
}


// Diese Prozedur veranlasst das Nehmen so vieler
// Koerner, wie vorhanden sind.
void nimmAlle()
{
    while (kornDa())
    {
        nimm();
    }
}

void kehrt()
{
    linksUm();
    linksUm();
}

void rechtsUm()
{
    linksUm();
    linksUm();
    linksUm();
}