/*
 * freecell.c - The various movement tests performed by Freecell Solver
 *
 * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000-2001
 *
 * This file is in the public domain (it's uncopyrighted).
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <limits.h>


#include "fcs_config.h"

#if FCS_STATE_STORAGE==FCS_STATE_STORAGE_LIBREDBLACK_TREE
#include <search.h>
#endif

#include "state.h"
#include "card.h"
#include "fcs_dm.h"
#include "fcs.h"

#include "fcs_isa.h"
#include "tests.h"
#include "ms_ca.h"

#ifdef DMALLOC
#include "dmalloc.h"
#endif

#define state_with_locations (*ptr_state_with_locations)
#define state (ptr_state_with_locations->s)
#define new_state_with_locations (*ptr_new_state_with_locations)
#define new_state (ptr_new_state_with_locations->s)


#ifndef min
#define min(a,b) (((a)<(b))?(a):(b))
#endif

/*
 * Throughout this code the following local variables are used to quickly
 * access the instance's members:
 *
 * state_stacks_num - the number of stacks in the state
 * state_freecells_num - the number of freecells in the state
 * sequences_are_built_by - the type of sequences of this board.
 * */

/*
 * This function tries to move stack cards that are present at the
 * top of stacks to the foundations.
 * */
int freecell_solver_sfs_move_top_stack_cards_to_founds(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();
    int stack;
    int cards_num;
    int deck;
    fcs_card_t card;
    fcs_card_t temp_card;
    int check;
    int state_stacks_num;

    fcs_move_t temp_move;

    tests_define_accessors();

    moves = hard_thread->reusable_move_stack;
    indirect_stacks_buffer = hard_thread->indirect_stacks_buffer;

    state_stacks_num = instance->stacks_num;

    for(stack=0;stack<state_stacks_num;stack++)
    {
        cards_num = fcs_stack_len(state, stack);
        if (cards_num)
        {
            /* Get the top card in the stack */
            card = fcs_stack_card(state,stack,cards_num-1);
            for(deck=0;deck<instance->decks_num;deck++)
            {
                if (fcs_foundation_value(state, deck*4+fcs_card_suit(card)) == fcs_card_card_num(card) - 1)
                {
                    /* We can put it there */

                    sfs_check_state_begin();


                    my_copy_stack(stack);
                    fcs_pop_stack_card(new_state, stack, temp_card);

                    fcs_increment_foundation(new_state, deck*4+fcs_card_suit(card));


		    fcs_move_init(temp_move);
                    fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FOUNDATION);
                    fcs_move_set_src_stack(temp_move,stack);
                    fcs_move_set_foundation(temp_move,deck*4+fcs_card_suit(card));

                    fcs_move_stack_push(moves, temp_move);

                    fcs_flip_top_card(stack);

                    /* The last move needs to be FCS_MOVE_TYPE_CANONIZE
                     * because it indicates that the internal order of the
                     * stacks
                     * and freecells may have changed. */
                    fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE);
                    fcs_move_stack_push(moves, temp_move);

                    sfs_check_state_end()
                    break;
                }
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}


/*
 * This test moves single cards that are present in the freecells to
 * the foundations.
 * */
int freecell_solver_sfs_move_freecell_cards_to_founds(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();
    int fc;
    int deck;
    fcs_card_t card;
    int check;
    fcs_move_t temp_move;
    int state_freecells_num;

    tests_define_accessors();

    state_freecells_num = instance->freecells_num;

    /* Now check the same for the free cells */
    for(fc=0;fc<state_freecells_num;fc++)
    {
        card = fcs_freecell_card(state, fc);
        if (fcs_card_card_num(card) != 0)
        {
            for(deck=0;deck<instance->decks_num;deck++)
            {
                if (fcs_foundation_value(state, deck*4+fcs_card_suit(card)) == fcs_card_card_num(card) - 1)
                {
                    /* We can put it there */
                    sfs_check_state_begin()

                    fcs_empty_freecell(new_state, fc);

                    fcs_increment_foundation(new_state, deck*4+fcs_card_suit(card));

		    fcs_move_init(temp_move);
                    fcs_move_set_type(temp_move,FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION);
                    fcs_move_set_src_freecell(temp_move,fc);
                    fcs_move_set_foundation(temp_move,deck*4+fcs_card_suit(card));

                    fcs_move_stack_push(moves, temp_move);

                    fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE);
                    fcs_move_stack_push(moves, temp_move);

                    sfs_check_state_end();
                }
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}

int freecell_solver_sfs_move_freecell_cards_on_top_of_stacks(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();
    
    int dest_cards_num;
    int ds, fc, dc;
    fcs_card_t dest_card, src_card, temp_card, dest_below_card;
    int check;

    fcs_move_t temp_move;
    int is_seq_in_dest;
    int num_cards_to_relocate;
    int freecells_to_fill, freestacks_to_fill;
    int a,b;
    int state_freecells_num, state_stacks_num, sequences_are_built_by;

    tests_define_accessors();

    state_freecells_num = instance->freecells_num;
    state_stacks_num = instance->stacks_num;
    sequences_are_built_by = instance->sequences_are_built_by;

    /* Let's try to put cards in the freecells on top of stacks */

    /* ds stands for destination stack */
    for(ds=0;ds<state_stacks_num;ds++)
    {
        dest_cards_num = fcs_stack_len(state, ds);

        /* If the stack is not empty we can proceed */
        if (dest_cards_num > 0)
        {
            /*
             * Let's search for a suitable card in the stack
             * */
            for(dc=dest_cards_num-1;dc>=0;dc--)
            {
                dest_card = fcs_stack_card(state, ds, dc);

                /* Scan the freecells */
                for(fc=0;fc<state_freecells_num;fc++)
                {
                    src_card = fcs_freecell_card(state, fc);

                    /* If the freecell is not empty and dest_card is its parent
                     * */
                    if ( (fcs_card_card_num(src_card) != 0) &&
                         fcs_is_parent_card(src_card,dest_card)     )
                    {
                        /* Let's check if we can put it there */

                        /* Check if the destination card is already below a
                         * suitable card */
                        is_seq_in_dest = 0;
                        if (dest_cards_num - 1 > dc)
                        {
                            dest_below_card = fcs_stack_card(state, ds, dc+1);
                            if (fcs_is_parent_card(dest_below_card, dest_card))
                            {
                                is_seq_in_dest = 1;
                            }
                        }


                        if (! is_seq_in_dest)
                        {
                            num_cards_to_relocate = dest_cards_num - dc - 1;

                            freecells_to_fill = min(num_cards_to_relocate, num_freecells);

                            num_cards_to_relocate -= freecells_to_fill;

                            if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD)
                            {
                                freestacks_to_fill = min(num_cards_to_relocate, num_freestacks);

                                num_cards_to_relocate -= freestacks_to_fill;
                            }
                            else
                            {
                                freestacks_to_fill = 0;
                            }

                            if (num_cards_to_relocate == 0)
                            {
                                /* We can move it */

                                sfs_check_state_begin()


                                /* Fill the freecells with the top cards */

                                my_copy_stack(ds);

                                for(a=0 ; a<freecells_to_fill ; a++)
                                {
                                    /* Find a vacant freecell */
                                    for(b=0;b<state_freecells_num;b++)
                                    {
                                        if (fcs_freecell_card_num(new_state, b) == 0)
                                        {
                                            break;
                                        }
                                    }
                                    fcs_pop_stack_card(new_state, ds, temp_card);

                                    fcs_put_card_in_freecell(new_state, b, temp_card);

				    fcs_move_init(temp_move);
                                    fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
                                    fcs_move_set_src_stack(temp_move,ds);
                                    fcs_move_set_dest_freecell(temp_move,b);
                                    fcs_move_stack_push(moves, temp_move);
                                }

                                /* Fill the free stacks with the cards below them */
                                for(a=0; a < freestacks_to_fill ; a++)
                                {
                                    /* Find a vacant stack */
                                    for(b=0;b<state_stacks_num;b++)
                                    {
                                        if (fcs_stack_len(new_state, b) == 0)
                                        {
                                            break;
                                        }
                                    }
                                    my_copy_stack(b);

                                    fcs_pop_stack_card(new_state, ds, temp_card);
                                    fcs_push_card_into_stack(new_state, b, temp_card);
                                    fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
                                    fcs_move_set_src_stack(temp_move,ds);
                                    fcs_move_set_dest_stack(temp_move,b);
                                    fcs_move_set_num_cards_in_seq(temp_move,1);
                                    fcs_move_stack_push(moves, temp_move);
                                }

                                /* Now put the freecell card on top of the stack */
                                
                                fcs_push_card_into_stack(new_state, ds, src_card);
                                fcs_empty_freecell(new_state, fc);
                                fcs_move_set_type(temp_move,FCS_MOVE_TYPE_FREECELL_TO_STACK);
                                fcs_move_set_src_freecell(temp_move,fc);
                                fcs_move_set_dest_stack(temp_move,ds);
                                fcs_move_stack_push(moves, temp_move);

                                sfs_check_state_end()
                            }
                        }
                    }
                }
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}



int freecell_solver_sfs_move_non_top_stack_cards_to_founds(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();
    
    int check;

    int stack;
    int cards_num;
    int c, a, b;
    fcs_card_t temp_card, card;
    int deck;
    int state_freecells_num;
    int state_stacks_num;

    fcs_move_t temp_move;
    fcs_move_init(temp_move);

    tests_define_accessors();

    state_freecells_num = instance->freecells_num;
    state_stacks_num = instance->stacks_num;



    /* Now let's check if a card that is under some other cards can be placed
     * in the foundations. */

    for(stack=0;stack<state_stacks_num;stack++)
    {
        cards_num = fcs_stack_len(state, stack);
        /*
         * We starts from cards_num-2 because the top card is already covered
         * by move_top_stack_cards_to_founds.
         * */
        for(c=cards_num-2 ; c >= 0 ; c--)
        {
            card = fcs_stack_card(state, stack, c);
            for(deck=0;deck<instance->decks_num;deck++)
            {
                if (fcs_foundation_value(state, deck*4+fcs_card_suit(card)) == fcs_card_card_num(card)-1)
                {
                    /* The card is foundation-able. Now let's check if we
                     * can move the cards above it to the freecells and
                     * stacks */

                    if ((num_freecells +
                        ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) ?
                            num_freestacks :
                            0
                        ))
                            >= cards_num-(c+1))
                    {
                        /* We can move it */

                        sfs_check_state_begin()

                        my_copy_stack(stack);


                        /* Fill the freecells with the top cards */
                        for(a=0 ; a<min(num_freecells, cards_num-(c+1)) ; a++)
                        {
                            /* Find a vacant freecell */
                            for(b=0; b<state_freecells_num; b++)
                            {
                                if (fcs_freecell_card_num(new_state, b) == 0)
                                {
                                    break;
                                }
                            }
                            fcs_pop_stack_card(new_state, stack, temp_card);

                            fcs_put_card_in_freecell(new_state, b, temp_card);

                            fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
                            fcs_move_set_src_stack(temp_move,stack);
                            fcs_move_set_dest_freecell(temp_move,b);

                            fcs_move_stack_push(moves, temp_move);

                            fcs_flip_top_card(stack);
                        }

                        /* Fill the free stacks with the cards below them */
                        for(a=0; a < cards_num-(c+1) - min(num_freecells, cards_num-(c+1)) ; a++)
                        {
                            /* Find a vacant stack */
                            for(b=0;b<state_stacks_num;b++)
                            {
                                if (fcs_stack_len(new_state, b) == 0)
                                {
                                    break;
                                }
                            }

                            my_copy_stack(b);

                            fcs_pop_stack_card(new_state, stack, temp_card);
                            fcs_push_card_into_stack(new_state, b, temp_card);

                            fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
                            fcs_move_set_src_stack(temp_move,stack);
                            fcs_move_set_dest_stack(temp_move,b);
                            fcs_move_set_num_cards_in_seq(temp_move,1);

                            fcs_move_stack_push(moves, temp_move);

                            fcs_flip_top_card(stack);
                        }

                        fcs_pop_stack_card(new_state, stack, temp_card);
                        fcs_increment_foundation(new_state, deck*4+fcs_card_suit(temp_card));

                        fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FOUNDATION);
                        fcs_move_set_src_stack(temp_move,stack);
                        fcs_move_set_foundation(temp_move,deck*4+fcs_card_suit(temp_card));

                        fcs_move_stack_push(moves, temp_move);

                        fcs_flip_top_card(stack);

                        sfs_check_state_end()
                    }
                    break;
                }
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}




int freecell_solver_sfs_move_stack_cards_to_a_parent_on_the_same_stack(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();
    int check;

    int stack, c, cards_num, a, dc,b;
    int is_seq_in_dest;
    fcs_card_t card, temp_card, prev_card;
    fcs_card_t dest_below_card, dest_card;
    int freecells_to_fill, freestacks_to_fill;
    int dest_cards_num, num_cards_to_relocate;
    int state_freecells_num;
    int state_stacks_num;
    int sequences_are_built_by;

    fcs_move_t temp_move;
    fcs_move_init(temp_move);

    tests_define_accessors();

    state_freecells_num = instance->freecells_num;
    state_stacks_num = instance->stacks_num;
    sequences_are_built_by = instance->sequences_are_built_by;

    /*
     * Now let's try to move a stack card to a parent card which is found
     * on the same stack.
     * */
    for (stack=0;stack<state_stacks_num;stack++)
    {
        cards_num = fcs_stack_len(state, stack);

        for (c=0 ; c<cards_num ; c++)
        {
            /* Find a card which this card can be put on; */

            card = fcs_stack_card(state, stack, c);


            /* Do not move cards that are already found above a suitable parent */
            a = 1;
            if (c != 0)
            {
                prev_card = fcs_stack_card(state, stack, c-1);
                if ((fcs_card_card_num(prev_card) == fcs_card_card_num(card)+1) &&
                    ((fcs_card_suit(prev_card) & 0x1) != (fcs_card_suit(card) & 0x1)))
                {
                   a = 0;
                }
            }
            if (a)
            {
#define ds stack
                /* Check if it can be moved to something on the same stack */
                dest_cards_num = fcs_stack_len(state, ds);
                for(dc=0;dc<c-1;dc++)
                {
                    dest_card = fcs_stack_card(state, ds, dc);
                    if (fcs_is_parent_card(card, dest_card))
                    {
                        /* Corresponding cards - see if it is feasible to move
                           the source to the destination. */

                        is_seq_in_dest = 0;
                        dest_below_card = fcs_stack_card(state, ds, dc+1);
                        if (fcs_is_parent_card(dest_below_card, dest_card))
                        {
                            is_seq_in_dest = 1;
                        }

                        if (!is_seq_in_dest)
                        {
                            num_cards_to_relocate = dest_cards_num - dc - 1;

                            freecells_to_fill = min(num_cards_to_relocate, num_freecells);

                            num_cards_to_relocate -= freecells_to_fill;

                            if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD)
                            {
                                freestacks_to_fill = min(num_cards_to_relocate, num_freestacks);

                                num_cards_to_relocate -= freestacks_to_fill;
                            }
                            else
                            {
                                freestacks_to_fill = 0;
                            }

                            if (num_cards_to_relocate == 0)
                            {
                                /* We can move it */

                                sfs_check_state_begin()


                                {
                                    int i_card_pos;
                                    fcs_card_t moved_card;
                                    int source_type, source_index;

                                    i_card_pos = fcs_stack_len(new_state,stack)-1;
                                    a = 0;

                                    my_copy_stack(ds);
                                    while(i_card_pos>c)
                                    {
                                        if (a < freecells_to_fill)
                                        {
                                            for(b=0;b<state_freecells_num;b++)
                                            {
                                                if (fcs_freecell_card_num(new_state, b) == 0)
                                                {
                                                    break;
                                                }
                                            }
                                            fcs_pop_stack_card(new_state, ds, temp_card);
                                            fcs_put_card_in_freecell(new_state, b, temp_card);

                                            fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
                                            fcs_move_set_src_stack(temp_move,ds);
                                            fcs_move_set_dest_freecell(temp_move,b);

                                            fcs_move_stack_push(moves, temp_move);

                                        }
                                        else
                                        {

                                            /*  Find a vacant stack */
                                            for(b=0;b<state_stacks_num;b++)
                                            {
                                                if (fcs_stack_len(new_state, b) == 0)
                                                {
                                                    break;
                                                }
                                            }

                                            my_copy_stack(b);

                                            fcs_pop_stack_card(new_state, ds, temp_card);
                                            fcs_push_card_into_stack(new_state, b, temp_card);

                                            fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
                                            fcs_move_set_src_stack(temp_move,ds);
                                            fcs_move_set_dest_stack(temp_move,b);
                                            fcs_move_set_num_cards_in_seq(temp_move,1);

                                            fcs_move_stack_push(moves, temp_move);

                                        }
                                        a++;

                                        i_card_pos--;
                                    }
                                    fcs_pop_stack_card(new_state, ds, moved_card);
                                    if (a < freecells_to_fill)
                                    {
                                        for(b=0;b<state_freecells_num;b++)
                                        {
                                            if (fcs_freecell_card_num(new_state, b) == 0)
                                            {
                                                break;
                                            }
                                        }
                                        fcs_put_card_in_freecell(new_state, b, moved_card);
                                        fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
                                        fcs_move_set_src_stack(temp_move,ds);
                                        fcs_move_set_dest_freecell(temp_move,b);
                                        fcs_move_stack_push(moves, temp_move);

                                        source_type = 0;
                                        source_index = b;
                                    }
                                    else
                                    {
                                        for(b=0;b<state_stacks_num;b++)
                                        {
                                            if (fcs_stack_len(new_state, b) == 0)
                                            {
                                                break;
                                            }
                                        }

                                        my_copy_stack(b);
                                        fcs_push_card_into_stack(new_state, b, moved_card);

                                        fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
                                        fcs_move_set_src_stack(temp_move,ds);
                                        fcs_move_set_dest_stack(temp_move,b);
                                        fcs_move_set_num_cards_in_seq(temp_move,1);
                                        fcs_move_stack_push(moves, temp_move);

                                        source_type = 1;
                                        source_index = b;
                                    }
                                    i_card_pos--;
                                    a++;

                                    while(i_card_pos>dc)
                                    {
                                        if (a < freecells_to_fill)
                                        {
                                            for(b=0;b<state_freecells_num;b++)
                                            {
                                                if (fcs_freecell_card_num(new_state, b) == 0)
                                                {
                                                    break;
                                                }
                                            }
                                            fcs_pop_stack_card(new_state, ds, temp_card);
                                            fcs_put_card_in_freecell(new_state, b, temp_card);

                                            fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
                                            fcs_move_set_src_stack(temp_move,ds);
                                            fcs_move_set_dest_freecell(temp_move,b);

                                            fcs_move_stack_push(moves, temp_move);
                                        }
                                        else
                                        {

                                            /*  Find a vacant stack */
                                            for(b=0;b<state_stacks_num;b++)
                                            {
                                                if (fcs_stack_len(new_state, b) == 0)
                                                {
                                                    break;
                                                }
                                            }

                                            fcs_pop_stack_card(new_state, ds, temp_card);
                                            my_copy_stack(b);
                                            fcs_push_card_into_stack(new_state, b, temp_card);

                                            fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
                                            fcs_move_set_src_stack(temp_move,ds);
                                            fcs_move_set_dest_stack(temp_move,b);
                                            fcs_move_set_num_cards_in_seq(temp_move,1);

                                            fcs_move_stack_push(moves, temp_move);

                                        }
                                        a++;

                                        i_card_pos--;
                                    }

                                    if (source_type == 0)
                                    {
                                        moved_card = fcs_freecell_card(new_state, source_index);
                                        fcs_empty_freecell(new_state, source_index);

                                        fcs_move_set_type(temp_move,FCS_MOVE_TYPE_FREECELL_TO_STACK);
                                        fcs_move_set_src_freecell(temp_move,source_index);
                                        fcs_move_set_dest_stack(temp_move,ds);
                                        fcs_move_stack_push(moves, temp_move);
                                    }
                                    else
                                    {
                                        my_copy_stack(source_index);
                                        fcs_pop_stack_card(new_state, source_index, moved_card);

                                        fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
                                        fcs_move_set_src_stack(temp_move,source_index);
                                        fcs_move_set_dest_stack(temp_move,ds);
                                        fcs_move_set_num_cards_in_seq(temp_move,1);
                                        fcs_move_stack_push(moves, temp_move);
                                    }

                                    fcs_push_card_into_stack(new_state, ds, moved_card);
                                }

                                sfs_check_state_end()
                            }
                        }
                    }
                }
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}
#undef ds


int freecell_solver_sfs_move_stack_cards_to_different_stacks(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();

    int check;

    int stack, c, cards_num, a, dc, ds,b;
    int is_seq_in_dest;
    fcs_card_t card, temp_card, this_card, prev_card;
    fcs_card_t dest_below_card, dest_card;
    int freecells_to_fill, freestacks_to_fill;
    int dest_cards_num, num_cards_to_relocate;
    int seq_end;
    int state_freecells_num;
    int state_stacks_num;
    int sequences_are_built_by;

    fcs_move_t temp_move;
    fcs_move_init(temp_move);

    tests_define_accessors();

    state_freecells_num = instance->freecells_num;
    state_stacks_num = instance->stacks_num;
    sequences_are_built_by = instance->sequences_are_built_by;

    /* Now let's try to move a card from one stack to the other     *
     * Note that it does not involve moving cards lower than king   *
     * to empty stacks                                              */

    for (stack=0;stack<state_stacks_num;stack++)
    {
        cards_num = fcs_stack_len(state, stack);

        for (c=0 ; c<cards_num ; c=seq_end+1)
        {
            /* Check if there is a sequence here. */
            for(seq_end=c ; seq_end<cards_num-1 ; seq_end++)
            {
                this_card = fcs_stack_card(state, stack, seq_end+1);
                prev_card = fcs_stack_card(state, stack, seq_end);

                if (fcs_is_parent_card(this_card,prev_card))
                {
                }
                else
                {
                    break;
                }
            }

            /* Find a card which this card can be put on; */

            card = fcs_stack_card(state, stack, c);

            /* Make sure the card is not flipped or else we can't move it */
            if (fcs_card_get_flipped(card) == 0)
            {
                for(ds=0 ; ds<state_stacks_num; ds++)
                {
                    if (ds != stack)
                    {
                        dest_cards_num = fcs_stack_len(state, ds);
                        for(dc=0;dc<dest_cards_num;dc++)
                        {
                            dest_card = fcs_stack_card(state, ds, dc);

                            if (fcs_is_parent_card(card, dest_card))
                            {
                                /* Corresponding cards - see if it is feasible to move
                                   the source to the destination. */

                                is_seq_in_dest = 0;
                                if (dest_cards_num - 1 > dc)
                                {
                                    dest_below_card = fcs_stack_card(state, ds, dc+1);
                                    if (fcs_is_parent_card(dest_below_card, dest_card))
                                    {
                                        is_seq_in_dest = 1;
                                    }
                                }

                                if (! is_seq_in_dest)
                                {
                                    num_cards_to_relocate = dest_cards_num - dc - 1 + cards_num - seq_end - 1;

                                    freecells_to_fill = min(num_cards_to_relocate, num_freecells);

                                    num_cards_to_relocate -= freecells_to_fill;

                                    if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD)
                                    {
                                        freestacks_to_fill = min(num_cards_to_relocate, num_freestacks);

                                        num_cards_to_relocate -= freestacks_to_fill;
                                    }
                                    else
                                    {
                                        freestacks_to_fill = 0;
                                    }

                                    if ((num_cards_to_relocate == 0) &&
                                       (calc_max_sequence_move(num_freecells-freecells_to_fill, num_freestacks-freestacks_to_fill) >=
                                        seq_end - c + 1))
                                    {
                                        /* We can move it */
                                        int from_which_stack;

                                        sfs_check_state_begin()


                                        /* Fill the freecells with the top cards */

                                        my_copy_stack(stack);
                                        my_copy_stack(ds);

                                        for(a=0 ; a<freecells_to_fill ; a++)
                                        {
                                            /* Find a vacant freecell */
                                            for(b=0;b<state_freecells_num;b++)
                                            {
                                                if (fcs_freecell_card_num(new_state, b) == 0)
                                                {
                                                    break;
                                                }
                                            }

                                            if (fcs_stack_len(new_state, ds) == dc+1)
                                            {
                                                from_which_stack = stack;
                                            }
                                            else
                                            {
                                                from_which_stack = ds;
                                            }
                                            my_copy_stack(from_which_stack);
                                            
                                            fcs_pop_stack_card(new_state, from_which_stack, temp_card);

                                            fcs_put_card_in_freecell(new_state, b, temp_card);

                                            fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
                                            fcs_move_set_src_stack(temp_move,from_which_stack);
                                            fcs_move_set_dest_freecell(temp_move,b);
                                            fcs_move_stack_push(moves, temp_move);

                                            fcs_flip_top_card(from_which_stack);
                                        }

                                        /* Fill the free stacks with the cards below them */
                                        for(a=0; a < freestacks_to_fill ; a++)
                                        {
                                            /*  Find a vacant stack */
                                            for(b=0;b<state_stacks_num;b++)
                                            {
                                                if (fcs_stack_len(new_state, b) == 0)
                                                {
                                                    break;
                                                }
                                            }

                                            if (fcs_stack_len(new_state, ds) == dc+1)
                                            {
                                                from_which_stack = stack;
                                            }
                                            else
                                            {
                                                from_which_stack = ds;
                                            }
                                            
                                            my_copy_stack(b);
                                            fcs_pop_stack_card(new_state, from_which_stack, temp_card);
                                            fcs_push_card_into_stack(new_state, b, temp_card);
                                            fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
                                            fcs_move_set_src_stack(temp_move,from_which_stack);
                                            fcs_move_set_dest_stack(temp_move,b);
                                            fcs_move_set_num_cards_in_seq(temp_move,1);
                                            fcs_move_stack_push(moves, temp_move);

                                            fcs_flip_top_card(from_which_stack);
                                        }

                                        for(a=c ; a <= seq_end ; a++)
                                        {
                                            fcs_push_stack_card_into_stack(new_state, ds, stack, a);
                                        }

                                        for(a=0; a < seq_end-c+1 ;a++)
                                        {
                                            fcs_pop_stack_card(new_state, stack, temp_card);
                                        }
                                        fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
                                        fcs_move_set_src_stack(temp_move,stack);
                                        fcs_move_set_dest_stack(temp_move,ds);
                                        fcs_move_set_num_cards_in_seq(temp_move,seq_end-c+1);
                                        fcs_move_stack_push(moves, temp_move);

                                        fcs_flip_top_card(stack);

                                        sfs_check_state_end()
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}



int freecell_solver_sfs_move_sequences_to_free_stacks(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();
    int check;

    int stack, cards_num, c, ds, a, b, seq_end;
    fcs_card_t this_card, prev_card, temp_card;
    int max_sequence_len;
    int num_cards_to_relocate, freecells_to_fill, freestacks_to_fill;
    int state_freecells_num;
    int state_stacks_num;
    int sequences_are_built_by;

    fcs_move_t temp_move;
    fcs_move_init(temp_move);

    tests_define_accessors();

    if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_NONE)
    {
        return FCS_STATE_IS_NOT_SOLVEABLE;
    }

    state_freecells_num = instance->freecells_num;
    state_stacks_num = instance->stacks_num;
    sequences_are_built_by = instance->sequences_are_built_by;

    max_sequence_len = calc_max_sequence_move(num_freecells, num_freestacks-1);

    /* Now try to move sequences to empty stacks */

    if (num_freestacks > 0)
    {
        for(stack=0;stack<state_stacks_num;stack++)
        {
            cards_num = fcs_stack_len(state, stack);

            for(c=0; c<cards_num; c=seq_end+1)
            {
                /* Check if there is a sequence here. */
                for(seq_end=c ; seq_end<cards_num-1; seq_end++)
                {
                    this_card = fcs_stack_card(state, stack, seq_end+1);
                    prev_card = fcs_stack_card(state, stack, seq_end);

                    if (! fcs_is_parent_card(this_card, prev_card))
                    {
                        break;
                    }
                }

                if ((fcs_stack_card_num(state, stack, c) != 13) &&
                    (instance->empty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY))
                {
                    continue;
                }

                if (seq_end == cards_num -1)
                {
                    /* One stack is the destination stack, so we have one     *
                     * less stack in that case                                */
                    while ((max_sequence_len < cards_num -c) && (c > 0))
                    {
                        c--;
                    }

                    if (
                        (c > 0) &&
                        ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY) ?
                            (fcs_card_card_num(fcs_stack_card(state, stack, c)) == 13) :
                            1
                        )
                       )
                    {
                        sfs_check_state_begin();


                        for(ds=0;ds<state_stacks_num;ds++)
                        {
                            if (fcs_stack_len(state, ds) == 0)
                                break;
                        }

                        my_copy_stack(ds);

                        for(a=c ; a <= cards_num-1 ; a++)
                        {
                            fcs_push_stack_card_into_stack(new_state, ds, stack, a);
                        }

                        my_copy_stack(stack);

                        for(a=0;a<cards_num-c;a++)
                        {
                            fcs_pop_stack_card(new_state, stack, temp_card);
                        }

                        fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
                        fcs_move_set_src_stack(temp_move,stack);
                        fcs_move_set_dest_stack(temp_move,ds);
                        fcs_move_set_num_cards_in_seq(temp_move,cards_num-c);

                        fcs_move_stack_push(moves, temp_move);


                        sfs_check_state_end()
                    }
                }
                else
                {
                    num_cards_to_relocate = cards_num - seq_end - 1;

                    freecells_to_fill = min(num_cards_to_relocate, num_freecells);

                    num_cards_to_relocate -= freecells_to_fill;

                    if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD)
                    {
                        freestacks_to_fill = min(num_cards_to_relocate, num_freestacks);

                        num_cards_to_relocate -= freestacks_to_fill;
                    }
                    else
                    {
                        freestacks_to_fill = 0;
                    }

                    if ((num_cards_to_relocate == 0) && (num_freestacks-freestacks_to_fill > 0))
                    {
                        /* We can move it */
                        int seq_start = c;
                        while (
                            (calc_max_sequence_move(
                                num_freecells-freecells_to_fill,
                                num_freestacks-freestacks_to_fill-1) < seq_end-seq_start+1)
                                &&
                            (seq_start <= seq_end)
                            )
                        {
                            seq_start++;
                        }
                        if ((seq_start <= seq_end) &&
                            ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY) ?
                                (fcs_card_card_num(fcs_stack_card(state, stack, seq_start)) == 13) :
                                1
                            )
                        )
                        {
                            sfs_check_state_begin();


                            /* Fill the freecells with the top cards */

                            my_copy_stack(stack);

                            for(a=0; a<freecells_to_fill ; a++)
                            {
                                /* Find a vacant freecell */
                                for(b=0;b<state_freecells_num;b++)
                                {
                                    if (fcs_freecell_card_num(new_state, b) == 0)
                                    {
                                        break;
                                    }
                                }
                                fcs_pop_stack_card(new_state, stack, temp_card);
                                fcs_put_card_in_freecell(new_state, b, temp_card);

                                fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
                                fcs_move_set_src_stack(temp_move,stack);
                                fcs_move_set_dest_freecell(temp_move,b);
                                fcs_move_stack_push(moves, temp_move);
                            }

                            my_copy_stack(stack);

                            /* Fill the free stacks with the cards below them */
                            for(a=0; a < freestacks_to_fill ; a++)
                            {
                                /* Find a vacant stack */
                                for(b=0; b<state_stacks_num; b++)
                                {
                                    if (fcs_stack_len(new_state, b) == 0)
                                    {
                                        break;
                                    }
                                }

                                my_copy_stack(b);
                                fcs_pop_stack_card(new_state, stack, temp_card);
                                fcs_push_card_into_stack(new_state, b, temp_card);
                                fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
                                fcs_move_set_src_stack(temp_move,stack);
                                fcs_move_set_dest_stack(temp_move,b);
                                fcs_move_set_num_cards_in_seq(temp_move,1);
                                fcs_move_stack_push(moves, temp_move);
                            }

                            /* Find a vacant stack */
                            for(b=0; b<state_stacks_num; b++)
                            {
                                if (fcs_stack_len(new_state, b) == 0)
                                {
                                    break;
                                }
                            }

                            my_copy_stack(b);

                            for(a=seq_start ; a <= seq_end ; a++)
                            {
                                fcs_push_stack_card_into_stack(new_state, b, stack, a);
                            }
                            for(a=seq_start ; a <= seq_end ; a++)
                            {
                                fcs_pop_stack_card(new_state, stack, temp_card);
                            }

                            fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
                            fcs_move_set_src_stack(temp_move,stack);
                            fcs_move_set_dest_stack(temp_move,b);
                            fcs_move_set_num_cards_in_seq(temp_move,seq_end-seq_start+1);
                            fcs_move_stack_push(moves, temp_move);

                            sfs_check_state_end();
                        }
                    }
                }
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}



int freecell_solver_sfs_move_freecell_cards_to_empty_stack(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();
    int check;
    int fc, stack;
    fcs_card_t card;

    fcs_move_t temp_move;

    int state_freecells_num;
    int state_stacks_num;

    /* Let's try to put cards that occupy freecells on an empty stack */

    tests_define_accessors();

    if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_NONE)
    {
        return FCS_STATE_IS_NOT_SOLVEABLE;
    }

    state_freecells_num = instance->freecells_num;
    state_stacks_num = instance->stacks_num;

    for(fc=0;fc<state_freecells_num;fc++)
    {
        card = fcs_freecell_card(state, fc);
        if (
            (instance->empty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY) ?
                (fcs_card_card_num(card) == 13) :
                (fcs_card_card_num(card) != 0)
           )
        {
            for(stack=0;stack<state_stacks_num;stack++)
            {
                if (fcs_stack_len(state, stack) == 0)
                {
                    break;
                }
            }
            if (stack != state_stacks_num)
            {
                /* We can move it */

                sfs_check_state_begin();

                my_copy_stack(stack);

                fcs_push_card_into_stack(new_state, stack, card);
                fcs_empty_freecell(new_state, fc);

		fcs_move_init(temp_move);
                fcs_move_set_type(temp_move,FCS_MOVE_TYPE_FREECELL_TO_STACK);
                fcs_move_set_src_freecell(temp_move,fc);
                fcs_move_set_dest_stack(temp_move,stack);
                fcs_move_stack_push(moves, temp_move);

                sfs_check_state_end()
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}

int freecell_solver_sfs_move_cards_to_a_different_parent(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();

    int check;

    int stack, cards_num, c, a, b, ds, dc;
    int is_seq_in_src, is_seq_in_dest;
    int num_cards_to_relocate;
    int dest_cards_num;
    fcs_card_t card, this_card, prev_card, temp_card;
    fcs_card_t dest_card, dest_below_card;
    int freecells_to_fill, freestacks_to_fill;

    fcs_move_t temp_move;

    int state_freecells_num;
    int state_stacks_num;
    int sequences_are_built_by;

    tests_define_accessors();

    state_freecells_num = instance->freecells_num;
    state_stacks_num = instance->stacks_num;
    sequences_are_built_by = instance->sequences_are_built_by;

    fcs_move_init(temp_move);

    /* This time try to move cards that are already on top of a parent to a different parent */

    for (stack=0;stack<state_stacks_num;stack++)
    {
        cards_num = fcs_stack_len(state, stack);

        for (c=0 ; c<cards_num ; c++)
        {
            /* Check if there is a sequence here. */
            is_seq_in_src = 1;
            for(a=c+1 ; a<cards_num ; a++)
            {
                this_card = fcs_stack_card(state, stack, a);
                prev_card = fcs_stack_card(state, stack, a-1);

                if (fcs_is_parent_card(this_card,prev_card))
                {
                }
                else
                {
                    is_seq_in_src = 0;
                    break;
                }
            }

            /* Find a card which this card can be put on; */

            card = fcs_stack_card(state, stack, c);


            /* Do not move cards that are already found above a suitable parent */
            a = 1;
            if (c != 0)
            {
                prev_card = fcs_stack_card(state, stack, c-1);
                if (fcs_is_parent_card(card,prev_card))
                {
                   a = 0;
                }
            }
            /* And do not move cards that are flipped */
            if (!a)
            {
                this_card = fcs_stack_card(state,stack,c);
                if (fcs_card_get_flipped(this_card))
                {
                    a = 0;
                }
            }
            if (!a)
            {
                for(ds=0 ; ds<state_stacks_num; ds++)
                {
                    if (ds != stack)
                    {
                        dest_cards_num = fcs_stack_len(state, ds);
                        for(dc=0;dc<dest_cards_num;dc++)
                        {
                            dest_card = fcs_stack_card(state, ds, dc);

                            if (fcs_is_parent_card(card,dest_card))
                            {
                                /* Corresponding cards - see if it is feasible to move
                                   the source to the destination. */

                                is_seq_in_dest = 0;
                                if (dest_cards_num - 1 > dc)
                                {
                                    dest_below_card = fcs_stack_card(state, ds, dc+1);
                                    if (fcs_is_parent_card(dest_below_card,dest_card))
                                    {
                                        is_seq_in_dest = 1;
                                    }
                                }

                                if (! is_seq_in_dest)
                                {
                                    if (is_seq_in_src)
                                    {
                                        num_cards_to_relocate = dest_cards_num - dc - 1;

                                        freecells_to_fill = min(num_cards_to_relocate, num_freecells);

                                        num_cards_to_relocate -= freecells_to_fill;

                                        if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD)
                                        {
                                            freestacks_to_fill = min(num_cards_to_relocate, num_freestacks);

                                            num_cards_to_relocate -= freestacks_to_fill;
                                        }
                                        else
                                        {
                                            freestacks_to_fill = 0;
                                        }

                                        if ((num_cards_to_relocate == 0) &&
                                           (calc_max_sequence_move(num_freecells-freecells_to_fill, num_freestacks-freestacks_to_fill) >=
                                            cards_num - c))
                                        {
                                            /* We can move it */

                                            sfs_check_state_begin()


                                            /* Fill the freecells with the top cards */

                                            my_copy_stack(ds);
                                            for(a=0 ; a<freecells_to_fill ; a++)
                                            {
                                                /* Find a vacant freecell */
                                                for(b=0;b<state_freecells_num;b++)
                                                {
                                                    if (fcs_freecell_card_num(new_state, b) == 0)
                                                    {
                                                        break;
                                                    }
                                                }
                                                
                                                fcs_pop_stack_card(new_state, ds, temp_card);

                                                fcs_put_card_in_freecell(new_state, b, temp_card);

                                                fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
                                                fcs_move_set_src_stack(temp_move,ds);
                                                fcs_move_set_dest_freecell(temp_move,b);
                                                fcs_move_stack_push(moves, temp_move);
                                            }

                                            /* Fill the free stacks with the cards below them */
                                            for(a=0; a < freestacks_to_fill ; a++)
                                            {
                                                /*  Find a vacant stack */
                                                for(b=0;b<state_stacks_num;b++)
                                                {
                                                    if (fcs_stack_len(new_state, b) == 0)
                                                    {
                                                        break;
                                                    }
                                                }

                                                my_copy_stack(b);

                                                fcs_pop_stack_card(new_state, ds, temp_card);
                                                fcs_push_card_into_stack(new_state, b, temp_card);

                                                fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
                                                fcs_move_set_src_stack(temp_move,ds);
                                                fcs_move_set_dest_stack(temp_move,b);
                                                fcs_move_set_num_cards_in_seq(temp_move,1);
                                                fcs_move_stack_push(moves, temp_move);
                                            }

                                            my_copy_stack(stack);

                                            for(a=c ; a <= cards_num-1 ; a++)
                                            {
                                                fcs_push_stack_card_into_stack(new_state, ds, stack, a);
                                            }

                                            for(a=0;a<cards_num-c;a++)
                                            {
                                                fcs_pop_stack_card(new_state, stack, temp_card);
                                            }

                                            fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
                                            fcs_move_set_src_stack(temp_move,stack);
                                            fcs_move_set_dest_stack(temp_move,ds);
                                            fcs_move_set_num_cards_in_seq(temp_move,cards_num-c);
                                            fcs_move_stack_push(moves, temp_move);

                                            sfs_check_state_end()
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}




int freecell_solver_sfs_empty_stack_into_freecells(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();

    int check;

    int stack, cards_num, c, b;
    fcs_card_t temp_card;
    int state_stacks_num;
    int state_freecells_num;

    fcs_move_t temp_move;

    tests_define_accessors();

    if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_NONE)
    {
        return FCS_STATE_IS_NOT_SOLVEABLE;
    }

    state_stacks_num = instance->stacks_num;
    state_freecells_num = instance->freecells_num;


    /* Now, let's try to empty an entire stack into the freecells, so other cards can
     * inhabit it */

    if (num_freestacks == 0)
    {
        for(stack=0;stack<state_stacks_num;stack++)
        {
            cards_num = fcs_stack_len(state, stack);
            if (cards_num <= num_freecells)
            {
                /* We can empty it */

                sfs_check_state_begin()
                
                my_copy_stack(stack);

                for(c=0;c<cards_num;c++)
                {
                    /* Find a vacant freecell */
                    for(b=0; b<state_freecells_num; b++)
                    {
                        if (fcs_freecell_card_num(new_state, b) == 0)
                        {
                            break;
                        }
                    }
                    fcs_pop_stack_card(new_state, stack, temp_card);

                    fcs_put_card_in_freecell(new_state, b, temp_card);

		    fcs_move_init(temp_move);
                    fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
                    fcs_move_set_src_stack(temp_move,stack);
                    fcs_move_set_dest_freecell(temp_move,b);
                    fcs_move_stack_push(moves, temp_move);
                }

                sfs_check_state_end()
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;

}

int freecell_solver_sfs_yukon_do_nothing(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    return FCS_STATE_IS_NOT_SOLVEABLE;
}

int freecell_solver_sfs_yukon_move_card_to_parent(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();

    int check;

    int stack, cards_num, c, a, ds;
    int dest_cards_num;
    fcs_card_t card, temp_card;
    fcs_card_t dest_card;

    int state_stacks_num;
    int sequences_are_built_by;

    fcs_move_t temp_move;

    tests_define_accessors();

    state_stacks_num = instance->stacks_num;
    sequences_are_built_by = instance->sequences_are_built_by;

    for( ds=0 ; ds < state_stacks_num ; ds++ )
    {
        dest_cards_num = fcs_stack_len(state, ds);
        if (dest_cards_num > 0)
        {
            dest_card = fcs_stack_card(state, ds, dest_cards_num-1);
            for( stack=0 ; stack < state_stacks_num ; stack++)
            {
                if (stack == ds)
                {
                    continue;
                }
                cards_num = fcs_stack_len(state, stack);
                for( c=cards_num-1 ; c >= 0 ; c--)
                {
                    card = fcs_stack_card(state, stack, c);
                    if (fcs_card_get_flipped(card))
                    {
                        break;
                    }
                    if (fcs_is_parent_card(card, dest_card))
                    {
                        /* We can move it there - now let's check to see
                         * if it is already above a suitable parent. */
                        if ((c == 0) ||
                            (! fcs_is_parent_card(card, fcs_stack_card(state, stack, c-1))))
                        {
                            /* Let's move it */
                            sfs_check_state_begin();

                            my_copy_stack(stack);
                            my_copy_stack(ds);

                            fcs_move_sequence(ds, stack, c, cards_num-1, a);

                            fcs_flip_top_card(stack);

                            sfs_check_state_end();
                        }

                    }
                }
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}

int freecell_solver_sfs_yukon_move_kings_to_empty_stack(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();

    int check;

    int stack, cards_num, c, a, ds;
    fcs_card_t card, temp_card;

    int state_stacks_num;

    fcs_move_t temp_move;

    tests_define_accessors();

    if (num_freestacks == 0)
    {
        return FCS_STATE_IS_NOT_SOLVEABLE;
    }

    state_stacks_num = instance->stacks_num;

    for( stack=0 ; stack < state_stacks_num ; stack++)
    {
        cards_num = fcs_stack_len(state, stack);
        for( c=cards_num-1 ; c >= 1 ; c--)
        {
            card = fcs_stack_card(state, stack, c);
            if (fcs_card_get_flipped(card))
            {
                break;
            }
            if (fcs_card_card_num(card) == 13)
            {
                /* It's a King - so let's move it */
                sfs_check_state_begin();


                for( ds=0 ; ds < state_stacks_num ; ds++)
                {
                    if (fcs_stack_len(state, ds) == 0)
                    {
                        break;
                    }
                }
                my_copy_stack(stack);
                my_copy_stack(ds);
                fcs_move_sequence(ds, stack, c, cards_num-1, a);


                fcs_flip_top_card(stack);

                sfs_check_state_end();
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}



#ifdef FCS_WITH_TALONS
/*
    Let's try to deal the Gypsy-type Talon.

  */
int freecell_solver_sfs_deal_gypsy_talon(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();

    int check;

    fcs_card_t temp_card;
    int a;

    fcs_move_t temp_move;

    tests_define_accessors();

    if (instance->talon_type != FCS_TALON_GYPSY)
    {
        return FCS_STATE_IS_NOT_SOLVEABLE;
    }

    moves = hard_thread->reusable_move_stack;
    indirect_stacks_buffer = hard_thread->indirect_stacks_buffer;

    if (fcs_talon_pos(state) < fcs_talon_len(state))
    {
        sfs_check_state_begin()
        for(a=0;a<state_stacks_num;a++)
        {
            temp_card = fcs_get_talon_card(new_state, fcs_talon_pos(new_state)+a);
            fcs_push_card_into_stack(new_state,a,temp_card);
        }
        fcs_talon_pos(new_state) += state_stacks_num;
        fcs_move_set_type(temp_move, FCS_MOVE_TYPE_DEAL_GYPSY_TALON);
        fcs_move_stack_push(moves, temp_move);

        sfs_check_state_end()
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}


int freecell_solver_sfs_get_card_from_klondike_talon(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();


    fcs_state_with_locations_t * talon_temp;

    fcs_move_t temp_move;

    int check;
    int num_redeals_left, num_redeals_done, num_cards_moved[2];
    int first_iter;
    fcs_card_t card_to_check, top_card;
    int s;
    int cards_num;
    int a;

    tests_define_accessors();

    if (instance->talon_type != FCS_TALON_KLONDIKE)
    {
        return FCS_STATE_IS_NOT_SOLVEABLE;
    }

    /* Duplicate the talon and its parameters into talon_temp */
    talon_temp = malloc(sizeof(fcs_state_with_locations_t));
    talon_temp->s.talon = malloc(fcs_klondike_talon_len(state)+1);
    memcpy(
        talon_temp->s.talon,
        ptr_state_with_locations->s.talon,
        fcs_klondike_talon_len(state)+1
        );
    memcpy(
        talon_temp->s.talon_params,
        ptr_state_with_locations->s.talon_params,
        sizeof(ptr_state_with_locations->s.talon_params)
        );

    /* Make sure we redeal the talon only once */
    num_redeals_left = fcs_klondike_talon_num_redeals_left(state);
    if ((num_redeals_left > 0) || (num_redeals_left < 0))
    {
        num_redeals_left = 1;
    }
    num_redeals_done = 0;
    num_cards_moved[0] = 0;
    num_cards_moved[1] = 0;

    first_iter = 1;
    while (num_redeals_left >= 0)
    {
        if ((fcs_klondike_talon_stack_pos(talon_temp->s) == -1) &&
            (fcs_klondike_talon_queue_pos(talon_temp->s) == fcs_klondike_talon_len(talon_temp->s)))
        {
            break;
        }
        if ((!first_iter) || (fcs_klondike_talon_stack_pos(talon_temp->s) == -1))
        {
            if (fcs_klondike_talon_queue_pos(talon_temp->s) == fcs_klondike_talon_len(talon_temp->s))
            {
                if (num_redeals_left > 0)
                {
                    fcs_klondike_talon_len(talon_temp->s) = fcs_klondike_talon_stack_pos(talon_temp->s);
                    fcs_klondike_talon_redeal_bare(talon_temp->s);

                    num_redeals_left--;
                    num_redeals_done++;
                }
                else
                {
                    break;
                }
            }
            fcs_klondike_talon_queue_to_stack(talon_temp->s);
            num_cards_moved[num_redeals_done]++;
        }
        first_iter = 0;

        card_to_check = fcs_klondike_talon_get_top_card(talon_temp->s);
        for(s=0 ; s<state_stacks_num ; s++)
        {
            cards_num = fcs_stack_len(state,s);
            top_card = fcs_stack_card(state,s,cards_num-1);
            if (fcs_is_parent_card(card_to_check, top_card))
            {
                /* We have a card in the talon that we can move
                to the stack, so let's move it */
                sfs_check_state_begin()

                new_state.talon = malloc(fcs_klondike_talon_len(talon_temp->s)+1);
                memcpy(
                    new_state.talon,
                    talon_temp->s.talon,
                    fcs_klondike_talon_len(talon_temp->s)+1
                    );

                memcpy(
                    ptr_new_state_with_locations->s.talon_params,
                    talon_temp->s.talon_params,
                    sizeof(ptr_state_with_locations->s.talon_params)
                );

                for(a=0;a<=num_redeals_done;a++)
                {
                    fcs_move_set_type(temp_move, FCS_MOVE_TYPE_KLONDIKE_FLIP_TALON);
                    fcs_move_set_num_cards_flipped(temp_move, num_cards_moved[a]);
                    fcs_move_stack_push(moves, temp_move);
                    if (a != num_redeals_done)
                    {
                        fcs_move_set_type(temp_move, FCS_MOVE_TYPE_KLONDIKE_REDEAL_TALON);
                        fcs_move_stack_push(moves,temp_move);
                    }
                }
                fcs_push_card_into_stack(new_state, s, fcs_klondike_talon_get_top_card(new_state));
                fcs_move_set_type(temp_move, FCS_MOVE_TYPE_KLONDIKE_TALON_TO_STACK);
                fcs_move_set_dest_stack(temp_move, s);
                fcs_klondike_talon_decrement_stack(new_state);

                sfs_check_state_end()
            }
        }
    }



#if 0
 cleanup:
#endif
    free(talon_temp->s.talon);
    free(talon_temp);

    return FCS_STATE_IS_NOT_SOLVEABLE;

}
       
#endif

int freecell_solver_sfs_atomic_move_card_to_empty_stack(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();
    int empty_stacks_filled_by, state_stacks_num;
    int stack, cards_num;
    fcs_card_t card, temp_card;
    fcs_move_t temp_move;
    int check;
    int empty_stack_idx;

    tests_define_accessors();

    if (num_freestacks == 0)
    {
        return FCS_STATE_IS_NOT_SOLVEABLE;
    }

    state_stacks_num = instance->stacks_num;

    for(empty_stack_idx=0;empty_stack_idx<state_stacks_num;empty_stack_idx++)
    {
        if (fcs_stack_len(state, empty_stack_idx) == 0)
        {
            break;
        }
    }

    empty_stacks_filled_by = instance->empty_stacks_fill;

    if (empty_stacks_filled_by == FCS_ES_FILLED_BY_NONE)
    {
        return FCS_STATE_IS_NOT_SOLVEABLE;
    }

    

    for(stack=0;stack<state_stacks_num;stack++)
    {
        cards_num = fcs_stack_len(state, stack);
        if (cards_num > 0)
        {
            card = fcs_stack_card(state, stack, cards_num-1);
            if ((empty_stacks_filled_by == FCS_ES_FILLED_BY_KINGS_ONLY) &&
                (fcs_card_card_num(card) != 13))
            {
                continue;
            }
            /* Let's move it */
            {
                sfs_check_state_begin();

                my_copy_stack(stack);

                fcs_pop_stack_card(new_state, stack, temp_card);
                

                my_copy_stack(empty_stack_idx);

                fcs_push_card_into_stack(new_state, empty_stack_idx, card);

                fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_STACK);
                fcs_move_set_src_stack(temp_move, stack);
                fcs_move_set_dest_stack(temp_move, empty_stack_idx);
                fcs_move_set_num_cards_in_seq(temp_move, 1);

                fcs_move_stack_push(moves, temp_move);

                fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE);
                fcs_move_stack_push(moves, temp_move);
                
                sfs_check_state_end()                
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}

int freecell_solver_sfs_atomic_move_card_to_parent(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();
    int state_stacks_num;
    int stack, cards_num, ds, ds_cards_num;
    fcs_card_t card, dest_card, temp_card;
    fcs_move_t temp_move;
    int check;
    int sequences_are_built_by;

    tests_define_accessors();

    state_stacks_num = instance->stacks_num;

    sequences_are_built_by = instance->sequences_are_built_by;
    

    for(stack=0;stack<state_stacks_num;stack++)
    {
        cards_num = fcs_stack_len(state, stack);
        if (cards_num > 0)
        {
            card = fcs_stack_card(state, stack, cards_num-1);
            
            for(ds=0;ds<state_stacks_num;ds++)
            {
                if (ds == stack)
                {
                    continue;
                }

                ds_cards_num = fcs_stack_len(state, ds);
                if (ds_cards_num > 0)
                {
                    dest_card = fcs_stack_card(state, ds, ds_cards_num-1);
                    if (fcs_is_parent_card(card, dest_card))
                    {
                        /* Let's move it */
                        {
                            sfs_check_state_begin();

                            my_copy_stack(stack);
                            my_copy_stack(ds);

                            fcs_pop_stack_card(new_state, stack, temp_card);
                            
                            fcs_push_card_into_stack(new_state, ds, card);

                            fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_STACK);
                            fcs_move_set_src_stack(temp_move, stack);
                            fcs_move_set_dest_stack(temp_move, ds);
                            fcs_move_set_num_cards_in_seq(temp_move, 1);

                            fcs_move_stack_push(moves, temp_move);

                            fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE);
                            fcs_move_stack_push(moves, temp_move);
                            
                            sfs_check_state_end()                
                        }
                    }
                }
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}

int freecell_solver_sfs_atomic_move_card_to_freecell(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();
    int state_stacks_num;
    int state_freecells_num;
    int stack, cards_num, ds;
    fcs_card_t card, temp_card;
    fcs_move_t temp_move;
    int check;
    int sequences_are_built_by;

    tests_define_accessors();

    state_stacks_num = instance->stacks_num;
    state_freecells_num = instance->freecells_num;

    sequences_are_built_by = instance->sequences_are_built_by;

    if (num_freecells == 0)
    {
        return FCS_STATE_IS_NOT_SOLVEABLE;
    }

    for(ds=0;ds<state_freecells_num;ds++)
    {
        if (fcs_freecell_card_num(state, ds) == 0)
        {
            break;
        }
    }
    
    

    for(stack=0;stack<state_stacks_num;stack++)
    {
        cards_num = fcs_stack_len(state, stack);
        if (cards_num > 0)
        {
            card = fcs_stack_card(state, stack, cards_num-1);

            /* Let's move it */
            {
                sfs_check_state_begin();

                my_copy_stack(stack);

                fcs_pop_stack_card(new_state, stack, temp_card);
                
                fcs_put_card_in_freecell(new_state, ds, card);

		fcs_move_init(temp_move);
                fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_FREECELL);
                fcs_move_set_src_stack(temp_move, stack);
                fcs_move_set_dest_freecell(temp_move, ds);

                fcs_move_stack_push(moves, temp_move);

                fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE);
                fcs_move_stack_push(moves, temp_move);
                
                sfs_check_state_end()                
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}

int freecell_solver_sfs_atomic_move_freecell_card_to_parent(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();
    int state_stacks_num, state_freecells_num;
    int fc, ds, ds_cards_num;
    fcs_card_t card, dest_card;
    fcs_move_t temp_move;
    int check;
    int sequences_are_built_by;

    tests_define_accessors();

    state_stacks_num = instance->stacks_num;
    state_freecells_num = instance->freecells_num;

    sequences_are_built_by = instance->sequences_are_built_by;

    
    

    for(fc=0;fc<state_freecells_num;fc++)
    {
        card = fcs_freecell_card(state, fc);
        if (fcs_card_card_num(card) == 0)
        {
            continue;
        }
        
        for(ds=0;ds<state_stacks_num;ds++)
        {
            ds_cards_num = fcs_stack_len(state, ds);
            if (ds_cards_num > 0)
            {
                dest_card = fcs_stack_card(state, ds, ds_cards_num-1);
                if (fcs_is_parent_card(card, dest_card))
                {
                    /* Let's move it */
                    {
                        sfs_check_state_begin();

                        my_copy_stack(ds);

                        fcs_empty_freecell(new_state, fc);
                        
                        fcs_push_card_into_stack(new_state, ds, card);

                        fcs_move_set_type(temp_move, FCS_MOVE_TYPE_FREECELL_TO_STACK);
                        fcs_move_set_src_freecell(temp_move, fc);
                        fcs_move_set_dest_stack(temp_move, ds);
                        fcs_move_set_num_cards_in_seq(temp_move, 1);

                        fcs_move_stack_push(moves, temp_move);

                        fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE);
                        fcs_move_stack_push(moves, temp_move);
                        
                        sfs_check_state_end()                
                    }
                }
            }
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}

int freecell_solver_sfs_atomic_move_freecell_card_to_empty_stack(
        freecell_solver_soft_thread_t * soft_thread,
        fcs_state_with_locations_t * ptr_state_with_locations,
        int num_freestacks,
        int num_freecells,
        fcs_derived_states_list_t * derived_states_list,
        int reparent
        )
{
    tests_declare_accessors();
    int state_stacks_num, state_freecells_num;
    int fc, ds;
    fcs_card_t card;
    fcs_move_t temp_move;
    int check;
    int sequences_are_built_by, empty_stacks_filled_by;

    tests_define_accessors();

    moves = hard_thread->reusable_move_stack;
    indirect_stacks_buffer = hard_thread->indirect_stacks_buffer;

    state_stacks_num = instance->stacks_num;
    state_freecells_num = instance->freecells_num;

    sequences_are_built_by = instance->sequences_are_built_by;

    if (num_freestacks == 0)
    {
        return FCS_STATE_IS_NOT_SOLVEABLE;
    }

    empty_stacks_filled_by = instance->empty_stacks_fill;

    if (empty_stacks_filled_by == FCS_ES_FILLED_BY_NONE)
    {
        return FCS_STATE_IS_NOT_SOLVEABLE;
    }
    
    for(ds=0;ds<state_stacks_num;ds++)
    {
        if (fcs_stack_len(state, ds) == 0)
        {
            break;
        }
    }

    for(fc=0;fc<state_freecells_num;fc++)
    {
        card = fcs_freecell_card(state, fc);
        if (fcs_card_card_num(card) == 0)
        {
            continue;
        }

        if ((empty_stacks_filled_by == FCS_ES_FILLED_BY_KINGS_ONLY) &&
            (fcs_card_card_num(card) != 13))
        {
            continue;
        }        

        {
            sfs_check_state_begin();

            my_copy_stack(ds);

            fcs_empty_freecell(new_state, fc);
            
            fcs_push_card_into_stack(new_state, ds, card);

            fcs_move_set_type(temp_move, FCS_MOVE_TYPE_FREECELL_TO_STACK);
            fcs_move_set_src_freecell(temp_move, fc);
            fcs_move_set_dest_stack(temp_move, ds);
            fcs_move_set_num_cards_in_seq(temp_move, 1);

            fcs_move_stack_push(moves, temp_move);

            fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE);
            fcs_move_stack_push(moves, temp_move);
            
            sfs_check_state_end()                
        }
    }

    return FCS_STATE_IS_NOT_SOLVEABLE;
}


#undef state_with_locations
#undef state
#undef new_state_with_locations
#undef new_state


freecell_solver_solve_for_state_test_t freecell_solver_sfs_tests[FCS_TESTS_NUM] =
{
    freecell_solver_sfs_move_top_stack_cards_to_founds,
    freecell_solver_sfs_move_freecell_cards_to_founds,
    freecell_solver_sfs_move_freecell_cards_on_top_of_stacks,
    freecell_solver_sfs_move_non_top_stack_cards_to_founds,
    freecell_solver_sfs_move_stack_cards_to_different_stacks,
    freecell_solver_sfs_move_stack_cards_to_a_parent_on_the_same_stack,
    freecell_solver_sfs_move_sequences_to_free_stacks,
    freecell_solver_sfs_move_freecell_cards_to_empty_stack,
    freecell_solver_sfs_move_cards_to_a_different_parent,
    freecell_solver_sfs_empty_stack_into_freecells,
    freecell_solver_sfs_simple_simon_move_sequence_to_founds,
    freecell_solver_sfs_simple_simon_move_sequence_to_true_parent,
    freecell_solver_sfs_simple_simon_move_whole_stack_sequence_to_false_parent,
    freecell_solver_sfs_simple_simon_move_sequence_to_true_parent_with_some_cards_above,
    freecell_solver_sfs_simple_simon_move_sequence_with_some_cards_above_to_true_parent,
    freecell_solver_sfs_simple_simon_move_sequence_with_junk_seq_above_to_true_parent_with_some_cards_above,
    freecell_solver_sfs_simple_simon_move_whole_stack_sequence_to_false_parent_with_some_cards_above,
    freecell_solver_sfs_simple_simon_move_sequence_to_parent_on_the_same_stack,
    freecell_solver_sfs_atomic_move_card_to_empty_stack,
    freecell_solver_sfs_atomic_move_card_to_parent,
    freecell_solver_sfs_atomic_move_card_to_freecell,
    freecell_solver_sfs_atomic_move_freecell_card_to_parent,
    freecell_solver_sfs_atomic_move_freecell_card_to_empty_stack,
#if 0
    freecell_solver_sfs_move_top_stack_cards_to_founds,
    freecell_solver_sfs_yukon_move_card_to_parent,
    freecell_solver_sfs_yukon_move_kings_to_empty_stack,
    freecell_solver_sfs_yukon_do_nothing,
    freecell_solver_sfs_yukon_do_nothing,
#endif
    freecell_solver_sfs_yukon_do_nothing,
    freecell_solver_sfs_yukon_do_nothing
#ifdef FCS_WITH_TALONS
        ,
    freecell_solver_sfs_deal_gypsy_talon,
    freecell_solver_sfs_get_card_from_klondike_talon
#endif
};
