#include <string.h> #include <stdio.h> #include <stdlib.h> #include <signal.h> #include "fcs_cl.h" struct freecell_solver_display_information_context_struct { int debug_iter_state_output; int freecells_num; int stacks_num; int decks_num; int parseable_output; int canonized_order_output; int display_10_as_t; int display_parent_iter_num; int debug_iter_output_on; int display_moves; int display_states; int standard_notation; }; typedef struct freecell_solver_display_information_context_struct freecell_solver_display_information_context_t; static void init_debug_context( freecell_solver_display_information_context_t * dc ) { dc->parseable_output = 0; dc->canonized_order_output = 0; dc->display_10_as_t = 0; dc->display_parent_iter_num = 0; dc->display_moves = 0; dc->display_states = 1; dc->standard_notation = 0; } static void my_iter_handler( void * user_instance, int iter_num, int depth, void * ptr_state, int parent_iter_num, void * lp_context ) { freecell_solver_display_information_context_t * context; context = (freecell_solver_display_information_context_t*)lp_context; fprintf(stdout, "Iteration: %i\n", iter_num); fprintf(stdout, "Depth: %i\n", depth); fprintf(stdout, "Stored-States: %i\n", freecell_solver_user_get_num_states_in_collection(user_instance) ); if (context->display_parent_iter_num) { fprintf(stdout, "Parent Iteration: %i\n", parent_iter_num); } fprintf(stdout, "\n"); if (context->debug_iter_state_output) { char * state_string = freecell_solver_user_iter_state_as_string( user_instance, ptr_state, context->parseable_output, context->canonized_order_output, context->display_10_as_t ); printf("%s\n---------------\n\n\n", state_string); free((void*)state_string); } #ifdef MYDEBUG { fcs_card_t card; int ret; char card_str[10]; ret = fcs_check_state_validity( ptr_state_with_locations, context->freecells_num, context->stacks_num, context->decks_num, &card ); if (ret != 0) { fcs_card_perl2user(card, card_str, context->display_10_as_t); if (ret == 3) { fprintf(stdout, "%s\n", "There's an empty slot in one of the stacks." ); } else { fprintf(stdout, "%s%s.\n", ((ret == 2)? "There's an extra card: " : "There's a missing card: "), card_str ); } exit(-1); } } #endif } struct help_screen_struct { char * key; char * screen; }; typedef struct help_screen_struct help_screen_t; help_screen_t help_screens[] = { { "configs", "These configurations are usually faster than the unmodified run:\n" "\n" " fc-solve -l cool-jives\n" " fc-solve -l john-galt-line\n" "\n" "Or if you want an accurate verdict:\n" "\n" " fc-solve -l fools-gold\n" "\n" "If you want to try constructing your own configurations refer to the\n" "USAGE file in the Freecell Solver distribution\n" }, { "options", "fc-solve [options] board_file\n" "\n" "If board_file is - or unspecified reads standard input\n" "\n" "Available Options:\n" "-h --help\n" " display the default help screen\n" "--help-summary\n" " display the summary help screen\n" "-i --iter-output\n" " display the iteration number and depth in every state that is checked\n" "-s --state-output\n" " also output the state in every state that is checked\n" "-p --parseable-output\n" " Output the states in a format that is friendly to perl, grep and\n" " friends.\n" "-c --canonized-order-output\n" " Output the stacks and freecells according to their canonic order.\n" " (That means that stacks and freecells won't retain their place.)\n" "-t --display-10-as-t\n" " Display the card 10 as a capital T instead of \"10\".\n" "-m --display-moves\n" " Display the moves instead of the intermediate states.\n" "-sam --display-states-and-moves \n" " Display both intermediate states and moves.\n" "-sn --standard-notation\n" " Display the moves in standard (non-verbose) notation.\n" " (Applicable only if -m was specified)\n" "-snx --standard-notation-extended\n" " Display the moves in extended standard notation while specifying the\n" " number of cards moved if applicable\n" "-pi --display-parent-iter \n" " Display the index of the parent iteration of each state in the\n" " run-time dump.\n" "\n" "--freecells-num [Freecells\' Number]\n" " The number of freecells present in the board.\n" "--stacks-num [Stacks\' Number]\n" " The number of stacks present in the board.\n" "--decks-num [Decks\' Number]\n" " The number of decks in the board.\n" "\n" "--sequences-are-built-by {suit|alternate_color|rank}\n" " Specifies the type of sequence\n" "--sequence-move {limited|unlimited}\n" " Specifies whether the sequence move is limited by the number of\n" " freecells or vacant stacks or not.\n" "--empty-stacks-filled-by {kings|none|all}\n" " Specifies which cards can fill empty stacks.\n" "\n" "--game [game] --preset [game] -g [game]\n" " Specifies the type of game. (Implies several of the game settings\n" " options above.). Available presets:\n" " bakers_dozen - Baker\'s Dozen\n" " bakers_game - Baker\'s Game\n" " beleaguered_castle - Beleaguered Castle\n" " citadel - Citadel\n" " cruel - Cruel\n" " der_katz - Der Katzenschwanz\n" " die_schlange - Die Schlange\n" " eight_off - Eight Off\n" " fan - Fan\n" " forecell - Forecell\n" " freecell - Freecell\n" " good_measure - Good Measure\n" " ko_bakers_game - Kings\' Only Baker\'s Game\n" " relaxed_freecell - Relaxed Freecell\n" " relaxed_seahaven - Relaxed Seahaven Towers\n" " seahaven - Seahaven Towers\n" " simple_simon - Simple Simon\n" " streets_and_alleys - Streets and Alleys\n" "\n" "-md [depth] --max-depth [depth] \n" " Specify a maximal search depth for the solution process.\n" "-mi [iter_num] --max-iters [iter_num] \n" " Specify a maximal number of iterations number.\n" "-mss [states_num] --max-stored-states [states_num] \n" " Specify the maximal number of states stored in memory.\n" "\n" "-to [tests_order] --tests-order [tests_order] \n" " Specify a test order string. Each test is represented by one character.\n" " Valid tests:\n" " Freecell Tests:\n" "\n" " '0' - put top stack cards in the foundations.\n" " '1' - put freecell cards in the foundations.\n" " '2' - put freecell cards on top of stacks.\n" " '3' - put non-top stack cards in the foundations.\n" " '4' - move stack cards to different stacks.\n" " '5' - move stack cards to a parent card on the same stack.\n" " '6' - move sequences of cards onto free stacks.\n" " '7' - put freecell cards on empty stacks.\n" " '8' - move cards to a different parent.\n" " '9' - empty an entire stack into the freecells.\n" "\n" " Atomic Freecell Tests:\n" "\n" " 'A' - move a stack card to an empty stack.\n" " 'B' - move a stack card to a parent on a different stack.\n" " 'C' - move a stack card to a freecell.\n" " 'D' - move a freecel card to a parent.\n" " 'E' - move a freecel card to an empty stack.\n" "\n" " Simple Simon Tests:\n" "\n" " 'a' - move a full sequence to the foundations.\n" " 'b' - move a sequence to a true parent of his.\n" " 'c' - move a whole stack sequence to a false parent (in order to\n" " clear the stack)\n" " 'd' - move a sequence to a true parent that has some cards above it.\n" " 'e' - move a sequence with some cards above it to a true parent.\n" " 'f' - move a sequence with a junk sequence above it to a true parent\n" " that has some cards above it.\n" " 'g' - move a whole stack sequence to a false parent which has some\n" " cards above it.\n" " 'h' - move a sequence to a parent on the same stack.\n" "\n" " Tests are grouped with parenthesis or square brackets. Each group\n" " will be randomized as a whole by the random-dfs scan.\n" "\n" "\n" "-me [solving_method] --method [solving_method]\n" " Specify a solving method. Available methods are:\n" " \"a-star\" - A*\n" " \"bfs\" - Breadth-First Search\n" " \"dfs\" - Depth-First Search (default)\n" " \"random-dfs\" - A randomized DFS\n" " \"soft-dfs\" - \"Soft\" DFS\n" "\n" "-asw [A* Weights] --a-star-weight [A* Weights]\n" " Specify weights for the A* scan, assuming it is used. The parameter\n" " should be a comma-separated list of numbers, each one is proportional\n" " to the weight of its corresponding test.\n" "\n" " The numbers are, in order:\n" " 1. The number of cards out.\n" " 2. The maximal sequence move.\n" " 3. The number of cards under sequences.\n" " 4. The length of the sequences which are found over renegade cards.\n" " 5. The depth of the board in the solution.\n" "\n" "-seed [seed_number]\n" " Set the seed for the random number generator used by the\n" " \"random-dfs\" scan.\n" "\n" "-nst --next-soft-thread\n" " Move to the next Soft-Thread. I.e: input another scan to run in\n" " parallel.\n" "-step [step iterations] --soft-thread-step [step iterations]\n" " Set the number of iterations in the step of the current soft-thread.\n" "-nht --next-hard-thread\n" " Move to the next Hard-Thread. This is a new group of scans to run\n" " in their own system thread (assuming the executable was compiled with\n" " support for them.)\n" "--st-name\n" " Set the name of the soft-thread.\n" "\n" "--prelude [prelude_string]\n" " Set the prelude string of the hard thread. A prelude is a static\n" " sequence of iterations quotas that are executed at the beginning of\n" " the search. The format is a list of [Limit]@[Soft-Thread Name]\n" " delimited by commas.\n" "\n" "-ni --next-instance\n" " Move to the next distinct solver instance. This is a separate scan\n" " which would run only if the previous ones returned an unsolvable\n" " verdict.\n" "\n" "-opt --optimize-solution\n" " Try and optimize the solution for a small number of moves.\n" "-opt-to --optimization-tests-order\n" " The test order of the optimization scan.\n" "\n" "\n" "--reparent-states\n" " Reparent states that have a larger depth than that of the state\n" " from which they were reached a posteriori.\n" "--calc-real-depth\n" " If --reparent-states is enabled, then explictly calculate the real\n" " depth of a state by tracing its path to the initial state\n" "--scans-synergy {none|dead-end-marks}\n" " Specifies the cooperation between the scans.\n" "\n" "\n" "--reset\n" " Reset the program to its initial, unconfigured state.\n" "--read-from-file [{num_skip},]filename\n" " Reads configuration parameter with the file while skipping num_skip\n" " arguments from the beginning.\n" "-l [configuration] --load-config [configuration]\n" " Reads the configuration [configruration] and configures the solver\n" " accordingly.\n" "\n" "\n" "Signals:\n" "SIGUSR1 - Prints the number of states that were checked so far to stderr.\n" "SIGUSR2 SIGUSR1 - Turns iteration output on/off.\n" "SIGUSR2 SIGUSR2 SIGUSR1 - Turns iteration's state output on/off.\n" "\n" "\n" "Freecell Solver was written by Shlomi Fish.\n" "Homepage: http://vipe.technion.ac.il/~shlomif/freecell-solver/\n" "Send comments and suggestions to shlomif@vipe.technion.ac.il\n" }, { "real-help", "The environment variable FREECELL_SOLVER_DEFAULT_HELP sets the default help\n" "screen. The name of the help screen is the same name as its \"--help-\" flag\n" "but without the preceding \"--help-\". Type:\n" "\n" " fc-solve --help-summary\n" "\n" "for the available help screens.\n" "\n" "Refer to your system's documentation for information on how to set environment\n" "variables.\n" }, { "problems", "To be discussed.\n" }, { "short-sol", "The following configurations may produce shorter solutions:\n" "\n" " fc-solve -opt\n" " fc-solve --method a-star -opt\n" " fc-solve --reparent-states -opt\n" " fc-solve --method a-star --reparent-states -opt\n" "\n" "If \"--method a-star\" is specified you can set the weights with\n" "-asw {comma separated list of 5 numeric weights}, which may improve\n" "the length of the solution. (refer to the USAGE file for more information)\n" }, { "summary", "fc-solve [flags] [board_file|-]\n" "\n" "Reads board from standard input by default or if a \"-\" is specified.\n" "\n" "- If it takes too long to finish, type \"fc-solve --help-configs\"\n" "- If it erroneously reports a board as unsolvable, try adding the\n" " \"-to 01ABCDE\" flag\n" "- If the solution is too long type \"fc-solve --help-short-sol\"\n" "- To present the moves only try adding \"-m\" or \"-m -snx\"\n" "- For a description of all options type \"fc-solve --help-options\"\n" "- To deal with other problems type \"fc-solve --help-problems\"\n" "- To turn --help into something more useful, type\n" " \"fc-solve --help-real-help\"\n" "\n" "Contact Shlomi Fish, shlomif@vipe.technion.ac.il for more information.\n" }, { NULL, NULL } } ; enum MY_FCS_CMD_LINE_RET_VALUES { EXIT_AND_RETURN_0 = FCS_CMD_LINE_USER, }; static void print_help_string(char * key) { int i; for(i=0;help_screens[i].key != NULL ; i++) { if (!strcmp(key, help_screens[i].key)) { printf("%s", help_screens[i].screen); } } } static int cmd_line_callback( void * instance, int argc, char * argv[], int arg, int * num_to_skip, int * ret, void * context ) { freecell_solver_display_information_context_t * dc; *num_to_skip = 0; dc = (freecell_solver_display_information_context_t * )context; if ((!strcmp(argv[arg], "-h")) || (!strcmp(argv[arg], "--help"))) { char * help_key; help_key = getenv("FREECELL_SOLVER_DEFAULT_HELP"); if (help_key == NULL) { help_key = "summary"; } print_help_string(help_key); *ret = EXIT_AND_RETURN_0; return FCS_CMD_LINE_STOP; } else if (!strncmp(argv[arg], "--help-", 7)) { print_help_string(argv[arg]+7); *ret = EXIT_AND_RETURN_0; return FCS_CMD_LINE_STOP; } else if ((!strcmp(argv[arg], "-i")) || (!strcmp(argv[arg], "--iter-output"))) { #define set_iter_handler() \ freecell_solver_user_set_iter_handler( \ instance, \ my_iter_handler, \ dc \ ); \ dc->debug_iter_output_on = 1; set_iter_handler(); } else if ((!strcmp(argv[arg], "-s")) || (!strcmp(argv[arg], "--state-output"))) { set_iter_handler(); dc->debug_iter_state_output = 1; #undef set_iter_handler } else if ((!strcmp(argv[arg], "-p")) || (!strcmp(argv[arg], "--parseable-output"))) { dc->parseable_output = 1; } else if ((!strcmp(argv[arg], "-c")) || (!strcmp(argv[arg], "--canonized-order-output"))) { dc->canonized_order_output = 1; } else if ((!strcmp(argv[arg], "-t")) || (!strcmp(argv[arg], "--display-10-as-t"))) { dc->display_10_as_t = 1; } else if ((!strcmp(argv[arg], "-m")) || (!strcmp(argv[arg], "--display-moves"))) { dc->display_moves = 1; dc->display_states = 0; } else if ((!strcmp(argv[arg], "-sn")) || (!strcmp(argv[arg], "--standard-notation"))) { dc->standard_notation = 1; } else if ((!strcmp(argv[arg], "-snx")) || (!strcmp(argv[arg], "--standard-notation-extended"))) { dc->standard_notation = 2; } else if ((!strcmp(argv[arg], "-sam")) || (!strcmp(argv[arg], "--display-states-and-moves"))) { dc->display_moves = 1; dc->display_states = 1; } else if ((!strcmp(argv[arg], "-pi")) || (!strcmp(argv[arg], "--display-parent-iter"))) { dc->display_parent_iter_num = 1; } else if ((!strcmp(argv[arg], "--reset"))) { init_debug_context(dc); freecell_solver_user_set_iter_handler( instance, NULL, NULL ); *num_to_skip = 0; return FCS_CMD_LINE_OK; } else { printf("Unimplemented option - \"%s\"!", argv[arg]); exit(-1); } *num_to_skip = 1; return FCS_CMD_LINE_SKIP; } static int command_num = 0; static int debug_iter_output_on = 0; static void select_signal_handler(int signal_num) { command_num = (command_num+1)%3; } static void * current_instance; static freecell_solver_display_information_context_t * dc; static void command_signal_handler(int signal_num) { if (command_num == 0) { fprintf( stderr, "The number of iterations is %i\n", freecell_solver_user_get_num_times(current_instance) ); } else if (command_num == 1) { if (debug_iter_output_on) { freecell_solver_user_set_iter_handler( current_instance, NULL, NULL ); debug_iter_output_on = 0; } else { freecell_solver_user_set_iter_handler( current_instance, my_iter_handler, dc ); debug_iter_output_on = 1; } } else if (command_num == 2) { dc->debug_iter_state_output = ! dc->debug_iter_state_output; } command_num = 0; } static char * known_parameters[] = { "-h", "--help", "--help-configs", "--help-options", "--help-problems", "--help-real-help", "--help-short-sol", "--help-summary", "-i", "--iter-output", "-s", "--state-output", "-p", "--parseable-output", "-c", "--canonized-order-output", "-t", "--display-10-as-t", "-m", "--display-moves", "-sn", "--standard-notation", "-snx", "--standard-notation-extended", "-sam", "--display-states-and-moves", "-pi", "--display-parent-iter", "--reset", NULL }; #define USER_STATE_SIZE 1024 int main(int argc, char * argv[]) { int parser_ret; void * instance; char * error_string; int arg; FILE * file; char user_state[USER_STATE_SIZE]; int ret; freecell_solver_display_information_context_t debug_context; init_debug_context(&debug_context); dc = &debug_context; instance = freecell_solver_user_alloc(); current_instance = instance; parser_ret = freecell_solver_user_cmd_line_parse_args( instance, argc, argv, 1, known_parameters, cmd_line_callback, &debug_context, &error_string, &arg ); if (parser_ret == EXIT_AND_RETURN_0) { freecell_solver_user_free(instance); return 0; } else if ( (parser_ret == FCS_CMD_LINE_PARAM_WITH_NO_ARG) ) { fprintf(stderr, "The command line parameter \"%s\" requires an argument" " and was not supplied with one.\n", argv[arg]); return (-1); } else if ( (parser_ret == FCS_CMD_LINE_ERROR_IN_ARG) ) { if (error_string != NULL) { fprintf(stderr, "%s", error_string); free(error_string); } freecell_solver_user_free(instance); return -1; } if ((arg == argc) || (!strcmp(argv[arg], "-"))) { file = stdin; if (!getenv("FREECELL_SOLVER_QUIET")) { fprintf(stderr, "%s", "Reading the board from the standard input.\n" "Type \"fc-solve --help\" for more usage information.\n" "To cancel this message set the FREECELL_SOLVER_QUIET environment variable.\n" ); } } else if (argv[arg][0] == '-') { fprintf(stderr, "Unknown option \"%s\". " "Type \"%s --help\" for usage information.\n", argv[arg], argv[0] ); freecell_solver_user_free(instance); return -1; } else { file = fopen(argv[arg], "r"); if (file == NULL) { fprintf(stderr, "Could not open file \"%s\" for input. Exiting.\n", argv[arg] ); freecell_solver_user_free(instance); return -1; } } memset(user_state, '\0', sizeof(user_state)); fread(user_state, sizeof(user_state[0]), USER_STATE_SIZE-1, file); fclose(file); /* Win32 Does not have those signals */ #ifndef WIN32 signal(SIGUSR1, command_signal_handler); signal(SIGUSR2, select_signal_handler); #endif #if 0 { int limit = 500; freecell_solver_user_limit_iterations(instance, limit); ret = freecell_solver_user_solve_board(instance, user_state); while (ret == FCS_STATE_SUSPEND_PROCESS) { limit += 500; freecell_solver_user_limit_iterations(instance, limit); ret = freecell_solver_user_resume_solution(instance); } } #else ret = freecell_solver_user_solve_board(instance, user_state); #endif if (ret == FCS_STATE_INVALID_STATE) { char * error_string; error_string = freecell_solver_user_get_invalid_state_error_string( instance, debug_context.display_10_as_t ); printf("%s\n", error_string); free(error_string); } else { if (ret == FCS_STATE_WAS_SOLVED) { printf("-=-=-=-=-=-=-=-=-=-=-=-\n\n"); { fcs_move_t move; FILE * move_dump; char * as_string; int move_num = 0; move_dump = stdout; if (debug_context.display_states) { as_string = freecell_solver_user_current_state_as_string( instance, debug_context.parseable_output, debug_context.canonized_order_output, debug_context.display_10_as_t ); fprintf(move_dump, "%s\n", as_string); free(as_string); fprintf(move_dump, "%s", "\n====================\n\n"); } while ( freecell_solver_user_get_next_move( instance, &move ) == 0 ) { if (debug_context.display_moves) { as_string = freecell_solver_user_move_to_string_w_state( instance, move, debug_context.standard_notation ); if (debug_context.display_states && debug_context.standard_notation) { fprintf(move_dump, "Move: "); } fprintf( move_dump, (debug_context.standard_notation ? "%s " : "%s\n" ), as_string ); move_num++; if (debug_context.standard_notation) { if ((move_num % 10 == 0) || debug_context.display_states) { fprintf(move_dump, "\n"); } } if (debug_context.display_states) { fprintf(move_dump, "\n"); } fflush(move_dump); free(as_string); } if (debug_context.display_states) { as_string = freecell_solver_user_current_state_as_string( instance, debug_context.parseable_output, debug_context.canonized_order_output, debug_context.display_10_as_t ); fprintf(move_dump, "%s\n", as_string); free(as_string); } if (debug_context.display_states || (!debug_context.standard_notation)) { fprintf(move_dump, "%s", "\n====================\n\n"); } } if (debug_context.standard_notation && (!debug_context.display_states)) { fprintf(move_dump, "\n\n"); } } printf("This game is solveable.\n"); } else { printf ("I could not solve this game.\n"); } printf( "Total number of states checked is %i.\n", freecell_solver_user_get_num_times(instance) ); #if 1 printf( "This scan generated %i states.\n", freecell_solver_user_get_num_states_in_collection(instance) ); #endif } freecell_solver_user_free(instance); return 0; }