#include // bool #include // atoi() #include // EXIT_{FAILURE,SUCCESS} #include #include "../libsok/sok.h" #include "levels.h" #include "action.h" #define UNUSED(a) ((void) (a)) #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 draw_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 draw_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 draw_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 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 ) { 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 draw_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 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 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, &DRAW_CBS, NULL)) { die("Couldn't print level"); } // 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 ) { die("Error solving level: %s", err); } static void print_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"); } 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)) { die("Couldn't load level %zu", level_num); } bool done = false; while (!done) { // draw screen draw(&ctx, level_num, level); // read input, check for error const action_t action = get_action(); 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_WARP: level_num = action.data; level = levels_get_level(level_num); // load level if (!sok_ctx_set_level(&ctx, level->data)) { die("Couldn't load level %zu", level_num); } 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 print_moves(&ctx, old_num_moves); } else { warn("Couldn't solve level"); } } break; default: // ignore break; } } // return success return EXIT_SUCCESS; }