понедельник, 7 мая 2012 г.

Поиск кратчайшего пути в лабиринте

Моя реализация волнового алгоритма Ли.
Лабиринт хранится в файле in.txt. (# - стена; S - старт; G - финиш; свободный сектор - точка или пробел)
В результате: в командную строку выводится количество шагов
                       в файл out.txt выводится лабиринт и все кратчайшие пути

/*
 Входной файл: in.txt
 Выходной файл: out.txt

 Условные обозначения во входном и выходном файлах:
 '.' = доступная позиция
 '#' = стена
 'S' = старт
 'G' = финиш
 '+' = путь
*/
  
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <time.h>
#include <dos.h>
#include <windows.h>
#include <stdio.h>

#define FALSE 0
#define TRUE 1

//максимальный размер лабиринта
#define MAX_NROWS 80
#define MAX_MCOLS 160

int nRows=0;
int mCols=0;
char maze[MAX_NROWS][MAX_MCOLS]={'#'}; //матрица-лабиринт


int  steps[MAX_NROWS][MAX_MCOLS]={0};//матрица расстояний от старта
                                     //до всех возможных точек

int find_min_path(int x,int y);
int find_path(int x, int y,int step);


//функция загрузки лабиринта из файла в массив
int load() {

       
    FILE *f;
    if ((f = fopen("in.txt","r"))==NULL)
        return FALSE; 

    char ch;
    int i=0,j=0; 
    do {
        do {
           ch = fgetc(f);
           if (ch==' ')    //пробелы заменяем на точки
            ch='.'; 
           
           maze[i][j]=ch;

           if (i>MAX_NROWS || j>MAX_MCOLS)
               return FALSE;   

           if (j>mCols)
                mCols=j;
           j++;

        } while (ch!='\n' && ch!=EOF);
        j=0;
        i++;
        if (i>nRows  )
            nRows=i;
    } while (!feof(f));

    fclose(f);


    return TRUE;

}

// функция сохранения результата в файл 
//(лабиринт со всеми кратчайшими путями)
int save() {
    FILE *f;
    f = fopen("out.txt","a+");
    


    for (int i = 0; i <nRows;i++ )
    {
        for (int j = 0; j <mCols;j++ )
        {
            fputc(maze[i][j],f);    
        }
        fputc('\n',f);

    }
    fputs("\n\n",f);

    fclose(f);


}

//функция нахождения произвольного символа в лабиринте
//(для нахождения координат старта и финиша)
void findSymbol(int &x,int &y,char Symbol) {
    for (int i = 0; i <nRows;i++ )
    {
        for (int j = 0; j <mCols;j++ )
        {
            if (maze[i][j]==Symbol){
                x=i;
                y=j;
                return;
            }
        }
     }

}

int num = 0; //минимальное количество шагов

int main(void)
{
        load(); 

        int x=0,y=0;
        findSymbol(x,y,'S');

        find_path(y, x,0);

        findSymbol(x,y,'G');

        find_min_path(y,x);

        printf("Success!\n");

        save();
        printf("Steps: %i\n",num);

        getch();
        return 0;
}

//рекурсивная функция заполнения матрицы расстояний
int find_path(int x, int y,int step)
{       

        //если вышли за пределы лабиринта - возвращаемся
        if ( x < 0 || x > mCols - 1 || y < 0 || y > nRows - 1 ) return FALSE;

        //если дошли до финиша - возвращаемся
        if ( maze[y][x] == 'G' ) return FALSE;

        //если наткнулись на стену - возвращаемся
        if (maze[y][x] == '#') return FALSE;

        
        //если путь свободен - увеличиваем количество шагов
        //и запоминаем его в мацциве
        if (maze[y][x]!='S' && maze[y][x]!='G') {
            step++;



            if (steps[y][x]>step || steps[y][x]==0)
            {
                steps[y][x]=step;
            }

        }
        
        //4 напровления - 4 вызова
        //идем в определенном направлении если мы там еще не были 
        //(расстояние м матрице расстояний==0)
        //или соблюдаются все условия:
        //1) расстояние там больше текущего+1
        //2) расстояние там не рамно текущее-1 
        //(что бы не пойти обратно тому же пути)

        if (maze[y][x+1]!='S')
           if ((steps[y][x+1]>(step+1) && steps[y][x+1]!=(step-1)) || steps[y][x+1]==0)
               if ( find_path(x + 1, y,step) == TRUE ) return FALSE;

        if (maze[y][x-1]!='S')
           if ((steps[y][x-1]>(step+1) && steps[y][x-1]!=(step-1) ) || steps[y][x-1]==0)
               if ( find_path(x - 1, y,step) == TRUE ) return FALSE;

        if (maze[y+1][x]!='S')
           if ((steps[y+1][x]>(step+1) && steps[y+1][x]!=(step-1) ) || steps[y+1][x]==0)
               if ( find_path(x, y + 1,step) == TRUE ) return FALSE;

        if (maze[y-1][x]!='S')
           if ((steps[y-1][x]>(step+1) && steps[y-1][x]!=(step-1) ) || steps[y-1][x]==0)
               if ( find_path(x, y - 1,step) == TRUE ) return FALSE;


        return FALSE;
}

//рекурсивная функция, обходящаа лабиринт с финиша до старта
//по кратчайшему пути

int find_min_path(int x,int y)
{

    //если вышли за пределы лабиринта - возвращаемся
    if ( x < 0 || x > mCols - 1 || y < 0 || y > nRows - 1 ) return FALSE;

    
    //если данная позиция - не старт и не финиш, то помечаем ее как путь
    if (maze[y][x]!='S' && maze[y][x]!='G') {
        maze[y][x] = '+';
        
    }
    
    //если дошли до старта - возвращаемся
    if ( maze[y-1][x] == 'S' ) return FALSE;
    if ( maze[y+1][x] == 'S' ) return FALSE;
    if ( maze[y][x-1] == 'S' ) return FALSE;
    if ( maze[y][x+1] == 'S' ) return FALSE;




    //изнаем расстояния во всех 4-х направлениях

    int up =   0;
    int right =0;
    int left = 0;
    int down = 0;

    if (y!=  0)
        up =   steps[y - 1][x];
    if (x!=  0)
        left  =steps[y][x - 1];
    if (x!=  mCols-1)
        right = steps[y][x+1];
    if (y!=  nRows-1)
        down = steps[y+1][x];

    //и ищем минимальное
    int min = up;

    if ((right<min && right!=0) || min==0)
        min = right;
    if ((left<min && left!=0)|| min==0)
        min = left;
    if ((down<min && down!=0) || min==0)
        min = down;

    if (num==0  )
        num=min;

    //там, где минимальное, туда и идём

    if (up==min  && up!=0)
        if ( find_min_path(x,y - 1) == TRUE ) return TRUE;


    if (right==min && right!=0 )
        if ( find_min_path(x + 1,y) == TRUE ) return TRUE;



    if (left==min  && left!=0)
        if ( find_min_path(x - 1,y) == TRUE ) return TRUE;



    if (down==min && down!=0)
        if ( find_min_path(x,y + 1) == TRUE ) return TRUE;

    

    return FALSE;


}