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.
630 lines
21 KiB
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 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.
|
|
*
|
|
* */
|
|
|
|
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 */
|