IT/소스코드

[C++] 3-dimension 63-Puzzle

zzun 2003. 7. 10. 06:41
반응형
프로그래밍 언어 / 2003년 1학기 / 한상영 교수님

[설명]
2차원에서의 15-puzzle을 3차원으로 확장한 개념.
임의의 퍼즐 배치를 입력으로 받아
Optimal Solution(최소move)을 구함.
Cell을 움직이거나, 움직임의 sequence를 입력받아 움직일 수 있음.

[puzzle.cpp]
#include <iostream.h>
#include <fstream.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#define WIDTH 4

short CELL[WIDTH][WIDTH][WIDTH];
short* CELL_S = &(CELL[0][0][0]);                // the 1-dimension alias of the 3-dimension array CELL
short MD[WIDTH*WIDTH*WIDTH][WIDTH*WIDTH*WIDTH];                        // save All possible MD values.
short ANS[1000] = {0};                                        // the optimal solution of minimum moving sequence
int bound;

bool read_input(char* filename)                        // at first, load input file (ex. from puzzle.in)
{
       ifstream in (filename);
       
       char buf;
       int current_cell = 0;
       int counter = 0;

       while (in.get(buf))
       {
               if (buf>='0' && buf<='9')
               {
                       current_cell = current_cell*10 + (buf - '0');
               }
               else if (buf == '$')
                       current_cell = 64;
               else
               {
                       if (current_cell > 64)
                       {
                               cout << "ERROR : Wrong input file." << endl;
                               return false;
                       }
                       else if (current_cell > 0)
                       {
                               CELL_S[counter] = current_cell;                        // save it in to the array
                               counter++;
                               current_cell = 0;
                       }
               }                                
       }

       in.close();

       int inv_count = 0;                        // Check whether this problem can be solved or not

       for (int i=1; i<WIDTH*WIDTH*WIDTH; i++)
       {
               if (CELL_S[i-1]>CELL_S[i])
                       inv_count++;
       }

       if (inv_count%2 == 0)
       {
               cout << "Impossible to solve!" << endl;
               return false;
       }

       return true;
}

void set_md()                                // compute Manhattan Distance of all possible pair of cells
{
       int i_x, i_y, i_z, j_x, j_y, j_z;

   for (int i=0; i<WIDTH*WIDTH*WIDTH; i++)
       {
               i_x = i % WIDTH;
               i_z = i / (WIDTH*WIDTH);
               i_y = (i - i_z*WIDTH*WIDTH) / WIDTH;
               for (int j=0; j<WIDTH*WIDTH*WIDTH; j++)
               {
                       j_x = j % WIDTH;
                       j_z = j / (WIDTH*WIDTH);
                       j_y = (j - j_z*WIDTH*WIDTH) / WIDTH;
                       
                       MD[i][j] = abs(i_x - j_x) + abs(i_y - j_y) + abs(i_z - j_z);                // |x1-x2| + |y1-y2| + |z1-z2|
               }
       }

       return;
}

int cur_MD()                                // return current sum of MDs
{
       int md = 0;

       for (int i=0; i<WIDTH*WIDTH*WIDTH; i++)
       {
               if (CELL_S[i] == 64)
                       continue;
               md += MD[i][CELL_S[i]-1];
       }

       return md;
}

bool move_to(int dir)                // move to dir
{
       int x, y, z, idx;                                // find the empty cell
       for (idx=0; idx<WIDTH*WIDTH*WIDTH; idx++)
               if (CELL_S[idx]==64) break;
       x = idx % WIDTH;
       z = idx / (WIDTH*WIDTH);
       y = (idx - z*WIDTH*WIDTH) / WIDTH;

       switch (dir)
       {
       case 1 :                                                // move up
               if (y != 0)
               {
                       CELL[z][y][x] = CELL[z][y-1][x];
                       CELL[z][y-1][x] = 64;
               }
               else
                       return false;                        // impossible to move
               break;

       case 2 :                                                // move down
               if (y != 3)
               {
                       CELL[z][y][x] = CELL[z][y+1][x];
                       CELL[z][y+1][x] = 64;
               }
               else                        
                       return false;
               break;
       
       case 3 :                                                // move left
               if (x != 0)
               {
                       CELL[z][y][x] = CELL[z][y][x-1];
                       CELL[z][y][x-1] = 64;
               }
               else
           return false;
               break;
       
       case 4 :                                                // move right
               if (x != 3)
               {
                       CELL[z][y][x] = CELL[z][y][x+1];
                       CELL[z][y][x+1] = 64;                        
               }
               else
           return false;
               break;

       case 5 :                                                // move forward
               if (z != 0)
               {
                       CELL[z][y][x] = CELL[z-1][y][x];
                       CELL[z-1][y][x] = 64;
               }
               else
           return false;        
               break;

       case 6 :                                                // move backward
               if (z != 3)
               {
                       CELL[z][y][x] = CELL[z+1][y][x];
                       CELL[z+1][y][x] = 64;                        
               }
               else
                       return false;
               break;
       
       default :
               return false;
       }

       return true;                                        // success to move
}

bool IDA(int depth, short prev)                // searching algorithm
{
       depth++;
       short opp;

       for (int i=1; i<=6; i++)
       {
               opp = (i%2 == 0) ? i-1 : i+1;                // the opposite side of moving direction.
               if (prev != opp && move_to(i))
               {
                       if (cur_MD()+depth<=bound)
                       {
                               if (depth == bound || IDA(depth, i))                // recursive call
                               {
                                       move_to(opp);                        // move back
                                       ANS[depth] = i;                        // save the answer
                                       return true;
                               }
                       }
                       move_to(opp);                        // move back
               }
       }

       return false;
}

void print_cur()                                        // print out the current status
{
       for (int i=0; i<WIDTH; i++)
       {
               for (int j=0; j<WIDTH; j++)
               {
                       for (int k=0; k<WIDTH; k++)
                       {
                               if (CELL[j][i][k] == 64)
                                       printf("  $");
                               else
                   printf("%3d", CELL[j][i][k]);
                       }
                       printf(" | ");
               }
               printf("\n");
       }
       return;
}

void search()                                                // find the optimal solution
{
       bound = cur_MD();                                // lower bound of moves in optimal solution

       if (bound != 0)
       {
               cout << "Lower Bound = " << bound << endl << "Now Searching..." << endl;

               cout << "Trying " << bound << endl;

       while(!IDA(0, -1))                        // searching start
               {
                       bound += 2;                                // if the solution wasn't found, try with bound+2 as the lower bound
                       cout << "Trying " << bound << endl;
               }

               cout << "Success!" << endl;
       }

       return;
}

int main(int argc, char* argv[])
{
       char *input_file, *output_file;//, *cmd;

       /*if (argc != 2)                                                // check the arguments
       {
               printf("ERROR : Please enter the input file name as an argument.\n");
               return 1;
       }*/

       input_file = "puzzle.in";//argv[1];

       if (!read_input(input_file))                // read the input file and save it into the array
               return 1;

       char cmd[20];
       //cmd = new char[20];

       set_md();                                                        // set the MD values

       print_cur();

       printf("$>");
       cin.getline(cmd, sizeof(cmd));                                // ready to accept a command

       while (strcmp("quit", cmd))
       {
               if (strlen(cmd)==1 && cmd[0] >= '1' && cmd[0] <= '6')                        // moving command
               {
                       if(!move_to((int)(cmd[0] - '0')))
                               cout << "Illegal Move!" << endl;                        // fail to move
                       else
                               print_cur();                                                                // success to move
               }
               else if (!strncmp("in ", cmd, 3))                        // read a sequence from a file
               {
                       char buf;
                       input_file = cmd + 3;
                       ifstream in(input_file);
                       cout << "Moving... : ";
                       while(in.get(buf))
                       {
                               if (buf >= '1' && buf <= '6')
                               {
                                       cout << buf;
                                       if(!move_to((int)(buf - '0')))
                                       {
                                               cout << " Illegal Move!" << endl;
                                               goto exit;
                                       }
                               }
                       }

                       exit :
                       in.close();
                       cout << endl;
                       print_cur();
               }
               else if (!strncmp("out ", cmd, 4))                        // solve the puzzle, and put out the optimal sequence of moves
               {
                       output_file = cmd + 4;
           search();
                       cout << "Optimal solution : " << bound << " moves." << endl;
                       ofstream out(output_file);
                       for (int i=1; ANS[i] != 0; i++)                        // ANS re-initialization for next solving
                       {
                               out << ANS[i];
                               ANS[i] = 0;
                               if (i%10 == 0)
                                       out << endl;
                       }
                       out.close();

                       print_cur();
               }
               else
                       printf("Wrong Command.\n");

               printf("$>");
               cin.getline(cmd, sizeof(cmd));
       }

       return 0;
}
반응형