#include // bool #include // errno #include // strerror() #include #include "util.h" #include "solve.h" struct solve_t_ { SDL_mutex *mutex; // thread-private copy of context sok_ctx_t ctx; // event type for custom SDL SOLVE_* events Uint32 solve_event_type; // number of moves at call to solve() size_t old_num_moves; // number of times the on_step cb has been invoked size_t num_steps; // last time the SOLVE_EVENT_STEP event was pushed Uint32 last_step_event_ticks; // result of solve bool result; // cancel solve bool cancel; }; static void push_event( solve_t * const data, const solve_event_type_t type, void *user_data ) { // alloc/clear event SDL_Event ev; memset(&ev, 0, sizeof(SDL_Event)); // init event ev.type = data->solve_event_type; ev.user.code = (Sint32) type; ev.user.data1 = data; ev.user.data2 = user_data; // push event if (!SDL_PushEvent(&ev)) { die("SDL_PushEvent(): %s", SDL_GetError()); } } static bool solve_on_step( const sok_ctx_t * const ctx, void *user_data ) { solve_t *data = user_data; UNUSED(ctx); // lock mutex if (SDL_LockMutex(data->mutex)) { die("SDL_LockMutex(): %s", SDL_GetError()); } // increment number of steps data->num_steps++; // cache number of steps and cancel state const bool cancel = data->cancel; // unlock mutex if (SDL_UnlockMutex(data->mutex)) { die("SDL_UnlockMutex(): %s", SDL_GetError()); } // get timestamp and next update timestamp const Uint32 ticks = SDL_GetTicks(), next_step_ticks = data->last_step_event_ticks + 100; // limit step events to every 100ms if (SDL_TICKS_PASSED(ticks, next_step_ticks)) { // cache timestamp data->last_step_event_ticks = ticks; // push event push_event(data, SOLVE_EVENT_STEP, NULL); } // return false if cancelled return !cancel; } static void solve_on_error( const char * const err, void *user_data ) { solve_t *data = user_data; UNUSED(user_data); // die("Error solving level: %s", err); push_event(data, SOLVE_EVENT_FAIL, (void*) err); } static const sok_solve_cbs_t SOLVE_CBS = { .on_step = solve_on_step, .on_error = solve_on_error, }; static int solve_thread( void * const thread_data ) { solve_t *data = thread_data; warn("solve started"); // solve board data->result = sok_solve(&(data->ctx), &SOLVE_CBS, data); if (data->result) { // push success push_event(data, SOLVE_EVENT_DONE, NULL); } warn("solve done"); return 0; } solve_t * solve( const sok_ctx_t * const ctx, const Uint32 solve_event_type ) { // create thread mutex SDL_mutex *mutex = SDL_CreateMutex(); if (!mutex) { die("SDL_CreateMutex(): %s", SDL_GetError()); } // alloc solve thread data solve_t *data = malloc(sizeof(solve_t)); if (!data) { die("malloc(): %s", strerror(errno)); } // init solve data data->mutex = mutex; data->ctx = *ctx; data->solve_event_type = solve_event_type; data->old_num_moves = ctx->num_moves; data->num_steps = 0; data->last_step_event_ticks = SDL_GetTicks(); data->result = false; data->cancel = false; // create solve thread SDL_Thread *thread = SDL_CreateThread(solve_thread, "solve", data); if (!thread) { die("SDL_CreateThread(): %s", SDL_GetError()); } // detach thread SDL_DetachThread(thread); SDL_Log("Solving (thread ID = %lu, address = %p)", SDL_GetThreadID(thread), thread); // return thread data return data; } void solve_fini( solve_t * const data, void (*on_done)(const bool, const sok_ctx_t *, const size_t, void *), void *user_data ) { if (on_done) { // call handler on_done(data->result, &(data->ctx), data->num_steps, user_data); } // free mutex SDL_DestroyMutex(data->mutex); data->mutex = NULL; // free thread data free(data); } void solve_cancel(solve_t * const data) { // lock mutex if (SDL_LockMutex(data->mutex)) { die("SDL_LockMutex(): %s", SDL_GetError()); } // set cancel data->cancel = true; // lock mutex if (SDL_UnlockMutex(data->mutex)) { die("SDL_UnlockMutex(): %s", SDL_GetError()); } } size_t solve_get_num_steps(solve_t * const data) { // lock mutex if (SDL_LockMutex(data->mutex)) { die("SDL_LockMutex(): %s", SDL_GetError()); } // get num steps const size_t num_steps = data->num_steps; // lock mutex if (SDL_UnlockMutex(data->mutex)) { die("SDL_UnlockMutex(): %s", SDL_GetError()); } // return num_steps return num_steps; } bool solve_get_result(solve_t * const data) { return data->result; }