showcase_snake.mod 7.8 KB

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