showcase_snake.mod 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. MODULE showcase_snake ;
  2. (*
  3. m2SDL3 demo, ported from SDL upstream examples/demo/01-snake
  4. (public domain): the classic Snake game on a 24x18 board.
  5. Arrows steer, R restarts, Q or Escape quits. The board packs
  6. each cell into 3 bits (7 = 162 bytes); collisions restart.
  7. SDL3 native notes
  8. - SDL_FRect (SHORTREAL) rects via MakeFRect; Uint64 ms ticks
  9. come from SDL_GetTicks (64 bit in SDL3).
  10. - Keyboard arrives decoded via SDL3Utils.PollM2Event (type at
  11. offset 0, keycode at offset 28); keycodes in SDL3Key.
  12. - Window close buttons decode as evQuit too.
  13. - Callback structure flattened into a main loop with a 30 s cap
  14. so the demo also terminates unattended.
  15. *)
  16. FROM SYSTEM IMPORT CARDINAL8;
  17. FROM SDL3 IMPORT SDL_Init, SDL_Quit, SDL_Delay,
  18. SDL_GetTicks, SDLInitVideo, SDLInitEvents;
  19. FROM SDL3Video IMPORT WindowHandle, SDL_DestroyWindow, WindowResizable;
  20. FROM SDL3Render IMPORT RendererHandle,
  21. SDL_CreateWindowAndRenderer, SDL_DestroyRenderer,
  22. SDL_SetRenderDrawColor, SDL_RenderClear, SDL_RenderPresent,
  23. SDL_SetRenderLogicalPresentation, LogicalLetterbox,
  24. SDL_RenderFillRect;
  25. FROM SDL3Rect IMPORT SDLFRect, MakeFRect;
  26. FROM SDL3Utils IMPORT PollM2Event, M2Event, evQuit, evKey;
  27. FROM SDL3Key IMPORT KeyEscape, KeyQ, KeyR,
  28. KeyRight, KeyLeft, KeyDown, KeyUp;
  29. FROM libc IMPORT printf, rand, srand;
  30. CONST StepMs = 125; BlockPx = 24; GW = 24; GH = 18; MaxMs = 30000;
  31. CONST WinW = GW * BlockPx; WinH = GH * BlockPx;
  32. CONST CellNothing = 0; CellRight = 1; CellUp = 2;
  33. CONST CellLeft = 3; CellDown = 4; CellFood = 5;
  34. CONST DirRight = 0; DirUp = 1; DirLeft = 2; DirDown = 3;
  35. VAR
  36. cells: ARRAY [0..161] OF CARDINAL8;
  37. pow2: ARRAY [0..7] OF CARDINAL;
  38. headX, headY, tailX, tailY, nextDir, inhibit: INTEGER;
  39. occupied: CARDINAL;
  40. win: WindowHandle;
  41. ren: RendererHandle;
  42. PROCEDURE CellAt (x, y: INTEGER) : CARDINAL;
  43. VAR shift, byte, adj, range: CARDINAL;
  44. BEGIN
  45. shift := VAL(CARDINAL, x + y * GW) * 3;
  46. byte := shift DIV 8;
  47. adj := shift MOD 8;
  48. range := VAL(CARDINAL, cells[byte])
  49. + VAL(CARDINAL, cells[byte+1]) * 256;
  50. RETURN range DIV pow2[adj] MOD 8
  51. END CellAt;
  52. PROCEDURE PutCell (x, y: INTEGER; ct: CARDINAL);
  53. VAR shift, byte, adj, range, p: CARDINAL;
  54. BEGIN
  55. shift := VAL(CARDINAL, x + y * GW) * 3;
  56. byte := shift DIV 8;
  57. adj := shift MOD 8;
  58. range := VAL(CARDINAL, cells[byte])
  59. + VAL(CARDINAL, cells[byte+1]) * 256;
  60. p := pow2[adj];
  61. range := range DIV (p * 8) * (p * 8) + range MOD p + ct * p;
  62. cells[byte] := VAL(CARDINAL8, range MOD 256);
  63. cells[byte+1] := VAL(CARDINAL8, range DIV 256)
  64. END PutCell;
  65. PROCEDURE NewFood;
  66. VAR x, y: INTEGER;
  67. BEGIN
  68. LOOP
  69. x := rand() MOD GW;
  70. y := rand() MOD GH;
  71. IF CellAt(x, y) = CellNothing THEN
  72. PutCell(x, y, CellFood);
  73. EXIT
  74. END
  75. END
  76. END NewFood;
  77. PROCEDURE SnakeInit;
  78. VAR i: INTEGER;
  79. BEGIN
  80. FOR i := 0 TO 161 DO cells[i] := VAL(CARDINAL8, 0) END;
  81. headX := GW DIV 2; tailX := headX;
  82. headY := GH DIV 2; tailY := headY;
  83. nextDir := DirRight;
  84. inhibit := 4;
  85. occupied := 4; DEC(occupied);
  86. PutCell(tailX, tailY, CellRight);
  87. FOR i := 0 TO 3 DO NewFood; INC(occupied) END
  88. END SnakeInit;
  89. PROCEDURE SnakeRedir (dir: INTEGER);
  90. VAR ct: CARDINAL;
  91. BEGIN
  92. ct := CellAt(headX, headY);
  93. IF ((dir = DirRight) AND (ct # CellLeft))
  94. OR ((dir = DirUp) AND (ct # CellDown))
  95. OR ((dir = DirLeft) AND (ct # CellRight))
  96. OR ((dir = DirDown) AND (ct # CellUp)) THEN
  97. nextDir := dir
  98. END
  99. END SnakeRedir;
  100. PROCEDURE Wrap (VAR v: INTEGER; max: INTEGER);
  101. BEGIN
  102. IF v < 0 THEN v := max - 1
  103. ELSIF v > max - 1 THEN v := 0 END
  104. END Wrap;
  105. PROCEDURE SnakeStep;
  106. VAR dirCell, ct: CARDINAL; px, py: INTEGER;
  107. BEGIN
  108. dirCell := VAL(CARDINAL, nextDir + 1);
  109. DEC(inhibit);
  110. IF inhibit = 0 THEN
  111. INC(inhibit);
  112. ct := CellAt(tailX, tailY);
  113. PutCell(tailX, tailY, CellNothing);
  114. IF ct = CellRight THEN INC(tailX)
  115. ELSIF ct = CellUp THEN DEC(tailY)
  116. ELSIF ct = CellLeft THEN DEC(tailX)
  117. ELSIF ct = CellDown THEN INC(tailY) END;
  118. Wrap(tailX, GW);
  119. Wrap(tailY, GH)
  120. END;
  121. px := headX; py := headY;
  122. IF nextDir = DirRight THEN INC(headX)
  123. ELSIF nextDir = DirUp THEN DEC(headY)
  124. ELSIF nextDir = DirLeft THEN DEC(headX)
  125. ELSIF nextDir = DirDown THEN INC(headY) END;
  126. Wrap(headX, GW);
  127. Wrap(headY, GH);
  128. ct := CellAt(headX, headY);
  129. IF (ct # CellNothing) AND (ct # CellFood) THEN
  130. SnakeInit;
  131. RETURN
  132. END;
  133. PutCell(px, py, dirCell);
  134. PutCell(headX, headY, dirCell);
  135. IF ct = CellFood THEN
  136. IF occupied = VAL(CARDINAL, GW * GH) THEN
  137. SnakeInit;
  138. RETURN
  139. END;
  140. NewFood;
  141. INC(inhibit);
  142. INC(occupied)
  143. END
  144. END SnakeStep;
  145. PROCEDURE HandleSym (sym: INTEGER; VAR quit: BOOLEAN);
  146. BEGIN
  147. IF sym = KeyEscape THEN quit := TRUE
  148. ELSIF sym = KeyQ THEN quit := TRUE
  149. ELSIF sym = KeyR THEN SnakeInit
  150. ELSIF sym = KeyRight THEN SnakeRedir(DirRight)
  151. ELSIF sym = KeyUp THEN SnakeRedir(DirUp)
  152. ELSIF sym = KeyLeft THEN SnakeRedir(DirLeft)
  153. ELSIF sym = KeyDown THEN SnakeRedir(DirDown) END
  154. END HandleSym;
  155. PROCEDURE DrawBoard;
  156. VAR gx, gy: INTEGER; ct: CARDINAL; r: SDLFRect;
  157. BEGIN
  158. SDL_SetRenderDrawColor(ren, VAL(CARDINAL8, 0), VAL(CARDINAL8, 0),
  159. VAL(CARDINAL8, 0), VAL(CARDINAL8, 255));
  160. SDL_RenderClear(ren);
  161. FOR gy := 0 TO GH - 1 DO
  162. FOR gx := 0 TO GW - 1 DO
  163. ct := CellAt(gx, gy);
  164. IF ct # CellNothing THEN
  165. r := MakeFRect(VAL(SHORTREAL, VAL(REAL, gx * BlockPx)),
  166. VAL(SHORTREAL, VAL(REAL, gy * BlockPx)),
  167. VAL(SHORTREAL, VAL(REAL, BlockPx)),
  168. VAL(SHORTREAL, VAL(REAL, BlockPx)));
  169. IF ct = CellFood THEN
  170. SDL_SetRenderDrawColor(ren, VAL(CARDINAL8, 80),
  171. VAL(CARDINAL8, 80),
  172. VAL(CARDINAL8, 255),
  173. VAL(CARDINAL8, 255))
  174. ELSE
  175. SDL_SetRenderDrawColor(ren, VAL(CARDINAL8, 0),
  176. VAL(CARDINAL8, 128),
  177. VAL(CARDINAL8, 0),
  178. VAL(CARDINAL8, 255))
  179. END;
  180. SDL_RenderFillRect(ren, r)
  181. END
  182. END
  183. END;
  184. r := MakeFRect(VAL(SHORTREAL, VAL(REAL, headX * BlockPx)),
  185. VAL(SHORTREAL, VAL(REAL, headY * BlockPx)),
  186. VAL(SHORTREAL, VAL(REAL, BlockPx)),
  187. VAL(SHORTREAL, VAL(REAL, BlockPx)));
  188. SDL_SetRenderDrawColor(ren, VAL(CARDINAL8, 255), VAL(CARDINAL8, 255),
  189. VAL(CARDINAL8, 0), VAL(CARDINAL8, 255));
  190. SDL_RenderFillRect(ren, r);
  191. SDL_RenderPresent(ren)
  192. END DrawBoard;
  193. VAR
  194. ev: M2Event;
  195. quit: BOOLEAN;
  196. start, now, lastStep: LONGCARD;
  197. i: INTEGER;
  198. BEGIN
  199. FOR i := 0 TO 7 DO
  200. IF i = 0 THEN pow2[0] := 1 ELSE pow2[i] := pow2[i-1] * 2 END
  201. END;
  202. srand(VAL(INTEGER, SDL_GetTicks()));
  203. IF NOT SDL_Init(SDLInitVideo + SDLInitEvents) THEN
  204. printf("showcase_snake: SDL_Init failed\n");
  205. HALT(1)
  206. END;
  207. IF NOT SDL_CreateWindowAndRenderer("m2SDL3 snake",
  208. WinW, WinH,
  209. VAL(LONGCARD, WindowResizable),
  210. win, ren) THEN
  211. printf("showcase_snake: CreateWindowAndRenderer failed\n");
  212. SDL_Quit;
  213. HALT(1)
  214. END;
  215. SDL_SetRenderLogicalPresentation(ren, WinW, WinH, LogicalLetterbox);
  216. SnakeInit;
  217. printf("showcase_snake: arrows steer, R restarts, Q quits\n");
  218. quit := FALSE;
  219. start := SDL_GetTicks();
  220. lastStep := start;
  221. WHILE NOT quit DO
  222. WHILE PollM2Event(ev) DO
  223. IF ev.kind = evQuit THEN quit := TRUE
  224. ELSIF (ev.kind = evKey) AND ev.pressed THEN
  225. HandleSym(ev.sym, quit)
  226. END
  227. END;
  228. now := SDL_GetTicks();
  229. WHILE now - lastStep >= VAL(LONGCARD, StepMs) DO
  230. SnakeStep;
  231. lastStep := lastStep + VAL(LONGCARD, StepMs)
  232. END;
  233. DrawBoard;
  234. IF now - start > VAL(LONGCARD, MaxMs) THEN quit := TRUE END;
  235. SDL_Delay(16)
  236. END;
  237. SDL_DestroyRenderer(ren);
  238. SDL_DestroyWindow(win);
  239. SDL_Quit;
  240. printf("showcase_snake: bye\n")
  241. END showcase_snake.