Rieselnde Sanduhr in C ausgeben?

4 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

Ich hoffe, ich verstehe die Frage korrekt. Zunächst einmal muss die Breite der Sanduhr in einer Variable definiert werden, wobei die Breite ungerade sein muss:

int n = 11;

if (n % 2 == 0) {
  printf("Fehler: Nur ungerade Breiten sind möglich!");
  exit(EXIT_FAILURE);
}

Nun geht es erst einmal darum, die Sanduhr ohne Sand auszugeben. Dafür benötigen wir mehrere for-Schleifen. Die äußere Schleife soll Zeile für Zeile durchlaufen. Insgesamt haben wir bei einer Breite von n eine Zeilenanzahl von n-2. Daher sollte die Schleife folgendermaßen aussehen:

for (int i = 0, j = n; i < n-2; ++i, j += (i < n/2) ? -2 : 2) {
  // ...
}

Wie du siehst, benötigen wir einen zweiten Zähler j. Dieser Zähler zählt die Breite in der jeweiligen Zeile. Diese verringert sich bis zur Mitte (n/2) um zwei und erhöht sich danach jede Zeile wieder um zwei. Die Differenz n-j gibt die Zahl der Leerzeichen an. Dabei gibt es links genauso viele Leerzeichen wie rechts. Somit können wir die linken Leerzeichen so ausgeben:

for (int k = 0; k < (n - j) / 2; ++k) {
  printf(" ");
}

Jetzt folgt die Ausgabe der eigentlichen Sanduhr, wobei wir mithilfe einer if-Abfrage dafür sorgen, dass in der ersten und letzten Zeile sowie in der ersten und letzten Spalte eine Raute, ansonsten ein Leerzeichen ausgegeben wird:

for (int k = 0; k < j; ++k) {
  if (i == 0 || i == n-3 || k == 0 || k == j - 1) {
    printf("#");
  } else {
    printf(" ");
  }
}

Bisher sieht unser Code also so aus:

int n = 11;

if (n % 2 == 0) {
  printf("Fehler: Nur ungerade Breiten sind möglich!");
  exit(EXIT_FAILURE);
}

for (int i = 0, j = n; i < n-2; ++i, j += (i < n/2) ? -2 : 2) {
  for (int k = 0; k < (n - j) / 2; ++k) {
    printf(" ");
  }

  for (int k = 0; k < j; ++k) {
    if (i == 0 || i == n-3 || k == 0 || k == j - 1) {
      printf("#");
    } else {
      printf(" ");
    }
  }
  printf("\n");
}

Diese Sanduhr müssen wir mehrmals ausgeben. Wir haben n-2 Zeilen. Darunter ist eine Zeile die Mitte, zwei Zeilen sind obere und untere Grenze. Somit gibt es ((n-2)-1-2) / 2 = (n-5)/2 Zeilen mit Sand. Die Sanduhr müssen wir daher (n-5)/2 + 1 mal ausgeben, um den ganzen Verlauf des Sandes bis ganz nach unten sehen zu können. Wir rahmen den gesamten Code daher durch eine weitere Schleife ein:

for (int x = 0; x < (n-5)/2 + 1; ++x) {
  // ...
}

Unser else teilen wir nun in ein else if und ein else, damit wir unterscheiden können, ob wir Sand oder Leerzeichen ausgeben:

if (...) {
  // ...
} else if ((i >= x+1 && i <= (n-5)/2) ||
    (i >= n-3-x && i <= n-4)) {
  printf("*");
} else {
  printf(" ");
}

Wie ergibt sich die Bedingung für den Sand? Nun, einmal müssen wir den Sand in der oberen und einmal in der unteren Hälfte betrachten. Oben muss die aktuelle Zeile zwischen der oberen Grenze (durch x definiert) und der Mitte liegen, unten zwischen der oberen Grenze (ergibt sich aus x und der Zeilenanzahl) und dem unteren Ende. Das ist auch alles rein mathematisch.

Hier ist der vollständige Code:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
  int n = 11;

  if (n % 2 == 0) {
    printf("Fehler: Nur ungerade Breiten sind möglich!");
    exit(EXIT_FAILURE);
  }

  for (int x = 0; x < (n-5)/2 + 1; ++x) {
    for (int i = 0, j = n; i < n-2; ++i, j += (i < n/2) ? -2 : 2) {
      for (int k = 0; k < (n - j) / 2; ++k) {
        printf(" ");
      }

      for (int k = 0; k < j; ++k) {
        if (i == 0 || i == n-3 || k == 0 || k == j - 1) {
          printf("#");
        } else if ((i >= x+1 && i <= (n-5)/2) ||
            (i >= n-3-x && i <= n-4)) {
          printf("*");
        } else {
          printf(" ");
        }
      }
      printf("\n");
    }

    printf("\n");
  }

  return 0;
}

Die Ausgabe ist folgende:

###########
 #*******#
  #*****#
   #***#
    # #
   #   #
  #     #
 #       #
###########

###########
 #       #
  #*****#
   #***#
    # #
   #   #
  #     #
 #*******#
###########

###########
 #       #
  #     #
   #***#
    # #
   #   #
  #*****#
 #*******#
###########

###########
 #       #
  #     #
   #   #
    # #
   #***#
  #*****#
 #*******#
###########

Ich hoffe, die Erklärung ist verständlich genug. Versuche, den Code mal nachzuvollziehen. Hier ein Live-Beispiel: https://repl.it/repls/SpringgreenWelltodoCores

Ich habe die andere Lösung leicht überarbeitet und an ANSI C angepasst. Außerdem wird die Konsole geleert, sodass es etwas dynamischer wirkt und die Lösung nicht in einzelnen Schritten ausgegeben wird. Die Sanduhr wird jede Sekunde aktualisiert. Die Zeit kannst Du bei dem oben angegeben Makro DELAY verändern.

#include <stdio.h>
#ifdef __unix__
  #include <unistd.h>
#elif defined _WIN32
  #include <windows.h>
  #define sleep(x) Sleep(1000 * x)
#endif

#define WIDTH 11
#define DELAY 1

void clear();

int main() {
  int i, j, k, x;

  if (WIDTH % 2 == 0)
    exit(EXIT_FAILURE);
  else
    clear();

  for (x = 0; x < (WIDTH-5)/2 + 1; ++x) {
    if (x > 0) {
      sleep(DELAY);
      clear();
    }

    for (i = 0, j = WIDTH; i < WIDTH-2; ++i, j += (i < WIDTH/2) ? -2 : 2) {
      for (k = 0; k < (WIDTH - j) / 2; ++k)
        putchar(' ');

      for (k = 0; k < j; ++k)
        if (i == 0 || i == WIDTH-3 || k == 0 || k == j - 1)
          putchar('#');
        else if ((i >= x+1 && i <= (WIDTH-5)/2)
                 || (i >= WIDTH-3-x && i <= WIDTH-4))
          putchar('*');
        else
          putchar(' ');
      putchar('\n');
    }
  }

  return 0;
}

void clear() {
  #ifdef __unix__
    system("clear");
  #elif defined _WIN32
    system("cls");
  #endif
}
Woher ich das weiß:Studium / Ausbildung

Ich gebe nur Anregungen / Tipps:

  • Nutze eine Schleife
  • Zeichne pro Schleifendurchlauf das Spielfeld (die Uhr) neu, verrücke zuvor die Zeilen

Möglicherweise hilft dir diese Frage weiter: https://www.gutefrage.net/frage/java-array-werte-verschieben---rekursiv - ist zwar Rekursion bei einem Wort, doch das Vorgehen ist ja ähnlich.

Schreibe dir den Ablauf erst einmal in Stichpunkten auf (fernab von Code) oder zeichne dir ein Ablaufdiagramm.

so?

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    while(1)
    {
        for(int i = 0; i < 50; i++)
        {
            printf("%*s##\n", i, " ");
            usleep(50000);
        }

        for(int i = 50; i > 0; i--)
        {
            printf("%*s##\n", i, " ");
            usleep(50000);
        }
    }

    return 0;
}
milos2  05.01.2019, 12:30

Ich habe deine und die andere Antwort kombiniert. Kannst Du Dir gern mal anschauen.

0