1 // This file is part of Freecell Solver. It is subject to the license terms in
2 // the COPYING.txt file found in the top-level directory of this distribution
3 // and at http://fc-solve.shlomifish.org/docs/distro/COPYING.html . No part of
4 // Freecell Solver, including this file, may be copied, modified, propagated,
5 // or distributed except according to the terms contained in the COPYING file.
6 //
7 // Copyright (c) 2000 Shlomi Fish
8 // lib.c - library interface functions of Freecell Solver.
9 #include "instance_for_lib.h"
10 #include "freecell-solver/fcs_user.h"
11 #include "fcs_user_internal.h"
12 #ifndef FCS_WITHOUT_FC_PRO_MOVES_COUNT
13 #include "fc_pro_iface_pos.h"
14 #endif
15 #ifdef FCS_ZERO_FREECELLS_MODE
16 #include "zerofc_freecell_moves.h"
17 #endif
18
19 #ifdef DEBUG
verify_state_sanity(const fcs_state * const ptr_state)20 static void verify_state_sanity(const fcs_state *const ptr_state)
21 {
22 #ifndef NDEBUG
23 for (int i = 0; i < 8; ++i)
24 {
25 const int l = fcs_state_col_len(*(ptr_state), i);
26 assert((l >= 0) && (l <= 7 + 12));
27 }
28 #endif
29 }
30 #endif
31
32 static
33 #ifdef FCS_SINGLE_HARD_THREAD
34 inline
35 #endif
36 void
fc_solve_instance__init_hard_thread(fcs_instance * const instance,fcs_hard_thread * const hard_thread)37 fc_solve_instance__init_hard_thread(
38 #ifndef FCS_SINGLE_HARD_THREAD
39 fcs_instance *const instance,
40 #endif
41 fcs_hard_thread *const hard_thread)
42 {
43 #ifndef FCS_SINGLE_HARD_THREAD
44 hard_thread->instance = instance;
45 #endif
46
47 HT_FIELD(hard_thread, num_soft_threads) = 0;
48
49 HT_FIELD(hard_thread, soft_threads) = NULL;
50
51 fc_solve_new_soft_thread(hard_thread);
52 #ifndef FCS_USE_PRECOMPILED_CMD_LINE_THEME
53 HT_FIELD(hard_thread, prelude_as_string) = NULL;
54 #endif
55 HT_FIELD(hard_thread, prelude) = NULL;
56 HT_FIELD(hard_thread, prelude_num_items) = 0;
57
58 fc_solve_reset_hard_thread(hard_thread);
59 fc_solve_compact_allocator_init(&(HT_FIELD(hard_thread, allocator)),
60 #ifdef FCS_SINGLE_HARD_THREAD
61 hard_thread->meta_alloc
62 #else
63 instance->meta_alloc
64 #endif
65 );
66
67 #ifdef FCS_WITH_MOVES
68 HT_FIELD(hard_thread, reusable_move_stack) = fcs_move_stack__new();
69 #endif
70 }
71 #ifndef FCS_SINGLE_HARD_THREAD
new_hard_thread(fcs_instance * const instance)72 static inline fcs_soft_thread *new_hard_thread(fcs_instance *const instance)
73 {
74 // Make sure we are not exceeding the maximal number of soft threads
75 // for an instance.
76 if (instance->next_soft_thread_id == MAX_NUM_SCANS)
77 {
78 return NULL;
79 }
80
81 instance->hard_threads =
82 SREALLOC(instance->hard_threads, instance->num_hard_threads + 1);
83
84 // Since we SREALLOC()ed the hard_threads, their addresses changed,
85 // so we need to update them.
86 HT_LOOP_START()
87 {
88 ST_LOOP_START() { soft_thread->hard_thread = hard_thread; }
89 }
90
91 fcs_hard_thread *const ret =
92 &(instance->hard_threads[instance->num_hard_threads]);
93 fc_solve_instance__init_hard_thread(instance, ret);
94 ++instance->num_hard_threads;
95
96 return &(ret->soft_threads[0]);
97 }
98 #endif
99
100 #ifndef FCS_FREECELL_ONLY
apply_preset_by_name(fcs_instance * const instance,const char * const name)101 static inline void apply_preset_by_name(
102 fcs_instance *const instance, const char *const name)
103 {
104 const fcs_preset *preset_ptr;
105 const_AUTO(ret, fc_solve_get_preset_by_name(name, &preset_ptr));
106 if (ret == FCS_PRESET_CODE_OK)
107 {
108 fc_solve_apply_preset_by_ptr(instance, preset_ptr);
109 }
110 }
111 #endif
112
113 static const fcs_stats initial_stats = {.num_checked_states = 0,
114 #ifndef FCS_DISABLE_NUM_STORED_STATES
115 .num_states_in_collection = 0
116 #endif
117 };
118
119 // This function allocates a Freecell Solver instance struct and set the
120 // default values in it. After the call to this function, the program can
121 // set parameters in it which are different from the default.
122 //
123 // Afterwards fc_solve_init_instance() should be called in order to really
124 // prepare it for solving.
alloc_instance(fcs_instance * const instance,meta_allocator * const meta_alloc)125 static inline void alloc_instance(
126 fcs_instance *const instance, meta_allocator *const meta_alloc)
127 {
128 *(instance) = (fcs_instance){
129 .meta_alloc = meta_alloc,
130 .i__stats = initial_stats,
131 #ifndef FCS_WITHOUT_MAX_NUM_STATES
132 .effective_max_num_checked_states = FCS_ITERS_INT_MAX,
133 #endif
134 #ifndef FCS_DISABLE_NUM_STORED_STATES
135 #ifndef FCS_WITHOUT_TRIM_MAX_STORED_STATES
136 .active_num_states_in_collection = 0,
137 .effective_trim_states_in_collection_from = FCS_ITERS_INT_MAX,
138 #endif
139 .effective_max_num_states_in_collection = FCS_ITERS_INT_MAX,
140 #endif
141 .instance_moves_order =
142 {
143 .num = 0,
144 #ifndef FCS_ZERO_FREECELLS_MODE
145 .groups = NULL,
146 #endif
147 },
148 .list_of_vacant_states = NULL,
149 #ifdef FCS_WITH_MOVES
150 .opt_moves =
151 {
152 .num = 0,
153 #ifndef FCS_ZERO_FREECELLS_MODE
154 .groups = NULL,
155 #endif
156 },
157 .solution_moves = (fcs_move_stack){.moves = NULL, .num_moves = 0},
158 .FCS_RUNTIME_OPTIMIZE_SOLUTION_PATH = false,
159 .FCS_RUNTIME_IN_OPTIMIZATION_THREAD = false,
160 .FCS_RUNTIME_OPT_TESTS_ORDER_WAS_SET = false,
161 #endif
162 #ifdef FCS_SINGLE_HARD_THREAD
163 #ifdef FCS_WITH_MOVES
164 .is_optimization_st = false,
165 #endif
166 #else
167 .num_hard_threads = 0,
168 .hard_threads = NULL,
169 #ifdef FCS_WITH_MOVES
170 .optimization_thread = NULL,
171 #endif
172 #endif
173 .next_soft_thread_id = 0,
174 #ifndef FCS_WITHOUT_ITER_HANDLER
175 .debug_iter_output_func = NULL,
176 #endif
177 .finished_hard_threads_count = 0,
178 #ifndef FCS_HARD_CODE_CALC_REAL_DEPTH_AS_FALSE
179 .FCS_RUNTIME_CALC_REAL_DEPTH = false,
180 #endif
181 #ifndef FCS_HARD_CODE_REPARENT_STATES_AS_FALSE
182 .FCS_RUNTIME_TO_REPARENT_STATES_REAL = false,
183 .FCS_RUNTIME_TO_REPARENT_STATES_PROTO = false,
184 #endif
185 #ifndef FCS_HARD_CODE_SCANS_SYNERGY_AS_TRUE
186 .FCS_RUNTIME_SCANS_SYNERGY = true,
187 #endif
188 #ifdef FCS_RCS_STATES
189 .rcs_states_cache.max_num_elements_in_cache = 10000,
190 #endif
191 #ifndef FCS_DISABLE_SIMPLE_SIMON
192 .is_simple_simon = false,
193 #endif
194 };
195 #if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH)
196 fc_solve_hash_init(meta_alloc, &(instance->hash),
197 #ifdef FCS_INLINED_HASH_COMPARISON
198 FCS_INLINED_HASH__STATES
199 #else
200 #ifdef FCS_WITH_CONTEXT_VARIABLE
201 fc_solve_state_compare_with_context,
202
203 NULL
204 #else
205 fc_solve_state_compare
206 #endif
207 #endif
208 );
209 #endif
210 #ifdef INDIRECT_STACK_STATES
211 #if FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH
212 fc_solve_hash_init(meta_alloc, &(instance->stacks_hash),
213 #ifdef FCS_INLINED_HASH_COMPARISON
214 FCS_INLINED_HASH__COLUMNS
215 #else
216 #ifdef FCS_WITH_CONTEXT_VARIABLE
217 cmp_stacks_w_context, NULL
218 #else
219 fc_solve_stack_compare_for_comparison
220 #endif
221 #endif
222 );
223 #endif
224 #endif
225
226 #ifndef FCS_FREECELL_ONLY
227 apply_preset_by_name(instance, "freecell");
228 #else
229 #ifndef FCS_ZERO_FREECELLS_MODE
230 FCS__DECL_ERR_BUF(no_use);
231 fc_solve_apply_moves_order(&(instance->instance_moves_order),
232 "[01][23456789]" FCS__PASS_ERR_STR(no_use));
233 #endif
234 #endif
235
236 #ifdef FCS_SINGLE_HARD_THREAD
237 fc_solve_instance__init_hard_thread(instance);
238 #else
239 new_hard_thread(instance);
240 #endif
241 }
242
243 #ifndef FCS_USE_PRECOMPILED_CMD_LINE_THEME
compile_prelude(fcs_hard_thread * const hard_thread)244 static inline void compile_prelude(fcs_hard_thread *const hard_thread)
245 {
246 bool last_one = false;
247 size_t num_items = 0;
248 fc_solve_prelude_item *prelude = NULL;
249
250 const char *p = HT_FIELD(hard_thread, prelude_as_string);
251
252 while (!last_one)
253 {
254 const size_t p_quota = (size_t)atol(p);
255 while ((*p) && isdigit(*p))
256 {
257 p++;
258 }
259 if (*p != '@')
260 {
261 free(prelude);
262 return;
263 }
264 p++;
265 const char *const p_scan = p;
266 while ((*p) && ((*p) != ','))
267 {
268 p++;
269 }
270 if ((*p) == '\0')
271 {
272 last_one = true;
273 }
274 char p_scan_copy[FCS_MAX_IDENT_LEN];
275 strncpy(p_scan_copy, p_scan, COUNT(p_scan_copy));
276 p_scan_copy[p - p_scan] = LAST(p_scan_copy) = '\0';
277 p++;
278
279 ST_LOOP_START()
280 {
281 if (!strcmp(soft_thread->name, p_scan_copy))
282 {
283 break;
284 }
285 }
286 if (soft_thread == end_soft_thread)
287 {
288 free(prelude);
289 return;
290 }
291 #define PRELUDE_GROW_BY 16
292 if (!(num_items & (PRELUDE_GROW_BY - 1)))
293 {
294 prelude = SREALLOC(prelude, num_items + PRELUDE_GROW_BY);
295 }
296 #undef PRELUDE_GROW_BY
297 prelude[num_items++] = (typeof(prelude[0])){
298 .scan_idx = (size_t)(soft_thread - ht_soft_threads),
299 .quota = p_quota};
300 }
301
302 HT_FIELD(hard_thread, prelude) = SREALLOC(prelude, num_items);
303 HT_FIELD(hard_thread, prelude_num_items) = num_items;
304 }
305 #endif
306
set_next_soft_thread(fcs_hard_thread * const hard_thread,const uint_fast32_t scan_idx,const fcs_iters_int quota,uint_fast32_t * const st_idx_ptr)307 static inline void set_next_soft_thread(fcs_hard_thread *const hard_thread,
308 const uint_fast32_t scan_idx, const fcs_iters_int quota,
309 uint_fast32_t *const st_idx_ptr)
310 {
311 (*st_idx_ptr) = scan_idx;
312 HT_FIELD(hard_thread, ht__max_num_checked_states) =
313 NUM_CHECKED_STATES + quota;
314 }
315
init_instance(fcs_instance * const instance)316 static inline void init_instance(fcs_instance *const instance)
317 {
318 HT_LOOP_START()
319 {
320 // The pointer to instance may change as the flares array get resized
321 // so the pointers need to be reassigned to it.
322 #ifndef FCS_SINGLE_HARD_THREAD
323 hard_thread->instance = instance;
324 #else
325 ST_LOOP_START() { soft_thread->hard_thread = instance; }
326 #endif
327 #ifndef FCS_USE_PRECOMPILED_CMD_LINE_THEME
328 if (HT_FIELD(hard_thread, prelude_as_string) &&
329 !HT_FIELD(hard_thread, prelude))
330 {
331 compile_prelude(hard_thread);
332 }
333 #endif
334 set_next_soft_thread(hard_thread, 0,
335 HT_FIELD(hard_thread, soft_threads)[0].checked_states_step,
336 &(HT_FIELD(hard_thread, st_idx)));
337 }
338
339 size_t total_move_funcs_bitmask = 0;
340 fc_solve_foreach_soft_thread(instance,
341 FOREACH_SOFT_THREAD_ACCUM_TESTS_ORDER, &total_move_funcs_bitmask);
342 fc_solve_foreach_soft_thread(instance,
343 FOREACH_SOFT_THREAD_DETERMINE_SCAN_COMPLETENESS,
344 &total_move_funcs_bitmask);
345 #ifdef FCS_WITH_MOVES
346 if (!STRUCT_QUERY_FLAG(instance, FCS_RUNTIME_OPT_TESTS_ORDER_WAS_SET))
347 {
348 // What this code does is convert the bit map of
349 // total_move_funcs_bitmask to a valid moves order.
350 size_t num_move_funcs = 0;
351 fcs_move_func *move_funcs =
352 SMALLOC(move_funcs, sizeof(total_move_funcs_bitmask) * 8);
353
354 for (size_t bit_idx = 0; total_move_funcs_bitmask != 0;
355 ++bit_idx, total_move_funcs_bitmask >>= 1)
356 {
357 if ((total_move_funcs_bitmask & 0x1) != 0)
358 {
359 move_funcs[num_move_funcs++].idx = bit_idx;
360 }
361 }
362 const_AUTO(old_move_funcs, move_funcs);
363 move_funcs = SREALLOC(move_funcs,
364 ((num_move_funcs & (~(MOVES_GROW_BY - 1))) + MOVES_GROW_BY));
365 if (unlikely(!move_funcs))
366 {
367 free(old_move_funcs);
368 }
369 instance->opt_moves = (typeof(instance->opt_moves)){
370 .num = 1,
371 #ifndef FCS_ZERO_FREECELLS_MODE
372 .groups = SMALLOC(instance->opt_moves.groups, MOVES_GROW_BY),
373 #endif
374 };
375 #ifndef FCS_ZERO_FREECELLS_MODE
376 instance->opt_moves.groups[0] = (typeof(instance->opt_moves.groups[0])){
377 .move_funcs = move_funcs,
378 .num = num_move_funcs,
379 .shuffling_type = FCS_NO_SHUFFLING,
380 };
381 #endif
382 STRUCT_TURN_ON_FLAG(instance, FCS_RUNTIME_OPT_TESTS_ORDER_WAS_SET);
383 }
384 #endif
385
386 #ifdef FCS_RCS_STATES
387 fcs_lru_cache *const cache = &(instance->rcs_states_cache);
388 #if (FCS_RCS_CACHE_STORAGE == FCS_RCS_CACHE_STORAGE_JUDY)
389 cache->states_values_to_keys_map = ((Pvoid_t)NULL);
390 #elif (FCS_RCS_CACHE_STORAGE == FCS_RCS_CACHE_STORAGE_KAZ_TREE)
391 cache->kaz_tree = fc_solve_kaz_tree_create(
392 fc_solve_compare_lru_cache_keys, NULL, instance->meta_alloc, NULL);
393 #else
394 #error Unknown FCS_RCS_CACHE_STORAGE
395 #endif
396
397 fc_solve_compact_allocator_init(
398 &(cache->states_values_to_keys_allocator), instance->meta_alloc);
399 cache->lowest_pri = NULL;
400 cache->highest_pri = NULL;
401 cache->recycle_bin = NULL;
402 cache->count_elements_in_cache = 0;
403 #endif
404 }
405
406 // These are all stack comparison functions to be used for the stacks
407 // cache when using INDIRECT_STACK_STATES
408 #ifdef INDIRECT_STACK_STATES
409 #if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH)
fc_solve_glib_hash_stack_hash_function(gconstpointer key)410 static guint fc_solve_glib_hash_stack_hash_function(gconstpointer key)
411 {
412 guint hash_value_int = 0;
413 // This hash function was ripped from the Perl source code.
414 // (It is not derived work however).
415 const char *s_ptr = (const char *)key;
416 const char *const s_end = s_ptr + fcs_col_len((const fcs_card *)key) + 1;
417 while (s_ptr < s_end)
418 {
419 hash_value_int += (hash_value_int << 5) + *(s_ptr++);
420 }
421 hash_value_int += (hash_value_int >> 5);
422
423 return hash_value_int;
424 }
425
fc_solve_glib_hash_stack_compare(gconstpointer a,gconstpointer b)426 static gint fc_solve_glib_hash_stack_compare(gconstpointer a, gconstpointer b)
427 {
428 return !(fc_solve_stack_compare_for_comparison(a, b));
429 }
430 #endif
431
432 #if ((FCS_STACK_STORAGE != FCS_STACK_STORAGE_GLIB_TREE) && \
433 (FCS_STACK_STORAGE != FCS_STACK_STORAGE_GLIB_HASH) && \
434 (FCS_STACK_STORAGE != FCS_STACK_STORAGE_JUDY))
435 #if (((FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH) && \
436 (defined(FCS_WITH_CONTEXT_VARIABLE)) && \
437 (!defined(FCS_INLINED_HASH_COMPARISON))) || \
438 (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL2_TREE) || \
439 (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE))
440
cmp_stacks_w_context(const void * const v_s1,const void * const v_s2,const void * context GCC_UNUSED)441 static int cmp_stacks_w_context(const void *const v_s1, const void *const v_s2,
442 #if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE)
443 const
444 #endif
445 void *context GCC_UNUSED)
446 {
447 return fc_solve_stack_compare_for_comparison(v_s1, v_s2);
448 }
449 #endif
450 #endif
451 #endif
452
453 #ifdef FCS_RCS_STATES
454
455 #if ((FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL2_TREE) || \
456 (FCS_STATE_STORAGE == FCS_STATE_STORAGE_KAZ_TREE))
457
rcs_states_get_state(fcs_instance * const instance,const fcs_collectible_state * const state)458 static inline fcs_state *rcs_states_get_state(
459 fcs_instance *const instance, const fcs_collectible_state *const state)
460 {
461 return ((state == instance->tree_new_state)
462 ? instance->tree_new_state_key
463 : fc_solve_lookup_state_key_from_val(instance, state));
464 }
465
rcs_cmp_states(const void * const void_a,const void * const void_b,void * const context)466 static int rcs_cmp_states(
467 const void *const void_a, const void *const void_b, void *const context)
468 {
469 fcs_instance *const instance = (fcs_instance *)context;
470
471 return fc_solve_state_compare(
472 rcs_states_get_state(instance, (const fcs_collectible_state *)void_a),
473 rcs_states_get_state(instance, (const fcs_collectible_state *)void_b));
474 }
475
476 #endif
477
478 #endif
479
480 #if ((FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL2_TREE) || \
481 (FCS_STATE_STORAGE == FCS_STATE_STORAGE_KAZ_TREE) || \
482 (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE))
483
484 #ifdef FCS_RCS_STATES
485 #define STATE_STORAGE_TREE_COMPARE() rcs_cmp_states
486 #define STATE_STORAGE_TREE_CONTEXT() instance
487 #else
488 #define STATE_STORAGE_TREE_COMPARE() fc_solve_state_compare_with_context
489 #define STATE_STORAGE_TREE_CONTEXT() NULL
490 #endif
491
492 #endif
493
set_next_prelude_item(fcs_hard_thread * const hard_thread,const fc_solve_prelude_item * const prelude,uint_fast32_t * const st_idx_ptr)494 static inline void set_next_prelude_item(fcs_hard_thread *const hard_thread,
495 const fc_solve_prelude_item *const prelude, uint_fast32_t *const st_idx_ptr)
496 {
497 const fc_solve_prelude_item next_item =
498 prelude[HT_FIELD(hard_thread, prelude_idx)++];
499 set_next_soft_thread(hard_thread, next_item.scan_idx,
500 (fcs_iters_int)next_item.quota, st_idx_ptr);
501 }
502
update_initial_cards_val(fcs_instance * const instance)503 static inline void update_initial_cards_val(fcs_instance *const instance)
504 {
505 #ifdef FCS_FREECELL_ONLY
506 #define SEQS_BUILT_BY
507 #else
508 const int sequences_are_built_by =
509 GET_INSTANCE_SEQUENCES_ARE_BUILT_BY(instance);
510 #define SEQS_BUILT_BY sequences_are_built_by,
511 #endif
512 // We cannot use typeof here because clang complains about double const.
513 const fcs_state *const s = &(instance->state_copy.s);
514
515 fcs_seq_cards_power_type cards_under_sequences = 0;
516 for (int a = 0; a < INSTANCE_STACKS_NUM; a++)
517 {
518 const_AUTO(col, fcs_state_get_col(*s, a));
519 const_AUTO(col_len, fcs_col_len(col));
520 if (col_len <= 1)
521 {
522 continue;
523 }
524 cards_under_sequences += FCS_SEQS_OVER_RENEGADE_POWER(
525 update_col_cards_under_sequences(SEQS_BUILT_BY col, col_len - 1));
526 }
527 instance->initial_cards_under_sequences_value = cards_under_sequences;
528 }
529
530 // This function associates a board with an fcs_instance and
531 // does other initialisations. After it, you must call resume_instance()
532 // repeatedly.
start_process_with_board(fcs_instance * const instance,fcs_state_keyval_pair * const init_state,fcs_state_keyval_pair * const initial_non_canonized_state GCC_UNUSED)533 static inline void start_process_with_board(fcs_instance *const instance,
534 fcs_state_keyval_pair *const init_state,
535 fcs_state_keyval_pair *const initial_non_canonized_state GCC_UNUSED)
536 {
537 #ifndef FCS_DISABLE_PATSOLVE
538 instance->initial_non_canonized_state = initial_non_canonized_state;
539 #endif
540
541 #if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE)
542 instance->tree =
543 rbinit(STATE_STORAGE_TREE_COMPARE(), STATE_STORAGE_TREE_CONTEXT());
544 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL2_TREE)
545
546 instance->tree = fcs_libavl2_states_tree_create(
547 STATE_STORAGE_TREE_COMPARE(), STATE_STORAGE_TREE_CONTEXT(), NULL);
548 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_KAZ_TREE)
549 instance->tree = fc_solve_kaz_tree_create(
550 STATE_STORAGE_TREE_COMPARE(), STATE_STORAGE_TREE_CONTEXT());
551 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE)
552 instance->tree = g_tree_new(fc_solve_state_compare);
553 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_JUDY)
554 instance->judy_array = ((Pvoid_t)NULL);
555 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH)
556 instance->hash =
557 g_hash_table_new(fc_solve_hash_function, fc_solve_state_compare_equal);
558 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH)
559 #ifdef FCS_RCS_STATES
560 instance->hash.instance = instance;
561 #endif
562 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GOOGLE_DENSE_HASH)
563 instance->hash = fc_solve_states_google_hash_new();
564 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH)
565 // Do nothing because it is allocated elsewhere.
566 #else
567 #error FCS_STATE_STORAGE is not defined
568 #endif
569
570 #ifdef INDIRECT_STACK_STATES
571 #if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL2_TREE)
572 instance->stacks_tree =
573 fcs_libavl2_stacks_tree_create(cmp_stacks_w_context, NULL, NULL);
574 #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH)
575 #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE)
576 instance->stacks_tree = rbinit(cmp_stacks_w_context, NULL);
577 #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE)
578 instance->stacks_tree = g_tree_new(fc_solve_stack_compare_for_comparison);
579 #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH)
580 instance->stacks_hash =
581 g_hash_table_new(fc_solve_glib_hash_stack_hash_function,
582 fc_solve_glib_hash_stack_compare);
583 #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GOOGLE_DENSE_HASH)
584 instance->stacks_hash = fc_solve_columns_google_hash_new();
585 #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_JUDY)
586 instance->stacks_judy_array = NULL;
587 #else
588 #error FCS_STACK_STORAGE is not set to a good value.
589 #endif
590 #endif
591
592 #if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE)
593 // Not working; ignore
594 db_open(
595 NULL, DB_BTREE, O_CREAT | O_RDWR, 0777, NULL, NULL, &(instance->db));
596 #endif
597 // Initialize the first state to init_state
598 instance->state_copy = (typeof(instance->state_copy)){.s = init_state->s,
599 .info = {
600 #ifdef INDIRECT_STACK_STATES
601 .stacks_copy_on_write_flags = ~0,
602 #endif
603 // Initialize the state to be a base state for the game tree
604 #ifdef FCS_WITH_DEPTH_FIELD
605 .depth = 0,
606 #endif
607 #ifdef FCS_WITH_MOVES
608 .moves_to_parent = NULL,
609 #endif
610 .visited = 0,
611 .parent = NULL,
612 .scan_visited = {0}}};
613 update_initial_cards_val(instance);
614
615 fcs_kv_state no_use,
616 pass_copy = FCS_STATE_keyval_pair_to_kv(&instance->state_copy);
617 fc_solve_check_and_add_state(
618 #ifdef FCS_SINGLE_HARD_THREAD
619 instance,
620 #else
621 instance->hard_threads,
622 #endif
623 &pass_copy, &no_use);
624
625 #ifndef FCS_SINGLE_HARD_THREAD
626 instance->current_hard_thread = instance->hard_threads;
627 #endif
628 {
629 HT_LOOP_START()
630 {
631 HT_FIELD(hard_thread, prelude_idx) = 0;
632 if (HT_FIELD(hard_thread, prelude))
633 {
634 set_next_prelude_item(hard_thread,
635 HT_FIELD(hard_thread, prelude),
636 &(HT_FIELD(hard_thread, st_idx)));
637 }
638 }
639 }
640 #ifndef FCS_HARD_CODE_REPARENT_STATES_AS_FALSE
641 STRUCT_SET_FLAG_TO(instance, FCS_RUNTIME_TO_REPARENT_STATES_REAL,
642 STRUCT_QUERY_FLAG(instance, FCS_RUNTIME_TO_REPARENT_STATES_PROTO));
643 #endif
644 }
645
free_hard_thread(fcs_hard_thread * const hard_thread)646 static inline void free_hard_thread(fcs_hard_thread *const hard_thread)
647 {
648 #ifndef FCS_USE_PRECOMPILED_CMD_LINE_THEME
649 free(HT_FIELD(hard_thread, prelude_as_string));
650 free(HT_FIELD(hard_thread, prelude));
651 #endif
652 fcs_move_stack_static_destroy(HT_FIELD(hard_thread, reusable_move_stack));
653 free(HT_FIELD(hard_thread, soft_threads));
654 fc_solve_compact_allocator_finish(&(HT_FIELD(hard_thread, allocator)));
655 }
656
free_instance(fcs_instance * const instance)657 static inline void free_instance(fcs_instance *const instance)
658 {
659 fc_solve_foreach_soft_thread(
660 instance, FOREACH_SOFT_THREAD_FREE_INSTANCE, NULL);
661
662 HT_LOOP_START() { free_hard_thread(hard_thread); }
663
664 #ifdef FCS_SINGLE_HARD_THREAD
665 #ifdef FCS_WITH_MOVES
666 if (instance->is_optimization_st)
667 {
668 fc_solve_free_instance_soft_thread_callback(
669 &(instance->optimization_soft_thread));
670 instance->is_optimization_st = false;
671 }
672 #endif
673 #else
674 free(instance->hard_threads);
675
676 #ifdef FCS_WITH_MOVES
677 if (instance->optimization_thread)
678 {
679 free_hard_thread(instance->optimization_thread);
680 free(instance->optimization_thread);
681 }
682 #endif
683 #endif
684 moves_order__free(&(instance->instance_moves_order));
685 #ifdef FCS_WITH_MOVES
686 if (STRUCT_QUERY_FLAG(instance, FCS_RUNTIME_OPT_TESTS_ORDER_WAS_SET))
687 {
688 moves_order__free(&(instance->opt_moves));
689 }
690 instance_free_solution_moves(instance);
691 #endif
692 }
693
recycle_ht(fcs_hard_thread * const hard_thread)694 static inline void recycle_ht(fcs_hard_thread *const hard_thread)
695 {
696 fc_solve_reset_hard_thread(hard_thread);
697 fc_solve_compact_allocator_recycle(&(HT_FIELD(hard_thread, allocator)));
698
699 ST_LOOP_START()
700 {
701 st_free_pq(soft_thread);
702 fc_solve_reset_soft_thread(soft_thread);
703
704 #ifndef FCS_DISABLE_PATSOLVE
705 const_SLOT(pats_scan, soft_thread);
706
707 if (pats_scan)
708 {
709 fc_solve_pats__recycle_soft_thread(pats_scan);
710 }
711 #endif
712 }
713 }
714
715 // This function should be called after the user has retrieved the results
716 // generated by the scan, as it will destroy them.
fc_solve_finish_instance(fcs_instance * const instance)717 static void fc_solve_finish_instance(fcs_instance *const instance)
718 {
719 // De-allocate the state collection
720 #if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE)
721 rbdestroy(instance->tree);
722 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL2_TREE)
723 fcs_libavl2_states_tree_destroy(instance->tree, NULL);
724 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_KAZ_TREE)
725 fc_solve_kaz_tree_free_nodes(instance->tree);
726 fc_solve_kaz_tree_destroy(instance->tree);
727 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE)
728 g_tree_destroy(instance->tree);
729 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_JUDY)
730 Word_t rc_word;
731 #ifdef JERR
732 #undef JERR
733 #define JERR ((Word_t)(-1))
734 #endif
735 JHSFA(rc_word, instance->judy_array);
736 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH)
737 g_hash_table_destroy(instance->hash);
738 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH)
739 // fc_solve_hash_free(&(instance->hash));
740 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GOOGLE_DENSE_HASH)
741 fc_solve_states_google_hash_free(instance->hash);
742 #else
743 #error FCS_STATE_STORAGE is not defined
744 #endif
745
746 // De-allocate the stack collection while free()'ing the stacks in the process
747 #ifdef INDIRECT_STACK_STATES
748 #if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH)
749 // fc_solve_hash_free(&(instance->stacks_hash));
750 #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL2_TREE)
751 fcs_libavl2_stacks_tree_destroy(instance->stacks_tree, NULL);
752 #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE)
753 rbdestroy(instance->stacks_tree);
754 #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE)
755 g_tree_destroy(instance->stacks_tree);
756 #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH)
757 g_hash_table_destroy(instance->stacks_hash);
758 #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GOOGLE_DENSE_HASH)
759 fc_solve_columns_google_hash_free(instance->stacks_hash);
760 #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_JUDY)
761 Word_t cache_rc_word;
762 JHSFA(cache_rc_word, instance->stacks_judy_array);
763 #else
764 #error FCS_STACK_STORAGE is not set to a good value.
765 #endif
766 #endif
767
768 #if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE)
769 instance->db->close(instance->db, 0);
770 #endif
771
772 #ifdef FCS_RCS_STATES
773
774 #if (FCS_RCS_CACHE_STORAGE == FCS_RCS_CACHE_STORAGE_JUDY)
775 Word_t rcs_rc_word;
776 JLFA(rcs_rc_word, instance->rcs_states_cache.states_values_to_keys_map);
777 #elif (FCS_RCS_CACHE_STORAGE == FCS_RCS_CACHE_STORAGE_KAZ_TREE)
778 fc_solve_kaz_tree_free_nodes(instance->rcs_states_cache.kaz_tree);
779 fc_solve_kaz_tree_destroy(instance->rcs_states_cache.kaz_tree);
780 #else
781 #error Unknown FCS_RCS_CACHE_STORAGE
782 #endif
783 fc_solve_compact_allocator_finish(
784 &(instance->rcs_states_cache.states_values_to_keys_allocator));
785 #endif
786 fc_solve_foreach_soft_thread(
787 instance, FOREACH_SOFT_THREAD_CLEAN_SOFT_DFS, NULL);
788 }
789
recycle_inst(fcs_instance * const instance)790 static inline void recycle_inst(fcs_instance *const instance)
791 {
792 fc_solve_finish_instance(instance);
793 #if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH)
794 fc_solve_hash_recycle(&(instance->hash));
795 #endif
796 #ifdef INDIRECT_STACK_STATES
797 #if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH)
798 fc_solve_hash_recycle(&(instance->stacks_hash));
799 #endif
800 #endif
801 #ifdef FCS_WITH_MOVES
802 instance_free_solution_moves(instance);
803 #endif
804 instance->i__stats = initial_stats;
805 instance->finished_hard_threads_count = 0;
806 #ifdef FCS_SINGLE_HARD_THREAD
807 recycle_ht(instance);
808 #ifdef FCS_WITH_MOVES
809 if (instance->is_optimization_st)
810 {
811 fc_solve_reset_soft_thread(&(instance->optimization_soft_thread));
812 }
813 #endif
814 #else
815 for (uint_fast32_t ht_idx = 0; ht_idx < instance->num_hard_threads;
816 ht_idx++)
817 {
818 recycle_ht(&(instance->hard_threads[ht_idx]));
819 }
820 #ifdef FCS_WITH_MOVES
821 if (instance->optimization_thread)
822 {
823 recycle_ht(instance->optimization_thread);
824 }
825 #endif
826 #endif
827 #ifdef FCS_WITH_MOVES
828 STRUCT_CLEAR_FLAG(instance, FCS_RUNTIME_IN_OPTIMIZATION_THREAD);
829 #endif
830 }
831
832 #ifdef FCS_WITH_MOVES
setup_opt_thread__helper(fcs_instance * const instance,fcs_soft_thread * const soft_thread)833 static inline void setup_opt_thread__helper(
834 fcs_instance *const instance, fcs_soft_thread *const soft_thread)
835 {
836 #ifndef FCS_ZERO_FREECELLS_MODE
837
838 if (STRUCT_QUERY_FLAG(instance, FCS_RUNTIME_OPT_TESTS_ORDER_WAS_SET))
839 {
840 if (soft_thread->by_depth_moves_order.by_depth_moves != NULL)
841 {
842 fc_solve_free_soft_thread_by_depth_move_array(soft_thread);
843 }
844
845 soft_thread->by_depth_moves_order =
846 (typeof(soft_thread->by_depth_moves_order)){
847 .num = 1,
848 .by_depth_moves =
849 SMALLOC1(soft_thread->by_depth_moves_order.by_depth_moves),
850 };
851 soft_thread->by_depth_moves_order.by_depth_moves[0] =
852 (typeof(soft_thread->by_depth_moves_order.by_depth_moves[0])){
853 .max_depth = SSIZE_MAX,
854 .moves_order = moves_order_dup(&(instance->opt_moves)),
855 };
856 }
857 #endif
858
859 soft_thread->super_method_type = FCS_SUPER_METHOD_BEFS_BRFS;
860 soft_thread->is_optimize_scan = true;
861 soft_thread->is_befs = false;
862 STRUCT_TURN_ON_FLAG(soft_thread, FCS_SOFT_THREAD_IS_A_COMPLETE_SCAN);
863 fc_solve_soft_thread_init_befs_or_bfs(soft_thread);
864 STRUCT_TURN_ON_FLAG(soft_thread, FCS_SOFT_THREAD_INITIALIZED);
865 STRUCT_TURN_ON_FLAG(instance, FCS_RUNTIME_IN_OPTIMIZATION_THREAD);
866 }
867
868 // This function optimizes the solution path using a BFS scan on the
869 // states in the solution path.
870 #ifdef FCS_SINGLE_HARD_THREAD
optimize_solution(fcs_instance * const instance)871 static inline fc_solve_solve_process_ret_t optimize_solution(
872 fcs_instance *const instance)
873 {
874 fcs_soft_thread *const optimization_soft_thread =
875 &(instance->optimization_soft_thread);
876
877 if (!instance->solution_moves.moves)
878 {
879 fc_solve_trace_solution(instance);
880 }
881
882 #ifndef FCS_HARD_CODE_REPARENT_STATES_AS_FALSE
883 STRUCT_TURN_ON_FLAG(instance, FCS_RUNTIME_TO_REPARENT_STATES_REAL);
884 #endif
885
886 if (!instance->is_optimization_st)
887 {
888 fc_solve_init_soft_thread(instance, optimization_soft_thread);
889 #ifndef FCS_ENABLE_PRUNE__R_TF__UNCOND
890 // Copy enable_pruning from the thread that reached the solution,
891 // because otherwise -opt in conjunction with -sp r:tf will fail.
892 optimization_soft_thread->enable_pruning =
893 instance->hard_thread.soft_threads[instance->hard_thread.st_idx]
894 .enable_pruning;
895 #endif
896 instance->is_optimization_st = true;
897 }
898
899 setup_opt_thread__helper(instance, optimization_soft_thread);
900 // Instruct the optimization hard thread to run indefinitely as
901 // far as it is concerned
902 instance->hard_thread.ht__max_num_checked_states = FCS_ITERS_INT_MAX;
903 return fc_solve_befs_or_bfs_do_solve(optimization_soft_thread);
904 }
905 #undef soft_thread
906 #else
optimize_solution(fcs_instance * const instance)907 static inline fc_solve_solve_process_ret_t optimize_solution(
908 fcs_instance *const instance)
909 {
910 fcs_soft_thread *soft_thread;
911 fcs_hard_thread *optimization_thread;
912
913 if (!instance->solution_moves.moves)
914 {
915 fc_solve_trace_solution(instance);
916 }
917
918 #ifndef FCS_HARD_CODE_REPARENT_STATES_AS_FALSE
919 STRUCT_TURN_ON_FLAG(instance, FCS_RUNTIME_TO_REPARENT_STATES_REAL);
920 #endif
921
922 if (!instance->optimization_thread)
923 {
924 instance->optimization_thread = optimization_thread =
925 SMALLOC1(optimization_thread);
926
927 fc_solve_instance__init_hard_thread(instance, optimization_thread);
928
929 fcs_hard_thread *const old_hard_thread = instance->current_hard_thread;
930
931 soft_thread = optimization_thread->soft_threads;
932
933 #ifndef FCS_ENABLE_PRUNE__R_TF__UNCOND
934 // Copy enable_pruning from the thread that reached the solution,
935 // because otherwise -opt in conjunction with -sp r:tf will fail.
936 soft_thread->enable_pruning =
937 old_hard_thread->soft_threads[old_hard_thread->st_idx]
938 .enable_pruning;
939 #endif
940 }
941 else
942 {
943 optimization_thread = instance->optimization_thread;
944 soft_thread = optimization_thread->soft_threads;
945 }
946
947 setup_opt_thread__helper(instance, soft_thread);
948 // Instruct the optimization hard thread to run indefinitely as
949 // far as it is concerned.
950 optimization_thread->ht__max_num_checked_states = FCS_ITERS_INT_MAX;
951 return fc_solve_befs_or_bfs_do_solve(soft_thread);
952 }
953 #endif
954 #endif
955
956 #ifdef DEBUG
957
958 #ifdef DEBUG_VERIFY_SOFT_DFS_STACK
verify_soft_dfs_stack(fcs_soft_thread * soft_thread)959 static void verify_soft_dfs_stack(fcs_soft_thread *soft_thread)
960 {
961 for (int depth = 0; depth < DFS_VAR(soft_thread, depth); ++depth)
962 {
963 var_AUTO(soft_dfs_info, &(DFS_VAR(soft_thread, soft_dfs_info)[depth]));
964 var_AUTO(rand_indexes, soft_dfs_info->derived_states_random_indexes);
965
966 const_AUTO(num_states, soft_dfs_info->derived_states_list.num_states);
967
968 for (size_t i = soft_dfs_info->current_state_index; i < num_states; ++i)
969 {
970 verify_state_sanity(
971 soft_dfs_info->derived_states_list.states[rand_indexes[i].idx]
972 .state_ptr);
973 }
974 }
975 }
976 #else
977 #define verify_soft_dfs_stack(soft_thread)
978 #endif
979
980 #define TRACE0(message) \
981 fcs_trace("%s. Depth=%ld ; the_soft_Depth=%ld ; Iters=%ld ; " \
982 "move_func_list_idx=%ld ; move_func_idx=%ld ; " \
983 "current_state_index=%ld ; num_states=%ld\n", \
984 message, (long)DFS_VAR(soft_thread, depth), \
985 (long)(the_soft_dfs_info - DFS_VAR(soft_thread, soft_dfs_info)), \
986 (long)(instance->i__stats.num_checked_states), \
987 (long)the_soft_dfs_info->move_func_list_idx, \
988 (long)the_soft_dfs_info->move_func_idx, \
989 (long)the_soft_dfs_info->current_state_index, \
990 (long)(derived_list->num_states))
991
992 #define VERIFY_STATE_SANITY() verify_state_sanity(&FCS_SCANS_the_state)
993
994 #define VERIFY_PTR_STATE_AND_DERIVED_TRACE0(string) \
995 { \
996 TRACE0(string); \
997 VERIFY_STATE_SANITY(); \
998 VERIFY_DERIVED_STATE(); \
999 verify_soft_dfs_stack(soft_thread); \
1000 }
1001
1002 #else
1003 #define TRACE0(no_use)
1004 #define VERIFY_PTR_STATE_AND_DERIVED_TRACE0(no_use)
1005 #endif
1006
fcs__is_state_a_dead_end(const fcs_collectible_state * const ptr_state)1007 static inline bool fcs__is_state_a_dead_end(
1008 const fcs_collectible_state *const ptr_state)
1009 {
1010 return (FCS_S_VISITED(ptr_state) & FCS_VISITED_DEAD_END);
1011 }
1012
1013 #ifndef FCS_DISABLE_NUM_STORED_STATES
1014 #ifndef FCS_WITHOUT_TRIM_MAX_STORED_STATES
1015 #if (!((FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) || \
1016 (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GOOGLE_DENSE_HASH)))
1017 #define free_states(i)
1018 #else
1019 #ifndef FCS_ZERO_FREECELLS_MODE
free_states_handle_soft_dfs_soft_thread(fcs_soft_thread * const soft_thread)1020 static inline void free_states_handle_soft_dfs_soft_thread(
1021 fcs_soft_thread *const soft_thread)
1022 {
1023 var_AUTO(soft_dfs_info, DFS_VAR(soft_thread, soft_dfs_info));
1024 const_AUTO(end_soft_dfs_info, soft_dfs_info + DFS_VAR(soft_thread, depth));
1025
1026 for (; soft_dfs_info < end_soft_dfs_info; soft_dfs_info++)
1027 {
1028 const_AUTO(rand_indexes, soft_dfs_info->derived_states_random_indexes);
1029
1030 // We start from current_state_index instead of current_state_index+1
1031 // because that is the next state to be checked - it is referenced
1032 // by current_state_index++ instead of ++current_state_index .
1033 rating_with_index *dest_rand_index_ptr =
1034 rand_indexes + soft_dfs_info->current_state_index;
1035 const rating_with_index *rand_index_ptr = dest_rand_index_ptr;
1036
1037 rating_with_index *const end_rand_index_ptr =
1038 rand_indexes + soft_dfs_info->derived_states_list.num_states;
1039
1040 fcs_derived_states_list_item *const states =
1041 soft_dfs_info->derived_states_list.states;
1042 for (; rand_index_ptr < end_rand_index_ptr; rand_index_ptr++)
1043 {
1044 if (!fcs__is_state_a_dead_end(
1045 states[rand_index_ptr->rating_with_index__idx].state_ptr))
1046 {
1047 *(dest_rand_index_ptr++) = *(rand_index_ptr);
1048 }
1049 }
1050 soft_dfs_info->derived_states_list.num_states =
1051 (size_t)(dest_rand_index_ptr - rand_indexes);
1052 }
1053 }
1054 #endif
1055
free_states_should_delete(void * const key,void * const context)1056 static bool free_states_should_delete(void *const key, void *const context)
1057 {
1058 fcs_instance *const instance = (fcs_instance *const)context;
1059 fcs_collectible_state *const ptr_state = (fcs_collectible_state *const)key;
1060
1061 if (fcs__is_state_a_dead_end(ptr_state))
1062 {
1063 FCS_S_NEXT(ptr_state) = instance->list_of_vacant_states;
1064 instance->list_of_vacant_states = ptr_state;
1065
1066 --instance->active_num_states_in_collection;
1067
1068 return true;
1069 }
1070 else
1071 {
1072 return false;
1073 }
1074 }
1075
free_states(fcs_instance * const instance)1076 static inline void free_states(fcs_instance *const instance)
1077 {
1078 // First of all, let's make sure the soft_threads will no longer
1079 // traverse to the freed states that are currently dead ends.
1080 HT_LOOP_START()
1081 {
1082 ST_LOOP_START()
1083 {
1084 if (soft_thread->super_method_type == FCS_SUPER_METHOD_DFS)
1085 {
1086 #ifndef FCS_ZERO_FREECELLS_MODE
1087 free_states_handle_soft_dfs_soft_thread(soft_thread);
1088 #endif
1089 }
1090 else if (soft_thread->is_befs)
1091 {
1092 pri_queue new_pq;
1093 fc_solve_pq_init(&(new_pq));
1094 const_AUTO(elems, BEFS_VAR(soft_thread, pqueue).elems);
1095 const_AUTO(end_element,
1096 elems + BEFS_VAR(soft_thread, pqueue).current_size);
1097 for (pq_element *next_element = elems + PQ_FIRST_ENTRY;
1098 next_element <= end_element; next_element++)
1099 {
1100 if (!fcs__is_state_a_dead_end((*next_element).val))
1101 {
1102 fc_solve_pq_push(&new_pq, (*next_element).val,
1103 (*next_element).rating);
1104 }
1105 }
1106 st_free_pq(soft_thread);
1107 BEFS_VAR(soft_thread, pqueue) = new_pq;
1108 }
1109 }
1110 }
1111
1112 // Now let's recycle the states.
1113 #if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH)
1114 fc_solve_hash_foreach(
1115 &(instance->hash), free_states_should_delete, ((void *)instance));
1116 #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GOOGLE_DENSE_HASH)
1117 fc_solve_states_google_hash_foreach(
1118 instance->hash, free_states_should_delete, ((void *)instance));
1119 #endif
1120 }
1121 #endif
1122 #endif
1123 #endif
1124
1125 #define SOFT_DFS_DEPTH_GROW_BY 64
increase_dfs_max_depth(fcs_soft_thread * const soft_thread)1126 static void increase_dfs_max_depth(fcs_soft_thread *const soft_thread)
1127 {
1128 const_AUTO(new_dfs_max_depth,
1129 DFS_VAR(soft_thread, dfs_max_depth) + SOFT_DFS_DEPTH_GROW_BY);
1130 DFS_VAR(soft_thread, soft_dfs_info) = SREALLOC(
1131 DFS_VAR(soft_thread, soft_dfs_info), (size_t)new_dfs_max_depth);
1132 memset(DFS_VAR(soft_thread, soft_dfs_info) +
1133 DFS_VAR(soft_thread, dfs_max_depth),
1134 '\0',
1135 SOFT_DFS_DEPTH_GROW_BY * sizeof(*DFS_VAR(soft_thread, soft_dfs_info)));
1136
1137 DFS_VAR(soft_thread, dfs_max_depth) = new_dfs_max_depth;
1138 }
1139
1140 #ifndef FCS_ZERO_FREECELLS_MODE
get_depth(const moves_by_depth_unit * curr_by_depth_unit)1141 static inline ssize_t get_depth(const moves_by_depth_unit *curr_by_depth_unit)
1142 {
1143 return curr_by_depth_unit->max_depth;
1144 }
1145 #endif
1146
was_pruned(const bool enable_pruning,fcs_collectible_state * ptr_state,fcs_soft_thread * const soft_thread,fcs_soft_dfs_stack_item * the_soft_dfs_info,fcs_kv_state * pass,fcs_derived_states_list * derived_list,fcs_moves_order * the_moves_list)1147 static inline bool was_pruned(const bool enable_pruning,
1148 fcs_collectible_state *ptr_state, fcs_soft_thread *const soft_thread,
1149 fcs_soft_dfs_stack_item *the_soft_dfs_info, fcs_kv_state *pass,
1150 fcs_derived_states_list *derived_list, fcs_moves_order *the_moves_list)
1151 {
1152 if (!fcs__should_state_be_pruned(enable_pruning, ptr_state))
1153 {
1154 return false;
1155 }
1156 fcs_collectible_state *const derived =
1157 fc_solve_sfs_raymond_prune(soft_thread, *pass);
1158 if (!derived)
1159 {
1160 return false;
1161 }
1162 the_soft_dfs_info->move_func_list_idx = the_moves_list->num;
1163 fc_solve_derived_states_list_add_state(derived_list, derived, 0);
1164 #ifndef FCS_ZERO_FREECELLS_MODE
1165 if (the_soft_dfs_info->derived_states_random_indexes_max_size < 1)
1166 {
1167 the_soft_dfs_info->derived_states_random_indexes_max_size = 1;
1168 the_soft_dfs_info->derived_states_random_indexes =
1169 SREALLOC(the_soft_dfs_info->derived_states_random_indexes,
1170 the_soft_dfs_info->derived_states_random_indexes_max_size);
1171 }
1172
1173 the_soft_dfs_info->derived_states_random_indexes[0].rating_with_index__idx =
1174 0;
1175 #endif
1176 return true;
1177 }
1178
dfs_run_moves(fcs_soft_thread * const soft_thread,fcs_soft_dfs_stack_item * const the_soft_dfs_info,const fcs_moves_group_kind init_shuffling_type,fcs_moves_order * the_moves_list,fcs_kv_state pass,fcs_derived_states_list * derived_list)1179 static inline fcs_moves_group_kind dfs_run_moves(
1180 fcs_soft_thread *const soft_thread,
1181 fcs_soft_dfs_stack_item *const the_soft_dfs_info,
1182 const fcs_moves_group_kind init_shuffling_type,
1183 fcs_moves_order *the_moves_list, fcs_kv_state pass,
1184 fcs_derived_states_list *derived_list)
1185 {
1186 if (the_soft_dfs_info->move_func_list_idx >= the_moves_list->num)
1187 {
1188 return init_shuffling_type;
1189 }
1190 #ifdef FCS_ZERO_FREECELLS_MODE
1191 const_AUTO(local_shuffling_type, init_shuffling_type);
1192 fc_solve_sfs_zerofc_0AB_atomic_all_moves(soft_thread, pass, derived_list);
1193 ++the_soft_dfs_info->move_func_list_idx;
1194 #else
1195 // Always do the first test
1196 const_AUTO(local_shuffling_type,
1197 the_moves_list->groups[the_soft_dfs_info->move_func_list_idx]
1198 .shuffling_type);
1199
1200 do
1201 {
1202 the_moves_list->groups[the_soft_dfs_info->move_func_list_idx]
1203 .move_funcs[the_soft_dfs_info->move_func_idx]
1204 .f(soft_thread, pass, derived_list);
1205
1206 // Move the counter to the next test
1207 if ((++the_soft_dfs_info->move_func_idx) ==
1208 the_moves_list->groups[the_soft_dfs_info->move_func_list_idx].num)
1209 {
1210 the_soft_dfs_info->move_func_list_idx++;
1211 the_soft_dfs_info->move_func_idx = 0;
1212 break;
1213 }
1214 } while ((local_shuffling_type != FCS_NO_SHUFFLING) ||
1215 (derived_list->num_states == 0));
1216 #endif
1217 return local_shuffling_type;
1218 }
1219
1220 #ifndef FCS_ZERO_FREECELLS_MODE
dfs_shuffle_states(fcs_soft_thread * const soft_thread,fcs_instance * const instance GCC_UNUSED,const size_t num_states,const fcs_moves_group_kind local_shuffling_type,fcs_rand_gen * const rand_gen,rating_with_index * const rand_array,const size_t orig_idx,fcs_moves_order * the_moves_list,fcs_derived_states_list_item * const derived_states,const fcs_state_weighting * const weighting)1221 static inline void dfs_shuffle_states(fcs_soft_thread *const soft_thread,
1222 fcs_instance *const instance GCC_UNUSED, const size_t num_states,
1223 const fcs_moves_group_kind local_shuffling_type,
1224 fcs_rand_gen *const rand_gen, rating_with_index *const rand_array,
1225 const size_t orig_idx, fcs_moves_order *the_moves_list,
1226 fcs_derived_states_list_item *const derived_states,
1227 const fcs_state_weighting *const weighting)
1228 {
1229 for (size_t i = 0; i < num_states; ++i)
1230 {
1231 rand_array[i].rating_with_index__idx = (int)i;
1232 }
1233 // Do not randomize/sort if there's only one derived
1234 // state or less, because in that case, there is nothing
1235 // to reorder.
1236 if (num_states <= 1)
1237 {
1238 return;
1239 }
1240 switch (local_shuffling_type)
1241 {
1242 case FCS_RAND: {
1243 for (size_t i = num_states - 1; i > 0; i--)
1244 {
1245 const typeof(i) j =
1246 ((size_t)fc_solve_rand_get_random_number(rand_gen) % (i + 1));
1247
1248 const_AUTO(swap_save, rand_array[i]);
1249 rand_array[i] = rand_array[j];
1250 rand_array[j] = swap_save;
1251 }
1252 }
1253 break;
1254
1255 case FCS_WEIGHTING:
1256 if (orig_idx < the_moves_list->num)
1257 {
1258 for (size_t i = 0; i < num_states; i++)
1259 {
1260 rand_array[i].rating = befs_rate_state(soft_thread, weighting,
1261 #ifdef FCS_RCS_STATES
1262 fc_solve_lookup_state_key_from_val(
1263 instance, derived_states[i].state_ptr),
1264 #else
1265 &(derived_states[i].state_ptr->s),
1266 #endif
1267 BEFS_MAX_DEPTH - calc_depth(derived_states[i].state_ptr));
1268 }
1269
1270 const_AUTO(end, rand_array + num_states);
1271 // Insertion-sort rand_array
1272 for (var_PTR(p, rand_array + 1); p < end; ++p)
1273 {
1274 var_AUTO(move, p);
1275 while (move > rand_array && move->rating < move[-1].rating)
1276 {
1277 const_AUTO(temp, *move);
1278 *move = move[-1];
1279 *(--move) = temp;
1280 }
1281 }
1282 }
1283 break;
1284
1285 case FCS_NO_SHUFFLING:
1286 break;
1287 case FCS_PERFORM_ALL_MOVE_FUNCS:
1288 break;
1289 }
1290 }
1291 #endif
1292
1293 // dfs_solve() is the event loop of the Random-DFS scan. DFS, which is
1294 // recursive in nature, is handled here without procedural recursion by using
1295 // some dedicated stacks for the traversal.
dfs_solve(fcs_soft_thread * const soft_thread)1296 static inline fc_solve_solve_process_ret_t dfs_solve(
1297 fcs_soft_thread *const soft_thread)
1298 {
1299 fcs_hard_thread *const hard_thread = soft_thread->hard_thread;
1300 fcs_instance *const instance = HT_INSTANCE(hard_thread);
1301 #ifndef FCS_ZERO_FREECELLS_MODE
1302 ssize_t by_depth_max_depth, by_depth_min_depth;
1303 #endif
1304 #if defined(FCS_WITH_DEPTH_FIELD) && \
1305 !defined(FCS_HARD_CODE_CALC_REAL_DEPTH_AS_FALSE)
1306 const bool calc_real_depth = fcs_get_calc_real_depth(instance);
1307 #endif
1308 #ifndef FCS_HARD_CODE_SCANS_SYNERGY_AS_TRUE
1309 const bool scans_synergy =
1310 STRUCT_QUERY_FLAG(instance, FCS_RUNTIME_SCANS_SYNERGY);
1311 #endif
1312
1313 const bool is_a_complete_scan =
1314 STRUCT_QUERY_FLAG(soft_thread, FCS_SOFT_THREAD_IS_A_COMPLETE_SCAN);
1315 const_AUTO(soft_thread_id, soft_thread->id);
1316 fcs_moves_order the_moves_list;
1317 fcs_moves_group_kind local_shuffling_type = FCS_NO_SHUFFLING;
1318 #ifndef FCS_DISABLE_NUM_STORED_STATES
1319 const_SLOT(effective_max_num_states_in_collection, instance);
1320 #endif
1321 FC__STACKS__SET_PARAMS();
1322
1323 #define DEPTH() (*depth_ptr)
1324 ssize_t *const depth_ptr = &(DFS_VAR(soft_thread, depth));
1325
1326 var_AUTO(
1327 the_soft_dfs_info, &(DFS_VAR(soft_thread, soft_dfs_info)[DEPTH()]));
1328
1329 ssize_t dfs_max_depth = DFS_VAR(soft_thread, dfs_max_depth);
1330 #ifndef FCS_ENABLE_PRUNE__R_TF__UNCOND
1331 const_SLOT(enable_pruning, soft_thread);
1332 #endif
1333
1334 DECLARE_STATE();
1335 PTR_STATE = the_soft_dfs_info->state;
1336 FCS_ASSIGN_STATE_KEY();
1337 fcs_derived_states_list *derived_list =
1338 &the_soft_dfs_info->derived_states_list;
1339 #ifndef FCS_ZERO_FREECELLS_MODE
1340 fcs_rand_gen *const rand_gen = &(DFS_VAR(soft_thread, rand_gen));
1341 #endif
1342 calculate_real_depth(calc_real_depth, PTR_STATE);
1343 #ifndef FCS_ZERO_FREECELLS_MODE
1344 const_AUTO(
1345 by_depth_units, DFS_VAR(soft_thread, moves_by_depth).by_depth_units);
1346 #else
1347 the_moves_list.num = 1;
1348 #endif
1349 TRACE0("Before depth loop");
1350 #ifndef FCS_ZERO_FREECELLS_MODE
1351 #define RECALC_BY_DEPTH_LIMITS() \
1352 { \
1353 by_depth_max_depth = get_depth(curr_by_depth_unit); \
1354 by_depth_min_depth = (curr_by_depth_unit == by_depth_units) \
1355 ? 0 \
1356 : get_depth(curr_by_depth_unit - 1); \
1357 the_moves_list = curr_by_depth_unit->move_funcs; \
1358 }
1359 #endif
1360
1361 fcs_iters_int *const instance_num_checked_states_ptr =
1362 &(instance->i__stats.num_checked_states);
1363 #ifndef FCS_SINGLE_HARD_THREAD
1364 fcs_iters_int *const hard_thread_num_checked_states_ptr =
1365 &(HT_FIELD(hard_thread, ht__num_checked_states));
1366 #endif
1367 const_AUTO(max_num_states, calc_ht_max_num_states(instance, hard_thread));
1368 #ifndef FCS_WITHOUT_ITER_HANDLER
1369 const_SLOT(debug_iter_output_func, instance);
1370 const_SLOT(debug_iter_output_context, instance);
1371 #endif
1372
1373 #ifndef FCS_ZERO_FREECELLS_MODE
1374 const moves_by_depth_unit *curr_by_depth_unit = by_depth_units;
1375 for (; (DEPTH() >= get_depth(curr_by_depth_unit)); ++curr_by_depth_unit)
1376 {
1377 }
1378 RECALC_BY_DEPTH_LIMITS();
1379 #endif
1380
1381 set_scan_visited(PTR_STATE, soft_thread_id);
1382 // The main loop. We exit out of it when DEPTH() is decremented below zero.
1383 while (1)
1384 {
1385 main_loop:
1386 // Increase the "maximal" depth if it is about to be exceeded.
1387 if (unlikely(DEPTH() + 1 >= dfs_max_depth))
1388 {
1389 increase_dfs_max_depth(soft_thread);
1390
1391 // Because the address of DFS_VAR(soft_thread, soft_dfs_info) may
1392 // be changed
1393 the_soft_dfs_info = &(DFS_VAR(soft_thread, soft_dfs_info)[DEPTH()]);
1394 dfs_max_depth = DFS_VAR(soft_thread, dfs_max_depth);
1395 // This too has to be re-synced
1396 derived_list = &the_soft_dfs_info->derived_states_list;
1397 }
1398
1399 TRACE0("Before current_state_index check");
1400 // All the resultant states in the last test conducted were covered
1401 if (the_soft_dfs_info->current_state_index == derived_list->num_states)
1402 {
1403 // Check if we already tried all the tests here.
1404 if (the_soft_dfs_info->move_func_list_idx == the_moves_list.num)
1405 {
1406 // Backtrack to the previous depth.
1407 if (is_a_complete_scan)
1408 {
1409 FCS_S_VISITED(PTR_STATE) |= FCS_VISITED_ALL_TESTS_DONE;
1410 MARK_AS_DEAD_END(PTR_STATE);
1411 }
1412
1413 // Set it now in case DEPTH() == 0 and we break
1414 if (unlikely(--DEPTH() < 0))
1415 {
1416 break;
1417 }
1418 --the_soft_dfs_info;
1419 derived_list = &the_soft_dfs_info->derived_states_list;
1420 PTR_STATE = the_soft_dfs_info->state;
1421 FCS_ASSIGN_STATE_KEY();
1422 soft_thread->num_vacant_freecells = count_num_vacant_freecells(
1423 LOCAL_FREECELLS_NUM, &FCS_SCANS_the_state);
1424 soft_thread->num_vacant_stacks = count_num_vacant_stacks(
1425 LOCAL_STACKS_NUM, &FCS_SCANS_the_state);
1426
1427 #ifndef FCS_ZERO_FREECELLS_MODE
1428 if (unlikely(DEPTH() < by_depth_min_depth))
1429 {
1430 curr_by_depth_unit--;
1431 RECALC_BY_DEPTH_LIMITS();
1432 }
1433 #endif
1434
1435 continue; // Just to make sure depth is not -1 now
1436 }
1437
1438 derived_list->num_states = 0;
1439
1440 TRACE0("Before iter_handler");
1441 // If this is the first test, then count the number of unoccupied
1442 // freecells and stacks and check if we are done.
1443 if ((the_soft_dfs_info->move_func_idx == 0) &&
1444 (the_soft_dfs_info->move_func_list_idx == 0))
1445 {
1446 #ifndef FCS_WITHOUT_ITER_HANDLER
1447 TRACE0("In iter_handler");
1448
1449 if (debug_iter_output_func)
1450 {
1451 debug_iter_output_func(debug_iter_output_context,
1452 (fcs_int_limit_t) * (instance_num_checked_states_ptr),
1453 (int)DEPTH(), (void *)instance, &pass,
1454 #ifdef FCS_WITHOUT_VISITED_ITER
1455 0
1456 #else
1457 ((DEPTH() == 0) ? 0
1458 : (fcs_int_limit_t)FCS_S_VISITED_ITER(
1459 DFS_VAR(soft_thread,
1460 soft_dfs_info)[DEPTH() - 1]
1461 .state))
1462 #endif
1463 );
1464 }
1465 #endif
1466 if (!was_pruned(enable_pruning, PTR_STATE, soft_thread,
1467 the_soft_dfs_info, &pass, derived_list,
1468 &the_moves_list))
1469 {
1470 const fcs_game_limit num_vacant_freecells =
1471 count_num_vacant_freecells(
1472 LOCAL_FREECELLS_NUM, &FCS_SCANS_the_state);
1473 const fcs_game_limit num_vacant_stacks =
1474 count_num_vacant_stacks(
1475 LOCAL_STACKS_NUM, &FCS_SCANS_the_state);
1476 // Check if we have reached the empty state
1477 if (unlikely((num_vacant_stacks == LOCAL_STACKS_NUM) &&
1478 (num_vacant_freecells == LOCAL_FREECELLS_NUM)))
1479 {
1480 FCS_SET_final_state();
1481 BUMP_NUM_CHECKED_STATES();
1482 TRACE0("Returning FCS_STATE_WAS_SOLVED");
1483 return FCS_STATE_WAS_SOLVED;
1484 }
1485 // Cache num_vacant_freecells and num_vacant_stacks.
1486 soft_thread->num_vacant_freecells = num_vacant_freecells;
1487 soft_thread->num_vacant_stacks = num_vacant_stacks;
1488 fc_solve__calc_positions_by_rank_data(soft_thread,
1489 &FCS_SCANS_the_state,
1490 (the_soft_dfs_info->positions_by_rank));
1491 }
1492 }
1493
1494 TRACE0("After iter_handler");
1495 #ifndef FCS_ZERO_FREECELLS_MODE
1496 const_AUTO(orig_idx, the_soft_dfs_info->move_func_list_idx);
1497 const fcs_state_weighting *const weighting =
1498 &(the_moves_list.groups[orig_idx].weighting);
1499 #endif
1500
1501 local_shuffling_type = dfs_run_moves(soft_thread, the_soft_dfs_info,
1502 local_shuffling_type, &the_moves_list, pass, derived_list);
1503 #ifndef FCS_ZERO_FREECELLS_MODE
1504 const_AUTO(num_states, derived_list->num_states);
1505 if (num_states >
1506 the_soft_dfs_info->derived_states_random_indexes_max_size)
1507 {
1508 the_soft_dfs_info->derived_states_random_indexes_max_size =
1509 num_states;
1510 the_soft_dfs_info->derived_states_random_indexes = SREALLOC(
1511 the_soft_dfs_info->derived_states_random_indexes,
1512 the_soft_dfs_info->derived_states_random_indexes_max_size);
1513 }
1514 dfs_shuffle_states(soft_thread, instance, num_states,
1515 local_shuffling_type, rand_gen,
1516 the_soft_dfs_info->derived_states_random_indexes, orig_idx,
1517 &the_moves_list, derived_list->states, weighting);
1518 #endif
1519 // We just performed a test, so the index of the first state that
1520 // ought to be checked in this depth is 0.
1521 the_soft_dfs_info->current_state_index = 0;
1522 }
1523
1524 const_AUTO(num_states, derived_list->num_states);
1525 fcs_derived_states_list_item *const derived_states =
1526 derived_list->states;
1527 var_AUTO(state_idx, the_soft_dfs_info->current_state_index - 1);
1528 #ifndef FCS_ZERO_FREECELLS_MODE
1529 const rating_with_index *rand_int_ptr =
1530 the_soft_dfs_info->derived_states_random_indexes + state_idx;
1531 #endif
1532
1533 while (++state_idx < num_states)
1534 {
1535 fcs_collectible_state *const single_derived_state = derived_states[
1536 #ifndef FCS_ZERO_FREECELLS_MODE
1537 (*(++rand_int_ptr)).rating_with_index__idx
1538 #else
1539 state_idx
1540 #endif
1541 ]
1542 .state_ptr;
1543
1544 VERIFY_PTR_STATE_AND_DERIVED_TRACE0("Verify [Before BUMP]");
1545
1546 if ((!fcs__is_state_a_dead_end(single_derived_state)) &&
1547 (!is_scan_visited(single_derived_state, soft_thread_id)))
1548 {
1549 BUMP_NUM_CHECKED_STATES();
1550 VERIFY_PTR_STATE_AND_DERIVED_TRACE0("Verify [After BUMP]");
1551 set_scan_visited(single_derived_state, soft_thread_id);
1552 #ifndef FCS_WITHOUT_VISITED_ITER
1553 FCS_S_VISITED_ITER(single_derived_state) =
1554 instance->i__stats.num_checked_states;
1555 #endif
1556 VERIFY_PTR_STATE_AND_DERIVED_TRACE0("Verify [aft set_visit]");
1557 // I'm using current_state_indexes[depth]-1 because we already
1558 // increased it by one, so now it refers to the next state.
1559 #ifndef FCS_ZERO_FREECELLS_MODE
1560 if (unlikely(++DEPTH() >= by_depth_max_depth))
1561 {
1562 curr_by_depth_unit++;
1563 RECALC_BY_DEPTH_LIMITS();
1564 }
1565 #else
1566 ++DEPTH();
1567 #endif
1568 the_soft_dfs_info->current_state_index = state_idx;
1569 ++the_soft_dfs_info;
1570 the_soft_dfs_info->state = PTR_STATE = single_derived_state;
1571 FCS_ASSIGN_STATE_KEY();
1572 VERIFY_PTR_STATE_AND_DERIVED_TRACE0("Verify after recurse");
1573
1574 the_soft_dfs_info->move_func_list_idx = 0;
1575 the_soft_dfs_info->move_func_idx = 0;
1576 the_soft_dfs_info->current_state_index = 0;
1577 derived_list = &the_soft_dfs_info->derived_states_list;
1578 derived_list->num_states = 0;
1579
1580 calculate_real_depth(calc_real_depth, PTR_STATE);
1581
1582 #ifndef FCS_DISABLE_NUM_STORED_STATES
1583 #ifndef FCS_WITHOUT_TRIM_MAX_STORED_STATES
1584 if (instance->active_num_states_in_collection >=
1585 instance->effective_trim_states_in_collection_from)
1586 {
1587 free_states(instance);
1588 }
1589 #endif
1590 #endif
1591 if (check_if_limits_exceeded())
1592 {
1593 TRACE0("Returning FCS_STATE_SUSPEND_PROCESS (inside "
1594 "current_state_index)");
1595 FCS_SET_final_state();
1596 return FCS_STATE_SUSPEND_PROCESS;
1597 }
1598 goto main_loop;
1599 }
1600 }
1601 the_soft_dfs_info->current_state_index = num_states;
1602 }
1603 // We need to bump the number of iterations so it will be ready with
1604 // a fresh iterations number for the next scan that takes place.
1605 BUMP_NUM_CHECKED_STATES();
1606 DEPTH() = -1;
1607
1608 return FCS_STATE_IS_NOT_SOLVEABLE;
1609 }
1610
init_dfs(fcs_soft_thread * const soft_thread)1611 static inline void init_dfs(fcs_soft_thread *const soft_thread)
1612 {
1613 fcs_instance *const instance = fcs_st_instance(soft_thread);
1614 // Allocate some space for the states at depth 0.
1615 DFS_VAR(soft_thread, depth) = 0;
1616 increase_dfs_max_depth(soft_thread);
1617 DFS_VAR(soft_thread, soft_dfs_info)
1618 [0].state = FCS_STATE_keyval_pair_to_collectible(&instance->state_copy);
1619 fc_solve_rand_init(
1620 &(DFS_VAR(soft_thread, rand_gen)), DFS_VAR(soft_thread, rand_seed));
1621
1622 #ifndef FCS_ZERO_FREECELLS_MODE
1623
1624 if (!DFS_VAR(soft_thread, moves_by_depth).by_depth_units)
1625 {
1626 const_SLOT(master_to_randomize, soft_thread);
1627 fcs_moves_by_depth_array *const arr_ptr =
1628 &(DFS_VAR(soft_thread, moves_by_depth));
1629 arr_ptr->by_depth_units = SMALLOC(arr_ptr->by_depth_units,
1630 (arr_ptr->num_units = soft_thread->by_depth_moves_order.num));
1631
1632 const fcs_by_depth_moves_order *const by_depth_moves_order =
1633 soft_thread->by_depth_moves_order.by_depth_moves;
1634
1635 var_AUTO(unit, arr_ptr->by_depth_units);
1636 const_AUTO(depth_num, soft_thread->by_depth_moves_order.num);
1637 for (size_t depth_idx = 0; depth_idx < depth_num; ++depth_idx, ++unit)
1638 {
1639 unit->max_depth = by_depth_moves_order[depth_idx].max_depth;
1640
1641 fcs_moves_group *const tests_order_groups =
1642 by_depth_moves_order[depth_idx].moves_order.groups;
1643
1644 const_AUTO(tests_order_num,
1645 by_depth_moves_order[depth_idx].moves_order.num);
1646
1647 const_AUTO(moves_list_of_lists, &(unit->move_funcs));
1648
1649 *moves_list_of_lists = (typeof(*moves_list_of_lists)){
1650 .num = 0,
1651 .groups = SMALLOC(moves_list_of_lists->groups, tests_order_num),
1652 };
1653
1654 for (size_t group_idx = 0; group_idx < tests_order_num; ++group_idx)
1655 {
1656 size_t num = 0;
1657 fcs_move_func *tests_list = NULL;
1658 add_to_move_funcs_list(&tests_list, &num,
1659 tests_order_groups[group_idx].move_funcs,
1660 tests_order_groups[group_idx].num);
1661 const_AUTO(tests_list_struct_ptr,
1662 &(moves_list_of_lists->groups[moves_list_of_lists->num++]));
1663
1664 const fcs_moves_group_kind shuffling_type =
1665 (master_to_randomize
1666 ? tests_order_groups[group_idx].shuffling_type
1667 : FCS_NO_SHUFFLING);
1668 *tests_list_struct_ptr = (typeof(*tests_list_struct_ptr)){
1669 .move_funcs = tests_list,
1670 .num = num,
1671 .shuffling_type = shuffling_type,
1672 };
1673
1674 if (shuffling_type == FCS_WEIGHTING)
1675 {
1676 tests_list_struct_ptr->weighting =
1677 tests_order_groups[group_idx].weighting;
1678
1679 fc_solve_initialize_befs_rater(
1680 soft_thread, &(tests_list_struct_ptr->weighting));
1681 }
1682 }
1683
1684 moves_list_of_lists->groups =
1685 SREALLOC(moves_list_of_lists->groups, moves_list_of_lists->num);
1686 }
1687 }
1688 #endif
1689 }
1690
1691 // Switch to the next soft thread in the hard thread, since we are going to call
1692 // continue and this is a while loop
switch_to_next_soft_thread(fcs_hard_thread * const hard_thread,const fastest_type_for_num_soft_threads__unsigned num_soft_threads,const fcs_soft_thread * const soft_threads,const fc_solve_prelude_item * const prelude,const size_t prelude_num_items,uint_fast32_t * const st_idx_ptr)1693 static inline void switch_to_next_soft_thread(
1694 fcs_hard_thread *const hard_thread,
1695 const fastest_type_for_num_soft_threads__unsigned num_soft_threads,
1696 const fcs_soft_thread *const soft_threads,
1697 const fc_solve_prelude_item *const prelude, const size_t prelude_num_items,
1698 uint_fast32_t *const st_idx_ptr)
1699 {
1700 if (HT_FIELD(hard_thread, prelude_idx) < prelude_num_items)
1701 {
1702 set_next_prelude_item(hard_thread, prelude, st_idx_ptr);
1703 }
1704 else
1705 {
1706 const fastest_type_for_num_soft_threads__unsigned next_st_idx =
1707 ((1 + (*st_idx_ptr)) % num_soft_threads);
1708 set_next_soft_thread(hard_thread, next_st_idx,
1709 soft_threads[next_st_idx].checked_states_step, st_idx_ptr);
1710 }
1711 }
1712
1713 #ifndef FCS_DISABLE_PATSOLVE
do_patsolve(fcs_soft_thread * const soft_thread)1714 static inline fc_solve_solve_process_ret_t do_patsolve(
1715 fcs_soft_thread *const soft_thread)
1716 {
1717 const_SLOT(hard_thread, soft_thread);
1718 const_SLOT(pats_scan, soft_thread);
1719 const_AUTO(start_from, pats_scan->num_checked_states);
1720
1721 pats_scan->max_num_checked_states =
1722 start_from +
1723 ((unsigned long)HT_FIELD(hard_thread, ht__max_num_checked_states) -
1724 (unsigned long)NUM_CHECKED_STATES);
1725 pats_scan->status = FCS_PATS__NOSOL;
1726 fc_solve_pats__do_it(pats_scan);
1727
1728 const_AUTO(after_scan_delta, pats_scan->num_checked_states - start_from);
1729 #ifndef FCS_SINGLE_HARD_THREAD
1730 HT_FIELD(hard_thread, ht__num_checked_states) += after_scan_delta;
1731 #endif
1732 HT_INSTANCE(hard_thread)->i__stats.num_checked_states += after_scan_delta;
1733
1734 switch (pats_scan->status)
1735 {
1736 case FCS_PATS__WIN:
1737 return FCS_STATE_WAS_SOLVED;
1738
1739 case FCS_PATS__NOSOL:
1740 return FCS_STATE_IS_NOT_SOLVEABLE;
1741
1742 case FCS_PATS__FAIL:
1743 return FCS_STATE_SUSPEND_PROCESS;
1744
1745 #ifndef __clang__
1746 default:
1747 __builtin_unreachable();
1748 #endif
1749 }
1750 }
1751 #endif
1752
solve(fcs_soft_thread * const soft_thread)1753 static inline fc_solve_solve_process_ret_t solve(
1754 fcs_soft_thread *const soft_thread)
1755 {
1756 switch (soft_thread->super_method_type)
1757 {
1758 case FCS_SUPER_METHOD_DFS:
1759 return dfs_solve(soft_thread);
1760
1761 case FCS_SUPER_METHOD_BEFS_BRFS:
1762 return fc_solve_befs_or_bfs_do_solve(soft_thread);
1763
1764 #ifndef FCS_DISABLE_PATSOLVE
1765 case FCS_SUPER_METHOD_PATSOLVE:
1766 return do_patsolve(soft_thread);
1767 #endif
1768
1769 #ifndef __clang__
1770 default:
1771 __builtin_unreachable();
1772 #endif
1773 }
1774 }
1775
1776 // instance__check_exceeded_stats() cannot be an inline function because if
1777 // it is, the code becomes considerably slower (at least on gcc-5.4.0 on x86-64
1778 // Linux).
1779 #ifdef FCS_WITHOUT_MAX_NUM_STATES
1780 #define instance__check_exceeded_stats(instance) false
1781 #else
1782 #ifdef FCS_DISABLE_NUM_STORED_STATES
1783 #define instance_check_exceeded__num_states(instance)
1784 #else
1785 #define instance_check_exceeded__num_states(instance) \
1786 || (instance->i__stats.num_states_in_collection >= \
1787 instance->effective_max_num_states_in_collection)
1788 #endif
1789 #define instance__check_exceeded_stats(instance) \
1790 ((ret == FCS_STATE_SUSPEND_PROCESS) && \
1791 ((instance->i__stats.num_checked_states >= \
1792 instance->effective_max_num_checked_states) \
1793 instance_check_exceeded__num_states(instance)))
1794 #endif
1795
run_hard_thread(fcs_hard_thread * const hard_thread)1796 static inline fc_solve_solve_process_ret_t run_hard_thread(
1797 fcs_hard_thread *const hard_thread)
1798 {
1799 const size_t prelude_num_items = HT_FIELD(hard_thread, prelude_num_items);
1800 #ifdef FCS_SINGLE_HARD_THREAD
1801 #define instance hard_thread
1802 #else
1803 fcs_instance *const instance = hard_thread->instance;
1804 #endif
1805 uint_fast32_t *const st_idx_ptr = &(HT_FIELD(hard_thread, st_idx));
1806 // Again, making sure that not all of the soft_threads in this
1807 // hard thread finished.
1808
1809 fc_solve_solve_process_ret_t ret = FCS_STATE_SUSPEND_PROCESS;
1810 const_AUTO(num_soft_threads, HT_FIELD(hard_thread, num_soft_threads));
1811 const fc_solve_prelude_item *const prelude = HT_FIELD(hard_thread, prelude);
1812 fcs_soft_thread *const soft_threads = HT_FIELD(hard_thread, soft_threads);
1813
1814 while (HT_FIELD(hard_thread, num_soft_threads_finished) < num_soft_threads)
1815 {
1816 fcs_soft_thread *const soft_thread = &(soft_threads[*st_idx_ptr]);
1817 // Move to the next thread if it's already finished
1818 if (STRUCT_QUERY_FLAG(soft_thread, FCS_SOFT_THREAD_IS_FINISHED))
1819 {
1820 switch_to_next_soft_thread(hard_thread, num_soft_threads,
1821 soft_threads, prelude, prelude_num_items, st_idx_ptr);
1822
1823 continue;
1824 }
1825
1826 if (!STRUCT_QUERY_FLAG(soft_thread, FCS_SOFT_THREAD_INITIALIZED))
1827 {
1828 init_dfs(soft_thread);
1829 fc_solve_soft_thread_init_befs_or_bfs(soft_thread);
1830
1831 #ifndef FCS_DISABLE_PATSOLVE
1832 const_SLOT(pats_scan, soft_thread);
1833 if (pats_scan)
1834 {
1835 fc_solve_pats__init_buckets(pats_scan);
1836 fc_solve_pats__init_clusters(pats_scan);
1837
1838 pats_scan->current_pos.s =
1839 instance->initial_non_canonized_state->s;
1840 #ifdef INDIRECT_STACK_STATES
1841 memset(pats_scan->current_pos.indirect_stacks_buffer, '\0',
1842 sizeof(pats_scan->current_pos.indirect_stacks_buffer));
1843 #ifndef HARD_CODED_NUM_STACKS
1844 const size_t stacks_num = INSTANCE_STACKS_NUM;
1845 #endif
1846 for (size_t i = 0; i < STACKS_NUM__VAL; ++i)
1847 {
1848 var_AUTO(src_col,
1849 fcs_state_get_col(pats_scan->current_pos.s, i));
1850 fcs_card *dest = &(
1851 pats_scan->current_pos.indirect_stacks_buffer[i << 6]);
1852 memmove(dest, src_col, fcs_col_len(src_col) + 1);
1853 fcs_state_get_col(pats_scan->current_pos.s, i) = dest;
1854 }
1855 #endif
1856 fc_solve_pats__initialize_solving_process(pats_scan);
1857 }
1858 #endif
1859 STRUCT_TURN_ON_FLAG(soft_thread, FCS_SOFT_THREAD_INITIALIZED);
1860 }
1861 ret = solve(soft_thread);
1862 // We use <= instead of == because it is possible that
1863 // there will be a few more iterations than what this
1864 // thread was allocated, due to the fact that
1865 // check_and_add_state is only called by the test
1866 // functions.
1867 //
1868 // It's a kludge, but it works.
1869 if (NUM_CHECKED_STATES >=
1870 HT_FIELD(hard_thread, ht__max_num_checked_states))
1871 {
1872 switch_to_next_soft_thread(hard_thread, num_soft_threads,
1873 soft_threads, prelude, prelude_num_items, st_idx_ptr);
1874 }
1875
1876 // It this thread indicated that the scan was finished,
1877 // disable the thread or even stop searching altogether.
1878 if (ret == FCS_STATE_IS_NOT_SOLVEABLE)
1879 {
1880 STRUCT_TURN_ON_FLAG(soft_thread, FCS_SOFT_THREAD_IS_FINISHED);
1881 if (++(HT_FIELD(hard_thread, num_soft_threads_finished)) ==
1882 num_soft_threads)
1883 {
1884 ++instance->finished_hard_threads_count;
1885 }
1886 // Check if this thread is a complete scan and if so,
1887 // terminate the search. Note that if the scans synergy is set,
1888 // then we may still need to continue running the other threads
1889 // which may have blocked some positions / states in the graph.
1890 #ifndef FCS_HARD_CODE_SCANS_SYNERGY_AS_TRUE
1891 if (STRUCT_QUERY_FLAG(
1892 soft_thread, FCS_SOFT_THREAD_IS_A_COMPLETE_SCAN) &&
1893 (!STRUCT_QUERY_FLAG(instance, FCS_RUNTIME_SCANS_SYNERGY)))
1894 {
1895 return FCS_STATE_IS_NOT_SOLVEABLE;
1896 }
1897 else
1898 #endif
1899 {
1900 // Else, make sure ret is something more sensible
1901 ret = FCS_STATE_SUSPEND_PROCESS;
1902 }
1903 }
1904
1905 const bool was_solved = (ret == FCS_STATE_WAS_SOLVED);
1906 if (was_solved || instance__check_exceeded_stats(instance))
1907 {
1908 #if (defined(FCS_WITH_MOVES) && (!defined(FCS_DISABLE_PATSOLVE)))
1909 instance->solving_soft_thread = soft_thread;
1910 #endif
1911 return ret;
1912 }
1913 }
1914
1915 return ret;
1916 }
1917 #ifdef FCS_SINGLE_HARD_THREAD
1918 #undef instance
1919 #endif
1920
1921 // Resume a solution process that was stopped in the middle
resume_instance(fcs_instance * const instance)1922 static inline fc_solve_solve_process_ret_t resume_instance(
1923 fcs_instance *const instance)
1924 {
1925 fc_solve_solve_process_ret_t ret = FCS_STATE_SUSPEND_PROCESS;
1926 // If the optimization thread is defined, it means we are in the
1927 // optimization phase of the total scan. In that case, just call
1928 // its scanning function.
1929 //
1930 // Else, proceed with the normal total scan.
1931 #ifdef FCS_WITH_MOVES
1932 if (STRUCT_QUERY_FLAG(instance, FCS_RUNTIME_IN_OPTIMIZATION_THREAD))
1933 {
1934 ret = fc_solve_befs_or_bfs_do_solve(
1935 #ifdef FCS_SINGLE_HARD_THREAD
1936 &(instance->optimization_soft_thread)
1937 #else
1938 &(instance->optimization_thread->soft_threads[0])
1939 #endif
1940 );
1941 }
1942 else
1943 #endif
1944 {
1945 #ifdef FCS_SINGLE_HARD_THREAD
1946 #define hard_thread instance
1947 #define NUM_HARD_THREADS() 1
1948 #else
1949 fcs_hard_thread *const end_of_hard_threads =
1950 instance->hard_threads + instance->num_hard_threads;
1951
1952 fcs_hard_thread *hard_thread = instance->current_hard_thread;
1953 #define NUM_HARD_THREADS() (instance->num_hard_threads)
1954 #endif
1955 // instance->finished_hard_threads_count signals to us that
1956 // all the incomplete soft threads terminated. It is necessary
1957 // in case the scan only contains incomplete threads.
1958 //
1959 // I.e: 01235 and 01246, where no thread contains all tests.
1960 while (instance->finished_hard_threads_count < NUM_HARD_THREADS())
1961 {
1962 // A loop on the hard threads.
1963 // Note that we do not initialize instance->ht_idx because:
1964 // 1. It is initialized before the first call to this function.
1965 // 2. It is reset to zero below.
1966 #ifndef FCS_SINGLE_HARD_THREAD
1967 for (; hard_thread < end_of_hard_threads; ++hard_thread)
1968 #endif
1969 {
1970 ret = run_hard_thread(hard_thread);
1971 if ((ret == FCS_STATE_IS_NOT_SOLVEABLE) ||
1972 (ret == FCS_STATE_WAS_SOLVED) ||
1973 instance__check_exceeded_stats(instance))
1974 {
1975 goto end_of_hard_threads_loop;
1976 }
1977 }
1978 #ifndef FCS_SINGLE_HARD_THREAD
1979 hard_thread = instance->hard_threads;
1980 #endif
1981 }
1982
1983 end_of_hard_threads_loop:
1984 #ifndef FCS_SINGLE_HARD_THREAD
1985 instance->current_hard_thread = hard_thread;
1986 #endif
1987
1988 // If all the incomplete scans finished, then terminate.
1989 if (instance->finished_hard_threads_count == NUM_HARD_THREADS())
1990 {
1991 ret = FCS_STATE_IS_NOT_SOLVEABLE;
1992 }
1993 }
1994 #ifdef FCS_WITH_MOVES
1995 // Call optimize_solution only once. Make sure that if it has already
1996 // run - we retain the old ret.
1997 if (ret == FCS_STATE_WAS_SOLVED &&
1998 STRUCT_QUERY_FLAG(instance, FCS_RUNTIME_OPTIMIZE_SOLUTION_PATH) &&
1999 !STRUCT_QUERY_FLAG(instance, FCS_RUNTIME_IN_OPTIMIZATION_THREAD))
2000 {
2001 ret = optimize_solution(instance);
2002 }
2003 #endif
2004 return ret;
2005 }
2006 #ifdef FCS_SINGLE_HARD_THREAD
2007 #undef hard_thread
2008 #endif
2009
2010 // A flare is an alternative scan algorithm to be tried. All flares in
2011 // a single instance are being evaluated and then one picks the shortest
2012 // solution out of all of them. (see fc-solve/docs/flares-functional-spec.txt )
2013 typedef struct
2014 {
2015 fcs_instance obj;
2016 #ifndef FCS_WITHOUT_MAX_NUM_STATES
2017 fc_solve_solve_process_ret_t ret_code;
2018 #define SET_flare_ret(flare, val) ((flare)->ret_code = (val))
2019 #elif defined(FCS_WITH_MOVES)
2020 #define SET_flare_ret(flare, val) (val)
2021 #endif
2022 // Whether the instance is ready to be input with (i.e: was recycled
2023 // already.)
2024 bool instance_is_ready;
2025 #ifdef FCS_WITH_FLARES
2026 char name[30];
2027 #endif
2028 #ifdef FCS_WITH_MOVES
2029 uint_fast32_t next_move_idx;
2030 fcs_moves_sequence_t moves_seq;
2031 #endif
2032 #ifndef FCS_WITHOUT_FC_PRO_MOVES_COUNT
2033 fcs_moves_processed fc_pro_moves;
2034 #endif
2035 fcs_stats obj_stats;
2036 #if defined(FCS_WITH_MOVES)
2037 bool was_solution_traced;
2038 #endif
2039 #ifdef FCS_WITH_MOVES
2040 fcs_state_locs_struct trace_solution_state_locs;
2041 #endif
2042 } flare_item;
2043
2044 #ifdef FCS_WITH_FLARES
2045 typedef enum
2046 {
2047 FLARES_PLAN_RUN_INDEFINITELY,
2048 FLARES_PLAN_RUN_COUNT_ITERS,
2049 FLARES_PLAN_CHECKPOINT,
2050 } flares_plan_type;
2051
2052 #ifndef FCS_WITHOUT_FC_PRO_MOVES_COUNT
2053 typedef enum
2054 {
2055 FLARES_CHOICE_FC_SOLVE_SOLUTION_LEN,
2056 FLARES_CHOICE_FCPRO_SOLUTION_LEN
2057 } flares_choice_type;
2058 #endif
2059
2060 typedef fcs_int_limit_t flare_iters_quota;
2061
normalize_iters_quota(const flare_iters_quota i)2062 static inline flare_iters_quota normalize_iters_quota(const flare_iters_quota i)
2063 {
2064 return max(i, 0);
2065 }
2066
2067 typedef struct
2068 {
2069 flare_item *flare;
2070 flares_plan_type type;
2071 flare_iters_quota remaining_quota, initial_quota;
2072 int_fast32_t count_iters;
2073 } flares_plan_item;
2074
2075 #endif
2076
2077 typedef struct
2078 {
2079 #ifdef FCS_WITH_FLARES
2080 flare_item *flares, *end_of_flares, *minimal_flare, *intract_minimal_flare;
2081 flares_plan_item *plan;
2082 size_t num_plan_items, current_plan_item_idx;
2083 char *flares_plan_string;
2084 // The default flares_plan_compiled is "False", which means that the
2085 // flares_plan_string was set and needs to be processed. Once
2086 // the compile function is called, it is set to "True" and it is set
2087 // to "False" if the flares_plan_string is set to a different value.
2088 //
2089 // Upon starting to run, one checks if flares_plan_compiled is false
2090 // and if so, compiles the flares plan, and sets the flares_plan_compiled
2091 // string to true.
2092 bool flares_plan_compiled;
2093 bool all_plan_items_finished_so_far;
2094 #else
2095 flare_item single_flare;
2096 #endif
2097 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
2098 fcs_int_limit_t limit;
2099 #endif
2100 } fcs_instance_item;
2101
2102 typedef struct
2103 {
2104 #ifdef FCS_WITH_NI
2105 // This is a list of several consecutive instances that are run
2106 // one after the other in case the previous ones could not solve
2107 // the board
2108 fcs_instance_item *current_instance, *instances_list,
2109 *end_of_instances_list;
2110 #else
2111 fcs_instance_item single_inst;
2112 #endif
2113 #ifndef FCS_WITHOUT_MAX_NUM_STATES
2114 // The global (sequence-wide) limit of the iterations. Used
2115 // by limit_iterations() and friends
2116 fcs_int_limit_t current_iterations_limit;
2117 fcs_iters_int effective_current_iterations_limit;
2118 fcs_int_limit_t current_soft_iterations_limit;
2119 #endif
2120 fcs_stats iterations_board_started_at;
2121 // The number of iterations that the current instance started solving from.
2122 fcs_stats init_num_checked_states;
2123 // A pointer to the currently active flare out of the sequence
2124 #if defined(FCS_WITH_NI) || defined(FCS_WITH_FLARES)
2125 #define ACTIVE_FLARE(user) ((user)->active_flare)
2126 #define SET_ACTIVE_FLARE(user, flare) ((user)->active_flare = (flare))
2127 flare_item *active_flare;
2128 #else
2129 #define ACTIVE_FLARE(user) (&((user)->single_inst.single_flare))
2130 #define SET_ACTIVE_FLARE(user, flare)
2131 #endif
2132 #define OBJ_STATS(user) (ACTIVE_FLARE(user)->obj_stats)
2133 fcs_state_keyval_pair state;
2134 #ifdef FCS_WITH_MOVES
2135 fcs_state_keyval_pair running_state;
2136 #endif
2137 #if defined(FCS_WITH_FLARES) || !defined(FCS_DISABLE_PATSOLVE)
2138 fcs_state_keyval_pair initial_non_canonized_state;
2139 #endif
2140 #ifdef FCS_WITH_MOVES
2141 fcs_state_locs_struct state_locs;
2142 fcs_state_locs_struct initial_state_locs;
2143 fc_solve_solve_process_ret_t ret_code;
2144 #define SET_user_ret(user, val) ((user)->ret_code = (val))
2145 #else
2146 #define SET_user_ret(user, val) (val)
2147 #endif
2148 #ifdef FCS_WITH_NI
2149 bool all_instances_were_suspended;
2150 #endif
2151 #ifdef FCS_WITH_ERROR_STRS
2152 state_validity_ret state_validity_ret;
2153 fcs_card state_validity_card;
2154 #endif
2155 #ifndef FCS_WITHOUT_ITER_HANDLER
2156 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
2157 freecell_solver_user_iter_handler_t iter_handler;
2158 #endif
2159 freecell_solver_user_long_iter_handler_t long_iter_handler;
2160 void *iter_handler_context;
2161 #endif
2162 #ifdef FCS_WITH_FLARES
2163 #ifndef FCS_WITHOUT_FC_PRO_MOVES_COUNT
2164 flares_choice_type flares_choice;
2165 #endif
2166 double flares_iters_factor;
2167 #endif
2168 fcs_soft_thread *soft_thread;
2169 DECLARE_IND_BUF_T(indirect_stacks_buffer)
2170 #define MAX_STATE_STRING_COPY_LEN 2048
2171 char state_string_copy[MAX_STATE_STRING_COPY_LEN];
2172 FCS_ON_NOT_FC_ONLY(fcs_preset common_preset;)
2173 FCS__DECL_ERR_BUF(error_string)
2174 meta_allocator meta_alloc;
2175 #ifndef FCS_USE_PRECOMPILED_CMD_LINE_THEME
2176 char *unrecognized_cmd_line_options[1];
2177 #endif
2178 } fcs_user;
2179
user_obj(fcs_user * const user)2180 static inline fcs_instance *user_obj(fcs_user *const user)
2181 {
2182 return &(ACTIVE_FLARE(user)->obj);
2183 }
2184
active_obj(void * const api_instance)2185 static inline fcs_instance *active_obj(void *const api_instance)
2186 {
2187 return user_obj((fcs_user *)api_instance);
2188 }
2189
curr_inst(fcs_user * const user)2190 static inline fcs_instance_item *curr_inst(fcs_user *const user)
2191 {
2192 #ifdef FCS_WITH_NI
2193 return user->current_instance;
2194 #else
2195 return &user->single_inst;
2196 #endif
2197 }
2198
2199 #ifdef FCS_WITH_NI
2200 #define INSTANCES_LOOP_START() \
2201 { \
2202 const_SLOT(end_of_instances_list, user); \
2203 for (fcs_instance_item *instance_item = user->instances_list; \
2204 instance_item < end_of_instances_list; ++instance_item) \
2205 {
2206 #else
2207 #define INSTANCES_LOOP_START() \
2208 do \
2209 { \
2210 const_AUTO(instance_item, curr_inst(user));
2211 #endif
2212
2213 #ifdef FCS_WITH_FLARES
2214
2215 #define INSTANCE_ITEM_FLARES_LOOP_START() \
2216 const flare_item *const end_of_flares = instance_item->end_of_flares; \
2217 for (flare_item *flare = instance_item->flares; flare < end_of_flares; \
2218 ++flare) \
2219 {
2220 #else
2221
2222 #define INSTANCE_ITEM_FLARES_LOOP_START() \
2223 flare_item *const flare = &(instance_item->single_flare); \
2224 {
2225 #endif
2226
2227 #define INSTANCE_ITEM_FLARES_LOOP_END() }
2228
2229 #ifdef FCS_WITH_NI
2230 #define INSTANCES_LOOP_END() \
2231 } \
2232 }
2233 #else
2234 #define INSTANCES_LOOP_END() \
2235 } \
2236 while (0) \
2237 ;
2238 #endif
2239
2240 #define FLARES_LOOP_START() \
2241 INSTANCES_LOOP_START() \
2242 INSTANCE_ITEM_FLARES_LOOP_START()
2243
2244 #ifdef FCS_WITH_FLARES
2245 #define FLARE_INLINE
2246 #else
2247 #define FLARE_INLINE inline
2248 #endif
2249
get_num_times_long(fcs_user * const user)2250 static inline fcs_iters_int get_num_times_long(fcs_user *const user)
2251 {
2252 return user->iterations_board_started_at.num_checked_states +
2253 OBJ_STATS(user).num_checked_states -
2254 user->init_num_checked_states.num_checked_states;
2255 }
2256
2257 #ifndef FCS_FREECELL_ONLY
calc_variant_suit_mask_and_desired_suit_value(fcs_instance * const instance GCC_UNUSED)2258 static inline void calc_variant_suit_mask_and_desired_suit_value(
2259 fcs_instance *const instance GCC_UNUSED)
2260 {
2261 #ifndef FCS_DISABLE_PATSOLVE
2262 instance->game_variant_suit_mask = FCS_PATS__COLOR;
2263 instance->game_variant_desired_suit_value = FCS_PATS__COLOR;
2264 if ((GET_INSTANCE_SEQUENCES_ARE_BUILT_BY(instance) ==
2265 FCS_SEQ_BUILT_BY_SUIT))
2266 {
2267 instance->game_variant_suit_mask = FCS_PATS__SUIT;
2268 instance->game_variant_desired_suit_value = 0;
2269 }
2270 #endif
2271 }
2272
apply_game_params_for_all_instances(fcs_user * const user)2273 static void apply_game_params_for_all_instances(fcs_user *const user)
2274 {
2275 FLARES_LOOP_START()
2276 {
2277 fcs_instance *const instance = &(flare->obj);
2278 instance->game_params = user->common_preset.game_params;
2279 calc_variant_suit_mask_and_desired_suit_value(instance);
2280 }
2281 INSTANCE_ITEM_FLARES_LOOP_END()
2282 INSTANCES_LOOP_END()
2283 }
2284 #endif
2285
2286 typedef struct
2287 {
2288 fcs_state *key;
2289 fcs_state_locs_struct locs;
2290 } standalone_state_ptrs;
2291
2292 #ifndef FCS_WITHOUT_ITER_HANDLER
iter_handler_wrapper(void * const api_instance,const fcs_int_limit_t iter_num,const int depth,void * lp_instance GCC_UNUSED,fcs_kv_state * const ptr_state,const fcs_int_limit_t parent_iter_num)2293 static void iter_handler_wrapper(void *const api_instance,
2294 const fcs_int_limit_t iter_num, const int depth,
2295 void *lp_instance GCC_UNUSED, fcs_kv_state *const ptr_state,
2296 const fcs_int_limit_t parent_iter_num)
2297 {
2298 fcs_user *const user = (fcs_user *)api_instance;
2299
2300 standalone_state_ptrs state_raw = {
2301 .key = ptr_state->key,
2302 };
2303 fc_solve_init_locs(&(state_raw.locs));
2304
2305 #define CALL(func_ptr, type) \
2306 (func_ptr)(api_instance, (type)iter_num, depth, (void *)&state_raw, \
2307 (type)parent_iter_num, user->iter_handler_context)
2308
2309 #ifdef FCS_BREAK_BACKWARD_COMPAT_1
2310 CALL(user->long_iter_handler, fcs_int_limit_t);
2311 #else
2312 if (user->long_iter_handler)
2313 {
2314 CALL(user->long_iter_handler, fcs_int_limit_t);
2315 }
2316 else
2317 {
2318 CALL(user->iter_handler, int);
2319 }
2320 #endif
2321 #undef CALL
2322 }
2323
set_debug_iter_output_func_to_val(fcs_user * const user,const instance_debug_iter_output_func value)2324 static inline void set_debug_iter_output_func_to_val(
2325 fcs_user *const user, const instance_debug_iter_output_func value)
2326 {
2327 FLARES_LOOP_START()
2328 flare->obj.debug_iter_output_func = value;
2329 INSTANCE_ITEM_FLARES_LOOP_END()
2330 INSTANCES_LOOP_END()
2331 }
2332
set_any_iter_handler(void * const api_instance,const freecell_solver_user_long_iter_handler_t long_iter_handler,const freecell_solver_user_iter_handler_t iter_handler,void * const iter_handler_context)2333 static inline void set_any_iter_handler(void *const api_instance,
2334 const freecell_solver_user_long_iter_handler_t long_iter_handler,
2335 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
2336 const freecell_solver_user_iter_handler_t iter_handler,
2337 #endif
2338 void *const iter_handler_context)
2339 {
2340 fcs_user *const user = (fcs_user *)api_instance;
2341
2342 user->long_iter_handler = long_iter_handler;
2343 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
2344 user->iter_handler = iter_handler;
2345 #endif
2346
2347 instance_debug_iter_output_func cb = NULL;
2348 if (
2349 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
2350 iter_handler ||
2351 #endif
2352 long_iter_handler)
2353 {
2354 user->iter_handler_context = iter_handler_context;
2355 cb = iter_handler_wrapper;
2356 }
2357 set_debug_iter_output_func_to_val(user, cb);
2358 }
2359 #endif
2360
user_next_flare(fcs_user * const user)2361 static FLARE_INLINE void user_next_flare(fcs_user *const user)
2362 {
2363 const_AUTO(instance_item, curr_inst(user));
2364 #ifdef FCS_WITH_FLARES
2365 const_AUTO(num_flares,
2366 (size_t)(instance_item->end_of_flares - instance_item->flares));
2367 instance_item->flares = SREALLOC(instance_item->flares, num_flares + 1);
2368 flare_item *const flare = instance_item->flares + num_flares;
2369 instance_item->end_of_flares = flare + 1;
2370 #else
2371 flare_item *const flare = &(instance_item->single_flare);
2372 #endif
2373 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
2374 instance_item->limit = -1;
2375 #endif
2376 fcs_instance *const instance = &(flare->obj);
2377
2378 SET_ACTIVE_FLARE(user, flare);
2379 alloc_instance(instance, &(user->meta_alloc));
2380 // Switch the soft_thread variable so it won't refer to the old instance
2381 user->soft_thread = &(INST_HT0(instance).soft_threads[0]);
2382
2383 #ifndef FCS_FREECELL_ONLY
2384 fc_solve_apply_preset_by_ptr(instance, &(user->common_preset));
2385 calc_variant_suit_mask_and_desired_suit_value(instance);
2386 #endif
2387
2388 #if defined(FCS_WITH_MOVES) || !defined(FCS_WITHOUT_MAX_NUM_STATES)
2389 SET_user_ret(user, SET_flare_ret(flare, FCS_STATE_NOT_BEGAN_YET));
2390 #endif
2391
2392 #ifndef FCS_WITHOUT_ITER_HANDLER
2393 instance->debug_iter_output_func = ((
2394 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
2395 user->iter_handler ||
2396 #endif
2397 user->long_iter_handler)
2398 ? iter_handler_wrapper
2399 : NULL);
2400 instance->debug_iter_output_context = user;
2401 #endif
2402
2403 #ifdef FCS_WITH_MOVES
2404 flare->moves_seq.num_moves = 0;
2405 flare->moves_seq.moves = NULL;
2406 #endif
2407
2408 #ifdef FCS_WITH_FLARES
2409 flare->name[0] = '\0';
2410 #endif
2411 #ifndef FCS_WITHOUT_FC_PRO_MOVES_COUNT
2412 flare->fc_pro_moves.moves = NULL;
2413 #endif
2414 flare->instance_is_ready = true;
2415 flare->obj_stats = initial_stats;
2416 }
2417
2418 #ifdef FCS_WITH_NI
2419 #define NI_INLINE
2420 #else
2421 #define NI_INLINE inline
2422 #endif
2423
user_next_instance(fcs_user * const user)2424 static NI_INLINE void user_next_instance(fcs_user *const user)
2425 {
2426 #ifdef FCS_WITH_NI
2427 const_AUTO(num_instances,
2428 (size_t)(user->end_of_instances_list - user->instances_list));
2429 user->instances_list = SREALLOC(user->instances_list, num_instances + 1);
2430
2431 user->end_of_instances_list =
2432 (user->current_instance = user->instances_list + num_instances) + 1;
2433 #endif
2434
2435 #ifdef FCS_WITH_FLARES
2436 *(curr_inst(user)) = (fcs_instance_item){
2437 .flares = NULL,
2438 .end_of_flares = NULL,
2439 .plan = NULL,
2440 .num_plan_items = 0,
2441 .flares_plan_string = NULL,
2442 .flares_plan_compiled = false,
2443 .current_plan_item_idx = 0,
2444 .minimal_flare = NULL,
2445 .intract_minimal_flare = NULL,
2446 .all_plan_items_finished_so_far = true,
2447 };
2448 #endif
2449
2450 // ret_code and limit are set at user_next_flare().
2451 user_next_flare(user);
2452 }
2453
2454 #ifdef FCS_WITH_ERROR_STRS
2455 #define ALLOC_ERROR_STRING(var, s) *(var) = strdup(s)
2456 #else
2457 #define ALLOC_ERROR_STRING(var, s)
2458 #endif
2459
2460 #ifndef FCS_ZERO_FREECELLS_MODE
2461 #ifdef FCS_WITH_ERROR_STRS
2462 #define SET_ERROR_VAR(var, s) *(var) = (((s)[0]) ? strdup(s) : NULL)
clear_error(fcs_user * const user)2463 static inline void clear_error(fcs_user *const user)
2464 {
2465 user->error_string[0] = '\0';
2466 }
2467 #else
2468 #define SET_ERROR_VAR(var, s)
2469 #define clear_error(user)
2470 #endif
2471 #endif
2472
2473 #ifdef FCS_BREAK_BACKWARD_COMPAT_1
2474 #define MYINLINE inline
2475 #else
2476 #define MYINLINE
2477 #endif
2478
user_initialize(fcs_user * const user)2479 static MYINLINE void user_initialize(fcs_user *const user)
2480 {
2481 #ifndef FCS_FREECELL_ONLY
2482 const fcs_preset *freecell_preset;
2483 fc_solve_get_preset_by_name("freecell", &freecell_preset);
2484 fcs_duplicate_preset(user->common_preset, *freecell_preset);
2485 #endif
2486
2487 fc_solve_meta_compact_allocator_init(&(user->meta_alloc));
2488
2489 #ifdef FCS_WITH_NI
2490 user->instances_list = NULL;
2491 user->end_of_instances_list = NULL;
2492 #endif
2493 #ifndef FCS_WITHOUT_ITER_HANDLER
2494 user->long_iter_handler = NULL;
2495 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
2496 user->iter_handler = NULL;
2497 #endif
2498 #endif
2499 #ifndef FCS_WITHOUT_MAX_NUM_STATES
2500 user->current_iterations_limit = -1;
2501 user->effective_current_iterations_limit = FCS_ITERS_INT_MAX;
2502 user->current_soft_iterations_limit = -1;
2503 #endif
2504
2505 user->iterations_board_started_at = initial_stats;
2506 #ifdef FCS_WITH_NI
2507 user->all_instances_were_suspended = true;
2508 #endif
2509 #ifdef FCS_WITH_FLARES
2510 #ifndef FCS_WITHOUT_FC_PRO_MOVES_COUNT
2511 user->flares_choice = FLARES_CHOICE_FC_SOLVE_SOLUTION_LEN;
2512 #endif
2513 user->flares_iters_factor = 1.0;
2514 #endif
2515 #ifndef FCS_USE_PRECOMPILED_CMD_LINE_THEME
2516 for (size_t i = 0; i < COUNT(user->unrecognized_cmd_line_options); ++i)
2517 {
2518 user->unrecognized_cmd_line_options[i] = NULL;
2519 }
2520 #endif
2521
2522 #ifndef FCS_ZERO_FREECELLS_MODE
2523 clear_error(user);
2524 #endif
2525 user_next_instance(user);
2526 }
2527
freecell_solver_user_alloc(void)2528 void DLLEXPORT *freecell_solver_user_alloc(void)
2529 {
2530 fcs_user *const ret = (fcs_user *)SMALLOC1(ret);
2531 user_initialize(ret);
2532 return (void *)ret;
2533 }
2534
2535 #ifndef FCS_FREECELL_ONLY
freecell_solver_user_apply_preset(void * const api_instance,const char * const preset_name)2536 int DLLEXPORT freecell_solver_user_apply_preset(
2537 void *const api_instance, const char *const preset_name)
2538 {
2539 const fcs_preset *new_preset_ptr;
2540 fcs_user *const user = (fcs_user *)api_instance;
2541
2542 const_AUTO(
2543 status1, fc_solve_get_preset_by_name(preset_name, &new_preset_ptr));
2544 if (status1 != FCS_PRESET_CODE_OK)
2545 {
2546 return (int)status1;
2547 }
2548
2549 FLARES_LOOP_START()
2550 const_AUTO(
2551 status2, fc_solve_apply_preset_by_ptr(&(flare->obj), new_preset_ptr));
2552
2553 if (status2 != FCS_PRESET_CODE_OK)
2554 {
2555 return (int)status2;
2556 }
2557 INSTANCE_ITEM_FLARES_LOOP_END()
2558 INSTANCES_LOOP_END()
2559
2560 fcs_duplicate_preset(user->common_preset, *new_preset_ptr);
2561
2562 return FCS_PRESET_CODE_OK;
2563 }
2564 #endif
2565
2566 #ifndef FCS_WITHOUT_MAX_NUM_STATES
freecell_solver_user_limit_iterations_long(void * const api_instance,const fcs_int_limit_t max_iters)2567 void DLLEXPORT freecell_solver_user_limit_iterations_long(
2568 void *const api_instance, const fcs_int_limit_t max_iters)
2569 {
2570 fcs_user *const user = (fcs_user *)api_instance;
2571 if (max_iters < 0)
2572 {
2573 user->current_iterations_limit = -1;
2574 user->effective_current_iterations_limit = FCS_ITERS_INT_MAX;
2575 }
2576 else
2577 {
2578 user->current_iterations_limit = max_iters;
2579 user->effective_current_iterations_limit = (fcs_iters_int)max_iters;
2580 }
2581 }
2582
freecell_solver_user_soft_limit_iterations_long(void * const api_instance,const fcs_int_limit_t max_iters)2583 void DLLEXPORT freecell_solver_user_soft_limit_iterations_long(
2584 void *const api_instance, const fcs_int_limit_t max_iters)
2585 {
2586 ((fcs_user *const)api_instance)->current_soft_iterations_limit = max_iters;
2587 }
2588
2589 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
freecell_solver_user_limit_iterations(void * const api_instance,const int max_iters)2590 void DLLEXPORT freecell_solver_user_limit_iterations(
2591 void *const api_instance, const int max_iters)
2592 {
2593 freecell_solver_user_limit_iterations_long(
2594 api_instance, (fcs_int_limit_t)max_iters);
2595 }
2596
freecell_solver_user_limit_current_instance_iterations(void * const api_instance,const int max_iters)2597 void DLLEXPORT freecell_solver_user_limit_current_instance_iterations(
2598 void *const api_instance, const int max_iters)
2599 {
2600 curr_inst((fcs_user *)api_instance)->limit = max_iters;
2601 }
2602 #endif
2603 #endif
2604
api_soft_thread(void * const api_instance)2605 static inline fcs_soft_thread *api_soft_thread(void *const api_instance)
2606 {
2607 return ((fcs_user *const)api_instance)->soft_thread;
2608 }
2609
freecell_solver_user_set_depth_tests_order(void * const api_instance GCC_UNUSED,const int min_depth GCC_UNUSED,const char * const moves_order GCC_UNUSED FCS__PASS_ERR_STR (char ** const error_string GCC_UNUSED))2610 int DLLEXPORT freecell_solver_user_set_depth_tests_order(
2611 void *const api_instance GCC_UNUSED, const int min_depth GCC_UNUSED,
2612 const char *const moves_order GCC_UNUSED FCS__PASS_ERR_STR(
2613 char **const error_string GCC_UNUSED))
2614 {
2615 #ifndef FCS_ZERO_FREECELLS_MODE
2616 fcs_soft_thread *const soft_thread = api_soft_thread(api_instance);
2617
2618 if (min_depth < 0)
2619 {
2620 ALLOC_ERROR_STRING(error_string, "Depth is negative.");
2621 return 1;
2622 }
2623
2624 size_t depth_idx = 0;
2625 if (min_depth > 0)
2626 {
2627 for (;; ++depth_idx)
2628 {
2629 if (depth_idx == soft_thread->by_depth_moves_order.num - 1)
2630 {
2631 break;
2632 }
2633 else if (min_depth <=
2634 soft_thread->by_depth_moves_order.by_depth_moves[depth_idx]
2635 .max_depth)
2636 {
2637 break;
2638 }
2639 }
2640
2641 ++depth_idx;
2642 }
2643
2644 if (depth_idx == soft_thread->by_depth_moves_order.num)
2645 {
2646 soft_thread->by_depth_moves_order.by_depth_moves =
2647 SREALLOC(soft_thread->by_depth_moves_order.by_depth_moves,
2648 ++soft_thread->by_depth_moves_order.num);
2649
2650 soft_thread->by_depth_moves_order.by_depth_moves[depth_idx]
2651 .moves_order.num = 0;
2652 soft_thread->by_depth_moves_order.by_depth_moves[depth_idx]
2653 .moves_order.groups = NULL;
2654 }
2655
2656 if (depth_idx > 0)
2657 {
2658 soft_thread->by_depth_moves_order.by_depth_moves[depth_idx - 1]
2659 .max_depth = min_depth;
2660 }
2661
2662 soft_thread->by_depth_moves_order.by_depth_moves[depth_idx].max_depth =
2663 SSIZE_MAX;
2664
2665 FCS__DECL_ERR_BUF(static_error_string);
2666 const int ret_code = fc_solve_apply_moves_order(
2667 &(soft_thread->by_depth_moves_order.by_depth_moves[depth_idx]
2668 .moves_order),
2669 moves_order FCS__PASS_ERR_STR(static_error_string));
2670
2671 SET_ERROR_VAR(error_string, static_error_string);
2672
2673 for (size_t d = depth_idx + 1; d < soft_thread->by_depth_moves_order.num;
2674 ++d)
2675 {
2676 moves_order__free(
2677 &(soft_thread->by_depth_moves_order.by_depth_moves[d].moves_order));
2678 }
2679
2680 soft_thread->by_depth_moves_order.by_depth_moves =
2681 SREALLOC(soft_thread->by_depth_moves_order.by_depth_moves,
2682 soft_thread->by_depth_moves_order.num = depth_idx + 1);
2683
2684 return ret_code;
2685 #else
2686 return 0;
2687 #endif
2688 }
2689
2690 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
freecell_solver_user_set_tests_order(void * api_instance,const char * moves_order FCS__PASS_ERR_STR (char ** error_string))2691 int DLLEXPORT freecell_solver_user_set_tests_order(void *api_instance,
2692 const char *moves_order FCS__PASS_ERR_STR(char **error_string))
2693 {
2694 return freecell_solver_user_set_depth_tests_order(
2695 api_instance, 0, moves_order FCS__PASS_ERR_STR(error_string));
2696 }
2697 #endif
2698
2699 typedef enum
2700 {
2701 FCS_COMPILE_FLARES_RET_OK = 0,
2702 FCS_COMPILE_FLARES_RET_COLON_NOT_FOUND,
2703 FCS_COMPILE_FLARES_RET_RUN_AT_SIGN_NOT_FOUND,
2704 FCS_COMPILE_FLARES_RET_UNKNOWN_FLARE_NAME,
2705 FCS_COMPILE_FLARES_RET_JUNK_AFTER_CP,
2706 FCS_COMPILE_FLARES_RET_UNKNOWN_COMMAND,
2707 FCS_COMPILE_FLARES_RUN_JUNK_AFTER_LAST_RUN_INDEF
2708 } fcs_compile_flares_ret;
2709
2710 #ifdef FCS_WITH_FLARES
create_plan_item(const flares_plan_type mytype,flare_item * const flare,const int_fast32_t count_iters)2711 static inline flares_plan_item create_plan_item(const flares_plan_type mytype,
2712 flare_item *const flare, const int_fast32_t count_iters)
2713 {
2714 return (const flares_plan_item){
2715 .type = mytype, .flare = flare, .count_iters = count_iters};
2716 }
2717
add_to_plan(fcs_instance_item * const instance_item,const flares_plan_type mytype,flare_item * const flare,const int_fast32_t count_iters)2718 static inline void add_to_plan(fcs_instance_item *const instance_item,
2719 const flares_plan_type mytype, flare_item *const flare,
2720 const int_fast32_t count_iters)
2721 {
2722 const_AUTO(next_item, instance_item->num_plan_items);
2723
2724 instance_item->plan =
2725 SREALLOC(instance_item->plan, ++(instance_item->num_plan_items));
2726
2727 instance_item->plan[next_item] =
2728 create_plan_item(mytype, flare, count_iters);
2729 }
2730
add_checkpoint_to_plan(fcs_instance_item * const instance_item)2731 static inline void add_checkpoint_to_plan(
2732 fcs_instance_item *const instance_item)
2733 {
2734 add_to_plan(instance_item, FLARES_PLAN_CHECKPOINT, NULL, -1);
2735 }
2736
2737 #define MAX_FLARE_LEN_NAME 32
find_flare(flare_item * const flares,const flare_item * const end_of_flares,const char * const proto_name,const size_t name_len)2738 static inline flare_item *find_flare(flare_item *const flares,
2739 const flare_item *const end_of_flares, const char *const proto_name,
2740 const size_t name_len)
2741 {
2742 char name[MAX_FLARE_LEN_NAME];
2743 strncpy(name, proto_name, MAX_FLARE_LEN_NAME - 1);
2744 name[min(MAX_FLARE_LEN_NAME - 1, name_len)] = '\0';
2745
2746 for (flare_item *flare = flares; flare < end_of_flares; flare++)
2747 {
2748 if (!strcmp(flare->name, name))
2749 {
2750 return flare;
2751 }
2752 }
2753 return NULL;
2754 }
2755
2756 #ifdef FCS_WITH_ERROR_STRS
2757 #define SET_ERROR(s) strcpy(user->error_string, s)
2758 #else
2759 #define SET_ERROR(s)
2760 #endif
2761
user_compile_all_flares_plans(fcs_user * const user)2762 static inline fcs_compile_flares_ret user_compile_all_flares_plans(
2763 fcs_user *const user)
2764 {
2765 INSTANCES_LOOP_START()
2766 if (instance_item->flares_plan_compiled)
2767 {
2768 continue;
2769 }
2770 flare_item *const flares = instance_item->flares;
2771 const_SLOT(end_of_flares, instance_item);
2772
2773 // If the plan string is NULL or empty, then set the plan
2774 // to run only the first flare indefinitely. (And then have
2775 // an implicit checkpoint for good measure.)
2776 if ((!instance_item->flares_plan_string) ||
2777 (!instance_item->flares_plan_string[0]))
2778 {
2779 if (instance_item->plan)
2780 {
2781 free(instance_item->plan);
2782 }
2783 instance_item->num_plan_items = 2;
2784 instance_item->plan =
2785 SMALLOC(instance_item->plan, instance_item->num_plan_items);
2786 // Set to the first flare.
2787 instance_item->plan[0] = create_plan_item(
2788 FLARES_PLAN_RUN_INDEFINITELY, instance_item->flares, -1);
2789 instance_item->plan[1] =
2790 create_plan_item(FLARES_PLAN_CHECKPOINT, NULL, -1);
2791
2792 instance_item->flares_plan_compiled = true;
2793 continue;
2794 }
2795
2796 // Tough luck - gotta parse the string. ;-)
2797 const char *item_end;
2798 const char *item_start = instance_item->flares_plan_string;
2799 if (instance_item->plan)
2800 {
2801 free(instance_item->plan);
2802 instance_item->plan = NULL;
2803 instance_item->num_plan_items = 0;
2804 }
2805 do
2806 {
2807 const char *cmd_end = strchr(item_start, ':');
2808 if (!cmd_end)
2809 {
2810 SET_ERROR("Could not find a \":\" for a command.");
2811 return FCS_COMPILE_FLARES_RET_COLON_NOT_FOUND;
2812 }
2813
2814 if (string_starts_with(item_start, "Run", cmd_end))
2815 {
2816 // It's a Run item - handle it.
2817 const int_fast32_t count_iters = atoi(++cmd_end);
2818 const char *at_sign = cmd_end;
2819 while ((*at_sign) && isdigit(*at_sign))
2820 {
2821 at_sign++;
2822 }
2823
2824 if (*at_sign != '@')
2825 {
2826 SET_ERROR("Could not find a \"@\" directly after "
2827 "the digits after the 'Run:' command.");
2828 return FCS_COMPILE_FLARES_RET_RUN_AT_SIGN_NOT_FOUND;
2829 }
2830 const char *const after_at_sign = at_sign + 1;
2831
2832 // Position item_end at the end of item (designated by
2833 // ",") or alternatively the end of the string.
2834 if (!((item_end = strchr(after_at_sign, ','))))
2835 {
2836 item_end = strchr(after_at_sign, '\0');
2837 }
2838
2839 flare_item *const flare = find_flare(flares, end_of_flares,
2840 after_at_sign, (size_t)(item_end - after_at_sign));
2841
2842 if (!flare)
2843 {
2844 SET_ERROR("Unknown flare name.");
2845 return FCS_COMPILE_FLARES_RET_UNKNOWN_FLARE_NAME;
2846 }
2847
2848 add_to_plan(
2849 instance_item, FLARES_PLAN_RUN_COUNT_ITERS, flare, count_iters);
2850 }
2851 else if (string_starts_with(item_start, "CP", cmd_end))
2852 {
2853 item_end = cmd_end + 1;
2854 if (!(((*item_end) == ',') || (!(*item_end))))
2855 {
2856 SET_ERROR("Junk after CP (Checkpoint) command.");
2857 return FCS_COMPILE_FLARES_RET_JUNK_AFTER_CP;
2858 }
2859
2860 add_checkpoint_to_plan(instance_item);
2861 }
2862 else if (string_starts_with(item_start, "RunIndef", cmd_end))
2863 {
2864 if (strchr(++cmd_end, ','))
2865 {
2866 SET_ERROR("Junk after last RunIndef command. Must "
2867 "be the final command.");
2868 return FCS_COMPILE_FLARES_RUN_JUNK_AFTER_LAST_RUN_INDEF;
2869 }
2870 item_end = strchr(cmd_end, '\0');
2871
2872 flare_item *const flare = find_flare(
2873 flares, end_of_flares, cmd_end, (size_t)(item_end - cmd_end));
2874 if (!flare)
2875 {
2876 SET_ERROR("Unknown flare name in RunIndef command.");
2877 return FCS_COMPILE_FLARES_RET_UNKNOWN_FLARE_NAME;
2878 }
2879 add_to_plan(instance_item, FLARES_PLAN_RUN_INDEFINITELY, flare, -1);
2880 }
2881 else
2882 {
2883 SET_ERROR("Unknown command.");
2884 return FCS_COMPILE_FLARES_RET_UNKNOWN_COMMAND;
2885 }
2886 item_start = item_end + 1;
2887 } while (*item_end);
2888
2889 if ((!instance_item->plan) ||
2890 instance_item->plan[instance_item->num_plan_items - 1].type !=
2891 FLARES_PLAN_CHECKPOINT)
2892 {
2893 add_checkpoint_to_plan(instance_item);
2894 }
2895
2896 instance_item->flares_plan_compiled = true;
2897 continue;
2898 INSTANCES_LOOP_END()
2899
2900 const_SLOT(flares_iters_factor, user);
2901 INSTANCES_LOOP_START()
2902 const_SLOT(num_plan_items, instance_item);
2903 const_SLOT(plan, instance_item);
2904 for (size_t i = 0; i < num_plan_items; i++)
2905 {
2906 flares_plan_item *const item = plan + i;
2907 switch (item->type)
2908 {
2909 case FLARES_PLAN_RUN_COUNT_ITERS:
2910 item->initial_quota =
2911 normalize_iters_quota((typeof(item->initial_quota))(
2912 flares_iters_factor *
2913 (typeof(flares_iters_factor))item->count_iters));
2914 break;
2915
2916 case FLARES_PLAN_CHECKPOINT:
2917 case FLARES_PLAN_RUN_INDEFINITELY:
2918 item->initial_quota = -1;
2919 break;
2920 }
2921 }
2922 INSTANCES_LOOP_END()
2923 #ifndef FCS_ZERO_FREECELLS_MODE
2924 clear_error(user);
2925 #endif
2926
2927 return FCS_COMPILE_FLARES_RET_OK;
2928 }
2929 #endif
2930
2931 #define MY_MARGIN 3
duplicate_string(char * const s,const char * const orig_str)2932 static inline bool duplicate_string(char *const s, const char *const orig_str)
2933 {
2934 const size_t len = strlen(orig_str);
2935 // If orig_str is the empty string then there is no penultimate character.
2936 if (len >= MAX_STATE_STRING_COPY_LEN - MY_MARGIN)
2937 {
2938 return false;
2939 }
2940 strcpy(s, orig_str);
2941 return true;
2942 }
2943 #undef MY_MARGIN
2944
recycle_flare(flare_item * const flare)2945 static inline void recycle_flare(flare_item *const flare)
2946 {
2947 if (!flare->instance_is_ready)
2948 {
2949 recycle_inst(&(flare->obj));
2950 flare->instance_is_ready = true;
2951 }
2952 }
2953
user__recycle_instance_item(fcs_user * const user,fcs_instance_item * const instance_item)2954 static void user__recycle_instance_item(
2955 fcs_user *const user, fcs_instance_item *const instance_item)
2956 {
2957 INSTANCE_ITEM_FLARES_LOOP_START()
2958 #ifndef FCS_WITHOUT_FC_PRO_MOVES_COUNT
2959 fc_solve_moves_processed_free(&(flare->fc_pro_moves));
2960 #endif
2961
2962 #ifndef FCS_WITHOUT_MAX_NUM_STATES
2963 if (flare->ret_code != FCS_STATE_NOT_BEGAN_YET)
2964 #endif
2965 {
2966 recycle_flare(flare);
2967 // We have to initialize init_num_checked_states to 0 here, because it
2968 // may
2969 // not get initialized again, and now the num_checked_states of the
2970 // instance
2971 // is equal to 0.
2972 user->init_num_checked_states = initial_stats;
2973
2974 #ifndef FCS_WITHOUT_MAX_NUM_STATES
2975 flare->ret_code = FCS_STATE_NOT_BEGAN_YET;
2976 #endif
2977 }
2978
2979 #ifdef FCS_WITH_MOVES
2980 if (flare->moves_seq.moves)
2981 {
2982 free(flare->moves_seq.moves);
2983 flare->moves_seq.moves = NULL;
2984 flare->moves_seq.num_moves = 0;
2985 flare->next_move_idx = 0;
2986 }
2987 #endif
2988
2989 flare->obj_stats = initial_stats;
2990 INSTANCE_ITEM_FLARES_LOOP_END()
2991
2992 #ifdef FCS_WITH_FLARES
2993 instance_item->current_plan_item_idx = 0;
2994 instance_item->minimal_flare = NULL;
2995 instance_item->intract_minimal_flare = NULL;
2996 #endif
2997 }
2998
2999 #ifdef FCS_WITH_MOVES
3000
3001 #ifndef FCS_USE_COMPACT_MOVE_TOKENS
3002 #define internal_move_to_user_move(x) (x)
3003 #define user_move_to_internal_move(x) (x)
3004 #else
internal_move_to_user_move(const fcs_internal_move internal_move)3005 static inline fcs_move_t internal_move_to_user_move(
3006 const fcs_internal_move internal_move)
3007 {
3008 fcs_move_t user_move;
3009
3010 // Convert the internal_move to a user move.
3011 fcs_move_set_src_stack(user_move, fcs_int_move_get_src(internal_move));
3012 fcs_move_set_dest_stack(user_move, fcs_int_move_get_dest(internal_move));
3013 fcs_move_set_type(user_move, fcs_int_move_get_type(internal_move));
3014 fcs_move_set_num_cards_in_seq(
3015 user_move, fcs_int_move_get_num_cards_in_seq(internal_move));
3016
3017 return user_move;
3018 }
user_move_to_internal_move(const fcs_move_t user_move)3019 static inline fcs_internal_move user_move_to_internal_move(
3020 const fcs_move_t user_move)
3021 {
3022 fcs_internal_move internal_move;
3023
3024 // Convert the internal_move to a user move.
3025 fcs_int_move_set_src(internal_move, fcs_move_get_src_stack(user_move));
3026 fcs_int_move_set_dest(internal_move, fcs_move_get_dest_stack(user_move));
3027 fcs_int_move_set_type(internal_move, fcs_move_get_type(user_move));
3028 fcs_int_move_set_num_cards_in_seq(
3029 internal_move, fcs_move_get_num_cards_in_seq(user_move));
3030
3031 return internal_move;
3032 }
3033 #endif
3034
calc_moves_seq(const fcs_move_stack * const solution_moves,fcs_moves_sequence_t * const moves_seq)3035 static inline void calc_moves_seq(const fcs_move_stack *const solution_moves,
3036 fcs_moves_sequence_t *const moves_seq)
3037 {
3038 moves_seq->num_moves = 0;
3039 moves_seq->moves = NULL;
3040
3041 const_SLOT(num_moves, solution_moves);
3042 fcs_internal_move *next_move_ptr = solution_moves->moves + num_moves;
3043 fcs_move_t *const ret_moves = SMALLOC(ret_moves, num_moves);
3044 if (!ret_moves)
3045 {
3046 return;
3047 }
3048
3049 for (size_t i = 0; i < num_moves; i++)
3050 {
3051 ret_moves[i] = internal_move_to_user_move(*(--next_move_ptr));
3052 }
3053
3054 moves_seq->num_moves = num_moves;
3055 moves_seq->moves = ret_moves;
3056 }
3057 #endif
3058
3059 #ifdef FCS_WITH_MOVES
trace_flare_solution(fcs_user * const user,flare_item * const flare)3060 static void trace_flare_solution(fcs_user *const user, flare_item *const flare)
3061 {
3062 if (flare->was_solution_traced)
3063 {
3064 return;
3065 }
3066
3067 fcs_instance *const instance = &(flare->obj);
3068 fc_solve_trace_solution(instance);
3069 flare->trace_solution_state_locs = user->state_locs;
3070 fc_solve_move_stack_normalize(&(instance->solution_moves), &(user->state),
3071 &(flare->trace_solution_state_locs)PASS_FREECELLS(
3072 INSTANCE_FREECELLS_NUM) PASS_STACKS(INSTANCE_STACKS_NUM));
3073
3074 calc_moves_seq(&(instance->solution_moves), &(flare->moves_seq));
3075 instance_free_solution_moves(instance);
3076 flare->next_move_idx = 0;
3077 flare->obj_stats = instance->i__stats;
3078 recycle_flare(flare);
3079 flare->was_solution_traced = true;
3080 }
3081 #endif
3082
3083 #ifdef FCS_WITH_FLARES
get_flare_move_count(fcs_user * const user GCC_UNUSED,flare_item * const flare GCC_UNUSED)3084 static uint_fast32_t get_flare_move_count(
3085 fcs_user *const user GCC_UNUSED, flare_item *const flare GCC_UNUSED)
3086 {
3087 #ifndef FCS_WITH_MOVES
3088 return 0;
3089 #else
3090 trace_flare_solution(user, flare);
3091 #define RET() return flare->moves_seq.num_moves
3092 #ifdef FCS_WITHOUT_FC_PRO_MOVES_COUNT
3093 RET();
3094 #else
3095 if (user->flares_choice == FLARES_CHOICE_FC_SOLVE_SOLUTION_LEN)
3096 {
3097 RET();
3098 }
3099 else
3100 {
3101 if (!flare->fc_pro_moves.moves)
3102 {
3103 fc_solve_moves_processed_gen(&(flare->fc_pro_moves),
3104 &(user->initial_non_canonized_state),
3105 #ifdef FCS_FREECELL_ONLY
3106 4,
3107 #else
3108 user->common_preset.game_params.freecells_num,
3109 #endif
3110 &(flare->moves_seq));
3111 }
3112
3113 return fc_solve_moves_processed_get_moves_left(&(flare->fc_pro_moves));
3114 }
3115 #endif
3116
3117 #undef RET
3118 #endif
3119 }
3120 #endif
3121
eval_resume_ret_code(fcs_user * const user GCC_UNUSED,const fc_solve_solve_process_ret_t ret_proto,const bool process_ret GCC_UNUSED)3122 static inline fc_solve_solve_process_ret_t eval_resume_ret_code(
3123 fcs_user *const user GCC_UNUSED,
3124 const fc_solve_solve_process_ret_t ret_proto,
3125 const bool process_ret GCC_UNUSED)
3126 {
3127 #ifdef FCS_WITH_NI
3128 const_AUTO(
3129 ret, (user->all_instances_were_suspended ? FCS_STATE_SUSPEND_PROCESS
3130 : ret_proto));
3131 #else
3132 const_AUTO(ret, ret_proto);
3133 #endif
3134 #ifndef FCS_WITHOUT_MAX_NUM_STATES
3135 if (ret == FCS_STATE_SUSPEND_PROCESS)
3136 {
3137 if (process_ret && (user->effective_current_iterations_limit >
3138 get_num_times_long(user)))
3139 {
3140 return FCS_STATE_SOFT_SUSPEND_PROCESS;
3141 }
3142 #ifdef FCS_WITH_FLARES
3143 const_AUTO(instance_item, curr_inst(user));
3144 if (instance_item->minimal_flare)
3145 {
3146 SET_ACTIVE_FLARE(user, instance_item->minimal_flare);
3147 user->init_num_checked_states = OBJ_STATS(user);
3148 return FCS_STATE_WAS_SOLVED;
3149 }
3150 #endif
3151 }
3152 #endif
3153 return ret;
3154 }
3155
start_flare(fcs_user * const user,fcs_instance * const instance)3156 static inline bool start_flare(
3157 fcs_user *const user, fcs_instance *const instance)
3158 {
3159 if (unlikely(!fc_solve_initial_user_state_to_c(user->state_string_copy,
3160 &(user->state), INSTANCE_FREECELLS_NUM, INSTANCE_STACKS_NUM,
3161 INSTANCE_DECKS_NUM, user->indirect_stacks_buffer)))
3162 {
3163 #ifdef FCS_WITH_ERROR_STRS
3164 user->state_validity_ret = FCS_STATE_VALIDITY__PREMATURE_END_OF_INPUT;
3165 #endif
3166 return false;
3167 }
3168
3169 #ifndef FCS_DISABLE_STATE_VALIDITY_CHECK
3170 #ifndef FCS_WITH_ERROR_STRS
3171 fcs_card state_validity_card;
3172 #endif
3173 const state_validity_ret state_validity =
3174 #ifdef FCS_WITH_ERROR_STRS
3175 user->state_validity_ret =
3176 #endif
3177 fc_solve_check_state_validity(
3178 &(user->state)PASS_FREECELLS(INSTANCE_FREECELLS_NUM)
3179 PASS_STACKS(INSTANCE_STACKS_NUM)
3180 PASS_DECKS(INSTANCE_DECKS_NUM),
3181 #ifdef FCS_WITH_ERROR_STRS
3182 &(user->state_validity_card)
3183 #else
3184 &state_validity_card
3185 #endif
3186 );
3187 if (unlikely(FCS_STATE_VALIDITY__OK != state_validity))
3188 {
3189 return false;
3190 }
3191 #endif
3192 #ifdef FCS_WITH_MOVES
3193 fc_solve_init_locs(&(user->initial_state_locs));
3194 user->state_locs = user->initial_state_locs;
3195 // running_state and initial_non_canonized_state are normalized states. So
3196 // We're duplicating state to it before user->state is canonized.
3197 FCS_STATE__DUP_keyval_pair(user->running_state, user->state);
3198 #endif
3199 #if defined(FCS_WITH_FLARES) || !defined(FCS_DISABLE_PATSOLVE)
3200 FCS_STATE__DUP_keyval_pair(user->initial_non_canonized_state, user->state);
3201 #endif
3202 #ifdef FCS_WITH_MOVES
3203 fc_solve_canonize_state_with_locs(&(user->state.s),
3204 &(user->state_locs)PASS_FREECELLS(INSTANCE_FREECELLS_NUM)
3205 PASS_STACKS(INSTANCE_STACKS_NUM));
3206 #else
3207 fc_solve_canonize_state(&(user->state.s)PASS_FREECELLS(
3208 INSTANCE_FREECELLS_NUM) PASS_STACKS(INSTANCE_STACKS_NUM));
3209 #endif
3210 init_instance(instance);
3211 return true;
3212 }
3213
3214 #ifndef FCS_WITHOUT_MAX_NUM_STATES
set_upper_limit(fcs_user * const user,fcs_instance_item * const instance_item GCC_UNUSED,fcs_instance * const instance,const fcs_int_limit_t current_iterations_limit,const flare_iters_quota iters_quota)3215 static inline bool set_upper_limit(
3216 fcs_user *const user, fcs_instance_item *const instance_item GCC_UNUSED,
3217 fcs_instance *const instance, const fcs_int_limit_t current_iterations_limit
3218 #ifdef FCS_WITH_FLARES
3219 ,
3220 const flare_iters_quota iters_quota
3221 #endif
3222 )
3223 {
3224 #ifdef FCS_BREAK_BACKWARD_COMPAT_1
3225 #define local_limit() (-1)
3226 #else
3227 #define local_limit() (instance_item->limit)
3228 #endif
3229 #ifdef FCS_WITH_FLARES
3230 #define NUM_ITERS_LIMITS 3
3231 #else
3232 #define NUM_ITERS_LIMITS 2
3233 #endif
3234 #define NUM_ITERS_LIMITS_MINUS_1 (NUM_ITERS_LIMITS - 1)
3235 const fcs_int_limit_t limits[NUM_ITERS_LIMITS_MINUS_1] = {
3236 current_iterations_limit
3237 #ifdef FCS_WITH_FLARES
3238 #define PARAMETERIZED_FIXED_LIMIT(increment) \
3239 (user->iterations_board_started_at.num_checked_states + \
3240 (fcs_iters_int)increment)
3241 #define PARAMETERIZED_LIMIT(increment) \
3242 (((increment) < 0) \
3243 ? (-1) \
3244 : (fcs_int_limit_t)PARAMETERIZED_FIXED_LIMIT(increment))
3245 ,
3246 PARAMETERIZED_LIMIT(iters_quota)
3247 #endif
3248 };
3249
3250 fcs_int_limit_t mymin = local_limit();
3251 for (size_t limit_idx = 0; limit_idx < NUM_ITERS_LIMITS_MINUS_1;
3252 limit_idx++)
3253 {
3254 const_AUTO(new_lim, limits[limit_idx]);
3255 if (new_lim >= 0)
3256 {
3257 mymin = (mymin < 0) ? new_lim : min(mymin, new_lim);
3258 }
3259 }
3260
3261 instance->effective_max_num_checked_states =
3262 ((mymin < 0)
3263 ? FCS_ITERS_INT_MAX
3264 : (instance->i__stats.num_checked_states +
3265 (fcs_iters_int)mymin -
3266 user->iterations_board_started_at.num_checked_states));
3267 return ((user->current_soft_iterations_limit >= 0) &&
3268 ((user->current_soft_iterations_limit <
3269 (fcs_int_limit_t)user->effective_current_iterations_limit)));
3270 }
3271 #endif
3272
flare__update_stats(fcs_user * const user,fcs_instance * const instance,flare_item * const flare,const flare_iters_quota iters_quota,flares_plan_item * const current_plan_item)3273 static inline void flare__update_stats(
3274 fcs_user *const user, fcs_instance *const instance, flare_item *const flare
3275 #ifdef FCS_WITH_FLARES
3276 ,
3277 const flare_iters_quota iters_quota,
3278 flares_plan_item *const current_plan_item
3279 #endif
3280 )
3281 {
3282 flare->obj_stats = instance->i__stats;
3283 const_AUTO(delta, flare->obj_stats.num_checked_states -
3284 user->init_num_checked_states.num_checked_states);
3285 user->iterations_board_started_at.num_checked_states += delta;
3286 #ifdef FCS_WITH_FLARES
3287 if (iters_quota >= 0)
3288 {
3289 current_plan_item->remaining_quota = normalize_iters_quota(
3290 (flare_iters_quota)iters_quota - (flare_iters_quota)delta);
3291 }
3292 #endif
3293 #ifndef FCS_DISABLE_NUM_STORED_STATES
3294 user->iterations_board_started_at.num_states_in_collection +=
3295 flare->obj_stats.num_states_in_collection -
3296 user->init_num_checked_states.num_states_in_collection;
3297 #endif
3298 user->init_num_checked_states = flare->obj_stats;
3299 }
3300
3301 #ifdef FCS_WITH_NI
3302 #define BUMP_CURR_INST() \
3303 user->current_instance++; \
3304 continue
3305 #else
3306 #define BUMP_CURR_INST() break
3307 #endif
resume_solution(fcs_user * const user)3308 static inline fc_solve_solve_process_ret_t resume_solution(fcs_user *const user)
3309 {
3310 fc_solve_solve_process_ret_t ret = FCS_STATE_IS_NOT_SOLVEABLE;
3311
3312 #ifndef FCS_WITHOUT_MAX_NUM_STATES
3313 bool process_ret = false;
3314 #else
3315 const bool process_ret = false;
3316 #endif
3317 #ifdef FCS_WITH_NI
3318 const_SLOT(end_of_instances_list, user);
3319 #endif
3320 // I expect user->current_instance to be initialized with some value.
3321 do
3322 {
3323 #ifndef FCS_WITHOUT_MAX_NUM_STATES
3324 process_ret = false;
3325 #endif
3326 const_AUTO(instance_item, curr_inst(user));
3327
3328 #ifdef FCS_WITH_FLARES
3329 if (instance_item->current_plan_item_idx ==
3330 instance_item->num_plan_items)
3331 {
3332 // If all the plan items finished so far, it means this instance
3333 // cannot be reused, because it will always yield a cannot
3334 // be found result. So instead of looping infinitely,
3335 // move to the next instance, or exit. */
3336 if (instance_item->all_plan_items_finished_so_far)
3337 {
3338 user__recycle_instance_item(user, instance_item);
3339 BUMP_CURR_INST();
3340 }
3341 // Otherwise - restart the plan again.
3342 else
3343 {
3344 instance_item->all_plan_items_finished_so_far = true;
3345 instance_item->current_plan_item_idx = 0;
3346 }
3347 }
3348
3349 flares_plan_item *const current_plan_item =
3350 &(instance_item->plan[instance_item->current_plan_item_idx++]);
3351 if (current_plan_item->type == FLARES_PLAN_CHECKPOINT)
3352 {
3353 if (instance_item->minimal_flare)
3354 {
3355 SET_ACTIVE_FLARE(user, instance_item->minimal_flare);
3356 user->init_num_checked_states = OBJ_STATS(user);
3357 return SET_user_ret(user, FCS_STATE_WAS_SOLVED);
3358 }
3359 continue;
3360 }
3361
3362 const flare_iters_quota iters_quota =
3363 current_plan_item->remaining_quota;
3364
3365 flare_item *const flare = current_plan_item->flare;
3366 #else
3367 flare_item *const flare = &(instance_item->single_flare);
3368 #endif
3369 fcs_instance *const instance = &(flare->obj);
3370 SET_ACTIVE_FLARE(user, flare);
3371 user->init_num_checked_states = instance->i__stats;
3372 #ifndef FCS_WITHOUT_MAX_NUM_STATES
3373 const bool began_now = (flare->ret_code == FCS_STATE_NOT_BEGAN_YET);
3374 #else
3375 const bool began_now = true;
3376 #endif
3377
3378 if (began_now)
3379 {
3380 if (unlikely(!start_flare(user, instance)))
3381 {
3382 return SET_user_ret(user, FCS_STATE_INVALID_STATE);
3383 }
3384 start_process_with_board(instance, &(user->state),
3385 #if defined(FCS_WITH_FLARES) || !defined(FCS_DISABLE_PATSOLVE)
3386 &(user->initial_non_canonized_state)
3387 #else
3388 NULL
3389 #endif
3390 );
3391 }
3392
3393 #ifndef FCS_WITHOUT_MAX_NUM_STATES
3394 const_AUTO(current_iterations_limit,
3395 (user->current_iterations_limit < 0
3396 ? user->current_soft_iterations_limit
3397 : user->current_soft_iterations_limit < 0
3398 ? user->current_iterations_limit
3399 : min(user->current_iterations_limit,
3400 user->current_soft_iterations_limit)));
3401 process_ret = set_upper_limit(
3402 user, instance_item, instance, current_iterations_limit
3403 #ifdef FCS_WITH_FLARES
3404 ,
3405 iters_quota
3406 #endif
3407 );
3408 #endif
3409
3410 #ifndef FCS_WITHOUT_MAX_NUM_STATES
3411 const bool was_run_now =
3412 began_now || (flare->ret_code == FCS_STATE_SUSPEND_PROCESS);
3413 #else
3414 const bool was_run_now = true;
3415 #endif
3416 ;
3417 if (was_run_now)
3418 {
3419 ret =
3420 #ifndef FCS_WITHOUT_MAX_NUM_STATES
3421 (instance->effective_max_num_checked_states >
3422 instance->i__stats.num_checked_states
3423 ? resume_instance(instance)
3424 : FCS_STATE_SUSPEND_PROCESS)
3425 #else
3426 resume_instance(instance)
3427 #endif
3428 ;
3429 #ifndef FCS_WITHOUT_MAX_NUM_STATES
3430 SET_flare_ret(flare, ret);
3431 #endif
3432 flare->instance_is_ready = false;
3433 }
3434 #ifdef FCS_WITH_NI
3435 if (ret != FCS_STATE_SUSPEND_PROCESS)
3436 {
3437 user->all_instances_were_suspended = false;
3438 }
3439 #endif
3440
3441 flare__update_stats(user, instance, flare
3442 #ifdef FCS_WITH_FLARES
3443 ,
3444 iters_quota, current_plan_item
3445 #endif
3446 );
3447
3448 if (ret == FCS_STATE_WAS_SOLVED)
3449 {
3450 #if defined(FCS_WITH_MOVES)
3451 flare->was_solution_traced = false;
3452 #endif
3453 #ifdef FCS_WITH_FLARES
3454 if ((!(instance_item->minimal_flare)) ||
3455 (get_flare_move_count(user, instance_item->minimal_flare) >
3456 get_flare_move_count(user, flare)))
3457 {
3458 instance_item->minimal_flare = flare;
3459 }
3460 ret = FCS_STATE_IS_NOT_SOLVEABLE;
3461 #else
3462 return SET_user_ret(user, FCS_STATE_WAS_SOLVED);
3463 #endif
3464 }
3465 else if (ret == FCS_STATE_IS_NOT_SOLVEABLE)
3466 {
3467 #ifdef FCS_WITH_FLARES
3468 if (was_run_now)
3469 {
3470 recycle_inst(instance);
3471 flare->instance_is_ready = true;
3472 }
3473 #else
3474 user__recycle_instance_item(user, instance_item);
3475 BUMP_CURR_INST();
3476 #endif
3477 }
3478 #ifndef FCS_WITHOUT_MAX_NUM_STATES
3479 else if (ret == FCS_STATE_SUSPEND_PROCESS)
3480 {
3481 #ifdef FCS_WITH_FLARES
3482 instance_item->intract_minimal_flare = flare;
3483 #endif
3484 #if defined(FCS_WITH_MOVES)
3485 flare->was_solution_traced = false;
3486 #endif
3487 // First - check if we exceeded our limit. If so - we must terminate
3488 // and return now.
3489 if (((current_iterations_limit >= 0) &&
3490 (user->iterations_board_started_at.num_checked_states >=
3491 (fcs_iters_int)current_iterations_limit))
3492 #ifndef FCS_DISABLE_NUM_STORED_STATES
3493 || (instance->i__stats.num_states_in_collection >=
3494 instance->effective_max_num_states_in_collection)
3495 #endif
3496 )
3497 {
3498 // Bug fix:
3499 // We need to resume from the last flare in case we exceed
3500 // the board iterations limit.
3501 #ifdef FCS_WITH_FLARES
3502 if (current_plan_item->type != FLARES_PLAN_RUN_COUNT_ITERS ||
3503 current_plan_item->remaining_quota)
3504 {
3505 --instance_item->current_plan_item_idx;
3506 break;
3507 }
3508 #else
3509 break;
3510 #endif
3511 }
3512
3513 #ifdef FCS_WITH_FLARES
3514 current_plan_item->remaining_quota =
3515 current_plan_item->initial_quota;
3516 #endif
3517 // Determine if we exceeded the instance-specific quota and if
3518 // so, designate it as unsolvable.
3519 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
3520 if ((local_limit() >= 0) &&
3521 (instance->i__stats.num_checked_states >= local_limit()))
3522 {
3523 flare->obj_stats = instance->i__stats;
3524 user__recycle_instance_item(user, instance_item);
3525 BUMP_CURR_INST();
3526 }
3527 #endif
3528 #ifdef FCS_WITH_FLARES
3529 instance_item->all_plan_items_finished_so_far = false;
3530 #elif !defined(FCS_WITHOUT_MAX_NUM_STATES)
3531 SET_flare_ret(flare, FCS_STATE_IS_NOT_SOLVEABLE);
3532 #endif
3533 }
3534 #endif
3535
3536 } while (
3537 #ifdef FCS_WITH_NI
3538 (user->current_instance < end_of_instances_list) &&
3539 #endif
3540 true);
3541
3542 return SET_user_ret(user, eval_resume_ret_code(user, ret, process_ret));
3543 }
3544
3545 #ifndef FCS_WITHOUT_EXPORTED_RESUME_SOLUTION
freecell_solver_user_resume_solution(void * const api_instance)3546 int DLLEXPORT freecell_solver_user_resume_solution(void *const api_instance)
3547 {
3548 const fc_solve_solve_process_ret_t ret =
3549 resume_solution((fcs_user *)api_instance);
3550 return (int)ret;
3551 }
3552 #endif
3553
freecell_solver_user_solve_board(void * const api_instance,const char * const state_as_string)3554 int DLLEXPORT freecell_solver_user_solve_board(
3555 void *const api_instance, const char *const state_as_string)
3556 {
3557 fcs_user *const user = (fcs_user *)api_instance;
3558
3559 if (!duplicate_string(user->state_string_copy, state_as_string))
3560 {
3561 return FCS_STATE_VALIDITY__PREMATURE_END_OF_INPUT;
3562 }
3563 #ifdef FCS_WITH_NI
3564 user->current_instance = user->instances_list;
3565 #endif
3566 #ifndef FCS_FREECELL_ONLY
3567 {
3568 FLARES_LOOP_START()
3569 fc_solve_apply_preset_by_ptr(&(flare->obj), &(user->common_preset));
3570 INSTANCE_ITEM_FLARES_LOOP_END()
3571 INSTANCES_LOOP_END()
3572 }
3573 #endif
3574 #ifdef FCS_WITH_FLARES
3575 if (user_compile_all_flares_plans(user) != FCS_COMPILE_FLARES_RET_OK)
3576 {
3577 return FCS_STATE_FLARES_PLAN_ERROR;
3578 }
3579 INSTANCES_LOOP_START()
3580 const_SLOT(num_plan_items, instance_item);
3581 const_SLOT(plan, instance_item);
3582 for (size_t i = 0; i < num_plan_items; i++)
3583 {
3584 flares_plan_item *item = plan + i;
3585 item->remaining_quota = item->initial_quota;
3586 }
3587 INSTANCES_LOOP_END()
3588 #endif
3589
3590 #ifdef FCS_WITHOUT_EXPORTED_RESUME_SOLUTION
3591 const fc_solve_solve_process_ret_t ret = resume_solution(user);
3592 return (int)ret;
3593 #else
3594 return freecell_solver_user_resume_solution(api_instance);
3595 #endif
3596 }
3597
3598 #ifdef FCS_WITH_MOVES
3599 #ifdef FCS_WITH_FLARES
SINGLE_FLARE(fcs_user * user)3600 static inline flare_item *SINGLE_FLARE(fcs_user *user)
3601 {
3602 var_AUTO(inst, curr_inst(user));
3603 return inst->minimal_flare ? inst->minimal_flare
3604 : inst->intract_minimal_flare;
3605 }
3606 #else
3607 #define SINGLE_FLARE(user) (&(curr_inst(user)->single_flare))
3608 #endif
calc_moves_flare(fcs_user * const user)3609 static inline flare_item *calc_moves_flare(fcs_user *const user)
3610 {
3611 flare_item *const flare = SINGLE_FLARE(user);
3612 trace_flare_solution(user, flare);
3613 return flare;
3614 }
3615
freecell_solver_user_get_next_move(void * const api_instance,fcs_move_t * const user_move)3616 int DLLEXPORT freecell_solver_user_get_next_move(
3617 void *const api_instance, fcs_move_t *const user_move)
3618 {
3619 fcs_user *const user = (fcs_user *)api_instance;
3620
3621 if (!((user->ret_code == FCS_STATE_WAS_SOLVED) ||
3622 (user->ret_code == FCS_STATE_SUSPEND_PROCESS)))
3623 {
3624 return 1;
3625 }
3626 flare_item *const flare = calc_moves_flare(user);
3627 if (flare->next_move_idx == flare->moves_seq.num_moves)
3628 {
3629 return 1;
3630 }
3631
3632 #ifndef HARD_CODED_ALL
3633 var_AUTO(instance, user_obj(user));
3634 #endif
3635
3636 fc_solve_apply_move(&(user->running_state.s), NULL,
3637 user_move_to_internal_move(
3638 *user_move = flare->moves_seq.moves[flare->next_move_idx++])
3639 PASS_FREECELLS(INSTANCE_FREECELLS_NUM)
3640 PASS_STACKS(INSTANCE_STACKS_NUM));
3641
3642 return 0;
3643 }
3644
freecell_solver_user_current_state_stringify(void * api_instance,char * const output_string FC_SOLVE__PASS_PARSABLE (int parseable_output),int canonized_order_output FC_SOLVE__PASS_T (int display_10_as_t))3645 DLLEXPORT void freecell_solver_user_current_state_stringify(void *api_instance,
3646 char *const output_string FC_SOLVE__PASS_PARSABLE(int parseable_output),
3647 int canonized_order_output FC_SOLVE__PASS_T(int display_10_as_t))
3648 {
3649 fcs_user *const user = (fcs_user *)api_instance;
3650 #ifndef HARD_CODED_ALL
3651 var_AUTO(instance, user_obj(user));
3652 #endif
3653
3654 fc_solve_state_as_string(output_string, &(user->running_state.s),
3655 &(user->initial_state_locs)PASS_FREECELLS(INSTANCE_FREECELLS_NUM)
3656 PASS_STACKS(INSTANCE_STACKS_NUM) PASS_DECKS(INSTANCE_DECKS_NUM)
3657 FC_SOLVE__PASS_PARSABLE(parseable_output),
3658 canonized_order_output FC_SOLVE__PASS_T(display_10_as_t));
3659 }
3660
3661 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
freecell_solver_user_current_state_as_string(void * api_instance FC_SOLVE__PASS_PARSABLE (int parseable_output),int canonized_order_output FC_SOLVE__PASS_T (int display_10_as_t))3662 DLLEXPORT char *freecell_solver_user_current_state_as_string(
3663 void *api_instance FC_SOLVE__PASS_PARSABLE(int parseable_output),
3664 int canonized_order_output FC_SOLVE__PASS_T(int display_10_as_t))
3665 {
3666 char *state_as_string = SMALLOC(state_as_string, 1000);
3667 freecell_solver_user_current_state_stringify(api_instance,
3668 state_as_string FC_SOLVE__PASS_PARSABLE(parseable_output),
3669 canonized_order_output FC_SOLVE__PASS_T(display_10_as_t));
3670 return state_as_string;
3671 }
3672 #endif
3673 #endif
3674
user_free_resources(fcs_user * const user)3675 static MYINLINE void user_free_resources(fcs_user *const user)
3676 {
3677 FLARES_LOOP_START()
3678 {
3679 fcs_instance *const instance = &(flare->obj);
3680
3681 if (!flare->instance_is_ready)
3682 {
3683 fc_solve_finish_instance(instance);
3684 }
3685 #if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH)
3686 fc_solve_hash_free(&(instance->hash));
3687 #endif
3688 #ifdef INDIRECT_STACK_STATES
3689 #if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH)
3690 fc_solve_hash_free(&(instance->stacks_hash));
3691 #endif
3692 #endif
3693 free_instance(instance);
3694 #ifdef FCS_WITH_FLARES
3695 flare->name[0] = '\0';
3696 #endif
3697 #ifndef FCS_WITHOUT_FC_PRO_MOVES_COUNT
3698 fc_solve_moves_processed_free(&(flare->fc_pro_moves));
3699 #endif
3700 #ifdef FCS_WITH_MOVES
3701 if (flare->moves_seq.moves)
3702 {
3703 free(flare->moves_seq.moves);
3704 flare->moves_seq.moves = NULL;
3705 flare->moves_seq.num_moves = 0;
3706 }
3707 #endif
3708 }
3709 INSTANCE_ITEM_FLARES_LOOP_END()
3710 #ifdef FCS_WITH_FLARES
3711 free(instance_item->flares);
3712 if (instance_item->flares_plan_string)
3713 {
3714 free(instance_item->flares_plan_string);
3715 }
3716 if (instance_item->plan)
3717 {
3718 free(instance_item->plan);
3719 }
3720 #endif
3721 INSTANCES_LOOP_END()
3722
3723 #ifdef FCS_WITH_NI
3724 free(user->instances_list);
3725 #endif
3726 fc_solve_meta_compact_allocator_finish(&(user->meta_alloc));
3727 #ifndef FCS_USE_PRECOMPILED_CMD_LINE_THEME
3728 for (size_t i = 0; i < COUNT(user->unrecognized_cmd_line_options); ++i)
3729 {
3730 free(user->unrecognized_cmd_line_options[i]);
3731 }
3732 #endif
3733 }
3734
freecell_solver_user_free(void * const api_instance)3735 void DLLEXPORT freecell_solver_user_free(void *const api_instance)
3736 {
3737 fcs_user *const user = (fcs_user *)api_instance;
3738 user_free_resources(user);
3739 free(user);
3740 }
3741
3742 int DLLEXPORT __attribute__((pure))
freecell_solver_user_get_current_depth(void * const api_instance)3743 freecell_solver_user_get_current_depth(void *const api_instance)
3744 {
3745 return (int)(DFS_VAR(api_soft_thread(api_instance), depth));
3746 }
3747
3748 #ifndef FCS_DISABLE_PATSOLVE
freecell_solver_user_set_patsolve_x_param(void * const api_instance,const int position,const int x_param_val FCS__PASS_ERR_STR (char ** const error_string))3749 extern int DLLEXPORT freecell_solver_user_set_patsolve_x_param(
3750 void *const api_instance, const int position,
3751 const int x_param_val FCS__PASS_ERR_STR(char **const error_string))
3752 {
3753 const_SLOT(pats_scan, api_soft_thread(api_instance));
3754 if (!pats_scan)
3755 {
3756 ALLOC_ERROR_STRING(error_string, "Not using the \"patsolve\" scan.");
3757 return 1;
3758 }
3759 if ((position < 0) ||
3760 (position >= (int)(COUNT(pats_scan->pats_solve_params.x))))
3761 {
3762 ALLOC_ERROR_STRING(error_string, "Position out of range.");
3763 return 2;
3764 }
3765
3766 pats_scan->pats_solve_params.x[position] = x_param_val;
3767 fc_solve_pats__set_cut_off(pats_scan);
3768 return 0;
3769 }
3770 #endif
3771
3772 #ifndef FCS_DISABLE_PATSOLVE
freecell_solver_user_set_patsolve_y_param(void * const api_instance,const int position,const double y_param_val FCS__PASS_ERR_STR (char ** const error_string))3773 extern int DLLEXPORT freecell_solver_user_set_patsolve_y_param(
3774 void *const api_instance, const int position,
3775 const double y_param_val FCS__PASS_ERR_STR(char **const error_string))
3776 {
3777 const_SLOT(pats_scan, api_soft_thread(api_instance));
3778 if (!pats_scan)
3779 {
3780 ALLOC_ERROR_STRING(error_string, "Not using the \"patsolve\" scan.");
3781 return 1;
3782 }
3783 if ((position < 0) ||
3784 (position >= (int)(COUNT(pats_scan->pats_solve_params.y))))
3785 {
3786 ALLOC_ERROR_STRING(error_string, "Position out of range.");
3787 return 2;
3788 }
3789
3790 pats_scan->pats_solve_params.y[position] = y_param_val;
3791 return 0;
3792 }
3793 #endif
3794
freecell_solver_user_set_solving_method(void * const api_instance,const int int_method)3795 void DLLEXPORT freecell_solver_user_set_solving_method(
3796 void *const api_instance, const int int_method)
3797 {
3798 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
3799 if (int_method == FCS_METHOD_HARD_DFS)
3800 {
3801 freecell_solver_user_set_solving_method(
3802 api_instance, FCS_METHOD_SOFT_DFS);
3803 return;
3804 }
3805 #endif
3806 fcs_super_method_type super_method_type = FCS_SUPER_METHOD_BEFS_BRFS;
3807 fcs_soft_thread *const soft_thread = api_soft_thread(api_instance);
3808 switch (int_method)
3809 {
3810 case FCS_METHOD_BFS:
3811 soft_thread->is_befs = false;
3812 break;
3813
3814 case FCS_METHOD_A_STAR:
3815 soft_thread->is_befs = true;
3816 break;
3817
3818 case FCS_METHOD_RANDOM_DFS:
3819 case FCS_METHOD_SOFT_DFS: {
3820 super_method_type = FCS_SUPER_METHOD_DFS;
3821 soft_thread->master_to_randomize =
3822 (int_method == FCS_METHOD_RANDOM_DFS);
3823 }
3824 break;
3825
3826 #ifndef FCS_DISABLE_PATSOLVE
3827 case FCS_METHOD_PATSOLVE: {
3828 super_method_type = FCS_SUPER_METHOD_PATSOLVE;
3829
3830 if (!soft_thread->pats_scan)
3831 {
3832 typeof(soft_thread->pats_scan) pats_scan = soft_thread->pats_scan =
3833 SMALLOC1(soft_thread->pats_scan);
3834 if (!pats_scan)
3835 {
3836 break;
3837 }
3838 fc_solve_pats__init_soft_thread(
3839 pats_scan, fcs_st_instance(soft_thread));
3840
3841 pats_scan->to_stack = true;
3842
3843 pats_scan->pats_solve_params =
3844 (freecell_solver_pats__x_y_params_preset
3845 [FC_SOLVE_PATS__PARAM_PRESET__FreecellSpeed]);
3846 fc_solve_pats__set_cut_off(pats_scan);
3847 }
3848 }
3849 break;
3850 #endif
3851 }
3852
3853 soft_thread->super_method_type = super_method_type;
3854 }
3855
3856 #ifndef FCS_USE_PRECOMPILED_CMD_LINE_THEME
freecell_solver_user_set_unrecognized_cmd_line_flag(void * const api_instance,const int flag_idx,const char * val)3857 int DLLEXPORT freecell_solver_user_set_unrecognized_cmd_line_flag(
3858 void *const api_instance, const int flag_idx, const char *val)
3859 {
3860 if (flag_idx != 0)
3861 {
3862 return 1;
3863 }
3864
3865 fcs_user *const user = (fcs_user *)api_instance;
3866 free(user->unrecognized_cmd_line_options[flag_idx]);
3867 user->unrecognized_cmd_line_options[flag_idx] = (val ? strdup(val) : NULL);
3868
3869 return 0;
3870 }
3871
3872 DLLEXPORT fc_solve_unrecognized_flag_status_type __attribute__((pure))
freecell_solver_user_get_unrecognized_cmd_line_flag_status(void * const api_instance,const int flag_idx)3873 freecell_solver_user_get_unrecognized_cmd_line_flag_status(
3874 void *const api_instance, const int flag_idx)
3875 {
3876 if (flag_idx != 0)
3877 {
3878 return FC_SOLVE__FLAG_STATUS__ERROR;
3879 }
3880
3881 fcs_user *const user = (fcs_user *)api_instance;
3882 const_AUTO(ret, user->unrecognized_cmd_line_options[flag_idx]);
3883 return (
3884 ret ? FC_SOLVE__FLAG_STATUS__VALID : FC_SOLVE__FLAG_STATUS__IS_NULL);
3885 }
3886
freecell_solver_user_get_unrecognized_cmd_line_flag(void * const api_instance,const int flag_idx)3887 DLLEXPORT char *freecell_solver_user_get_unrecognized_cmd_line_flag(
3888 void *const api_instance, const int flag_idx)
3889 {
3890 if (flag_idx != 0)
3891 {
3892 return NULL;
3893 }
3894
3895 fcs_user *const user = (fcs_user *)api_instance;
3896 const_AUTO(ret, user->unrecognized_cmd_line_options[flag_idx]);
3897 return strdup(ret ? ret : "");
3898 }
3899
3900 #endif
3901
3902 #if !(defined(FCS_BREAK_BACKWARD_COMPAT_1) && defined(FCS_FREECELL_ONLY))
3903 #ifndef HARD_CODED_NUM_FREECELLS
3904
freecell_solver_user_set_num_freecells(void * const api_instance,const int freecells_num)3905 int DLLEXPORT freecell_solver_user_set_num_freecells(
3906 void *const api_instance, const int freecells_num)
3907 {
3908 if ((freecells_num < 0) || (freecells_num > MAX_NUM_FREECELLS))
3909 {
3910 return 1;
3911 }
3912
3913 fcs_user *const user = (fcs_user *)api_instance;
3914 user->common_preset.game_params.freecells_num =
3915 (fcs_game_limit)freecells_num;
3916 apply_game_params_for_all_instances(user);
3917
3918 return 0;
3919 }
3920
3921 #ifdef FC_SOLVE_JAVASCRIPT_QUERYING
freecell_solver_user_get_num_freecells(void * const api_instance)3922 int DLLEXPORT freecell_solver_user_get_num_freecells(void *const api_instance)
3923 {
3924 return (((fcs_user *const)api_instance)
3925 ->common_preset.game_params.freecells_num);
3926 }
3927 #endif
3928
3929 #else
3930
freecell_solver_user_set_num_freecells(void * api_instance GCC_UNUSED,int freecells_num GCC_UNUSED)3931 int DLLEXPORT __attribute__((const)) freecell_solver_user_set_num_freecells(
3932 void *api_instance GCC_UNUSED, int freecells_num GCC_UNUSED)
3933 {
3934 return 0;
3935 }
3936
3937 #endif
3938
3939 #ifndef HARD_CODED_NUM_STACKS
freecell_solver_user_set_num_stacks(void * const api_instance,const int stacks_num)3940 int DLLEXPORT freecell_solver_user_set_num_stacks(
3941 void *const api_instance, const int stacks_num)
3942 {
3943 if ((stacks_num < 0) || (stacks_num > MAX_NUM_STACKS))
3944 {
3945 return 1;
3946 }
3947
3948 fcs_user *const user = (fcs_user *)api_instance;
3949 user->common_preset.game_params.stacks_num = (fcs_game_limit)stacks_num;
3950 apply_game_params_for_all_instances(user);
3951
3952 return 0;
3953 }
3954
3955 #ifdef FC_SOLVE_JAVASCRIPT_QUERYING
freecell_solver_user_get_num_stacks(void * const api_instance)3956 int DLLEXPORT freecell_solver_user_get_num_stacks(void *const api_instance)
3957 {
3958 return (
3959 ((fcs_user *const)api_instance)->common_preset.game_params.stacks_num);
3960 }
3961 #endif
3962
3963 #else
3964
freecell_solver_user_set_num_stacks(void * api_instance GCC_UNUSED,int stacks_num GCC_UNUSED)3965 int DLLEXPORT __attribute__((const)) freecell_solver_user_set_num_stacks(
3966 void *api_instance GCC_UNUSED, int stacks_num GCC_UNUSED)
3967 {
3968 return 0;
3969 }
3970
3971 #endif
3972
3973 #ifndef HARD_CODED_NUM_DECKS
freecell_solver_user_set_num_decks(void * api_instance,int decks_num)3974 int DLLEXPORT freecell_solver_user_set_num_decks(
3975 void *api_instance, int decks_num)
3976 {
3977 fcs_user *const user = (fcs_user *)api_instance;
3978
3979 if ((decks_num < 0) || (decks_num > MAX_NUM_DECKS))
3980 {
3981 return 1;
3982 }
3983
3984 user->common_preset.game_params.decks_num = (fcs_game_limit)decks_num;
3985 apply_game_params_for_all_instances(user);
3986
3987 return 0;
3988 }
3989
3990 #else
3991
freecell_solver_user_set_num_decks(void * api_instance GCC_UNUSED,int decks_num GCC_UNUSED)3992 int DLLEXPORT __attribute__((const)) freecell_solver_user_set_num_decks(
3993 void *api_instance GCC_UNUSED, int decks_num GCC_UNUSED)
3994 {
3995 return 0;
3996 }
3997
3998 #endif
3999 #endif
4000
4001 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
4002 int DLLEXPORT
4003 #if ((!defined(FCS_FREECELL_ONLY)) || (!defined(HARD_CODED_NUM_STACKS)) || \
4004 (!defined(HARD_CODED_NUM_FREECELLS)) || (!defined(HARD_CODED_NUM_DECKS)))
4005 #else
4006 __attribute__((const))
4007 #endif
freecell_solver_user_set_game(void * const api_instance,const int freecells_num,const int stacks_num,const int decks_num,const int sequences_are_built_by GCC_UNUSED,const int unlimited_sequence_move GCC_UNUSED,const int empty_stacks_fill GCC_UNUSED)4008 freecell_solver_user_set_game(void *const api_instance, const int freecells_num,
4009 const int stacks_num, const int decks_num,
4010 const int sequences_are_built_by GCC_UNUSED,
4011 const int unlimited_sequence_move GCC_UNUSED,
4012 const int empty_stacks_fill GCC_UNUSED)
4013 {
4014 if (freecell_solver_user_set_num_freecells(api_instance, freecells_num))
4015 {
4016 return 1;
4017 }
4018 if (freecell_solver_user_set_num_stacks(api_instance, stacks_num))
4019 {
4020 return 2;
4021 }
4022 if (freecell_solver_user_set_num_decks(api_instance, decks_num))
4023 {
4024 return 3;
4025 }
4026 #ifndef FCS_FREECELL_ONLY
4027 if (freecell_solver_user_set_sequences_are_built_by_type(
4028 api_instance, sequences_are_built_by))
4029 {
4030 return 4;
4031 }
4032 if (freecell_solver_user_set_sequence_move(
4033 api_instance, unlimited_sequence_move))
4034 {
4035 return 5;
4036 }
4037 if (freecell_solver_user_set_empty_stacks_filled_by(
4038 api_instance, empty_stacks_fill))
4039 {
4040 return 6;
4041 }
4042 #endif
4043 return 0;
4044 }
4045 #endif
4046
4047 fcs_iters_int DLLEXPORT __attribute__((pure))
freecell_solver_user_get_num_times_long(void * api_instance)4048 freecell_solver_user_get_num_times_long(void *api_instance)
4049 {
4050 fcs_user *const user = (fcs_user *)api_instance;
4051 return get_num_times_long(user);
4052 }
4053
4054 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
4055 int DLLEXPORT __attribute__((pure))
freecell_solver_user_get_num_times(void * const api_instance)4056 freecell_solver_user_get_num_times(void *const api_instance)
4057 {
4058 return (int)freecell_solver_user_get_num_times_long(api_instance);
4059 }
4060
4061 int DLLEXPORT __attribute__((const))
freecell_solver_user_get_limit_iterations(void * const api_instance GCC_UNUSED)4062 freecell_solver_user_get_limit_iterations(void *const api_instance GCC_UNUSED)
4063 {
4064 #ifndef FCS_WITHOUT_MAX_NUM_STATES
4065 return (int)active_obj(api_instance)->effective_max_num_checked_states;
4066 #else
4067 return 0;
4068 #endif
4069 }
4070 #endif
4071
4072 #ifdef FCS_WITH_MOVES
freecell_solver_user_get_moves_left(void * const api_instance GCC_UNUSED)4073 int DLLEXPORT freecell_solver_user_get_moves_left(
4074 void *const api_instance GCC_UNUSED)
4075 {
4076 fcs_user *const user = (fcs_user *)api_instance;
4077 if (user->ret_code == FCS_STATE_WAS_SOLVED)
4078 {
4079 const flare_item *const flare = calc_moves_flare(user);
4080 return (int)(flare->moves_seq.num_moves - flare->next_move_idx);
4081 }
4082 else
4083 {
4084 return 0;
4085 }
4086 }
4087 #endif
4088
4089 #ifdef FCS_WITH_MOVES
freecell_solver_user_set_solution_optimization(void * const api_instance,const int optimize)4090 void DLLEXPORT freecell_solver_user_set_solution_optimization(
4091 void *const api_instance, const int optimize)
4092 {
4093 STRUCT_SET_FLAG_TO(
4094 active_obj(api_instance), FCS_RUNTIME_OPTIMIZE_SOLUTION_PATH, optimize);
4095 }
4096
freecell_solver_user_stringify_move_w_state(void * const api_instance,char * const output_string,const fcs_move_t move,const int standard_notation)4097 DLLEXPORT extern void freecell_solver_user_stringify_move_w_state(
4098 void *const api_instance, char *const output_string, const fcs_move_t move,
4099 const int standard_notation)
4100 {
4101 fcs_user *const user = (fcs_user *)api_instance;
4102
4103 fc_solve_move_to_string_w_state(
4104 output_string, &(user->running_state), move, standard_notation);
4105 }
4106 #endif
4107
4108 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
freecell_solver_user_move_to_string(const fcs_move_t move GCC_UNUSED,const int standard_notation GCC_UNUSED)4109 DLLEXPORT char *freecell_solver_user_move_to_string(
4110 const fcs_move_t move GCC_UNUSED, const int standard_notation GCC_UNUSED)
4111 {
4112 char *const ret = SMALLOC(ret, 256);
4113 if (!ret)
4114 {
4115 return NULL;
4116 }
4117 #ifdef FCS_WITH_MOVES
4118 fc_solve_move_to_string_w_state(
4119 ret, NULL, move, (standard_notation == 2) ? 1 : standard_notation);
4120 #else
4121 ret[0] = '\0';
4122 #endif
4123 return ret;
4124 }
4125
freecell_solver_user_move_to_string_w_state(void * const api_instance GCC_UNUSED,const fcs_move_t move GCC_UNUSED,const int standard_notation GCC_UNUSED)4126 DLLEXPORT char *freecell_solver_user_move_to_string_w_state(
4127 void *const api_instance GCC_UNUSED, const fcs_move_t move GCC_UNUSED,
4128 const int standard_notation GCC_UNUSED)
4129 {
4130 char *ret = SMALLOC(ret, 256);
4131 if (!ret)
4132 {
4133 return NULL;
4134 }
4135 #ifdef FCS_WITH_MOVES
4136 freecell_solver_user_stringify_move_w_state(
4137 api_instance, ret, move, standard_notation);
4138 #else
4139 ret[0] = '\0';
4140 #endif
4141 return ret;
4142 }
4143 #endif
4144
4145 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
freecell_solver_user_limit_depth(void * const api_instance GCC_UNUSED,const int max_depth GCC_UNUSED)4146 void DLLEXPORT freecell_solver_user_limit_depth(
4147 void *const api_instance GCC_UNUSED, const int max_depth GCC_UNUSED)
4148 {
4149 }
4150 #endif
4151
4152 #if !(defined(FCS_BREAK_BACKWARD_COMPAT_1) && defined(FCS_FREECELL_ONLY))
4153 int DLLEXPORT __attribute__((const))
freecell_solver_user_get_max_num_freecells(void)4154 freecell_solver_user_get_max_num_freecells(void)
4155 {
4156 return MAX_NUM_FREECELLS;
4157 }
4158
4159 int DLLEXPORT __attribute__((const))
freecell_solver_user_get_max_num_stacks(void)4160 freecell_solver_user_get_max_num_stacks(void)
4161 {
4162 return MAX_NUM_STACKS;
4163 }
4164
4165 int DLLEXPORT __attribute__((const))
freecell_solver_user_get_max_num_decks(void)4166 freecell_solver_user_get_max_num_decks(void)
4167 {
4168 return MAX_NUM_DECKS;
4169 }
4170 #endif
4171
4172 #ifdef FCS_WITH_ERROR_STRS
freecell_solver_user_get_invalid_state_error_into_string(void * const api_instance,char * const string,const int print_ts GCC_UNUSED)4173 void freecell_solver_user_get_invalid_state_error_into_string(
4174 void *const api_instance, char *const string
4175 #ifndef FC_SOLVE_IMPLICIT_T_RANK
4176 ,
4177 const int print_ts GCC_UNUSED
4178 #endif
4179 )
4180 {
4181 fcs_user *const user = (fcs_user *)api_instance;
4182
4183 const_AUTO(ret, user->state_validity_ret);
4184 switch (ret)
4185 {
4186 case FCS_STATE_VALIDITY__OK:
4187 string[0] = '\0';
4188 break;
4189
4190 case FCS_STATE_VALIDITY__EMPTY_SLOT:
4191 strcpy(string, "There's an empty slot in one of the stacks.");
4192 break;
4193
4194 case FCS_STATE_VALIDITY__MISSING_CARD:
4195 case FCS_STATE_VALIDITY__EXTRA_CARD: {
4196 // user->state_validity_card is only valid for these states,
4197 // so we should call fc_solve_card_stringify on there only.
4198 char card_str[4];
4199 #ifdef FCS_WITH_MOVES
4200 fc_solve_card_stringify(
4201 user->state_validity_card, card_str FC_SOLVE__PASS_T(print_ts));
4202 #else
4203 card_str[0] = '\0';
4204 #endif
4205
4206 sprintf(string, "%s%s.",
4207 ((ret == FCS_STATE_VALIDITY__EXTRA_CARD)
4208 ? "There's an extra card: "
4209 : "There's a missing card: "),
4210 card_str);
4211 }
4212 break;
4213
4214 case FCS_STATE_VALIDITY__PREMATURE_END_OF_INPUT:
4215 strcpy(string, "Not enough input.");
4216 break;
4217 }
4218 }
4219 #endif
4220
4221 #ifdef FCS_WITH_ERROR_STRS
4222 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
freecell_solver_user_get_invalid_state_error_string(void * const api_instance,const int print_ts)4223 char *freecell_solver_user_get_invalid_state_error_string(
4224 void *const api_instance
4225 #ifndef FC_SOLVE_IMPLICIT_T_RANK
4226 ,
4227 const int print_ts
4228 #endif
4229 )
4230 {
4231 char *ret = malloc(80);
4232 freecell_solver_user_get_invalid_state_error_into_string(
4233 api_instance, ret PASS_T(print_ts));
4234 return ret;
4235 }
4236 #endif
4237 #endif
4238
4239 #ifndef FCS_FREECELL_ONLY
freecell_solver_user_set_sequences_are_built_by_type(void * const api_instance,const int sequences_are_built_by)4240 int DLLEXPORT freecell_solver_user_set_sequences_are_built_by_type(
4241 void *const api_instance, const int sequences_are_built_by)
4242 {
4243 if ((sequences_are_built_by < 0) || (sequences_are_built_by > 2))
4244 {
4245 return 1;
4246 }
4247 fcs_user *const user = (fcs_user *)api_instance;
4248
4249 user->common_preset.game_params.game_flags &= (~0x3);
4250 user->common_preset.game_params.game_flags |= sequences_are_built_by;
4251
4252 apply_game_params_for_all_instances(user);
4253
4254 return 0;
4255 }
4256 #endif
4257
4258 #ifndef FCS_FREECELL_ONLY
freecell_solver_user_set_sequence_move(void * const api_instance,const int unlimited_sequence_move)4259 int DLLEXPORT freecell_solver_user_set_sequence_move(
4260 void *const api_instance, const int unlimited_sequence_move)
4261 {
4262 fcs_user *const user = (fcs_user *)api_instance;
4263
4264 user->common_preset.game_params.game_flags &= (~(1 << 4));
4265 user->common_preset.game_params.game_flags |=
4266 ((unlimited_sequence_move != 0) << 4);
4267
4268 apply_game_params_for_all_instances(user);
4269 return 0;
4270 }
4271 #endif
4272
4273 #ifndef FCS_FREECELL_ONLY
freecell_solver_user_set_empty_stacks_filled_by(void * const api_instance,const int empty_stacks_fill)4274 int DLLEXPORT freecell_solver_user_set_empty_stacks_filled_by(
4275 void *const api_instance, const int empty_stacks_fill)
4276 {
4277 if ((empty_stacks_fill < 0) || (empty_stacks_fill > 2))
4278 {
4279 return 1;
4280 }
4281
4282 fcs_user *const user = (fcs_user *const)api_instance;
4283 user->common_preset.game_params.game_flags &= (~(0x3 << 2));
4284 user->common_preset.game_params.game_flags |= (empty_stacks_fill << 2);
4285 apply_game_params_for_all_instances(user);
4286 return 0;
4287 }
4288 #endif
4289
freecell_solver_user_set_a_star_weight(void * const api_instance,const int my_index,const double weight)4290 int DLLEXPORT freecell_solver_user_set_a_star_weight(
4291 void *const api_instance, const int my_index, const double weight)
4292 {
4293 fcs_soft_thread *const soft_thread = api_soft_thread(api_instance);
4294 if ((my_index < 0) ||
4295 (my_index >=
4296 (int)(COUNT(
4297 BEFS_VAR(soft_thread, weighting).befs_weights.weights))))
4298 {
4299 return 1;
4300 }
4301 if (weight < 0)
4302 {
4303 return 2;
4304 }
4305
4306 BEFS_VAR(soft_thread, weighting).befs_weights.weights[my_index] = weight;
4307 return 0;
4308 }
4309
4310 #ifdef FCS_COMPILE_DEBUG_FUNCTIONS
fc_solve_user_INTERNAL_get_befs_weight(void * const api_instance,const int my_index)4311 double DLLEXPORT __attribute__((pure)) fc_solve_user_INTERNAL_get_befs_weight(
4312 void *const api_instance, const int my_index)
4313 {
4314 return BEFS_VAR(api_soft_thread(api_instance), weighting)
4315 .befs_weights.weights[my_index];
4316 }
4317
4318 #endif
4319
4320 #ifndef FCS_WITHOUT_ITER_HANDLER
freecell_solver_user_set_iter_handler_long(void * api_instance,freecell_solver_user_long_iter_handler_t long_iter_handler,void * iter_handler_context)4321 void DLLEXPORT freecell_solver_user_set_iter_handler_long(void *api_instance,
4322 freecell_solver_user_long_iter_handler_t long_iter_handler,
4323 void *iter_handler_context)
4324 {
4325 set_any_iter_handler(api_instance, long_iter_handler,
4326 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
4327 NULL,
4328 #endif
4329 iter_handler_context);
4330 }
4331 #endif
4332
4333 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
4334 #ifndef FCS_WITHOUT_ITER_HANDLER
freecell_solver_user_set_iter_handler(void * const api_instance,const freecell_solver_user_iter_handler_t iter_handler,void * const iter_handler_context)4335 void DLLEXPORT freecell_solver_user_set_iter_handler(void *const api_instance,
4336 const freecell_solver_user_iter_handler_t iter_handler,
4337 void *const iter_handler_context)
4338 {
4339 set_any_iter_handler(
4340 api_instance, NULL, iter_handler, iter_handler_context);
4341 }
4342 #else
freecell_solver_user_set_iter_handler(void * const api_instance GCC_UNUSED,const freecell_solver_user_iter_handler_t iter_handler GCC_UNUSED,void * const iter_handler_context GCC_UNUSED)4343 void DLLEXPORT freecell_solver_user_set_iter_handler(
4344 void *const api_instance GCC_UNUSED,
4345 const freecell_solver_user_iter_handler_t iter_handler GCC_UNUSED,
4346 void *const iter_handler_context GCC_UNUSED)
4347 {
4348 }
4349 #endif
4350 #endif
4351
4352 #ifdef FCS_WITH_MOVES
4353
4354 #ifdef HARD_CODED_ALL
4355 #define HARD_CODED_UNUSED GCC_UNUSED
4356 #else
4357 #define HARD_CODED_UNUSED
4358 #endif
4359
freecell_solver_user_iter_state_stringify(void * const api_instance HARD_CODED_UNUSED,char * output_string,void * const ptr_state_void FC_SOLVE__PASS_PARSABLE (const int parseable_output),const int canonized_order_output PASS_T (const int display_10_as_t))4360 DLLEXPORT extern void freecell_solver_user_iter_state_stringify(
4361 void *const api_instance HARD_CODED_UNUSED, char *output_string,
4362 void *const ptr_state_void FC_SOLVE__PASS_PARSABLE(
4363 const int parseable_output),
4364 const int canonized_order_output PASS_T(const int display_10_as_t))
4365 {
4366 #ifndef HARD_CODED_ALL
4367 fcs_instance *const instance = active_obj(api_instance);
4368 #endif
4369
4370 const_AUTO(ptr_state, ((const standalone_state_ptrs *const)ptr_state_void));
4371
4372 fc_solve_state_as_string(output_string, ptr_state->key,
4373 &(ptr_state->locs)PASS_FREECELLS(INSTANCE_FREECELLS_NUM)
4374 PASS_STACKS(INSTANCE_STACKS_NUM) PASS_DECKS(INSTANCE_DECKS_NUM)
4375 FC_SOLVE__PASS_PARSABLE(parseable_output),
4376 canonized_order_output PASS_T(display_10_as_t));
4377 }
4378 #endif
4379
4380 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
freecell_solver_user_iter_state_as_string(void * const api_instance GCC_UNUSED,void * const ptr_state_void GCC_UNUSED FC_SOLVE__PASS_PARSABLE (const int parseable_output GCC_UNUSED),const int canonized_order_output GCC_UNUSED PASS_T (const int display_10_as_t GCC_UNUSED))4381 DLLEXPORT char *freecell_solver_user_iter_state_as_string(
4382 void *const api_instance GCC_UNUSED,
4383 void *const ptr_state_void GCC_UNUSED FC_SOLVE__PASS_PARSABLE(
4384 const int parseable_output GCC_UNUSED),
4385 const int canonized_order_output GCC_UNUSED PASS_T(
4386 const int display_10_as_t GCC_UNUSED))
4387 {
4388 char *state_as_string = SMALLOC(state_as_string, 1000);
4389 #ifdef FCS_WITH_MOVES
4390 freecell_solver_user_iter_state_stringify(api_instance, state_as_string,
4391 ptr_state_void FC_SOLVE__PASS_PARSABLE(parseable_output),
4392 canonized_order_output PASS_T(display_10_as_t));
4393 #else
4394 state_as_string[0] = '\0';
4395 #endif
4396 return state_as_string;
4397 }
4398 #endif
4399
freecell_solver_user_set_random_seed(void * const api_instance,const int seed)4400 void DLLEXPORT freecell_solver_user_set_random_seed(
4401 void *const api_instance, const int seed)
4402 {
4403 DFS_VAR(api_soft_thread(api_instance), rand_seed) = seed;
4404 }
4405
4406 #ifndef FCS_DISABLE_NUM_STORED_STATES
4407 fcs_int_limit_t DLLEXPORT __attribute__((pure))
freecell_solver_user_get_num_states_in_collection_long(void * api_instance)4408 freecell_solver_user_get_num_states_in_collection_long(void *api_instance)
4409 {
4410 fcs_user *const user = (fcs_user *)api_instance;
4411
4412 return (fcs_int_limit_t)(
4413 user->iterations_board_started_at.num_states_in_collection +
4414 OBJ_STATS(user).num_states_in_collection -
4415 user->init_num_checked_states.num_states_in_collection);
4416 }
4417
4418 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
4419 int DLLEXPORT __attribute__((pure))
freecell_solver_user_get_num_states_in_collection(void * const api_instance)4420 freecell_solver_user_get_num_states_in_collection(void *const api_instance)
4421 {
4422 return (int)freecell_solver_user_get_num_states_in_collection_long(
4423 api_instance);
4424 }
4425 #endif
4426
freecell_solver_user_limit_num_states_in_collection_long(void * api_instance,fcs_int_limit_t max_num_states)4427 void DLLEXPORT freecell_solver_user_limit_num_states_in_collection_long(
4428 void *api_instance, fcs_int_limit_t max_num_states)
4429 {
4430 active_obj(api_instance)->effective_max_num_states_in_collection =
4431 ((max_num_states < 0) ? FCS_ITERS_INT_MAX
4432 : (fcs_iters_int)max_num_states);
4433 }
4434
4435 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
freecell_solver_user_limit_num_states_in_collection(void * const api_instance,const int max_num_states)4436 void DLLEXPORT freecell_solver_user_limit_num_states_in_collection(
4437 void *const api_instance, const int max_num_states)
4438 {
4439 freecell_solver_user_limit_num_states_in_collection_long(
4440 api_instance, (fcs_int_limit_t)max_num_states);
4441 }
4442 #endif
4443
4444 #ifndef FCS_WITHOUT_TRIM_MAX_STORED_STATES
freecell_solver_set_stored_states_trimming_limit(void * const api_instance GCC_UNUSED,const long max_num_states GCC_UNUSED)4445 DLLEXPORT extern void freecell_solver_set_stored_states_trimming_limit(
4446 void *const api_instance GCC_UNUSED, const long max_num_states GCC_UNUSED)
4447 {
4448 active_obj(api_instance)->effective_trim_states_in_collection_from =
4449 ((max_num_states < 0) ? FCS_ITERS_INT_MAX
4450 : (fcs_iters_int)max_num_states);
4451 }
4452 #endif
4453 #endif
4454
freecell_solver_user_next_soft_thread(void * const api_instance)4455 int DLLEXPORT freecell_solver_user_next_soft_thread(void *const api_instance)
4456 {
4457 fcs_user *const user = (fcs_user *const)api_instance;
4458 const_AUTO(
4459 soft_thread, fc_solve_new_soft_thread(user->soft_thread->hard_thread));
4460 if (soft_thread == NULL)
4461 {
4462 return 1;
4463 }
4464
4465 user->soft_thread = soft_thread;
4466 return 0;
4467 }
4468
freecell_solver_user_set_soft_thread_step(void * const api_instance,const int checked_states_step)4469 extern void DLLEXPORT freecell_solver_user_set_soft_thread_step(
4470 void *const api_instance, const int checked_states_step)
4471 {
4472 api_soft_thread(api_instance)->checked_states_step =
4473 (fcs_iters_int)checked_states_step;
4474 }
4475
4476 #if (!(defined(FCS_SINGLE_HARD_THREAD) && defined(FCS_BREAK_BACKWARD_COMPAT_1)))
freecell_solver_user_next_hard_thread(void * const api_instance)4477 int DLLEXPORT freecell_solver_user_next_hard_thread(void *const api_instance)
4478 {
4479 #ifdef FCS_SINGLE_HARD_THREAD
4480 return freecell_solver_user_next_soft_thread(api_instance);
4481 #else
4482 fcs_user *const user = (fcs_user *)api_instance;
4483
4484 fcs_soft_thread *const soft_thread = new_hard_thread(user_obj(user));
4485
4486 if (soft_thread == NULL)
4487 {
4488 return 1;
4489 }
4490
4491 user->soft_thread = soft_thread;
4492
4493 return 0;
4494 #endif
4495 }
4496 #endif
4497
4498 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
4499 int DLLEXPORT __attribute__((pure))
freecell_solver_user_get_num_soft_threads_in_instance(void * const api_instance)4500 freecell_solver_user_get_num_soft_threads_in_instance(void *const api_instance)
4501 {
4502 return (int)active_obj(api_instance)->next_soft_thread_id;
4503 }
4504 #endif
4505
4506 #ifndef FCS_HARD_CODE_CALC_REAL_DEPTH_AS_FALSE
freecell_solver_user_set_calc_real_depth(void * const api_instance,const int calc_real_depth)4507 void DLLEXPORT freecell_solver_user_set_calc_real_depth(
4508 void *const api_instance, const int calc_real_depth)
4509 {
4510 STRUCT_SET_FLAG_TO(
4511 active_obj(api_instance), FCS_RUNTIME_CALC_REAL_DEPTH, calc_real_depth);
4512 }
4513 #endif
4514
4515 #ifndef FCS_USE_PRECOMPILED_CMD_LINE_THEME
freecell_solver_user_set_soft_thread_name(void * const api_instance,const freecell_solver_str_t name)4516 void DLLEXPORT freecell_solver_user_set_soft_thread_name(
4517 void *const api_instance, const freecell_solver_str_t name)
4518 {
4519 fcs_soft_thread *const soft_thread = api_soft_thread(api_instance);
4520 strncpy(soft_thread->name, name, COUNT(soft_thread->name));
4521 LAST(soft_thread->name) = '\0';
4522 }
4523 #endif
4524
4525 #ifdef FCS_WITH_FLARES
freecell_solver_user_set_flare_name(void * const api_instance GCC_UNUSED,const freecell_solver_str_t name GCC_UNUSED)4526 void DLLEXPORT freecell_solver_user_set_flare_name(
4527 void *const api_instance GCC_UNUSED,
4528 const freecell_solver_str_t name GCC_UNUSED)
4529 {
4530 flare_item *const flare =
4531 curr_inst((fcs_user *)api_instance)->end_of_flares - 1;
4532 strncpy(flare->name, name, COUNT(flare->name));
4533 LAST(flare->name) = '\0';
4534 }
4535 #endif
4536
4537 #ifndef FCS_USE_PRECOMPILED_CMD_LINE_THEME
freecell_solver_user_set_hard_thread_prelude(void * const api_instance,const char * const prelude)4538 int DLLEXPORT freecell_solver_user_set_hard_thread_prelude(
4539 void *const api_instance, const char *const prelude)
4540 {
4541 fcs_user *const user = (fcs_user *)api_instance;
4542 fcs_hard_thread *const hard_thread = user->soft_thread->hard_thread;
4543
4544 free(HT_FIELD(hard_thread, prelude_as_string));
4545 HT_FIELD(hard_thread, prelude_as_string) = strdup(prelude);
4546
4547 return 0;
4548 }
4549 #else
fc_solve_user_set_ht_compiled_prelude(void * const api_instance,const size_t num,const fc_solve_prelude_item * const prelude)4550 void DLLEXPORT fc_solve_user_set_ht_compiled_prelude(void *const api_instance,
4551 const size_t num, const fc_solve_prelude_item *const prelude)
4552 {
4553 fcs_user *const user = (fcs_user *)api_instance;
4554 fcs_hard_thread *const hard_thread = user->soft_thread->hard_thread;
4555
4556 HT_FIELD(hard_thread, prelude_num_items) = num;
4557 HT_FIELD(hard_thread, prelude) = prelude;
4558 }
4559
4560 #endif
4561
4562 #ifdef FCS_WITH_FLARES
freecell_solver_user_set_flares_plan(void * const api_instance GCC_UNUSED,const char * const flares_plan_string GCC_UNUSED)4563 int DLLEXPORT freecell_solver_user_set_flares_plan(
4564 void *const api_instance GCC_UNUSED,
4565 const char *const flares_plan_string GCC_UNUSED)
4566 {
4567 fcs_user *const user = (fcs_user *)api_instance;
4568 const_AUTO(instance_item, curr_inst(user));
4569 free(instance_item->flares_plan_string);
4570 instance_item->flares_plan_string =
4571 (flares_plan_string ? strdup(flares_plan_string) : NULL);
4572 instance_item->flares_plan_compiled = false;
4573
4574 return 0;
4575 }
4576 #endif
4577
freecell_solver_user_recycle(void * api_instance)4578 void DLLEXPORT freecell_solver_user_recycle(void *api_instance)
4579 {
4580 fcs_user *const user = (fcs_user *)api_instance;
4581
4582 INSTANCES_LOOP_START()
4583 user__recycle_instance_item(user, instance_item);
4584 INSTANCES_LOOP_END()
4585 user->iterations_board_started_at = initial_stats;
4586 }
4587
4588 #ifdef FCS_WITH_MOVES
freecell_solver_user_set_optimization_scan_tests_order(void * const api_instance GCC_UNUSED,const char * const moves_order GCC_UNUSED FCS__PASS_ERR_STR (char ** const error_string GCC_UNUSED))4589 int DLLEXPORT freecell_solver_user_set_optimization_scan_tests_order(
4590 void *const api_instance GCC_UNUSED,
4591 const char *const moves_order GCC_UNUSED FCS__PASS_ERR_STR(
4592 char **const error_string GCC_UNUSED))
4593 {
4594 #ifndef FCS_ZERO_FREECELLS_MODE
4595 var_AUTO(obj, active_obj(api_instance));
4596 moves_order__free(&obj->opt_moves);
4597 STRUCT_CLEAR_FLAG(obj, FCS_RUNTIME_OPT_TESTS_ORDER_WAS_SET);
4598 FCS__DECL_ERR_BUF(static_error_string);
4599 const int ret = fc_solve_apply_moves_order(
4600 &(obj->opt_moves), moves_order FCS__PASS_ERR_STR(static_error_string));
4601 SET_ERROR_VAR(error_string, static_error_string);
4602 if (!ret)
4603 {
4604 STRUCT_TURN_ON_FLAG(obj, FCS_RUNTIME_OPT_TESTS_ORDER_WAS_SET);
4605 }
4606 return ret;
4607 #else
4608 return 0;
4609 #endif
4610 }
4611 #endif
4612
4613 #ifndef FCS_ENABLE_PRUNE__R_TF__UNCOND
freecell_solver_user_set_pruning(void * api_instance,const char * pruning FCS__PASS_ERR_STR (char ** error_string))4614 extern int DLLEXPORT freecell_solver_user_set_pruning(void *api_instance,
4615 const char *pruning FCS__PASS_ERR_STR(char **error_string))
4616 {
4617 fcs_soft_thread *const soft_thread = api_soft_thread(api_instance);
4618
4619 if (!strcmp(pruning, "r:tf"))
4620 {
4621 soft_thread->enable_pruning = true;
4622 }
4623 else if (pruning[0] == '\0')
4624 {
4625 soft_thread->enable_pruning = false;
4626 }
4627 else
4628 {
4629 ALLOC_ERROR_STRING(
4630 error_string, "Unknown pruning value - must be \"r:tf\" or empty.");
4631 return 1;
4632 }
4633 return 0;
4634 }
4635 #endif
4636
4637 #ifndef FCS_HARD_CODE_REPARENT_STATES_AS_FALSE
freecell_solver_user_set_reparent_states(void * const api_instance,const int to_reparent_states)4638 void DLLEXPORT freecell_solver_user_set_reparent_states(
4639 void *const api_instance, const int to_reparent_states)
4640 {
4641 STRUCT_SET_FLAG_TO(active_obj(api_instance),
4642 FCS_RUNTIME_TO_REPARENT_STATES_PROTO, to_reparent_states);
4643 }
4644 #endif
4645
4646 #ifndef FCS_HARD_CODE_SCANS_SYNERGY_AS_TRUE
freecell_solver_user_set_scans_synergy(void * const api_instance,const int synergy)4647 void DLLEXPORT freecell_solver_user_set_scans_synergy(
4648 void *const api_instance, const int synergy)
4649 {
4650 STRUCT_SET_FLAG_TO(
4651 active_obj(api_instance), FCS_RUNTIME_SCANS_SYNERGY, synergy);
4652 }
4653 #endif
4654
4655 #ifdef FCS_WITH_NI
freecell_solver_user_next_instance(void * const api_instance)4656 int DLLEXPORT freecell_solver_user_next_instance(void *const api_instance)
4657 {
4658 user_next_instance((fcs_user *)api_instance);
4659
4660 return 0;
4661 }
4662 #endif
4663
4664 #ifdef FCS_WITH_FLARES
freecell_solver_user_next_flare(void * const api_instance)4665 int DLLEXPORT freecell_solver_user_next_flare(void *const api_instance)
4666 {
4667 user_next_flare((fcs_user *)api_instance);
4668
4669 return 0;
4670 }
4671 #endif
4672
4673 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
freecell_solver_user_reset(void * const api_instance)4674 int DLLEXPORT freecell_solver_user_reset(void *const api_instance)
4675 {
4676 fcs_user *const user = (fcs_user *)api_instance;
4677 user_free_resources(user);
4678 user_initialize(user);
4679 return 0;
4680 }
4681
4682 DLLEXPORT const char *__attribute__((const))
freecell_solver_user_get_lib_version(void * api_instance GCC_UNUSED)4683 freecell_solver_user_get_lib_version(void *api_instance GCC_UNUSED)
4684 {
4685 return VERSION;
4686 }
4687 #endif
4688
4689 #ifndef FCS_USE_PRECOMPILED_CMD_LINE_THEME
4690 DLLEXPORT __attribute__((pure)) const char *
freecell_solver_user_get_current_soft_thread_name(void * const api_instance)4691 freecell_solver_user_get_current_soft_thread_name(void *const api_instance)
4692 {
4693 #ifdef FCS_SINGLE_HARD_THREAD
4694 const fcs_hard_thread *const hard_thread = active_obj(api_instance);
4695 #else
4696 const fcs_hard_thread *const hard_thread =
4697 active_obj(api_instance)->current_hard_thread;
4698 #endif
4699
4700 return HT_FIELD(hard_thread, soft_threads)[HT_FIELD(hard_thread, st_idx)]
4701 .name;
4702 }
4703 #endif
4704
4705 #ifdef FCS_WITH_ERROR_STRS
4706 DLLEXPORT __attribute__((const)) const char *
freecell_solver_user_get_last_error_string(void * const api_instance)4707 freecell_solver_user_get_last_error_string(void *const api_instance)
4708 {
4709 return (((fcs_user *const)api_instance)->error_string);
4710 }
4711 #endif
4712
4713 #ifndef FCS_BREAK_BACKWARD_COMPAT_1
4714 #ifdef FCS_RCS_STATES
freecell_solver_user_set_cache_limit(void * const api_instance,const long limit)4715 int DLLEXPORT freecell_solver_user_set_cache_limit(
4716 void *const api_instance, const long limit)
4717 {
4718 if (limit <= 0)
4719 {
4720 return -1;
4721 }
4722 fcs_user *const user = (fcs_user *)api_instance;
4723
4724 FLARES_LOOP_START()
4725 flare->obj.rcs_states_cache.max_num_elements_in_cache = limit;
4726 INSTANCE_ITEM_FLARES_LOOP_END()
4727 INSTANCES_LOOP_END()
4728
4729 return 0;
4730 }
4731 #endif
4732 #endif
4733
4734 #ifdef FCS_WITH_MOVES
freecell_solver_user_get_moves_sequence(void * const api_instance,fcs_moves_sequence_t * const moves_seq)4735 int DLLEXPORT freecell_solver_user_get_moves_sequence(
4736 void *const api_instance, fcs_moves_sequence_t *const moves_seq)
4737 {
4738 fcs_user *const user = (fcs_user *)api_instance;
4739 if (user->ret_code != FCS_STATE_WAS_SOLVED)
4740 {
4741 return -2;
4742 }
4743 const_AUTO(src_moves_seq, &(SINGLE_FLARE(user)->moves_seq));
4744 moves_seq->moves = memdup(src_moves_seq->moves,
4745 (sizeof(src_moves_seq->moves[0]) *
4746 (size_t)(moves_seq->num_moves = src_moves_seq->num_moves)));
4747 return 0;
4748 }
4749 #endif
4750
4751 #ifdef FCS_WITH_FLARES
4752 #ifndef FCS_WITHOUT_FC_PRO_MOVES_COUNT
freecell_solver_user_set_flares_choice(void * api_instance GCC_UNUSED,const char * const new_flares_choice_string GCC_UNUSED)4753 DLLEXPORT extern int freecell_solver_user_set_flares_choice(
4754 void *api_instance GCC_UNUSED,
4755 const char *const new_flares_choice_string GCC_UNUSED)
4756 {
4757 fcs_user *const user = (fcs_user *)api_instance;
4758
4759 if (!strcmp(new_flares_choice_string, "fc_solve"))
4760 {
4761 user->flares_choice = FLARES_CHOICE_FC_SOLVE_SOLUTION_LEN;
4762 }
4763 else if (!strcmp(new_flares_choice_string, "fcpro"))
4764 {
4765 user->flares_choice = FLARES_CHOICE_FCPRO_SOLUTION_LEN;
4766 }
4767 else
4768 {
4769 return -1;
4770 }
4771 return 0;
4772 }
4773 #endif
4774 #endif
4775
4776 #ifdef FCS_WITH_FLARES
freecell_solver_user_set_flares_iters_factor(void * const api_instance GCC_UNUSED,const double new_factor GCC_UNUSED)4777 DLLEXPORT extern void freecell_solver_user_set_flares_iters_factor(
4778 void *const api_instance GCC_UNUSED, const double new_factor GCC_UNUSED)
4779 {
4780 fcs_user *const user = (fcs_user *)api_instance;
4781
4782 user->flares_iters_factor = new_factor;
4783 }
4784 #endif
4785
4786 #ifdef FCS_COMPILE_DEBUG_FUNCTIONS
fc_solve_user_INTERNAL_compile_all_flares_plans(void * const api_instance GCC_UNUSED,char ** const error_string GCC_UNUSED)4787 int DLLEXPORT fc_solve_user_INTERNAL_compile_all_flares_plans(
4788 void *const api_instance GCC_UNUSED, char **const error_string GCC_UNUSED)
4789 {
4790 #ifdef FCS_WITH_FLARES
4791 fcs_user *const user = (fcs_user *)api_instance;
4792 const fcs_compile_flares_ret ret = user_compile_all_flares_plans(user);
4793 #ifdef FCS_WITH_ERROR_STRS
4794 #ifndef FCS_ZERO_FREECELLS_MODE
4795 SET_ERROR_VAR(error_string, user->error_string);
4796 #endif
4797 #else
4798 *error_string = NULL;
4799 #endif
4800 return (int)ret;
4801 #else
4802 *error_string = NULL;
4803 return 0;
4804 #endif
4805 }
4806
4807 #ifdef FCS_WITH_FLARES
4808 #define FLARES_ATTR __attribute__((const))
4809 #else
4810 #define FLARES_ATTR __attribute__((pure))
4811 #endif
fc_solve_user_INTERNAL_get_flares_plan_num_items(void * const api_instance GCC_UNUSED)4812 int DLLEXPORT FLARES_ATTR fc_solve_user_INTERNAL_get_flares_plan_num_items(
4813 void *const api_instance GCC_UNUSED)
4814 {
4815 #ifdef FCS_WITH_FLARES
4816 return (int)curr_inst((fcs_user *const)api_instance)->num_plan_items;
4817 #else
4818 return 0;
4819 #endif
4820 }
4821
4822 const DLLEXPORT FLARES_ATTR char *
fc_solve_user_INTERNAL_get_flares_plan_item_type(void * const api_instance GCC_UNUSED,const int item_idx GCC_UNUSED)4823 fc_solve_user_INTERNAL_get_flares_plan_item_type(
4824 void *const api_instance GCC_UNUSED, const int item_idx GCC_UNUSED)
4825 {
4826 #ifdef FCS_WITH_FLARES
4827 switch (curr_inst((fcs_user *const)api_instance)->plan[item_idx].type)
4828 {
4829 case FLARES_PLAN_RUN_INDEFINITELY:
4830 return "RunIndef";
4831 case FLARES_PLAN_RUN_COUNT_ITERS:
4832 return "Run";
4833 case FLARES_PLAN_CHECKPOINT:
4834 return "CP";
4835 #ifndef __clang__
4836 default:
4837 __builtin_unreachable();
4838 #endif
4839 }
4840 #else
4841 return "";
4842 #endif
4843 }
4844
fc_solve_user_INTERNAL_get_flares_plan_item_flare_idx(void * const api_instance GCC_UNUSED,const int item_idx GCC_UNUSED)4845 int DLLEXPORT FLARES_ATTR fc_solve_user_INTERNAL_get_flares_plan_item_flare_idx(
4846 void *const api_instance GCC_UNUSED, const int item_idx GCC_UNUSED)
4847 {
4848 #ifdef FCS_WITH_FLARES
4849 const_AUTO(instance_item, curr_inst((fcs_user *const)api_instance));
4850 return (int)(instance_item->plan[item_idx].flare - instance_item->flares);
4851 #else
4852 return 0;
4853 #endif
4854 }
4855
4856 int DLLEXPORT FLARES_ATTR
fc_solve_user_INTERNAL_get_flares_plan_item_iters_count(void * const api_instance GCC_UNUSED,const int item_idx GCC_UNUSED)4857 fc_solve_user_INTERNAL_get_flares_plan_item_iters_count(
4858 void *const api_instance GCC_UNUSED, const int item_idx GCC_UNUSED)
4859 {
4860 #ifdef FCS_WITH_FLARES
4861 return (int)curr_inst((fcs_user *const)api_instance)
4862 ->plan[item_idx]
4863 .count_iters;
4864 #else
4865 return 0;
4866 #endif
4867 }
4868
4869 int DLLEXPORT __attribute__((pure))
fc_solve_user_INTERNAL_get_num_by_depth_tests_order(void * const api_instance GCC_UNUSED)4870 fc_solve_user_INTERNAL_get_num_by_depth_tests_order(
4871 void *const api_instance GCC_UNUSED)
4872 {
4873 #ifndef FCS_ZERO_FREECELLS_MODE
4874 return (int)api_soft_thread(api_instance)->by_depth_moves_order.num;
4875 #else
4876 return 1;
4877 #endif
4878 }
4879
4880 int DLLEXPORT __attribute__((pure))
fc_solve_user_INTERNAL_get_by_depth_tests_max_depth(void * const api_instance GCC_UNUSED,const int depth_idx GCC_UNUSED)4881 fc_solve_user_INTERNAL_get_by_depth_tests_max_depth(
4882 void *const api_instance GCC_UNUSED, const int depth_idx GCC_UNUSED)
4883 {
4884 #ifndef FCS_ZERO_FREECELLS_MODE
4885 return (int)api_soft_thread(api_instance)
4886 ->by_depth_moves_order.by_depth_moves[depth_idx]
4887 .max_depth;
4888 #else
4889 return 1;
4890 #endif
4891 }
4892
4893 #endif
4894