// This file is part of Freecell Solver. It is subject to the license terms in // the COPYING.txt file found in the top-level directory of this distribution // and at http://fc-solve.shlomifish.org/docs/distro/COPYING.html . No part of // Freecell Solver, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the COPYING file. // // Copyright (c) 2000 Shlomi Fish // simpsim.c - a module that contains Simple Simon moves. #include "simpsim.h" #include "scans.h" // This is a fallback in case this module is still compiled with // FCS_DISABLE_SIMPLE_SIMON. #ifdef FCS_DISABLE_SIMPLE_SIMON char fc_solve_simple_simon_nothing; #else #include "meta_move_funcs_helpers.h" static inline bool fcs_is_ss_false_parent( const fcs_card parent, const fcs_card child) { return (fcs_card_rank(parent) == fcs_card_rank(child) + 1); } static inline bool fcs_is_ss_suit_true( const fcs_card parent, const fcs_card child) { return (fcs_card_suit(parent) == fcs_card_suit(child)); } #include "fcs_is_ss_true_parent.h" #define fcs_is_ss_true_parent(parent, child) \ fc_solve_is_ss_true_parent[parent][child] #define STACK_SOURCE_LOOP_START(min_num_cards) \ for (stack_i source_stack_idx = 0; source_stack_idx < LOCAL_STACKS_NUM; \ ++source_stack_idx) \ { \ const_AUTO(col, fcs_state_get_col(state_key, source_stack_idx)); \ const int col_len = fcs_col_len(col); \ if (col_len < min_num_cards) \ { \ continue; \ } #define STACK_SOURCE_LOOP_END() } #define STACK_DEST_LOOP_START(min_num_cards) \ for (stack_i dest_stack_idx = 0; dest_stack_idx < LOCAL_STACKS_NUM; \ ++dest_stack_idx) \ { \ if (dest_stack_idx == source_stack_idx) \ { \ continue; \ } \ \ const_AUTO(dest_col, fcs_state_get_col(state_key, dest_stack_idx)); \ const int dest_col_len = fcs_col_len(dest_col); \ \ if (dest_col_len < min_num_cards) \ { \ continue; \ } #define STACK_DEST_LOOP_END() } #ifndef HARD_CODED_NUM_STACKS #define SIMPS_SET_GAME_PARAMS() SET_GAME_PARAMS() #else #define SIMPS_SET_GAME_PARAMS() #endif #define SIMPS_define_accessors() \ tests_define_accessors(); \ SIMPS_SET_GAME_PARAMS() #define SIMPS_define_vacant_stacks_accessors() \ SIMPS_define_accessors(); \ const fcs_game_limit num_vacant_stacks = soft_thread->num_vacant_stacks #define CALC_POSITIONS_BY_RANK() \ const fcs_pos_by_rank *const positions_by_rank = \ (const fcs_pos_by_rank *)fc_solve_calc_positions_by_rank_location( \ soft_thread) #define STACKS_MAP_LEN MAX_NUM_STACKS static inline void init_stacks_map(bool *const stacks_map, const stack_i source_stack_idx, const stack_i dest_stack_idx) { for (stack_i i = 0; i < STACKS_MAP_LEN; i++) { stacks_map[i] = false; } stacks_map[source_stack_idx] = stacks_map[dest_stack_idx] = true; } typedef struct { size_t num_separate_false_seqs; int seq_points[MAX_NUM_CARDS_IN_A_STACK]; // the stacks to move each false sequence of the junk to. size_t junk_move_to_stacks[MAX_NUM_STACKS]; // The number of stacks that are left unoccupied during and after the // process of moving the junk sequences to different stacks. int after_junk_num_freestacks; // above_num_true_seqs[] - the number of true sequences in each false // sequence size_t above_num_true_seqs[MAX_NUM_CARDS_IN_A_STACK]; } sequences_analysis; #include "simple_simon_rank_seqs.h" static inline void fcs_col_pop_seq( fcs_cards_column src_col, const size_t cards_num) { fcs_card *const src_cards_ptr = &fcs_col_get_card(src_col, (fcs_col_len(src_col) -= cards_num)); const size_t cards_size = (((size_t)cards_num) * sizeof(fcs_card)); memset(src_cards_ptr, 0, cards_size); } DECLARE_MOVE_FUNCTION(fc_solve_sfs_simple_simon_move_sequence_to_founds) { SIMPS_define_accessors(); STACK_SOURCE_LOOP_START(13) fcs_card *start_card_ptr = &fcs_col_get_card(col, col_len - FCS_MAX_RANK); size_t suit = 0; // Check if the top 13 cards are a sequence for (; suit < FCS_NUM_SUITS; ++suit) { if (!memcmp(start_card_ptr, simple_simon_rank_seqs[suit], sizeof(simple_simon_rank_seqs[suit]))) { break; } } if (suit < FCS_NUM_SUITS) { // We can move this sequence up there sfs_check_state_begin(); my_copy_stack(source_stack_idx); var_AUTO( new_src_col, fcs_state_get_col(new_state_key, source_stack_idx)); fcs_col_pop_seq(new_src_col, FCS_MAX_RANK); fcs_set_foundation(new_state_key, suit, FCS_MAX_RANK); fcs_move_stack_non_seq_push( moves, FCS_MOVE_TYPE_SEQ_TO_FOUNDATION, source_stack_idx, suit); sfs_check_state_end(); } STACK_SOURCE_LOOP_END() } #define LOOK_FOR_TRUE_PARENT_with_ds_dc__START(card) \ if (!fcs_card_is_king(card)) \ { \ const fcs_pos_by_rank pos = positions_by_rank[FCS_POS_IDX( \ fcs_card_rank(card) + 1, fcs_card_suit(card))]; \ if (pos.col < 0) \ { \ continue; \ } \ const stack_i dest_stack_idx = (stack_i)pos.col; \ \ if (dest_stack_idx != source_stack_idx) \ { \ const int dest_card_height = pos.height; \ const_AUTO( \ dest_col, fcs_state_get_col(state_key, dest_stack_idx)); \ const int dest_col_len = fcs_col_len(dest_col); #define LOOK_FOR_TRUE_PARENT_with_ds_dc__END() \ } \ } #define LOOK_FOR_TRUE_PARENT_AT_TOP__START(card) \ LOOK_FOR_TRUE_PARENT_with_ds_dc__START( \ card) if (dest_card_height == dest_col_len - 1) \ { #define LOOK_FOR_TRUE_PARENT_AT_TOP__END() \ } \ LOOK_FOR_TRUE_PARENT_with_ds_dc__END() // TODO: // // Convert to fc_solve_get_the_positions_by_rank_data. DECLARE_MOVE_FUNCTION(fc_solve_sfs_simple_simon_move_sequence_to_true_parent) { // card - the current card (at height height) // num_true_seqs - the number of true sequences (i.e: sequences of a // unified suit) in the source sequence. SIMPS_define_vacant_stacks_accessors(); CALC_POSITIONS_BY_RANK(); STACK_SOURCE_LOOP_START(1) // Loop on the cards in the stack and try to look for a true // parent on top one of the stacks fcs_card card = fcs_col_get_card(col, col_len - 1); size_t num_true_seqs = 1; for (int height = col_len - 2;; --height) { LOOK_FOR_TRUE_PARENT_AT_TOP__START(card) // This is a suitable parent - let's check if we have enough empty // stacks to make the move feasible We can do it - so let's move sfs_check_state_begin(); copy_two_stacks(source_stack_idx, dest_stack_idx); fcs_move_sequence((size_t)(dest_stack_idx), source_stack_idx, (size_t)(col_len - height - 1)); sfs_check_state_end(); LOOK_FOR_TRUE_PARENT_AT_TOP__END() // Stop if we reached the bottom of the stack if (height == -1) { break; } const fcs_card prev_card = card; card = fcs_col_get_card(col, height); // If this is no longer a sequence - move to the next stack if (!fcs_is_ss_false_parent(card, prev_card)) { break; } if (!fcs_is_ss_suit_true(card, prev_card)) { ++num_true_seqs; // We can no longer perform the move so go to the next stack. if (calc_max_simple_simon_seq_move(num_vacant_stacks) < num_true_seqs) { break; } } } STACK_SOURCE_LOOP_END() } static inline int get_seq_h( const fcs_const_cards_column col, size_t *const num_true_seqs_out_ptr) { const int col_len = fcs_col_len(col); fcs_card card = fcs_col_get_card(col, col_len - 1); size_t num_true_seqs = 1; int height; // Stop if we reached the bottom of the stack for (height = col_len - 2; height > -1; --height) { const fcs_card next_card = fcs_col_get_card(col, height); // If this is no longer a sequence - move to the next stack if (!fcs_is_ss_false_parent(next_card, card)) { break; } if (!fcs_is_ss_suit_true(next_card, card)) { ++num_true_seqs; } card = next_card; } *num_true_seqs_out_ptr = num_true_seqs; return height + 1; } DECLARE_MOVE_FUNCTION( fc_solve_sfs_simple_simon_move_whole_stack_sequence_to_false_parent) { // card - the current card // dest_card - the card at the top of "dest_stack_idx". // num_true_seqs - the number of true sequences on the current // false sequence SIMPS_define_vacant_stacks_accessors(); const size_t max_seq_move = calc_max_simple_simon_seq_move(num_vacant_stacks); STACK_SOURCE_LOOP_START(1) { size_t num_true_seqs; // This means that the loop exited prematurely and the stack does not // contain a sequence if ((get_seq_h(col, &num_true_seqs) > 0) || (max_seq_move < num_true_seqs)) { continue; } } const size_t height = 0; const fcs_card card = fcs_col_get_card(col, height); STACK_DEST_LOOP_START(1) const fcs_card dest_card = fcs_col_get_card(dest_col, dest_col_len - 1); if (!(fcs_is_ss_false_parent(dest_card, card))) { continue; } sfs_check_state_begin(); copy_two_stacks(source_stack_idx, dest_stack_idx); fcs_move_sequence( (size_t)(dest_stack_idx), source_stack_idx, (size_t)(col_len)); sfs_check_state_end(); STACK_DEST_LOOP_END() STACK_SOURCE_LOOP_END() } static inline void generic_populate_seq_points(const fcs_cards_column dest_col, const int dest_card_height, sequences_analysis *const seqs, const int dest_col_len) { size_t num_separate_false_seqs = seqs->num_separate_false_seqs; seqs->above_num_true_seqs[num_separate_false_seqs] = 1; fcs_card above_card = fcs_col_get_card(dest_col, dest_col_len - 1); for (int card_height = dest_col_len - 2; card_height > dest_card_height; --card_height) { const fcs_card up_above_card = fcs_col_get_card(dest_col, card_height); if (!fcs_is_ss_false_parent(up_above_card, above_card)) { seqs->seq_points[num_separate_false_seqs++] = card_height + 1; seqs->above_num_true_seqs[num_separate_false_seqs] = 1; } seqs->above_num_true_seqs[num_separate_false_seqs] += !fcs_is_ss_suit_true(up_above_card, above_card); above_card = up_above_card; } if (dest_card_height <= dest_col_len - 2) { seqs->seq_points[num_separate_false_seqs++] = dest_card_height + 1; } seqs->num_separate_false_seqs = num_separate_false_seqs; } static inline void populate_seq_points(const fcs_cards_column dest_col, const int dest_card_height, sequences_analysis *const seqs) { seqs->num_separate_false_seqs = 0; generic_populate_seq_points( dest_col, dest_card_height, seqs, fcs_col_len(dest_col)); } static inline bool generic_false_seq_index_loop(const int stacks_num, fcs_kv_state raw_state_raw, const int num_vacant_stacks, const fcs_cards_column col, sequences_analysis *const seqs, const stack_i source_stack_idx, const stack_i dest_stack_idx, const bool behavior_flag, const bool should_src_col, const fcs_card src_card, const size_t num_src_junk_true_seqs) { const size_t num_separate_false_seqs = seqs->num_separate_false_seqs; bool stacks_map[STACKS_MAP_LEN]; init_stacks_map(stacks_map, source_stack_idx, dest_stack_idx); int after_junk_num_freestacks = num_vacant_stacks; const size_t false_seq_index_limit = num_separate_false_seqs + (should_src_col ? 1 : 0); size_t false_seq_idx; for (false_seq_idx = 0; false_seq_idx < false_seq_index_limit; false_seq_idx++) { const bool is_ultimate_iter = (false_seq_idx == num_separate_false_seqs); // Find a suitable place to put it const fcs_card the_card = is_ultimate_iter ? src_card : fcs_col_get_card(col, seqs->seq_points[false_seq_idx]); const size_t the_num_true_seqs = is_ultimate_iter ? num_src_junk_true_seqs : seqs->above_num_true_seqs[false_seq_idx]; // Let's try to find a suitable parent on top one of the stacks int clear_junk_dest_stack; for (clear_junk_dest_stack = 0; clear_junk_dest_stack < stacks_num; ++clear_junk_dest_stack) { const fcs_const_cards_column clear_junk_dest_col = fcs_state_get_col(state_key, clear_junk_dest_stack); const int clear_junk_stack_len = fcs_col_len(clear_junk_dest_col); if (!((clear_junk_stack_len > 0) && (!stacks_map[clear_junk_dest_stack]))) { continue; } if (fcs_is_ss_false_parent(fcs_col_get_card(clear_junk_dest_col, clear_junk_stack_len - 1), the_card)) { if (calc_max_simple_simon_seq_move(after_junk_num_freestacks) >= the_num_true_seqs) { goto found; } } } // Check if there is a vacant stack if (behavior_flag || (!((num_vacant_stacks > 0) && (calc_max_simple_simon_seq_move( after_junk_num_freestacks - 1) >= the_num_true_seqs)))) { break; } --after_junk_num_freestacks; // Find an empty stack and designate it as the destination for the junk for (clear_junk_dest_stack = 0;; ++clear_junk_dest_stack) { if (fcs_state_col_is_empty(state_key, clear_junk_dest_stack) && (!stacks_map[clear_junk_dest_stack])) { break; } } found: stacks_map[clear_junk_dest_stack] = true; seqs->junk_move_to_stacks[false_seq_idx] = (size_t)clear_junk_dest_stack; } seqs->after_junk_num_freestacks = after_junk_num_freestacks; return (false_seq_idx == false_seq_index_limit); } static inline bool false_seq_index_loop(const int stacks_num, fcs_kv_state raw_state_raw, const int num_vacant_stacks, const fcs_cards_column col, sequences_analysis *const seqs, const stack_i source_stack_idx, const stack_i dest_stack_idx, const bool behavior_flag) { return generic_false_seq_index_loop(stacks_num, raw_state_raw, num_vacant_stacks, col, seqs, source_stack_idx, dest_stack_idx, behavior_flag, // Params that should be ignored in this case. false, fc_solve_empty_card, 0); } #define IS_false_seq_index_loop( \ col, behavior_flag, source_stack_idx, dest_stack_idx) \ false_seq_index_loop(LOCAL_STACKS_NUM, raw_state_raw, num_vacant_stacks, \ col, &seqs, source_stack_idx, dest_stack_idx, behavior_flag) #define POPULATE_AND_CHECK_IF_FALSE_SEQ( \ col, height, source_stack_idx, dest_stack_idx, behavior_flag) \ ({ \ populate_seq_points(col, height, &seqs); \ IS_false_seq_index_loop( \ col, behavior_flag, source_stack_idx, dest_stack_idx); \ }) static inline void move_sequences_analysis_seqs_loop( fcs_kv_state *const ptr_to_pass_new_state SFS__PASS_MOVE_STACK( fcs_move_stack *const moves), const sequences_analysis *const seqs_ptr, const stack_i source_col_idx, const int source_col_cards_num IND_BUF_T_PARAM(indirect_stacks_buffer)) { #define pass_new_state (*ptr_to_pass_new_state) for (size_t seq_index = 0; seq_index < seqs_ptr->num_separate_false_seqs; seq_index++) { const_AUTO(dest_col_i, seqs_ptr->junk_move_to_stacks[seq_index]); my_copy_stack(dest_col_i); fcs_move_sequence(dest_col_i, (size_t)source_col_idx, ((seq_index == 0) ? (size_t)source_col_cards_num : (size_t)seqs_ptr->seq_points[seq_index - 1]) - (size_t)seqs_ptr->seq_points[seq_index]); } #undef pass_new_state } DECLARE_MOVE_FUNCTION( fc_solve_sfs_simple_simon_move_sequence_to_true_parent_with_some_cards_above) { // card - the card in height "height" // num_separate_false_seqs - this variable tells how many distinct false // sequences exist above the true parent // seq_points[] - the separation points of the false sequences (i.e: where // they begin and end) SIMPS_define_vacant_stacks_accessors(); CALC_POSITIONS_BY_RANK(); STACK_SOURCE_LOOP_START(1) size_t num_true_seqs = 1; for (int height = col_len - 2;; --height) { const fcs_card card = fcs_col_get_card(col, height + 1); bool should_search = true; size_t increment_num_true_seqs = 0; bool should_break; // Stop if we reached the bottom of the stack if (!((should_break = (height == -1)))) { const fcs_card h_above_card = fcs_col_get_card(col, height); // If this is no longer a sequence - move to the next stack if (!fcs_is_ss_false_parent(h_above_card, card)) { should_break = true; } else if ((should_search = (!fcs_is_ss_suit_true(h_above_card, card)))) { increment_num_true_seqs = 1; } } if (should_search) { LOOK_FOR_TRUE_PARENT_with_ds_dc__START(card) // This is a suitable parent - let's check if there's a sequence // above it. sequences_analysis seqs; if ((POPULATE_AND_CHECK_IF_FALSE_SEQ(dest_col, dest_card_height, source_stack_idx, dest_stack_idx, false) && (calc_max_simple_simon_seq_move( seqs.after_junk_num_freestacks) >= num_true_seqs))) { // We can do it - so let's move everything. // Notice that we only put the child in a different stack // than the parent and let it move to the parent in the // next iteration of the program sfs_check_state_begin(); copy_two_stacks(source_stack_idx, dest_stack_idx); // Move the junk cards to their place move_sequences_analysis_seqs_loop( &pass_new_state SFS__PASS_MOVE_STACK(moves), &seqs, dest_stack_idx, dest_col_len PASS_IND_BUF_T(indirect_stacks_buffer)); // Move the source seq on top of the dest seq fcs_move_sequence(dest_stack_idx, source_stack_idx, (size_t)(col_len - height - 1)); sfs_check_state_end(); } LOOK_FOR_TRUE_PARENT_with_ds_dc__END() } if (should_break) { break; } num_true_seqs += increment_num_true_seqs; } STACK_SOURCE_LOOP_END() } DECLARE_MOVE_FUNCTION( fc_solve_sfs_simple_simon_move_sequence_with_some_cards_above_to_true_parent) { SIMPS_define_vacant_stacks_accessors(); CALC_POSITIONS_BY_RANK(); STACK_SOURCE_LOOP_START(1) for (ssize_t src_card_height = col_len - 1; src_card_height >= 0; src_card_height--) { const fcs_card h_card = fcs_col_get_card(col, src_card_height); fcs_card card = h_card; size_t num_true_seqs = 1; for (size_t end_of_src_seq = (size_t)(src_card_height) + 1; end_of_src_seq < (size_t)col_len; ++end_of_src_seq) { const fcs_card above_card = fcs_col_get_card(col, end_of_src_seq); if (!fcs_is_ss_false_parent(card, above_card)) { // Split the cards above it into false sequences LOOK_FOR_TRUE_PARENT_AT_TOP__START(h_card) // This is a suitable parent - let's check if we // have enough empty stacks to make the move feasible sequences_analysis seqs; if ((POPULATE_AND_CHECK_IF_FALSE_SEQ(col, (int)end_of_src_seq - 1, source_stack_idx, dest_stack_idx, false) && (calc_max_simple_simon_seq_move( seqs.after_junk_num_freestacks) > num_true_seqs))) { sfs_check_state_begin(); copy_two_stacks(source_stack_idx, dest_stack_idx); // Move the junk cards to their place move_sequences_analysis_seqs_loop( &pass_new_state SFS__PASS_MOVE_STACK(moves), &seqs, source_stack_idx, col_len PASS_IND_BUF_T(indirect_stacks_buffer)); fcs_move_sequence(dest_stack_idx, source_stack_idx, end_of_src_seq - (size_t)src_card_height); sfs_check_state_end(); } LOOK_FOR_TRUE_PARENT_AT_TOP__END() break; } if (!fcs_is_ss_suit_true(card, above_card)) { ++num_true_seqs; } card = above_card; } } STACK_SOURCE_LOOP_END() } // start, end, src_stack. typedef struct { int seq_len; stack_i src_stack; } s_e_src_type; static inline s_e_src_type calc_start_end_src_stack(const int seq_index, const sequences_analysis *const seqs, const int after_end_of_junk, const int col_len, const stack_i source_stack_idx, const stack_i dest_stack_idx, const int dest_col_len) { if ((size_t)seq_index == seqs->num_separate_false_seqs) { return (const s_e_src_type){.seq_len = col_len - after_end_of_junk, .src_stack = source_stack_idx}; } else { return (const s_e_src_type){ .seq_len = (((seq_index == 0) ? dest_col_len : seqs->seq_points[seq_index - 1]) - seqs->seq_points[seq_index]), .src_stack = dest_stack_idx}; } } DECLARE_MOVE_FUNCTION( fc_solve_sfs_simple_simon_move_sequence_with_junk_seq_above_to_true_parent_with_some_cards_above) { // card - the current card in "stack" // num_separate_false_seqs - the number of false sequences // seq_points[] - the places in which the false sequences of the junk begin // and end // num_src_junk_true_seqs - the number of true seqs in the false seq above // the source card. // num_true_seqs - the number of true sequences in the false seq which we // wish to move. SIMPS_define_vacant_stacks_accessors(); CALC_POSITIONS_BY_RANK(); STACK_SOURCE_LOOP_START(1) size_t num_src_junk_true_seqs; const int after_end_of_junk = get_seq_h(col, &num_src_junk_true_seqs); if (!after_end_of_junk) { continue; } int height = after_end_of_junk; fcs_card card = fcs_col_get_card(col, height); size_t num_true_seqs = 1; for (--height; height > -1; --height) { const fcs_card next_card = fcs_col_get_card(col, height); if (!fcs_is_ss_false_parent(next_card, card)) { card = next_card; break; } if (!fcs_is_ss_suit_true(next_card, card)) { ++num_true_seqs; } card = next_card; } // Start at the card below the top one, so we know there's some junk above LOOK_FOR_TRUE_PARENT_with_ds_dc__START( card) if (dest_card_height <= dest_col_len - 2) { // This is a suitable parent - let's check if there's a sequence above // it. sequences_analysis seqs; populate_seq_points(dest_col, dest_card_height, &seqs); if (generic_false_seq_index_loop(LOCAL_STACKS_NUM, raw_state_raw, num_vacant_stacks, dest_col, &seqs, source_stack_idx, dest_stack_idx, false, true, fcs_col_get_card(col, after_end_of_junk), num_src_junk_true_seqs) && (calc_max_simple_simon_seq_move(seqs.after_junk_num_freestacks) >= num_true_seqs)) { sfs_check_state_begin(); copy_two_stacks(source_stack_idx, dest_stack_idx); // Move the junk cards to their place for (size_t seq_index = 0; seq_index < seqs.num_separate_false_seqs + 1; seq_index++) { const s_e_src_type s_e = calc_start_end_src_stack( (int)seq_index, &seqs, after_end_of_junk, col_len, source_stack_idx, dest_stack_idx, dest_col_len); const_AUTO(dest_col_i, seqs.junk_move_to_stacks[seq_index]); copy_two_stacks(s_e.src_stack, dest_col_i); fcs_move_sequence( dest_col_i, (size_t)s_e.src_stack, (size_t)s_e.seq_len); } // Move the source seq on top of the dest seq fcs_move_sequence(dest_stack_idx, source_stack_idx, (size_t)(after_end_of_junk - height)); sfs_check_state_end(); } } LOOK_FOR_TRUE_PARENT_with_ds_dc__END() STACK_SOURCE_LOOP_END() return; } typedef fcs_pos_by_rank ds_dc_type; static inline void sort_ds_dcs(ds_dc_type *const ds_dcs, const int len) { #define start ds_dcs ds_dc_type *const end = start + len; for (ds_dc_type *b = start + 1; b < end; b++) { for (ds_dc_type *c = b; (c > start) && (c[0].col < c[-1].col || (c[0].col == c[-1].col && c[0].height > c[-1].height)); c--) { const_AUTO(swap_temp, c[-1]); c[-1] = c[0]; c[0] = swap_temp; } } #undef start } DECLARE_MOVE_FUNCTION( fc_solve_sfs_simple_simon_move_whole_stack_sequence_to_false_parent_with_some_cards_above) { // card - the current card SIMPS_define_vacant_stacks_accessors(); CALC_POSITIONS_BY_RANK(); // We never fill empty stacks with junk cards in this move function, // so as a result, after_junk_num_freestacks == num_vacant_stacks and // is constant here. const size_t max_seq_move = calc_max_simple_simon_seq_move(num_vacant_stacks); STACK_SOURCE_LOOP_START(1) { size_t num_true_seqs; if (get_seq_h(col, &num_true_seqs) || (max_seq_move < num_true_seqs)) { continue; } } const size_t height = 0; const fcs_card card = fcs_col_get_card(col, height); if (fcs_card_is_king(card)) { continue; } ds_dc_type ds_dcs[4]; size_t len = 0; for (size_t parent_suit = 0; parent_suit < 4; parent_suit++) { const fcs_pos_by_rank pos = positions_by_rank[FCS_POS_IDX( fcs_card_rank(card) + 1, parent_suit)]; if ((pos.col < 0) || ((stack_i)pos.col == source_stack_idx)) { continue; } ds_dcs[len++] = pos; } // This is done to preserve the original order in the solutions. sort_ds_dcs(ds_dcs, (int)len); for (size_t i = 0; i < len; i++) { const stack_i dest_stack_idx = (stack_i)ds_dcs[i].col; const size_t dest_card_height = (size_t)ds_dcs[i].height; const_AUTO(dest_col, fcs_state_get_col(state_key, dest_stack_idx)); // This is a suitable parent - let's check if there's a sequence above // it. sequences_analysis seqs; if (POPULATE_AND_CHECK_IF_FALSE_SEQ(dest_col, (int)dest_card_height, source_stack_idx, dest_stack_idx, true)) { sfs_check_state_begin(); copy_two_stacks(source_stack_idx, dest_stack_idx); // Move the junk cards to their place move_sequences_analysis_seqs_loop( &pass_new_state SFS__PASS_MOVE_STACK(moves), &seqs, dest_stack_idx, fcs_col_len(dest_col) PASS_IND_BUF_T(indirect_stacks_buffer)); fcs_move_sequence( dest_stack_idx, source_stack_idx, (size_t)(col_len)); sfs_check_state_end(); } } STACK_SOURCE_LOOP_END() } DECLARE_MOVE_FUNCTION( fc_solve_sfs_simple_simon_move_sequence_to_parent_on_the_same_stack) { SIMPS_define_vacant_stacks_accessors(); STACK_SOURCE_LOOP_START(3) // Search for a parent card for (int parent_card_height = 0; parent_card_height < col_len - 2; parent_card_height++) { const fcs_card parent_card = fcs_col_get_card(col, parent_card_height); if (fcs_is_ss_true_parent( parent_card, fcs_col_get_card(col, parent_card_height + 1))) { continue; } for (int child_card_height = parent_card_height + 2; child_card_height < col_len; child_card_height++) { if (!fcs_is_ss_true_parent( parent_card, fcs_col_get_card(col, child_card_height))) { continue; } // We have a matching parent and child cards // Now let's try to find stacks to place the cards above the child // card. int end_of_child_seq = child_card_height; size_t child_num_true_seqs = 1; while ( (end_of_child_seq + 1 < col_len) && fcs_is_ss_false_parent(fcs_col_get_card(col, end_of_child_seq), fcs_col_get_card(col, end_of_child_seq + 1))) { child_num_true_seqs += (!fcs_is_ss_true_parent( fcs_col_get_card(col, end_of_child_seq), fcs_col_get_card(col, end_of_child_seq + 1))); ++end_of_child_seq; } sequences_analysis seqs; populate_seq_points(col, end_of_child_seq, &seqs); // Add the child to the seq_points const_AUTO(child_seq_index, seqs.num_separate_false_seqs); seqs.above_num_true_seqs[seqs.num_separate_false_seqs] = child_num_true_seqs; seqs.seq_points[seqs.num_separate_false_seqs++] = child_card_height; // Add the cards between the parent and the child to the seq_points generic_populate_seq_points( col, parent_card_height, &seqs, child_card_height); // Let's check if we can move the child after we are done moving all // the junk cards if (!(IS_false_seq_index_loop( col, false, source_stack_idx, source_stack_idx) && (calc_max_simple_simon_seq_move( seqs.after_junk_num_freestacks) >= child_num_true_seqs))) { continue; } sfs_check_state_begin(); // Move the junk cards to their place my_copy_stack(source_stack_idx); move_sequences_analysis_seqs_loop( &pass_new_state SFS__PASS_MOVE_STACK(moves), &seqs, source_stack_idx, col_len PASS_IND_BUF_T(indirect_stacks_buffer)); const_AUTO(src_col_i, seqs.junk_move_to_stacks[child_seq_index]); my_copy_stack(src_col_i); fcs_move_sequence(source_stack_idx, src_col_i, (size_t)(end_of_child_seq - child_card_height + 1)); sfs_check_state_end(); } } STACK_SOURCE_LOOP_END() } DECLARE_MOVE_FUNCTION(fc_solve_sfs_simple_simon_move_sequence_to_false_parent) { // num_true_seqs - the number of true sequences on the current // false sequence SIMPS_define_vacant_stacks_accessors(); const size_t max_seq_move = calc_max_simple_simon_seq_move(num_vacant_stacks); STACK_SOURCE_LOOP_START(1) size_t num_true_seqs; const int height = get_seq_h(col, &num_true_seqs); // Let's check if we have enough empty stacks to make the move // feasible. if (max_seq_move < num_true_seqs) { continue; } const fcs_card src_card = fcs_col_get_card(col, height); // take the sequence and try and put it on another stack STACK_DEST_LOOP_START(1) if (!fcs_is_ss_false_parent( fcs_col_get_card(dest_col, dest_col_len - 1), src_card)) { continue; } sfs_check_state_begin(); copy_two_stacks(source_stack_idx, dest_stack_idx); fcs_move_sequence( dest_stack_idx, source_stack_idx, (size_t)(col_len - height)); sfs_check_state_end(); STACK_DEST_LOOP_END() STACK_SOURCE_LOOP_END() } #endif // #ifdef FCS_DISABLE_SIMPLE_SIMON