Thread: The puzzle again...Swapping elements of 2D array

  1. #1
    Registered User
    Join Date
    Oct 2008
    Posts
    25

    The puzzle again...Swapping elements of 2D array

    Ok, my puzzle program is almost complete. I just can't figure out how to swap the number the user chooses to put in the blank space, and the blank space.

    Here's the function that sees if the move is valid and if it is, calls swap to switch the number to move and the blank space

    Code:
    int doMove(int puzzle[][PUZZLE_SIDE], int move)
    {
        int j = 0;
        int k = 0;
        int a = 0;
        int b = 0;
    
        for(j = 0; j < 4; j++)
        {
            for(k = 0; k < 4; k++)  // Loop through the puzzle
            {
                if(puzzle[j][k] == move) // If the position in the puzzle array is equal to the number to user wants to move, set a and b to the row
                {                        // and column of this position
                    a = j;
                    b = k;        
                }
            }
        }
    
        if(puzzle[a-1][b] == 0 || puzzle[a+1][b] == 0 || puzzle[a][b-1] == 0 || puzzle[a][b+1] == 0) // If the row immediately above or below
        {                                                                                            // the number to move is 0 (in the same column)    
            swap(&a,&b);                                                                             // or if the column to the immediate
            return 1;                                                                                // left or right of the number (in the same row)
        }                                                                                            // is 0, the move is valid and 1 is returned.
        else
        {
            return 0;  // Otherwise the move is invalid and 0 is returned.
        }                                                                                               
    }
    And here's the swap function

    Code:
    void swap(int *a, int *b) 
    {
        int temp;
    
        temp = *a;
        *a = *b;
        *b = temp;  
    }

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Do you want to swap a and b? Of course not.
    Do you want to swap puzzle[a][b] and puzzle[a-1][b] (or maybe puzzle[a][b] and puzzle[a+1][b], etc.)? Of course you do.

  3. #3
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Where you go
    Code:
        if(puzzle[a-1][b] == 0 || puzzle[a+1][b] == 0 || puzzle[a][b-1] == 0 || puzzle[a][b+1] == 0)
    how do you know that a and b have been set? It's possible that they're both 0, if the nested for loops didn't find the number that the user wanted to move (from my limited understanding of your program). In that case you'd be looking at puzzle[-1][0], which is not a very good idea.

    Or what if the user just picked a number on the top row -- then you'd have the same problem. Moral of the issue: always check to make sure a is at least 1 before you go using a-1 as an index.

    Plus your swap function just swaps the values of a and b, which isn't what you want. You want something more like this:
    Code:
    swap(&puzzle[a][b],&puzzle[X][Y]);
    I'm not sure what X and Y are here, but I suspect they depend on which puzzle element is zero.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  4. #4
    Registered User
    Join Date
    Oct 2008
    Posts
    25
    Yea exactly. How would I go about doing that though? The program is an assignment for class and we're not allowed to change the function prototypes.

  5. #5
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Well, you'd want something like this.
    Code:
    if(a > 0 && puzzle[a-1][b] == 0) {
        swap(&puzzle[a][b], &puzzle[a-1][b]);
    }
    In other words, if [a-1][b] is the spot that's zero, swap the found element and [a-1][b]. Right?

    Of course, that still doesn't solve the problem where the user enters a number that isn't found . . . .
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  6. #6
    Registered User
    Join Date
    Oct 2008
    Posts
    25
    The for loops will find the number the user wants to move. It's understood in the assignment that they enter a number that's actually in the puzzle. Just for kicks though I ran the program and entered '27' for the number to move and it just returned 0 (which in main prints out "enter a valid number to move to the open spot.")

  7. #7
    Registered User
    Join Date
    Oct 2008
    Posts
    25
    Here let me post the whole program. You guys are really helpful and I know I'm getting close to making this work, I appreciate it.

    Code:
    #include <stdio.h>
    #include <time.h>
    
    #define MAX_FILE_LENGTH 30
    #define PUZZLE_SIDE 4
    #define EMPTY_SLOT 0
    
    void loadPuzzle(int puzzle[][PUZZLE_SIDE], FILE *fin);
    int getMove();
    void printPuzzle(int puzzle[][PUZZLE_SIDE]);      // Prototypes for the needed functions of the game
    int doMove(int puzzle[][PUZZLE_SIDE], int move);
    void swap(int *a, int *b);
    int solved(int puzzle[][PUZZLE_SIDE]);
    
    int main(void) 
    {    
        int puzzle[PUZZLE_SIDE][PUZZLE_SIDE];
        char filename[MAX_FILE_LENGTH+1];
        int ans;
        
        srand(time(0));
        
        printf("Welcome to the PUZZLE-15 game!\n");
        
        // Get the puzzle file.
        printf("Enter the file storing all of the puzzle configurations.\n");
        scanf("%s", filename);    
        
        while (ans != 2) {
            
            FILE *fin;
            fin = fopen(filename, "r");
        
            // Load the puzzle.
            loadPuzzle(puzzle, fin);
            fclose(fin);
            
            // Let's play!
            int move;
            
            printPuzzle(puzzle);
            move = getMove();
            
            // Keep on playing until the user gives up.
            while (move!=0) {
                  
                // Execute this move, seeing if it's okay.
                int okay = doMove(puzzle, move);
                
                // Print an error message for an invalid move.
                if (!okay) 
                {
                    printf("Sorry, that is not a valid square to slide into ");
                    printf("the open slot.\nNo move has been executed.\n");     
                }
                
                // Get out of the game for a winning move!
                else if (solved(puzzle))
                    break;
                
                // Go get the next move.
                printPuzzle(puzzle);
                move = getMove();
            }
            
            // Output an appropriate puzzle ending message.
            if (move != 0)
                printf("Great, you solved the puzzle!!!\n");
            else
                printf("Sorry, looks like you gave up on the puzzle.\n");
            
            // Get their next selection.
            printf("Which selection would you like?\n");
            printf("1. Load a new puzzle.\n");
            printf("2. Quit.\n");
            scanf("%d", &ans);
        }
        
    }
    
    // Pre-conditions: fin is pointed to the beginning of a file with a valid
    //                 file format for this problem.
    // Post-conditions: A random puzzle from the file pointed to by fin will be
    //                  stored in puzzle.
    void loadPuzzle(int puzzle[][PUZZLE_SIDE], FILE *fin)
    {
        int i = 0;
        int j = 0;
        int k = 0;
        int puzzlenumber = 0; 
        int num_puzzles = 0;
    
        fscanf(fin, "%d", &num_puzzles);                 // Reads the first integer from the file, the number of puzzles
        printf("There are %d puzzles.\n", num_puzzles);
        
        puzzlenumber = rand() % num_puzzles;  // Sets up a random puzzle number from 0 - (num_puzzles - 1) inclusive
    
        for(i = 0; i <= puzzlenumber; i++) // Loops through the entire file, only storing the random puzzle
        {
            for(j = 0; j < 4; j++)
            {
                for(k = 0; k < 4; k++)
                {
                    fscanf(fin, "%d", &puzzle[j][k]);
                }
            }
        }     
    }
    
    // Pre-conditions: none.
    // Post-conditions: A basic menu will be prompted and the user's result returned.
    int getMove() 
    {
        int move;
        printf("Enter the number to move into the empty slot\n");  // Asks the user for the number they wish to move into the empty slot
        scanf("%d", &move);
        return move;   
    }
    
    // Pre-conditions: A valid puzzle is stored in puzzle.
    // Post-conditions: A depiction of the puzzle will be printed out.
    void printPuzzle(int puzzle[][PUZZLE_SIDE]) 
    {
        int j = 0;
        int k = 0;
    
        for(j = 0; j < 4; j++)
        {                           // Loop through the puzzle
            for(k = 0; k < 4; k++)
            {
                if(puzzle[j][k] == EMPTY_SLOT) // When the EMPTY_SLOT (0) is found, an underscore is printed out
                {
                    printf("  _");
                }
                else
                {
                    printf("%3d", puzzle[j][k]); // Otherwise, the remaining elements of the puzzle array are printed out
                }
            }
        printf("\n");
        }
    
    }
    
    // Pre-conditions: puzzle stores a valid puzzle configuration.
    // Post-conditions: If move is a valid square to slide into the open slot,
    //                  the move is executed and 1 is returned. Otherwise, 0
    //                  is returned and no change is made to puzzle.
    int doMove(int puzzle[][PUZZLE_SIDE], int move)
    {
        int j = 0;
        int k = 0;
        int a = 0;
        int b = 0;
    
        for(j = 0; j < 4; j++)
        {
            for(k = 0; k < 4; k++)  // Loop through the puzzle
            {
                if(puzzle[j][k] == move) // If the position in the puzzle array is equal to the number to user wants to move, set a and b to the row
                {                        // and column of this position
                    a = j;
                    b = k;        
                }
            }
        }
    
        if(puzzle[a-1][b] == 0 || puzzle[a+1][b] == 0 || puzzle[a][b-1] == 0 || puzzle[a][b+1] == 0) // If the row immediately above or below
        {                                                                                            // the number to move is 0 (in the same column)    
            swap(&a,&b);                                                                             // or if the column to the immediate
            return 1;                                                                                // left or right of the number (in the same row)
        }                                                                                            // is 0, the move is valid and 1 is returned.
        else
        {
            return 0;  // Otherwise the move is invalid and 0 is returned.
        }                                                                                               
    }
    
    // Pre-condition: none
    // Post-condition: swaps the values in the variables pointed to by a and b.
    void swap(int *a, int *b) 
    {
        int temp;
    
        temp = *a;
        *a = *b;
        *b = temp;  
    }
    
    // Pre-condition: puzzle stores a valid puzzle configuration.
    // Post-condition: Returns 1 if puzzles is solved, 0 otherwise.
    int solved(int puzzle[][PUZZLE_SIDE]) 
    {
        if(puzzle[0][0] == 1 && puzzle[0][1] == 2 && puzzle[0][2] == 3 && puzzle[0][3] == 4
           && puzzle[1][0] == 5 && puzzle[1][1] == 6 && puzzle[1][2] == 7 && puzzle[1][3] == 8
           && puzzle[2][0] == 9 && puzzle[2][1] == 10 && puzzle[2][2] == 11 && puzzle[2][3] == 12
           && puzzle[3][0] == 13 && puzzle[3][1] == 14 && puzzle[3][2] == 15 && puzzle[3][3] == 0)
        {
            return 1;
        }
        else
        {
            return 0;
        } 
    }

  8. #8
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Well, yes. If the use enters a number that isn't found, the for loops won't set a and b, which will then retain their original values of 0 and 0. In other words, it would be like the user typed the first number (the one at puzzle[0][0]).

    Personally, I'd initialize a and b to something like -1, so that after the for loop you can check if the loops actually found the number or not -- and if not, return accordingly. Or you could set a "number_found" flag. Or you could just not worry about it.

    [edit] Wait, now there's a whole program for me to look at! [/edit]
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  9. #9
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by crazygopedder View Post
    The for loops will find the number the user wants to move. It's understood in the assignment that they enter a number that's actually in the puzzle. Just for kicks though I ran the program and entered '27' for the number to move and it just returned 0 (which in main prints out "enter a valid number to move to the open spot.")
    Then you got lucky that neither of the two valid checks in that case came up good. The problem is that (0,0) is a perfectly valid thing for (a, b) to be afterwards -- you should start them off at say (-1, -1) and if they haven't changed after the for-loop, jump early.

  10. #10
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Your solved() function is very ugly. You managed to do the rest of the code with for loops; why not this function?

    Code:
        printf("Enter the file storing all of the puzzle configurations.\n");
        scanf("&#37;s", filename);
    scanf("%s") isn't the best way to read filenames. What if those filenames contained spaces or were particularly long?

    BTW: rand() and srand() are in <stdlib.h>.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  11. #11
    Registered User
    Join Date
    Oct 2008
    Posts
    25
    Yea I know gets would be better but for this assignment it's not necessary. I'm still confused though. I know I need to swap the elements of the array, but like I said earlier we can't change the function prototypes. So swap is destined to swap ints.

  12. #12
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by crazygopedder View Post
    Yea I know gets would be better but for this assignment it's not necessary. I'm still confused though. I know I need to swap the elements of the array, but like I said earlier we can't change the function prototypes. So swap is destined to swap ints.
    Good! That's what you want!

    Edit: Of course at some point you need to figure out which way the piece can move. (Deciding that the move is valid is not enough--you must know what the move is actually going to be.)
    Last edited by tabstop; 10-24-2008 at 06:37 PM.

  13. #13
    Registered User
    Join Date
    Oct 2008
    Posts
    25
    I am forever indebted to you guys. Thanks for all your help. My program is now complete. I changed domove to this:

    Code:
    int doMove(int puzzle[][PUZZLE_SIDE], int move)
    {
        int j = 0;
        int k = 0;
        int a = 0;
        int b = 0;
        int c = 0;
        int d = 0;
    
        for(j = 0; j < 4; j++)
        {
            for(k = 0; k < 4; k++)  // Loop through the puzzle
            {
                if(puzzle[j][k] == move) // If the position in the puzzle array is equal to the number to user wants to move, set a and b to the row
                {                        // and column of this position
                    a = j;
                    b = k;        
                }
                else if(puzzle[j][k] == EMPTY_SLOT)
                {
                    c = j;
                    d = k;
                }
            }
        }
    
        if(puzzle[a-1][b] == 0 || puzzle[a+1][b] == 0 || puzzle[a][b-1] == 0 || puzzle[a][b+1] == 0) // If the row immediately above or below
        {                                                                                            // the number to move is 0 (in the same column)    
            swap(&puzzle[a][b],&puzzle[c][d]);                                                                             // or if the column to the immediate
            return 1;                                                                                // left or right of the number (in the same row)
        }                                                                                            // is 0, the move is valid and 1 is returned.
        else
        {
            return 0;  // Otherwise the move is invalid and 0 is returned.
        }                                                                                               
    }
    And now it works great! Now I just gotta make solved not so ugly and I'm done

  14. #14
    Registered User
    Join Date
    Nov 2008
    Posts
    31

    alternate way to do any of the functions

    Is there any alternate way to code any of the six functions?

  15. #15
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by cprogrammer22 View Post
    Is there any alternate way to code any of the six functions?
    Yes, lots. Why?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Swapping rows in a 2D array
    By bassist11 in forum C Programming
    Replies: 5
    Last Post: 03-11-2010, 12:04 PM
  2. Replies: 16
    Last Post: 05-29-2009, 07:25 PM
  3. 2D array becoming "deallocaded"
    By Aaron M in forum C Programming
    Replies: 2
    Last Post: 09-23-2006, 07:53 AM
  4. Merge sort please
    By vasanth in forum C Programming
    Replies: 2
    Last Post: 11-09-2003, 12:09 PM
  5. 2d Array access by other classes
    By deaths_seraphim in forum C++ Programming
    Replies: 1
    Last Post: 10-02-2001, 08:05 AM