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