SJS wrote:
> /*
>  * $Id: sliding_nine.c,v 1.2 2007/12/24 19:55:58 stremler Exp $
>  */
> 
> /*
>  * This program 'solves' the 3x3 sliding-puzzle problem.
>  * Not robustly, it was hacked together in an evening. No design,
>  * no testing, no good C programming habits.
>  *
>  * Author: Stewart Stremler
>  * License: public domain
>  */
> 
> #include <stdlib.h>
> #include <stdio.h>
> #include <strings.h>
> 
> #ifndef FALSE
> #define FALSE 0
> #define TRUE 1
> #endif
> 
> #define ERROR_EMPTY_ARG "Programmer error. Empty argument in %s at %d\n"
> #define ERROR_MALLOC_FAIL "Failed to allocate memory. In %s at %d\n"
> #define ERROR_BAD_BOARD "Bad board configuration: '%s' in %s at %d\n"
> #define ERROR_SWITCH_BROKE "Unknown value in switch: %d in %s at %d\n"
> 
> /* change BLANK to use a different 'empty' character. '_' might be good. */
> #define BLANK ' '
#define BLANK '.'
/* allows entry of board with 10-key */
> 
> /* The fundamental board structure. We have nine puzzle slots, but
>  * we want to treat it as a string, so we need space for the terminating
>  * null.  We want to eliminate repeats, so we keep track of the parent,
>  * which also offers a simple way to print out the final pieces in order.
>  */
> typedef struct {
>    char board[10];
>    void *moves[4];
>    void *prev;
> } configuration;
> 
> /* We need a simple queue of arbitrary size, so we use a singly linked list.
>  */
> typedef struct {
>    configuration *data;
>    void *next;
> } dequenode;
> 
> /* We push new items to the tail and pull them from the head for fast
>  * O(1) access.
>  * TODO - also keep a count of the # of elements in the queue?
>  */
> typedef struct {
>    dequenode *head;
>    dequenode *tail;
> } deque;
> 
> /* ----------------------------------------------------------------- */
> 
> /* make_dequeue
>  * creates a new queue.
>  * exits on malloc failure.
>  */
> deque *make_deque() {
>    deque *p;
>    p = malloc( sizeof( deque ) );
>    if ( p == NULL ) {
>       fprintf( stderr, ERROR_MALLOC_FAIL, __FILE__, __LINE__ );
>       exit( EXIT_FAILURE );
>    }
> 
>    p->head = NULL;
>    p->tail = NULL;
> }
> 
> /* deque_insert
>  * inserts the current node into the queue.
>  * exits on malloc failure.
>  */
> void deque_insert( deque *d, configuration *current ) {
>    dequenode *n, *t;
>    n = malloc( sizeof( dequenode ) );
>    if ( n == NULL ) {
>       fprintf( stderr, ERROR_MALLOC_FAIL, __FILE__, __LINE__ );
>       exit( EXIT_FAILURE );
>    }
> 
>    n->data = current;
> 
>    if ( d->head == NULL ) {
>       d->head = n;
>    } else {
>       t = d->tail;
>       t->next = (struct dequenode *)n;
>       /*d->tail->next = n;*/
>    }
>    d->tail = n;
> }
> 
> /* deque_remove
>  * removes the oldest node from the queue.
>  * do not call if queue is empty.
>  */
> configuration *deque_remove( deque *d ) {
>    configuration *p;
>    dequenode *q;
> 
>    q = d->head;
>    p = q->data;
> 
>    if ( d->head == d->tail ) {
>       d->head = NULL;
>       d->tail = NULL;
>    } else {
>       d->head = (dequenode *)d->head->next;
>    }
> 
>    free( q );
> 
>    return p;
> }
> 
> /* deque_empty
>  * answers if the queue is empty.
>  */
> int deque_empty( deque *d ) {
>    return d->head == NULL;
> }
> 
> /* deque_size
>  * answers the number of items in the queue.
>  * warning - very slow. Walks the linked list. See TODO for struct.
>  */
> int deque_size( deque *d ) {
>    int count = 0;
>    dequenode *p = d->head;
>    dequenode *end = d->tail;
>    while ( p != end ) { count++; p = p->next; }
>    return count;
> }
> 
> /* ----------------------------------------------------------------- */
> 
> /* got_match
>  * Answers if the two configurations are equivalent.
>  * Exits on programmer error.
>  */
> int got_match( configuration *current, configuration *final ) {
>    if ( current == NULL ) {
>       return FALSE;
>    }
>    if ( final == NULL ) {
>       fprintf( stderr, ERROR_EMPTY_ARG, __FILE__, __LINE__ );
>       /*return FALSE;*/
>       exit( EXIT_FAILURE );
>    }
> 
>    return ! (strncmp( current->board, final->board, 10 ));
> }
> 
> /* got_repeat
>  * Walks the ancestor list looking for a configuration that is equivalent
>  * to the current node. This is an optimization step.
>  */
> int got_repeat( configuration *current ) {
>    configuration *p;
>    p = (configuration *)current->prev;
>    while ( p != NULL ) {
>       if ( got_match( p, current ) ) return TRUE;
>       p = (configuration *)p->prev;
>    }
>    return FALSE;
> }
> 
> /* new
>  * Answers a clone of the the provided configuration. A NULL configuration
>  * will cause an uninitialized configuration to be created.
>  * TODO - how much of the brute-force initialization can we remove?
>  */
> configuration *new( configuration *current ) {
>    configuration *p;
> 
>    p = malloc( sizeof( configuration ) );
>    if ( p == NULL ) {
>       fprintf( stderr, ERROR_MALLOC_FAIL, __FILE__, __LINE__);
>       exit( EXIT_FAILURE );
>    }
>    p->board[0] = '\0';
>    p->board[1] = '\0';
>    p->board[2] = '\0';
>    p->board[3] = '\0';
>    p->board[4] = '\0';
>    p->board[5] = '\0';
>    p->board[6] = '\0';
>    p->board[7] = '\0';
>    p->board[8] = '\0';
>    p->board[9] = '\0';
>    p->board[10] = '\0';
>    p->prev = NULL;
>    p->moves[0] = NULL;
>    p->moves[1] = NULL;
>    p->moves[2] = NULL;
>    p->moves[3] = NULL;
> 
>    if ( current != NULL ) {
>       char *a, *b;
>       p->prev = (struct configuration *)current;
>       strncpy( p->board, current->board, 10 );
>    } 
> 
>    return p;
> }
> 
> /* swap_tile
>  * Swaps the board elements at the specified location.
>  * Assumes arguments are valid and sensible.
>  */
> void swap_tile( configuration *current, int source, int dest ) {
>    char temp;
>    temp = current->board[dest]; 
>    current->board[dest] = current->board[source]; 
>    current->board[source] = temp;
> }
> 
> /* make_child
>  * Creates a unique new child of the current configuration using the
>  * specified swap of tiles and adding to the specified move slot.
>  * If the child repeats an ancestor state, it is discarded.
>  */
> void make_child( configuration *current, int move, int dest, int source ) {
>    configuration *p;
>    p = new( current );
>    swap_tile( p, source, dest );
>    if ( ! got_repeat( p ) ) {
>       current->moves[move] = (struct configuration *)p;
>    } else {
>       free( p );
>    }
> }
> 
> /* fill_children
>  * Creates all the possible children of the current configuration and
>  * adds them to the current configuration.
>  */
> void fill_children( configuration *current ) {
>    configuration *p;
>    char temp;
>    int i;
>    int blank = -1;
> 
>    for ( i = 0; i < 9; i++ ) {
>       if ( current->board[i] == BLANK ) {
>          blank = i;
>          break;
>       }
>    }
> 
>    if ( blank < 0 ) {
>       fprintf( stderr, ERROR_BAD_BOARD, current->board, __FILE__, __LINE__);
>       return;
>    }
> 
>    /*
>     *   0  |  1  |  2
>     * -----+-----+-----
>     *   3  |  4  |  5
>     * -----+-----+-----
>     *   6  |  7  |  8
>     *
>     */
> 
>    switch ( blank ) {
>       case 0: /* 1->0, 3->0 */
>          make_child( current, 0, 0, 1 );
>          make_child( current, 1, 0, 3 );
>          break;
>       case 1: /* 0->1, 2->1, 4->1 */
>          make_child( current, 0, 1, 0 );
>          make_child( current, 1, 1, 2 );
>          make_child( current, 2, 1, 4 );
>          break;
>       case 2: /* 1->2, 5->2 */
>          make_child( current, 0, 2, 1 );
>          make_child( current, 1, 2, 5 );
>          break;
>       case 3: /* 0->3, 4->3, 6-> 3 */
>          make_child( current, 0, 3, 0 );
>          make_child( current, 1, 3, 4 );
>          make_child( current, 2, 3, 6 );
>          break;
>       case 4: /* 1->4, 3->4, 5->4, 7->4 */
>          make_child( current, 0, 4, 1 );
>          make_child( current, 1, 4, 3 );
>          make_child( current, 2, 4, 5 );
>          make_child( current, 3, 4, 7 );
>          break;
>       case 5: /* 2->5, 4->5, 8->5 */
>          make_child( current, 0, 5, 2 );
>          make_child( current, 1, 5, 4 );
>          make_child( current, 2, 5, 8 );
>          break;
>       case 6: /* 3->6, 7->6 */
>          make_child( current, 0, 6, 3 );
>          make_child( current, 1, 6, 7 );
>          break;
>       case 7: /* 6->7, 4->7, 8->7 */
>          make_child( current, 0, 7, 4 );
>          make_child( current, 1, 7, 6 );
>          make_child( current, 2, 7, 8 );
>          break;
>       case 8: /* 5->8, 7->8 */
>          make_child( current, 0, 8, 5 );
>          make_child( current, 1, 8, 7 );
>          break;
>       default:
>          fprintf( stderr, ERROR_SWITCH_BROKE, blank, __FILE__, __LINE__ );
>    }
> }
> 
> /* walk_solutions
>  * Breadth-first walk of the solution tree and find the first solution.
>  */
> configuration *walk_solutions( configuration *initial, configuration *final ) 
> {
>    configuration *current;
>    deque *queue;
>    int i;
> 
>    queue = make_deque();
>    deque_insert( queue, initial );
> 
>    while ( TRUE ) {
>       current = deque_remove( queue );
>       if ( got_match( current, final ) ) {
>          return current; /* leak memory like mad */
>       }
> 
> #ifdef MONITOR_PROGRESS
>       i = deque_size( queue );
>       if ( i % 100 == 0 ) {
>          if ( i < 1000 ) fprintf(stderr, ".");
>          else if ( i < 10000 ) fprintf(stderr, ":");
>          else if ( i < 100000 ) fprintf(stderr, "!");
>          else fprintf(stderr, "#");
>       }
> #endif
> 
>       fill_children( current );
>       for ( i = 0; i < 4; i++ ) {
>          if ( current->moves[i] != NULL ) {
>             deque_insert( queue, (configuration *)current->moves[i] );
>          }
>       }/* for */
> 
>    }/* while */
> }
> 
> /* print_solution_size
>  * Outputs the number of moves it took to solve the problem.
>  */
> void print_solution_size( configuration *end ) {
>    configuration *p;
>    int count;
> 
>    count = 0;
>    p = end;
>    while ( p != NULL ) { 
>       p = (configuration *)p->prev; 
>       count++; 
>    }
> 
>    fprintf( stdout, "There are %d steps in the solution.\n", count);
> }
> 
> /* print_solutions
>  * Print the solution-stack, in reasonable order.
>  * Recursive.
>  */
> void print_solutions( configuration *end ) {
>    if ( end->prev != NULL ) {
>       print_solutions( (configuration *)end->prev );
>    }
>    int row, col;
>    for ( row = 0; row < 3; row++ ) {
>       for ( col = 0; col < 3; col++ ) {
>          fprintf( stdout, " [%c] ", end->board[row * 3 + col] );
>       }
>       fprintf( stdout, "\n" );
>    }
>    fprintf( stdout, "\n" );
> }
> 
> /* ----------------------------------------------------------------- */
> 
> /* usage
>  * Outputs how to use the program.
>  */
> void usage( char **argv ) {
>    fprintf( stderr, "Usage: %s start end\n", argv[0]);
>    fprintf( stderr, "\tstart = 9 character board configuration\n");
>    fprintf( stderr, "\tend   = 9 character board configuration\n");
>    fprintf( stderr, "\t(Note that a blank is REQUIRED!)\n");
>    /* change this if change definition of BLANK define */
> }
> 
> /* main
>  * program entry-point
>  */
> int main( int argc, char **argv ) {
>    configuration *start;
>    configuration *end;
>    configuration *solution;
> 
>    if ( argc != 3 ) { usage( argv ); return EXIT_FAILURE; }
> 
>    start = new( NULL );
>    end = new( NULL );
> 
>    if ( strlen( argv[1] ) != 9 ) { usage( argv ); return EXIT_FAILURE; }
>    if ( strlen( argv[2] ) != 9 ) { usage( argv ); return EXIT_FAILURE; }
> 
>    strncpy( start->board, argv[1], 10 );
>    strncpy( end->board, argv[2], 10 );
> 
>    fprintf( stdout, "Starting with '%s' and trying for '%s'\n",
>             start->board, end->board );
>    fprintf( stdout, "start: %p \t end: %p\n", start, end );
> 
>    solution = walk_solutions( start, end );
> 
>    print_solution_size( solution );
> 
>    print_solutions( solution );
> 
>    return EXIT_SUCCESS;
> }
> -----------------------------------------------------------------------------
> 
> -- 
> [email protected]
> http://www.kernel-panic.org/cgi-bin/mailman/listinfo/kplug-lpsg

-- 
[email protected]
http://www.kernel-panic.org/cgi-bin/mailman/listinfo/kplug-lpsg

Reply via email to