demo.mod 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. (*$R-,I-,O-*)
  2. MODULE Demo;
  3. IMPORT SYSTEM,IO,Window,Lib,Process ;
  4. FROM Lib IMPORT RANDOM ;
  5. CONST
  6. xNum = Window.ScreenWidth;
  7. yNum = Window.ScreenDepth;
  8. CarNum = 100 ;
  9. TYPE
  10. xCoord = [0..xNum-1] ;
  11. yCoord = [0..yNum-1] ;
  12. Coord = RECORD
  13. x : xCoord ;
  14. y : yCoord ;
  15. END ;
  16. Direction = (East,South,North,West) ;
  17. DirSet = SET OF Direction ;
  18. Location = RECORD
  19. Entry,Exit : DirSet ;
  20. Occupied : BOOLEAN ;
  21. Scenery : SHORTCARD ;
  22. END ;
  23. DA = ARRAY Direction OF Direction ;
  24. CarRange = [0..CarNum-1] ;
  25. CONST
  26. Reverse = DA(West,North,South,East) ; (* reverse of Direction *)
  27. VAR
  28. World : ARRAY xCoord,yCoord OF Location;
  29. Car : ARRAY CarRange OF
  30. RECORD
  31. Pos : Coord;
  32. Color : Window.Color;
  33. Speed : CARDINAL ; (* MPH*10 *)
  34. LastD : Direction ;
  35. END;
  36. ToNorth : ARRAY yCoord OF CARDINAL ;
  37. ToSouth : ARRAY yCoord OF CARDINAL ;
  38. ToEast : ARRAY xCoord OF CARDINAL ;
  39. ToWest : ARRAY xCoord OF CARDINAL ;
  40. IsBW : BOOLEAN ;
  41. VAR (*W+*) (* Volatile *)
  42. AvSpeed : CARDINAL;
  43. MaxCar : CarRange ;
  44. (*$W=*)
  45. PROCEDURE Plot(x : xCoord; y : yCoord );
  46. TYPE
  47. Chars = ARRAY SHORTCARD [0..15] OF SHORTCARD ;
  48. CONST
  49. Road = Chars(176,32,32,201,32,200,186,204,32,205,187,203,188,202,185,206);
  50. VAR
  51. loc : Location;
  52. sc : SHORTCARD ;
  53. color : Window.Color ;
  54. BEGIN
  55. loc := World[x,y] ;
  56. sc := Road[SHORTCARD(loc.Entry+loc.Exit)];
  57. IF sc=176 THEN
  58. IF IsBW THEN color := Window.LightGray
  59. ELSE color := Window.Brown
  60. END ;
  61. ELSIF sc=32 THEN RETURN
  62. ELSE color := Window.LightGray ;
  63. END ;
  64. Window.TextColor(color);
  65. Window.DirectWrite(x+1,y+1,ADR(sc),1) ;
  66. END Plot;
  67. PROCEDURE PlotCar( v : CarRange ; dir : Direction );
  68. TYPE
  69. Chars = ARRAY Direction OF SHORTCARD ;
  70. CONST
  71. CarSym = Chars(16,31,30,17) ;
  72. BEGIN
  73. Window.TextColor(Car[v].Color);
  74. Window.DirectWrite(Car[v].Pos.x+1,Car[v].Pos.y+1,ADR(CarSym[dir]),1) ;
  75. END PlotCar;
  76. PROCEDURE NewCar ;
  77. VAR
  78. x : xCoord ;
  79. y : yCoord ;
  80. v : CarRange ;
  81. d : Direction ;
  82. BEGIN
  83. INC(MaxCar) ;
  84. v := MaxCar ;
  85. d := MAX(Direction);
  86. LOOP
  87. IF d=MAX(Direction) THEN
  88. REPEAT
  89. x := RANDOM(xNum); y := RANDOM(yNum) ;
  90. UNTIL NOT World[x,y].Occupied ;
  91. d := MIN(Direction) ;
  92. ELSE
  93. INC(d) ;
  94. END ;
  95. IF d IN World[x,y].Exit THEN EXIT END ;
  96. END ;
  97. World[x,y].Occupied := TRUE ;
  98. WITH Car[v] DO
  99. Pos.x := x ; Pos.y := y ;
  100. Speed := RANDOM(100) ;
  101. LastD := Direction(RANDOM(4));
  102. END ;
  103. IF IsBW THEN Car[v].Color := Window.White ;
  104. ELSE Car[v].Color := Window.Color( 9+RANDOM(7) ) ;
  105. END ;
  106. PlotCar(v,d) ;
  107. END NewCar ;
  108. PROCEDURE ParkCar ( v : CarRange ) ;
  109. VAR
  110. x : xCoord ;
  111. y : yCoord ;
  112. BEGIN
  113. x := Car[v].Pos.x ; y := Car[v].Pos.y ;
  114. IF MaxCar = 0 THEN HALT END ;
  115. World[x,y].Occupied := FALSE ;
  116. Plot(x,y);
  117. WHILE v<MaxCar DO
  118. Car[v] := Car[v+1] ; INC(v) ;
  119. END ;
  120. DEC(MaxCar) ;
  121. END ParkCar ;
  122. PROCEDURE MoveCar ( v:CarRange ) ;
  123. VAR
  124. exit : Direction;
  125. x,newx : xCoord ;
  126. y,newy : yCoord ;
  127. tried : DirSet ;
  128. CONST
  129. SpeedLimit = 450 ; (* 30 MPH ?! *)
  130. BEGIN
  131. WITH Car[v] DO
  132. IF Speed < SpeedLimit THEN
  133. INC(Speed,RANDOM(80)) ;
  134. IF Speed>SpeedLimit THEN Speed := SpeedLimit END ;
  135. IF Speed<RANDOM(SpeedLimit) THEN RETURN END ;
  136. END ;
  137. x := Pos.x; y := Pos.y;
  138. END ;
  139. tried := DirSet{};
  140. LOOP
  141. exit := Direction(RANDOM(4));
  142. IF exit IN World[x,y].Exit THEN
  143. newx := x; newy := y;
  144. CASE exit OF
  145. | East : newx := ToEast[x];
  146. | South : newy := ToSouth[y];
  147. | North : newy := ToNorth[y];
  148. | West : newx := ToWest[x];
  149. END;
  150. IF World[newx,newy].Occupied THEN
  151. Car[v].Speed := Car[v].Speed DIV 4 (* Brake *) ; RETURN ;
  152. ELSE
  153. World[x,y].Occupied := FALSE ;
  154. Plot(x,y);
  155. World[newx,newy].Occupied := TRUE ;
  156. WITH Car[v] DO
  157. Pos.x := newx; Pos.y := newy;
  158. IF LastD<>exit THEN
  159. (* Cornering *)
  160. Speed := Speed DIV 2 ;
  161. LastD := exit ;
  162. END ;
  163. END ;
  164. PlotCar(v,exit);
  165. RETURN ;
  166. END;
  167. END;
  168. INCL(tried,exit);
  169. IF tried = DirSet{East..West} THEN
  170. Car[v].Speed := 0 (* Stop *) ; RETURN ;
  171. RETURN ;
  172. END;
  173. END;
  174. END MoveCar;
  175. PROCEDURE InitWorld;
  176. PROCEDURE ok( x:xCoord ; y:yCoord ) : BOOLEAN;
  177. (* Returns whether we have planning permission to build road at x,y *)
  178. (* Tries to avoid making very small plots of land *)
  179. BEGIN
  180. RETURN ( World[ x,y ].Exit = DirSet{} ) OR
  181. ( World[ ToEast[x],y ].Exit = DirSet{} ) OR
  182. ( World[ ToEast[x],ToSouth[y] ].Exit = DirSet{} ) OR
  183. ( World[ x,ToSouth[y] ].Exit = DirSet{} ) ;
  184. END ok;
  185. VAR
  186. x,newx : xCoord ;
  187. y,newy : yCoord ;
  188. n : CARDINAL;
  189. newdir,
  190. newentry: Direction;
  191. v : CarRange ;
  192. BEGIN
  193. FOR x := MIN(xCoord) TO MAX(xCoord) DO
  194. ToEast[x] := (x+1)MOD xNum ;
  195. ToWest[x] := (x+xNum-1)MOD xNum ;
  196. END ;
  197. FOR y := MIN(yCoord) TO MAX(yCoord) DO
  198. ToSouth[y] := (y+1)MOD yNum ;
  199. ToNorth[y] := (y+yNum-1)MOD yNum ;
  200. END ;
  201. Lib.Fill(ADR(World),SIZE(World),0) ;
  202. x := xNum DIV 2; y := yNum DIV 2;
  203. newx := x; newy := y;
  204. newdir := Direction(RANDOM(4));
  205. n := 1000 + RANDOM(1000);
  206. (* initialise map using randomish walk of n steps *)
  207. WHILE n <> 0 DO
  208. DEC(n);
  209. IF RANDOM(4)=0 THEN
  210. newdir := Direction(RANDOM(4));
  211. END;
  212. WHILE newdir IN World[x,y].Entry DO (* enforce one-way streets *)
  213. newdir := Direction(RANDOM(4)) ;
  214. END;
  215. newx := x; newy := y;
  216. CASE newdir OF
  217. | East : newx := ToEast[x] ;
  218. | South : newy := ToSouth[y] ;
  219. | North : newy := ToNorth[y] ;
  220. | West : newx := ToWest[x] ;
  221. END;
  222. newentry := Reverse[newdir] ;
  223. IF NOT ( newentry IN World[newx,newy].Entry ) THEN
  224. INCL( World[newx,newy].Entry, newentry );
  225. IF (RANDOM(50)=0) OR
  226. ( ok( newx, newy ) AND
  227. ok( ToWest[newx], newy ) AND
  228. ok( ToWest[newx], ToNorth[newy] ) AND
  229. ok( newx, ToNorth[newy] )
  230. )
  231. THEN
  232. INCL( World[x,y].Exit, newdir );
  233. Plot( x, y );
  234. Plot( newx,newy );
  235. Plot( ToEast[x],y );
  236. Plot( ToWest[x],y );
  237. Plot( x, ToNorth[y] );
  238. Plot( x, ToSouth[y] );
  239. x := newx; y := newy;
  240. ELSE
  241. EXCL( World[newx,newy].Entry, newentry );
  242. END;
  243. ELSE
  244. x := newx; y := newy;
  245. END;
  246. END;
  247. (* make some cars *)
  248. MaxCar := MAX(CARDINAL) ;
  249. FOR v := 1 TO CarNum-RANDOM(50) DO
  250. NewCar ;
  251. END;
  252. END InitWorld;
  253. PROCEDURE Statistics ;
  254. CONST
  255. MsgWindowDef = Window.WinDef ( 5,5, 37,10,
  256. Window.Blue,Window.LightGray,
  257. FALSE,TRUE,FALSE,TRUE,
  258. Window.SingleFrame,
  259. Window.Red, Window.LightGray );
  260. VAR
  261. MsgW : Window.WinType;
  262. MsgX,MsgY : CARDINAL;
  263. WD : Window.WinDef ;
  264. count : CARDINAL ;
  265. change : BOOLEAN ;
  266. BEGIN
  267. WD := MsgWindowDef ;
  268. IF IsBW THEN
  269. WD.Foreground := Window.Black ;
  270. WD.FrameBack := Window.LightGray ;
  271. WD.FrameFore := Window.Black ;
  272. END ;
  273. MsgW := Window.Open( WD );
  274. Window.SetTitle(MsgW,' JPI TopSpeed Modula-2 Demo ',Window.CenterUpperTitle) ;
  275. MsgX := 5 ; MsgY := 5 ;
  276. Window.Use(MsgW);
  277. count := 200 ;
  278. LOOP
  279. Process.Delay(1) ;
  280. IO.WrLn;
  281. IO.WrStr('Cars: ');IO.WrCard(MaxCar+1,1) ;
  282. IO.WrStr(' Average Speed: ');IO.WrCard(AvSpeed DIV 10, 1 );
  283. IO.WrChar('.');IO.WrCard(AvSpeed MOD 10, 1 );
  284. IF count=0 THEN
  285. MsgX := RANDOM(50);
  286. MsgY := RANDOM(20);
  287. count := 200 ;
  288. ELSE
  289. DEC(count) ;
  290. END ;
  291. change := TRUE ;
  292. IF WD.X1+2<MsgX THEN INC(WD.X1,3);INC(WD.X2,3);
  293. ELSIF WD.X1>MsgX+2 THEN DEC(WD.X1,3);DEC(WD.X2,3);
  294. ELSIF WD.Y1=MsgY THEN change := FALSE ;
  295. END ;
  296. IF WD.Y1<MsgY THEN INC(WD.Y1);INC(WD.Y2);
  297. ELSIF WD.Y1>MsgY THEN DEC(WD.Y1);DEC(WD.Y2);
  298. END ;
  299. IF change THEN
  300. Window.Change(MsgW,WD.X1,WD.Y1,WD.X2,WD.Y2) ;
  301. END ;
  302. END ;
  303. END Statistics;
  304. PROCEDURE RunSimulation ;
  305. VAR
  306. v : CARDINAL;
  307. i : CARDINAL;
  308. SumSpeed : CARDINAL ;
  309. up : BOOLEAN ;
  310. c : CHAR ;
  311. BEGIN
  312. up := TRUE ;
  313. LOOP
  314. SumSpeed := 0;
  315. v := MaxCar+1 ;
  316. REPEAT
  317. DEC(v) ;
  318. MoveCar(v) ;
  319. SumSpeed := SumSpeed + Car[v].Speed ;
  320. UNTIL v=0;
  321. IF IO.KeyPressed() THEN
  322. c := IO.RdKey() ;
  323. EXIT
  324. END ;
  325. AvSpeed := SumSpeed DIV (MaxCar+1);
  326. (* Add or subtract card *)
  327. IF RANDOM(5)=0 THEN
  328. IF MaxCar=1 THEN up := TRUE
  329. ELSIF MaxCar=CarNum-1 THEN up := FALSE ;
  330. END ;
  331. IF up THEN NewCar ELSE ParkCar(RANDOM(MaxCar+1)) END ;
  332. Window.TextColor(Window.Black) ;
  333. END ;
  334. END ;
  335. END RunSimulation ;
  336. PROCEDURE Intro ;
  337. VAR
  338. DescWin : Window.WinType ;
  339. r : CARDINAL ;
  340. BEGIN
  341. Window.CursorOff ;
  342. Window.SetProcessLocks(Process.Lock,Process.Unlock) ;
  343. DescWin := Window.Open(Window.WinDef(0,11,79,25,Window.LightGray,Window.Black,
  344. FALSE,TRUE,FALSE,TRUE,
  345. Window.DoubleFrame,Window.Black,Window.LightGray)) ;
  346. Window.SetTitle(DescWin,' JPI TopSpeed Modula-2 : Traffic Simulation ',Window.CenterUpperTitle);
  347. IO.WrLn;
  348. IO.WrStr(' This program is a simple simulation of traffic flow in a closed road system.');
  349. IO.WrLn;
  350. IO.WrStr(' Up to 200 cars move randomly within a one-way network of roads.');IO.WrLn;
  351. IO.WrLn;
  352. IO.WrStr(' The average speed is monitored by a separate process and displayed in');IO.WrLn;
  353. IO.WrStr(' the Statistics window. 30.0 MPH indicates no congestion.');IO.WrLn;
  354. IO.WrLn;
  355. IO.WrStr(' Any resemblance to rush-hour traffic in New York is purely coincidental.');IO.WrLn;
  356. IO.WrLn; IO.WrLn; Window.TextColor(Window.White) ;
  357. IO.WrStr(' Press any key to start the simulation and then any key to terminate.');IO.WrLn;
  358. Window.Use(Window.FullScreen);
  359. Window.SetWrap(FALSE) ;
  360. Window.TextBackground(Window.Black) ;
  361. IF IsBW THEN
  362. Window.TextColor(Window.LightGray) ;
  363. ELSE
  364. Window.TextColor(Window.Green) ;
  365. END ;
  366. FOR r := 1 TO Window.ScreenDepth DO
  367. Window.GotoXY(1,r) ;
  368. IO.WrCharRep(CHAR(178),Window.ScreenWidth) ;
  369. END ;
  370. Window.TextBackground(Window.Black) ;
  371. Lib.RANDOMIZE;
  372. InitWorld;
  373. WHILE NOT IO.KeyPressed() DO END ;
  374. IF IO.RdKey()=' ' THEN END ;
  375. Window.Close(DescWin) ;
  376. END Intro;
  377. PROCEDURE CheckBW ;
  378. VAR
  379. R : SYSTEM.Registers ;
  380. BEGIN
  381. R.AH := 15 ;
  382. Lib.Intr(R,10H) ;
  383. IsBW := (R.AL = 0)OR(R.AL = 2)OR(R.AL = 5)OR(R.AL = 6)OR(R.AL = 7) ;
  384. END CheckBW ;
  385. BEGIN
  386. CheckBW ;
  387. Intro ;
  388. AvSpeed := 300 ;
  389. Process.StartScheduler ;
  390. Process.StartProcess(Statistics,2000H,1) ;
  391. RunSimulation ;
  392. Lib.NoSound ;
  393. Window.GotoXY(1,Window.ScreenDepth);
  394. Window.CursorOn ;
  395. END Demo.
  396.