Universität Ulm,
Fakultät für Mathematik und Wirtschaftswissenschaften,
SAI,
WS 1998/99,
Allgemeine Informatik I
Lösung zu Blatt 13 --- Allgemeine Informatik I (WS 1998/99)
GameOfLife Hauptprogramm
(*
Allgemeine Informatik I / Programmieren I WS 1998/1999
Musterloesung fuer das Blatt 13, Aufgabe 15
*)
MODULE GameOfLife;
FROM Arguments IMPORT InitArgs, GetFlag, FetchString, AllArgs, Usage;
FROM ASCII IMPORT nl;
FROM FtdIO IMPORT FwriteString, FwriteLn;
FROM MainWin IMPORT WriteString, Write, WriteLn, Read,
Flush, SetPos, Clear, lines, columns;
FROM StdIO IMPORT FILE, MODE, Fopen, Fgetc, stdin, stderr;
FROM SysExit IMPORT Exit;
FROM SysPerror IMPORT Perror;
FROM World IMPORT Init, Lines, Columns, GetMode, Get, Set,
Mode, NextGeneration;
VAR
(* Kommandozeilenargumente *)
query: BOOLEAN; (* mit Rueckfragen? *)
input: FILE; (* von hier ist die Ausgangssituation einzulesen *)
(* Abfragestatus *)
continue: BOOLEAN;
PROCEDURE ProcessArgs;
VAR
flag: CHAR;
filename: ARRAY [0..511] OF CHAR;
wmode: Mode; (* Mit oder ohne Rand *)
BEGIN
InitArgs("[-q] [-b] world");
query := FALSE; wmode := torus;
WHILE GetFlag(flag) DO
CASE flag OF
| "q": query := TRUE;
| "b": wmode := border;
ELSE
Usage;
END;
END;
FetchString(filename);
IF ~Fopen(input, filename, read, (* buffered = *) TRUE) THEN
Perror(filename); Exit(1);
END;
Init(columns, lines, wmode);
AllArgs;
END ProcessArgs;
PROCEDURE ReadWorld(input: FILE): BOOLEAN;
(* Einlesen der Welt von ``input'':
- Bei Problemen wird eine Fehlermeldung ausgegeben und
FALSE zurueckgeliefert.
- Im Erfolgsfall wird in world die Eingabe in der
vorgefundenen Form abgelegt.
- Die Limits, die durch die Bildschirmgroesse (lines + columns)
und maxsize gegeben sind, werden beruecksichtigt.
*)
VAR
line, column: CARDINAL; (* aktuelle Position in der Welt *)
ch: CHAR; (* zuletzt eingelesenes Zeichen *)
BEGIN
line := 0; column := 0;
WHILE Fgetc(ch, input) DO
IF ch = nl THEN
INC(line); column := 0;
ELSE
IF (line = Lines()) OR (column = Columns()) THEN
WriteString("Die Eingabe ist zu gross!"); WriteLn;
RETURN FALSE
END;
Set(line, column, ch); INC(column);
END;
END;
IF (ORD(column) >= Columns()) OR (ORD(line) >= Lines()) THEN
WriteString("Diese Welt ist zu gross fuer diesen Bildschirm!");
WriteLn;
RETURN FALSE
END;
RETURN TRUE
END ReadWorld;
PROCEDURE Weiter() : BOOLEAN;
(* Implementierung der Option -q *)
VAR
answer: CHAR;
BEGIN
IF continue THEN
RETURN TRUE
ELSE
SetPos(lines-1, 0);
WriteString("Weiter?"); Read(answer);
SetPos(lines-1, 0);
WriteString(" "); Flush;
CASE answer OF
| "c": continue := TRUE; RETURN TRUE
| "n": RETURN FALSE
ELSE
RETURN TRUE
END;
END;
END Weiter;
PROCEDURE WriteWorld(height, width: CARDINAL);
(* Ausgabe von world auf stdout *)
VAR
line, column: CARDINAL;
BEGIN
FOR line := 0 TO height-1 DO
SetPos(line, 0);
FOR column := 0 TO width-1 DO
Write(Get(line, column));
END;
END;
Flush;
END WriteWorld;
BEGIN
Clear; Flush;
ProcessArgs;
IF ReadWorld(input) THEN
continue := FALSE;
REPEAT
NextGeneration();
WriteWorld(Lines(), Columns());
UNTIL query & ~Weiter();
END;
SetPos(lines-1, 0); Flush;
END GameOfLife.
World.m2
IMPLEMENTATION MODULE World;
TYPE
NeighbourCount = [0..8];
NeighbourCountSet = SET OF NeighbourCount;
CONST
maxsize = 80; (* maximale Seitenlaenge der Welt *)
space = " "; (* unbesetzt *)
inhabitated = "X"; (* besetzt *)
lonely = NeighbourCountSet{0, 1};
birth = NeighbourCountSet{3};
overpop = NeighbourCountSet{4..8};
TYPE
WorldSize = INTEGER [0..maxsize];
WorldIndex = INTEGER [-1..maxsize];
World = ARRAY WorldIndex, WorldIndex OF CHAR;
VAR
world, newWorld: World;
noflines, nofcolumns: INTEGER; (* Maximale Groesse wg. BS und maxsize *)
wmode: Mode;
PROCEDURE InitWorld(VAR world: World);
(* Initialisiert die Feld fuer die Eingabe *)
VAR i, j: WorldIndex;
BEGIN
FOR i := MIN(WorldIndex) TO MAX(WorldIndex) DO
FOR j := MIN(WorldIndex) TO MAX(WorldIndex) DO
world[i, j] := space;
END;
END;
END InitWorld;
PROCEDURE Init(columns, lines: CARDINAL; mode: Mode);
BEGIN
wmode := mode;
InitWorld(world);
nofcolumns := columns - 1; noflines := lines - 1;
IF noflines > maxsize THEN noflines := maxsize -1 ; END;
IF nofcolumns > maxsize THEN nofcolumns := maxsize -1 ; END;
END Init;
PROCEDURE GetMaximumSize(): CARDINAL;
BEGIN
RETURN maxsize;
END GetMaximumSize;
PROCEDURE Columns():CARDINAL;
BEGIN
RETURN nofcolumns;
END Columns;
PROCEDURE Lines(): CARDINAL;
BEGIN
RETURN noflines;
END Lines;
PROCEDURE GetMode(): Mode;
BEGIN
RETURN wmode;
END GetMode;
PROCEDURE Get(line, column: CARDINAL): CHAR;
BEGIN
RETURN world[line, column];
END Get;
PROCEDURE Set(line, column: CARDINAL; char: CHAR);
BEGIN
world[line, column] := char;
END Set;
PROCEDURE NextGeneration();
PROCEDURE Neighbors (l, c: WorldIndex):CARDINAL;
(* Zaehlt die Nachbarn der gleichen Art *)
VAR
neighbors: [0..8];
i, j: [-1 .. 1];
nl, nc: WorldIndex;
BEGIN
neighbors := 0;
FOR i := -1 TO 1 DO
FOR j := -1 TO 1 DO
IF (i # 0) OR (j # 0) THEN (* eigenes Feld ignorieren *)
IF wmode = border THEN
nl := l + i; nc := c + j;
ELSE (* Torus *)
nl := (noflines + l + i) MOD noflines;
nc := (nofcolumns + c + j) MOD nofcolumns;
END;
IF world[nl, nc] # space THEN
INC(neighbors);
END;
END;
END;
END;
RETURN neighbors
END Neighbors;
VAR
i, j: WorldIndex;
neighbors: CARDINAL;
BEGIN (* NextGeneration *)
IF wmode = border THEN
FOR i := -1 TO noflines DO
world[i, -1] := space; world[i, nofcolumns] := space;
END;
FOR j := -1 TO nofcolumns DO
world[-1, j] := space; world[noflines, j] := space;
END;
END;
FOR i := 0 TO noflines - 1 DO
FOR j := 0 TO nofcolumns -1 DO
neighbors := Neighbors(i, j);
IF neighbors IN (lonely + overpop) THEN
(* Gleich mit Neighbors < 2 or Neighbors > 3 *)
newWorld[i, j] := space;
ELSIF neighbors IN birth THEN
(* Gleich mit Neighbors = 3 *)
newWorld[i, j] := inhabitated;
ELSE
newWorld[i, j] := world[i, j];
END;
END;
END;
world := newWorld;
END NextGeneration;
BEGIN
wmode := torus;
END World.
Ingo Melzer, 10. Februar 1999