DEMO.MOD 12 KB

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