Sektion Angewandte Informationsverarbeitung

Übungen zu Systemnahe Software I, Wintersemester 1996/97

Blatt 6

Beispiellösung zu Aufgabe 10 (20 Punkte)

Das C-Programm

Die Musterlösung zu diesem Übungsblatt stammt von Dr. Andreas Borchert.

/*
 * Software I     WS 89/90
 * Blatt 8     10. Aufgabe
 * AFB         12/89
 * ANSI C und kleine Korrekturen: atl 12/96
 *
 * Simulation von Wator
 * (aus Spektrum der Wissenschaft: Computer-Kurzweil)
 */

#include <stdio.h>

#define TRUE  1
#define FALSE  0

/* externe Funktionen aus der C-Bibliothek */
extern long lrand48();
extern void srand48();
extern void exit();

/*
 * Wator wird in quadratische Sektoren unterteilt;
 * insgesamt gibt es ZEILEN*SPALTEN Sektoren
 */
#define ZEILEN  14
#define SPALTEN  32

#define RICHTUNGEN 4  /* jedes Feld hat 4 Nachbarn */

/*
 * in einem Sektor gibt es entweder einen Hai, einen Fisch oder nur Wasser
 */
#define  LEER  ' '
#define HAI  'O'
#define  FISCH  '.'

int nfische;      /* Anzahl der Fische und */
int nhaie;      /* ... Haie zu Beginn */
int fbrut;      /* wann haben die Fische Nachwuchs? */
int hbrut;      /* und die Haie? */
int fasten;      /* solange halten die Haie ohne Nahrung durch */
char wator[ZEILEN][SPALTEN];  /* das Meer von Wator */
int geburt[ZEILEN][SPALTEN];  /* Geburtzeit des Lebewesens auf wator[x][y] */
int hunger[ZEILEN][SPALTEN];  /* Hungerzeit des Hais auf wator[x][y] */
int gezogen[ZEILEN][SPALTEN];  /* Lebewesen auf wator[x][y] bereits bewegt? */
int chronos;      /* Uhr */

void wator_ausgabe()      /* Ausgabe des Feldes `wator' */
{  int zeile, spalte;

  for (zeile = 0; zeile < ZEILEN; ++ zeile)
  {  for (spalte = 0; spalte < SPALTEN; ++ spalte)
      putchar(wator[zeile][spalte]);
    putchar('\n');
  }
}

void skip_nl()        /* ueberliest den Zeilentrenner */
{  int ch;

  while ((ch = getchar()) != EOF && ch != '\n')
    ;
}

int weiter()      /* liefert TRUE, falls es weiter gehen soll */
{  int ch;

  printf("Weiter? ");
  if ((ch = getchar()) == EOF)
    return FALSE;
  if (ch == '\n')
    return TRUE;
  skip_nl();
  return ch != 'n' && ch != 'N';
}

/*
 * liest einen Wert ein
 *    `prompt' wird zu Beginn ausgegeben
 *    `tip'    ist die Voreinstellung (wird bei Eingabe von <RETURN> gewaehlt)
 */
int wert_einlesen(char *prompt, int tip)
{  int wert;
  int ch;

  do
  {  printf("%s [%3d] ", prompt, tip);
    if ((ch = getchar()) == EOF)
      exit(1);  /* Abbruch */
    if (ch == '\n')
      return tip;
    ungetc(ch, stdin);   /* ch zum Wiedereinlesen zurueckgeben */
    scanf("%d", & wert); /* scanf beruecksichtigt jetzt auch `ch' */
    skip_nl();
  } while (wert <= 0);
  return wert;
}

/*
 * Initialisierung der Random-Funktion, muss vor dem ersten Aufruf
 * von random() aufgerufen werden
 */
void random_init()
{  long seedval;

  printf("Bitte eine beliebige Zahl eingeben: ");
  scanf("%ld", & seedval); skip_nl();
  srand48(seedval);
}

/* liefert eine gleichverteilte Zufallszahl innerhalb [von..bis] */
int random(int von, int bis)
{  long rand;

  rand = lrand48();
  return von + rand % (bis - von + 1);
}

/*
 * fuellt `anzahl' mal das `lebewesen' zufallsverteilt
 * auf freie Felder des Feldes `wator';
 * `brut' ist der [hf]brut Parameter des entsprechenden Lebewesens
 */
void fuelle(int anzahl, char lebewesen, int brut)
{  int zeile, spalte;
  int i;

  for (i = 0; i < anzahl; ++i)
  {  do
    {  zeile = random(0, ZEILEN-1);
      spalte = random(0, SPALTEN-1);
    }
    while (wator[zeile][spalte] != LEER);
    wator[zeile][spalte] = lebewesen;
    geburt[zeile][spalte] = random(0, brut-1);
    hunger[zeile][spalte] = random(0, fasten-1);
  }
}

/*
 * initialisiert das Feld `wator' und belegt es mit
 *   `nfische'    Fischen und
 *   `nhaie'      Haie
 * falls die Anzahl zu gross ist, wird FALSE zurueckgeliefert
 */
int init_wator(int nfische, int nhaie)
{  int zeile, spalte;

  if (nfische + nhaie > ZEILEN*SPALTEN)
    return FALSE;

  for (zeile = 0; zeile < ZEILEN; ++ zeile)
    for (spalte = 0; spalte < SPALTEN; ++ spalte)
      wator[zeile][spalte] = LEER;

  fuelle(nhaie, HAI, hbrut);
  fuelle(nfische, FISCH, fbrut);
  return TRUE;      /* alles OK */
}

void init_gezogen()      /* Initialisierung von gezogen[][] */
{  int zeile, spalte;

  for (zeile = 0; zeile < ZEILEN; ++ zeile)
    for (spalte = 0; spalte < SPALTEN; ++ spalte)
      gezogen[zeile][spalte] = FALSE;
}

/*
 * Ueberpruefe, ob in wator[zeile][spalte] feldtyp vorliegt,
 * wenn ja, wird in nx[index] und ny[index] zeile und spalte abgelegt
 * und 1 zurueckgeliefert, ansonsten 0;
 * die Koordinaten zeile, spalte duerfen den Rand um eins verlassen -
 * in diesem Fall wird zeile und spalte entsprechend der Torusform
 * korrigiert
 */
int teste(int zeile, int spalte, char feldtyp, 
          int nx[RICHTUNGEN], int ny[RICHTUNGEN], int index)
{
  /* Torusform von Wator */
  if (zeile == -1)
    zeile = ZEILEN-1;
  else if (zeile == ZEILEN)
    zeile = 0;
  if (spalte == -1)
    spalte = SPALTEN-1;
  else if (spalte == SPALTEN)
    spalte = 0;

  if (wator[zeile][spalte] == feldtyp)
  {  nx[index] = zeile;
    ny[index] = spalte;
    return 1;
  }
  else
    return 0;
}

/*
 * sucht nach einem Nachbarfeld zu [zeile][spalte] mit
 * der Belegung `feldtyp';
 * falls erfolgreich (Return-Wert TRUE),
 * wird in x und y die Nachbarposition abgelegt
 */
int suche(int zeile, int spalte, char feldtyp, int *x, int *y)
{  int anzahl;    /* Anzahl der Moeglichkeiten */
  int i;
  int nx[RICHTUNGEN], ny[RICHTUNGEN];  /* Nachbarfelder */

  anzahl = 0;
  anzahl += teste(zeile-1, spalte  , feldtyp, nx, ny, anzahl);
  anzahl += teste(zeile  , spalte-1, feldtyp, nx, ny, anzahl);
  anzahl += teste(zeile  , spalte+1, feldtyp, nx, ny, anzahl);
  anzahl += teste(zeile+1, spalte  , feldtyp, nx, ny, anzahl);

  if (anzahl)
  {  i = random(0, anzahl-1);
    * x = nx[i];
    * y = ny[i];
    return TRUE;
  }
  else
    return FALSE;
}

/*
 * bewege das Lebewesen auf [zeile][spalte] (HAI oder FISCH)
 * entsprechend seiner Vorlieben;
 * ggf. gibt es ein Junges
 */
void bewege_tier(int zeile, int spalte)
{  int nx, ny;    /* die Nachbarkoordinaten */
  char art;    /* HAI oder FISCH */
  int brut;    /* Wert von hbrut oder fbrut je nach art */
  int bewegen;    /* kann es sich bewegen? */
  
  art = wator[zeile][spalte];
  if (art == FISCH)
  {  bewegen = suche(zeile, spalte, LEER, & nx, & ny);
    brut = fbrut;
  }
  else /* art == HAI */
  {  if (suche(zeile, spalte, FISCH, & nx, & ny))
    {  bewegen = TRUE;
      hunger[nx][ny] = 0;
    }
    else
    {  bewegen = suche(zeile, spalte, LEER, & nx, & ny);
      hunger[nx][ny] = hunger[zeile][spalte] + 1;
    }
    if (hunger[zeile][spalte] == fasten)
    {  bewegen = FALSE;
      wator[zeile][spalte] = LEER;
    }
    brut = hbrut;
  }
  if (bewegen)
  {  wator[nx][ny] = art;
    geburt[nx][ny] = geburt[zeile][spalte];
    if (geburt[zeile][spalte] % brut != chronos % brut)
      wator[zeile][spalte] = LEER;
    else  /* Geburt */
      hunger[zeile][spalte] = 0;
    gezogen[nx][ny] = TRUE;
  }
}

/*
 * bewege alle Tiere der Art `art'
 */
void bewege_tiere(char art)
{  int zeile, spalte;

  for (zeile = 0; zeile < ZEILEN; ++ zeile)
    for (spalte = 0; spalte < SPALTEN; ++ spalte)
      if (! gezogen[zeile][spalte] &&
          wator[zeile][spalte] == art)
        bewege_tier(zeile, spalte);
}

void statistik_ausgabe()
{
  int zeile, spalte;
  int haie = 0, fische = 0;
  char art;

  for (zeile = 0; zeile < ZEILEN; ++ zeile){
    for (spalte = 0; spalte < SPALTEN; ++ spalte){
      art = wator[zeile][spalte];
      if( art == FISCH ) fische++;
      else if( art == HAI ) haie++;
    }
  }
  printf( "Statistik %d %d %d\n", chronos, fische, haie );
}

int main()
{
  printf("\n*** WA-TOR ***\n\n");

  printf("Parameter:\n");
  nfische = wert_einlesen("nfische", 200);
  nhaie =   wert_einlesen("nhaie  ",  20);
  fbrut =   wert_einlesen("fbrut  ",   3);
  hbrut =   wert_einlesen("hbrut  ",  10);
  fasten =  wert_einlesen("fasten ",   3);

  random_init();
  if (! init_wator(nfische, nhaie))
  {  printf("Zuviele Haie oder Fische!\n");
    exit(1);
  }

  for (chronos = 0; wator_ausgabe(), statistik_ausgabe(), weiter(); ++chronos)
  {  init_gezogen();
    bewege_tiere(FISCH);
    bewege_tiere(HAI);
  }

  return 0;
}

Visualisierung mit GnuPlot

Automatisiert man die Eingabe und lenkt die Ausgabe in eine Datei, so kann man die ausgegebene Statistik sehr leicht graphisch darstellen, mit Hilfe des Programms GNUPLOT.

Dazu holen wir aus der Ausgabe des Programms nur die Statistik-Zeilen heraus und legen sie in einer Datei ab:
oberon$ cat wator.dat
Statistik 0 200 20
Statistik 1 232 22
Statistik 2 271 23
Statistik 3 299 28
...

GNUPLOT läßt sich interaktiv bedienen, oder durch Skripte. Diese sehen z.B. so aus:
oberon$ cat stat.plt
plot "wator.dat" using 2:3 title "Fische" with lines, \
"wator.dat" using 2:4 title "Haie" with lines

In den interaktiven Modus kommt mann mit dem Kommando "gnuplot". Einmal in GNUPLOT kann man sich die Dokumentation mit "help" ansehen. Das Plot-Skript lädt man (vom Gnuplot-Prompt aus) mit 'load "stat.plt"'.

Viel Spaß beim Experimentieren ...