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.
1115 lines
28 KiB
1115 lines
28 KiB
/*
|
|
* state.c - state functions module for Freecell Solver
|
|
*
|
|
* Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
|
|
*
|
|
* This file is in the public domain (it's uncopyrighted).
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include "fcs_config.h"
|
|
#include "state.h"
|
|
#include "card.h"
|
|
#include "fcs_enums.h"
|
|
#include "app_str.h"
|
|
|
|
#ifdef DMALLOC
|
|
#include "dmalloc.h"
|
|
#endif
|
|
|
|
|
|
#ifndef min
|
|
#define min(a,b) ((a)<(b)?(a):(b))
|
|
#endif
|
|
|
|
|
|
#ifdef DEBUG_STATES
|
|
|
|
fcs_card_t freecell_solver_empty_card = {0,0};
|
|
|
|
#elif defined(COMPACT_STATES) || defined (INDIRECT_STACK_STATES)
|
|
|
|
fcs_card_t freecell_solver_empty_card = (fcs_card_t)0;
|
|
|
|
#endif
|
|
|
|
static int fcs_card_compare(const void * card1, const void * card2)
|
|
{
|
|
const fcs_card_t * c1 = (const fcs_card_t *)card1;
|
|
const fcs_card_t * c2 = (const fcs_card_t *)card2;
|
|
|
|
if (fcs_card_card_num(*c1) > fcs_card_card_num(*c2))
|
|
{
|
|
return 1;
|
|
}
|
|
else if (fcs_card_card_num(*c1) < fcs_card_card_num(*c2))
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
if (fcs_card_suit(*c1) > fcs_card_suit(*c2))
|
|
{
|
|
return 1;
|
|
}
|
|
else if (fcs_card_suit(*c1) < fcs_card_suit(*c2))
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_STATES
|
|
static int fcs_stack_compare(const void * s1, const void * s2)
|
|
{
|
|
fcs_card_t card1 = ((const fc_stack_t *)s1)->cards[0];
|
|
fcs_card_t card2 = ((const fc_stack_t *)s2)->cards[0];
|
|
|
|
return fcs_card_compare(&card1, &card2);
|
|
}
|
|
#elif defined(COMPACT_STATES)
|
|
static int fcs_stack_compare(const void * s1, const void * s2)
|
|
{
|
|
fcs_card_t card1 = ((fcs_card_t*)s1)[1];
|
|
fcs_card_t card2 = ((fcs_card_t*)s2)[1];
|
|
|
|
return fcs_card_compare(&card1, &card2);
|
|
}
|
|
#elif defined(INDIRECT_STACK_STATES)
|
|
|
|
|
|
#if MAX_NUM_DECKS == 1
|
|
static int fcs_stack_compare_for_stack_sort(const void * s1, const void * s2)
|
|
{
|
|
fcs_card_t card1 = ((fcs_card_t*)s1)[1];
|
|
fcs_card_t card2 = ((fcs_card_t*)s2)[1];
|
|
|
|
return fcs_card_compare(&card1, &card2);
|
|
}
|
|
#endif
|
|
|
|
int freecell_solver_stack_compare_for_comparison(const void * v_s1, const void * v_s2)
|
|
{
|
|
const fcs_card_t * s1 = (const fcs_card_t *)v_s1;
|
|
const fcs_card_t * s2 = (const fcs_card_t *)v_s2;
|
|
|
|
int min_len;
|
|
int a, ret;
|
|
|
|
min_len = min(s1[0], s2[0]);
|
|
|
|
for(a=0;a<min_len;a++)
|
|
{
|
|
ret = fcs_card_compare(s1+a+1,s2+a+1);
|
|
if (ret != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
/*
|
|
* The reason I do the stack length comparisons after the card-by-card
|
|
* comparison is to maintain correspondence with
|
|
* fcs_stack_compare_for_stack_sort, and with the one card comparison
|
|
* of the other state representation mechanisms.
|
|
* */
|
|
if (s1[0] < s2[0])
|
|
{
|
|
return -1;
|
|
}
|
|
else if (s1[0] > s2[0])
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef FCS_WITH_TALONS
|
|
static int fcs_talon_compare_with_context(const void * p1, const void * p2, fcs_compare_context_t context)
|
|
{
|
|
fcs_card_t * t1 = (fcs_card_t *)p1;
|
|
fcs_card_t * t2 = (fcs_card_t *)p2;
|
|
|
|
if (t1[0] < t2[0])
|
|
{
|
|
return -1;
|
|
}
|
|
else if (t1[0] > t2[0])
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return memcmp(t1,t2,t1[0]+1);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG_STATES
|
|
void freecell_solver_canonize_state(fcs_state_with_locations_t * state, int freecells_num, int stacks_num)
|
|
{
|
|
int b,c;
|
|
|
|
fc_stack_t temp_stack;
|
|
fcs_card_t temp_freecell;
|
|
int temp_loc;
|
|
|
|
/* Insertion-sort the stacks */
|
|
for(b=1;b<stacks_num;b++)
|
|
{
|
|
c = b;
|
|
while(
|
|
(c>0) &&
|
|
(fcs_stack_compare(
|
|
&(state->s.stacks[c]),
|
|
&(state->s.stacks[c-1])
|
|
) < 0)
|
|
)
|
|
{
|
|
temp_stack = state->s.stacks[c];
|
|
state->s.stacks[c] = state->s.stacks[c-1];
|
|
state->s.stacks[c-1] = temp_stack;
|
|
|
|
temp_loc = state->stack_locs[c];
|
|
state->stack_locs[c] = state->stack_locs[c-1];
|
|
state->stack_locs[c-1] = temp_loc;
|
|
|
|
c--;
|
|
}
|
|
}
|
|
|
|
/* Insertion sort the freecells */
|
|
|
|
for(b=1;b<freecells_num;b++)
|
|
{
|
|
c = b;
|
|
while(
|
|
(c>0) &&
|
|
(fcs_card_compare(
|
|
&(state->s.freecells[c]),
|
|
&(state->s.freecells[c-1])
|
|
) < 0)
|
|
)
|
|
{
|
|
temp_freecell = state->s.freecells[c];
|
|
state->s.freecells[c] = state->s.freecells[c-1];
|
|
state->s.freecells[c-1] = temp_freecell;
|
|
|
|
temp_loc = state->fc_locs[c];
|
|
state->fc_locs[c] = state->fc_locs[c-1];
|
|
state->fc_locs[c-1] = temp_loc;
|
|
|
|
c--;
|
|
}
|
|
}
|
|
}
|
|
|
|
#elif defined(COMPACT_STATES)
|
|
|
|
void freecell_solver_canonize_state(
|
|
fcs_state_with_locations_t * state,
|
|
int freecells_num,
|
|
int stacks_num)
|
|
{
|
|
int b,c;
|
|
|
|
char temp_stack[(MAX_NUM_CARDS_IN_A_STACK+1)];
|
|
fcs_card_t temp_freecell;
|
|
char temp_loc;
|
|
|
|
/* Insertion-sort the stacks */
|
|
|
|
for(b=1;b<stacks_num;b++)
|
|
{
|
|
c = b;
|
|
while(
|
|
(c>0) &&
|
|
(fcs_stack_compare(
|
|
state->s.data+c*(MAX_NUM_CARDS_IN_A_STACK+1),
|
|
state->s.data+(c-1)*(MAX_NUM_CARDS_IN_A_STACK+1)
|
|
) < 0)
|
|
)
|
|
{
|
|
memcpy(temp_stack, state->s.data+c*(MAX_NUM_CARDS_IN_A_STACK+1), (MAX_NUM_CARDS_IN_A_STACK+1));
|
|
memcpy(state->s.data+c*(MAX_NUM_CARDS_IN_A_STACK+1), state->s.data+(c-1)*(MAX_NUM_CARDS_IN_A_STACK+1), (MAX_NUM_CARDS_IN_A_STACK+1));
|
|
memcpy(state->s.data+(c-1)*(MAX_NUM_CARDS_IN_A_STACK+1), temp_stack, (MAX_NUM_CARDS_IN_A_STACK+1));
|
|
|
|
temp_loc = state->stack_locs[c];
|
|
state->stack_locs[c] = state->stack_locs[c-1];
|
|
state->stack_locs[c-1] = temp_loc;
|
|
|
|
c--;
|
|
}
|
|
}
|
|
|
|
/* Insertion-sort the freecells */
|
|
|
|
for(b=1;b<freecells_num;b++)
|
|
{
|
|
c = b;
|
|
|
|
while(
|
|
(c>0) &&
|
|
(fcs_card_compare(
|
|
state->s.data+FCS_FREECELLS_OFFSET+c,
|
|
state->s.data+FCS_FREECELLS_OFFSET+c-1
|
|
) < 0)
|
|
)
|
|
{
|
|
temp_freecell = (state->s.data[FCS_FREECELLS_OFFSET+c]);
|
|
state->s.data[FCS_FREECELLS_OFFSET+c] = state->s.data[FCS_FREECELLS_OFFSET+c-1];
|
|
state->s.data[FCS_FREECELLS_OFFSET+c-1] = temp_freecell;
|
|
|
|
temp_loc = state->fc_locs[c];
|
|
state->fc_locs[c] = state->fc_locs[c-1];
|
|
state->fc_locs[c-1] = temp_loc;
|
|
|
|
c--;
|
|
}
|
|
}
|
|
}
|
|
#elif defined(INDIRECT_STACK_STATES)
|
|
void freecell_solver_canonize_state(
|
|
fcs_state_with_locations_t * state,
|
|
int freecells_num,
|
|
int stacks_num)
|
|
{
|
|
int b,c;
|
|
fcs_card_t * temp_stack;
|
|
fcs_card_t temp_freecell;
|
|
char temp_loc;
|
|
|
|
/* Insertion-sort the stacks */
|
|
for(b=1;b<stacks_num;b++)
|
|
{
|
|
c = b;
|
|
while(
|
|
(c>0) &&
|
|
(
|
|
#if MAX_NUM_DECKS > 1
|
|
freecell_solver_stack_compare_for_comparison
|
|
#else
|
|
fcs_stack_compare_for_stack_sort
|
|
#endif
|
|
(
|
|
(state->s.stacks[c]),
|
|
(state->s.stacks[c-1])
|
|
)
|
|
< 0
|
|
)
|
|
)
|
|
{
|
|
temp_stack = state->s.stacks[c];
|
|
state->s.stacks[c] = state->s.stacks[c-1];
|
|
state->s.stacks[c-1] = temp_stack;
|
|
|
|
temp_loc = state->stack_locs[c];
|
|
state->stack_locs[c] = state->stack_locs[c-1];
|
|
state->stack_locs[c-1] = temp_loc;
|
|
|
|
c--;
|
|
}
|
|
}
|
|
|
|
/* Insertion sort the freecells */
|
|
|
|
for(b=1;b<freecells_num;b++)
|
|
{
|
|
c = b;
|
|
while(
|
|
(c>0) &&
|
|
(fcs_card_compare(
|
|
&(state->s.freecells[c]),
|
|
&(state->s.freecells[c-1])
|
|
) < 0)
|
|
)
|
|
{
|
|
temp_freecell = state->s.freecells[c];
|
|
state->s.freecells[c] = state->s.freecells[c-1];
|
|
state->s.freecells[c-1] = temp_freecell;
|
|
|
|
temp_loc = state->fc_locs[c];
|
|
state->fc_locs[c] = state->fc_locs[c-1];
|
|
state->fc_locs[c-1] = temp_loc;
|
|
|
|
c--;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
static void fcs_state_init(
|
|
fcs_state_with_locations_t * state,
|
|
int stacks_num
|
|
#ifdef INDIRECT_STACK_STATES
|
|
,fcs_card_t * indirect_stacks_buffer
|
|
#endif
|
|
)
|
|
{
|
|
int a;
|
|
memset((void*)&(state->s), 0, sizeof(fcs_state_t));
|
|
for(a=0;a<MAX_NUM_STACKS;a++)
|
|
{
|
|
state->stack_locs[a] = a;
|
|
}
|
|
#ifdef INDIRECT_STACK_STATES
|
|
for(a=0;a<stacks_num;a++)
|
|
{
|
|
state->s.stacks[a] = &indirect_stacks_buffer[a << 7];
|
|
memset(state->s.stacks[a], '\0', MAX_NUM_DECKS*52+1);
|
|
}
|
|
for(;a<MAX_NUM_STACKS;a++)
|
|
{
|
|
state->s.stacks[a] = NULL;
|
|
}
|
|
#endif
|
|
for(a=0;a<MAX_NUM_FREECELLS;a++)
|
|
{
|
|
state->fc_locs[a] = a;
|
|
}
|
|
}
|
|
|
|
|
|
#if (FCS_STATE_STORAGE != FCS_STATE_STORAGE_INDIRECT)
|
|
int freecell_solver_state_compare(const void * s1, const void * s2)
|
|
{
|
|
return memcmp(s1,s2,sizeof(fcs_state_t));
|
|
}
|
|
|
|
int freecell_solver_state_compare_equal(const void * s1, const void * s2)
|
|
{
|
|
return (!memcmp(s1,s2,sizeof(fcs_state_t)));
|
|
}
|
|
|
|
|
|
int freecell_solver_state_compare_with_context(
|
|
const void * s1,
|
|
const void * s2,
|
|
fcs_compare_context_t context
|
|
)
|
|
{
|
|
(void)context;
|
|
return memcmp(s1,s2,sizeof(fcs_state_t));
|
|
}
|
|
#else
|
|
int freecell_solver_state_compare_indirect(const void * s1, const void * s2)
|
|
{
|
|
return memcmp(*(fcs_state_with_locations_t * *)s1, *(fcs_state_with_locations_t * *)s2, sizeof(fcs_state_t));
|
|
}
|
|
|
|
int freecell_solver_state_compare_indirect_with_context(const void * s1, const void * s2, void * context)
|
|
{
|
|
return memcmp(*(fcs_state_with_locations_t * *)s1, *(fcs_state_with_locations_t * *)s2, sizeof(fcs_state_t));
|
|
}
|
|
#endif
|
|
|
|
static const char * const freecells_prefixes[] = { "FC:", "Freecells:", "Freecell:", ""};
|
|
static const char * const foundations_prefixes[] = { "Decks:", "Deck:", "Founds:", "Foundations:", "Foundation:", "Found:", ""};
|
|
static const char * const talon_prefixes[] = { "Talon:", "Queue:" , ""};
|
|
static const char * const num_redeals_prefixes[] = { "Num-Redeals:", "Readels-Num:", "Readeals-Number:", ""};
|
|
|
|
#ifdef WIN32
|
|
#define strncasecmp(a,b,c) (strnicmp((a),(b),(c)))
|
|
#endif
|
|
|
|
int freecell_solver_initial_user_state_to_c(
|
|
const char * string,
|
|
fcs_state_with_locations_t * out_state,
|
|
int freecells_num,
|
|
int stacks_num,
|
|
int decks_num
|
|
#ifdef FCS_WITH_TALONS
|
|
,int talon_type
|
|
#endif
|
|
#ifdef INDIRECT_STACK_STATES
|
|
, fcs_card_t * indirect_stacks_buffer
|
|
#endif
|
|
)
|
|
{
|
|
fcs_state_with_locations_t ret_with_locations;
|
|
|
|
int s,c;
|
|
const char * str;
|
|
fcs_card_t card;
|
|
int first_line;
|
|
|
|
int prefix_found;
|
|
const char * const * prefixes;
|
|
int i;
|
|
int decks_index[4];
|
|
|
|
fcs_state_init(
|
|
&ret_with_locations,
|
|
stacks_num
|
|
#ifdef INDIRECT_STACK_STATES
|
|
, indirect_stacks_buffer
|
|
#endif
|
|
);
|
|
str = string;
|
|
|
|
first_line = 1;
|
|
|
|
#define ret (ret_with_locations.s)
|
|
/* Handle the end of string - shouldn't happen */
|
|
#define handle_eos() \
|
|
{ \
|
|
if ((*str) == '\0') \
|
|
{ \
|
|
return FCS_USER_STATE_TO_C__PREMATURE_END_OF_INPUT; \
|
|
} \
|
|
}
|
|
|
|
#ifdef FCS_WITH_TALONS
|
|
if (talon_type == FCS_TALON_KLONDIKE)
|
|
{
|
|
fcs_klondike_talon_num_redeals_left(ret) = -1;
|
|
}
|
|
#endif
|
|
|
|
for(s=0;s<stacks_num;s++)
|
|
{
|
|
/* Move to the next stack */
|
|
if (!first_line)
|
|
{
|
|
while((*str) != '\n')
|
|
{
|
|
handle_eos();
|
|
str++;
|
|
}
|
|
str++;
|
|
}
|
|
first_line = 0;
|
|
|
|
prefixes = freecells_prefixes;
|
|
prefix_found = 0;
|
|
for(i=0;prefixes[i][0] != '\0'; i++)
|
|
{
|
|
if (!strncasecmp(str, prefixes[i], strlen(prefixes[i])))
|
|
{
|
|
prefix_found = 1;
|
|
str += strlen(prefixes[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (prefix_found)
|
|
{
|
|
for(c=0;c<freecells_num;c++)
|
|
{
|
|
fcs_empty_freecell(ret, c);
|
|
}
|
|
for(c=0;c<freecells_num;c++)
|
|
{
|
|
if (c!=0)
|
|
{
|
|
while(
|
|
((*str) != ' ') &&
|
|
((*str) != '\t') &&
|
|
((*str) != '\n') &&
|
|
((*str) != '\r')
|
|
)
|
|
{
|
|
handle_eos();
|
|
str++;
|
|
}
|
|
if ((*str == '\n') || (*str == '\r'))
|
|
{
|
|
break;
|
|
}
|
|
str++;
|
|
}
|
|
|
|
while ((*str == ' ') || (*str == '\t'))
|
|
{
|
|
str++;
|
|
}
|
|
if ((*str == '\r') || (*str == '\n'))
|
|
break;
|
|
|
|
if ((*str == '*') || (*str == '-'))
|
|
{
|
|
card = fcs_empty_card;
|
|
}
|
|
else
|
|
{
|
|
card = fcs_card_user2perl(str);
|
|
}
|
|
|
|
fcs_put_card_in_freecell(ret, c, card);
|
|
}
|
|
|
|
while (*str != '\n')
|
|
{
|
|
handle_eos();
|
|
str++;
|
|
}
|
|
s--;
|
|
continue;
|
|
}
|
|
|
|
prefixes = foundations_prefixes;
|
|
prefix_found = 0;
|
|
for(i=0;prefixes[i][0] != '\0'; i++)
|
|
{
|
|
if (!strncasecmp(str, prefixes[i], strlen(prefixes[i])))
|
|
{
|
|
prefix_found = 1;
|
|
str += strlen(prefixes[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (prefix_found)
|
|
{
|
|
int d;
|
|
|
|
for(d=0;d<decks_num*4;d++)
|
|
{
|
|
fcs_set_foundation(ret, d, 0);
|
|
}
|
|
|
|
for(d=0;d<4;d++)
|
|
{
|
|
decks_index[d] = 0;
|
|
}
|
|
while (1)
|
|
{
|
|
while((*str == ' ') || (*str == '\t'))
|
|
str++;
|
|
if ((*str == '\n') || (*str == '\r'))
|
|
break;
|
|
d = fcs_u2p_suit(str);
|
|
str++;
|
|
while (*str == '-')
|
|
str++;
|
|
c = fcs_u2p_card_number(str);
|
|
while (
|
|
(*str != ' ') &&
|
|
(*str != '\t') &&
|
|
(*str != '\n') &&
|
|
(*str != '\r')
|
|
)
|
|
{
|
|
handle_eos();
|
|
str++;
|
|
}
|
|
|
|
fcs_set_foundation(ret, (decks_index[d]*4+d), c);
|
|
decks_index[d]++;
|
|
if (decks_index[d] >= decks_num)
|
|
{
|
|
decks_index[d] = 0;
|
|
}
|
|
}
|
|
s--;
|
|
continue;
|
|
}
|
|
|
|
#ifdef FCS_WITH_TALONS
|
|
prefixes = talon_prefixes;
|
|
prefix_found = 0;
|
|
for(i=0;prefixes[i][0] != '\0'; i++)
|
|
{
|
|
if (!strncasecmp(str, prefixes[i], strlen(prefixes[i])))
|
|
{
|
|
prefix_found = 1;
|
|
str += strlen(prefixes[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (prefix_found)
|
|
{
|
|
/* Input the Talon */
|
|
int talon_size;
|
|
|
|
talon_size = MAX_NUM_DECKS*52+16;
|
|
ret.talon = malloc(sizeof(fcs_card_t)*talon_size);
|
|
fcs_talon_pos(ret) = 0;
|
|
|
|
for(c=0 ; c < talon_size ; c++)
|
|
{
|
|
/* Move to the next card */
|
|
if (c!=0)
|
|
{
|
|
while(
|
|
((*str) != ' ') &&
|
|
((*str) != '\t') &&
|
|
((*str) != '\n') &&
|
|
((*str) != '\r')
|
|
)
|
|
{
|
|
handle_eos();
|
|
str++;
|
|
}
|
|
if ((*str == '\n') || (*str == '\r'))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
while ((*str == ' ') || (*str == '\t'))
|
|
{
|
|
str++;
|
|
}
|
|
|
|
if ((*str == '\n') || (*str == '\r'))
|
|
{
|
|
break;
|
|
}
|
|
|
|
card = fcs_card_user2perl(str);
|
|
|
|
fcs_put_card_in_talon(ret, c+(talon_type==FCS_TALON_KLONDIKE), card);
|
|
}
|
|
fcs_talon_len(ret) = c;
|
|
|
|
if (talon_type == FCS_TALON_KLONDIKE)
|
|
{
|
|
int talon_len;
|
|
|
|
talon_len = fcs_talon_len(ret);
|
|
fcs_klondike_talon_len(ret) = talon_len;
|
|
fcs_klondike_talon_stack_pos(ret) = -1;
|
|
fcs_klondike_talon_queue_pos(ret) = 0;
|
|
}
|
|
|
|
s--;
|
|
continue;
|
|
}
|
|
|
|
prefixes = num_redeals_prefixes;
|
|
prefix_found = 0;
|
|
for(i=0;prefixes[i][0] != '\0'; i++)
|
|
{
|
|
if (!strncasecmp(str, prefixes[i], strlen(prefixes[i])))
|
|
{
|
|
prefix_found = 1;
|
|
str += strlen(prefixes[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (prefix_found)
|
|
{
|
|
while ((*str < '0') && (*str > '9') && (*str != '\n'))
|
|
{
|
|
handle_eos();
|
|
str++;
|
|
}
|
|
if (*str != '\n')
|
|
{
|
|
int num_redeals;
|
|
|
|
num_redeals = atoi(str);
|
|
if (talon_type == FCS_TALON_KLONDIKE)
|
|
{
|
|
fcs_klondike_talon_num_redeals_left(ret) =
|
|
(num_redeals < 0) ?
|
|
(-1) :
|
|
((num_redeals > 127) ? 127 : num_redeals)
|
|
;
|
|
}
|
|
}
|
|
s--;
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
for(c=0 ; c < MAX_NUM_CARDS_IN_A_STACK ; c++)
|
|
{
|
|
/* Move to the next card */
|
|
if (c!=0)
|
|
{
|
|
while(
|
|
((*str) != ' ') &&
|
|
((*str) != '\t') &&
|
|
((*str) != '\n') &&
|
|
((*str) != '\r')
|
|
)
|
|
{
|
|
handle_eos();
|
|
str++;
|
|
}
|
|
if ((*str == '\n') || (*str == '\r'))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
while ((*str == ' ') || (*str == '\t'))
|
|
{
|
|
str++;
|
|
}
|
|
if ((*str == '\n') || (*str == '\r'))
|
|
{
|
|
break;
|
|
}
|
|
card = fcs_card_user2perl(str);
|
|
|
|
fcs_push_card_into_stack(ret, s, card);
|
|
}
|
|
}
|
|
|
|
*out_state = ret_with_locations;
|
|
return FCS_USER_STATE_TO_C__SUCCESS;
|
|
}
|
|
|
|
#undef ret
|
|
#undef handle_eos
|
|
|
|
int freecell_solver_check_state_validity(
|
|
fcs_state_with_locations_t * state_with_locations,
|
|
int freecells_num,
|
|
int stacks_num,
|
|
int decks_num,
|
|
#ifdef FCS_WITH_TALONS
|
|
int talon_type,
|
|
#endif
|
|
fcs_card_t * misplaced_card)
|
|
{
|
|
int cards[4][14];
|
|
int c, s, d, f;
|
|
|
|
fcs_state_t * state;
|
|
|
|
state = (&(state_with_locations->s));
|
|
|
|
/* Initialize all cards to 0 */
|
|
for(d=0;d<4;d++)
|
|
{
|
|
for(c=1;c<=13;c++)
|
|
{
|
|
cards[d][c] = 0;
|
|
}
|
|
}
|
|
|
|
/* Mark the cards in the decks */
|
|
for(d=0;d<decks_num*4;d++)
|
|
{
|
|
for(c=1;c<=fcs_foundation_value(*state, d);c++)
|
|
{
|
|
cards[d%4][c]++;
|
|
}
|
|
}
|
|
|
|
/* Mark the cards in the freecells */
|
|
for(f=0;f<freecells_num;f++)
|
|
{
|
|
if (fcs_freecell_card_num(*state, f) != 0)
|
|
{
|
|
cards
|
|
[fcs_freecell_card_suit(*state, f)]
|
|
[fcs_freecell_card_num(*state, f)] ++;
|
|
}
|
|
}
|
|
|
|
/* Mark the cards in the stacks */
|
|
for(s=0;s<stacks_num;s++)
|
|
{
|
|
for(c=0;c<fcs_stack_len(*state,s);c++)
|
|
{
|
|
if (fcs_stack_card_num(*state, s, c) == 0)
|
|
{
|
|
*misplaced_card = fcs_empty_card;
|
|
return 3;
|
|
}
|
|
cards
|
|
[fcs_stack_card_suit(*state, s, c)]
|
|
[fcs_stack_card_num(*state, s, c)] ++;
|
|
|
|
}
|
|
}
|
|
|
|
#ifdef FCS_WITH_TALONS
|
|
/* Mark the cards in the (gypsy) talon */
|
|
if ((talon_type == FCS_TALON_GYPSY) || (talon_type == FCS_TALON_KLONDIKE))
|
|
{
|
|
for(c = ((talon_type == FCS_TALON_GYPSY)?fcs_talon_pos(*state):1) ;
|
|
c < ((talon_type==FCS_TALON_GYPSY) ? fcs_talon_len(*state) : (fcs_klondike_talon_len(*state)+1)) ;
|
|
c++)
|
|
{
|
|
if (fcs_get_talon_card(*state,c) != fcs_empty_card)
|
|
{
|
|
cards
|
|
[fcs_card_suit(fcs_get_talon_card(*state, c))]
|
|
[fcs_card_card_num(fcs_get_talon_card(*state, c))] ++;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Now check if there are extra or missing cards */
|
|
|
|
for(d=0;d<4;d++)
|
|
{
|
|
for(c=1;c<=13;c++)
|
|
{
|
|
if (cards[d][c] != decks_num)
|
|
{
|
|
*misplaced_card = fcs_empty_card;
|
|
fcs_card_set_suit(*misplaced_card, d);
|
|
fcs_card_set_num(*misplaced_card, c);
|
|
return (cards[d][c] < decks_num) ? 1 : 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#undef state
|
|
|
|
|
|
char * freecell_solver_state_as_string(
|
|
fcs_state_with_locations_t * state_with_locations,
|
|
int freecells_num,
|
|
int stacks_num,
|
|
int decks_num,
|
|
int parseable_output,
|
|
int canonized_order_output,
|
|
int display_10_as_t
|
|
)
|
|
{
|
|
fcs_state_t * state;
|
|
char freecell[10], decks[MAX_NUM_DECKS*4][10], stack_card_[10];
|
|
int a, card_num_is_null, b;
|
|
int max_num_cards, s, card_num, len;
|
|
|
|
char str2[128], str3[128], * str2_ptr, * str3_ptr;
|
|
|
|
freecell_solver_append_string_t * app_str;
|
|
|
|
int stack_locs[MAX_NUM_STACKS];
|
|
int freecell_locs[MAX_NUM_FREECELLS];
|
|
|
|
state = (&(state_with_locations->s));
|
|
|
|
if (canonized_order_output)
|
|
{
|
|
for(a=0;a<stacks_num;a++)
|
|
{
|
|
stack_locs[a] = a;
|
|
}
|
|
for(a=0;a<freecells_num;a++)
|
|
{
|
|
freecell_locs[a] = a;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(a=0;a<stacks_num;a++)
|
|
{
|
|
stack_locs[(int)(state_with_locations->stack_locs[a])] = a;
|
|
}
|
|
for(a=0;a<freecells_num;a++)
|
|
{
|
|
freecell_locs[(int)(state_with_locations->fc_locs[a])] = a;
|
|
}
|
|
}
|
|
|
|
for(a=0;a<decks_num*4;a++)
|
|
{
|
|
fcs_p2u_card_number(
|
|
fcs_foundation_value(*state, a),
|
|
decks[a],
|
|
&card_num_is_null,
|
|
display_10_as_t,
|
|
0
|
|
);
|
|
if (decks[a][0] == ' ')
|
|
decks[a][0] = '0';
|
|
}
|
|
|
|
app_str = freecell_solver_append_string_alloc(512);
|
|
|
|
if(!parseable_output)
|
|
{
|
|
for(a=0;a<((freecells_num/4)+((freecells_num%4==0)?0:1));a++)
|
|
{
|
|
str2_ptr = str2;
|
|
str3_ptr = str3;
|
|
for(b=0;b<min(freecells_num-a*4, 4);b++)
|
|
{
|
|
str2_ptr += sprintf(str2_ptr, "%3s ",
|
|
fcs_card_perl2user(
|
|
fcs_freecell_card(
|
|
*state,
|
|
freecell_locs[a*4+b]
|
|
),
|
|
freecell,
|
|
display_10_as_t
|
|
)
|
|
);
|
|
str3_ptr += sprintf(str3_ptr, "--- ");
|
|
}
|
|
if (a < decks_num)
|
|
{
|
|
freecell_solver_append_string_sprintf(
|
|
app_str,
|
|
"%-16s H-%1s C-%1s D-%1s S-%1s\n",
|
|
str2,
|
|
decks[a*4],
|
|
decks[a*4+1],
|
|
decks[a*4+2],
|
|
decks[a*4+3]
|
|
);
|
|
}
|
|
else
|
|
{
|
|
freecell_solver_append_string_sprintf(
|
|
app_str,
|
|
"%s\n", str2
|
|
);
|
|
}
|
|
freecell_solver_append_string_sprintf(
|
|
app_str,
|
|
"%s\n", str3
|
|
);
|
|
}
|
|
for(;a<decks_num;a++)
|
|
{
|
|
freecell_solver_append_string_sprintf(
|
|
app_str,
|
|
"%-16s H-%1s C-%1s D-%1s S-%1s\n",
|
|
"",
|
|
decks[a*4],
|
|
decks[a*4+1],
|
|
decks[a*4+2],
|
|
decks[a*4+3]
|
|
);
|
|
}
|
|
freecell_solver_append_string_sprintf(
|
|
app_str,
|
|
"%s",
|
|
"\n\n"
|
|
);
|
|
|
|
for(s=0;s<stacks_num;s++)
|
|
{
|
|
freecell_solver_append_string_sprintf(app_str, "%s", " -- ");
|
|
}
|
|
freecell_solver_append_string_sprintf(
|
|
app_str,
|
|
"%s",
|
|
"\n"
|
|
);
|
|
|
|
max_num_cards = 0;
|
|
for(s=0;s<stacks_num;s++)
|
|
{
|
|
if (fcs_stack_len(*state, stack_locs[s]) > max_num_cards)
|
|
{
|
|
max_num_cards = fcs_stack_len(*state, stack_locs[s]);
|
|
}
|
|
}
|
|
|
|
for(card_num=0;card_num<max_num_cards;card_num++)
|
|
{
|
|
for(s = 0; s<stacks_num; s++)
|
|
{
|
|
if (card_num >= fcs_stack_len(*state, stack_locs[s]))
|
|
{
|
|
freecell_solver_append_string_sprintf(
|
|
app_str,
|
|
" "
|
|
);
|
|
}
|
|
else
|
|
{
|
|
freecell_solver_append_string_sprintf(
|
|
app_str,
|
|
"%3s ",
|
|
fcs_card_perl2user(
|
|
fcs_stack_card(
|
|
*state,
|
|
stack_locs[s],
|
|
card_num),
|
|
stack_card_,
|
|
display_10_as_t
|
|
)
|
|
);
|
|
}
|
|
}
|
|
freecell_solver_append_string_sprintf(app_str, "%s", "\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
freecell_solver_append_string_sprintf(app_str, "%s", "Foundations: ");
|
|
for(a=0;a<decks_num;a++)
|
|
{
|
|
freecell_solver_append_string_sprintf(
|
|
app_str,
|
|
"H-%s C-%s D-%s S-%s ",
|
|
decks[a*4],
|
|
decks[a*4+1],
|
|
decks[a*4+2],
|
|
decks[a*4+3]
|
|
);
|
|
}
|
|
|
|
freecell_solver_append_string_sprintf(app_str, "%s", "\nFreecells: ");
|
|
|
|
for(a=0;a<freecells_num;a++)
|
|
{
|
|
freecell_solver_append_string_sprintf(
|
|
app_str,
|
|
"%3s",
|
|
fcs_card_perl2user(
|
|
fcs_freecell_card(
|
|
*state,
|
|
freecell_locs[a]
|
|
),
|
|
freecell,
|
|
display_10_as_t
|
|
)
|
|
);
|
|
if (a < freecells_num-1)
|
|
{
|
|
freecell_solver_append_string_sprintf(app_str, "%s", " ");
|
|
}
|
|
}
|
|
freecell_solver_append_string_sprintf(app_str, "%s", "\n");
|
|
|
|
for(s=0;s<stacks_num;s++)
|
|
{
|
|
freecell_solver_append_string_sprintf(app_str, "%s", ": ");
|
|
|
|
len = fcs_stack_len(*state, stack_locs[s]);
|
|
for(card_num=0;card_num<len;card_num++)
|
|
{
|
|
fcs_card_perl2user(
|
|
fcs_stack_card(
|
|
*state,
|
|
stack_locs[s],
|
|
card_num
|
|
),
|
|
stack_card_,
|
|
display_10_as_t
|
|
);
|
|
freecell_solver_append_string_sprintf(app_str, "%s", stack_card_);
|
|
if (card_num < len-1)
|
|
{
|
|
freecell_solver_append_string_sprintf(app_str, "%s", " ");
|
|
}
|
|
}
|
|
freecell_solver_append_string_sprintf(app_str, "%s", "\n");
|
|
}
|
|
}
|
|
|
|
return freecell_solver_append_string_finalize(app_str);
|
|
}
|