home *** CD-ROM | disk | FTP | other *** search
- (* -------------------------------------------------------------------------- *)
- (* TRAFFIC.MOD - eine Verkehrssimulation *)
- (* Copyright (C) 1987 Jensen & Partners International *)
- (* Freie deutsche Übersetzungen und Kommentare von Karsten Gieselmann *)
- (* -------------------------------------------------------------------------- *)
-
- (*$A-,B-,I-,O-,Q-,R-,S-,V-,Z-*) (* Full Speed!! *)
-
- MODULE Traffic;
-
- IMPORT SYSTEM, IO, Window, Lib, Process; (* benötigte Module *)
-
- CONST
- xNum = Window.ScreenWidth;
- yNum = Window.ScreenDepth;
- CarNum = 100; (* Maximalzahl von fahrenden Autos *)
-
- TYPE
- xCoord = [0..xNum-1];
- yCoord = [0..yNum-1];
- Coord = RECORD
- x : xCoord;
- y : yCoord;
- END;
- Direction = (East, South, North, West);
- DirSet = SET OF Direction;
- Location = RECORD
- Entry,Exit : DirSet;
- Occupied : BOOLEAN;
- Scenery : SHORTCARD;
- END;
- DA = ARRAY Direction OF Direction;
- CarRange = [0..CarNum-1];
-
- CONST
- Reverse = DA(West, North, South, East); (* Kehrstück von Direction *)
-
- VAR
- World : ARRAY xCoord,yCoord OF Location; (* die Straßenkarte *)
- Car : ARRAY CarRange OF
- RECORD
- Pos : Coord; (* Position des Autos in "World" *)
- Color : Window.Color;
- Speed : CARDINAL; (* Geschwindigkeit in km/h*10 *)
- LastD : Direction;
- END;
- ToNorth : ARRAY yCoord OF CARDINAL;
- ToSouth : ARRAY yCoord OF CARDINAL;
- ToEast : ARRAY xCoord OF CARDINAL;
- ToWest : ARRAY xCoord OF CARDINAL;
- IsBW : BOOLEAN;
-
- (*W+*) (* folgende Variablen wegen Prozeßzugriff nicht in Register halten! *)
-
- VAR
- AvSpeed : CARDINAL; (* Durchschnittsgeschwindigkeit * 10 *)
- MaxCar : CarRange; (* Zahl der gerade im Netz befindlichen Autos *)
-
- (*$W=*) (* den letzten Zustand von $W wieder herstellen *)
-
- (* -------------------------------------------------------------------------- *)
-
- PROCEDURE Plot(x : xCoord; y : yCoord);
- (* setzt ein "Landschaftszeichen" entsprechend der Straßenkarte *)
- TYPE
- Chars = ARRAY SHORTCARD [0..15] OF SHORTCARD;
- CONST
- Road = Chars(176,32,32,201,32,200,186,204,32,205,187,203,188,202,185,206);
- VAR
- loc : Location;
- sc : SHORTCARD;
- color : Window.Color;
- BEGIN
- loc := World[x, y];
- sc := Road[SHORTCARD(loc.Entry+loc.Exit)];
- IF sc=176 THEN
- IF IsBW THEN color := Window.LightGray
- ELSE color := Window.Brown
- END;
- ELSIF sc=32 THEN RETURN
- ELSE color := Window.LightGray;
- END;
- Window.TextColor(color);
- Window.DirectWrite(x+1, y+1, ADR(sc), 1);
- END Plot;
-
- (* -------------------------------------------------------------------------- *)
-
- PROCEDURE PlotCar(v : CarRange; dir : Direction);
- (* setzt ein Auto an die Bildschirmposition (x,y) *)
- TYPE
- Chars = ARRAY Direction OF SHORTCARD;
- CONST
- CarSym = Chars(16, 31, 30, 17);
- BEGIN
- Window.TextColor(Car[v].Color);
- Window.DirectWrite(Car[v].Pos.x+1, Car[v].Pos.y+1, ADR(CarSym[dir]), 1);
- END PlotCar;
-
- (* -------------------------------------------------------------------------- *)
-
- PROCEDURE NewCar;
- (* läßt ein neues Auto zu und placiert es im Straßennetz *)
- VAR
- x : xCoord;
- y : yCoord;
- v : CarRange;
- d : Direction;
- BEGIN
- INC(MaxCar);
- v := MaxCar;
- d := MAX(Direction);
- LOOP
- IF d=MAX(Direction) THEN
- REPEAT
- x := Lib.RANDOM(xNum);
- y := Lib.RANDOM(yNum);
- UNTIL NOT World[x,y].Occupied; (* keine Doppelbelegung! *)
- d := MIN(Direction);
- ELSE
- INC(d);
- END;
- IF d IN World[x,y].Exit THEN EXIT END;
- END;
- World[x,y].Occupied := TRUE; (* Position ist jetzt mit einem Auto besetzt *)
- WITH Car[v] DO
- Pos.x := x; Pos.y := y;
- Speed := Lib.RANDOM(100); (* zufällige Geschwindigkeit *)
- LastD := Direction(Lib.RANDOM(4));
- END;
- IF IsBW THEN Car[v].Color := Window.White;
- ELSE Car[v].Color := Window.Color(9+Lib.RANDOM(7));
- END;
- PlotCar(v,d); (* und das Auto auf den Bildschirm bringen! *)
- END NewCar;
-
- (* -------------------------------------------------------------------------- *)
-
- PROCEDURE ParkCar(v : CarRange);
- (* zieht das Auto Nr.v aus dem Verkehr *)
- VAR
- x : xCoord;
- y : yCoord;
- BEGIN
- x := Car[v].Pos.x;
- y := Car[v].Pos.y;
- IF MaxCar = 0 THEN HALT END;
- World[x,y].Occupied := FALSE; (* Position ist nicht mehr besetzt! *)
- Plot(x, y); (* das Autosymbol mit Landschaft überschreiben *)
- WHILE v<MaxCar DO
- Car[v] := Car[v+1]; INC(v); (* die restlichen Autodaten umkopieren *)
- END;
- DEC(MaxCar);
- END ParkCar;
-
- (* -------------------------------------------------------------------------- *)
-
- PROCEDURE MoveCar(v : CarRange);
- (* erhöht die Geschwindigkeit des v-ten Autos; bei Zusammenstößen mit einem
- anderen Auto bzw. in Kurven verringert sie sich jedoch wieder. Das Auto
- wird je nach Speed und Straßenverlauf an seine neue Position gesetzt. *)
- VAR
- exit : Direction;
- x,newx : xCoord;
- y,newy : yCoord;
- tried : DirSet;
- CONST
- SpeedLimit = 800;
- BEGIN
- WITH Car[v] DO
- IF Speed < SpeedLimit THEN
- INC(Speed, Lib.RANDOM(80)); (* etwas Gas geben... *)
- IF Speed > SpeedLimit THEN (* ...oder vielleicht Radarkontrolle? *)
- Speed := SpeedLimit (* Lieber langsamer fahren! *)
- END;
- IF Speed < Lib.RANDOM(SpeedLimit) THEN
- RETURN
- END;
- END;
- x := Pos.x;
- y := Pos.y;
- END;
- tried := DirSet{};
- LOOP
- exit := Direction(Lib.RANDOM(4));
- IF exit IN World[x,y].Exit THEN
- newx := x;
- newy := y;
- CASE exit OF
- | East : newx := ToEast[x];
- | South : newy := ToSouth[y];
- | North : newy := ToNorth[y];
- | West : newx := ToWest[x];
- END;
- IF World[newx,newy].Occupied THEN (* neuer Platz schon belegt... *)
- Car[v].Speed := Car[v].Speed DIV 4; (* ...Achtung, Crashhhhh!!! *)
- RETURN;
- ELSE
- World[x,y].Occupied := FALSE;
- Plot(x, y); (* alte Position ist wieder Landschaft *)
- World[newx,newy].Occupied := TRUE;
- WITH Car[v] DO
- Pos.x := newx;
- Pos.y := newy;
- IF LastD <> exit THEN (* Vorsicht Kurve... *)
- Speed := Speed DIV 2; (* ...lieber langsamer fahren! *)
- LastD := exit;
- END;
- END;
- PlotCar(v, exit); (* Auto an neue Position schreiben *)
- RETURN;
- END;
- END;
- INCL(tried, exit);
- IF tried = DirSet{East..West} THEN
- Car[v].Speed := 0;
- RETURN;
- END;
- END;
- END MoveCar;
-
- (* -------------------------------------------------------------------------- *)
-
- PROCEDURE InitWorld;
- (* generiert das Einbahnstraßennetz und setzt die Autos an ihre Positionen *)
-
- PROCEDURE ok(x :xCoord; y : yCoord) : BOOLEAN;
- (* gibt an, ob die Genehmigung für den Bau
- einer Straße bei (x,y) erteilt wird! *)
- BEGIN
- RETURN (World[x,y].Exit = DirSet{}) OR
- (World[ToEast[x],y].Exit = DirSet{}) OR
- (World[ToEast[x],ToSouth[y]].Exit = DirSet{}) OR
- (World[x,ToSouth[y]].Exit = DirSet{});
- END ok;
-
- VAR
- x,newx : xCoord;
- y,newy : yCoord;
- n : CARDINAL;
- newdir,
- newentry: Direction;
- v : CarRange;
- BEGIN
- FOR x := MIN(xCoord) TO MAX(xCoord) DO
- ToEast[x] := (x+1) MOD xNum;
- ToWest[x] := (x+xNum-1) MOD xNum;
- END;
- FOR y := MIN(yCoord) TO MAX(yCoord) DO
- ToSouth[y] := (y+1) MOD yNum;
- ToNorth[y] := (y+yNum-1) MOD yNum;
- END;
- Lib.Fill(ADR(World), SIZE(World), 0);
- x := xNum DIV 2;
- y := yNum DIV 2;
- newx := x;
- newy := y;
- newdir := Direction(Lib.RANDOM(4));
- n := 1000 + Lib.RANDOM(1000);
- WHILE n <> 0 DO (* Aufbau des Straßennetzes durch zufällige Schrittwahl *)
- DEC(n);
- IF Lib.RANDOM(4)=0 THEN
- newdir := Direction(Lib.RANDOM(4));
- END;
- WHILE newdir IN World[x,y].Entry DO (* Einbahnstraße erzwingen *)
- newdir := Direction(Lib.RANDOM(4));
- END;
- newx := x; newy := y;
- CASE newdir OF
- | East : newx := ToEast[x];
- | South : newy := ToSouth[y];
- | North : newy := ToNorth[y];
- | West : newx := ToWest[x];
- END;
- newentry := Reverse[newdir];
- IF NOT (newentry IN World[newx,newy].Entry) THEN
- INCL(World[newx,newy].Entry, newentry);
- IF (Lib.RANDOM(50)=0) OR
- (ok(newx, newy) AND
- ok(ToWest[newx], newy) AND
- ok(ToWest[newx], ToNorth[newy]) AND
- ok(newx, ToNorth[newy]))
- THEN
- INCL(World[x,y].Exit, newdir);
- Plot(x, y );
- Plot(newx, newy );
- Plot(ToEast[x], y );
- Plot(ToWest[x], y );
- Plot(x, ToNorth[y]);
- Plot(x, ToSouth[y]);
- x := newx;
- y := newy;
- ELSE
- EXCL(World[newx,newy].Entry, newentry);
- END;
- ELSE
- x := newx;
- y := newy;
- END;
- END;
- MaxCar := MAX(CARDINAL);
- FOR v := 1 TO CarNum-Lib.RANDOM(50) DO (* ein paar Autos zufällig verteilen *)
- NewCar;
- END;
- END InitWorld;
-
- (* -------------------------------------------------------------------------- *)
-
- PROCEDURE Statistics;
- (* diese Prozedur läuft als Prozeß quasiparallel (abwechselnd scheibchen-
- weise) mit der Endloschleife in "RunSimulation" und zeigt jeweils die
- aktuell gültige Durchschnittsgeschwindigleit aller im Netz befindlichen
- Autos an. Nach jeder 200. Ausgabe wird das Ausgabefenster verschoben. *)
- CONST
- MsgWindowDef = Window.WinDef (5,5, 37,10, (* das Ausgabefenster *)
- Window.Blue,Window.LightGray,
- FALSE,TRUE,FALSE,TRUE,
- Window.SingleFrame,
- Window.Red, Window.LightGray);
- VAR
- MsgW : Window.WinType;
- MsgX,MsgY : CARDINAL;
- WD : Window.WinDef;
- count : CARDINAL;
- change : BOOLEAN;
- BEGIN
- WD := MsgWindowDef; (* Dieser Teil (bis LOOP) wird nur einmal ausgeführt! *)
- IF IsBW THEN
- WD.Foreground := Window.Black;
- WD.FrameBack := Window.LightGray;
- WD.FrameFore := Window.Black;
- END;
- MsgW := Window.Open(WD);
- Window.SetTitle(MsgW,' JPI TopSpeed Modula-2 Demo ',Window.CenterUpperTitle);
- MsgX := 5; MsgY := 5;
- Window.Use(MsgW); (* jetzt das Ausgabefenster benutzen! *)
- count := 200;
- LOOP
- Process.Delay(1);
- IO.WrLn; (* Daten ausgeben *)
- IO.WrStr('Cars: ');IO.WrCard(MaxCar+1,1);
- IO.WrStr(' Average Speed: ');IO.WrCard(AvSpeed DIV 10, 1);
- IO.WrChar('.');IO.WrCard(AvSpeed MOD 10, 1);
- IF count=0 THEN
- MsgX := Lib.RANDOM(50); (* nach jedem 200. Durchlauf wird das... *)
- MsgY := Lib.RANDOM(20); (* ... Ausgabefenster zufällig verschoben! *)
- count := 200;
- ELSE
- DEC(count);
- END;
- change := TRUE;
- IF WD.X1+2<MsgX THEN INC(WD.X1,3); INC(WD.X2,3);
- ELSIF WD.X1>MsgX+2 THEN DEC(WD.X1,3); DEC(WD.X2,3);
- ELSIF WD.Y1=MsgY THEN change := FALSE;
- END;
- IF WD.Y1<MsgY THEN INC(WD.Y1); INC(WD.Y2);
- ELSIF WD.Y1>MsgY THEN DEC(WD.Y1); DEC(WD.Y2);
- END;
- IF change THEN
- Window.Change(MsgW, WD.X1, WD.Y1, WD.X2, WD.Y2);
- END;
- END;
- END Statistics;
-
- (* -------------------------------------------------------------------------- *)
-
- PROCEDURE RunSimulation;
- (* Der Hauptteil des Programms: alle Autos werden mit zufällig geänderter
- Geschwindigkeit weiterbewegt (Kurve, Unfall?), bei durchschnittlich je-
- dem 5. Aufruf wird die Zahl der Autos um eins erhöht bzw. verringert. *)
- VAR
- v : CARDINAL;
- i : CARDINAL;
- SumSpeed : CARDINAL;
- up : BOOLEAN;
- c : CHAR;
- BEGIN
- up := TRUE;
- LOOP (* diese Simulationsschleife läuft solange... *)
- SumSpeed := 0;
- v := MaxCar+1;
- REPEAT (* alle vorhandenen Autos bewegen... *)
- DEC(v); (* ...und Geschwindigkeiten aufsummieren *)
- MoveCar(v);
- SumSpeed := SumSpeed + Car[v].Speed;
- UNTIL v=0;
- IF IO.KeyPressed() THEN
- c := IO.RdKey(); (* ...bis eine Taste gedrückt wurde...*)
- EXIT (* ...dann ist Ende der Vorstellung! *)
- END;
- AvSpeed := SumSpeed DIV (MaxCar+1); (* Durchschnitt bilden *)
- IF Lib.RANDOM(5)=0 THEN (* Anzahl der Autos verändern *)
- IF MaxCar=1 THEN
- up := TRUE
- ELSIF MaxCar=CarNum-1 THEN
- up := FALSE;
- END;
- IF up THEN
- NewCar (* die Blechlawine wird größer! *)
- ELSE
- ParkCar(Lib.RANDOM(MaxCar+1)); (* TÜV ist abgelaufen! *)
- END;
- Window.TextColor(Window.Black);
- END;
- END;
- END RunSimulation;
-
- (* -------------------------------------------------------------------------- *)
-
- PROCEDURE Intro;
- (* Anzeige des Titelbildschirms, Generierung des Straßennetzes *)
- VAR
- DescWin : Window.WinType;
- r : CARDINAL;
- BEGIN
- Window.CursorOff;
- Window.SetProcessLocks(Process.Lock,Process.Unlock);
- DescWin := Window.Open(Window.WinDef(0,11,79,25,Window.LightGray,Window.Black,
- FALSE,TRUE,FALSE,TRUE,
- Window.DoubleFrame,Window.Black,Window.LightGray));
-
- Window.SetTitle(DescWin,' JPI TopSpeed Modula-2 : Verkehrssimulation ',Window.CenterUpperTitle);
- IO.WrLn;
- IO.WrStr(' Dieses Programm simuliert den Verkehrsfluß in einem geschlossenen System.');
- IO.WrLn;
- IO.WrStr(' Bis zu 200 Autos bewegen sich zufällig in einem Netz von Einbahnstraßen.');IO.WrLn;
- IO.WrLn;
- IO.WrStr(' Ein Prozeß zeigt die mittlere Geschwindigkeit der sich bewegenden Autos an.');IO.WrLn;
- IO.WrStr(' Bei Geschwindigkeiten von unter 20.0 km/h besteht akute Staugefahr!.');IO.WrLn;
- IO.WrLn;
- IO.WrStr(' Jede Ähnlichkeit mit dem Rush-Hour-Verkehr in New York ist rein zufällig!');IO.WrLn;
- IO.WrLn; IO.WrLn; Window.TextColor(Window.White);
- IO.WrStr(' Start und Abbruch der Simulation durch Drücken einer beliebigen Taste.');IO.WrLn;
- Window.Use(Window.FullScreen);
- Window.SetWrap(FALSE);
- Window.TextBackground(Window.Black);
- IF IsBW THEN
- Window.TextColor(Window.LightGray);
- ELSE
- Window.TextColor(Window.Green);
- END;
- FOR r := 1 TO Window.ScreenDepth DO (* Bildschirm mit Rastermuster füllen *)
- Window.GotoXY(1,r);
- IO.WrCharRep(CHAR(178),Window.ScreenWidth);
- END;
- Window.TextBackground(Window.Black);
- Lib.RANDOMIZE; (* Zufallszahlengenerator initialisieren *)
- InitWorld; (* Straßennetz generieren und Autos placieren *)
- WHILE NOT IO.KeyPressed() DO END; (* auf Starttaste warten... *)
- IF IO.RdKey()=' ' THEN END; (* ...und Taste auslesen... *)
- Window.Close(DescWin); (* ...und Info-Fenster schließen! *)
- END Intro;
-
- (* -------------------------------------------------------------------------- *)
-
- PROCEDURE CheckBW;
- (* ermittelt, ob der eingestellte Bildschirmmodus farbig oder monochrom ist *)
- VAR
- R : SYSTEM.Registers;
- BEGIN
- R.AH := 15; (* Ermittle aktuellen Bildschirmstatus *)
- Lib.Intr(R,10H);
- IsBW := (R.AL = 0) (* Text BW 40x25 *)
- OR (R.AL = 2) (* Text BW 80x25 *)
- OR (R.AL = 4) (* Grafik BW 320x200 *)
- OR (R.AL = 6) (* Grafik BW 640x200 *)
- OR (R.AL = 7); (* Grafik BW 720x350 *)
- END CheckBW;
-
- (* -------------------------------------------------------------------------- *)
-
- BEGIN
- CheckBW; (* welcher Bildschirmmodus ist aktiv? *)
- Intro;
- AvSpeed := 500;
- Process.StartScheduler; (* Scheduler initialisieren ... *)
- Process.StartProcess(Statistics, 2000H, 1); (* ...den Prozess starten... *)
- RunSimulation; (* ...und ab geht die Post! *)
- Lib.NoSound;
- Window.GotoXY(1, Window.ScreenDepth);
- Window.CursorOn;
- END Traffic.