/* * caas.c - the various possible implementations of the function * freecell_solver_check_and_add_state(). * * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 * * This file is in the public domain (it's uncopyrighted). */ #ifndef FC_SOLVE__CAAS_C #define FC_SOLVE__CAAS_C #include #include #include #include "fcs_dm.h" #include "fcs.h" #include "fcs_isa.h" #include "lookup2.h" #ifdef INDIRECT_STACK_STATES #include "fcs_hash.h" #endif #include "caas.h" #include "ms_ca.h" #include "test_arr.h" #ifdef DMALLOC #include "dmalloc.h" #endif /* The objective of the fcs_caas_check_and_insert macros is: 1. To check if new_state is already in the prev_states collection. 2. If not, to add it and to set check to true. 3. If so, to set check to false. */ #if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) #ifdef FCS_WITH_MHASH #define fcs_caas_check_and_insert() \ /* \ Calculate the has function of the state. \ */ \ { \ char * temp_ptr; \ instance->mhash_context = mhash_init(instance->mhash_type); \ mhash(instance->mhash_context, (void *)new_state, sizeof(fcs_state_t)); \ temp_ptr = mhash_end(instance->mhash_context); \ /* Retrieve the first 32 bits and make them the hash value */ \ hash_value_int = *(SFO_hash_value_t*)temp_ptr; \ free(temp_ptr); \ } \ \ if (hash_value_int < 0) \ { \ /* \ * This is a bit mask that nullifies the sign bit of the \ * number so it will always be positive \ * */ \ hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1))); \ } \ check = ((*existing_state = freecell_solver_hash_insert( \ instance->hash, \ new_state, \ hash_value_int, \ 1 \ )) == NULL); #else #define fcs_caas_check_and_insert() \ { \ const char * s_ptr = (char*)new_state; \ const char * s_end = s_ptr+sizeof(fcs_state_t); \ hash_value_int = 0; \ while (s_ptr < s_end) \ { \ hash_value_int += (hash_value_int << 5) + *(s_ptr++); \ } \ hash_value_int += (hash_value_int>>5); \ } \ if (hash_value_int < 0) \ { \ /* \ * This is a bit mask that nullifies the sign bit of the \ * number so it will always be positive \ * */ \ hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1))); \ } \ check = ((*existing_state = freecell_solver_hash_insert( \ instance->hash, \ new_state, \ freecell_solver_lookup2_hash_function( \ (ub1 *)new_state, \ sizeof(fcs_state_t), \ 24 \ ), \ hash_value_int, \ 1 \ )) == NULL); #endif #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT) #define fcs_caas_check_and_insert() \ /* Try to see if the state is found in indirect_prev_states */ \ if ((pos_ptr = (fcs_state_with_locations_t * *)bsearch(&new_state, \ instance->indirect_prev_states, \ instance->num_indirect_prev_states, \ sizeof(fcs_state_with_locations_t *), \ freecell_solver_state_compare_indirect)) == NULL) \ { \ /* It isn't in prev_states, but maybe it's in the sort margin */ \ pos_ptr = (fcs_state_with_locations_t * *)freecell_solver_bsearch( \ &new_state, \ instance->indirect_prev_states_margin, \ instance->num_prev_states_margin, \ sizeof(fcs_state_with_locations_t *), \ freecell_solver_state_compare_indirect_with_context, \ NULL, \ &found); \ \ if (found) \ { \ check = 0; \ *existing_state = *pos_ptr; \ } \ else \ { \ /* Insert the state into its corresponding place in the sort \ * margin */ \ memmove((void*)(pos_ptr+1), \ (void*)pos_ptr, \ sizeof(fcs_state_with_locations_t *) * \ (instance->num_prev_states_margin- \ (pos_ptr-instance->indirect_prev_states_margin) \ )); \ *pos_ptr = new_state; \ \ instance->num_prev_states_margin++; \ \ if (instance->num_prev_states_margin >= PREV_STATES_SORT_MARGIN) \ { \ /* The sort margin is full, let's combine it with the main array */ \ if (instance->num_indirect_prev_states + instance->num_prev_states_margin > instance->max_num_indirect_prev_states) \ { \ while (instance->num_indirect_prev_states + instance->num_prev_states_margin > instance->max_num_indirect_prev_states) \ { \ instance->max_num_indirect_prev_states += PREV_STATES_GROW_BY; \ } \ instance->indirect_prev_states = realloc(instance->indirect_prev_states, sizeof(fcs_state_with_locations_t *) * instance->max_num_indirect_prev_states); \ } \ \ freecell_solver_merge_large_and_small_sorted_arrays( \ instance->indirect_prev_states, \ instance->num_indirect_prev_states, \ instance->indirect_prev_states_margin, \ instance->num_prev_states_margin, \ sizeof(fcs_state_with_locations_t *), \ freecell_solver_state_compare_indirect_with_context, \ NULL \ ); \ \ instance->num_indirect_prev_states += instance->num_prev_states_margin; \ \ instance->num_prev_states_margin=0; \ } \ check = 1; \ } \ \ } \ else \ { \ *existing_state = *pos_ptr; \ check = 0; \ } #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE) #define fcs_caas_check_and_insert() \ *existing_state = (fcs_state_with_locations_t *)rbsearch(new_state, instance->tree); \ check = ((*existing_state) == new_state); #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) || (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) #if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) #define fcs_libavl_states_tree_insert(a,b) avl_insert((a),(b)) #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) #define fcs_libavl_states_tree_insert(a,b) rb_insert((a),(b)) #endif #define fcs_caas_check_and_insert() \ *existing_state = fcs_libavl_states_tree_insert(instance->tree, new_state); \ check = (*existing_state == NULL); #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE) #define fcs_caas_check_and_insert() \ *existing_state = g_tree_lookup(instance->tree, (gpointer)new_state); \ if (*existing_state == NULL) \ { \ /* The new state was not found. Let's insert it. \ * The value must be the same as the key, so g_tree_lookup() \ * will return it. */ \ g_tree_insert( \ instance->tree, \ (gpointer)new_state, \ (gpointer)new_state \ ); \ check = 1; \ } \ else \ { \ check = 0; \ } #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) #define fcs_caas_check_and_insert() \ *existing_state = g_hash_table_lookup(instance->hash, (gpointer)new_state); \ if (*existing_state == NULL) \ { \ /* The new state was not found. Let's insert it. \ * The value must be the same as the key, so g_tree_lookup() \ * will return it. */ \ g_hash_table_insert( \ instance->hash, \ (gpointer)new_state, \ (gpointer)new_state \ \ ); \ check = 1; \ } \ else \ { \ check = 0; \ } #elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE) #define fcs_caas_check_and_insert() \ { \ DBT key, value; \ key.data = new_state; \ key.size = sizeof(*new_state); \ if (instance->db->get( \ instance->db, \ NULL, \ &key, \ &value, \ 0 \ ) == 0) \ { \ /* The new state was not found. Let's insert it. \ * The value must be the same as the key, so g_tree_lookup() \ * will return it. */ \ \ value.data = key.data; \ value.size = key.size; \ instance->db->put( \ instance->db, \ NULL, \ &key, \ &value, \ 0); \ check = 1; \ } \ else \ { \ check = 0; \ *existing_state = (fcs_state_with_locations_t *)(value.data); \ } \ } #else #error no define #endif #ifdef INDIRECT_STACK_STATES static GCC_INLINE void freecell_solver_cache_stacks( freecell_solver_hard_thread_t * hard_thread, fcs_state_with_locations_t * new_state ) { int a; #if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH) SFO_hash_value_t hash_value_int; #endif void * cached_stack; fcs_card_t * new_ptr; freecell_solver_instance_t * instance = hard_thread->instance; int stacks_num = instance->stacks_num; for(a=0 ; astacks_copy_on_write_flags & (1 << a))) { continue; } /* new_state->s.stacks[a] = realloc(new_state->s.stacks[a], fcs_stack_len(new_state->s, a)+1); */ fcs_compact_alloc_typed_ptr_into_var(new_ptr, char, hard_thread->stacks_allocator, (fcs_stack_len(new_state->s, a)+1)); memcpy(new_ptr, new_state->s.stacks[a], (fcs_stack_len(new_state->s, a)+1)); new_state->s.stacks[a] = new_ptr; #if FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH /* Calculate the hash value for the stack */ /* This hash function was ripped from the Perl source code. * (It is not derived work however). */ { const char * s_ptr = (char*)(new_state->s.stacks[a]); const char * s_end = s_ptr+fcs_stack_len(new_state->s, a)+1; hash_value_int = 0; while (s_ptr < s_end) { hash_value_int += (hash_value_int << 5) + *(s_ptr++); } hash_value_int += (hash_value_int >> 5); } if (hash_value_int < 0) { /* * This is a bit mask that nullifies the sign bit of the * number so it will always be positive * */ hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1))); } cached_stack = (void *)freecell_solver_hash_insert( instance->stacks_hash, new_state->s.stacks[a], (SFO_hash_value_t)freecell_solver_lookup2_hash_function( (ub1 *)new_state->s.stacks[a], (fcs_stack_len(new_state->s, a)+1), 24 ), hash_value_int, 1 ); #define replace_with_cached(condition_expr) \ if (cached_stack != NULL) \ { \ fcs_compact_alloc_release(hard_thread->stacks_allocator); \ new_state->s.stacks[a] = cached_stack; \ } replace_with_cached(cached_stack != NULL); #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) || (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE) cached_stack = #if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) avl_insert( #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE) rb_insert( #endif instance->stacks_tree, new_state->s.stacks[a] ); #if 0 ) /* In order to settle gvim and other editors that are keen on parenthesis matching */ #endif replace_with_cached(cached_stack != NULL); #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE) cached_stack = (void *)rbsearch( new_state->s.stacks[a], instance->stacks_tree ); replace_with_cached(cached_stack != new_state->s.stacks[a]); #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE) cached_stack = g_tree_lookup( instance->stacks_tree, (gpointer)new_state->s.stacks[a] ); /* replace_with_cached contains an if statement */ replace_with_cached(cached_stack != NULL) else { g_tree_insert( instance->stacks_tree, (gpointer)new_state->s.stacks[a], (gpointer)new_state->s.stacks[a] ); } #elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH) cached_stack = g_hash_table_lookup( instance->stacks_hash, (gconstpointer)new_state->s.stacks[a] ); replace_with_cached(cached_stack != NULL) else { g_hash_table_insert( instance->stacks_hash, (gpointer)new_state->s.stacks[a], (gpointer)new_state->s.stacks[a] ); } #endif } } #else #define freecell_solver_cache_stacks(instance, new_state) #endif #ifdef FCS_WITH_TALONS void freecell_solver_cache_talon( freecell_solver_instance_t * instance, fcs_state_with_locations_t * new_state ) { void * cached_talon; SFO_hash_value_t hash_value_int; new_state->s.talon = realloc(new_state->s.talon, fcs_klondike_talon_len(new_state->s)+1); #error Add Hash Code hash_value_int = *(SFO_hash_value_t*)instance->hash_value; if (hash_value_int < 0) { /* * This is a bit mask that nullifies the sign bit of the * number so it will always be positive * */ hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1))); } cached_talon = (void *)freecell_solver_hash_insert( instance->talons_hash, new_state->s.talon, hash_value_int, 1 ); if (cached_talon != NULL) { free(new_state->s.talon); new_state->s.talon = cached_talon; } } #endif #if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) guint freecell_solver_hash_function(gconstpointer key) { guint hash_value; const char * s_ptr = (char*)key; const char * s_end = s_ptr+sizeof(fcs_state_t); hash_value = 0; while (s_ptr < s_end) { hash_value += (hash_value << 5) + *(s_ptr++); } hash_value += (hash_value >> 5); return hash_value; } #endif /* * check_and_add_state() does the following things: * * 1. Check if the number of iterations exceeded its maximum, and if so * return FCS_STATE_EXCEEDS_MAX_NUM_TIMES in order to terminate the * solving process. * 2. Check if the maximal depth was reached and if so return * FCS_STATE_EXCEEDS_MAX_DEPTH * 3. Canonize the state. * 4. Check if the state is already found in the collection of the states * that were already checked. * If it is: * * 5a. Return FCS_STATE_ALREADY_EXISTS * * If it isn't: * * 5b. Call solve_for_state() on the board. * * */ GCC_INLINE int freecell_solver_check_and_add_state( freecell_solver_soft_thread_t * soft_thread, fcs_state_with_locations_t * new_state, fcs_state_with_locations_t * * existing_state ) { #if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) SFO_hash_value_t hash_value_int; #endif #if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT) fcs_state_with_locations_t * * pos_ptr; int found; #endif freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread; freecell_solver_instance_t * instance = hard_thread->instance; int check; if (check_if_limits_exceeded()) { return FCS_STATE_BEGIN_SUSPEND_PROCESS; } freecell_solver_cache_stacks(hard_thread, new_state); fcs_canonize_state(new_state, instance->freecells_num, instance->stacks_num); fcs_caas_check_and_insert(); if (check) { /* The new state was not found in the cache, and it was already inserted */ if (new_state->parent) { new_state->parent->num_active_children++; } instance->num_states_in_collection++; if (new_state->moves_to_parent != NULL) { new_state->moves_to_parent = freecell_solver_move_stack_compact_allocate( hard_thread, new_state->moves_to_parent ); } return FCS_STATE_DOES_NOT_EXIST; } else { return FCS_STATE_ALREADY_EXISTS; } } /* * This implementation crashes for some reason, so don't use it. * * */ #if 0 static char meaningless_data[16] = "Hello World!"; int freecell_solver_check_and_add_state(freecell_solver_instance_t * instance, fcs_state_with_locations_t * new_state, int depth) { DBT key, value; if ((instance->max_num_times >= 0) && (instance->max_num_times <= instance->num_times)) { return FCS_STATE_EXCEEDS_MAX_NUM_TIMES; } if ((instance->max_depth >= 0) && (instance->max_depth <= depth)) { return FCS_STATE_EXCEEDS_MAX_DEPTH; } fcs_canonize_state(new_state, instance->freecells_num, instance->stacks_num); freecell_solver_cache_stacks(instance, new_state); key.data = new_state; key.size = sizeof(*new_state); if (instance->db->get( instance->db, NULL, &key, &value, 0 ) == 0) { /* The new state was not found. Let's insert it. * The value should be non-NULL or else g_hash_table_lookup() will * return NULL even if it exists. */ value.data = meaningless_data; value.size = 8; instance->db->put( instance->db, NULL, &key, &value, 0); if (freecell_solver_solve_for_state(instance, new_state, depth+1,0) == FCS_STATE_WAS_SOLVED) { return FCS_STATE_WAS_SOLVED; } else { return FCS_STATE_IS_NOT_SOLVEABLE; } } else { /* free (value.data) ; */ return FCS_STATE_ALREADY_EXISTS; } } #endif #endif /* #ifndef FC_SOLVE__CAAS_C */