aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--meson.build1
-rw-r--r--src/libsok/sok-ctx.c2
-rw-r--r--src/libsok/sok.h10
-rw-r--r--src/text/action.c64
-rw-r--r--src/text/action.h24
-rw-r--r--src/text/main.c166
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;
}