You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegames/kpat/freecell-solver/caas.c

630 lines
21 KiB

/*
* 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#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 ; a<stacks_num ; a++)
{
/*
* If the stack is not a copy - it is already cached so skip
* to the next stack
* */
if (! (new_state->stacks_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 */