#include // bool #include // atoi() #include // EXIT_{FAILURE,SUCCESS} #include #include "../libsok/sok.h" #include "levels.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': static char print_buf[(SOK_LEVEL_MAX_WIDTH + 1) * SOK_LEVEL_MAX_HEIGHT + 1]; static bool print_on_size( const sok_ctx_t * const ctx, const sok_pos_t size, void * const data ) { UNUSED(ctx); UNUSED(data); // fprintf(stderr, "size: x = %u, y = %u\n", size.x, size.y); memset(print_buf, ' ', sizeof(print_buf)); print_buf[(size.x + 1) * size.y + 1] = '\0'; for (size_t i = 0; i < size.y; i++) { print_buf[(i + 1) * (size.x + 1) - 1] = '\n'; } return true; } static bool print_on_home( const sok_ctx_t * const ctx, const sok_pos_t pos, const bool has_goal, void * const data ) { UNUSED(data); print_buf[pos.y * (ctx->level.size.x + 1) + pos.x] = has_goal ? '+' : '@'; return true; } static bool print_on_wall( const sok_ctx_t * const ctx, const sok_pos_t pos, void * const data ) { // fprintf(stderr, "wall: x = %u, y = %u\n", pos.x, pos.y); UNUSED(data); print_buf[pos.y * (ctx->level.size.x + 1) + pos.x] = '#'; return true; } static bool print_on_goal( const sok_ctx_t * const ctx, const sok_pos_t pos, const bool has_player, const bool has_box, void * const data ) { UNUSED(data); const char c = has_player ? '+' : (has_box ? '*' : '.'); print_buf[pos.y * (ctx->level.size.x + 1) + pos.x] = c; return true; } static bool print_on_box( const sok_ctx_t * const ctx, const sok_pos_t pos, const bool has_goal, void * const data ) { UNUSED(data); print_buf[pos.y * (ctx->level.size.x + 1) + pos.x] = has_goal ? '*' : '$'; return true; } 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, }; static void print_level( const sok_ctx_t * const ctx ) { // fill print buffer if (!sok_ctx_walk(ctx, &PRINT_CBS, NULL)) { fprintf(stderr, "Couldn't print level\n"); exit(EXIT_FAILURE); } // print level printf("%s\n", print_buf); } static void solve_on_error( const char * const err ) { fprintf(stderr, "Error solving level: %s\n", err); exit(EXIT_FAILURE); } static void print_moves( const sok_ctx_t * const ctx, const size_t skip_moves ) { printf("Moves (%zu): ", 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); } } printf("\n"); } int main(int argc, char *argv[]) { 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)) { fprintf(stderr, "Couldn't load level %zu\n", level_num); return EXIT_FAILURE; } 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!)" : ""); if (!fgets(buf, sizeof(buf), stdin)) { done = true; break; } switch (buf[0]) { case EOF: case 'q': 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"); } break; case 'u': if (!sok_ctx_undo(&ctx)) { fprintf(stderr, "W: undo failed\n"); } break; case 'n': 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)) { fprintf(stderr, "Couldn't load level %zu\n", level_num); return EXIT_FAILURE; } } else { fprintf(stderr, "W: cannot advance to next level\n"); } break; CASE_DIGIT { level_num = atoi(buf) % levels_get_num_levels(); 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; } } break; case 's': { // 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 print_moves(&ctx, old_num_moves); } else { fprintf(stderr, "W: Couldn't solve level\n"); } } break; default: // ignore break; } } return 0; }