aboutsummaryrefslogtreecommitdiff
path: root/src/sdl/solve.c
blob: a3e27e80fbbed25b15e60dfa09452b1944318744 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#include <stdbool.h> // bool
#include <errno.h> // errno
#include <string.h> // strerror()
#include <SDL.h>
#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;
}