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; }
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 ...