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

1. ## 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. 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. 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.

4. 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. 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 . . . .

6. 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. 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

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");

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("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.
{
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. 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.

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

9. Originally Posted by crazygopedder
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. 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>.

11. 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. Originally Posted by crazygopedder
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.)

13. 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. ## alternate way to do any of the functions

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

15. Originally Posted by cprogrammer22
Is there any alternate way to code any of the six functions?
Yes, lots. Why?