| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- MODULE showcase_snake ;
- (*
- m2SDL3 demo, ported from SDL upstream examples/demo/01-snake
- (public domain): the classic Snake game on a 24x18 board.
- Arrows steer, R restarts, Q or Escape quits. The board packs
- each cell into 3 bits (7 = 162 bytes); collisions restart.
- SDL3 native notes
- - SDL_FRect (SHORTREAL) rects via MakeFRect; Uint64 ms ticks
- come from SDL_GetTicks (64 bit in SDL3).
- - Keyboard arrives decoded via SDL3Utils.PollM2Event (type at
- offset 0, keycode at offset 28); keycodes in SDL3Key.
- - Window close buttons decode as evQuit too.
- - Callback structure flattened into a main loop with a 30 s cap
- so the demo also terminates unattended.
- *)
- FROM SYSTEM IMPORT CARDINAL8;
- FROM SDL3 IMPORT SDL_Init, SDL_Quit, SDL_Delay,
- SDL_GetTicks, SDLInitVideo, SDLInitEvents;
- FROM SDL3Video IMPORT WindowHandle, SDL_DestroyWindow, WindowResizable;
- FROM SDL3Render IMPORT RendererHandle,
- SDL_CreateWindowAndRenderer, SDL_DestroyRenderer,
- SDL_SetRenderDrawColor, SDL_RenderClear, SDL_RenderPresent,
- SDL_SetRenderLogicalPresentation, LogicalLetterbox,
- SDL_RenderFillRect;
- FROM SDL3Rect IMPORT SDLFRect, MakeFRect;
- FROM SDL3Utils IMPORT PollM2Event, M2Event, evQuit, evKey;
- FROM SDL3Key IMPORT KeyEscape, KeyQ, KeyR,
- KeyRight, KeyLeft, KeyDown, KeyUp;
- FROM libc IMPORT printf, rand, srand;
- CONST StepMs = 125; BlockPx = 24; GW = 24; GH = 18; MaxMs = 30000;
- CONST WinW = GW * BlockPx; WinH = GH * BlockPx;
- CONST CellNothing = 0; CellRight = 1; CellUp = 2;
- CONST CellLeft = 3; CellDown = 4; CellFood = 5;
- CONST DirRight = 0; DirUp = 1; DirLeft = 2; DirDown = 3;
- VAR
- cells: ARRAY [0..161] OF CARDINAL8;
- pow2: ARRAY [0..7] OF CARDINAL;
- headX, headY, tailX, tailY, nextDir, inhibit: INTEGER;
- occupied: CARDINAL;
- win: WindowHandle;
- ren: RendererHandle;
- PROCEDURE CellAt (x, y: INTEGER) : CARDINAL;
- VAR shift, byte, adj, range: CARDINAL;
- BEGIN
- shift := VAL(CARDINAL, x + y * GW) * 3;
- byte := shift DIV 8;
- adj := shift MOD 8;
- range := VAL(CARDINAL, cells[byte])
- + VAL(CARDINAL, cells[byte+1]) * 256;
- RETURN range DIV pow2[adj] MOD 8
- END CellAt;
- PROCEDURE PutCell (x, y: INTEGER; ct: CARDINAL);
- VAR shift, byte, adj, range, p: CARDINAL;
- BEGIN
- shift := VAL(CARDINAL, x + y * GW) * 3;
- byte := shift DIV 8;
- adj := shift MOD 8;
- range := VAL(CARDINAL, cells[byte])
- + VAL(CARDINAL, cells[byte+1]) * 256;
- p := pow2[adj];
- range := range DIV (p * 8) * (p * 8) + range MOD p + ct * p;
- cells[byte] := VAL(CARDINAL8, range MOD 256);
- cells[byte+1] := VAL(CARDINAL8, range DIV 256)
- END PutCell;
- PROCEDURE NewFood;
- VAR x, y: INTEGER;
- BEGIN
- LOOP
- x := rand() MOD GW;
- y := rand() MOD GH;
- IF CellAt(x, y) = CellNothing THEN
- PutCell(x, y, CellFood);
- EXIT
- END
- END
- END NewFood;
- PROCEDURE SnakeInit;
- VAR i: INTEGER;
- BEGIN
- FOR i := 0 TO 161 DO cells[i] := VAL(CARDINAL8, 0) END;
- headX := GW DIV 2; tailX := headX;
- headY := GH DIV 2; tailY := headY;
- nextDir := DirRight;
- inhibit := 4;
- occupied := 4; DEC(occupied);
- PutCell(tailX, tailY, CellRight);
- FOR i := 0 TO 3 DO NewFood; INC(occupied) END
- END SnakeInit;
- PROCEDURE SnakeRedir (dir: INTEGER);
- VAR ct: CARDINAL;
- BEGIN
- ct := CellAt(headX, headY);
- IF ((dir = DirRight) AND (ct # CellLeft))
- OR ((dir = DirUp) AND (ct # CellDown))
- OR ((dir = DirLeft) AND (ct # CellRight))
- OR ((dir = DirDown) AND (ct # CellUp)) THEN
- nextDir := dir
- END
- END SnakeRedir;
- PROCEDURE Wrap (VAR v: INTEGER; max: INTEGER);
- BEGIN
- IF v < 0 THEN v := max - 1
- ELSIF v > max - 1 THEN v := 0 END
- END Wrap;
- PROCEDURE SnakeStep;
- VAR dirCell, ct: CARDINAL; px, py: INTEGER;
- BEGIN
- dirCell := VAL(CARDINAL, nextDir + 1);
- DEC(inhibit);
- IF inhibit = 0 THEN
- INC(inhibit);
- ct := CellAt(tailX, tailY);
- PutCell(tailX, tailY, CellNothing);
- IF ct = CellRight THEN INC(tailX)
- ELSIF ct = CellUp THEN DEC(tailY)
- ELSIF ct = CellLeft THEN DEC(tailX)
- ELSIF ct = CellDown THEN INC(tailY) END;
- Wrap(tailX, GW);
- Wrap(tailY, GH)
- END;
- px := headX; py := headY;
- IF nextDir = DirRight THEN INC(headX)
- ELSIF nextDir = DirUp THEN DEC(headY)
- ELSIF nextDir = DirLeft THEN DEC(headX)
- ELSIF nextDir = DirDown THEN INC(headY) END;
- Wrap(headX, GW);
- Wrap(headY, GH);
- ct := CellAt(headX, headY);
- IF (ct # CellNothing) AND (ct # CellFood) THEN
- SnakeInit;
- RETURN
- END;
- PutCell(px, py, dirCell);
- PutCell(headX, headY, dirCell);
- IF ct = CellFood THEN
- IF occupied = VAL(CARDINAL, GW * GH) THEN
- SnakeInit;
- RETURN
- END;
- NewFood;
- INC(inhibit);
- INC(occupied)
- END
- END SnakeStep;
- PROCEDURE HandleSym (sym: INTEGER; VAR quit: BOOLEAN);
- BEGIN
- IF sym = KeyEscape THEN quit := TRUE
- ELSIF sym = KeyQ THEN quit := TRUE
- ELSIF sym = KeyR THEN SnakeInit
- ELSIF sym = KeyRight THEN SnakeRedir(DirRight)
- ELSIF sym = KeyUp THEN SnakeRedir(DirUp)
- ELSIF sym = KeyLeft THEN SnakeRedir(DirLeft)
- ELSIF sym = KeyDown THEN SnakeRedir(DirDown) END
- END HandleSym;
- PROCEDURE DrawBoard;
- VAR gx, gy: INTEGER; ct: CARDINAL; r: SDLFRect;
- BEGIN
- SDL_SetRenderDrawColor(ren, VAL(CARDINAL8, 0), VAL(CARDINAL8, 0),
- VAL(CARDINAL8, 0), VAL(CARDINAL8, 255));
- SDL_RenderClear(ren);
- FOR gy := 0 TO GH - 1 DO
- FOR gx := 0 TO GW - 1 DO
- ct := CellAt(gx, gy);
- IF ct # CellNothing THEN
- r := MakeFRect(VAL(SHORTREAL, VAL(REAL, gx * BlockPx)),
- VAL(SHORTREAL, VAL(REAL, gy * BlockPx)),
- VAL(SHORTREAL, VAL(REAL, BlockPx)),
- VAL(SHORTREAL, VAL(REAL, BlockPx)));
- IF ct = CellFood THEN
- SDL_SetRenderDrawColor(ren, VAL(CARDINAL8, 80),
- VAL(CARDINAL8, 80),
- VAL(CARDINAL8, 255),
- VAL(CARDINAL8, 255))
- ELSE
- SDL_SetRenderDrawColor(ren, VAL(CARDINAL8, 0),
- VAL(CARDINAL8, 128),
- VAL(CARDINAL8, 0),
- VAL(CARDINAL8, 255))
- END;
- SDL_RenderFillRect(ren, r)
- END
- END
- END;
- r := MakeFRect(VAL(SHORTREAL, VAL(REAL, headX * BlockPx)),
- VAL(SHORTREAL, VAL(REAL, headY * BlockPx)),
- VAL(SHORTREAL, VAL(REAL, BlockPx)),
- VAL(SHORTREAL, VAL(REAL, BlockPx)));
- SDL_SetRenderDrawColor(ren, VAL(CARDINAL8, 255), VAL(CARDINAL8, 255),
- VAL(CARDINAL8, 0), VAL(CARDINAL8, 255));
- SDL_RenderFillRect(ren, r);
- SDL_RenderPresent(ren)
- END DrawBoard;
- VAR
- ev: M2Event;
- quit: BOOLEAN;
- start, now, lastStep: LONGCARD;
- i: INTEGER;
- BEGIN
- FOR i := 0 TO 7 DO
- IF i = 0 THEN pow2[0] := 1 ELSE pow2[i] := pow2[i-1] * 2 END
- END;
- srand(VAL(INTEGER, SDL_GetTicks()));
- IF NOT SDL_Init(SDLInitVideo + SDLInitEvents) THEN
- printf("showcase_snake: SDL_Init failed\n");
- HALT(1)
- END;
- IF NOT SDL_CreateWindowAndRenderer("m2SDL3 snake",
- WinW, WinH,
- VAL(LONGCARD, WindowResizable),
- win, ren) THEN
- printf("showcase_snake: CreateWindowAndRenderer failed\n");
- SDL_Quit;
- HALT(1)
- END;
- SDL_SetRenderLogicalPresentation(ren, WinW, WinH, LogicalLetterbox);
- SnakeInit;
- printf("showcase_snake: arrows steer, R restarts, Q quits\n");
- quit := FALSE;
- start := SDL_GetTicks();
- lastStep := start;
- WHILE NOT quit DO
- WHILE PollM2Event(ev) DO
- IF ev.kind = evQuit THEN quit := TRUE
- ELSIF (ev.kind = evKey) AND ev.pressed THEN
- HandleSym(ev.sym, quit)
- END
- END;
- now := SDL_GetTicks();
- WHILE now - lastStep >= VAL(LONGCARD, StepMs) DO
- SnakeStep;
- lastStep := lastStep + VAL(LONGCARD, StepMs)
- END;
- DrawBoard;
- IF now - start > VAL(LONGCARD, MaxMs) THEN quit := TRUE END;
- SDL_Delay(16)
- END;
- SDL_DestroyRenderer(ren);
- SDL_DestroyWindow(win);
- SDL_Quit;
- printf("showcase_snake: bye\n")
- END showcase_snake.
|