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.
638 lines
13 KiB
638 lines
13 KiB
/*
|
|
* preset.c - game presets management for Freecell Solver
|
|
*
|
|
* Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
|
|
*
|
|
* This file is in the public domain (it's uncopyrighted).
|
|
*
|
|
*/
|
|
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "fcs.h"
|
|
#include "preset.h"
|
|
|
|
#ifdef DMALLOC
|
|
#include "dmalloc.h"
|
|
#endif
|
|
|
|
enum fcs_presets_ids
|
|
{
|
|
FCS_PRESET_BAKERS_DOZEN,
|
|
FCS_PRESET_BAKERS_GAME,
|
|
FCS_PRESET_CRUEL,
|
|
FCS_PRESET_DER_KATZENSCHWANZ,
|
|
FCS_PRESET_DIE_SCHLANGE,
|
|
FCS_PRESET_EIGHT_OFF,
|
|
FCS_PRESET_FAN,
|
|
FCS_PRESET_FORECELL,
|
|
FCS_PRESET_FREECELL,
|
|
FCS_PRESET_GOOD_MEASURE,
|
|
FCS_PRESET_KINGS_ONLY_BAKERS_GAME,
|
|
FCS_PRESET_RELAXED_FREECELL,
|
|
FCS_PRESET_RELAXED_SEAHAVEN_TOWERS,
|
|
FCS_PRESET_SEAHAVEN_TOWERS,
|
|
FCS_PRESET_SIMPLE_SIMON,
|
|
FCS_PRESET_YUKON,
|
|
FCS_PRESET_BELEAGUERED_CASTLE
|
|
};
|
|
|
|
static const fcs_preset_t fcs_presets[16] =
|
|
{
|
|
{
|
|
FCS_PRESET_BAKERS_DOZEN,
|
|
0,
|
|
13,
|
|
1,
|
|
|
|
FCS_SEQ_BUILT_BY_RANK,
|
|
0,
|
|
FCS_ES_FILLED_BY_NONE,
|
|
|
|
"0123456789",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_BAKERS_GAME,
|
|
4,
|
|
8,
|
|
1,
|
|
|
|
FCS_SEQ_BUILT_BY_SUIT,
|
|
0,
|
|
FCS_ES_FILLED_BY_ANY_CARD,
|
|
|
|
"[01][23456789]",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_BELEAGUERED_CASTLE,
|
|
0,
|
|
8,
|
|
1,
|
|
|
|
FCS_SEQ_BUILT_BY_RANK,
|
|
0,
|
|
FCS_ES_FILLED_BY_ANY_CARD,
|
|
|
|
"[01][23456789]",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_CRUEL,
|
|
0,
|
|
12,
|
|
1,
|
|
|
|
FCS_SEQ_BUILT_BY_SUIT,
|
|
0,
|
|
FCS_ES_FILLED_BY_NONE,
|
|
|
|
"0123456789",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_DER_KATZENSCHWANZ,
|
|
8,
|
|
9,
|
|
2,
|
|
|
|
FCS_SEQ_BUILT_BY_ALTERNATE_COLOR,
|
|
1,
|
|
FCS_ES_FILLED_BY_NONE,
|
|
|
|
"[01][23456789]",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_DIE_SCHLANGE,
|
|
8,
|
|
9,
|
|
2,
|
|
|
|
FCS_SEQ_BUILT_BY_ALTERNATE_COLOR,
|
|
0,
|
|
FCS_ES_FILLED_BY_NONE,
|
|
|
|
"[01][23456789]",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_EIGHT_OFF,
|
|
8,
|
|
8,
|
|
1,
|
|
|
|
FCS_SEQ_BUILT_BY_SUIT,
|
|
0,
|
|
FCS_ES_FILLED_BY_KINGS_ONLY,
|
|
|
|
"[01][23456789]",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_FAN,
|
|
0,
|
|
18,
|
|
1,
|
|
|
|
FCS_SEQ_BUILT_BY_SUIT,
|
|
0,
|
|
FCS_ES_FILLED_BY_KINGS_ONLY,
|
|
|
|
"[01][23456789]",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_FORECELL,
|
|
4,
|
|
8,
|
|
1,
|
|
|
|
FCS_SEQ_BUILT_BY_ALTERNATE_COLOR,
|
|
0,
|
|
FCS_ES_FILLED_BY_KINGS_ONLY,
|
|
|
|
"[01][23456789]",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_FREECELL,
|
|
4,
|
|
8,
|
|
1,
|
|
|
|
FCS_SEQ_BUILT_BY_ALTERNATE_COLOR,
|
|
0,
|
|
FCS_ES_FILLED_BY_ANY_CARD,
|
|
|
|
"[01][23456789]",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_GOOD_MEASURE,
|
|
0,
|
|
10,
|
|
1,
|
|
|
|
FCS_SEQ_BUILT_BY_RANK,
|
|
0,
|
|
FCS_ES_FILLED_BY_NONE,
|
|
|
|
"0123456789",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_KINGS_ONLY_BAKERS_GAME,
|
|
4,
|
|
8,
|
|
1,
|
|
|
|
FCS_SEQ_BUILT_BY_SUIT,
|
|
0,
|
|
FCS_ES_FILLED_BY_KINGS_ONLY,
|
|
|
|
"[01][23456789]",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_RELAXED_FREECELL,
|
|
4,
|
|
8,
|
|
1,
|
|
|
|
FCS_SEQ_BUILT_BY_ALTERNATE_COLOR,
|
|
1,
|
|
FCS_ES_FILLED_BY_ANY_CARD,
|
|
|
|
"[01][23456789]",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_RELAXED_SEAHAVEN_TOWERS,
|
|
4,
|
|
10,
|
|
1,
|
|
|
|
FCS_SEQ_BUILT_BY_SUIT,
|
|
1,
|
|
FCS_ES_FILLED_BY_KINGS_ONLY,
|
|
|
|
"[01][23456789]",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_SEAHAVEN_TOWERS,
|
|
4,
|
|
10,
|
|
1,
|
|
|
|
FCS_SEQ_BUILT_BY_SUIT,
|
|
0,
|
|
FCS_ES_FILLED_BY_KINGS_ONLY,
|
|
|
|
"[01][23456789]",
|
|
"0123456789",
|
|
},
|
|
{
|
|
FCS_PRESET_SIMPLE_SIMON,
|
|
0,
|
|
10,
|
|
1,
|
|
|
|
FCS_SEQ_BUILT_BY_SUIT,
|
|
0,
|
|
FCS_ES_FILLED_BY_ANY_CARD,
|
|
|
|
"abcdefgh",
|
|
"abcdefgh",
|
|
},
|
|
};
|
|
|
|
struct fcs_preset_name_struct
|
|
{
|
|
const char name[32];
|
|
int preset_id;
|
|
};
|
|
|
|
typedef struct fcs_preset_name_struct fcs_preset_name_t;
|
|
|
|
static const fcs_preset_name_t fcs_preset_names[23] =
|
|
{
|
|
{
|
|
"bakers_dozen",
|
|
FCS_PRESET_BAKERS_DOZEN,
|
|
},
|
|
{
|
|
"bakers_game",
|
|
FCS_PRESET_BAKERS_GAME,
|
|
},
|
|
{
|
|
"beleaguered_castle",
|
|
FCS_PRESET_BELEAGUERED_CASTLE,
|
|
},
|
|
{
|
|
"citadel",
|
|
FCS_PRESET_BELEAGUERED_CASTLE,
|
|
},
|
|
{
|
|
"cruel",
|
|
FCS_PRESET_CRUEL,
|
|
},
|
|
{
|
|
"der_katzenschwanz",
|
|
FCS_PRESET_DER_KATZENSCHWANZ,
|
|
},
|
|
{
|
|
"der_katz",
|
|
FCS_PRESET_DER_KATZENSCHWANZ,
|
|
},
|
|
{
|
|
"die_schlange",
|
|
FCS_PRESET_DIE_SCHLANGE,
|
|
},
|
|
{
|
|
"eight_off",
|
|
FCS_PRESET_EIGHT_OFF,
|
|
},
|
|
{
|
|
"fan",
|
|
FCS_PRESET_FAN,
|
|
},
|
|
{
|
|
"forecell",
|
|
FCS_PRESET_FORECELL,
|
|
},
|
|
{
|
|
"freecell",
|
|
FCS_PRESET_FREECELL,
|
|
},
|
|
{
|
|
"good_measure",
|
|
FCS_PRESET_GOOD_MEASURE,
|
|
},
|
|
{
|
|
"ko_bakers_game",
|
|
FCS_PRESET_KINGS_ONLY_BAKERS_GAME,
|
|
},
|
|
{
|
|
"kings_only_bakers_game",
|
|
FCS_PRESET_KINGS_ONLY_BAKERS_GAME,
|
|
},
|
|
{
|
|
"relaxed_freecell",
|
|
FCS_PRESET_RELAXED_FREECELL,
|
|
},
|
|
{
|
|
"relaxed_seahaven_towers",
|
|
FCS_PRESET_RELAXED_SEAHAVEN_TOWERS,
|
|
},
|
|
{
|
|
"relaxed_seahaven",
|
|
FCS_PRESET_RELAXED_SEAHAVEN_TOWERS,
|
|
},
|
|
{
|
|
"seahaven_towers",
|
|
FCS_PRESET_SEAHAVEN_TOWERS,
|
|
},
|
|
{
|
|
"seahaven",
|
|
FCS_PRESET_SEAHAVEN_TOWERS,
|
|
},
|
|
{
|
|
"simple_simon",
|
|
FCS_PRESET_SIMPLE_SIMON,
|
|
},
|
|
{
|
|
"streets_and_alleys",
|
|
FCS_PRESET_BELEAGUERED_CASTLE,
|
|
},
|
|
{
|
|
"yukon",
|
|
FCS_PRESET_YUKON,
|
|
},
|
|
};
|
|
|
|
static int fcs_get_preset_id_by_name(
|
|
const char * name
|
|
)
|
|
{
|
|
int a;
|
|
int ret = -1;
|
|
int num_elems;
|
|
|
|
num_elems = ( (int) (sizeof(fcs_preset_names)/sizeof(fcs_preset_names[0])));
|
|
for(a=0;a<num_elems;a++)
|
|
{
|
|
if (!strcmp(name, fcs_preset_names[a].name))
|
|
{
|
|
ret = fcs_preset_names[a].preset_id;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int freecell_solver_char_to_test_num(char c)
|
|
{
|
|
if ((c >= '0') && (c <= '9'))
|
|
{
|
|
return c-'0';
|
|
}
|
|
else if ((c >= 'a') && (c <= 'h'))
|
|
{
|
|
return c-'a'+10;
|
|
}
|
|
else if ((c >= 'A') && (c <= 'Z'))
|
|
{
|
|
return c-'A'+18;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#ifndef min
|
|
#define min(a,b) (((a)<(b))?(a):(b))
|
|
#endif
|
|
|
|
struct internal_tests_order_struct
|
|
{
|
|
int tests_order_num;
|
|
int tests_order[FCS_TESTS_NUM];
|
|
};
|
|
|
|
typedef struct internal_tests_order_struct internal_tests_order_t;
|
|
|
|
int freecell_solver_apply_tests_order(
|
|
fcs_tests_order_t * tests_order,
|
|
const char * string,
|
|
char * * error_string
|
|
)
|
|
|
|
{
|
|
int a;
|
|
int len;
|
|
int test_index;
|
|
int is_group, is_start_group;
|
|
if (tests_order->tests)
|
|
{
|
|
free(tests_order->tests);
|
|
tests_order->max_num = 10;
|
|
tests_order->num = 0;
|
|
tests_order->tests = malloc(sizeof(tests_order->tests[0])*tests_order->max_num );
|
|
}
|
|
|
|
#if 0
|
|
instance->tests_order_num = min(strlen(string), FCS_TESTS_NUM);
|
|
#endif
|
|
len = strlen(string);
|
|
test_index = 0;
|
|
is_group = 0;
|
|
is_start_group = 0;
|
|
for(a=0;(a<len) ;a++)
|
|
{
|
|
if ((string[a] == '(') || (string[a] == '['))
|
|
{
|
|
if (is_group)
|
|
{
|
|
*error_string = strdup("There's a nested random group.");
|
|
return 1;
|
|
}
|
|
is_group = 1;
|
|
is_start_group = 1;
|
|
continue;
|
|
}
|
|
|
|
if ((string[a] == ')') || (string[a] == ']'))
|
|
{
|
|
if (is_start_group)
|
|
{
|
|
*error_string = strdup("There's an empty group.");
|
|
return 2;
|
|
}
|
|
if (! is_group)
|
|
{
|
|
*error_string = strdup("There's a renegade right parenthesis or bracket.");
|
|
return 3;
|
|
}
|
|
is_group = 0;
|
|
is_start_group = 0;
|
|
continue;
|
|
}
|
|
if (test_index == tests_order->max_num)
|
|
{
|
|
tests_order->max_num += 10;
|
|
tests_order->tests = realloc(tests_order->tests, sizeof(tests_order->tests[0]) * tests_order->max_num);
|
|
}
|
|
tests_order->tests[test_index] = (freecell_solver_char_to_test_num(string[a])%FCS_TESTS_NUM) | (is_group ? FCS_TEST_ORDER_FLAG_RANDOM : 0) | (is_start_group ? FCS_TEST_ORDER_FLAG_START_RANDOM_GROUP : 0);
|
|
|
|
test_index++;
|
|
is_start_group = 0;
|
|
}
|
|
if (a != len)
|
|
{
|
|
*error_string = strdup("The Input string is too long.");
|
|
return 4;
|
|
}
|
|
|
|
tests_order->num = test_index;
|
|
*error_string = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int freecell_solver_apply_preset_by_ptr(
|
|
freecell_solver_instance_t * instance,
|
|
const fcs_preset_t * preset_ptr
|
|
)
|
|
{
|
|
char * no_use;
|
|
|
|
#define preset (*preset_ptr)
|
|
if (preset.freecells_num > MAX_NUM_FREECELLS)
|
|
{
|
|
return FCS_PRESET_CODE_FREECELLS_EXCEED_MAX;
|
|
}
|
|
if (preset.stacks_num > MAX_NUM_STACKS)
|
|
{
|
|
return FCS_PRESET_CODE_STACKS_EXCEED_MAX;
|
|
}
|
|
if (preset.decks_num > MAX_NUM_DECKS)
|
|
{
|
|
return FCS_PRESET_CODE_DECKS_EXCEED_MAX;
|
|
}
|
|
instance->freecells_num = preset.freecells_num;
|
|
instance->stacks_num = preset.stacks_num;
|
|
instance->decks_num = preset.decks_num;
|
|
|
|
instance->sequences_are_built_by = preset.sequences_are_built_by;
|
|
instance->unlimited_sequence_move = preset.unlimited_sequence_move;
|
|
instance->empty_stacks_fill = preset.empty_stacks_fill;
|
|
|
|
/*
|
|
* This code makes sure that all the tests in all the existing
|
|
* soft threads are acceptable by the new preset.
|
|
* */
|
|
|
|
{
|
|
int ht_idx, st_idx;
|
|
for(ht_idx = 0; ht_idx < instance->num_hard_threads ; ht_idx++)
|
|
{
|
|
for(st_idx = 0; st_idx < instance->hard_threads[ht_idx]->num_soft_threads; st_idx++)
|
|
{
|
|
freecell_solver_soft_thread_t * soft_thread = instance->hard_threads[ht_idx]->soft_threads[st_idx];
|
|
|
|
int num_valid_tests;
|
|
const char * s;
|
|
|
|
/* Check every test */
|
|
|
|
for(num_valid_tests=0;num_valid_tests < soft_thread->tests_order.num; num_valid_tests++)
|
|
{
|
|
for(s = preset.allowed_tests;*s != '\0';s++)
|
|
{
|
|
/* Check if this test corresponds to this character */
|
|
if ((soft_thread->tests_order.tests[num_valid_tests] & FCS_TEST_ORDER_NO_FLAGS_MASK) == ((freecell_solver_char_to_test_num(*s)%FCS_TESTS_NUM)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
/* If the end of the string was reached, it means
|
|
* this test is unacceptable by this preset. */
|
|
if (*s == '\0')
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (num_valid_tests < soft_thread->tests_order.num)
|
|
{
|
|
freecell_solver_apply_tests_order(
|
|
&(soft_thread->tests_order),
|
|
preset.tests_order,
|
|
&no_use);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Assign the master tests order */
|
|
|
|
{
|
|
freecell_solver_apply_tests_order(
|
|
&(instance->instance_tests_order),
|
|
preset.tests_order,
|
|
&no_use);
|
|
}
|
|
#undef preset
|
|
return FCS_PRESET_CODE_OK;
|
|
}
|
|
|
|
static int fcs_get_preset_by_id(
|
|
int preset_id,
|
|
const fcs_preset_t * * preset_ptr
|
|
)
|
|
{
|
|
int preset_index;
|
|
int num_elems;
|
|
|
|
num_elems = ( (int) (sizeof(fcs_presets)/sizeof(fcs_presets[0])));
|
|
|
|
for(preset_index=0 ; preset_index < num_elems ; preset_index++)
|
|
{
|
|
if (fcs_presets[preset_index].preset_id == preset_id)
|
|
{
|
|
*preset_ptr = &(fcs_presets[preset_index]);
|
|
return FCS_PRESET_CODE_OK;
|
|
}
|
|
}
|
|
|
|
return FCS_PRESET_CODE_NOT_FOUND;
|
|
}
|
|
|
|
int freecell_solver_get_preset_by_name(
|
|
const char * name,
|
|
const fcs_preset_t * * preset_ptr
|
|
)
|
|
{
|
|
int preset_id;
|
|
|
|
preset_id = fcs_get_preset_id_by_name(name);
|
|
if (preset_id >= 0)
|
|
{
|
|
return fcs_get_preset_by_id(
|
|
preset_id,
|
|
preset_ptr
|
|
);
|
|
}
|
|
else
|
|
{
|
|
return FCS_PRESET_CODE_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
int freecell_solver_apply_preset_by_name(
|
|
freecell_solver_instance_t * instance,
|
|
const char * name
|
|
)
|
|
{
|
|
int ret;
|
|
const fcs_preset_t * preset_ptr;
|
|
|
|
ret = freecell_solver_get_preset_by_name(
|
|
name,
|
|
&preset_ptr
|
|
);
|
|
|
|
if (ret != FCS_PRESET_CODE_OK)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
return freecell_solver_apply_preset_by_ptr(instance, preset_ptr);
|
|
}
|