aboutsummaryrefslogtreecommitdiff
path: root/src/sdl/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sdl/main.c')
-rw-r--r--src/sdl/main.c301
1 files changed, 300 insertions, 1 deletions
diff --git a/src/sdl/main.c b/src/sdl/main.c
index 0fb4389..831d3f7 100644
--- a/src/sdl/main.c
+++ b/src/sdl/main.c
@@ -1,3 +1,302 @@
+#include <stdbool.h> // bool
+#include <stdlib.h> // EXIT_{FAILURE,SUCCESS}
+#include <string.h> // strerror
+#include <errno.h> // errno
+#include <SDL.h>
+#include <SDL_image.h>
+#include "../text/levels.h"
+#include "../libsok/sok.h"
+#include "action.h"
+
+#define warn(...) do { \
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__); \
+} while (0)
+
+#define die(...) do { \
+ SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__); \
+ exit(EXIT_FAILURE); \
+} while (0)
+
+static void
+solve_on_error(
+ const char * const err
+) {
+ die("Error solving level: %s", err);
+}
+
+static void
+draw_moves(
+ const sok_ctx_t * const ctx,
+ const size_t skip_moves
+) {
+ printf("Solution (%zu moves): ", ctx->num_moves - skip_moves);
+ for (size_t i = skip_moves; i < ctx->num_moves; i++) {
+ if (ctx->moves[i].dir >= SOK_DIR_LAST) {
+ die("invalid move: %u", ctx->moves[i].dir);
+ }
+
+ fputs(SOK_DIR_TO_STR(ctx->moves[i].dir), stdout);
+ }
+ printf("\n");
+}
+
+static const SDL_Color
+RED = { .r = 0x00, .g = 0xFF, .b = 0x00, .a = 0xFF },
+BLACK = { .r = 0x00, .g = 0x00, .b = 0x00, .a = 0xFF };
+
+static SDL_Color
+get_bg(
+ const sok_ctx_t * const ctx
+) {
+ return (sok_ctx_is_done(ctx)) ? RED : BLACK;
+}
+
+bool draw_on_wall(
+ const sok_ctx_t * const ctx,
+ const sok_pos_t pos,
+ void * const data
+) {
+ SDL_Renderer *renderer = data;
+ SDL_Rect rect = { 32 * pos.x, 32 * pos.y, 32, 32 };
+
+ SDL_SetRenderDrawColor(renderer, 0x66, 0x66, 0x66, 0xFF);
+
+ if (SDL_RenderFillRect(renderer, &rect)) {
+ die("SDL_RenderFillRect(): %s", SDL_GetError());
+ }
+
+ return true;
+}
+
+bool draw_on_goal(
+ const sok_ctx_t * const ctx,
+ const sok_pos_t pos,
+ const bool has_player,
+ const bool has_box,
+ void * const data
+) {
+ SDL_Renderer *renderer = data;
+ SDL_Rect rect = { 32 * pos.x, 32 * pos.y, 32, 32 };
+
+ SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF);
+
+ if (SDL_RenderFillRect(renderer, &rect)) {
+ die("SDL_RenderFillRect(): %s", SDL_GetError());
+ }
+
+ return true;
+}
+
+bool draw_on_home(
+ const sok_ctx_t * const ctx,
+ const sok_pos_t pos,
+ const bool has_goal,
+ void * const data
+) {
+ SDL_Renderer *renderer = data;
+ SDL_Rect rect = { 32 * pos.x, 32 * pos.y, 32, 32 };
+
+ SDL_SetRenderDrawColor(renderer, 0xFF, has_goal ? 0xFF : 0x00, 0x00, 0xFF);
+
+ if (SDL_RenderFillRect(renderer, &rect)) {
+ die("SDL_RenderFillRect(): %s", SDL_GetError());
+ }
+
+ return true;
+}
+
+bool draw_on_box(
+ const sok_ctx_t * const ctx,
+ const sok_pos_t pos,
+ const bool has_goal,
+ void * const data
+) {
+ SDL_Renderer *renderer = data;
+ SDL_Rect rect = { 32 * pos.x, 32 * pos.y, 32, 32 };
+
+ SDL_SetRenderDrawColor(renderer, 0x00, has_goal ? 0xFF : 0x00, 0xFF, 0xFF);
+
+ if (SDL_RenderFillRect(renderer, &rect)) {
+ die("SDL_RenderFillRect(): %s", SDL_GetError());
+ }
+
+ return true;
+}
+
+static sok_ctx_walk_cbs_t
+DRAW_CBS = {
+ .on_wall = draw_on_wall,
+ .on_goal = draw_on_goal,
+ .on_home = draw_on_home,
+ .on_box = draw_on_box,
+};
+/*
+ * typedef struct {
+ * sok_ctx_walk_pos_cb_t on_size,
+ * on_wall;
+ * sok_ctx_walk_tile_cb_t on_home,
+ * on_box;
+ * sok_ctx_walk_goal_cb_t on_goal;
+ * sok_ctx_walk_move_cb_t on_move;
+ * } sok_ctx_walk_cbs_t;
+ *
+ * _Bool sok_ctx_walk(
+ * const sok_ctx_t * const,
+ * const sok_ctx_walk_cbs_t * const,
+ * void * const
+ * );
+ */
+
+static void
+draw(
+ SDL_Renderer * const renderer,
+ const sok_ctx_t * const ctx,
+ const size_t level_num,
+ const level_t * const level
+) {
+ // clear
+ const SDL_Color c = get_bg(ctx);
+ SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, c.a);
+ SDL_RenderClear(renderer);
+
+ sok_ctx_walk(ctx, &DRAW_CBS, renderer);
+
+ // flip
+ SDL_RenderPresent(renderer);
+}
+
int main(int argc, char *argv[]) {
- return 0;
+ size_t warp_buf = 0;
+ size_t level_num = (argc > 1) ? atoi(argv[1]) : 0;
+ const level_t *level = levels_get_level(level_num);
+
+ // init context
+ sok_ctx_t ctx;
+ sok_ctx_init(&ctx, NULL);
+
+ if (!sok_ctx_set_level(&ctx, level->data)) {
+ die("Couldn't load level %zu", level_num);
+ }
+
+ // init sdl
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
+ die("SDL_Init(): %s", SDL_GetError());
+ }
+
+ if (atexit(SDL_Quit)) {
+ die("atexit(): %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ // create window and renderer
+ SDL_Window *win;
+ SDL_Renderer *renderer;
+ if (SDL_CreateWindowAndRenderer(800, 600, SDL_WINDOW_RESIZABLE, &win, &renderer)) {
+ die("SDL_CreateWindowAndRenderer(): %s", SDL_GetError());
+ }
+
+ bool done = false;
+ SDL_Event ev;
+ while (!done) {
+ while (SDL_PollEvent(&ev)) {
+ switch (ev.type) {
+ case SDL_QUIT:
+ done = true;
+ break;
+ case SDL_KEYUP:
+ {
+ const action_t action = get_action(ev.key.keysym.sym);
+
+ switch (action.type) {
+ case ACTION_QUIT:
+ done = true;
+ break;
+ case ACTION_MOVE:
+ if (!sok_ctx_move(&ctx, (sok_dir_t) action.data)) {
+ warn("move %s failed", SOK_DIR_TO_STR((sok_dir_t) action.data));
+ }
+
+ break;
+ case ACTION_UNDO:
+ if (!sok_ctx_undo(&ctx)) {
+ warn("undo failed");
+ }
+
+ break;
+ case ACTION_NEXT:
+ if (sok_ctx_is_done(&ctx)) {
+ // advance level
+ level_num++;
+ level = levels_get_level(level_num);
+
+ // load next level
+ if (!sok_ctx_set_level(&ctx, level->data)) {
+ die("Couldn't load level %zu", level_num);
+ }
+ } else {
+ warn("cannot advance to next level");
+ }
+
+ break;
+ case ACTION_RESET:
+ // reset level
+ if (!sok_ctx_set_level(&ctx, level->data)) {
+ die("Couldn't load level %zu", level_num);
+ }
+
+ break;
+ case ACTION_WARP:
+ level_num = warp_buf;
+ level = levels_get_level(level_num);
+
+ // load level
+ if (!sok_ctx_set_level(&ctx, level->data)) {
+ die("Couldn't load level %zu", level_num);
+ }
+
+ warp_buf = 0;
+
+ break;
+ case ACTION_WARP_BUF_PUSH:
+ warp_buf = 10 * warp_buf + action.data;
+
+ break;
+ case ACTION_WARP_BUF_POP:
+ warp_buf /= 10;
+
+ break;
+ case ACTION_SOLVE:
+ {
+ // get current number of moves
+ const size_t old_num_moves = ctx.num_moves;
+
+ if (sok_solve(&ctx, solve_on_error)) {
+ // found solution, print it
+ draw_moves(&ctx, old_num_moves);
+ } else {
+ warn("Couldn't solve level");
+ }
+ }
+ default:
+ // ignore
+ break;
+ }
+ }
+
+ break;
+ default:
+ // ignore
+ break;
+ }
+ }
+
+ draw(renderer, &ctx, level_num, level);
+ }
+
+ // fini renderer, window
+ SDL_DestroyRenderer(renderer);
+ SDL_DestroyWindow(win);
+
+ // return success
+ return EXIT_SUCCESS;
}