diff options
-rw-r--r-- | meson.build | 1 | ||||
-rw-r--r-- | src/libsok/sok-ctx.c | 2 | ||||
-rw-r--r-- | src/libsok/sok.h | 10 | ||||
-rw-r--r-- | src/text/action.c | 64 | ||||
-rw-r--r-- | src/text/action.h | 24 | ||||
-rw-r--r-- | src/text/main.c | 166 |
6 files changed, 165 insertions, 102 deletions
diff --git a/meson.build b/meson.build index 0bb17be..e98133c 100644 --- a/meson.build +++ b/meson.build @@ -12,6 +12,7 @@ sources = [ executable('sok-text', sources + [ 'src/text/main.c', 'src/text/levels.c', + 'src/text/action.c', ], dependencies: []) # text interface diff --git a/src/libsok/sok-ctx.c b/src/libsok/sok-ctx.c index 460c7ad..d514fe3 100644 --- a/src/libsok/sok-ctx.c +++ b/src/libsok/sok-ctx.c @@ -333,7 +333,7 @@ sok_ctx_move_box( bool sok_ctx_is_done( - sok_ctx_t * const ctx + const sok_ctx_t * const ctx ) { return ctx->num_goals_left == 0; } diff --git a/src/libsok/sok.h b/src/libsok/sok.h index 3e64afd..a633d12 100644 --- a/src/libsok/sok.h +++ b/src/libsok/sok.h @@ -24,6 +24,14 @@ typedef enum { SOK_DIR_LAST, } sok_dir_t; +#define SOK_DIR_TO_STR(dir) ( \ + ((dir) == SOK_DIR_RIGHT) ? "r" : \ + (((dir) == SOK_DIR_UP) ? "u" : \ + ((((dir) == SOK_DIR_LEFT) ? "l" : \ + (((((dir) == SOK_DIR_DOWN) ? "d" : \ + "X" \ +))))))) + typedef struct { sok_pos_t pos; sok_dir_t dir; @@ -128,7 +136,7 @@ void sok_ctx_init(sok_ctx_t * const ctx, void *user_data); _Bool sok_ctx_set_level(sok_ctx_t * const ctx, const char * const level); -_Bool sok_ctx_is_done(sok_ctx_t * const); +_Bool sok_ctx_is_done(const sok_ctx_t * const); _Bool sok_ctx_move(sok_ctx_t * const, const sok_dir_t); _Bool sok_ctx_undo(sok_ctx_t * const); diff --git a/src/text/action.c b/src/text/action.c new file mode 100644 index 0000000..3d63bce --- /dev/null +++ b/src/text/action.c @@ -0,0 +1,64 @@ +#include <stdio.h> // fgets() +#include <stdlib.h> // atoi() +#include "../libsok/sok.h" +#include "action.h" + +#define CASE_DIGIT \ + case '0': \ + case '1': \ + case '2': \ + case '3': \ + case '4': \ + case '5': \ + case '6': \ + case '7': \ + case '8': \ + case '9': + +static const sok_dir_t +key_to_dir(const char c) { + switch (c) { + case 'k': return SOK_DIR_UP; + case 'h': return SOK_DIR_LEFT; + case 'j': return SOK_DIR_DOWN; + case 'l': return SOK_DIR_RIGHT; + default: return SOK_DIR_LAST; + } +} + +action_t +get_action(void) { + char buf[128]; + + // read input, check for error + if (!fgets(buf, sizeof(buf), stdin)) { + return (action_t) { .type = ACTION_QUIT }; + } + + switch (buf[0]) { + case EOF: + case 'q': + return (action_t) { .type = ACTION_QUIT }; + case 'h': + case 'j': + case 'k': + case 'l': + return (action_t) { + .type = ACTION_MOVE, + .data = key_to_dir(buf[0]) + }; + case 'u': + return (action_t) { .type = ACTION_UNDO }; + case 'n': + return (action_t) { .type = ACTION_NEXT }; + CASE_DIGIT + return (action_t) { + .type = ACTION_WARP, + .data = atoi(buf), + }; + case 's': + return (action_t) { .type = ACTION_SOLVE }; + default: + return (action_t) { .type = ACTION_NONE }; + } +} diff --git a/src/text/action.h b/src/text/action.h new file mode 100644 index 0000000..d77b3ef --- /dev/null +++ b/src/text/action.h @@ -0,0 +1,24 @@ +#ifndef ACTION_H +#define ACTION_H + +#include <stdint.h> + +typedef enum { + ACTION_NONE, + ACTION_QUIT, + ACTION_MOVE, + ACTION_WARP, + ACTION_UNDO, + ACTION_NEXT, + ACTION_SOLVE, + ACTION_LAST, +} action_type_t; + +typedef struct { + action_type_t type; + uint64_t data; +} action_t; + +action_t get_action(void); + +#endif /* ACTION_H */ diff --git a/src/text/main.c b/src/text/main.c index eb8d9b2..80b6440 100644 --- a/src/text/main.c +++ b/src/text/main.c @@ -4,25 +4,23 @@ #include <stdio.h> #include "../libsok/sok.h" #include "levels.h" +#include "action.h" #define UNUSED(a) ((void) (a)) -#define CASE_DIGIT \ - case '0': \ - case '1': \ - case '2': \ - case '3': \ - case '4': \ - case '5': \ - case '6': \ - case '7': \ - case '8': \ - case '9': +#define warn(fmt, ...) do { \ + fprintf(stderr, "W: " fmt "\n", ##__VA_ARGS__); \ +} while (0) + +#define die(fmt, ...) do { \ + fprintf(stderr, "ERROR: " fmt "\n", ##__VA_ARGS__); \ + exit(EXIT_FAILURE); \ +} while (0) static char print_buf[(SOK_LEVEL_MAX_WIDTH + 1) * SOK_LEVEL_MAX_HEIGHT + 1]; static bool -print_on_size( +draw_on_size( const sok_ctx_t * const ctx, const sok_pos_t size, void * const data @@ -42,7 +40,7 @@ print_on_size( } static bool -print_on_home( +draw_on_home( const sok_ctx_t * const ctx, const sok_pos_t pos, const bool has_goal, @@ -54,7 +52,7 @@ print_on_home( } static bool -print_on_wall( +draw_on_wall( const sok_ctx_t * const ctx, const sok_pos_t pos, void * const data @@ -66,7 +64,7 @@ print_on_wall( } static bool -print_on_goal( +draw_on_goal( const sok_ctx_t * const ctx, const sok_pos_t pos, const bool has_player, @@ -80,7 +78,7 @@ print_on_goal( } static bool -print_on_box( +draw_on_box( const sok_ctx_t * const ctx, const sok_pos_t pos, const bool has_goal, @@ -92,34 +90,41 @@ print_on_box( } static sok_ctx_walk_cbs_t -PRINT_CBS = { - .on_size = print_on_size, - .on_home = print_on_home, - .on_wall = print_on_wall, - .on_goal = print_on_goal, - .on_box = print_on_box, +DRAW_CBS = { + .on_size = draw_on_size, + .on_home = draw_on_home, + .on_wall = draw_on_wall, + .on_goal = draw_on_goal, + .on_box = draw_on_box, }; static void -print_level( - const sok_ctx_t * const ctx +draw( + const sok_ctx_t * const ctx, + const size_t level_num, + const level_t * const level ) { // fill print buffer - if (!sok_ctx_walk(ctx, &PRINT_CBS, NULL)) { - fprintf(stderr, "Couldn't print level\n"); - exit(EXIT_FAILURE); + if (!sok_ctx_walk(ctx, &DRAW_CBS, NULL)) { + die("Couldn't print level"); } - // print level - printf("%s\n", print_buf); + // print title, level, and console + printf( + "%s: %s (#%zu)\n" // set name, level name, and level number + "%s\n" // level + "%zu%s> ", // console + level->pack, level->name, level_num, + print_buf, + ctx->num_moves, sok_ctx_is_done(ctx) ? " (won!)" : "" + ); } static void solve_on_error( const char * const err ) { - fprintf(stderr, "Error solving level: %s\n", err); - exit(EXIT_FAILURE); + die("Error solving level: %s", err); } static void @@ -127,25 +132,13 @@ print_moves( const sok_ctx_t * const ctx, const size_t skip_moves ) { - printf("Moves (%zu): ", ctx->num_moves - skip_moves); + printf("Solution (%zu moves): ", ctx->num_moves - skip_moves); for (size_t i = skip_moves; i < ctx->num_moves; i++) { - switch (ctx->moves[i].dir) { - case SOK_DIR_UP: - fputs("u", stdout); - break; - case SOK_DIR_DOWN: - fputs("d", stdout); - break; - case SOK_DIR_LEFT: - fputs("l", stdout); - break; - case SOK_DIR_RIGHT: - fputs("r", stdout); - break; - default: - fprintf(stderr, "Error: invalid move: %u", ctx->moves[i].dir); - exit(EXIT_FAILURE); + 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"); } @@ -159,58 +152,34 @@ int main(int argc, char *argv[]) { sok_ctx_init(&ctx, NULL); if (!sok_ctx_set_level(&ctx, level->data)) { - fprintf(stderr, "Couldn't load level %zu\n", level_num); - return EXIT_FAILURE; + die("Couldn't load level %zu", level_num); } - char buf[1024]; bool done = false; while (!done) { - printf("%s: %s (#%zu)\n", level->pack, level->name, level_num); - print_level(&ctx); - printf("%zu%s> ", ctx.num_moves, sok_ctx_is_done(&ctx) ? " (won!)" : ""); + // draw screen + draw(&ctx, level_num, level); - if (!fgets(buf, sizeof(buf), stdin)) { - done = true; - break; - } + // read input, check for error + const action_t action = get_action(); - switch (buf[0]) { - case EOF: - case 'q': + switch (action.type) { + case ACTION_QUIT: done = true; break; - case 'k': - if (!sok_ctx_move(&ctx, SOK_DIR_UP)) { - fprintf(stderr, "W: move up failed\n"); - } - - break; - case 'h': - if (!sok_ctx_move(&ctx, SOK_DIR_LEFT)) { - fprintf(stderr, "W: move left failed\n"); - } - - break; - case 'j': - if (!sok_ctx_move(&ctx, SOK_DIR_DOWN)) { - fprintf(stderr, "W: move down failed\n"); - } - - break; - case 'l': - if (!sok_ctx_move(&ctx, SOK_DIR_RIGHT)) { - fprintf(stderr, "W: move right failed\n"); + 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 'u': + case ACTION_UNDO: if (!sok_ctx_undo(&ctx)) { - fprintf(stderr, "W: undo failed\n"); + warn("undo failed"); } break; - case 'n': + case ACTION_NEXT: if (sok_ctx_is_done(&ctx)) { // advance level level_num++; @@ -218,28 +187,24 @@ int main(int argc, char *argv[]) { // load next level if (!sok_ctx_set_level(&ctx, level->data)) { - fprintf(stderr, "Couldn't load level %zu\n", level_num); - return EXIT_FAILURE; + die("Couldn't load level %zu", level_num); } } else { - fprintf(stderr, "W: cannot advance to next level\n"); + warn("cannot advance to next level"); } break; - CASE_DIGIT - { - level_num = atoi(buf) % levels_get_num_levels(); - level = levels_get_level(level_num); + case ACTION_WARP: + level_num = action.data; + level = levels_get_level(level_num); - // load level - if (!sok_ctx_set_level(&ctx, level->data)) { - fprintf(stderr, "Couldn't load level %zu\n", level_num); - return EXIT_FAILURE; - } + // load level + if (!sok_ctx_set_level(&ctx, level->data)) { + die("Couldn't load level %zu", level_num); } break; - case 's': + case ACTION_SOLVE: { // get current number of moves const size_t old_num_moves = ctx.num_moves; @@ -248,7 +213,7 @@ int main(int argc, char *argv[]) { // found solution, print it print_moves(&ctx, old_num_moves); } else { - fprintf(stderr, "W: Couldn't solve level\n"); + warn("Couldn't solve level"); } } @@ -259,5 +224,6 @@ int main(int argc, char *argv[]) { } } - return 0; + // return success + return EXIT_SUCCESS; } |