From 75b505db33132fb84af2c02f39ea6cd5bda47e86 Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Sun, 17 Sep 2023 20:53:08 +1000 Subject: [PATCH] examples: simple game of life impl using SDL2 thru ffi & native java collections --- examples/gameoflife/SDL2.pork | 89 +++++++++++++++ examples/gameoflife/gameoflife.pork | 171 ++++++++++++++++++++++++++++ 2 files changed, 260 insertions(+) create mode 100644 examples/gameoflife/SDL2.pork create mode 100644 examples/gameoflife/gameoflife.pork diff --git a/examples/gameoflife/SDL2.pork b/examples/gameoflife/SDL2.pork new file mode 100644 index 0000000..42d5df0 --- /dev/null +++ b/examples/gameoflife/SDL2.pork @@ -0,0 +1,89 @@ +/* Just enough SDL2 to run game of life */ + +// SDL.h + +export let SDL_INIT_VIDEO = 32 + +export func SDL_Init(flags) + native ffi "SDL2:SDL_Init:int:unsigned int" + +export func SDL_Quit() + native ffi "SDL2:SDL_Quit:void" + +// SDL_video.h + +export let SDL_WINDOW_ALLOW_HIGHDPI = 8192 + +export let SDL_WINDOWPOS_UNDEFINED_MASK = 536805376 +export func SDL_WINDOWPOS_UNDEFINED_DISPLAY(x) { SDL_WINDOWPOS_UNDEFINED_MASK | x } +export let SDL_WINDOWPOS_UNDEFINED = SDL_WINDOWPOS_UNDEFINED_DISPLAY(0) + +export let SDL_WINDOWPOS_CENTERED_MASK = 805240832 +export func SDL_WINDOWPOS_CENTERED_DISPLAY(x) { SDL_WINDOWPOS_CENTERED_MASK | x } +export let SDL_WINDOWPOS_CENTERED = SDL_WINDOWPOS_CENTERED_DISPLAY(0) + +export func SDL_CreateWindow(title, x, y, w, h, flags) + native ffi "SDL2:SDL_CreateWindow:void*:char*,int,int,int,int,unsigned int" + +export func SDL_DestroyWindow(window) + native ffi "SDL2:SDL_DestroyWindow:void:void*" + +// SDL_event.h + +export func SDL_PumpEvents() + native ffi "SDL2:SDL_PumpEvents:void" + +export func SDL_WaitEvent(event) + native ffi "SDL2:SDL_WaitEvent:int:void*" + + +// SDL_keyboard.h + +export let KMOD_NONE = 0 +export let KMOD_LSHIFT = 1 +export let KMOD_RSHIFT = 2 +export let KMOD_LCTRL = 64 +export let KMOD_RCTRL = 128 +export let KMOD_LALT = 256 +export let KMOD_RALT = 512 +export let KMOD_LGUI = 1024 +export let KMOD_RGUI = 2048 +export let KMOD_NUM = 4096 +export let KMOD_MODE = 16384 +export let KMOD_CAPS = 8192 +export let KMOD_SCROLL = 32768 + +export func SDL_GetModState() + native ffi "SDL2:SDL_GetModState:int" + +// SDL_renderer.h + +export let SDL_RENDERER_PRESENTVSYNC = 4 + +export func SDL_CreateRenderer(window, index, flags) + native ffi "SDL2:SDL_CreateRenderer:void*:void*,int,unsigned int" + +export func SDL_DestroyRenderer(renderer) + native ffi "SDL2:SDL_DestroyRenderer:void:void*" + +export func SDL_RenderSetLogicalSize(renderer, w, h) + native ffi "SDL2:SDL_RenderSetLogicalSize:int:void*,int,int" + +export func SDL_RenderPresent(renderer) + native ffi "SDL2:SDL_RenderPresent:void:void*" + +export func SDL_SetRenderDrawColor(renderer, r, g, b, a) + native ffi "SDL2:SDL_SetRenderDrawColor:int:void*,unsigned int,unsigned int,unsigned int,unsigned int" + +export func SDL_RenderClear(renderer) + native ffi "SDL2:SDL_RenderClear:int:void*" + +export func SDL_RenderDrawLine(renderer, x1, y1, x2, y2) + native ffi "SDL2:SDL_RenderDrawLine:int:void*,int,int,int,int" + +// SDL_hints.h + +export let SDL_HINT_RENDER_LOGICAL_SIZE_MODE = "SDL_RENDER_LOGICAL_SIZE_MODE" + +export func SDL_SetHint(name, value) + native ffi "SDL2:SDL_SetHint:int:char*,char*" diff --git a/examples/gameoflife/gameoflife.pork b/examples/gameoflife/gameoflife.pork new file mode 100644 index 0000000..c465a8a --- /dev/null +++ b/examples/gameoflife/gameoflife.pork @@ -0,0 +1,171 @@ +import local SDL2 +import java java.util.List +import java java.util.ArrayList +import java java.util.Collections + +let cellSize = 16 +let gridWidth = 64 +let gridHeight = 40 + +func clearScreen(renderer) { + SDL_SetRenderDrawColor(renderer, 240, 181, 84, 255) + SDL_RenderClear(renderer) +} + +func drawGrid(renderer) { + let w = cellSize * gridWidth + let h = cellSize * gridHeight + + SDL_SetRenderDrawColor(renderer, 243, 232, 115, 255) + var i = 1 + while i < gridWidth { + let x = i * cellSize + SDL_RenderDrawLine(renderer, x, 0, x, h) + i++ + } + i = 1 + while i < gridHeight { + let y = i * cellSize + SDL_RenderDrawLine(renderer, 0, y, w, y) + i++ + } +} + +func drawCells(renderer, cells, swap) { + SDL_SetRenderDrawColor(renderer, 89, 145, 57, 255) + + var i = 0 + var iy = 0 + while iy < gridHeight { + var ix = 0 + while ix < gridWidth { + let mask = if swap { 2 } else { 1 } + if (java_util_ArrayList_get(cells, i) & mask) == mask { + let x = ix * cellSize + let y = iy * cellSize + SDL_RenderDrawLine(renderer, x, y, x + cellSize, y) + SDL_RenderDrawLine(renderer, x, y + cellSize, x + cellSize, y + cellSize) + SDL_RenderDrawLine(renderer, x, y, x, y + cellSize) + SDL_RenderDrawLine(renderer, x + cellSize, y, x + cellSize, y + cellSize) + SDL_RenderDrawLine(renderer, x, y, x + cellSize, y + cellSize) + SDL_RenderDrawLine(renderer, x + cellSize, y, x, y + cellSize) + } + i++ + ix++ + } + iy++ + } +} + +func createCellGrid() { + let numCells = gridWidth * gridHeight + let init = java_util_Collections_nCopies(numCells, 0) + java_util_ArrayList_new_collection(init) +} + +func getCell(cells, swap, x, y) { + if (x >= 0) and (y >= 0) and (x < gridWidth) and (y < gridHeight) { + let mask = if swap { 2 } else { 1 } + (java_util_ArrayList_get(cells, x + y * gridWidth) & mask) != 0 + } else { + false + } +} + +func setCell(cells, swap, x, y, state) { + if (x >= 0) and (y >= 0) and (x < gridWidth) and (y < gridHeight) { + let mask = if swap { 2 } else { 1 } + let idx = x + y * gridWidth + let value = java_util_ArrayList_get(cells, idx) + if state { java_util_ArrayList_set(cells, idx, value | mask) } + else { java_util_ArrayList_set(cells, idx, value & (~mask)) } + } +} + +func countNeighbours(cells, swap, x, y) { + var count = 0 + if getCell(cells, swap, x, y - 1) { count++ } + if getCell(cells, swap, x + 1, y - 1) { count++ } + if getCell(cells, swap, x + 1, y) { count++ } + if getCell(cells, swap, x + 1, y + 1) { count++ } + if getCell(cells, swap, x, y + 1) { count++ } + if getCell(cells, swap, x - 1, y + 1) { count++ } + if getCell(cells, swap, x - 1, y) { count++ } + if getCell(cells, swap, x - 1, y - 1) { count++ } + count +} + +func gameOfLife(cells, swap) { + var iy = 0 + while iy < gridHeight { + var ix = 0 + while ix < gridWidth { + let neighbours = countNeighbours(cells, not swap, ix, iy) + let live = if getCell(cells, not swap, ix, iy) { + (neighbours == 2) or (neighbours == 3) + } else { + neighbours == 3 + } + setCell(cells, swap, ix, iy, live) + ix++ + } + iy++ + } +} + +func createGosperGun(cells, swap, x, y) { + for i in [ + [ 1, 5], [ 2, 5], [ 1, 6], [ 2, 6], [11, 5], [11, 6], [11, 7], [12, 4], + [12, 8], [13, 3], [14, 3], [13, 9], [14, 9], [15, 6], [16, 4], [16, 8], + [17, 5], [17, 6], [17, 7], [18, 6], [18, 6], [21, 3], [22, 3], [21, 4], + [22, 4], [21, 5], [22, 5], [23, 2], [23, 6], [25, 1], [25, 2], [25, 6], + [25, 7], [35, 3], [36, 3], [35, 4], [36, 4] + ] { + setCell(cells, false, + x + java_util_List_get(i, 0), + y + java_util_List_get(i, 1), + true) + } +} + +export func main() { + SDL_Init(SDL_INIT_VIDEO) + + let winWidth = cellSize * gridWidth + let winHeight = cellSize * gridHeight + let window = SDL_CreateWindow( + "Game of Swine", + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + winWidth, winHeight, + SDL_WINDOW_ALLOW_HIGHDPI) + SDL_SetHint(SDL_HINT_RENDER_LOGICAL_SIZE_MODE, "letterbox") + let rend = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC) + SDL_RenderSetLogicalSize(rend, winWidth, winHeight) + + let cells = createCellGrid() + createGosperGun(cells, false, 1, 1) + + var page = false + var running = true + while running { + SDL_WaitEvent(None) + let modifiers = SDL_GetModState() + if (modifiers & KMOD_RSHIFT) == KMOD_RSHIFT { running = false } + + clearScreen(rend) + drawGrid(rend) + drawCells(rend, cells, page) + + if (modifiers & KMOD_LSHIFT) == KMOD_LSHIFT { + page = not page + gameOfLife(cells, page) + } + + SDL_RenderPresent(rend) + SDL_PumpEvents() + } + + SDL_DestroyRenderer(rend) + SDL_DestroyWindow(window) + SDL_Quit() +}