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 // meta_move_funcs_helpers.h - header file of the move functions of Freecell
9 // Solver.
10 //
11 // The move functions code itself is found in freecell.c and simpsim.c.
12 #pragma once
13 
14 #ifdef __cplusplus
15 extern "C" {
16 #endif
17 
max0(const int e)18 static inline int max0(const int e) { return max(e, 0); }
19 #ifdef FCS_FREECELL_ONLY
20 #define calc_max_sequence_move(num_freecells, num_empty_cols)                  \
21     (((num_freecells) + 1) << (num_empty_cols))
22 #else
23 // The number of cards that can be moved is
24 // (freecells_number + 1) * 2 ^ (free_stacks_number)
25 //
26 // See the Freecell FAQ and the source code of PySol
27 #define calc_max_sequence_move(num_freecells, num_empty_cols)                  \
28     (INSTANCE_UNLIMITED_SEQUENCE_MOVE                                          \
29             ? INT_MAX                                                          \
30             : ((empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD)                \
31                       ? (((num_freecells) + 1) << (num_empty_cols))            \
32                       : ((num_freecells) + 1)))
33 
calc_max_simple_simon_seq_move(const int num_empty_cols)34 static inline size_t calc_max_simple_simon_seq_move(const int num_empty_cols)
35 {
36     return ((num_empty_cols < 0) ? 0 : (1 << num_empty_cols));
37 }
38 #endif
39 
40 // These are some macros to make it easier for the programmer.
41 #define ptr_state_key (raw_state_raw.key)
42 #define ptr_new_state &(pass_new_state)
43 #define state_key (*ptr_state_key)
44 #define new_state_key (*(pass_new_state.key))
45 
46 #define sfs_check_state_begin()                                                \
47     fc_solve_sfs_check_state_begin(hard_thread, &pass_new_state,               \
48         raw_state_raw SFS__PASS_MOVE_STACK(moves))
49 
50 #ifdef FCS_HARD_CODE_REPARENT_STATES_AS_FALSE
51 #define sfs_check_state_end__arg
52 #else
53 #define sfs_check_state_end__arg raw_state_raw,
54 #endif
55 
56 #define sfs_check_state_end()                                                  \
57     fc_solve_derived_states_list_add_state(derived_states_list,                \
58         fc_solve_sfs_check_state_end(soft_thread,                              \
59             sfs_check_state_end__arg &pass_new_state FCS__pass_moves(moves)),  \
60         state_context_value)
61 
fc_solve_move_sequence_function(fcs_state * const s FCS__pass_moves (fcs_move_stack * const moves),const size_t dest_col_i,const size_t src_col_i,const size_t cards_num)62 static inline void fc_solve_move_sequence_function(
63     fcs_state *const s FCS__pass_moves(fcs_move_stack *const moves),
64     const size_t dest_col_i, const size_t src_col_i, const size_t cards_num)
65 {
66     fcs_col_transfer_cards(fcs_state_get_col(*s, dest_col_i),
67         fcs_state_get_col(*s, src_col_i), cards_num);
68     fcs_move_stack_params_push(
69         moves, FCS_MOVE_TYPE_STACK_TO_STACK, src_col_i, dest_col_i, cards_num);
70 }
71 
72 #define fcs_move_sequence(to, from, cards_num)                                 \
73     fc_solve_move_sequence_function(                                           \
74         &(new_state_key)FCS__pass_moves(moves), to, from, cards_num)
75 
76 #ifdef FCS_RCS_STATES
77 
78 #define tests_define_accessors_rcs_states()                                    \
79     fcs_state my_new_out_state_key;                                            \
80     pass_new_state.key = &my_new_out_state_key
81 
82 #else
83 #define tests_define_accessors_rcs_states()
84 #endif
85 
86 #ifdef FCS_FREECELL_ONLY
87 
88 #define tests_define_accessors_freecell_only()                                 \
89     {                                                                          \
90     }
91 
92 #define tests__is_filled_by_any_card() true
93 #define IS_FILLED_BY_KINGS_ONLY() false
94 #define IS_FILLED_BY_NONE() false
95 
96 #else
97 
98 #ifdef FCS_SINGLE_HARD_THREAD
99 #define tests_define_accessors_freecell_only()                                 \
100     fcs_instance *const instance = hard_thread;
101 #else
102 #define tests_define_accessors_freecell_only()                                 \
103     fcs_instance *const instance = hard_thread->instance;
104 #endif
105 
106 #define tests__is_filled_by_any_card()                                         \
107     (empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD)
108 
109 #define IS_FILLED_BY_KINGS_ONLY()                                              \
110     (empty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY)
111 
112 #define IS_FILLED_BY_NONE() (empty_stacks_fill == FCS_ES_FILLED_BY_NONE)
113 
114 #endif
115 
116 #ifdef FCS_WITH_MOVES
117 #define tests_define_accessors_move_stack()                                    \
118     fcs_move_stack *const moves = &(HT_FIELD(hard_thread, reusable_move_stack))
119 #else
120 #define tests_define_accessors_move_stack()
121 #endif
122 
123 #define tests_state_context_val int state_context_value = 0;
124 // This macro defines these accessors to have some value.
125 #define tests_define_accessors_no_stacks(MORE)                                 \
126     fcs_hard_thread *const hard_thread = soft_thread->hard_thread;             \
127     tests_define_accessors_move_stack();                                       \
128     MORE fcs_kv_state pass_new_state;                                          \
129     tests_define_accessors_freecell_only();                                    \
130     tests_define_accessors_rcs_states()
131 
132 #ifdef INDIRECT_STACK_STATES
133 #define tests_define_indirect_stack_states_accessors()                         \
134     fcs_card *indirect_stacks_buffer =                                         \
135         HT_FIELD(hard_thread, indirect_stacks_buffer);
136 #else
137 #define tests_define_indirect_stack_states_accessors()
138 #endif
139 
140 #define tests_define_accessors__generic(MORE)                                  \
141     tests_define_accessors_no_stacks(MORE);                                    \
142     tests_define_indirect_stack_states_accessors()
143 
144 #define tests_define_accessors()                                               \
145     tests_define_accessors__generic(tests_state_context_val)
146 #define my_copy_stack(idx)                                                     \
147     fcs_copy_stack(                                                            \
148         new_state_key, *(pass_new_state.val), idx, indirect_stacks_buffer)
149 #define copy_two_stacks(idx1, idx2)                                            \
150     my_copy_stack(idx1);                                                       \
151     my_copy_stack(idx2)
152 
153 // This macro assists in implementing this prune:
154 //
155 // https://groups.yahoo.com/neo/groups/fc-solve-discuss/conversations/topics/1121
156 //
157 // There is no point in moving the last card in a
158 // column to a parent on a different column, because then the column won't
159 // be able to be filled, and will be left to disuse. Furthermore, after
160 // moved to a parent, the card might block other cards that can be placed
161 // on the parent.
162 //
163 // TODO : implement it for FCS_ES_FILLED_BY_KINGS_ONLY
164 #define MOVE_FUNCS__should_not_empty_columns() IS_FILLED_BY_NONE()
165 
166 #define MOVE_FUNCS__define_common()                                            \
167     tests_define_accessors();                                                  \
168     MOVE_FUNCS__define_seqs_built_by();                                        \
169     MOVE_FUNCS__define_empty_stacks_fill()
170 
171 #ifdef FCS_FREECELL_ONLY
172 
173 #define MOVE_FUNCS__define_seqs_built_by()
174 #define MOVE_FUNCS__define_empty_stacks_fill()
175 #define PASS_sequences_are_built_by(param)
176 #include "pos_by_rank__freecell.h"
177 #define POS_BY_RANK_STEP (2)
178 #define FCS_POS_IDX_TO_CHECK_START_LOOP(src_card)                              \
179     const_AUTO(pos_pos, pos_by_rank__freecell[(int)src_card]);                 \
180     const int8_t *pos_idx_to_check = &positions_by_rank[pos_pos.start];        \
181     const int8_t *const last_pos_idx = &positions_by_rank[pos_pos.end];        \
182                                                                                \
183     for (; pos_idx_to_check < last_pos_idx;                                    \
184          pos_idx_to_check += suit_positions_by_rank_step)
185 
186 #else
187 
188 #define MOVE_FUNCS__define_seqs_built_by()                                     \
189     const int sequences_are_built_by =                                         \
190         GET_INSTANCE_SEQUENCES_ARE_BUILT_BY(instance);
191 #define MOVE_FUNCS__define_empty_stacks_fill()                                 \
192     const int empty_stacks_fill = INSTANCE_EMPTY_STACKS_FILL;
193 #define PASS_sequences_are_built_by(param) , param
194 #define POS_BY_RANK_STEP                                                       \
195     ((sequences_are_built_by == FCS_SEQ_BUILT_BY_RANK)                         \
196             ? 1                                                                \
197             : (sequences_are_built_by == FCS_SEQ_BUILT_BY_SUIT) ? 4 : 2)
198 #define FCS_PROTO_CARD_SUIT_POSITIONS_BY_RANK_INITIAL_OFFSET(card)             \
199     ((sequences_are_built_by == FCS_SEQ_BUILT_BY_RANK)                         \
200             ? 0                                                                \
201             : (sequences_are_built_by == FCS_SEQ_BUILT_BY_SUIT)                \
202                   ? fcs_card_suit(card)                                        \
203                   : ((fcs_card_suit(card) ^ 0x1) & (0x2 - 1)))
204 
205 #define FCS_POS_IDX_TO_CHECK_START_LOOP(src_card)                              \
206     const int8_t *pos_idx_to_check = &positions_by_rank[(                      \
207         FCS_POS_BY_RANK_WIDTH * (fcs_card_rank(src_card)))];                   \
208     const int8_t *const last_pos_idx =                                         \
209         pos_idx_to_check + FCS_POS_BY_RANK_WIDTH;                              \
210                                                                                \
211     for (pos_idx_to_check += POS_BY_RANK_MAP(                                  \
212              FCS_PROTO_CARD_SUIT_POSITIONS_BY_RANK_INITIAL_OFFSET(src_card));  \
213          pos_idx_to_check < last_pos_idx;                                      \
214          pos_idx_to_check += suit_positions_by_rank_step)
215 
216 #endif
217 
218 #define SET_empty_stack_idx(empty_stack_idx)                                   \
219     stack_i empty_stack_idx;                                                   \
220     for (empty_stack_idx = 0; empty_stack_idx < LOCAL_STACKS_NUM;              \
221          empty_stack_idx++)                                                    \
222     {                                                                          \
223         if (fcs_state_col_is_empty(state_key, empty_stack_idx))                \
224         {                                                                      \
225             break;                                                             \
226         }                                                                      \
227     }
228 
229 #define CALC_num_cards_in_col_threshold()                                      \
230     (MOVE_FUNCS__should_not_empty_columns() ? 1 : 0)
231 
232 #ifdef __cplusplus
233 }
234 #endif
235