Skip to content

Instantly share code, notes, and snippets.

@edubart
Created April 25, 2019 21:08
Show Gist options
  • Select an option

  • Save edubart/d01c5c63e6733792670fa770ecf74740 to your computer and use it in GitHub Desktop.

Select an option

Save edubart/d01c5c63e6733792670fa770ecf74740 to your computer and use it in GitHub Desktop.
-- import SDL headers
!!cdefine 'SDL_DISABLE_IMMINTRIN_H'
!!cinclude '<SDL2/SDL.h>'
!!linklib 'SDL2'
-- import SDL structures
local SDL_Event = @record {
type: uint32,
padding: array<byte, 56>
}
local SDL_Keysym = @record {
scancode: cint,
sym: int32,
mod: uint16,
unused: uint32
}
local SDL_KeyboardEvent = @record {
type: uint32,
timestamp: uint32,
windowID: uint32,
state: uint8,
repeated: uint8,
padding: uint16,
keysym: SDL_Keysym
}
local SDL_Rect = @record {
x: cint, y: cint,
w: cint, h: cint,
};
-- import SDL pointers
local SDL_Window = @pointer<record{}>
local SDL_Renderer = @pointer<record{}>
-- import SDL constants
local SDL_INIT_VIDEO: uint32 !cimport
local SDL_WINDOWPOS_UNDEFINED: cint !cimport
local SDL_WINDOW_OPENGL: uint32 !cimport
local SDL_QUIT: uint32 !cimport
local SDL_KEYDOWN: uint32 !cimport
local SDLK_UP: int32 !cimport
local SDLK_DOWN: int32 !cimport
local SDLK_LEFT: int32 !cimport
local SDLK_RIGHT: int32 !cimport
local SDL_RENDERER_ACCELERATED: uint32 !cimport
local SDL_RENDERER_PRESENTVSYNC: uint32 !cimport
-- import SDL functions
local function SDL_Init(flags: uint32): int32 !cimport end
local function SDL_CreateWindow(title: cstring, x: cint, y: cint, w: cint, h: cint, flags: uint32): SDL_Window !cimport end
local function SDL_Quit() !cimport end
local function SDL_DestroyWindow(window: SDL_Window) !cimport end
local function SDL_Delay(ms: uint32) !cimport end
local function SDL_PollEvent(event: pointer<SDL_Event>): int32 !cimport end
local function SDL_WaitEvent(event: pointer<SDL_Event>): int32 !cimport end
local function SDL_CreateRenderer(window: SDL_Window, index: cint, flags: uint32): SDL_Renderer !cimport end
local function SDL_DestroyRenderer(renderer: SDL_Renderer) !cimport end
local function SDL_RenderPresent(renderer: SDL_Renderer) !cimport end
local function SDL_RenderClear(renderer: SDL_Renderer) !cimport end
local function SDL_SetRenderDrawColor(renderer: SDL_Renderer, r: uint8, g: uint8, b: uint8, a: uint8): cint !cimport end
local function SDL_RenderFillRect(renderer: SDL_Renderer, rect: pointer<SDL_Rect>): cint !cimport end
local function rand(): int32 !cimport('rand') end
-- game types
local Point2D = @record{x: integer, y: integer}
local Direction = @enum<uint8>{NONE=0_u8, UP, DOWN, RIGHT, LEFT}
local Color = @record{r: uint8, g: uint8, b: uint8}
-- game constants
local const TILE_SIZE = 16
local const SCREEN_SIZE = 512
local const MAP_SIZE = 32 --SCREEN_SIZE / TILE_SIZE
local COLOR_PINK = Color{r=255, g=0, b=255}
local COLOR_GREEN = Color{r=0, g=255, b=0}
local COLOR_BLACK = Color{r=0, g=0, b=0}
-- game state variables
local renderer
local movedir = Direction.UP
local quit = false
local movecounter = 0
local headpos, tailpos, applepos
local TileMap = @array<array<Direction, MAP_SIZE>, MAP_SIZE>
local tiles: TileMap
-- poll input events
local function poll_events()
local event: SDL_Event
while SDL_PollEvent(&event) do
switch event.type
case SDL_QUIT then
quit = true
case SDL_KEYDOWN then
local kevent = @pointer<SDL_KeyboardEvent>(&event)
switch kevent.keysym.sym
case SDLK_UP then
if movedir ~= Direction.DOWN then
movedir = Direction.UP
end
case SDLK_DOWN then
if movedir ~= Direction.UP then
movedir = Direction.DOWN
end
case SDLK_RIGHT then
if movedir ~= Direction.LEFT then
movedir = Direction.RIGHT
end
case SDLK_LEFT then
if movedir ~= Direction.RIGHT then
movedir = Direction.LEFT
end
end
end
end
end
local function move_point(pos: Point2D, dir: Direction)
switch dir
case Direction.UP then
pos.y = pos.y - 1
case Direction.DOWN then
pos.y = pos.y + 1
case Direction.RIGHT then
pos.x = pos.x + 1
case Direction.LEFT then
pos.x = pos.x - 1
end
return pos
end
local function respawn_apple()
-- respawn until there is no collision with its body
repeat
applepos = Point2D{
x = rand() % MAP_SIZE,
y = rand() % MAP_SIZE
}
until tiles[applepos.x][applepos.y] == Direction.NONE
end
local function init_game()
for i=0,MAP_SIZE-1 do
for j=0,MAP_SIZE-1 do
tiles[i][j] = Direction.NONE
end
end
headpos = Point2D{x=MAP_SIZE//2, y=MAP_SIZE//2}
tailpos = Point2D{x=headpos.x, y=headpos.y+1}
tiles[headpos.x][headpos.y] = Direction.UP
tiles[tailpos.x][tailpos.y] = Direction.UP
movedir = Direction.UP
respawn_apple()
end
local function poll_game()
movecounter = movecounter + 1
if movecounter < 8 then return end
movecounter = 0
tiles[headpos.x][headpos.y] = movedir
headpos = move_point(headpos, movedir)
-- check collision with map boundaries
if headpos.x >= MAP_SIZE or headpos.y >= MAP_SIZE or
headpos.x < 0 or headpos.y < 0 then
init_game() -- game over
return
end
-- check collisions with its body
if tiles[headpos.x][headpos.y] ~= Direction.NONE then
init_game()
return
end
tiles[headpos.x][headpos.y] = movedir
-- check collision with apple
if headpos.x == applepos.x and headpos.y == applepos.y then
respawn_apple()
else
-- eat tail
local taildir = tiles[tailpos.x][tailpos.y]
tiles[tailpos.x][tailpos.y] = Direction.NONE
tailpos = move_point(tailpos, taildir)
end
end
local function draw_background(color: Color)
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, 255)
SDL_RenderClear(renderer)
end
local function draw_tile(pos: Point2D, color: Color)
local rect = SDL_Rect{
x = @int32(pos.x * TILE_SIZE),
y = @int32(pos.y * TILE_SIZE),
w = @int32(TILE_SIZE),
h = @int32(TILE_SIZE)
}
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, 255)
SDL_RenderFillRect(renderer, &rect)
end
local function draw_snake()
-- draw apple
draw_tile(applepos, COLOR_PINK)
-- draw the snake body
for x=0,MAP_SIZE-1 do
for y=0,MAP_SIZE-1 do
if tiles[x][y] ~= Direction.NONE then
-- snake is present at this tile
draw_tile(Point2D{x=x, y=y}, COLOR_GREEN)
end
end
end
end
local function draw()
draw_background(COLOR_BLACK)
draw_snake()
SDL_RenderPresent(renderer)
end
local function go()
SDL_Init(SDL_INIT_VIDEO)
-- create window
local window = SDL_CreateWindow("An SDL2 Window",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
@int32(SCREEN_SIZE), @int32(SCREEN_SIZE), SDL_WINDOW_OPENGL)
assert(window, "Could not create window")
-- create renderer
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC)
assert(renderer, "Could not create renderer")
init_game()
-- draw loop
repeat
poll_events()
poll_game()
draw()
until quit
-- cleanup and finish
SDL_DestroyWindow(window)
SDL_DestroyRenderer(renderer)
SDL_Quit()
end
go()
--TODO: rinitialize records to zero
--TODO: const for complex types (Color)
--TODO: unions
--TODO: syntax sugar for pointer, syntax sugar for array
--TODO: function type overload
--TODO: multiple returns
--TODO: var&
--TODO: integer constants should not warn if the value is in range
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment